From 8b62bbc0c3a257a13aa188d0f905e6ccff309c04 Mon Sep 17 00:00:00 2001 From: Orzu Ionut Date: Thu, 10 Jun 2021 16:59:13 +0300 Subject: [PATCH] WIP OCR --- README.md | 20 + app/Ingest/Convertor.php | 356 +- app/Ingest/DocumentHandler.php | 2 + app/Ingest/OCR.php | 95 + app/Jobs/IngestDocuments.php | 37 +- app/Jobs/SendToCore.php | 14 +- app/Parser/ParseTextArray.php | 75 +- composer.json | 4 +- composer.lock | 140 +- resources/libraries/deskew/Bin/deskew | Bin 0 -> 758232 bytes resources/libraries/deskew/Bin/deskew-arm | Bin 0 -> 702956 bytes resources/libraries/deskew/CmdLineOptions.pas | 443 ++ resources/libraries/deskew/Gui/aboutform.lfm | 92 + resources/libraries/deskew/Gui/aboutform.pas | 76 + .../libraries/deskew/Gui/advoptionsform.lfm | 212 + .../libraries/deskew/Gui/advoptionsform.pas | 126 + resources/libraries/deskew/Gui/config.pas | 24 + resources/libraries/deskew/Gui/datamodule.lfm | 24 + resources/libraries/deskew/Gui/datamodule.pas | 117 + resources/libraries/deskew/Gui/deskewgui.icns | Bin 0 -> 3543 bytes resources/libraries/deskew/Gui/deskewgui.ico | Bin 0 -> 5430 bytes resources/libraries/deskew/Gui/deskewgui.lpi | 236 + resources/libraries/deskew/Gui/deskewgui.lpr | 28 + resources/libraries/deskew/Gui/mainform.lfm | 441 ++ resources/libraries/deskew/Gui/mainform.pas | 284 ++ resources/libraries/deskew/Gui/options.pas | 231 + resources/libraries/deskew/Gui/runner.pas | 174 + resources/libraries/deskew/Gui/utils.pas | 133 + resources/libraries/deskew/ImageUtils.pas | 697 +++ .../libraries/deskew/Imaging/Imaging.pas | 4350 ++++++++++++++++ .../deskew/Imaging/ImagingBitmap.pas | 856 ++++ .../deskew/Imaging/ImagingCanvases.pas | 2127 ++++++++ .../deskew/Imaging/ImagingClasses.pas | 1107 ++++ .../deskew/Imaging/ImagingColors.pas | 246 + .../deskew/Imaging/ImagingComponents.pas | 1308 +++++ .../libraries/deskew/Imaging/ImagingDds.pas | 1145 +++++ .../deskew/Imaging/ImagingExtras.pas | 148 + .../deskew/Imaging/ImagingFormats.pas | 4464 +++++++++++++++++ .../libraries/deskew/Imaging/ImagingGif.pas | 1291 +++++ .../libraries/deskew/Imaging/ImagingIO.pas | 685 +++ .../libraries/deskew/Imaging/ImagingJpeg.pas | 769 +++ .../deskew/Imaging/ImagingNetworkGraphics.pas | 2714 ++++++++++ .../deskew/Imaging/ImagingOptions.inc | 219 + .../deskew/Imaging/ImagingPortableMaps.pas | 977 ++++ .../libraries/deskew/Imaging/ImagingPsd.pas | 801 +++ .../deskew/Imaging/ImagingQuartz.pas | 206 + .../deskew/Imaging/ImagingRadiance.pas | 495 ++ .../libraries/deskew/Imaging/ImagingTarga.pas | 620 +++ .../libraries/deskew/Imaging/ImagingTiff.pas | 104 + .../deskew/Imaging/ImagingTiffMac.pas | 74 + .../libraries/deskew/Imaging/ImagingTypes.pas | 568 +++ .../deskew/Imaging/ImagingUtility.pas | 1735 +++++++ .../libraries/deskew/Imaging/ImagingWic.pas | 183 + .../deskew/Imaging/JpegLib/imjcapimin.pas | 401 ++ .../deskew/Imaging/JpegLib/imjcapistd.pas | 222 + .../deskew/Imaging/JpegLib/imjccoefct.pas | 521 ++ .../deskew/Imaging/JpegLib/imjccolor.pas | 530 ++ .../deskew/Imaging/JpegLib/imjcdctmgr.pas | 513 ++ .../deskew/Imaging/JpegLib/imjchuff.pas | 1116 +++++ .../deskew/Imaging/JpegLib/imjcinit.pas | 95 + .../deskew/Imaging/JpegLib/imjcmainct.pas | 343 ++ .../deskew/Imaging/JpegLib/imjcmarker.pas | 724 +++ .../deskew/Imaging/JpegLib/imjcmaster.pas | 701 +++ .../deskew/Imaging/JpegLib/imjcomapi.pas | 130 + .../deskew/Imaging/JpegLib/imjconfig.inc | 126 + .../deskew/Imaging/JpegLib/imjcparam.pas | 701 +++ .../deskew/Imaging/JpegLib/imjcphuff.pas | 962 ++++ .../deskew/Imaging/JpegLib/imjcprepct.pas | 406 ++ .../deskew/Imaging/JpegLib/imjcsample.pas | 631 +++ .../deskew/Imaging/JpegLib/imjdapimin.pas | 503 ++ .../deskew/Imaging/JpegLib/imjdapistd.pas | 377 ++ .../deskew/Imaging/JpegLib/imjdcoefct.pas | 895 ++++ .../deskew/Imaging/JpegLib/imjdcolor.pas | 501 ++ .../deskew/Imaging/JpegLib/imjdct.pas | 109 + .../deskew/Imaging/JpegLib/imjddctmgr.pas | 328 ++ .../deskew/Imaging/JpegLib/imjdeferr.pas | 497 ++ .../deskew/Imaging/JpegLib/imjdhuff.pas | 1205 +++++ .../deskew/Imaging/JpegLib/imjdinput.pas | 416 ++ .../deskew/Imaging/JpegLib/imjdmainct.pas | 610 +++ .../deskew/Imaging/JpegLib/imjdmarker.pas | 2648 ++++++++++ .../deskew/Imaging/JpegLib/imjdmaster.pas | 679 +++ .../deskew/Imaging/JpegLib/imjdmerge.pas | 514 ++ .../deskew/Imaging/JpegLib/imjdphuff.pas | 1061 ++++ .../deskew/Imaging/JpegLib/imjdpostct.pas | 341 ++ .../deskew/Imaging/JpegLib/imjdsample.pas | 592 +++ .../deskew/Imaging/JpegLib/imjerror.pas | 462 ++ .../deskew/Imaging/JpegLib/imjfdctflt.pas | 175 + .../deskew/Imaging/JpegLib/imjfdctfst.pas | 237 + .../deskew/Imaging/JpegLib/imjfdctint.pas | 297 ++ .../deskew/Imaging/JpegLib/imjidctasm.pas | 793 +++ .../deskew/Imaging/JpegLib/imjidctflt.pas | 285 ++ .../deskew/Imaging/JpegLib/imjidctfst.pas | 410 ++ .../deskew/Imaging/JpegLib/imjidctint.pas | 440 ++ .../deskew/Imaging/JpegLib/imjidctred.pas | 525 ++ .../deskew/Imaging/JpegLib/imjinclude.pas | 126 + .../deskew/Imaging/JpegLib/imjmemmgr.pas | 1283 +++++ .../deskew/Imaging/JpegLib/imjmemnobs.pas | 259 + .../deskew/Imaging/JpegLib/imjmorecfg.pas | 219 + .../deskew/Imaging/JpegLib/imjpeglib.pas | 1300 +++++ .../deskew/Imaging/JpegLib/imjquant1.pas | 1009 ++++ .../deskew/Imaging/JpegLib/imjquant2.pas | 1551 ++++++ .../deskew/Imaging/JpegLib/imjutils.pas | 232 + .../Imaging/LibTiff/Compiled/adler32.obj | Bin 0 -> 726 bytes .../Imaging/LibTiff/Compiled/compress.obj | Bin 0 -> 763 bytes .../deskew/Imaging/LibTiff/Compiled/crc32.obj | Bin 0 -> 11970 bytes .../Imaging/LibTiff/Compiled/deflate.obj | Bin 0 -> 8912 bytes .../Imaging/LibTiff/Compiled/inffast.obj | Bin 0 -> 2709 bytes .../Imaging/LibTiff/Compiled/inflate.obj | Bin 0 -> 11402 bytes .../Imaging/LibTiff/Compiled/inftrees.obj | Bin 0 -> 2737 bytes .../Imaging/LibTiff/Compiled/jcapimin.obj | Bin 0 -> 2721 bytes .../Imaging/LibTiff/Compiled/jcapistd.obj | Bin 0 -> 2133 bytes .../Imaging/LibTiff/Compiled/jccoefct.obj | Bin 0 -> 3615 bytes .../Imaging/LibTiff/Compiled/jccolor.obj | Bin 0 -> 3330 bytes .../Imaging/LibTiff/Compiled/jcdctmgr.obj | Bin 0 -> 3337 bytes .../Imaging/LibTiff/Compiled/jchuff.obj | Bin 0 -> 5176 bytes .../Imaging/LibTiff/Compiled/jcinit.obj | Bin 0 -> 1970 bytes .../Imaging/LibTiff/Compiled/jcmainct.obj | Bin 0 -> 1885 bytes .../Imaging/LibTiff/Compiled/jcmarker.obj | Bin 0 -> 4002 bytes .../Imaging/LibTiff/Compiled/jcmaster.obj | Bin 0 -> 4593 bytes .../Imaging/LibTiff/Compiled/jcomapi.obj | Bin 0 -> 1758 bytes .../Imaging/LibTiff/Compiled/jcparam.obj | Bin 0 -> 5494 bytes .../Imaging/LibTiff/Compiled/jcphuff.obj | Bin 0 -> 4664 bytes .../Imaging/LibTiff/Compiled/jcprepct.obj | Bin 0 -> 2957 bytes .../Imaging/LibTiff/Compiled/jcsample.obj | Bin 0 -> 3998 bytes .../Imaging/LibTiff/Compiled/jctrans.obj | Bin 0 -> 3432 bytes .../Imaging/LibTiff/Compiled/jdapimin.obj | Bin 0 -> 3230 bytes .../Imaging/LibTiff/Compiled/jdapistd.obj | Bin 0 -> 2606 bytes .../Imaging/LibTiff/Compiled/jdatadst.obj | Bin 0 -> 1786 bytes .../Imaging/LibTiff/Compiled/jdatasrc.obj | Bin 0 -> 1806 bytes .../Imaging/LibTiff/Compiled/jdcoefct.obj | Bin 0 -> 5655 bytes .../Imaging/LibTiff/Compiled/jdcolor.obj | Bin 0 -> 3065 bytes .../Imaging/LibTiff/Compiled/jddctmgr.obj | Bin 0 -> 2645 bytes .../Imaging/LibTiff/Compiled/jdhuff.obj | Bin 0 -> 4839 bytes .../Imaging/LibTiff/Compiled/jdinput.obj | Bin 0 -> 3084 bytes .../Imaging/LibTiff/Compiled/jdmainct.obj | Bin 0 -> 3280 bytes .../Imaging/LibTiff/Compiled/jdmarker.obj | Bin 0 -> 9817 bytes .../Imaging/LibTiff/Compiled/jdmaster.obj | Bin 0 -> 3924 bytes .../Imaging/LibTiff/Compiled/jdmerge.obj | Bin 0 -> 3088 bytes .../Imaging/LibTiff/Compiled/jdphuff.obj | Bin 0 -> 5335 bytes .../Imaging/LibTiff/Compiled/jdpostct.obj | Bin 0 -> 2329 bytes .../Imaging/LibTiff/Compiled/jdsample.obj | Bin 0 -> 3392 bytes .../Imaging/LibTiff/Compiled/jdtrans.obj | Bin 0 -> 1976 bytes .../Imaging/LibTiff/Compiled/jerror.obj | Bin 0 -> 7729 bytes .../Imaging/LibTiff/Compiled/jfdctflt.obj | Bin 0 -> 2306 bytes .../Imaging/LibTiff/Compiled/jfdctfst.obj | Bin 0 -> 2216 bytes .../Imaging/LibTiff/Compiled/jfdctint.obj | Bin 0 -> 2527 bytes .../Imaging/LibTiff/Compiled/jidctflt.obj | Bin 0 -> 2770 bytes .../Imaging/LibTiff/Compiled/jidctfst.obj | Bin 0 -> 2699 bytes .../Imaging/LibTiff/Compiled/jidctint.obj | Bin 0 -> 3151 bytes .../Imaging/LibTiff/Compiled/jidctred.obj | Bin 0 -> 3159 bytes .../Imaging/LibTiff/Compiled/jmemmgr.obj | Bin 0 -> 5150 bytes .../Imaging/LibTiff/Compiled/jmemnobs.obj | Bin 0 -> 1900 bytes .../Imaging/LibTiff/Compiled/jquant1.obj | Bin 0 -> 5131 bytes .../Imaging/LibTiff/Compiled/jquant2.obj | Bin 0 -> 6662 bytes .../Imaging/LibTiff/Compiled/jutils.obj | Bin 0 -> 2162 bytes .../LibTiff/Compiled/libtiffpack-win32.a | Bin 0 -> 895446 bytes .../LibTiff/Compiled/libtiffpack-win64.a | Bin 0 -> 1076500 bytes .../Imaging/LibTiff/Compiled/tif_aux.obj | Bin 0 -> 3844 bytes .../Imaging/LibTiff/Compiled/tif_close.obj | Bin 0 -> 1700 bytes .../Imaging/LibTiff/Compiled/tif_codec.obj | Bin 0 -> 2396 bytes .../Imaging/LibTiff/Compiled/tif_color.obj | Bin 0 -> 4056 bytes .../Imaging/LibTiff/Compiled/tif_compress.obj | Bin 0 -> 3597 bytes .../Imaging/LibTiff/Compiled/tif_dir.obj | Bin 0 -> 13015 bytes .../Imaging/LibTiff/Compiled/tif_dirinfo.obj | Bin 0 -> 13115 bytes .../Imaging/LibTiff/Compiled/tif_dirread.obj | Bin 0 -> 17601 bytes .../Imaging/LibTiff/Compiled/tif_dirwrite.obj | Bin 0 -> 11692 bytes .../Imaging/LibTiff/Compiled/tif_dumpmode.obj | Bin 0 -> 1803 bytes .../Imaging/LibTiff/Compiled/tif_error.obj | Bin 0 -> 1640 bytes .../LibTiff/Compiled/tif_extension.obj | Bin 0 -> 1730 bytes .../Imaging/LibTiff/Compiled/tif_fax3.obj | Bin 0 -> 23904 bytes .../Imaging/LibTiff/Compiled/tif_fax3sm.obj | Bin 0 -> 100394 bytes .../Imaging/LibTiff/Compiled/tif_flush.obj | Bin 0 -> 1468 bytes .../Imaging/LibTiff/Compiled/tif_getimage.obj | Bin 0 -> 35495 bytes .../Imaging/LibTiff/Compiled/tif_jpeg.obj | Bin 0 -> 13364 bytes .../Imaging/LibTiff/Compiled/tif_luv.obj | Bin 0 -> 15799 bytes .../Imaging/LibTiff/Compiled/tif_lzw.obj | Bin 0 -> 7571 bytes .../Imaging/LibTiff/Compiled/tif_next.obj | Bin 0 -> 1877 bytes .../Imaging/LibTiff/Compiled/tif_ojpeg.obj | Bin 0 -> 1257 bytes .../Imaging/LibTiff/Compiled/tif_open.obj | Bin 0 -> 4558 bytes .../Imaging/LibTiff/Compiled/tif_packbits.obj | Bin 0 -> 2935 bytes .../Imaging/LibTiff/Compiled/tif_pixarlog.obj | Bin 0 -> 15694 bytes .../Imaging/LibTiff/Compiled/tif_predict.obj | Bin 0 -> 6382 bytes .../Imaging/LibTiff/Compiled/tif_print.obj | Bin 0 -> 9579 bytes .../Imaging/LibTiff/Compiled/tif_read.obj | Bin 0 -> 6820 bytes .../Imaging/LibTiff/Compiled/tif_strip.obj | Bin 0 -> 4135 bytes .../Imaging/LibTiff/Compiled/tif_swab.obj | Bin 0 -> 2463 bytes .../Imaging/LibTiff/Compiled/tif_thunder.obj | Bin 0 -> 2100 bytes .../Imaging/LibTiff/Compiled/tif_tile.obj | Bin 0 -> 3520 bytes .../Imaging/LibTiff/Compiled/tif_version.obj | Bin 0 -> 1414 bytes .../Imaging/LibTiff/Compiled/tif_warning.obj | Bin 0 -> 1658 bytes .../Imaging/LibTiff/Compiled/tif_write.obj | Bin 0 -> 6475 bytes .../Imaging/LibTiff/Compiled/tif_zip.obj | Bin 0 -> 3748 bytes .../deskew/Imaging/LibTiff/Compiled/trees.obj | Bin 0 -> 12422 bytes .../Imaging/LibTiff/Compiled/uncompr.obj | Bin 0 -> 681 bytes .../deskew/Imaging/LibTiff/Compiled/zutil.obj | Bin 0 -> 1784 bytes .../deskew/Imaging/LibTiff/ImagingTiffLib.pas | 663 +++ .../deskew/Imaging/LibTiff/LibDelphi.pas | 214 + .../deskew/Imaging/LibTiff/LibJpegDelphi.pas | 548 ++ .../deskew/Imaging/LibTiff/LibTiffDelphi.pas | 1510 ++++++ .../deskew/Imaging/LibTiff/LibTiffDynLib.pas | 803 +++ .../deskew/Imaging/LibTiff/ZLibDelphi.pas | 89 + .../libraries/deskew/Imaging/ZLib/dzlib.pas | 523 ++ .../libraries/deskew/Imaging/ZLib/imadler.pas | 114 + .../deskew/Imaging/ZLib/iminfblock.pas | 951 ++++ .../deskew/Imaging/ZLib/iminfcodes.pas | 576 +++ .../deskew/Imaging/ZLib/iminffast.pas | 318 ++ .../deskew/Imaging/ZLib/iminftrees.pas | 781 +++ .../deskew/Imaging/ZLib/iminfutil.pas | 222 + .../deskew/Imaging/ZLib/impaszlib.pas | 520 ++ .../libraries/deskew/Imaging/ZLib/imtrees.pas | 2249 +++++++++ .../libraries/deskew/Imaging/ZLib/imzconf.inc | 25 + .../deskew/Imaging/ZLib/imzdeflate.pas | 2129 ++++++++ .../deskew/Imaging/ZLib/imzinflate.pas | 750 +++ .../libraries/deskew/Imaging/ZLib/imzutil.pas | 191 + resources/libraries/deskew/MainUnit.pas | 416 ++ resources/libraries/deskew/Readme.md | 165 + .../libraries/deskew/RotationDetector.pas | 228 + .../libraries/deskew/Scripts/Compile.bat | 4 + .../deskew/Scripts/build_gui_release_macos.sh | 84 + .../deskew/Scripts/compile-arm-linux.sh | 14 + resources/libraries/deskew/Scripts/compile.sh | 8 + .../deskew/Scripts/create_cli_release_zip.sh | 40 + .../deskew/Scripts/create_gui_release_zip.sh | 29 + .../deskew/Scripts/get_gui_versions.sh | 7 + resources/libraries/deskew/deskew.dpr | 47 + resources/libraries/deskew/deskew.dproj | 115 + resources/libraries/deskew/deskew.lpi | 269 + resources/libraries/deskew/deskew.lpr | 47 + resources/python/dewarp | 1 + resources/python/unproject | 1 + 230 files changed, 74846 insertions(+), 79 deletions(-) create mode 100644 app/Ingest/OCR.php create mode 100755 resources/libraries/deskew/Bin/deskew create mode 100755 resources/libraries/deskew/Bin/deskew-arm create mode 100755 resources/libraries/deskew/CmdLineOptions.pas create mode 100755 resources/libraries/deskew/Gui/aboutform.lfm create mode 100755 resources/libraries/deskew/Gui/aboutform.pas create mode 100755 resources/libraries/deskew/Gui/advoptionsform.lfm create mode 100755 resources/libraries/deskew/Gui/advoptionsform.pas create mode 100755 resources/libraries/deskew/Gui/config.pas create mode 100755 resources/libraries/deskew/Gui/datamodule.lfm create mode 100755 resources/libraries/deskew/Gui/datamodule.pas create mode 100755 resources/libraries/deskew/Gui/deskewgui.icns create mode 100755 resources/libraries/deskew/Gui/deskewgui.ico create mode 100755 resources/libraries/deskew/Gui/deskewgui.lpi create mode 100755 resources/libraries/deskew/Gui/deskewgui.lpr create mode 100755 resources/libraries/deskew/Gui/mainform.lfm create mode 100755 resources/libraries/deskew/Gui/mainform.pas create mode 100755 resources/libraries/deskew/Gui/options.pas create mode 100755 resources/libraries/deskew/Gui/runner.pas create mode 100755 resources/libraries/deskew/Gui/utils.pas create mode 100755 resources/libraries/deskew/ImageUtils.pas create mode 100755 resources/libraries/deskew/Imaging/Imaging.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingBitmap.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingCanvases.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingClasses.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingColors.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingComponents.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingDds.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingExtras.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingFormats.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingGif.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingIO.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingJpeg.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingNetworkGraphics.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingOptions.inc create mode 100755 resources/libraries/deskew/Imaging/ImagingPortableMaps.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingPsd.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingQuartz.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingRadiance.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingTarga.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingTiff.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingTiffMac.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingTypes.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingUtility.pas create mode 100755 resources/libraries/deskew/Imaging/ImagingWic.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjcapimin.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjcapistd.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjccoefct.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjccolor.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjcdctmgr.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjchuff.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjcinit.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjcmainct.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjcmarker.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjcmaster.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjcomapi.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjconfig.inc create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjcparam.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjcphuff.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjcprepct.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjcsample.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjdapimin.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjdapistd.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjdcoefct.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjdcolor.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjdct.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjddctmgr.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjdeferr.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjdhuff.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjdinput.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjdmainct.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjdmarker.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjdmaster.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjdmerge.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjdphuff.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjdpostct.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjdsample.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjerror.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjfdctflt.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjfdctfst.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjfdctint.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjidctasm.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjidctflt.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjidctfst.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjidctint.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjidctred.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjinclude.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjmemmgr.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjmemnobs.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjmorecfg.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjpeglib.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjquant1.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjquant2.pas create mode 100755 resources/libraries/deskew/Imaging/JpegLib/imjutils.pas create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/adler32.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/compress.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/crc32.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/deflate.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/inffast.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/inflate.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/inftrees.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jcapimin.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jcapistd.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jccoefct.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jccolor.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jcdctmgr.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jchuff.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jcinit.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jcmainct.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jcmarker.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jcmaster.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jcomapi.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jcparam.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jcphuff.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jcprepct.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jcsample.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jctrans.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jdapimin.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jdapistd.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jdatadst.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jdatasrc.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jdcoefct.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jdcolor.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jddctmgr.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jdhuff.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jdinput.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jdmainct.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jdmarker.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jdmaster.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jdmerge.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jdphuff.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jdpostct.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jdsample.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jdtrans.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jerror.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jfdctflt.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jfdctfst.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jfdctint.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jidctflt.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jidctfst.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jidctint.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jidctred.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jmemmgr.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jmemnobs.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jquant1.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jquant2.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/jutils.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/libtiffpack-win32.a create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/libtiffpack-win64.a create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_aux.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_close.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_codec.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_color.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_compress.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_dir.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_dirinfo.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_dirread.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_dirwrite.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_dumpmode.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_error.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_extension.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_fax3.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_fax3sm.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_flush.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_getimage.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_jpeg.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_luv.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_lzw.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_next.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_ojpeg.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_open.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_packbits.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_pixarlog.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_predict.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_print.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_read.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_strip.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_swab.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_thunder.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_tile.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_version.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_warning.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_write.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_zip.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/trees.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/uncompr.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/Compiled/zutil.obj create mode 100755 resources/libraries/deskew/Imaging/LibTiff/ImagingTiffLib.pas create mode 100755 resources/libraries/deskew/Imaging/LibTiff/LibDelphi.pas create mode 100755 resources/libraries/deskew/Imaging/LibTiff/LibJpegDelphi.pas create mode 100755 resources/libraries/deskew/Imaging/LibTiff/LibTiffDelphi.pas create mode 100755 resources/libraries/deskew/Imaging/LibTiff/LibTiffDynLib.pas create mode 100755 resources/libraries/deskew/Imaging/LibTiff/ZLibDelphi.pas create mode 100755 resources/libraries/deskew/Imaging/ZLib/dzlib.pas create mode 100755 resources/libraries/deskew/Imaging/ZLib/imadler.pas create mode 100755 resources/libraries/deskew/Imaging/ZLib/iminfblock.pas create mode 100755 resources/libraries/deskew/Imaging/ZLib/iminfcodes.pas create mode 100755 resources/libraries/deskew/Imaging/ZLib/iminffast.pas create mode 100755 resources/libraries/deskew/Imaging/ZLib/iminftrees.pas create mode 100755 resources/libraries/deskew/Imaging/ZLib/iminfutil.pas create mode 100755 resources/libraries/deskew/Imaging/ZLib/impaszlib.pas create mode 100755 resources/libraries/deskew/Imaging/ZLib/imtrees.pas create mode 100755 resources/libraries/deskew/Imaging/ZLib/imzconf.inc create mode 100755 resources/libraries/deskew/Imaging/ZLib/imzdeflate.pas create mode 100755 resources/libraries/deskew/Imaging/ZLib/imzinflate.pas create mode 100755 resources/libraries/deskew/Imaging/ZLib/imzutil.pas create mode 100755 resources/libraries/deskew/MainUnit.pas create mode 100755 resources/libraries/deskew/Readme.md create mode 100755 resources/libraries/deskew/RotationDetector.pas create mode 100755 resources/libraries/deskew/Scripts/Compile.bat create mode 100755 resources/libraries/deskew/Scripts/build_gui_release_macos.sh create mode 100755 resources/libraries/deskew/Scripts/compile-arm-linux.sh create mode 100755 resources/libraries/deskew/Scripts/compile.sh create mode 100755 resources/libraries/deskew/Scripts/create_cli_release_zip.sh create mode 100755 resources/libraries/deskew/Scripts/create_gui_release_zip.sh create mode 100755 resources/libraries/deskew/Scripts/get_gui_versions.sh create mode 100755 resources/libraries/deskew/deskew.dpr create mode 100755 resources/libraries/deskew/deskew.dproj create mode 100755 resources/libraries/deskew/deskew.lpi create mode 100755 resources/libraries/deskew/deskew.lpr create mode 160000 resources/python/dewarp create mode 160000 resources/python/unproject diff --git a/README.md b/README.md index 42badbd..df95676 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,26 @@ php artisan migrate php artisan queue:deploy-supervisor supervisorctl start all +# Tesseract OCR +add-apt-repository ppa:alex-p/tesseract-ocr-devel +apt-get update +apt install tesseract-ocr + +# Unpaper +apt-get install unpaper + +# Deskew +cd DESKEW_INSTALLATION_DIRECTORY +cd Bin +./deskew INPUT OUTPUT + +# Dewarp +pip3 install opencv-python + +# MAT2 (Metadata remover) - Not used at the moment +pip3 install mat2 +apt-get install gir1.2-poppler-0.18 + ``` ## Local Usage diff --git a/app/Ingest/Convertor.php b/app/Ingest/Convertor.php index 1883c75..641aa31 100644 --- a/app/Ingest/Convertor.php +++ b/app/Ingest/Convertor.php @@ -5,6 +5,7 @@ namespace App\Ingest; use Illuminate\Support\Facades\Storage; use Symfony\Component\Process\Exception\ProcessFailedException; use Symfony\Component\Process\Process; +use League\HTMLToMarkdown\HtmlConverter; class Convertor { @@ -22,6 +23,10 @@ class Convertor $this->type = $type; } + /** + * @return mixed + * @throws \Exception + */ public function execute() { if ($this->type === 'txt') { @@ -29,7 +34,9 @@ class Convertor } if ($this->type === 'pdf') { - $this->convertPdfToText(); +// $this->convertPdfToText(); + $this->convertPdfToMD(); +// $this->getHtmlContentsFromPdfWithImages(); return $this->path; } @@ -75,14 +82,13 @@ class Convertor $this->storage->delete($this->path); - $this->path = str_replace($this->type, 'docx', $this->path); + $this->path = str_replace(".$this->type", '.docx', $this->path); } /** * Convert docx file to text * - * - * @return string|void + * @return void */ private function convertDocumentToText() { @@ -109,7 +115,149 @@ class Convertor $this->path = str_replace(['.docx', '.bin'], '.txt', $this->path); } - private function convertPdfToText() + protected function convertPdfToText() + { + $this->prepareForConvertPDF(); + + $images = $this->getImagesFromPDF(); + + $contents = $this->getTextContentsFromPDF(); + + if (!$contents && count($images) === 0) { + throw new \Exception('Could not read from file.'); + } + + // Handle images and image contents. + if (count($images) > 0) { + foreach ($images as $image) { + try { + $ocr = new OCR($this->storage->path($image)); + + $imageContents = $ocr->execute(); + + $contents = $contents . "\n" . $imageContents; + } catch (\Exception $exception) { + \Illuminate\Support\Facades\Log::info('something wrong: ' . $exception->getMessage()); + } + } + + $dir = str_replace('.pdf', '', $this->path); + + $this->storage->deleteDirectory($dir); + } + + $this->storage->delete($this->path); + + $this->path = str_replace('.pdf', '.txt', $this->path); + + $this->storage->put($this->path, $contents); + } + + protected function convertPdfToMD() + { +// $this->prepareForConvertPDF(); + + $result = $this->getContentsFromPdf(); + + if ( ! $result['has_images'] && ! $result['has_text']) { + throw new \Exception('Cannot get pdf file contents.'); + } + + if ($result['has_text']) { + if ($result['has_images']) { + // Both text and images. + throw new \Exception('Not supported for now.'); + } + + // Delete directory because the contents are in the '$result' variable. + $this->storage->deleteDirectory($this->path); + + $mdContents = ''; + + foreach ($result['htmls'] as $html) { + $converter = new HtmlConverter(); + $converter->getConfig()->setOption('strip_tags', true); + + $contents = $converter->convert($html); + + $mdContents = $mdContents . $contents; + } + + $this->path = "$this->path.md"; + + $this->storage->put($this->path, $mdContents); + + return; + } + + // Only contains images. + $imagesContent = ''; + $files = $this->storage->allFiles($this->path); + + foreach ($files as $file) { + // Only get the image files from the directory, it may contain some empty html files too. + if (in_array(pathinfo($file, PATHINFO_EXTENSION), ['jpg', 'png'])) { + $ocr = new OCR($this->storage->path($file)); + + $imagesContent = $imagesContent . $ocr->execute(); + } + } + + // We are done with the images processing, delete directory. + $this->storage->deleteDirectory($this->path); + + $this->path = "$this->path.md"; + + $this->storage->put($this->path, $imagesContent); + } + + private function convertToHtml() + { + (new Process(['export HOME=' . env('USER_HOME_PATH')]))->run(); + + $process = new Process([ + 'soffice', + '--headless', + '--convert-to', + 'html:HTML:EmbedImages', + $this->storage->path($this->path), + '--outdir', + $this->storage->path('contracts') + ]); + + $process->run(); + + if (!$process->isSuccessful()) { + throw new ProcessFailedException($process); + } + + $this->storage->delete($this->path); + + $this->path = str_replace(".$this->type", '.html', $this->path); + } + + private function convertToXML() + { + //Convert the file to xml using pdftohtml to xml and run a python scrypt to fix the paragraphs + $process = new Process([ + 'pdftohtml', + '-xml', + '-i', + $this->storage->path($this->path) + ]); + + $process->run(); + + if (!$process->isSuccessful()) { + throw new ProcessFailedException($process); + } + + $this->storage->delete($this->path); + + $this->path = str_replace(".$this->type", '.xml', $this->path); + } + + protected function prepareForConvertPDF() { (new Process(['export HOME=' . env('USER_HOME_PATH')]))->run(); @@ -124,6 +272,34 @@ class Convertor if (!$process->isSuccessful()) { throw new ProcessFailedException($process); } + } + + protected function getImagesFromPDF() + { + $dir = str_replace('.pdf', '', $this->path); + + $this->storage->makeDirectory($dir); + + $process = new Process([ + 'pdfimages', + '-p', + $this->storage->path($this->path), + '-tiff', + $this->storage->path("$dir/ocr") + ]); + + $process->run(); + + if (!$process->isSuccessful()) { + throw new ProcessFailedException($process); + } + + return $this->storage->allFiles($dir); + } + + protected function getTextContentsFromPDF() + { + $outputPath = $this->storage->path(str_replace('.pdf', '.txt', $this->path)); $process = new Process([ 'python3', @@ -131,7 +307,7 @@ class Convertor '-i', $this->storage->path($this->path), '-o', - $this->storage->path(str_replace('.pdf', '.txt', $this->path)) + $outputPath ]); $process->run(); @@ -140,23 +316,21 @@ class Convertor throw new ProcessFailedException($process); } - $this->storage->delete($this->path); - - $this->path = str_replace('pdf', 'txt', $this->path); + return file_get_contents($outputPath); } - private function convertToHtml() + protected function getHtmlContentsFromPdfWithImages() { - (new Process(['export HOME=' . env('USER_HOME_PATH')]))->run(); + $dirName = str_replace('.pdf', '', $this->path); + $this->storage->makeDirectory($dirName); + + $outputPath = $this->storage->path("$dirName/html"); $process = new Process([ - 'soffice', - '--headless', - '--convert-to', - 'html:HTML:EmbedImages', + 'pdftohtml', + '-noframes', $this->storage->path($this->path), - '--outdir', - $this->storage->path('contracts') + $outputPath ]); $process->run(); @@ -167,17 +341,49 @@ class Convertor $this->storage->delete($this->path); - $this->path = str_replace($this->type, 'html', $this->path); + $this->path = $dirName; + + $converter = new HtmlConverter(); + $converter->getConfig()->setOption('strip_tags', true); + + $files = $this->storage->allFiles($this->path); + + $htmlFileIndex = null; + + foreach ($files as $index => $file) { + // if (pathinfo($file, PATHINFO_BASENAME) === 'html-html.html') { + // if (pathinfo($file, PATHINFO_EXTENSION) === 'html') { + if (pathinfo($file, PATHINFO_BASENAME) === 'html.html') { + $htmlFileIndex = $index; + + break; + } + } + + $htmlContents = $this->storage->get($files[$htmlFileIndex]); + $contents = $converter->convert($htmlContents); + +// $this->storage->deleteDirectory($this->path); + + $this->path = "$this->path.md"; + + $this->storage->put($this->path, $contents); + + dd(3); } - private function convertToXML() + protected function getContentsFromPdf() { - //Convert the file to xml using pdftohtml to xml and run a python scrypt to fix the paragraphs + $dirName = str_replace('.pdf', '', $this->path); + $this->storage->makeDirectory($dirName); + + $outputPath = $this->storage->path("$dirName/html"); + $process = new Process([ 'pdftohtml', '-xml', - '-i', - $this->storage->path($this->path) + $this->storage->path($this->path), + $outputPath ]); $process->run(); @@ -188,6 +394,110 @@ class Convertor $this->storage->delete($this->path); - $this->path = str_replace($this->type, 'xml', $this->path); + $this->path = $dirName; + + $contents = $this->storage->get("$this->path/html.xml"); + + $xml = simplexml_load_string($contents); + + $fonts = []; + + foreach ($xml->page as $page) { + foreach ($page as $p) { + if ($p->getName() === 'fontspec') { + $fonts[(int) $p['id']]['family'] = (string) $p['family']; + $fonts[(int) $p['id']]['size'] = (string) $p['size']; + $fonts[(int) $p['id']]['color'] = (string) $p['color']; + } + } + } + + $htmls = []; + $hasImages = false; + $hasText = false; + + try { + foreach ($xml->page as $page) { + $html = ''; + + $previousP = null; + + foreach ($page as $p) { + if ($p->getName() == 'image') { + $html = $html . ''; + + $hasImages = true; + } + + if ($p->getName() == 'text') { + $id = (int) $p['font']; + $font_size = $fonts[$id]['size']; + $font_color = $fonts[$id]['color']; + $font_family = $fonts[$id]['family']; + + $style = ''; + $style = $style . 'position: absolute;'; + $style = $style . "color: $font_color;"; + $style = $style . "font-family: $font_family;"; + $style = $style . "font-weight: 900;"; + $style = $style . "width: " . $p['width'] . "px;"; + $style = $style . "height: " . $p['height'] . "px;"; + $style = $style . "top: " . $p['top'] . "px;"; + $style = $style . "left: " . $p['left'] . "px;"; + +// $style = $style . "font-size: $font_size" . "px;"; + + if ($p->i) { + $content = '' . $p->i . ''; + } else if ($p->b) { + $content = '' . $p->b . ''; + } else { + $content = $p; + } + + // @TODO Must chain paragraphs if top are almost same. + + $tag = $this->getTag($p, $previousP, $font_size); + + $html = $html . '<' . $tag . ' style="' . $style . '">' . $content . ''; + + $hasText = true; + } + + $previousP = $p; + } + + $htmls[] = '' . $html . ''; + } + } catch (\Exception $exception) { + \Illuminate\Support\Facades\Log::info($exception->getTraceAsString()); + } + + return [ + 'has_images' => $hasImages, + 'has_text' => $hasText, + 'htmls' => $htmls, + ]; + } + + protected function getTag($p, $previousP, $size) + { + if ($size > 24) { + return 'h1'; + } + + if ($size > 18) { + return 'h2'; + } + + if ($size > 16) { + return 'h3'; + } + + if ($previousP && $p['top'] - $previousP['top'] <= 5) { + return 'span'; + } + + return 'p'; } } diff --git a/app/Ingest/DocumentHandler.php b/app/Ingest/DocumentHandler.php index cf9a8db..25b4963 100644 --- a/app/Ingest/DocumentHandler.php +++ b/app/Ingest/DocumentHandler.php @@ -13,6 +13,7 @@ class DocumentHandler const DOCX_MIME_TYPE = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; const DOC_MIME_TYPE = 'application/msword'; const RTF_MIME_TYPE = 'text/rtf'; + const APPLICATION_RTF_MIME_TYPE = 'application/rtf'; const ODT_MIME_TYPE = 'application/vnd.oasis.opendocument.text'; const PDF_MIME_TYPE = 'application/pdf'; const PDF_WPS_MIME_TYPE = 'application/wps-office.pdf'; @@ -26,6 +27,7 @@ class DocumentHandler self::DOCX_WPS_TYPE => 'docx', self::DOC_MIME_TYPE => 'doc', self::RTF_MIME_TYPE => 'rtf', + self::APPLICATION_RTF_MIME_TYPE => 'rtf', self::ODT_MIME_TYPE => 'odt', self::PDF_MIME_TYPE => 'pdf', self::PDF_WPS_MIME_TYPE => 'pdf', diff --git a/app/Ingest/OCR.php b/app/Ingest/OCR.php new file mode 100644 index 0000000..ecb8ad9 --- /dev/null +++ b/app/Ingest/OCR.php @@ -0,0 +1,95 @@ +path = $path; + } + + public function execute() + { + $this->preProcess(); + + $text = $this->extractText(); + + return $text; + } + + protected function preProcess() + { + $this->applyDewarp(); + $this->applyDeskew(); + } + + protected function applyDewarp() + { + $executablePath = resource_path('python/dewarp/page_dewarp.py'); + + $process = new Process([ + 'python3', + $executablePath, + $this->path, + ]); + + $process->run(); + + if (!$process->isSuccessful()) { + throw new ProcessFailedException($process); + } + + $fileName = pathinfo($this->path, PATHINFO_FILENAME); + $filePath = $fileName . '_thresh.png'; + $directory = pathinfo($this->path, PATHINFO_DIRNAME); + $newPath = "$directory/$filePath"; + + $moved = File::move(base_path($filePath), $newPath); + + if ( ! $moved) { + throw new \Exception('Something went wrong while moving file.'); + } + + $this->path = $newPath; + } + + protected function applyDeskew() + { + $executablePath = resource_path('libraries/deskew/Bin/deskew'); + $newPath = pathinfo($this->path, PATHINFO_DIRNAME) . '/deskewed.png'; + + $process = new Process([ + $executablePath, + $this->path, + '-o', + $newPath + ]); + + $process->run(); + + if ( ! $process->isSuccessful()) { + throw new ProcessFailedException($process); + } + + $this->path = $newPath; + } + + protected function extractText() + { + $t = new TesseractOCR($this->path); + +// $t->oem(4); + + $t->psm(4); + + return $t->run(); + } +} diff --git a/app/Jobs/IngestDocuments.php b/app/Jobs/IngestDocuments.php index 010b5d9..2af003a 100644 --- a/app/Jobs/IngestDocuments.php +++ b/app/Jobs/IngestDocuments.php @@ -52,6 +52,7 @@ class IngestDocuments implements ShouldQueue * Create a new job instance. * * @param string $path + * @param $type */ public function __construct(string $path, $type) { @@ -73,27 +74,37 @@ class IngestDocuments implements ShouldQueue public function handle() { $convertor = new Convertor($this->path, $this->type); - $this->path = $convertor->execute(); - $content = $this->getContent(); - - if ( ! $content) { + try { + $this->path = $convertor->execute(); + } catch (\Exception $exception) { $this->failed(); - - return; } - $content = $this->convertToUTF8($content); + // @TODO Replace later, the convertor will create the .md file. + if ($this->type !== 'pdf') { + $content = $this->getContent(); - try { - $filePath = $this->storeContent($content); + if ( ! $content) { + $this->failed(); - SendToCore::dispatch($filePath); - } catch (\Exception $e) { - Log::error('Error writing in to the file: ' . $e->getMessage()); + return; + } + + $content = $this->convertToUTF8($content); + + try { + $filePath = $this->storeContent($content); + } catch (\Exception $e) { + Log::error('Error writing in to the file: ' . $e->getMessage()); // report($e); + } + } else { + $filePath = $this->path; } + + SendToCore::dispatch($filePath); } public function failed() @@ -109,7 +120,7 @@ class IngestDocuments implements ShouldQueue // $this->storage->delete($this->path); // } - SendToCore::dispatch($this->path); + SendToCore::dispatch($this->path, true); } protected function getContent() diff --git a/app/Jobs/SendToCore.php b/app/Jobs/SendToCore.php index 903c5b5..e90d9fc 100644 --- a/app/Jobs/SendToCore.php +++ b/app/Jobs/SendToCore.php @@ -22,6 +22,8 @@ class SendToCore implements ShouldQueue private $id; + protected $hasFailed; + /** * @var \Illuminate\Contracts\Filesystem\Filesystem */ @@ -30,13 +32,15 @@ class SendToCore implements ShouldQueue /** * Create a new job instance. * - * @param $filePath + * @param null $filePath + * @param bool $hasFailed */ - public function __construct($filePath = null) + public function __construct($filePath = null, $hasFailed = false) { $this->url = env('WEBHOOK_CORE_URL') . '/webhooks'; $this->secret = env('WEBHOOK_CORE_SECRET'); $this->filePath = $filePath; + $this->hasFailed = $hasFailed; $string = str_replace('contracts/', '', $this->filePath); $result = explode('.', $string); @@ -54,7 +58,7 @@ class SendToCore implements ShouldQueue $content = ''; // File exists, send content. - if ($this->filePath) { + if ($this->filePath && ! $this->hasFailed) { $this->storage = Storage::disk('local'); // @TODO Check if the file exists multiple times? @@ -76,6 +80,10 @@ class SendToCore implements ShouldQueue public function failed() { if ($this->filePath) { + if ( ! $this->storage) { + $this->storage = Storage::disk('local'); + } + $this->storage->delete($this->filePath); } } diff --git a/app/Parser/ParseTextArray.php b/app/Parser/ParseTextArray.php index 3052530..46cedfd 100644 --- a/app/Parser/ParseTextArray.php +++ b/app/Parser/ParseTextArray.php @@ -6,7 +6,6 @@ use Illuminate\Support\Facades\Log; class ParseTextArray { - /** * @var array */ @@ -103,7 +102,6 @@ class ParseTextArray */ private $pdf; - /** * ParseTextArray constructor. * @@ -115,18 +113,18 @@ class ParseTextArray $this->pdf = $pdf; } - public function fromFile($filePath) { if (file_exists($filePath)) { $fileContent = file_get_contents($filePath); return $this->buildTheStructure(array_filter(explode(PHP_EOL, $fileContent))); - } else { - Log::error('The given file does not exists!'); } - } + Log::error('The given file does not exists!'); + + return ''; + } /** * Build the child structure and extract relevant data from the text content @@ -148,7 +146,7 @@ class ParseTextArray if (array_key_exists($i, $alreadyHandled)) { continue; } - //extract the content and count the number of the empty spaces from the begining + // Extract the content and count the number of the empty spaces from the beginning. $data[ $i ] = [ 'content' => trim($textAsArray[ $i ]), @@ -161,9 +159,13 @@ class ParseTextArray $data[ $i ][ 'content' ] = trim(ltrim(str_replace($numbering, '', $data[ $i ][ 'content' ]), '.')); } - if ($this->pdf && strpos($textAsArray[ $i ], 'Page') !== false && strpos($textAsArray[ $i ], - 'of') !== false) { + if ( + $this->pdf && + strpos($textAsArray[ $i ], 'Page') !== false && + strpos($textAsArray[ $i ], 'of') !== false + ) { $alreadyHandled[] = $i; + break; } @@ -340,17 +342,14 @@ class ParseTextArray if (strlen($data[ $i ][ 'content' ])) { $response[] = $data[ $i ]; - } - $alreadyHandled[] = $i; - + $alreadyHandled[] = $i; } return $this->recheckClauses($response); } - /** * Recheck missed clauses and assign them to a parent if is the case * @@ -362,22 +361,36 @@ class ParseTextArray { $checkedClauses = []; $alreadyManaged = []; + for ($i = 0; $i < count($clauses); $i++) { if (array_key_exists($i, $alreadyManaged)) { continue; } + $data [ $i ] = $clauses[ $i ]; $j = $i + 1; - if (isset($clauses[ $j ]) && $clauses[ $j ][ 'content' ] && $this->hasNumbering($data[ $i ]) && ((! $this->hasNumbering($clauses[ $j ])) || (($this->hasNumbering($clauses[ $j ]) && is_numeric($clauses[ $j ][ 'numbering' ]) && count(array_filter(explode('.', - $clauses[ $j ][ 'numbering' ]))) > 1 && is_numeric($clauses[ $i ][ 'numbering' ]) && count(array_filter(explode('.', - $clauses[ $i ][ 'numbering' ]))) <= 1)))) { - - + if ( + isset($clauses[ $j ]) && + $clauses[ $j ][ 'content' ] && + $this->hasNumbering($data[ $i ]) && + ( + (! $this->hasNumbering($clauses[ $j ])) || + ( + $this->hasNumbering($clauses[ $j ]) && + is_numeric($clauses[ $j ][ 'numbering' ]) && + count(array_filter(explode('.', $clauses[ $j ][ 'numbering' ]))) > 1 && + is_numeric($clauses[ $i ][ 'numbering' ]) && + count(array_filter(explode('.', $clauses[ $i ][ 'numbering' ]))) <= 1 + ) + ) + ) { for ($j; $j < count($clauses); $j++) { - - if (isset($clauses[ $j ][ 'numbering' ]) && is_numeric($clauses[ $j ][ 'numbering' ]) && count(array_filter(explode('.', - $clauses[ $j ][ 'numbering' ]))) == 1) { + if ( + isset($clauses[ $j ][ 'numbering' ]) && + is_numeric($clauses[ $j ][ 'numbering' ]) && + count(array_filter(explode('.', $clauses[ $j ][ 'numbering' ]))) == 1 + ) { break; } @@ -385,7 +398,9 @@ class ParseTextArray $alreadyManaged[] = $j; } } + $alreadyManaged[] = $i; + if ($data[ $i ][ 'content' ]) { $checkedClauses[] = $data[ $i ]; } @@ -394,7 +409,6 @@ class ParseTextArray return $checkedClauses; } - /** * Build the child structure based on the spaces before the text * @@ -561,7 +575,6 @@ class ParseTextArray return $parent; } - /** * Check if paragraph is a list * @@ -571,14 +584,9 @@ class ParseTextArray */ private function paragraphIsList($paragraph) { - if (substr(trim($paragraph[ 'content' ]), -1) == ':') { - return true; - } - - return false; + return substr(trim($paragraph[ 'content' ]), -1) === ':'; } - /** * Check if last child from the paragraph is a list * @@ -598,7 +606,6 @@ class ParseTextArray return false; } - private function getLastChildForParagraph($paragraph) { if ($this->hasChild($paragraph)) { @@ -610,7 +617,6 @@ class ParseTextArray return $paragraph; } - /** * Check if a paragraph has any child * @@ -627,7 +633,6 @@ class ParseTextArray return false; } - /** * Extract numbering from a given paragraph * @@ -674,7 +679,6 @@ class ParseTextArray return false; } - /** * Check if a paragraph is between clauses * @@ -713,7 +717,6 @@ class ParseTextArray return false; } - private function getLastChildFromParagraph($paragraph) { if (isset($paragraph[ 'children' ])) { @@ -723,7 +726,6 @@ class ParseTextArray return $paragraph; } - private function appendToLastChildFromParagraph($paragraph, $append) { if (isset($paragraph[ 'children' ])) { @@ -735,7 +737,6 @@ class ParseTextArray return $paragraph; } - /** * Check if a paragraph has numbering * @@ -752,7 +753,6 @@ class ParseTextArray return false; } - /** * Uppercase all values in the array * @@ -769,6 +769,5 @@ class ParseTextArray //remove unwanted chars return strtoupper(str_replace(['.'], '', $value)); } - } diff --git a/composer.json b/composer.json index ec190b9..4016c9d 100644 --- a/composer.json +++ b/composer.json @@ -13,10 +13,12 @@ "fideloper/proxy": "^4.0", "laravel/framework": "^6.2", "laravel/tinker": "^2.0", + "league/html-to-markdown": "^5.0", "phpoffice/phpword": "^0.17.0", "predis/predis": "^1.1", "spatie/laravel-webhook-server": "^1.13", - "spatie/pdf-to-text": "^1.3" + "spatie/pdf-to-text": "^1.3", + "thiagoalessio/tesseract_ocr": "^2.11" }, "require-dev": { "facade/ignition": "^1.4", diff --git a/composer.lock b/composer.lock index 9fd9e2b..6f0e730 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "fbc2d12145e4b8457c2af1363e8c18b6", + "content-hash": "c8ed2965a1b6b6e180ee0bf935ffbb26", "packages": [ { "name": "cebe/markdown", @@ -958,6 +958,95 @@ ], "time": "2020-05-18T15:13:39+00:00" }, + { + "name": "league/html-to-markdown", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/html-to-markdown.git", + "reference": "c4dbebbebe0fe454b6b38e6c683a977615bd7dc2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/c4dbebbebe0fe454b6b38e6c683a977615bd7dc2", + "reference": "c4dbebbebe0fe454b6b38e6c683a977615bd7dc2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xml": "*", + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "mikehaertl/php-shellcommand": "^1.1.0", + "phpstan/phpstan": "^0.12.82", + "phpunit/phpunit": "^8.5 || ^9.2", + "scrutinizer/ocular": "^1.6", + "unleashedtech/php-coding-standard": "^2.7", + "vimeo/psalm": "^4.6" + }, + "bin": [ + "bin/html-to-markdown" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "psr-4": { + "League\\HTMLToMarkdown\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + }, + { + "name": "Nick Cernis", + "email": "nick@cern.is", + "homepage": "http://modernnerd.net", + "role": "Original Author" + } + ], + "description": "An HTML-to-markdown conversion helper for PHP", + "homepage": "https://github.com/thephpleague/html-to-markdown", + "keywords": [ + "html", + "markdown" + ], + "support": { + "issues": "https://github.com/thephpleague/html-to-markdown/issues", + "source": "https://github.com/thephpleague/html-to-markdown/tree/5.0.0" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://www.patreon.com/colinodell", + "type": "patreon" + } + ], + "time": "2021-03-29T01:29:08+00:00" + }, { "name": "monolog/monolog", "version": "2.1.0", @@ -3627,6 +3716,55 @@ ], "time": "2020-05-30T20:06:45+00:00" }, + { + "name": "thiagoalessio/tesseract_ocr", + "version": "2.11.2", + "source": { + "type": "git", + "url": "https://github.com/thiagoalessio/tesseract-ocr-for-php.git", + "reference": "502c62abc1235189921fcdfae83f78926eb15246" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thiagoalessio/tesseract-ocr-for-php/zipball/502c62abc1235189921fcdfae83f78926eb15246", + "reference": "502c62abc1235189921fcdfae83f78926eb15246", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpunit/php-code-coverage": "^2.2.4 || ^9.0.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "thiagoalessio\\TesseractOCR\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "thiagoalessio", + "email": "thiagoalessio@me.com" + } + ], + "description": "A wrapper to work with Tesseract OCR inside PHP.", + "keywords": [ + "OCR", + "Tesseract", + "text recognition" + ], + "support": { + "irc": "irc://irc.freenode.net/tesseract-ocr-for-php", + "issues": "https://github.com/thiagoalessio/tesseract-ocr-for-php/issues", + "source": "https://github.com/thiagoalessio/tesseract-ocr-for-php" + }, + "time": "2021-04-09T21:05:43+00:00" + }, { "name": "tijsverkoyen/css-to-inline-styles", "version": "2.2.2", diff --git a/resources/libraries/deskew/Bin/deskew b/resources/libraries/deskew/Bin/deskew new file mode 100755 index 0000000000000000000000000000000000000000..11315d876f8d826f778b1bced9ab2cc8cf095676 GIT binary patch literal 758232 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

_KP|8R+Blec=f?AY<% ziBp+JSs~JOkGqNR^JGLs03&+Q>JO1C$3h{ih#s;l7e(S%T?RD%d5he1v-i1)aznVy zH+!G!eW3Fd=A{!?J36s`-FAmoVy)Os7=9A<^W(WwXo0QN{mU20ngT%&A#jZ}8P)pd zEy?(ow<$D1!jUE;;yhs$!DH%@$8 Dz&TA)^hzqf!{>$VT`P)I-0$tHkw_jURH_k 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 Z3h<$e-QQo7o=kQCq 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

    cGY6zea&4Ssee-U)n)wm?5C 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�U@*jV)_^T9}a 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

    =@sz0DkqId}*wp?_|fe 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�mG$uX9eD-0U3Cv}4%!Nh6Rh(UL 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�s~vEubz15YfmncSL@uOd0%0kxrI9|tsXSxHSo+lkA0@U?%~wfEasutyk*69!SPn~%RkTr zlehBM7_FV~lZ?;QR@o%cxs4b!Ue_g??Shh*tj_9 zZZYYI>w0LnlfzDi)}h7iN7(kzzG3aw3wJX=zgU&sg zn-X005w^w$|;01f- zj{n0!vu3;jyFuk^J+6EJ(RoX?=Q>L>>gQ(qS-KVv0ybu@)TlrEC}%4U8;-k>u-np zF41={=j&O%7Cp1pdXKRuEPc`B?IAqT$7pmgGg8jAv_m{ASSfE?`PYJDZFv)B)O7e*!*sdIbjX=6X7Cwe=SShW z$pGF=zH9vm{CQSes&KZO%AZPjn(7_3A7r!1W^nVQ?~y(D<+78@_eqay&z0GO1^%+| zWtZZl+f(cNSAq9l#+;pzbWNSu#yVFKUjQPi?X%eZjw@fl>XP(s_`V{Y*Y$?tjAbKg zyIJYcrN#$)3i-_44ao(%tM_at`}7}wtuEieKGf4F`;2;1*PBdT^}IiIn<=kV9%DX-+FEhWm1UV3v*GQhZ)Gx##bASFn;Q!C6&fXS$!=kwW53l^6fEIgs zm#>T@efdS*@?8JX31d8hVR)*IV~GShtX z%$?Q#9gI)34@EH8Ki3xHymaYl)k8A(Zt!=aBO7;Z`DEU;4pHAUhq9mkIgj=f!FO2C zB5`htd+d?DPb)sPx*s3R9`05d0rwW*LMO-(#sJ8bF7Kh;vipYKse_9=mo8U%zW^t5 zZ9P_zE|Ja^zX;vxo9rr&dIYvXfG^3|upXiZ)kA3=Xh-u!Auj4PK2uuoEPdiStNYuP zp7%U-DmsayyhFdt3zAEJAIK%yO0t<`Ge|ZGCw2IT_A2CD>gC{BzKx=d*SwPA>%2$% ze^9_@%(Hwf$#kw?HUV{Ni|q(x&x6>F`X=8Cb0d5573t--aFdt!tx0b?yDt6YAKyMX z-?5^w?X+b2$un{(wf(7n<}FGt@RoVG(u3HB^7%Z-yo7voZU<)_oBH4z#-`Ud?JK_2 z!7pQ;N)$pOm_~u9-*9PnOIR4~duJ{58iA@{f2*xM?CTT;n7=MdQM` z&PK>>8Gp^e2m7e%EuUx&$Y*A1o?COA!QLvxNq56U#ETx;PsrcnKb>#0ZI~vU!@KxT zV@|b0c9inWNpCFcY*?)qo$dyA;#JzU8PZ*?vb%cyM%Jlx0AreSW9iIx)@>0#jdL*t z8-8nRgq}XEw7I z=eWoN>a=C}JyX=ULgxq4rad!<(rGQQ#zx$4WY;8ho!RU>{pMz8;jCsSIh*xHZ*6wk z=QKN8&uw-#zk~R9Ham~<7r$!`7uh(Oa|GJFo*StPZOW|x9(@BHsV{@Rxtw{~hl|)` zzHCs8ViG;(p(#AU*Pvx&+&Kb zkcHf()#x+~!Fd~Ti>Q-yp=UeY=l?q*7}ADX$x+01pOnWy~oBs6Q{gR@XHR+b*x zPyLg$Np-+q(pHx*_8oyM`rqIRoH?xjjyaDQT&XQh;8J_Z9A`sOdu+{G0&wP3!Pj*b zUmpTrVVfFk-B(1~S!d(^DiX)Mx}i%CKktJRgLMV;A9^S6pZEKN1F@s2JNZoA>1);9 zP2CaHUF#sK)P26K`&rce{nQJ-9HKGgF03vZ$`a3?x*9PlPObE=npnk~Dv3Y~e- z8F#6l)9e(@_~;*Gn?xLdbu5%l)4S5dkfC%0Ly(D@K-who@8}EfaD{1ymA~bsmG+R?Q)xgWq z>ZYj{Cyn4_G~G18>Za!AyzlWBzSIu2|KyZYIuU!2A;)UH=a1EXewVmxA98O?RF2F^ zjFcn4{O!@nk)v%_i8FVkbDxaqo+j!_y<*sxj0?D@o(RD{Dvmdba{9EezN4Mtv3FvRhF3z+D&w&h^g$}8yM{3Z+tEtll zTc>5zX*8OUy&^r5^f=2OT;;?cQ}Oj^%ncr8$8dgUzrJbAq_%{5T>RkGGp@rAqVJN0 zd(2%MoR9Af!%K$n7~9YWbks!tjn>^O{#f!Ow=_2D(|L$3ggWE) z40ui7CveY<&dRT|@niB|H9COaj@E{}mosxDyY8rhN1c~!@W!5hoi*?*{G<1`^$gqk zIzv%7_(9y_)oErgF>&}ibY{A~E4@j_*g*UwJcM*6+0K~)fxIMbDBD@v$Jt<lzz;QlY+Onn0W+5{6C?3+gah^OwJuCvwxI+k5NhdSx}CGjHo^%uQ7iY}-n(}YvL zAC2z+pvCEB;54i&pCOK~LU-+mzFcFs=lk?5nlfo3G!?WlR}#*Gdss3h z(T{9D=CceAB=@xLUcB7Ovv43ktHv^#uMKgn`)88KsyT$Z6GZ1>Nrq`mCK)z06Uenz zo+Z~(gi#%)xii@(Cp$6ZE;{as9Pn-}pY)dEki5HyJusoX)3q`8rH?j6H;5~i6?binf;<;7D zc-M_Zoz8N>UCMz@+(JJ~-)-S+(BPZLH|b8LYv)<_4(NF{&l*ST zInDk)$sTKC;0v~Ko@|eKMYkt|i-_|#iJz&oitm{h*g2d58Tx~B=d(Zd0<;<%#GQ9H zJLyZSqk0A3--`!~w!3CO(EEMl$2oI>n*a~Yd!8eGuX#L=y?t6+ zrDyH0R+_lEV{=Pcydb@Gq#eBl|L_*$$8(Lpcs1{(!f-`pMCL>Uc?iC;NUbAdhm0Svd8yOzCal&$w!(1l>Ssh^tslyB|@ zb8@C05(3kco%f<=u&Kc%=QqI(p`gNkyR8qXsgz2@nReTbrKb7c<^1Q{>&;YI1~;km!+dOIpJ z&bM`Jtwl5F6!kZL(xc+XIDXNn+*jY}Ze_;{_%SJdgzwVFiQhy05%6PugdXlc2t8zi zd$x3!Fl(ZUlCRTwmd)jQ8N=V^8$PDZ_;QS$mFY2ZctkqY0edTko^gP)7XH;bp1*)+ zO&$wVcAVBNDlUzU5tgTOm>6rAwcgVmmH#U2Kj0agrRTh&%3er0YDZ{SMf>O9SidBl zqViw0y%{O*|5h7~Otf@eRWH648_2?*4Nph$cQg2tOk9L7P2)S^QhVfue}{914E)`psNdxhOR=mHOi|=SKxCx_+*XJYv&#Drx%Q!gx@3R%F@9Rvb~DzY?7@7 z-3_mg8K2zzE$gO5SC1R{5zv*9@z@Y%9LU*CBWMe{68(vGuqn&AR}Pzde;BK4E$EiO zj?Nf6InTU0mkC$tcq{~FWYvTd!DElX^@ zE}&jRwtuu9)o}DW)8ZAj9;Z?dcf4be8u&0{5cL(jF}w~6zKD)FtKj6#wk{5J`96L) z^tf5?DPQOce4$_Cud@>V*D1qyxP^Y1`k%AzE`yH;zM3;jJ{azN;{Fxg1(xJLo97f^ zEB`dlQ#<7Y`f_Wt+zw~`_~ zcFdlbg}dL`;HPHMCidl~zqkA)bnKAemz66&w zW;67xI;vm6^PUwM`i;1-uN;SS8A*$NoY7=(qBAt+7f0l?@cv!27uM%;_A3#ud;45x zW${lZXl-$EIrO^{#I6p%b<-y^f$QdSMd2&NXRrD>m+KGHvB$3%Jm<$wl*Zd^?A4E_|f2McYGuM80>?&m+o&&xFg|j=aAl zP5Ysbn6swnr16U(+cd9iU_PWy^7A{Pw9GHr_d}c%5@eIWD}@CegI$ z>=lEr%l(3r->}5FWYZbgIwp?3(S1f5XR+UO#w);PO$B!t8o%jTq&>US$)8GiTKG-% zS6f5o2|CPiz(7&_yfb_6Ftm{q?>EgRl*hB25r?kZsvr}qYo74 zVPmjvOtJypvB%(U7Puo`xNCBj7ahj5!kxZRPiR$gE1-Gi+RqVe&V)u60GGPzJ37U{ z5^cDiS9CgpJb7qAWf|F~Z_cqr&P!@D@4#_Vr$A2uAF95B=jJmd_WoM_&y3`MaA)Wc znJk`{Tu`XGMDweyj+Yq06GO6(e&@U=mYco5uTN_O)gEvx9VU1R6(_l;Ij5%&9ez$S zUOW?rXIf2qJr^g#k5i-zB@3lLJ-%tqE;?5e-g)lk&<{P(c$T@J%$LheUDyQqF9SE< zm!=J~;h{_6hcmq3K1h`Z&)4CXuIGG=#7U&nx`lA9g2%J?shu#--W086&>lX`Wt~Iw%(s~JpK=2 zL%qm9il?R8vjA@9%8%lqzdONG4m@E4X>6MWXDRU2+Vf@R!~(lXe)lN;jy*Bo_tTR{ z;*UA8`Y%+$UwhZua*@4L!rk*`57j8R18=Q4;XS||vdNrHKZo|!dx2d0g2A6T3mID2 zZ15uAONdX|T=Eln>0D`PM}Q~UC!YgX;zRn`;FWZVFx`Skx=%%LdLQk(2%KI-z7uQE z#8lg^)*9{7cuebD61Hvi@SpCNhaZYszo+{-CCe|c?Q5hR!Pro9FPHb7%DZ^awdeXV zmgT(cpe^-kGq4!XlvY^UIG1*4o+hLtgBP1-u}w4EwiTT@gco3$v72CBWYf)v;N(*l zCw1fz-F=*Q(S_ziHP(vKUh3QyXEy(yZzr9p{1@Ip+e)3q)B5%nzNuaTPX|7^^N6d<75mmx zXRWml&w&Zg;n2QMt;d%x(%K5G@%$1pOM6#)YNe+SgL7|}^&htB-L19ZBS#GU;}zOD zS)FTl;kMfO!g}cp(r7;UwXbJ?sl!+{Dz~)eN^(p01WYAdNGSSKC>{*Xk~xwG1wTf; zBi~MD^@B==PgQmSFTKuq{sn6_JMDIm;Dy;8)PEg+m z2~}V1QB-{uKbpLa)^{UmRNrVl#ZxDF`Myt4SJA~oCk3?gPlPUY(LPoO|BLoZ%ZAjx zw#?A;N@y;OzX2H^KHpfDeNCVE`~UugUp0LPS-X9bd7s8Vr2UyZM`f_q z6*vWCfQLRx2jka;(70sq0z%bAzKR9ZS!+Wje;4ws^#z)9)pI+~lGl2czc*{2<&Vh{ zC%X&2-=gp7G;yBnp7XI&GG<>iy2q>s)7?+uH|fVDbiMfu=5F%rkK}pH@%ZK$Gq5LO zk7TCut3GC~Ts|z|YTkD)@&!6%e_!xTx?XLN9Min7>Ke$wY~Nn&8>Q#?||Wnc_DRupUzO)63<>xbR6KR4fynrC@-_1gxqg4^(Al3wAuHWw9iw11{%V?XcL|m z*|o_d=m$NowG5)~HLM#DU&}_BU9o2PJ-<50K5A$STuG<9jIl*)c_T|N_o1n?Z@)Z0 zOMF-l%#aU-XYtxf=q<`4qS=MOL7y9%)w9<32(~E)Ijm6Q9S1pScp8~$#)5@Miu$H| zO%>v6Dc7afu?FpK8z1D=+??_)0H*Yia4Y=D2Pzvyw!tooqjup){LUG9@^Ouzr3j8B z16X7Hy2yYflCl9z7a08rsQ?U zEwI&xT2tksTZP-t(LR+UdU_F^t+4@p>C5i@?u(Y5=-;6>llFC?r)Zmb4saWxC+Nt~ z6Le*K=!J)09X&up*^DUBV#4Ao=t1p79To1W2mpm?|AgJZMW`|Xue@#Zw* zwANboMNckR4~Z?$S#!hj=Mt~Atx4>fCA$*lOdwD8N(LQZZJ>*R>1Hz}o#imqoU^vl z$m+#>H*3~`?~+Dzs5{zQ)Xqig(k^4$1?HGb0* zYy7#iCCU8oF{!7^6TGL(E@e*SIYyb-=zc2U3?|!7*yqkj54s6&Fq3%8U^3CNPjT}T z+xBJJ?i)<~;ibXz65Ebpkk3A?TCm)~2 znLr+gG_Kl49(UR@zm8{T`ZAw=hkj}XBkqX^v?#D2mX$8!H0gicP8n-PTHFE3gu&XK8A1t;RM2o zgcAwR1`kd2cXNS$CoVaAx!=NbY|e5&Id{3AI>!O;vj$T$QiDyTKZU!wn{Ak|VGH3j z`XdgUdh#|{IE^FVxZpi);KXgddK)&_u#s>wa2)cxqCxW430FKP!Bw5Xm4(xm8q~O> z1AEf7Va$egggQscadPhr|DxNec8&9=e|edi`<;y)n)Vngz~i%#D|3v0FW~*aw}5|> zGoxn#OXKG`q>*g69a@|3T~g|0j=@ET+;Hrnfw!N)y|tv#cYHW)1-@T=a`63&d_Rlt zGx59W`-k~1+fL8;56$>oV>88Ru8scg)BM{s&T75vos2^@p7-$AQqNaDdT5}P_!ju= zx4>1NKYApPL$diL=L{_5;Gb~6{l|V1++{DgEf(&xjCTe12k5#G?rz}fTvoyTrQl}3 z2e7ZC4O*9K#&*b`PzKfF8`%SrO;^5Ib7)WXhcark`pp}qei>W8>5=+ zQ{B?EQR{{EEZoeo&klQ*y4j(=oO2F?@tOE=sL?4BFlyf1z;m%?YBT1*UD%8Kj%Sw@GtS>!y%ylJ4}>d~y=q zRD3d+Q#NPu7o>ympQpB#-}=;Wd{g?&ateK}Ia0P#(s3p1|vws9|KU}t>qBZme$u`|SjhoF^a&c6=RT}nFnRYd3akxq9QDBa7XTg&)d zG%25~?pBy;)7?cn&!*c$I_=HaJFmr=iOzrix@PC1^IDwvl4j>l!kL6!>Rb9EmHlyf zQsKwtV>3T4C($#Ld5)uJlIR(y^T%bE_?Y4r-{9BroV@x5KYq;({!;SZa;#U}koAhP zFG9OYXW}HCC(Ci}WIKkio^S$T1K~u%MncgbeLJAE#tuQ|OCQzA4v{XvCaA+ESj<_Z zFa9{Nuc9(tc8K!EftPVUK^f=}=ZBO*UcyGgI>My(D5iIT$6eTvgy_3EJ)`fq$2G89 zpaIdx4eJjL%q<`X(~K9qK;EPZE7&)_qCAyzKiWB0LcFJXq%D1^9-PlqihFr~L28+? zf#$#m$-;W_aTj*_hvg}R^@PU}HV__9*hrX!=bO?`p^Kj?C$djjUHnwJ-s(H*M?bSXr@aKBM1An!bXGbu>MT|B(w!MG$1ajZD=YX~G0)D~AhMtUf zGN+N^{PygZ!R6)tI{2@hcRl|#JSRV}@webN17D9JpC3cNK8BusEakDT6S=FgLz;Ku zf1)+t!mHLZ0RMi{eLa~YT}Jy_o-I%Eo-H4ndbS+HZk)`si`^K*ZgjeMR=nbt+(`fN zy!1xjz5YgjDKKw6-z)a)m{yc*Rhcehl1)xyvEGrK%J$*!*iHxzgY%Lk*WokHVsjFj z&vQI%RO~L%F1!foyp=w(ez zuk)yXhU#bIT|$@dF~S(3XTIG+o)!3zHOCRm4K;Kz9xOchg4=af$t@IK_z(OC@(pPY z&Ox@u!L8Cdg<#%QxC-+oN6Cvk8v33p0J{s=g+(Q|D}b$g2!y9^>s(@N*C-!#+OY++ zVrXwRw5q;4pFWb!6VMrRo^_Y(z&~baY!0-(W1@6fM^Sf`y4WCY$}7(C-~oJQcbvSe zxPvpM8OQNwUp47(pV(b2c-(13pNUo$Fm7434j#j{OL;3w^LhWv^8@>88f9}{+X&uv z((vJUc$+j$@Og9gdE^@WFFTuO$(?$5P`us#t6|=jZK3hD>aDuU&m+HxlV&{#xRC!z ze&bL6%P`M|GH3{Q@T`2}A@1gfxFa9*?!zDOA%Eddd}_)AFW~E8;n6#VId;~)da+WB zxpJL9=Q!sTzxw9o#fLd#z)3GNxV>ZIvSI;zi~ohcITiSO7p@lCF=;UG_#qpdk)gcxoq2!9o8?F7VBL$>XgJvPrxarz zewpkAF|$4tc&9^K^%>+Va-flUcVvUP4@>RU`S6Y86&;Jl8odm(G_uVrY@3x==U_KP zVBvQh0gLlihG03~jbp+(4LMY+ZblZUUTOK-3AsmHxKlr>|7z8(vYjC;Cw-%V<)m(` z2}^xIe+~6Ps=(SgXDj1U#Rq+={>-6|IN3XlyO+4Hx~Uv~`K>`cgR~~znbeJp7)twy z$v>-`IfJcc?4xx+q7@VGg>X%r$sfa?7sr;D|6RHnoAX}mp|84=SYuNO*X1s01}t&z zSyfvuEf`;UX+dgD$w8*+UZ!MvYq{0KUi7ei21_>cpA5DS_D@{$ zk>@1xF>dAK)BT57dwoKAsT0iSg|t@>?S*F*e&ZT!He|QvL+|2Q{TYnY%(;=z&EcEo z!M|$LY2G`}bCjnvCpFBcqFc*jdEtt+2EYt?IK-oPif5MEfsP#MGK@!iPAEV9;Y*MU z@An_HG^{#{ACw-uB4Kbw+=6U(DP?%|O8-vM%Fc2*6aMQw%TD4xTmKIHjIy^Bzm4Zc z`cD3#6l)<nBu98|ifp_^q-pbjB+7MK0Iutl__z|9$-DbT^~IgRp2r#p(Y_#d|w( zPUU@XX({VaA4v(|=LL`}Mqsc$`6fL;Rj$+8*G&62G4L7T{O*8@yQ+zp~$k@QV*g zX`M8C0KIO;V8eNn1@M;!f3nZgLEkj!dx+l@-yOblrmpJ*_>E^)oB5v=jVz5j-F}Oa9`HdBb*A_!j%$$*6?!hoH)5t5%eNQ3hC#CQyIQS3n z=}zWZ{_nZePd;#`b5+qP@QwJG(lF+$QyRv6E@M8)=dH(i#Vy_$MYYfPDXPGfjWHgW zvd;u_A>lf28u%t(C+*1U?6xa)4xqXJ#0BOB@=Lp3(VsasJ5+{@j8>)+cf1vV0S3u4x{zZsxdOTvn?8A?q^>mlP8>ui0zX z>1XksbOdW?>Y!)sZ_qj#?IqA2kYp$87>&;|-`5GfE#}|Fe+mDk{8vB^T5pg3>w6@5 zXkh7-LjzjtDjmljhPLD^=&=JHVx6kioWIr3{U&T!m8r8{RG#FS&Q8HLymkH066#%{<8~LF z)jj(zg`c#3^&ozfUk{FNb89}1rMYbyMuazg%Ug+JtnnL;T3I*w5Mbv$91PW+YP zUHmnPQ2cc)q4;YuVddG&dp<6isP(kK$S_9$tr~wfHk#@Zg&QZ0;MNlgZUdp|o8uKr1Que-g6b zSmc7{4=3|n$6Vl2+O9domF|Xqhi}+DDBQ(LH;-S-a1YwZ^(GX_4q7IUm|CW zi~-JZq;s%?kLOu-utR#KS&aN>@^#i9wyTvd$1``>WabXf8O+?(J9ymC``ZRHTY3kl z486a7FtfdPuyyEt?qFtT?_fPP{gBm~Td$SD_Hf%B9-!K@r@AWopuwkS5-ZrRpM9S~lFlNI#LiOD`>Ls45CG(9A zLXRZ0)=PQ>9l|`r9`F1XXD`yBb$8OaljlN$nTU#COnOH8MtTQ*BffCZIa`nSigEgT zop-u93&HK0S8{r|#|K<35?!$W=jo1Z@DQ}Y9f{4*-V*xcZyOE`bU|ZX&{!8VwuJAW zs-!Uw8p|*T!T4&9rA?(1eXfAML|a|Zkm6RjJ^fb~Xg@sSyk$AQE$T@dr6-fnTpiDv ze@>eDXJ;@<2XVgDlfHqp(y5IRotoMirVVJMi8RfmPmm@lzdO$^e)puEix1!i^phmb zF{GbB8qxhko|8lK<{{jGw$w(>w%FOavt{3u?JfI`+tRY{_`6#61$z|&c`BN2MNeov zGj&Hm+tP_qO2i0zB$C|%rC2z2pD zoWl~;#o^qL_Fzhvg?=Xam^AMnPyHLR`4Th6H2xUuYw6uTv~Alr0lqCH9vD0@W7|II z0VlC+P`)U1?;{$|t39l5m~b+1#1m$$3_V{%ex1c&=02GBh-uEl%mwT!yT}&FVa)~f z)H&E!E#*7G(Iwjh+fH+GcOtWweeck~gWmAG_74(=2EM=?WH_(gfv#7X?WA`zTH~rU z`@uR~?Zsfv+;*cMkmJ(P!E>%OA3E22GPMD}KYG!VPGqja`2F!$S--#1OINIOV#O5u z5U&(6@8=k&CIcPVXtrG zU3SfE@-OAx*n{Zb1;lZOyZ>?EEa01Tk>boVwt&i8Kzg-37%OH}maY8!RyERYA^ za}qzP=0I3`b&m@y`C=H8?NE6ORi1PYtG42Nzo$%b@$%$cb+DjRWIq!8NX*G7758QlVYxzCQ zm5V>;!mD%O+u0rXB$3f;3N7VTY1xD=9_B>*?L>XE`A)U@PO{%l;+t^8{YyUghwoIssK0LE zS$!iLt(AGODbUt&qAlrFo@Kl1yJ%lB=x%UuL>pz3uVB|c zvB%&s@cdW@FfTzq>_`M_qNWkQf_UNZU6FY9*gUq1_*02LKN24&ehu*+@$ZbpzwB(g z0QeUO{@Ww*FAxv>3k3g9M7}HgZi#@ zK8xY0HQ0dai#wsWyNMUQi9Xdw=DcF_EWUi7pP-*5A0%g9Jal;HYGjjS*e{V`Goi~+ z&i{sZ&4Ul)c6g1@$hcvgkM^Xy=Oy>271ov-7)N1~`fhLDm+jTLx_59MHrG-o*w^o2 zryCm(TQP<$7tA>a<3`Wnu5BZCn~UwFZ_RPe7xJr#Hk}LayW9(-^T~oS^se$G=B|%L zb_-{dd3M2Bitoaim;Oq5gX0!&o9Y!~o$MVYKGj8DWU>6}X9!-`yV&PV96rcuW7%LF zyV|Th%NMqw=b6(6_wR1&9lU5p@8HFW-a)NNWUoj`YY@&$>>t#+#3X4`>9xqc=NThE zPv1Uo#`?$e9Ika+%$-+vlCHtRRQnzDyXwbz)r@!F=RZss;C1WX3~N9Rd?{E1(g@vW zD8B)^YT$V)x_lmGpGUp6Fy7T#k!WAd`6O##SPNn9nDp?06qvilACc^0jY9@`knQ1I zgB~NZ*Keea(mAYy z#5aolp7#qUb6+WU2=SeDt2%Q!Q&jw6=-@DR8G3$aKoj|U>$tlqM*dj(r{zu9G3!qC ziW>XGh<8{hio*0G`Ab=UW69purh zo$4+0JQnNN`h9oSvhOQzlqMg5CO6U-qRGu3bcA21jyA`*_n{uVY^EL?!MRy0h%X7T{sNjdHv81PuRiWlJ;C_&PkIkh&~)4}d~r%s_YhCS-0tF1(y0G>k(0{ruwTaIE>H1`<`Y%lCD=NleTq#$*;o1E#~j~O zw&H^4T3*fIz$X~2yvHVqU`Rljb zvBP)6UvV!}RQzcCwS=ZfstM(mzt2(Zt9Lb=1=CvgEL>Jc(?>6Q8x(Jzt+M{|ZUB_n5ui%bFJ!=kw z>0QpaIlSAYy-F*Zbv@)H=d*Ett{)wH##k@Po?4cJlXL?X=vFUArEg(7ucnQ2 z#@VJW@?GtnP+pInCE8v@ImTb+Ne<2{F6R9jV3{;7dV@3z6-OS8pIqm>qWGbcxW&L~ z?s&3a?>(_tG2uShzli5`-YLZegw4EnM&9*(p?Sx?YtEGfJIi?+OM&gO)==pe>$_8D z%5WLC30}f|s(%4#)}>D^vNyXVn9c645pjZ>AZ}fH81{QC>==0jS9wC%x&x#Vw$fF? zj#00ndPiXYiD1+II>uZ9?5Boc*NpDFiadvoeSN40U^$CT7#Pq%m@5lr*kQqcw;|_SiH_Xs=|KWKUGy zsBFobP!>u4NcPOFA$#^SK9ZfF^}cGOY?aBsJv@+PeODYmUcHCE5dU5bA7CTT@&U&1 z0ZJ$Ivi|L2d}p#1b@uoqTPR-jhA64Lg?gCwX=lFS%L9O!&|1<>%yCzV&+iobHY_Mdj66A+0rjJ?(r4 z|DJH8wHB?66Lj`f92>h`cA}j_kS>ky*wCM1{Ls*`rl@u3sVr&JmzL^T*UtW7Gj|xn zhp%~q!o&D(@#oujmv<-f9xpung7WXDT`IHBwohx|7)tr`@ z$9uB=(7+As2gnHy-)T=}3h+X5i@=+a$U-Vjb`f^Zye612!$=zr+Wa`&r0eb#tE&_7IFUZW9k^ zoc0;*xf{ngEnLr_JJQ8N;s@=66RuQWy`TT>U_ApkXI)UZH^-boX}-s?E$ZQ&2H8UR zqZ-9W_@gxbCcV~P^qF-Jz>2~>a}2n~$17MCZghQn2v_pV@qka8pSU4d+YqkTPucQX zx#KwHu?HlTpTzinlGY^%PdRLR?TOMJN3;LK!)NcTH+5`x)=$B=Icci5ev0yh`#0o6 zzMgvPU4602;w`|L^!ZwJ^4jt-z+&Bkp}{mXQ9g$GWJ?pe=Z7?<CM;~2JKH*`G?i>W)nkUfvY~KGs*@FMl&kha<_P-M@jnLSi zLvyR}O=wSycj53JQt_A8XE^JsMU z^(t+qjix?oK)?sr_miSBL%Q zW`nbs1ZOt*ITbowgG|>Np+}vd|3#}_Y9%r~@1MjT83f~=YiyeD+BBS1lu*BF{3_ZD zWKd7}lq$3!m`TRYf{Cmhs0XH=CsPOUg38zX_t{4*I@vY9#aVK}NdK40OIY}l{~mjD z6n8-MZe<0uZe)dP;d2M?YofbZloQocXFvzfGj|u#-7(TrmhRqe=}z)sJo@P%u&U{& zWBB$k*as6^mcuO-Yy;oy&XW#Hd#6AAs2`BQ?SmV9{Y*C z=XZIXl2=#u#XFCig8g;ugUCTAyS6ME*IqmqJ#p|5XB~E3P-?)o?2v7Py(U{taNE&S zx70DVAYBT6*ILYZ_Z}Kh+TUWoXbqy$1#)?KjYWMYYeGpw-weZpZraIz3v)&L?+aiY z0LJuD>hvsi3h6A|$EiJo^3~qm#Qk6)FQg_-aZ}x}$mKd0=ef%GY1kFA@mO!&hfd_0 zMe3t<&t87VG|m?A_&yDLubsAS=;&ZhEr*W*{meeiX?9Ow$k)bSX7^6XUnbii)N%52 z&1L>nbE(MtzNK}C2F`@0?j~Ni`D0@vysr=bgNE}y`-lhLLOCy5UN0P`y`;m?N_fWS z6`b|Pe9^!(-lwsjrxG8hkpFI*zt`qRek4@>W^md|*}crOXic>GZ#g(kGluYT!1Feh zr+02D&+KBYR+@EMg-wi;@wMgH{{vo{I&=8SxdSSlE4LPM<*DF8c^&k%>OYZs(Uu`u za^Dx2e}@;eCsE~{fsJ;zNgvF?uMwZH4@h!G@e(pvTq&Y%+M?SUqQE(WeaXSH0S_pwEoiEl-h0VD#|^^&TEAF zLhvg2STMn}p&6Gv+dxkcP=EVP@T~(R?5BLyA z%P&79_u7n^zxaod`K3cuFPHP)R1fBzN5BYit~(HP_JhlK`mj^zo0u&0ss7)@{n_l({ue6Z zKc{!jzG$ZX348WM%ZRftoWVlkJA=(daLbvd!1k|Eo^&J+V^~dZZB#q-}^UHSZxWEVS+lIval8hTS$?MJV3bLa6e;O8AYM?a2B2v%CEJl?J?< zGiSiPXwJ~1rMX?%r~JFe0f+Kx;0#|~qK9>2-x)kBLch0+m8Yd=-x-ngtH+W4gh=`? zD!tk!I#xfcZE7QJ(|Y`PNs&GaK}mMn;#9kf6Dq<_vh;4q%4i8J9#;~2yLN()}A$qCK7W^7vB z18eF>-P9K`)lcvU5r2m8b%Y1W$?$%gAy1U=NtggG2zXhMnX!)hPe>#r*FC7?}zs~S&pu1I%$17lr zrMtIP=^LaZ*p|>g#NHtqng#Sc`vCGXhpf$^0?skeuwY4NP2@S$S*f>Y_8wsFJgnDTU>RMl^yCx$D}6}+L;8=phx#wn)jN$$ z3)(jWIKtI*)eSh+c<<9z$DL%;O9v}on+>Id^?sZU$I`)Pj#CGpHBKG;wkkSUwBHEr zk4p!GcNZK;*17*hb?~lG2iNFJr5Q^HE6sRy@D&ld9FGpZFp_>XJX?$I9CY?Mk@Q14 znD!Zax5B;%b?~GJjgO^+)z-0e@VBfk8A}HX)>t}NX^akr-kQD9_17-+tI@$@^y64M zSoW6D!8PDWufE&1kFi(n{vhq2*tA9mQ~zqVo^<2m54*nf$3~Pd?2^FdQSyx9{r+U(NVA95ZmPtQjNRL?|6B zc#Ic}4ra}kp`)Lrp7z}ZIsZnh7gj1h8=)D-)@FRb`CAU>Z<+CR4ZcXV1v>id=jkhK zV(E6HpP_Ff4~}yDd#Z)=KgI)xdP&#H?ynq^PqJ_>1J0OwIZa*Rn>vkydDb|%j`asG zAm`L3>2}G)5O;IzyO9;tA=KxgZ4i#1-=uWer@6x{$S++k+#B6aywUBdzi`es2b{a$ zJRDyS@o`0;gyZXK`BPzev`KwXSwGFWs9g!ixhj=Ur zkL|6QzsLU>bm~xt(RBS2V~n@wkymypGTww2+3+ib;-x>c?;j$Jj+H&8Hx3BuQfJQfr zl^(r)sZG0T9BJp-wBVzrjUMXvH&|Y|b`042$D!M|Ms+)MVQ>jfE62P}_Q_y;T?5~} z;M?eS$MPF#E9qSLHo9GK#0#XY^d&gi^<|^mRUgugI>ya1_c%uTrQ6Sn>h@Y?qOa#f zbvyMk^&`&_Z2<1MRdl=faK;~w)a{C&Mtr4iSNuuDAEDb>C!sYm9%JK5y*`;d((5S$ zZxp>A=6z}Oywd6J7m3wd(T| zEu7b)&-YuI_WznbKW?1*{Dg7pbI+!$)aT-{sqol;>vQC9#DD&J>hlk??n`_(9(}&h zrX7zyzs#l`uRcG`^2&ef^D*o&^zK`aLZ6>=6#D!fRrI;|@T~`q)aQy%6JM#%6+eUc zs6Kbo%x|-v+RbQB*&?5{j^}8t@nP;%(wPtn52twccCkl*^*O00vCpsYS?`nx<1dQD zANJ~EZ%Q$zo+>o*d@N~>byK^J=lyuy!F)Jf$9l3wx?gg&>z!XZ%o(4$_vCTpy5`Ss zcz@Elk-ydb{Cz=PQ0V2Oh%VqU8adQ4^H8b?>BK1Z#NXt~2iX z$bNaUqCPL~4eE18q(0A$Q6Is1=rz;_o9MZ4jey#BcSW5VEsnkrsnh+DI?asWC~98` z)@?QFW#(YC{-|(S&~NyCqJEL+I)qm${3z&|#P1o4M#q3!J9Uc2UY!4?Q0KtSwBgw{ zoNGhud-EM%pb57V){*`i`;H7_efZ_UTq~jGCIs*EHvB6aB1cX9X2Jo!e~VD%3*IBx z^@mn(+xL_mGaFw3{sP?@qVq9CU;o0lXIY;RjeA__Ym8w6|4EQv=e3%#W0f&TYDUxG zx0EIw8K+dve=tUfivdX0?HZ9FR}q$QaIr|gWqLOw9Zmc+t-BkqWo9m z_X+FudBFk3X!zCqk-k;)yL`PdBLs5PSI~42)}tj-DC0t@f`Yd zqG_)n?U;BI9B>!H3Fmn(5Z|N9n$ui{;JTw zbIY><&)*v1`7bFi{b=}+K0RX2zh&J}Ir!6R*1HmiOT+Zu=sfu$ONXBvBc1N0jq>k@ z!t|rkXO8`5L0^V)<4UEc4psd^iS_%!w3m#LHpJ!m!lji1?+EjNKO;9Q{DonjH%9V| z$H#OxbFAWfGk>J|SAnlNp~r06q4^`>hV)1Hz)1TGo3?WP$nZaV`e1JOWSGAde>3rw zHmTxQ5-+>zLczyZcK<4HnZNL!5 zhg6rAS8oFfj%>GVMO{9+C#XwDq%QBTQ5V5@=WD1-)c#YQW>(baMvI$+%)f=WIosB! zn(ZezZ8hq2G`8Q%6}I0#WbMC8mq;H#H}dh0$M%D7jI9>hejl*!_Y=zY`?cclN?*nT!Gn*eIcnQ$$P%F>$MS8D@K#Rl9={xNNsbWrCy={lanIkDEC9vt}Y z3ETF$EuLw&nLp(FkbF6c`M#;Fch%Y;*&Czb^po~mHG8j9_8#~MZ9609Ek2FE5xA1u zL-wBiwr~u%Q8+{XMqnN78r*+=CXhMuH;##K=wmdS@f#M#8^b!&p3&?)t%ndUI>t!% zFE-urVLFuy%+OX1ZQd7bx?@NemFJQL$G?1dpfSupYJGbAm*JaezNnqoUt#CHowTys z=G*Wx8!oV6rwtz?&eOQ=QR!bx zCzW_bmeTg%cjo!+1EsgNy_2=~{*bL=>8TbBr602QYu%Ne zcZ`+x=AM1GDGhx-#IIHCjW+0RLxj(7RzCW=s;zhPx_!&WD8CYK*yTans?gxomIfKe z*GxBL<5`?RFEw#CWaHWN&{<8{>&V8lY2Q6Y+R$Fx`ICV6&kfTCO*CBD+OQ~U(tWlvSwb|D{WZm!8all9r* zp8H|{9%r%|yY3)o4@B*{h1Q?;ugnvMwo7wgFrVn+GoKdmUB%$-5Y7N_sFT@WdfqRC zJ=F5;ja3KPV$WC9;WzloqhP$dXoEyZmk=sAEIlJPo-SRb!ggz#$U%f zV>|QC+Ef!FKTUWG?~?B)+xXK7CEwfZ_jxw{LK~u+O+NIw39%Q9eE$-mUC#2<{)xV)4h}qhD*pEL`tmM?-g@?;UCx=@&Q3^n zJNWTiIee~~&XWF+{jxg(^Iw6v#==|`feDXnZvd`f!oxG7{VqB=5jq*GoF7HXxsP%} zJRYZZrz3i%(k=vt)%G=|-ac>dcB^}~0rOGLTNgeG3N1cPjNk+MSY5L$`l{AC?%Qoy zD=2HREvqw9)>wG@@k`*TR$HMN(^lF%+MciFw*18r{5>8U2mXLN+FEnrugk)G4=`77 zX1MT|Q&@)%j*1!Ih;Pw?-tT|!=X zFKLg(4sbH<7jFJjZnWaB=N`h1_SsmOs~*XWH&p#Xd9zOU3tIU#$JSl;oyO9mrFpZ` zQ0HnkV3=op1nw!yQ;6i@tn3!b(D_R`yFS?!!6$cQ8hmm`ro$bXsWh^5?(#nELEh@b ziW`#M#SLeiQsfM=p?V{*1NgMd(4gS+9Nx$BTH*h?=6EINKZorT;;?^=wEH7zpAXZ5 z&(X$F4G}s(Uyc|{Nya|9|M0-SgfP^0;J73F%RT-B=&`7OB)S*k@A0Qd*LhY)o@2uc zZMe*azq8>#+K_#+M%VtqzBlkLUE6No8TT2T`UV@GX2T0?cqO66Qq0L1UCX+n#HyO@ zVBD0#w>pZ>y?Km$X8bgYJ~nHaz*jXomTyz*)GPJ4*>?o(R^wf#t!Li>>*tnEKD6t# z>tv1TqB{9kz$>cZzEy70J&-kluUAysJKIiY9YH(Qt^Z0Q*M2Jp68c$*{e#?lS1{JXV5s+GZ9 z#%5dRjghuO?=@wd)3Scw8k_G1^1W>J*8YgzI-;$$XbpIux9~m&tO$uvCFI zJh8^;LftE|mGe@!o;p$&zH6*D3047E>0!MpSQ~(~Vdh9!vjxkkA?sMbr2Rc%|4ko9 z`g9~c`l7Z>(Z1hE`byt|bYLBHp8!tf_~YQ>bS&rQ}OsEs)NL z*Sez1UOYI^#F}Hh-#tG#Uj_ba8b49@>h9{D78ROHbZ{%1B% zYFDs6NO^k5BfDk|>y%R7+LA|p?H~RM>uj$0!@+@7cKs3-Uz^SqAzjJw@_iqrerqU4 z<^6A4Rvl@=ak}dHPn5Uh*Gjjx^zp|9EAW+IBPM=Myz1X$>+;`oF#iwE!5~jYn-jPf z*?-h?Fv#Q4_DT)S!SH=(yrVr`*5CPGW)3DD=$pEV`KqhIm3*47We(<23*(Jr*s0H3 zfAv^%Fnu=NSaUEs<4<+3oP&{U`1U?K2SdM&ZqKdz?>QK76P{^f#t`_$Lc0F1FbDIG zKRP&Yhqd=a2mh&aFyLx5d+mrhm@4UBmpPcKw9J}-#JVHrV6bay&8d70{0VPEb1rhf82Ydouv4CiLac4 zQT#WE&oU=s)_dI?+|xw5i!(>YJr}jTl=pAQTWkI%hu>-yf7w8*tPQjRTPJ!}=AwT; zI53HGP{Ok^TdWO4I%5N!z8l{pHccEG=*~YLw$WNBUwg^EKpkf9SDLk@H$Eyj!}iZt zh>trzFY8R)@XTLx1`M!dziq65C;RPV22VdPYn@S;r&`_5r0ym5kB4;+?YFS*zh})% zSohD_vZD5z+Hjf5v-SUvM@H8F6BYFzYp+@=HgfLe6>Pv3d}h&pi_X2AV*NNXZD`g^ zSo&ua;IDuj9WSTLz$wRwP;vq9%}8k}qi;e_q4^vA1i ze5>XUe`J)ptrSf1gni(q*7=N6aN*?XcFz9PciE%&5&PCpShw$M_(d}(jFcZ+l~3h~-pE^Nr#SNaU{7$C+K{aHQdk!GlxMUx z*f)XO9tBWDA*_ZYomZG<(@n`FZLu&m6yI*#sGOUJ#^t~!Jf!9 zEu{bN@49IZY>W&Uc>wW z1K#Ur09Swe-p*BcDu73uh6;lXRVzD@6ocVU2b z{Sk%l-#u-bx_$?A{k?wk56+%CcPky$V})mNZ|d|q+?!*BdIcX*=ZDpqJ_Fm%Hk_%` zU&5Kbz+Rlc*>5@EOJWbzAvw8rY}Rfx36V-1=4k2E7qQJzj@U+?lso2ntkh7R#KRR(1clI{$?_ zKZ7%E(|2*EZ90hakDq!Y2ZJTKi$( zsUn^#!!CLD@yo9^{3ot41U(CD41HK*SbQ;g!H3w#*t0%ymge71UH((bX0iM?Ygo`` z{dxVMe=}ew4FLNG19lW(&9+OsFzQtA*}Q&n?Ce4uBJEnXvz{1*Zu3RR!#2KHomZ-J zwK{W68{5A2KVrByV-%Ye++*tOjaASOG28wRaDRA!zKi2yvkm`r0C>hIO!(IZfM-8t z!hdfdc(otkihp$4WmtR2w?kNLb)a>q}T~y6gb-r{D65*n`pD;Rg?ZAK!&H;1_B9Y0Any z1+%K3PGIaffKMjZZ;7=${nQWrmD4H~z_veaAo_s+Hv@jsKzu+a|J{K1`b4w={&)KN zL`=^`y4NRSdKS|4TAnD@@_7BC`G^@da#$MN1SmHwAFbG&z*y8ml+W-N#KIG*GA zh&p%S%<-NDJdE{V%$m!1KV?P#eF^-<2;WCT*?wug)y4T+tOM%)vW@Tcs&9Xf0t~2{ngY0tcx76J5Tgh+wkBo4%;1P=8*#T@W0_*eMz4E)6XxPkj`4BV%1 zJ&ffC?tXa(-Vww}-bq94)vI~u_W^Z1?ltiLGp>Ix;uolQ)Fow$ad;QLGdc!(kxAZs zc3aP{4E{cEC>*T*1 z@Qnui9$d?~q9H4>*M}nyE6~yt9k%Sep6Fbv&3Sb-nMYV`IydZNvPIuyq^MS;xIG zFDY#1XI1*MIMZfcukOFD&WJOK{D;)}m^z=tnKrW*XKgdb^;-`4z9;Lc!q9;qVqa;k z`+vZ-z|oKVkNV#fHg&Fn<7ER!H?FalM?dmpe0OJpeB5l{c;3MACa(V=Vjt=11UL|1 zBaSBvoc6iXz|n#0?tyS1-bNf@1IMQf9N)q9o5W$KZ?k-djPLLh&xZ{>8w@zul|#h`m1t{)iyN4wIq=L{U<4IGPb{lEY?EF1AZ zWmA7+;2C1zxgFQ5iHCIzIk0Zze5TXKnsxlVf#)3d%GPq30zB}OE*j6P2A*yM&uHKg zaYgWv+8)XFLwh+Cu(xQMfKO$Yxi*S*_!EQ1f8tuqwf3iv{-=a5^*g}IxFX~Ncpv&; z+Rgv!)c&(O{T$Mz|Ljix9n$~mq*%Y*z2^Yr2A*yO?oGxT@_ei*;`(ttm)gqxtBYi; zk?-^&57wKu;k{G&PTxMvjoy!aceD=-pD(QsyvpMP-vIl&9KMUot{dx%v=2N>+1tC+ zxk8=Ssx#hO752FcXZpaOQunm$wC9ksc#gFT;=Eg(_o}n@jp-B9p8pzW+Vcd?Tx+6z z;Lrd3PiH?wUki1hePGJ)7uSA})WJ^!hWkR$@8z?9HlBS7&nBw;{q?P{8+l^LGlF^g z(M9^Vq;<=O96oB15A(|d`-Lb=I-iy}dvA4cE(1;sss{E?fHPGaCcd8=_&yAL|H^(U zVAyssXA8YG{ce;!o$pGVTktNewgEQ*=j*EPOXaT;p8o0#Lsr)y&-a)IeD_x`^9&y~ zA9=){lgLAyD~&qK#+cKB_;-L2uE7peSM zmXY{Z82H>G+?LB)t@c~zATfehwGJQ{!#|IeJrMcJ0RUY3HYj=c= z(c^W!CPVw6dffh7z|mHTwdB4NFlnwHy(n$+GBlE4nH~q9O zXCvS91Muqg6N!U%X_#Rj|M2FY&Wf06KXt==V=>>WX}^O!j}F8$d_(4&X4K1ZD+A4i6gJq=cB#)4SOJx#`WKK`+83yz18sbq@UNGjG1-}wP0f4`=Jah&q3VlZ_8`H z(DHu{cCoGNTi63R%f{Xk$}hYTbHo4ouHzRn4y*UI#GaJdmbERB0s6y{_cO?A`k)Ug zzW5ECKl_P%pEAdfJtYuZ-IMwRS3$O*ypl8^yV^Aq#Vjk8ha2`$M8^z)RdB zK33H;8Q%^X2VeFE><1LJdf)L`Mcvl#_nvCIJ=f~*cmdB}F<=g%oIXd|% z#~kKIS`O-re%LUaX%kTLL>|t0IP={VGoOwFF?Npe!K;+{hsAzOQr`Z#=(=%?O>;~` zdX@344cD7+@XRA!zNz3N17*#y9`7E3wtMas#CVa`Uq8zFSq%AIrQvL8^Sp5g!u<-d zvV09k832~P2j!4~c!f1-YZu|(1e~bb5bC!6(>-WsLO#@g`YY7`A*eU5BQfjOlp$pg z+c80}rQIUxxUGxp|4lg7ajWD-r|E0>9+nB8fqUv+CeGBoH#EJ=%>OF$gV$l;f&6B$ZQxqJPnr`z+d=#F z*+)-u-+iC`!;}Br+dEbc+Y(yYj2_yd9Q=rBkXExaraA* z0n>;N_HaTc=q(dd=v9IbJ|BkX0c&ES2eDe62XXib_s&?{J@i_c=LGUR zg*=B;9>lgKkI4T+#+)U8@`WoB{YRSF=SQ>d9t0+S7DiptTniQE%8yD zeK`LPZR?Ov!rX-Rd>nhaJi_;XkY_aRkD$#xjJ7yTwZ;F0U;J&X;V8|J?HBYW96|nk ztm$PPy$yV&84{m`{QH3GYdA)td`|(-+kAUQ-{3c4Gdp&4l5Tfz?;Gsz{*nBWPiqy&H)585_*&iJ=&@EN4yXDIc7eJvW*vM&nA9@>wNWm2+GMdJm{mv`37~q4QIwi zm^TaayyJ1+>GMJ6&kEhPY{WfZ`qSBu0*23gMHnlA|FHsZunVz~SI06P{SVXRb*s2$ z`HAO2@PU0KgVga^iegD!h{~0`E!#Zvgsl;vJI)FUmh4zaLe2FCb2* zah?OOEO!pd@mt7#rzk^J5B3+xtwsB7L_5^BV;5j{qptR;x;le#*Ivw(`F)T9+8S-g zF66)IZSKwcxx_yKpWo-~&GZ%E+9mK=XJ+G^1K4tWcY`{0_-Tw$p%b>yiO(f$&j{ahE7ZZ{hhuH=r(i$I-+%B z0rcV?!11}(3#og-uEmhEs8{F()8uulxYl}+fWJh&7%$d=J)7vlJ$1+rd|Y!t*-wGK zFfaAx-%tn8*Oc^TTm-$Jc}N-_1>=PeL3oVDC&K@p;QLSzJ=kP6!((Zhz!}CU zIbG>qdsRo5_y*0Wwyu3>C+*mKI&wl#USpSahEPLxI@ADk>+^?Sl-+TCe zHuV?rAHgT=ZZ7XXE$-QWVGlOz*^`WcRQ*l9wGRjPXydnb@UC$pA95eB<8D$wSp$=Q`?WVM*VUhxb2{`ThVzNbUqI} zmQlalJMAh}zkdgOmQlZ-0j_PTekZH?<#&6yXPdSkERVU@+-somuu&HHEfcIFxjys5 z(AzBkuEbN8)%g-&@)3`v%x+7j(@)ryu+Udr*G@fUKl!=D7gLn;#(v7#1|C+XDCdde zj8}gyvDH(~Wx#c}D(Aa+M*GBaYCYC*T=x0=MknL4sBE#u_wUp_`bBX+pY@ODw9SOa znek4c(~M(l9bma%Mqcv6`n#2KLfSWsw5dolWy>`<*Bi3+sXVKZR+&OZzhpUqn|`QQ z7ghmRehL|VLCI*YM@B;+AD@y@n;|2u3ud{t7-hg(Vo|P#j5M@uSGneZmv^zI18awb zEQHQL7O-zUM~!?K-wRwX;~h@FZPaWVuLIW)1U~D`TAa1cXnB}@EBU$uZ@3Hq%#)xYoKFhe;r4^cA9#p+OW63uH$+x@Z{~U|MFj@u5rzu*{iwKp6#f9)z@x80|Lw=R^8-(x^4qQ5S%F7S z5l%esz!~GUvGxv(*Wfo8&x!vG+JWk=n?j@ zPn?IWcPhW&7<}W;K-O7??c<*Kex*m>1HLT7FL(^N{u~F|-BsZ&p! zmpWzkqx1)@BFyihKg_6>WB%X6cV)RTo&u{YF|N11+U`Gw{_>tKlyjcA_7`{39*yPP z7wnPc|1|7`b*50oY3O66!u__wjs8#I#`q+O8|~MHdn@ac`0b}o`Ekw!oebCkp*tz) zX`g^)VLuO_O_T$5g?AJlAD8dnm5p<@?(=imPQa(m$2>2SKllo?M~r7T!$#=|4j61NbfiaO&~f z-M~8*`k=p=Pz8O+HrnlEA-`u6CzL#?e~ba3Os-e}<`{Fwqh4qivh1DsrUG;b*Kd65 zPiIjFXY_cCy552BY@*Ip+EMUyG?8H+O=M!eF_YJrZ_E_)jn5_~LYJsp?3bALA>8{7 z9jEUbg^oXqG;J%X<2zBNHl^beQKqAqpUCm(_^Mw>Ib6W=97D(70InYieAby-ob#1l zzlk_2`xyPb3ff8fKK;phfg$S!9$EhpYYSckU)n}aRI=WhM%I|06*A{oTgIxSthH<+ zkTvQ>$d&T1gPc)c$@|0LU$;}%$90h94^Vb(FSIQ0?njn~!Sg3m$TCaG@|zx6ZUnBF zlI18vmVcf`mK?MF5#Q5fd*S#o8+y?j_+d|$A9@jZtjjmHt?MCw?>oJdeBHhCf7pXD zT~}71BQY82+5QgL^P_m@2|9)OmK%}AyuE%O$9`QIQ`)*>Hu550Z$U;k@#U+$!;$wY z7;2*8th&ae7{)Qc-ptK&wg+=?%xZ4ZCm&K*i$GIeN;W#so#GHWyM|^QP_yq zov1JPLjm|e9f%uN?S!9*vwvG+QgmCwZrzrcVsA@K3v5dibZkoms&EaQb1-%!jR&6N ze&wC;LANCq!ETph%=mvj^TFBoxBlRC>%u(iFpfPqILG(Cn&X>BojS3kdp=_20nGEw zu`$;dSPY)pdS1!jo9G;qm&mhQI~S^0dp`92wN9*6%aSqs37PCq+Iott#ps9HdbUm4 zo7jYBtp((3Vj?f_Xr~YUMj>KAi$^T(UcBdnv+ai_CiYL6nAkUGV&a9`iHWxO#KfM` ziHR!Y-5o@pj^|Gy_K?U4Y)#}>Z%z24TN7i8wk87LXMEMxL|##A=UUA5aDHcOU{_)S z@Xv<4*CL*O8)9*!OTUt3pbzv4<{-^AK>2HPt@y_EZIm|~tWf}TfZ@%fs zcMI~}=gC)!e1x@;uh^jd5nPKhLT}nE#!t5Vhd^H$qrly$1EUp@D-Im61;X_R}8jk*s!mH7LyYq}0Xetf^^Rh$nTTGN$} zwHLqoH0np`2w~^r`j41X{tvX#YD2HGP!8%pyDR-1BfO6fAcBAz)vq0iwduh$gLwi9?3+L9%iDIlr-g%F`loPFEpxeOXgRjSL zM`dCxnBng@rD=%g;N1sb?RK;)tUbZ}&7<(uQp4P&V0>Pf*`NbFSK)c4b@WuG@AXsU zrx*Mm$2gGvCEGP)c-1!ARRCiL#I0DSnUu{H$m{-xx0^?PC{VyOOSx^BRH zYHj9Xl-<{T`0Blh8eAXZGmOz*!gK5k&$*do+v3~~b$F-G>TCl%b!0o>^8&4%AECT~ z|9yFZ>^Lvub5`P=QIUA*5iIWMsZ zI2preg>7aG1HR^~)Ccqj78UzMC6lWk*V z*D;nCJbBv>pVKD6?|$REnQH_N#LseR|CvsIk!2C`LEEG3fG2xXW?nYzQSw=acvjSj z^aSS=-FalXluu_5VKxCL`P~e@lJy7~_^Kt}+W=!(_jY{^&$lVQnI^AWyVs7}i|f3^ z>TI{JVV*?j8tag9ZJj(Zu?_NLjO1y*-PwvhKQNVfzR)!dI9|4%?0U(5rt3IhOx>dV zbAX#`h1&zqbe#Ym!sH=;G2&~`hck^xJK@O}1+SD_w$^#*`w66R%~2urlCmKT`!&;s zYnX9>q3qaSPJzy8*|03WsFcliA)8fQH$gVr1#D3_)5NvEwVQdiE4}jD&?~?^Wo33! z4sl%XoOwg%rrFdz{S6Z6W#TufE&uFCZr4w!xY=##%VuF>z7W)EDXizsLCcN87qg`^|QQSVPxzj+mVPR|oyJlI%o!u(UXQcJ0Gz)&Y&U;d&F|I@$u&-E4E3Y$f&3^0T>@|s=$!ii_ zunFB`)+D}B)O(hE5Jx7~tuS8}V7DXfoT!Un*S|m>T$h;C50sO(?>wHff9|jB_K_wHNzJKiPGj{Gf~+>v3(N3A4z65itTaM$s?@cnC>@2ZpjEP0EY16ZCO?5@QR>kL!3|g55ZR`*zru4&DpBf2M0IWXW=A z+(XoQq@g$y57%E&u9TY(vF*QuT&bTNFLYx(E^Gz#%!2)W7jr7dk%zXF_X*r@%5v_@ z&_BF|JafPY``6&m_>s&%7Bu*6FJ*hi{({lDEa>mNB-DtHQ>x)?L!MVR}s{j2D^uLy>t2y?> z#9XxfNyxkNh8sFd1B)?coCu$AargW1n^|WtJh{i{|6py?o4Y?adzjZ9?WeMeFt)A2 z*fszgQjM{#%`q*;E{F0k?#siNFE6ocPF`YXZC+x>6xe?7b@$T6-P`ZjEBz_^u}zM%9*Y{zr05~nQQ=~(0-%R`?&KWrW80So!enMbu6dDmIs@r!SAgk)UYyBP*DO~Q@w3CqPakiLmo@Ip ze8g$7er+FQ`d1jsTmUa$$N9f;{szwfh4Vg~{}bm61|B^=(s*u${_hn0pl^p9k072v z9}8o|SC2r}^iP>4ueXY8U!bk463@`rc4yx3Ce|Bt_V^~ghPCuhqR)Qq6rUXlFt2?6 z8vJ)Noq4vZG=UfN*tf5RzeAm&f6X$RW3LOS?^NaR8{?YeX<(>hi_jOaPOhiELfXieo!bU7EYZP?Jw-~tEdd`C$WwsXh z&Ot`|Q5WBY%=cw2PP~ZoH*o%IoWG9qew_ab=Y6n!)sWBo$h#kH=3?OA1MDl;tPx`! z3$%Ovsq3^@jgu(53|o#{&l`2`S82XUay(_inR47^!=C}oqht0aj?!mB`{wxQFs^AQ ze8BUKpYdH*+5Y$~%=&v4OI{b5{$?H~eecIq{h~kaUnUCwt z)`Gs)g&g&~(n!;~?6=x_*bcP(*%p}Aj?ramhk7ws4nzLow;C%t^3vi#Vjj)c> z>0N)%_K)t%H~n^w|NaW|Cu@9fp+6Y=mPCO{i+>^wU#R0(HG<*X4UG)Fr4wy%A z@VlGzr`Q&^!GGFjz`QG-uSslzKlL7bs8Srq;XCcXu@Al!eJmUP)M}j1<9H8g=Wv|I zA$+OdN?+>M-$-9-_h`1`wjRcs7ui@Rk2Xu5H{yNm0?eJhB6PK?=S}qKY^%roe)O4s zj4yn`j_*SL-N`hob7uzphS3u-CRyBFhWxuSFt&%ypNsoq+!Mzxz^@fJtWH1r^aG%A zAVA+m#%H$w6zle`VqcH%r32qN#3Z;jnRDgL|5>XF&(Qy34fI2>+uH%N8+4d99Xhfd z*RRaW zXc8a$OvH4A3<5=d*!RWVpG3Y+;A5PhaW~>7ziX{gDC4L^E7G5`vV@%GSA%wyKT(1> zO9c7Oksh9t7VMm$Cu9S;_}b7{qwKA~WmzBVnu+}sS_Kb?O(IQRw~A|BZme0#J*;9C zy6jQRLGfLK15Ecn>Xd(rDt~ob&)wi>G5GP%oS1-(ModQf#iINopHqG~p2_mxgM4=( z-yFQtk*fTyMTmny9-`dQQ6iQ(6Yz^bXDDcR%bhdDFUn^<_osIPDqkb!!u_Tsvmo+*;7>v09Q`C9X%7Cn`Gy0j&PdP$gFmHdZ2YnZxM12c0B<*wnfREX^hlz{6{W0L948MZ( zWA-TeB8bhmbzS#$>~9GCr(uT}TOfQD+7x-+MVyGm%t0FF6FYgY^ZAjFu*6IMi1w8D z30n<)eQt(~A88oIkTN|mj{~L}euCL95J#G@YZk^Lwv-RY<>79ihKHFVh%K4rX37CD)qP3pUAGp8iMG=#K)n7d%pO= z*;?upWJCSsdkxfGj4Fj(b}a`_)yNZV>zdO#F;Ry+pGTfnQ7vY@pLWn96}z^X25G!K-roUa%Ek%7CqWY`y;Leo>7TMKzljx9HX32R?3O* zu~SZLYwX*%rJ(;P@|7arg*5b8pMnl}VtdLt1RcfPNQQsUDe9?(abJdi$0^a)V8@Pr z;K+;XrN;X(mIPhK@)^hH+zj=F_VqKMJr%Ua;<>gjE_tn!y7ptr40d(c@WtKN0DcbO zgplXAEBLTpcqnjB zjRGEd2=9wO(KQX%)}|-Ad|RGC-)x;Bf2%;7bGa`o+SGU2zn8XsaMp%A&SPDQ@by&O zYysXGrrwCQ?#oLA?Zw?4%Fp`~zGeS1Vs?j=pGW^m>Qj(wzH=8G-`j50JR%^KXaHOs40@7z8-}|sntY48gAnn)$ ztf@o&yejkj6l)~l1LkAg89*Eu<=p-{{ARQj3-1GdAJNlNd`KLr*$wzZ;|D;9gXk)!cKY@K8JbadZ%Hi{zdPY7Q|JKdt9Nbso zAkXRe92Kl+iicx$p_*{5=9ncY0^a;Be9scKp=gwj&#)?S1aa_pXRyAZ*qE_UQ^anL#~PZ}+clAtdE5@ z*e%WBX1k^_gfl2v)`;aTP4R{eVLKd)MPjyr}=G})5m*_T6jK9g-&;_2-jmRsD^+^|Au9n zt2SKM2yrLVi06T5cs)+|NjSFS^>dti=8Lq%3!_a0lQ3g&wKM`#5^agwbuec3de|1( zn#dnw=?9H9-Ume@SojRWmc zA|x6tD2nQs9lp!p-%O###?+X8G13qq--~DZEwp zG<$u^yDn7o;QCmkrKuL(RwD%9F1Crg&cQ|g*I|)@#?AZnbDev(>Dc;pvqTjt97AMU zX!a}zj{NoI3H7i6{g#Illh*Wy930e*rZ8G^GX*N;Nm}gCLc(CnH1br_vaX>9XAQ@^ zjiIKRPeh>BDlhGSU!H(Iv;jRgIwI&Aw8F!lq#^2+Dr(A&sLgwWC)QB`ADhnwgA$*q z2f>Q+avP0+>Q37Qj-=NS4c7=2RXEH%cUP4yywp4?NO)>O^2YY3I*^pIVA*UOx@?p+ z+I|S8Jefw{uS&PGW?2di&-~JYNqV%Mw%Ocm{kzM?Bm7J_RSIDj`h#=YyN0suRQUR| z6M(0J>#HZJ;pokX#!QWZnW>M|dhpR74R4~e*dQ#0E+6~pP)j@l7pb|0#deiDiPvrn zKOAlpg-XRI+}3!wDQ?Hm?dX!b;W&zzT3ew1d+2ES%BCtYxbw^UGHjtH<0+?vi$u|+ z&@rrUPJ(&hs0%Fj^cpw~Hf~h8sYA7*`{5?tL*R|aMUqNmoxs0Foyk-Ui}A8$r)p@R!@g}Eo@-d*wc`zSbx0i9w{sPJKl{Ws z^#A%yx{F@KieHuPN8?D9w;jHP^tDEKJ8k5`_mArHjQHdcUh^2Y?LRs2n+rMbu) z6oV7mGOo~VJU~K zY{u<8Yc>Wy4~93|Yon{xwfr5nt9e8giO+r`Ibpj>twR0Df2at{LmMtoe#NwP7~9yj4QzSzr)~kWc5QflER3<7LtTI;erKQDl4}X7t3n{>AFT_2HugjUA8G+Xh)<=C)Be-(0WL9Z<3f(!MfMONV8Tr zN-^cRnmo%c7aiJM;`022j|CgA`sytbc#G(gx2R5-0xf2~>2W)>Mf*GR>|)wG$F#vs zksce09w;oFb=z%58ACu)CUC@rbyH%qS5TRxq2SnC*b3w~4WX)_Xv3y(WAiPL%11n9 z;4KFi1@TC4q#Qo8D2fgsAz}xSItWL|*dw1Bd%7+9Cfeu%yB4=n z;!@nx5H?W-M}4wy1{bqLxtfK6RxH>RG}g5(8=`h<+EpI9ylnnfaVASEyBT&O|QAoUV%P02Du3wz$xnA z!)r~b2|*6Xr4IH`N~jTC3a!LCA?6JsVv?~WnhwE_s(%lz2Uf2}8HAT-x`Yq4U{n*| zTwvod6aZQtgKCHEH8Trl1_IO8!BxKHe%AkT4?f<;SClL&TT%wR!s9gYHrLdLH-yoI zG3?udao!e;?Y5}#+!pq8Tg0GE_Mh~H3vA08ZySX%#f9hjBq)qCYF^(gP@iHrSn z8ADpeSd4w+)WU#@UPw50TvKuAzLx!v1d-22;$BvI*p7!D3^y702#ZjYb#+xN3@u@M zl4DER18}H^SlJqD$o;q2g(8R~agIW|+dd&H(yl29HP*CXs4H3~jgK%dGOH!?jsTq7 zEh}B(JACE`p&6u%1)L0ja6+Rq(cwJ#rI6gy~Opf$qJxs(ETo@4oU?++| zkPdW)YYiQt{!~T~1(ljXZBF92K|Mn`Oua$-BQ}`!28n6piqONX5oHL4UTHq?{+fly z$?icBu(C_h;z&U8%dwXpqdFGt1}g)+QV(=IJ@5s?w?q1Q_>d=X@OQa!p}<(_-3=?- zR1+5dgY+v&lkaE0;GsF`uN@w)oF?3saI7iR=+K-GdgFIFG+4HxL8XbPG}}RQ zhlghEAkxe&D{Xjq!RC*JW07QilBV@VcR9BYBF((AbXlkGdFUP=M7p_UOG5E_QPd>OZJ<~ECAWMp3@T04VwXi-G>>^`+WQ?I zuC%@6l~IMDO9+{3*|MCAzU4Tr6>8Ev=%Lv-h%|G{RyM)n3oW7z#T>n` zMS)9|32%tRHcRbww;B5@ z?(&}OhxWzJ^J?0o%e3uv48Jb-e3W^QhqnJ)4liCkzSMG$gsepms;1#gx?{iU(4j8R zA4IzOWvJn0byDEKt@WAx+zt=j+J5L>s;p@jMR`j?BC4&+OS)OlxXZg^5a|vtQ*(++ zxpaAHkGFW}zA}h({bj{9HHc^5+Yq6{9HCRMX^#cn^B&rrgGhT=*@Bkl6yhK3{76=9X1qA{^f1`f#$GlyRqr=CnbiIihSu9MPYoan|xB-PO-JWn&wv8brFo z%1RJxjzt!-@?|T1$n}GG=)X zdgvY+M7n4{dSOSoUe{sLp1apw=JyAYHf*F=|5H*Qu6fXCw=8qJhwk|AIDA}b8yZ31 zv5p3^udJuFIdrJcMT1CpblHkfUAP7@HK!jmW&MJOcHB^bZdLeXcV*jCEb9j_rAwu5{M*uwYpeR!)f^%<>)n zM?Qb;uPXQac2(y?*WC5dn;yAi!OXzilG|oYpIJJ$eEOV{(goA+EGaFYe%l>ofjef- zymNNx?D9`qbAjhDd`PZc+4PrgnZ9R~sZBk5FF*5=*QD(P@BB>(F5&8y6R2wsj2hJph`VS3jv_J>}L+s{qFw9P@FM;;6(C#G&H_ z%*(lUeb0HC+8G;WU_Xgu8hwn~+8gZ~H{gfUX8gos6?0%buXqVlWXy#3RmMGW*4NK# zk^wmh!+9%_Hm_OC?dZJZ>~6L431aT$eR+5t{>ps3M<~a2UmSJcFA0%72mbCBQa0}! zUH8Q;jLJFBSA?Uk`(R}$`lqLif@_L=&Bda<)LgX!ckOk!;psZVX@abwPsD0i+rP; z5t%B}$T!OBfGQkJN16Milg||%SuYVDnhx3 zbx~MTN2S6~=J&QU-cxJL=XK&<{V6hUyw?&Z=`32dq)a}QX{>`8#=1qq%#aHiab?03 z%53T{mWxqLDSzpoIc1xQoY`XOE>Sq?0o-LVoykVJ?YW=kxySSmeh!}L;vRo;e3q@R z`DMag^XFo>2vREG=;Hk{R+s$>Byk zufU`q;@);mJr>wt5?ZXfRg=VWRWUh{H1EdMf>3iqO&M0u#aD$I;+Unh{pvgoi59NQ603dCYalFO7u{CZTqRoWkqIu?z48okm>z881pIE2~ zzNyQsOLLt>{)phJaQML_*d#n>gEC=;j|@V1m_=9-j&tsZ9bX3yuBRa!?Fr8sw_n|l zRcEfx;B(^U*~a<8H?fzXItOu{f*Afbb#B91uNfsgs#MZx-rU@x7FCQig7qvnb%?OM zHlK-c8F5%SMs5M4gFhC>i=0PD0h0`rGj1&7{vJF=V0#MU=Q2+3fiVNopt3t1oF{mk zkGN(^d`7UBvClMoC4;l)0V8A0efV;Mso<*-aodz|MgX)k9gGpM3?eR?5|0^R1iiz6 zODXphK|#jjQo^PP;vE2NN||_rZs1`KVtg{5>5+{mP_{okoGY+31u?Xge0l;={0>%1 zxD4Q;V$zeiMHiXZ(47sg>Rhc&D*f}jK6?tu>${8(wla?2Vsqop}EqZvJXc*Z9% zMa)Hxf+FY)pMlP82I2Vx?*@r`2gyW{x3OVeGUp8EA(1G)San-9g-w`}0@Sol85Wugn14yS_NAaess z%ZR*9#R|EpSZ&1hZuZn#>GG=bvEMd$6EQq39TP_~x~F_HQqa#m<+~hlPKt+7$0x^I8&WqDx!@uMf^}ctbS4 zS*>iu1QniZ+SpUTz=ai1$R#t0JXj3rz)~Mn#CUp%U9T2D%Q|xN1hryZS$J^rImdgY z4b2bQekM5%an=c@%oodR&`9`BOieI&k6Wok2u(;N-O#pqqax^-GV*20l6tar}3 ziUs;umVyY5XqTEFuHSHEi`SUeZB`&MKlOqEAuJ=rDwr@{&Vb{q7lWuWxp4p{Qs-Ka zLO(LqxJ6nJJXCnxb%Sy=$f+m7N!@o~$(MEn4L5%3CW0t@TubAWEv(jSBz^uQ9M?^i zbIpwyRH5Bb6OpFzxb>Pv10Y;~FWd+dk619};Gm6icu_b!Fx0msA6SOo#O$gp?)Itj zdtj&!uKZ$gpjZNw#KX4IhY#X`fHkD1$$IOHPp%fiBmAU}xa-Z}4LBWWM<4Z=R6t^! zW8!klS}A}FP~xG^6(crTk2e+?YHUP}^vVrKZ=G^BG{p@F%{S#G1`2!=*S*pzSvGeY z1D?7XGz51Wn=0^#SyIzp*J3GjAFHitrz|IoeFIhxm^w_|)bfgNj)rZLN1Ddt)(a&s z4~#6QutAF##Uz8$g3?%PtHyWQ;hf|g-2_*ARKz?nXu=G$dj&mHdqDFtf_F(Fi z;KhK_bz&R*U?GTHcNyhtyIAI~mNWwdKS|$hKNa~%H2IvmqRS`HKum5#5>HD0R0dMh z^h|uZqe-P(De)mgQoW4!ulZZLva&MSd%2i&WZg%Gz}bXeP!w6=OvR%Moq|hQzrbhW z5;3Uc`<1d|P2#fQ6^Z|jd>HTO!=rrVK8ab&($r-<6rHvQcT(;t+eZuE>VnwB)hd0h9t21ou$oG|S6+bjqvz=Z%t*0#EY6 zcCn&j;XNxWf>=Pj9&14PdPf+uWwowyxn&5*hgw`dByF3Vu}1#7a4g(}ZV2JRrD8TF zReOUQ2E{c309HUJZKa|mV@oz3FD#*Z&uUn${ojJ{3NWqO>4luJ|<(X4HzaooGg*d z?=Fvk=u;lbEEtJUZ8y{KVAI!O>6C@5y3&j_E;Xe~x+f z)Y*lzh0`l>y8Xl!xjjQn?&;w$C}G(iJi3EfvZO)wqMVUGvZ(4${mec^~p_rahj`XTn|q-N4{@wF~g>xJQXQRC6E z2Z2KIbJNXy52Gm_LX(7_CTdE6 zr^Cr%8tdM`DQvEG!qWKs?lukHfTsbk+~bWSxP(qUoBTO_K5P=-s4&fNy1Mk5Vg<)K z(B(Fa6H?R2&IKf4SO$%wO25}G%Bu&R63*S8P*(902MVmg6wSMUZ<*>4?2YyG{%Os) zg%6@s$R|CYI$JoB@KN|ODPNW3n1#2g03#j(}y^L^mb+GpXK+MtMpamn&S!_W<-~kyBkp z+P;-d$v1;_!5s6h_;mM~0K-}k^*V797ww~`4?r-<8+MXBx@`!=xk(nELmAkrOPR$d}vm5Q)QckN-P zd@N?iAkxh%D_;iBKu|Sl9`(>XJ-9SsFJdm5^`KY!6L&fNC!PB%t=HT#MlGdqbva4% zfQROsL8O^iCRWS!DQCeOPPtg0YX_0$h_Vu{LRcEXItlnUu6FZ+hwipPq?-@E@PIHl$i-yVFDS=pfSc(?+11b11vZn}5Vz-V1|BdnDG6Z-`>rtVu2| zzUh(MZ=YRqdti3S^x5V3GpD5Fw&`R{+F%cF(Lv&^^JmT)VA38EET`SGT5-*d_JW`r{Fz%W z`$3yuP~=b7MH{|Oc&YnelJ<7&O1t)CrnU1OeBBVoIpCS}u9gGsVK~N#-~)=6;~rk# z10P2{yo|jm>ymlde+%;5oMj!FmSx3}e<$ww8yXb$F*XO+N5I35H}NjQKfC1-d?(lP zLSlU4JXDQ|+@k?Rk{@yP4MeINH9JX&uLzd5++G499sKIj>|| z<8d&3AI|f?=in~-ex_B0gU_mQp8q%U`U2>$#dRFV+E=l@`R}m48Am(P`O6I^+pCz9 z=boPDP>uke={-H$f90fg;F`Z(3$v^jaO}tN3J#6|`0gO}wehH1MhBF9m{03Wyw;J? z#k|Mo?3r3#MZ0Fwd(T7fghB6|dKP%#(&;-*<(&>X9t zErQ-%YTKo*QZ_|63tff2NykF$_|M&Zy$3!Idw8Am*v0WW|F%r)B#v`~L)x5YAPXEU|EA}h@*l@_y7E^&ooOus z?HxGge-XA5M?Gk4!*Ome?s4#!r)0#oM?F6e86AKv*z=ZKCUXrL>Gpm(`$nhoC+6Ri zX;tIM!eQf}KM}>5zv00}V$xH36^N_X&hO{GOc}Y!AAWS{RGbbU%+8|T#fS!ld`Py*TVO{0Y24r@Hel=GaOMn%j&?v-5nhky4`>*SQX z5AMI-x3Kokmu`Awc6nJ@$=tG;)63@IdjoTdXU&r2Fc^=7azcH`GKD7dG;+Z;j#6veQNOWlz_#17|z8B$O z?ksNl)Rn(>%1QXODd->it4nXc2VRhEEYF~?ZTD&DUWbS7E1pqP|yycF}L#|{4taPOf7ani z%ize1mo9tO;~2<5lds^nB(SzY`*YRMxzgV{v{GnxI=CD;Sl^a>Tn#=p zU{cM?hS>&>czDQp&*4e)aJjmek8hnc#HG*7rF16U(k_P%WfmAjy2FE2k!B|p1MG6{ ziBc(B#+f&HKLl4RHg8zRZ(O+2l3%xwaD2C}BDo+?rez~d%y}`Z!p9OhdlNY6w1!+9 zbkidXO756_$L(ds(`Vj!C*!xXil@)LeeRs;<#Xl+ZYvMme&@`R;!grn(qKL*C5I1q zd1@3X)A;VkZ~^4x;d&sAgJm|?6w&^3?^r^ZFxW~qdC#&fTeY-oIYAUJq%-{^&b=tV z=N(t#_Pz^C`c1}~Cc-S$%i@i4ew?V)y@0`5LXo7>1ij+Q6=fc{;PRC+Nx_NoRxqoS z2lFl4*u)TZGL7|Ks1iqdCILD3C*S`WDkfLNQX9aJ^i0}q0i2wR^uU>uY{+-6AGqYi z7~nWRl@gyZnRNm%a&E2Ovt>F4kpC}%Cvt< zgU>tRfwH`i4%V1_D8=|TWu55>0N~*mSiXbhk(~Q2R-K$}$)@IzflA z%M8omlWm)2BF^17j9)8u(az$F#{3Qp=TUe_?@mJbtUzWkw4Sn%!DJ+7Ax@l<4;Ao@ zr+aP&Ux8xF(6}h^)`O#6&eciIZbJwG9>zvrTW%ivVv&v`BnSR@>GI;efO9RvIEGx$TuYp;C z;_}(0b7z&phL1D?e&mlbZbV#@@}sP6oJ|+F`<;OX@Ufd4W!gR_W&;0c5RGFWo*R5ix}Qa29C!aVP1{Hu>`M(q5BwI)SO?sKUzFePQ@6R~`dkS(rWL)hG zYSNu+&>^k!#{HGj9hanA;@Wf1^aH(nvap`K{wmv``EGz{lVldH6T^4$t-m#%`}=Xv za?*b4zLfd+F7`-|KvqNc%2&baK>emP+EAJT98CkS`C(tRJXk5-i(Y|ioqi*(iZSj+ ztUabSE5Vyn;{7UTC8#Kk+sd~f?GRs~!D>&Q ziBQ6EJ-3NZzA@>3cbB$9bYdR1kc^+iL;Gmpso{brgQ+AO*NK+4$d6j$eLi?B_+AIr zi>mcABBfT+2{p#x(QU??qlk&gg~=&t)oOIa2K4wzxT(03oibjh#+$KRpDcum_s;Q+ zpJYB0H`m%4<#1p;W6fIur}xakxQ*ZU#0o{R_*6NYTm*|$yj&q(UspU$cHG|RjV zat9ukS$H_I4E@oO9oJ>j*~m@f=t)UBY#YWq&FaM-Q69dN!!^WVd}yKypP+0G^Gl-G z{XeYN_u`|Q{3t1wd~e1S2fv=dPrPFNcP(GKLEXYr0e(%3?>HDT4#%)~TP(m|$y1Up*S{v;U1-89`|`^rC$;v%HRWWwk*{*HcPuOS1EhbQ%>fcRcSk``^0s}3kJd^8@L47Y{3xK5v37mL!692sKqIhs>B`{y&O48?hT=KmDO-{#1 z*{Nc85PsSkUpvMXl@(z4Ngh0XtArv@vJxbmjU2iyE4#%WNRm>8AXbgnqZ=}dYS}D= z32WnKxze<{7(T@Xr%3JomV71%>G$;{_@42skMYM%VFq%2OnRQabT$fE1QyH zbMWf7R0yu#4Al7fj*$z0GP~wVW!9urIY%R(YL~)uaT=zSsi$oVn50@M95!$$pO_8| zzNjU%wWTSIzTV*G73i3y3C6$K(Otj{7jdjeth@@ZRpXxVIsS&{2DvL!xD@;4P)GSQ z$60Ov<&5X}Y+DX3m;7DnIBR&B+7PNtx)+)*=ds8Cx0~+q6m)~9FMVF0oDW0zC?4Vt zL6|SZJ=ZalX1j-`JuJ_F-gD`4VgCG}{A8tg&st!VZAHwJEh%1Dv2-C)Wg7dvGQCTty#!xZfZ`f?*#77h z!ax-^%U*Xwvtg@f|FmySzaNb%`AxdyRv?NN?OGC-vM^p4_C-Q-;;~VEKF!6XgiXOC zpfySfL*LZIhmO4A69$vA)Do6HoSR1hZ7?a}IDRy7HgCix3&e?yUYnZm;Yxa)7sW4g zy7`?2+!d6vEt{0|Pm-f-r2sZ|ED(;J$Sgt+Mx8Jb>=ZWFp=C-Xs1acc<^p~#Yrd3u z5wx1+*U}797efoVOkKYlv`m?aaayt-8yT8Y<#yQ)!HbTkNxnJL@7l4 z_Suz+maCOOIak2v~F>g ztFLWQ*qDn&Q)Rmtnvz}Ajom*@j)#)66uPTuN(4!ocjC${r>nt>#xYPKrM&vuc!?=l zL{&yFJ$isN1vQaIj9H-^QAg=*VHD8#@h(V=;WF^5W#y_vQ9fc4SduXFaAlSaykHy1 z_4ayfkI;<8V*E~UvW2B2CdU&wXt+F)+77 z!&@$0Z-kOxW?elGT5%u$tm2eZtOXBQaS0hp3P5Jyj%OVKYI#tN- zN7RNj7vo7aXQk4K2W#|4bN^r_iJ@pQw_D+ObPC`x4Cy7zP?ck(F@S5wb2XRg!Z$L- zV2b6X3^>=!v1TfmDHsE$#OE0x5f|l8T)b|_S*)Y+z#U41Z=4?Zh?2^A-h;s5E$g^Z zYJhYvCjcZc>2VDW>z6+*7naG!!MVdj7#Fgv1%Ml=X{kY$mc#pi;kEbqe2n)}$>6O` z@80#7WmkRliL1Wy5oEKrwzht_)oQ(?waE78WM%k<4$02S%z(~x1oAV7WMx<@-yfM} zjS_qLQ#ScVUFyA{rQ6nQ4IhSume}cKIieHfpTBWA!4iBuNK64SZ9m4GT0Y+V;~;lA zY6xkwi&-b&F zo!CS zZY`{BSSOW9Jink-MBM2=3$Ov+$P`2;K@N<0_^jcoElG;hqwUb6ML6@|z52Ccwe66V z&Ee&hWwO2RQ!+)sNyCWq1>^ooWLo*${4&G zUvM|2DrHK}Dr9VdADYLVOlNdOe0g4)s${y+zw(pth0XjMR#RdsFDYVRBF(W%yNUS3 zcAZuAk$40SPP86-%$8x#n;6C;3$dwf)TvF$1IO357CsPNkGdoop6VLvKm&KGKfSo>T%64^RCLy{83JY1prwI>2_r!-T=Q zy}YUz%7+mSHZK(G8Fwl@$QtR^gOlT&`zz6d)?0^ISsxmbjH$KbTF2D(7YwnE;5mO2 zf=k2kjgi=c3#Ig%4RzRr=i9;OrV)A8eh;6Mt{xmdJEluMR~HVk)&{_D8a_t^tClV_ zc#DEJ%8TD5isqbAZh0&kpJz?O!JpQtv2R=+%@ILaRQ;xbrh5eC-0z{= zs%Q=d-QkLEB|=JE@~{?2UqE?DcgkpYc@GRC-F!uN1;R@pYw0#YTXD!k_x%)fFQ;F; z7#qInAEgbyTD{|2v5dCLX&~%5^}DI1W8CtYbB%Ma_2Kg4;>Bo~#Z3)Zl3>UliT-oId=JQcti+pfSgjnMI=>?4 zQaZVp`wVJkc|U$Llkc}3aM(j_=A!TE09mlg-_mPc__Ww0#1 zeCdjHix-xLYJ!z@JZg)B(M9*kB7y{2GV(aK^Pv)%OhPnaq`ek+DKfR zKMs0^;*dR=!;=X!vY!r)eFyvdA=Xu@zxVWo3OD=yRQ)=9yJ1zaFceg-!XjJ{d3b1f z;1ZP+7|vIu*#B3C9}YJnk-oxeoOzhA8CJkw8EJ0btTRA02}^s&!-T~SO}PTLxG`EE zl4G7~!0<5PvaEp1N2$eg9wtn-$443I>2PiI|MX3U_9H9eBd_e6d6@WzSb==nZrR?# zwZQM{G!H8L{nZ6fS|g24Z#8Vc9EYW^KW`rD9e-?#ygrTo7~O~%Kc~Y2UR|9Y^Ra9f zke6dh?{oG^7sZ#SQ!7RP$-dg^N5*)_@u=iSbxH>>Heb5V0yyWOFXI>_V<6hYgD!)A z0{JectZYAeJbB*ee^{Oa6Z`f@m(y=fyJLtLPZpF9vHaCTto`d9pSJ_odOSHIxUiz! z=_iO% zRxBx79TdH?2{+S#OVv-B_$5vt(~S$4seX{x+&e;Z?+p3=^Ml@k8$L`JtglEqv9`*@ zw#n;l&2Dlj?b|ZM%6Z(;&-1vhen9qQBZ8%+D^lnuBxpdR|1}Ai);Ay+?Oa#psvLq&ri*=4?OB$jG8(~x6CJ`$~ zB0F5`gUS5M-*o=r1O$Gl7Z>CCkR$6Q$I|rzjDVxwv7cCkGY_+$K%Y8X`1rLYE$hOd ztzbFs=3&C3Ulny2uDv(Xh^4^k`f2p3qa;puankkI=vVV3d?~)DT%WeDM!%Xz6RbH+ z*H5Ew%_W@VGJQXdK5``COfJb|Ey~TqEc;OS9)y(~%9w+;udAgaFdGmd@r2ZPwsJmk_~tydfu@I4T^8j!4j645@1# z=Yzk-8{9meSNvTmkG21BaXiAET(Be!Z#g%*c`KOa++T_O4j8>w7eCkr%5c7)wm#nO9=D=SKICHqbGkqat|OBNR|EnT&!A}9@u ze8zXa5V^qDFpHJJk?HiMmM>h8#4*N;eOfyFLWy3`kye$bfSmp@SCAYS-b&i}bB3W`b|O!!!RPo?^wo~ipWRR7Cb zjz08gm9BvmAI!Jp7UM}&EPe!kS`%q@hmQ6_i$k)6i&Yh3|JM@V;60akM-L~P%5&jwQ3|5 zp*?Mj8+GL!jJBjLZq$`(eH-1VE4vJ&M{E9sAWZU%U;Pm4cBFEs#Vp(3MSI--)He!0 zoDVgJ?%dL6@+}fFk5l%wc@;r>IuzUSTH5nrx%yVgm#w*>31nM0p?3VCJs$b?J%Vk; zFPc-fmCwqZvV)=7ox2pTw(e-dPCV8MDGLR0|0iauVU4~)+C~n$jCN9)Pw^D_Jben; zrTXtI#;0^IdeC*2^r3eZF&48-ulCO8%RS{i#!gr6nBkY%OcvK2@YxS58LDk$*wleC zbd@rQ4MbB6&HWD!vxJ%A;7|wp-$nYLg}pB+Ir16=>+sw7?-Og0=UUUi?7zGSND01` z|H0uF|0w(k9q4~I8GaG}y0W=k0(Bm&w(;Na|HA*|96fR)r9w0RgCnfNU(5A+&l9S0u6E#_;@gVr)wU7=$o!+IMT z5;{4G zAj=|!nH0@A)0vmtz<-o>jQT~_*_gv^%tLL=w0}XigEY>;tx;Cy;!?ep*;r6yW2P_V zivQ#udx^|u8XKs;6Vv+&)!uoMS1ljC$3l7hTEe4z3{}j;FM+SNv%F8hGijf{=MF6I zxpl#h-j7keC-YDqA&Pw(yG++jP1jAfo+GF4z@mEO)Am^Jq~)8D8 zwCDz4DIZ@WMRWbIq9`m0B-Oxw**F;nADBa@6y=MSnYt!WihML4L(dAIzAeuv&U7UU z&2}hGsb3=9(0G$qEwpcl9t&RX(Bb?_T10JrrVc08Fe!UZsYW($?pEX&1Q(h4CbGg; zV#Xs@l>UyDD?X_6bI{4ygudspf6?I4Lq;3)=qt6*DTqWr#IAjFC2rk_*R^Wjy3vh9 zJ8zf2xnSBfl908oRW~*b8{9;qJ+AKmR&!r-B?xj^)0z+ zp>mExd*IlQ=9THzmfV=**rxJBd?ldHwd+r3u^>hi7D{~=*e6Bhh^9j5`x327rgCAp z+sphdct7fG`MpV<`?~m<^b2u)G-eOX7nTxFnwzJxM#{>5XS02SSk`VN`@LY_9)Evd zvweP^3)SUhKXioBwh@(Ps*kc^rm)CzOSJ|~me4bK?o`}J>vqw9l*N3leFLwtHy0J= zDlMV(b01{dWaDRK2l^Sn@8{wxC`*-F^(^5fzeD3J;iH(z?m)i-$ENZVaIAetyM$fj zT74nEX9W{9OzdvR=gl2tm_sX<^5r^M+{MX9z?LVp!`foPH)HI#oinE}uMm5W)YqEk z@)5MfW+ArK731iXLOQJm%hHTkBSK-8`GMMmJCQ=P*fuI6nk6A_GJsov*?T z2hu-FiYxDEZrH|8Va4L7bzW%ZC%tawC-JqPXB79UIKufYD(51ONM_r-CF4)!7FL?O z4dQ+~&WB`2gj?iN3!gx_(VQ}ulC`z4&Vnd&m(e$FWsfeu%@xmkh&|@~z#>r)QASJ! z=!*u5J{7jp(MAB=6|a2D?rPyxfT09*mCTo$f8(_%NF9!H_jfo@tOKkS;UvF?%&W^%Q{Dt2Y%(;-eh947g6_@ovSEyTuCr4FSwNURA<(c!yc=GaX0yEJ+yMmYxB$ATIrbf94f z4WKNi#$f-g(TEZ%ADZI^Rhjv4!3eN|Qv9UhXQ8s5IgjT+Y(qAxEQ=~ZcxiD*!&oT~ zxkqWI9!6XIhum@lxgrD2o3}v*?hQ#GW#@6i{ce-D*sPB|w9OA1;qs{Y(wu-Jc~mTv zE%Ds35|)Y3%Xm@lZ$eeBlq-BLptBw2E>t=XQD2&ewM5w3&$pw3nKnz7!f}dawOlu< zcBs! z{-vRiT}3abl+fIOpS_9^&+D%D7Uv%Mr7PK1DMk6b^@BS{l^WcKp>9ylO|tKv#%t6c zQSXnmwTbU@R(?}`qkKoPwFyR0*x%Qz(CbmBD)p#+H(I!BLVVKM?~SR}by?UrYcu(g zx}kk_*}fW2ewQMhf26*nlI~h5z`M0T*fXB2=2=g62q5K^m5fd>1wEh9yjLUSi-c_Y zvDWfr+mX+-@!I6u5BL18M^Gu>%;&7-^U2CYg;yjbw49qnGJ4p^R15P`n`g(Lzcv{+ zy{O0-2P$RUUyC;DB~O+Lkn+x&R9<8%R3cH-!4!K+SEi81E!LfzBbsDa&X1S7X048g zr}f!Zf!;^r=Vgex%O+2y z&Lau2-omKIeolj4&*G9ZsL_Q|-qT!&BjU3#sRMY~MR_PiqF$fj@veV7*S6e4$V|To23F znCgsNXcF-+^;Zo16$5|8z+W-&R}B0W1J@7(-Gp2JZq$k^DyzE_+cysP$^vQLxI-35 z8{*6=fw1{5EEY(`9q<_dsXCaK$K&2vVEJmXnDi5xfya6+oMA%B^p-+h(0308Jg`rV zR0j3~k=l!W6{L!=-k($r)-<04YW41HQ@%U%Ig5KyfvuSLAyqlti7f@lR6Em$Z3XrM z6LWmna$sMc4?7Fg<@+$b!G~1|)PNEifmI89SUoWF1t0c3;J?L(WdPvL_ves5Ehr6p zZ||OWKL2pf`yYIH;ERLr9{TF=k*1@^_8&iS>NH#SnbPkgw?1E3^fZ1u|75R&D--;Z zH#qOOrS`f%2B`V-*sP={q$ArxRdT9&AYZgjExy~cn~}1%oGnc_K60`0_H3fG_5RwA z`u=g?#PzrK9rFB%6E|;vI_+=MkM(|gjPsEl-Pdb|?$~$HKOi9Bp2W0iq;~Iks>l87 z(|@ad!!>d8UH|y8-1Y+5{J@rvyiQuo0AZ(|;tva@T^&gnZQaQn}aKS{s$`<43_ zo@%1y2$j|2F46F9_KkZhXkT3Ay*Yr2!&M>c=A7aIQi`j>ZM=;O#2D5Qfwy=NBNo3)tXR+4?m$MV! zz0Iz_>m0i{%AKv4`givGywj}jE3=r#tqD16SE#<0R(h7RBD3-i5tWYh-tQJ)6y2)_}e{?u=$i znZcJ_9pVYfpe*lT%LQ!`>DshIyw{u=_1l%?i2IXnj12$OKmCCQjOtGC$4;BJJNH`jS%>NLk7Gok(=~ZS|TpYYryPSlNG7NczW#FIPO$d-olC6AwGT z_k@1j+llMms@UiGkZUtTAk(_AE} znd_=Ils);@FI`17Pjk|ws+N3T{p}53|4_s$AMDlAD9=YHzV-2u{d=nB{h^pA3b~ft z9i0?fw&TGrzbntHMLdy-oNBHzjRrq7OjT^qFSLq@|B^lVlIm|gaMBqy9>%Zh@5j7P zvLCB_qMEUz7hKu9Px3voR~N3!2u%GX5mrexi@*2xD(VfvSgr!|`h0`odi=a5o?j7k z^pPnKuP*6)?-pVHL(s7N?q6qj+w#$7VV3DJplW>2@tbyUtP{_F7xX`cMK8O)G4lR@ z3G;7)ZcD!N#HXuveD=IB|0?JoALLX8EZF;^%x{;(^VQGRkI>CBJh(xaFABQj(;wdW z;L0~&t`p{81ikt~$a~&bzk7MDFkcXK@6wD?&%N80tr6z)f(CY~pO73FarALv{#j7J z12a8`9}1~oA)+{KZARmp>}Z=LEgw=%mK&s~-R6Az?l%XnK9ki0BsA)N0I~OAzb}vt6ab5W<$%UDEW#-~KCI~(FLoAi*?{>ZAQngk zvH=572`mTJ19dnr!q2Ni2k!h1 zbGAt<#_8(1@~4#)xH26bLZR`Aoop0`H-2K+ud)|5Xy}j>$@Zr9XKK^9? zr=NX(;NTZuQpc=?_ZP+c8BIsOK6d=XH{YH-b^5#SfB5mt*>gYreEz~OpI`d*x8E=S zaRnVayYDZG_gAwaR{^jGGKQ=|8r|x{-hsXv`i5CP>|N+9)*{ZKe;@j$Cy<8Fe*k^f zYNXd}ANC>immY`x+t7D~z6f@b(2xBX`hEXI+Qa@Q&>#8-(i{EU{m^f%hJE<;8T8Aa zf_>;ehu#N#BEjbX^g(#e@cbb3XCcFE=)Zv81^PPh{1W;Q?5CIw`-h;Xy%kN+ew?73o=K-{&DEj)XbZ&M=h#euUOm3hB;QirQ%4ZYUmdU)Z7=NIRyPS zfru9$^oo2HWltcw_(!)2=(7b1{VLtILBCcYq-=&)&}J`o9A?6XmCtvY4Rfr(sOkqb zwa_mU2s&VJZGwJ}K-gpZJ;UlDuR!hN-Te&EX9y%-4)d&se!0N2uRY{)8T$PKt~X5Z zp7?Js7AjEtxU>5z=uHBt4OPAepx-8NzUM@@;Qye!!%TQ7Am4j7%n1VZw|?qc5B)NM zRsM%Mu`Ng|mZ? z5@=j<#d90>aoB(ANq?Jp7bv#T%$21s>`4H^05m*9bgv`na3_cHFHC zGvV@}9hxkd;{?((uV~gnUm!~>l{Q-gKL}!oK2E>;@=EeF>OQD}BQ2+i;??cen3w&Pd(n-4$a>7iA9eJ;H>X|pZG^sBz$H}g>;ERg1~Z}N`54ccFvkgu9XQv0J@l0VK{MWDC!uc?SjoD1 zChWo)yaL@zCVEvtKU<&*2fZGGev`nAfx&JejYuDX2NtI}8=y}Uh<)Oq({bpl1-fro z>X*G6;TIS(<};rL=$8s~_h06b^cG}?nb7^*E555>E)wV;{CAh*&~FvEJLP-Vtc-Dy zvyUQA098OWunA}Y)M_T~AvFM1fUS!C(fZjUm?sL{w$k9S7y253M@;wm2Ax423NxW` z$Yzg9m=gpx_bu~21bwYQ=1Z5o63!xy1UmOk^QnQpNTBnSp*~6HP{+Vb__X1W^KzJr z1YRBTrN?pTw+ei{$;Tt^CzNr4sV~0fS_S<~ffX6WZhN7x707zVN2C22vcgO#zrDMr z0OnMI;Ab|lZP2e0IJmCTH|RY2DKHa0*tNx}2<9Y#K3U(oG(f*f;ND(EF4_yQ3p3## zO|?EIm?sMC`mU?fR_NCYyxKd~{WA221fJQnQT|%A{2$?m?cNz2r0#ojE>Ml6-o-2Wd%hvhpJk0wA z#@-g;lJp1U6IgX&l;3jb^#aaM?DA@YzFy#Gzdc^DS0JCjhQ14Zs-VvnFs>+M$D!XO z5I8N}C+#Zi3v}AKkkvzP64+jMpC*oZV|@YI9H7q)eO;tW6oSiB?H+20j@!A#hG#ui@iIB=hqk6j3OA_T)0P z!iyF^=%T=FxP=?>qB!G9V@^`GycJ$qaG;Bw8aqBV*_@Is@!Hf1uW8_wZAUJ9VcgRS zuj$0gj$HP59dCu#P2e>(ZhTy_s&Eq@6)%`9O6FC2WjhWLZj-VU!nBiGH~H5FwjYDBWRs-T&0D_%44Kqx7fy?lQQ?4UeavGc$) zt1#XwIa$JJj~96fi69r)2`9F4X-IgX9LYsqLM|(IwsL7mc%dBeS`Rx@W1`1NyF@8` z=E^~(qjzdvvmaO5j)o_cbbK1`burOkH$EmBYlev_eW0?$7q~uy2SPdGMb^LzT);~R zXv<4O9t!1%*9O=@tXqYV%641AXxl#4z@0iz5L4^82iIruP$;!rw!FxiIvwrt(vXKj zIpXyk>{vxS%=FdLlJ6ncM!^U~&C50&$(mKf!)(h-Lmmp{h}Rz2NzKkOw?Wd4B~QpF zN@~=hUvioHCN_%d8=5}@uNa$Gs{AG2w9e~Yg_o)oQr!L-x!!}*W7ZQNW5rX5xk;pc~RcCR*rd5+|#_IJSRx)wxuv; zA&k6_skRI~$wh8@`O<1ei<$^?tiVsjX`Zuf&Su{WbCtktz6oAIJ*?f&ggH#$o;Szy z9pmaUPJW)m-q=fkN0c`U210>2APvX{3V;e=DL|?kv=-P3>;Vn{CxJ_VM>KczSKLSZOuc{ z9{Df%cVu3oJSppM$)Dm#LZ-EhTlriqkIXN&^#7;4WLq|6cw5R#j_fOC^Xk>2uiRK8 z5PH~+Ed!1Ndrr8q^+#~-nO3aANlXM2I9aCfGUa%TqMq^CREcEOD$b#iC*!R|~88Nz^|LGG+^ zpgXIEeN(hMs|2!RVQ08I+dsmcd0>4_HQ+k7*kD?y)oNWPmlqX*YCSTGbLj3#lRF(N zPUn-jDZ3K1TDR2Fa%@n7os@FywJph8=#rX)&7n|kIAn_e#(7MY_yzbZ_k9`tFTIa;XL}CNq3vebae0E7*sBm_ylhLx7>)`zx;x0Y@^fD4fVwOPpY^~ zY&l*Srp5NJvV3h0P?SFxp}{_59Ie9l_v4P`61*ui6=2hGVIFykeJw5d*B9rDj-Nb> z4$stLi`#;-(vrDhS_5LwNO#s(wB)YE=OH+s6X%0YoRTstC2iW|3D?ZEyAp?D`!`)> zTUJ`cH;%O^gX0%{rr~&oViXJIP8fYYE`?RD zvXu7POuH%!3;A7{{K8HY!8~3r+swntq9`f+(wUCnrwtTEFQ;rCDipC--O9^$8WL`z zvZ3V?;v@im(5W~qAYLmUpa>1rhheW{Q4xI2$Ng}5xF61bksp?sgQLQzY${tj=}y>E zZ4nM6N6F*Y{bHLg3gY~Hy%rbpmb5BwPnofChC(ThVKtx@yrw#*|1_NQVKAV2ga|y~ z$~IjbdQeD5d9)C)mIWOlB{WzrnZvGMx*H5(HEJ`Bbenl*KK<(W9p}0t(zmEuV~7j8 zdDPM20|pbnDtVzpsYS*%+9pkl614WBuMDzU4v0m!Haixj1Daa~9OhBc_7o;>Leynh@+=V*82S%)l%osLvOLY-bXHP1RhOdd=5C)j&H2e1n;7Ce zw(@*!$iVh@Kh%8vg@kF;I^$lDKbiY1EM<_vkZ)kW<{(RkH+B@m5T$d?iwEBhtPM?_ zJkh$>FvJp|)^$O2)Uk4P`PfOB;>_aKowaNuke^LV)mF#w(`r)-^(BK*q4LHS9Yb`A zO}Lo|-C&$;)0TGAM%XUtyjR1|R^kUW^AnV8EVaiNfrI3zC&xJ|r!gxm!^6X=mq&d? z9GWvv%llBusKg=v!MKP>Im@f4gDM=^Gvp%_-q%$6h;+`*9Mp{t#j*EtQXy`TMc2lP zvBFbn`RqAz@GN3dPDNpHd9kYVtK@HeAu3p2k@F@=YoKe*+fdT|m6D?LV0p?o@Vzas zdg6jTG??YcemJp%()1d6UyrjXs1?DP`XxA21w%(YDn%5WVmbo=EsM4sV>I}4M|n<( zsqjwaI1eR`?Kb{{GO09(u8_VopRTqS?2B>U6Sbbaf7h1Kayx@$-$rRqtmL4RJ#dN@ zzagy63G=$V|8 zH;*zlKWWRlVr_1_4NnHh5~3tr$iT?x3N9n14IiRG&i1H&yz-}n6sNq=kU0mZuR&P! zNa(Pg_Nh?Z8f}}uu1U;LO644-+e#%5XXu-9=pO9_n6yBvPcfkUwo%aD>CMk0)Q0BK z6vjY(yP7-UVYn!IB1hv^S97a^k|ldjvPaeiDF@p!L@}O|zW`%I-c(7ZZK$_|XM^l8 zY%XNm`Dw#sS?1Y?SBT0$0A(g#i;K50H!=?0P8v?PiS00l3^BUk?E_7?C`?<8#ZYw* zmP?6o5DhBgxo9SwpW41`)0QBowz8(mzzLtDvb&8xh;x zXBwh2V3s9Mw2su4;*t+KB#~xYPzHG)5z{be4%?=BtMj4AyY0_>D!=+qw;CynDNdR| zqKRWjT5eF;Zi9AEIP1Na2ahjSgBO@wS)Kc;CkX^KU zVcI!GIdhFvCC2jhG`0fY4S4>?=|c8~u0!NU60nSvwQ?x+Lv9$CK5j^uoJB=z)i!mV zilJFXE$7H(Z#T=3RGPC!m@S_-viSYxyt@$JzUrUC&_ghkoiG)*wgu7F)=F}U2#0i?aw=m07ZRPtmo2}4&MqfmU;9H_6L z_-tpKj=m|UtOUaf^t1}fi*rlRK#RUQAB>v^YkWpl8KsF&8ZCGf&LzyRFp0sj(oTUr z^^zhhsE=(a&*iO!SH9;Rboj{U9&II+mfA`)=M@?ZG?Wr;C@;enq1sx>n7!*}IeXuV zrP)GzHI{4>i)`ZnZDrhF)Cv@63;+<3*W+g>|5rv8ZS8{yAGykeYV!P}gA?hC8jxm{=!*b9e-s|IN4ZYT%xb3-$L!-W zi%U!C)5ctz(LB%WC3NyFs>vLz2r$p>mr*>l7|C&&yrM=0#_tPP8l`yZiJ9n=Rv5lA zP(OuhEz|lAK6CY<0Hnwd07x171WFCZ<2k{(e?ql=OfHF5_>E^e8$&oG{_ZpxtV?@`%gbr)zT*@c(wcki{sdQri zjZVxpmr~d*i(e4Q)+#;)YnICK_1!!%$twu-O)wbAFU9((Tr9l8uQE5HZY``CNmHa9 zhRDp;lka>c+n7I>KEFzbEztpX80O-$mmC%FNNmZmz31WP?p%4vypqxdC42;hudL!S z`4aT;FuWhAk3dJvHoojJ?9uGwf@PY{p8Zyzjj$8sIrvD5Be_+rH9lb=8z3goRJ79W z4wZ9XG|gpROrZ20l#F94#HfXf-KunCVW^{d8?0hl*xrd7)+TS;VX03R*6QTQFH+`7 zdny@x)~+3_6syrGr9~)O#W@D$TOrNZh*SIV7->+hT91Q5h#c?Ok687WN8tbsG!5x3 z)fulc$WQt(SLk>e%(oo3z?t&ogr8&I+w!)LRgJCsV%C=6+4BGlEo+v<5;{Ir`ezw3 zd%j%DGpq}xeN(iKm)8H1J{SlEXd}(_0PXD|bOHi_9zZ-m2k(sp=s6uAt#(U?@QnrH z0D2z)bOm|?#FzFOW&mS=&H%j|2$20$pct47Oae%r0RZ`x1kird0^kmS_RZ1ep(J23 zKzzyFLZAZ32hsqtHw(xG41gXWdwBrKMlz5;!vK1w2EFS7P`HzA-jO|WNAKe7yrcKT z)1EuEAOA}|R@=Mc05&KH%9X~+*~Qh(-NVxh`NCOwXMSz<(n;g&?BwF?>gwVGHh;rA z<`UrAxnGpdE%3%1_x^VTMc?z-(;fqoU)kAZ>PKfoVsgutczSj17TmqZz(IpUhYVE? z;)|zq_{OKD-;kL#ZTd~wGj7hq$Hut@g|{0`%v zUi0KL8)}|?Ze#89FTA?B{@?%Evh}~OnGbX_pX6jYgGo7^iB4a_78s3_i;J_1JBh4u zS^zi)xVZL>?5zPA+bjXo+GKb(ERYg zR%KrDr-U}28^sTlB9W7@Jid|&3e-|6fKZfFw8w0V;Qt=c%km1J?l| z0F`kn&vaIOH=qwdWjhR@vQKr#Xn;0ShXaFvzQAB00-#NIRJTywLwjMV{7~7aC`jx8 zu%zAD>oSuku%z_p4gG5ac4zmy>#{MoV0ZS8vEGr3gV$wVHtLGAa@S>U*6G4KcT1j- zTacBzI6HYlztOLS?h3MXpB}wq>}FlR(dp4k29NC7$6osOeBuiMMQ3vN{X{|J??><0 z*iaa9D)Dvg+jrf0ra3ETr{2v-2ygQB?v-~x3`6VL!uDyJm=PyKI*JnOSJ;j;6tn4kSVOuSl|RA*Q= zaoeJMQ#KnPowDO@ZIl1Lu1K_qRu0T$;AEqAIoC^w5OY?|NYTmIa|FgAarp588j-cRjxx{B5_-YYVEg zYxJwKUYz&TEgR>qzRBs$=$|@$Fy@M8XVf|0_pkqBN%E^@k4)aa_#WLWcRW1ljlcE( zy7R{oKlVA??`XhB;Xm~L>Y3aXQ(r3i=WS2tJa*&1ivMw|*Sqn*xxF>w7teP_{pz}V z_=UgU@YGCE+2Zj#}|XX>H67_Q{4{^JkjOT7jA#@=I7=-G5z_% zH8Y;ge|(yZdrD^8y!gM#%OuF3&(E$=5x8!VcVgIb)%}yiwE^ZDfd^OCXf8qDB+$6C z!aFh^`z!^L({Fb#f<8l_bI{u!HP9~;cr)sD=i|^f3Zy4q_0o>U9ialxOuOi$hdxQ* zrQe>QBF5F$D;_9oE+}g*s4H;~3Tje+m=wJm)8bFjyWqJ$YjnbS)u8lT>MKz)6}^_q zsD|{UbJE}lDy~PEX51>A^k%qJUR`JAiWlrQU-; zI`9rK1b7}O1Wp0nfi=Jk;2jf*L`82K^b-8`K;0A<&0FCxA`>eGl|K(Ca|21AP(nMbLSm^FY4? z{SLGzXiw0mK%WA=1@sosFG0Tq9Su4f^mWkJLGJ>+3-ou;-$C6$-9aA!eE@Vk=y=e# zLEi=)3_2KeBj`rZxuA1FzXkmkv>RwQ(AA);L2m-R2^7a8vClzAf{p~;0=fls0q6qI zi=Y=l{XqRd9|e6BbPDJc&<{aB1RVxC4D@BtmqANGOF_?oo&oI(+81;k=sM6@ptC@a zfF1!&1Wg3p0lEY9ZqU0yuYz6$rCwJOpao_F{y+v02^atmAPoow3V>iB8;Aoc02d$? z2m$m!5Re7L0w%y29PR~DfL*`d1fd0AwzCdrl83+Wtft~<%Vj|64Q7dBT;o|D*2IUUrA(W?3UN*|x zM)|a&d|OeSu2K12qe729pmXQIz#ynDP+f)UCRDJE>TaWYw4!>pqIzAU>V1uhr#}rn zJv=?Vyu7_BIVs5~Stx4#g~CMwTy+*IP$-HwwW2swD;b}X%6OI3CVHD0ZKAuGv6blW z(OUFV$6Yy}pUM=KbDU?1Qss}*7mSjpMTw6@DNjOeFdH>NC0oxLSQE;t#ibiu;bsch zr#?v%>{Y@J(^N{<%`I^EmLED8i~gL%pEJrU{ZS8y{*>~J(rQ-HwzNey+;|8m4Ah=_ zA|zfY?{o*d59$FLT{>1252*P9Ix`PCeQ`ExH}v2Y&b>l${V-;!8>cI0NL4p3&Hp z23>apR|&9i6&9|-!c|zf3JX_Z;VLZ9=!o3902DuR{7f|})#@}pr?EJNo_Nz(oyOuc zPNs1;Sn=^SjiqUYNE5ntj(?q&EX4k_FeUJ5JCNAv?|?m!3R0QUep0h)ih2hf0t223(@Cb4OfO0^%3TBsi#qp) zCed4qS_^ZkKuJI^FRjj-?T48##%q+O4(4oug9nC_NRAZ+NNtORq=2=mFwuqf)5XO_ z<4VH+ei7)O+r&1E{8>B-5@$t`3Wnm}?L z8Lne)UhX;%ji-~BGm9`5U=_D1CznNF(my{!mz*AM%9+a|O!)l9BFakXGZ~9eeshgR z7J;u9^vR4x=%!AJ$T8uIKyEp{&b1nFvjs{SN?VGq2IPtcP%R)8vJ_X8FDaTSKQuwt z1p56#$_0v&(NE=*>=8e@PkJBjc0T^TVuyYyX-EwQ$R6c8mVsQ8;l+NKO9n+rd*LvW zp6s2)JwiWh!Fke7lsgqA?a}-i@so<|g-HXbd`I;Kcu{fWs{2+iUZ+U2v}cx0yoiIn K3DOd%wD*53v0iEb literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/CmdLineOptions.pas b/resources/libraries/deskew/CmdLineOptions.pas new file mode 100755 index 0000000..290c8be --- /dev/null +++ b/resources/libraries/deskew/CmdLineOptions.pas @@ -0,0 +1,443 @@ +{ + Deskew + by Marek Mauder + http://galfar.vevb.net/deskew + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +unit CmdLineOptions; + +interface + +uses +{$IFNDEF FPC} + Types, + StrUtils, +{$ENDIF} + SysUtils, + Classes, + ImagingTypes, + ImagingUtility, + ImageUtils; + +const + DefaultThreshold = 128; + DefaultMaxAngle = 10; + DefaultSkipAngle = 0.01; + SDefaultOutputFile = 'out.png'; + +type + TThresholdingMethod = ( + // Use explicit threshold [0..255] + tmExplicit, + // Use adaptive thresholding: Otsu's method + tmOtsu + ); + + TOperationalFlag = ( + ofAutoCrop, + ofDetectOnly + ); + TOperationalFlags = set of TOperationalFlag; + + TCmdLineOptions = class + private + FInputFile: string; + FOutputFile: string; + FMaxAngle: Double; + FSkipAngle: Double; + FResamplingFilter: TResamplingFilter; + FThresholdingMethod: TThresholdingMethod; + FThresholdLevel: Integer; + FContentRect: TRect; + FBackgroundColor: TColor32; + FForcedOutputFormat: TImageFormat; + FOperationalFlags: TOperationalFlags; + FShowStats: Boolean; + FShowParams: Boolean; + FShowTimings: Boolean; + FJpegCompressionQuality: Integer; + FTiffCompressionScheme: Integer; + FFormatSettings: TFormatSettings; + FErrorMessage: string; + function GetIsValid: Boolean; + public + constructor Create; + // Parses command line arguments to get optiosn set by user + function ParseCommnadLine: Boolean; + function OptionsToString: string; + + property InputFile: string read FInputFile; + property OutputFile: string read FOutputFile; + // Max expected rotation angle - algo then works in range [-MaxAngle, MaxAngle] + property MaxAngle: Double read FMaxAngle; + // Skew threshold angle - skip deskewing if detected skew angle is in range (-MinAngle, MinAngle) + property SkipAngle: Double read FSkipAngle; + // Resampling filter used for rotations + property ResamplingFilter: TResamplingFilter read FResamplingFilter; + // Thresholding method used when converting images to binary black/white format + property ThresholdingMethod: TThresholdingMethod read FThresholdingMethod; + // Threshold for black/white pixel classification for explicit thresholding method + property ThresholdLevel: Integer read FThresholdLevel; + // Rect where to do the skew detection on the page image + property ContentRect: TRect read FContentRect; + // Background color for the rotated image + property BackgroundColor: TColor32 read FBackgroundColor; + // Forced output format (applied just before saving the output) + property ForcedOutputFormat: TImageFormat read FForcedOutputFormat; + // On/Off flags that control parts of the whole operation + property OperationalFlags: TOperationalFlags read FOperationalFlags; + // Show skew detection stats + property ShowStats: Boolean read FShowStats; + // Show current params to user (for testing etc.) + property ShowParams: Boolean read FShowParams; + // Show timing of processing steps to user + property ShowTimings: Boolean read FShowTimings; + // Compression quality of JPEG outputs (also embedded) in range [1, 100(best)] + property JpegCompressionQuality: Integer read FJpegCompressionQuality; + // Compression scheme of TIFF outputs. Values and default in imaginglib. + property TiffCompressionScheme: Integer read FTiffCompressionScheme; + + property IsValid: Boolean read GetIsValid; + property ErrorMessage: string read FErrorMessage; + end; + +implementation + +uses + TypInfo, Imaging, ImagingTiff; + +const + TiffCompressionNames: array[TiffCompressionOptionNone..TiffCompressionOptionGroup4] of string = ( + 'none', 'lzw', 'rle', 'deflate', 'jpeg', 'g4' + ); + +{ TCmdLineOptions } + +constructor TCmdLineOptions.Create; +begin + FThresholdLevel := DefaultThreshold; + FMaxAngle := DefaultMaxAngle; + FSkipAngle := DefaultSkipAngle; + FResamplingFilter := rfLinear; + FThresholdingMethod := tmOtsu; + FContentRect := Rect(0, 0, 0, 0); // whole page + FBackgroundColor := $FF000000; + FOutputFile := SDefaultOutputFile; + FOperationalFlags := []; + FShowStats := False; + FShowParams := False; + FShowTimings:= False; + FForcedOutputFormat := ifUnknown; + FJpegCompressionQuality := -1; // use imaginglib default + FTiffCompressionScheme := -1; // use imaginglib default + FFormatSettings := ImagingUtility.GetFormatSettingsForFloats; +end; + +function TCmdLineOptions.GetIsValid: Boolean; +begin + Result := (InputFile <> '') and (MaxAngle > 0) and (SkipAngle >= 0) and + ((ThresholdingMethod in [tmOtsu]) or (ThresholdingMethod = tmExplicit) and (ThresholdLevel > 0)); +end; + +function TCmdLineOptions.ParseCommnadLine: Boolean; +var + I: LongInt; + Param, Arg: string; + + // From Delphi RTL StrUtils.pas - for compiling in Delphi 7 + function SplitString(const S, Delimiters: string): TDynStringArray; + var + StartIdx: Integer; + FoundIdx: Integer; + SplitPoints: Integer; + CurrentSplit: Integer; + I: Integer; + + function FindDelimiter(const Delimiters, S: string; StartIdx: Integer = 1): Integer; + var + Stop: Boolean; + Len: Integer; + begin + Result := 0; + Len := Length(S); + Stop := False; + while (not Stop) and (StartIdx <= Len) do + if IsDelimiter(Delimiters, S, StartIdx) then + begin + Result := StartIdx; + Stop := True; + end + else + Inc(StartIdx); + end; + + begin + Result := nil; + if S <> '' then + begin + SplitPoints := 0; + for I := 1 to Length(S) do + begin + if IsDelimiter(Delimiters, S, I) then + Inc(SplitPoints); + end; + + SetLength(Result, SplitPoints + 1); + StartIdx := 1; + CurrentSplit := 0; + repeat + FoundIdx := FindDelimiter(Delimiters, S, StartIdx); + if FoundIdx <> 0 then + begin + Result[CurrentSplit] := Copy(S, StartIdx, FoundIdx - StartIdx); + Inc(CurrentSplit); + StartIdx := FoundIdx + 1; + end; + until CurrentSplit = SplitPoints; + Result[SplitPoints] := Copy(S, StartIdx, Length(S) - StartIdx + 1); + end; + end; + + function CheckParam(const Param, Value: string): Boolean; + var + StrArray: TDynStringArray; + ValLower, S: string; + TempColor: Cardinal; + Val64: Int64; + I, J: Integer; + begin + Result := True; + ValLower := LowerCase(Value); + + if Param = '-o' then + FOutputFile := Value + else if Param = '-a' then + begin + if not TryStrToFloat(Value, FMaxAngle, FFormatSettings) then + FErrorMessage := 'Invalid value for max angle parameter: ' + Value; + end + else if Param = '-l' then + begin + if not TryStrToFloat(Value, FSkipAngle, FFormatSettings) then + FErrorMessage := 'Invalid value for skip angle parameter: ' + Value; + end + else if Param = '-t' then + begin + if ValLower = 'a' then + FThresholdingMethod := tmOtsu + else + begin + FThresholdingMethod := tmExplicit; + if not TryStrToInt(Value, FThresholdLevel) then + FErrorMessage := 'Invalid value for treshold parameter: ' + Value; + end; + end + else if Param = '-b' then + begin + if TryStrToInt64('$' + ValLower, Val64) then + begin + TempColor := Cardinal(Val64 and $FFFFFFFF); + if TempColor <= $FF then + begin + // Just one channel given, replicate for all channels + opaque + FBackgroundColor := Color32($FF, Byte(TempColor), Byte(TempColor), Byte(TempColor)).Color; + end + else if (TempColor <= $FFFFFF) and (Length(ValLower) <= 6) then + begin + // RGB given, set alpha to 255 for background + FBackgroundColor := $FF000000 or TempColor; + end + else + begin + // Full ARGB given + FBackgroundColor := TempColor; + end; + end + else + FErrorMessage := 'Invalid value for background color parameter: ' + Value; + end + else if Param = '-f' then + begin + if ValLower = 'b1' then + FForcedOutputFormat := ifBinary + else if ValLower = 'g8' then + FForcedOutputFormat := ifGray8 + else if ValLower = 'rgb24' then + FForcedOutputFormat := ifR8G8B8 + else if ValLower = 'rgba32' then + FForcedOutputFormat := ifA8R8G8B8 + else + FErrorMessage := 'Invalid value for format parameter: ' + Value; + end + else if Param = '-q' then + begin + if ValLower = 'nearest' then + FResamplingFilter := rfNearest + else if ValLower = 'linear' then + FResamplingFilter := rfLinear + else if ValLower = 'cubic' then + FResamplingFilter := rfCubic + else if ValLower = 'lanczos' then + FResamplingFilter := rfLanczos + else + FErrorMessage := 'Invalid value for resampling filter parameter: ' + Value; + end + else if Param = '-g' then + begin + if Pos('c', ValLower) > 0 then + Include(FOperationalFlags, ofAutoCrop); + if Pos('d', ValLower) > 0 then + Include(FOperationalFlags, ofDetectOnly); + end + else if Param = '-s' then + begin + if Pos('s', ValLower) > 0 then + FShowStats := True; + if Pos('p', ValLower) > 0 then + FShowParams := True; + if Pos('t', ValLower) > 0 then + FShowTimings := True; + end + else if Param = '-r' then + begin + StrArray := SplitString(ValLower, ','); + if Length(StrArray) = 4 then + begin + FContentRect.Left := StrToInt(StrArray[0]); + FContentRect.Top := StrToInt(StrArray[1]); + FContentRect.Right := StrToInt(StrArray[2]); + FContentRect.Bottom := StrToInt(StrArray[3]); + end; + end + else if Param = '-c' then + begin + StrArray := SplitString(ValLower, ','); + for I := 0 to High(StrArray) do + begin + S := StrArray[I]; + if Pos('t', S) = 1 then + begin + S := Copy(S, 2); + FTiffCompressionScheme := -1; + + for J := Low(TiffCompressionNames) to High(TiffCompressionNames) do + begin + if S = TiffCompressionNames[J] then + begin + FTiffCompressionScheme := J; + Break; + end; + end; + + if FTiffCompressionScheme = -1 then + begin + FErrorMessage := 'Invalid TIFF output compression spec: ' + S; + Exit(False); + end; + end + else if Pos('j', S) = 1 then + begin + S := Copy(S, 2); + if not TryStrToInt(S,FJpegCompressionQuality) then + begin + FErrorMessage := 'Invalid JPEG output compression spec: ' + S; + Exit(False); + end; + end + else + begin + FErrorMessage := 'Invalid output compression parameter: ' + S; + Exit(False); + end; + end; + end + else + begin + FErrorMessage := 'Unknown parameter: ' + Param; + end; + + if FErrorMessage <> '' then + Result := False; + end; + +begin + Result := True; + I := 1; + + while I <= ParamCount do + begin + Param := ParamStr(I); + if Pos('-', Param) = 1 then + begin + Arg := ParamStr(I + 1); + Inc(I); + if not CheckParam(Param, Arg) then + begin + Result := False; + Exit; + end; + end + else + FInputFile := Param; + + Inc(I); + end; + + if FInputFile = '' then + FErrorMessage := 'No input file given'; +end; + +function TCmdLineOptions.OptionsToString: string; +var + I: Integer; + CompJpegStr, CompTiffStr, FilterStr, CmdParams: string; +begin + CmdParams := ''; + for I := 1 to ParamCount do + CmdParams := CmdParams + ParamStr(I) + ' '; + + FilterStr := LowerCase(Copy(TypInfo.GetEnumName(TypeInfo(TResamplingFilter), Integer(FResamplingFilter)), 3)); + CompJpegStr := Iff(JpegCompressionQuality = -1, 'default', IntToStr(JpegCompressionQuality)); + CompTiffStr := 'default'; + if TiffCompressionScheme >= 0 then + CompTiffStr := TiffCompressionNames[TiffCompressionScheme]; + + Result := + 'Parameters: ' + CmdParams + sLineBreak + + ' input file = ' + InputFile + sLineBreak + + ' output file = ' + OutputFile + sLineBreak + + ' max angle = ' + FloatToStr(MaxAngle) + sLineBreak + + ' background color = ' + IntToHex(BackgroundColor, 8) + sLineBreak + + ' resampling filter = ' + FilterStr + sLineBreak + + ' thresholding method = ' + Iff(ThresholdingMethod = tmExplicit, 'explicit', 'auto otsu') + sLineBreak + + ' threshold level = ' + IntToStr(ThresholdLevel) + sLineBreak + + ' content rect = ' + Format('%d,%d,%d,%d', [ContentRect.Left, ContentRect.Top, ContentRect.Right, ContentRect.Bottom]) + sLineBreak + + ' output format = ' + Iff(ForcedOutputFormat = ifUnknown, 'default', Imaging.GetFormatName(ForcedOutputFormat)) + sLineBreak + + ' skip angle = ' + FloatToStr(SkipAngle) + sLineBreak + + ' oper flags = ' + Iff(ofAutoCrop in FOperationalFlags, 'auto-crop ', '') + Iff(ofDetectOnly in FOperationalFlags, 'detect-only ', '') + sLineBreak + + ' show info = ' + Iff(ShowParams, 'params ', '') + Iff(ShowStats, 'stats ', '') + Iff(ShowTimings, 'timings ', '') + sLineBreak + + ' output compression = jpeg:' + CompJpegStr + ' tiff:' + CompTiffStr + sLineBreak; +end; + +end. diff --git a/resources/libraries/deskew/Gui/aboutform.lfm b/resources/libraries/deskew/Gui/aboutform.lfm new file mode 100755 index 0000000..e1413f9 --- /dev/null +++ b/resources/libraries/deskew/Gui/aboutform.lfm @@ -0,0 +1,92 @@ +object FormAbout: TFormAbout + Left = 425 + Height = 209 + Top = 332 + Width = 378 + BorderIcons = [biSystemMenu] + BorderStyle = bsDialog + Caption = 'About' + ClientHeight = 209 + ClientWidth = 378 + Color = clWhite + OnCreate = FormCreate + Position = poMainFormCenter + LCLVersion = '1.8.4.0' + object ImageIcon: TImage + Left = 14 + Height = 128 + Top = 24 + Width = 128 + AntialiasingMode = amOff + Center = True + Proportional = True + Stretch = True + end + object BtnClose: TButton + AnchorSideLeft.Control = Owner + AnchorSideLeft.Side = asrCenter + AnchorSideRight.Control = Owner + AnchorSideRight.Side = asrCenter + AnchorSideBottom.Control = Owner + AnchorSideBottom.Side = asrBottom + Left = 162 + Height = 25 + Top = 174 + Width = 55 + Anchors = [akLeft, akBottom] + AutoSize = True + BorderSpacing.Bottom = 10 + Caption = 'Close' + Default = True + OnClick = BtnCloseClick + TabOrder = 0 + end + object LabTitle: TLabel + Left = 160 + Height = 30 + Top = 24 + Width = 90 + Caption = 'App Title' + Font.Color = 11428096 + Font.Height = 30 + Font.Style = [fsBold] + Layout = tlBottom + ParentColor = False + ParentFont = False + end + object LabVersion: TLabel + AnchorSideLeft.Control = LabTitle + AnchorSideTop.Control = LabTitle + AnchorSideTop.Side = asrBottom + AnchorSideBottom.Control = LabTitle + AnchorSideBottom.Side = asrBottom + Left = 160 + Height = 15 + Top = 54 + Width = 21 + Caption = 'v1.0' + Layout = tlBottom + ParentColor = False + end + object Label1: TLabel + Left = 160 + Height = 15 + Top = 119 + Width = 93 + Caption = 'by Marek Mauder' + ParentColor = False + end + object LabWeb: TLabel + Cursor = crHandPoint + Left = 160 + Height = 15 + Top = 136 + Width = 162 + Caption = 'http://galfar.vevb.net/deskew/' + Font.Color = 16744448 + Font.Style = [fsUnderline] + ParentColor = False + ParentFont = False + OnClick = LabWebClick + end +end diff --git a/resources/libraries/deskew/Gui/aboutform.pas b/resources/libraries/deskew/Gui/aboutform.pas new file mode 100755 index 0000000..a595689 --- /dev/null +++ b/resources/libraries/deskew/Gui/aboutform.pas @@ -0,0 +1,76 @@ +unit AboutForm; + +interface + +uses + Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls, + StdCtrls; + +type + + { TFormAbout } + + TFormAbout = class(TForm) + BtnClose: TButton; + ImageIcon: TImage; + Label1: TLabel; + LabWeb: TLabel; + LabTitle: TLabel; + LabVersion: TLabel; + procedure BtnCloseClick(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure LabWebClick(Sender: TObject); + end; + +var + FormAbout: TFormAbout; + +implementation + +uses + LCLIntf, DataModule, Config; + +{$R *.lfm} + +{ TFormAbout } + +procedure TFormAbout.BtnCloseClick(Sender: TObject); +begin + Close; +end; + +procedure TFormAbout.FormCreate(Sender: TObject); +var + Icon: TIcon; +begin + LabTitle.Caption := Application.Title; + LabVersion.Caption := 'v' + Module.VersionString; + LabWeb.Caption := Config.WebLink; + + if Config.LogoImageResName = '' then + begin + Icon := TIcon.Create; + try + Icon.LoadFromResourceName(HInstance, 'MAINICON'); + ImageIcon.Picture.Assign(Icon); + {$IF Defined(DARWIN)} + ImageIcon.Stretch := False; // Currently broken in Cocoa WS + {$ENDIF} + finally + Icon.Free; + end; + end + else + begin + ImageIcon.Stretch := False; + ImageIcon.Picture.LoadFromResourceName(HInstance, Config.LogoImageResName); + end; +end; + +procedure TFormAbout.LabWebClick(Sender: TObject); +begin + OpenURL(LabWeb.Caption); +end; + +end. + diff --git a/resources/libraries/deskew/Gui/advoptionsform.lfm b/resources/libraries/deskew/Gui/advoptionsform.lfm new file mode 100755 index 0000000..8809586 --- /dev/null +++ b/resources/libraries/deskew/Gui/advoptionsform.lfm @@ -0,0 +1,212 @@ +object FormAdvOptions: TFormAdvOptions + Left = 140 + Height = 322 + Top = 346 + Width = 454 + BorderIcons = [biSystemMenu] + BorderStyle = bsDialog + Caption = 'Advanced Options' + ClientHeight = 322 + ClientWidth = 454 + Color = clWhite + OnCreate = FormCreate + OnDestroy = FormDestroy + Position = poMainFormCenter + LCLVersion = '1.8.4.0' + object Panel1: TPanel + Left = 8 + Height = 306 + Top = 8 + Width = 438 + Align = alClient + BorderSpacing.Around = 8 + BevelOuter = bvNone + ClientHeight = 306 + ClientWidth = 438 + TabOrder = 0 + object LabTitle: TLabel + Left = 0 + Height = 23 + Top = 4 + Width = 438 + Align = alTop + BorderSpacing.Top = 4 + BorderSpacing.Bottom = 4 + Caption = 'Advanced Options ' + Font.Color = 11428096 + Font.Height = 24 + Font.Style = [fsBold] + ParentColor = False + ParentFont = False + end + object LabMaxAngle: TLabel + AnchorSideLeft.Control = LabForcedFormat + AnchorSideTop.Control = LabForcedFormat + AnchorSideTop.Side = asrBottom + Left = 8 + Height = 15 + Top = 72 + Width = 112 + BorderSpacing.Top = 18 + Caption = 'Max. angle [degrees]:' + ParentColor = False + end + object SpinEditMaxAngle: TFloatSpinEdit + AnchorSideLeft.Control = LabMaxAngle + AnchorSideLeft.Side = asrBottom + AnchorSideTop.Control = LabMaxAngle + AnchorSideTop.Side = asrCenter + Left = 128 + Height = 23 + Top = 68 + Width = 64 + BorderSpacing.Left = 8 + DecimalPlaces = 1 + Increment = 1 + MaxValue = 90 + MinValue = 1 + TabOrder = 1 + Value = 10 + end + object LabSkipAngle: TLabel + AnchorSideLeft.Control = LabForcedFormat + AnchorSideTop.Control = LabMaxAngle + AnchorSideTop.Side = asrBottom + Left = 8 + Height = 15 + Top = 105 + Width = 109 + BorderSpacing.Top = 18 + BorderSpacing.Right = 8 + Caption = 'Skip angle [degrees]:' + ParentColor = False + end + object SpinEditSkipAngle: TFloatSpinEdit + AnchorSideLeft.Control = SpinEditMaxAngle + AnchorSideTop.Control = LabSkipAngle + AnchorSideTop.Side = asrCenter + AnchorSideRight.Side = asrBottom + Left = 128 + Height = 23 + Top = 101 + Width = 64 + Increment = 1 + MaxValue = 45 + MinValue = 0 + TabOrder = 2 + Value = 0.01 + end + object LabForcedFormat: TLabel + AnchorSideLeft.Control = Panel1 + AnchorSideTop.Control = LabTitle + AnchorSideTop.Side = asrBottom + Left = 8 + Height = 15 + Top = 39 + Width = 80 + BorderSpacing.Left = 8 + BorderSpacing.Top = 12 + Caption = 'Output format:' + ParentColor = False + end + object ComboOutputFormat: TComboBox + AnchorSideLeft.Control = LabForcedFormat + AnchorSideLeft.Side = asrBottom + AnchorSideTop.Control = LabForcedFormat + AnchorSideTop.Side = asrCenter + Left = 100 + Height = 23 + Top = 35 + Width = 256 + BorderSpacing.Left = 12 + ItemHeight = 15 + Style = csDropDownList + TabOrder = 0 + end + object LabDeskewExe: TLabel + AnchorSideLeft.Control = CheckDefaultExecutable + AnchorSideTop.Control = CheckDefaultExecutable + AnchorSideTop.Side = asrBottom + Left = 8 + Height = 15 + Top = 207 + Width = 102 + BorderSpacing.Top = 12 + Caption = 'Deskew executable:' + ParentColor = False + end + object CheckDefaultExecutable: TCheckBox + AnchorSideLeft.Control = LabForcedFormat + AnchorSideTop.Side = asrCenter + AnchorSideBottom.Side = asrCenter + Left = 8 + Height = 19 + Top = 176 + Width = 181 + Caption = 'Use default Deskew executable' + Checked = True + State = cbChecked + TabOrder = 3 + end + object EdDeskewExePath: TEdit + AnchorSideLeft.Control = LabDeskewExe + AnchorSideLeft.Side = asrBottom + AnchorSideTop.Control = LabDeskewExe + AnchorSideTop.Side = asrCenter + AnchorSideRight.Control = BtnBrowseDeskewExePath + AnchorSideBottom.Side = asrCenter + Left = 118 + Height = 23 + Top = 203 + Width = 233 + Anchors = [akTop, akLeft, akRight] + BorderSpacing.Left = 8 + TabOrder = 4 + end + object BtnBrowseDeskewExePath: TButton + AnchorSideLeft.Side = asrBottom + AnchorSideTop.Control = EdDeskewExePath + AnchorSideTop.Side = asrCenter + AnchorSideRight.Control = Panel1 + AnchorSideRight.Side = asrBottom + AnchorSideBottom.Side = asrBottom + Left = 357 + Height = 25 + Top = 202 + Width = 73 + Action = AtcBrowseDeskewExe + Anchors = [akTop, akRight] + AutoSize = True + BorderSpacing.Left = 6 + BorderSpacing.Right = 8 + TabOrder = 5 + end + object BtnResetOptions: TButton + AnchorSideLeft.Control = Panel1 + AnchorSideBottom.Control = Panel1 + AnchorSideBottom.Side = asrBottom + Left = 8 + Height = 25 + Top = 277 + Width = 108 + Action = ActResetOptions + Anchors = [akLeft, akBottom] + AutoSize = True + BorderSpacing.Left = 8 + BorderSpacing.Bottom = 4 + TabOrder = 6 + end + end + object ActionList: TActionList + left = 216 + top = 8 + object AtcBrowseDeskewExe: TAction + Caption = 'Browse...' + OnExecute = AtcBrowseDeskewExeExecute + end + object ActResetOptions: TAction + Caption = 'Reset Options...' + OnExecute = ActResetOptionsExecute + end + end +end diff --git a/resources/libraries/deskew/Gui/advoptionsform.pas b/resources/libraries/deskew/Gui/advoptionsform.pas new file mode 100755 index 0000000..40379d3 --- /dev/null +++ b/resources/libraries/deskew/Gui/advoptionsform.pas @@ -0,0 +1,126 @@ +unit AdvOptionsForm; + +interface + +uses + Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, + ExtCtrls, Spin, ActnList, Options, Config; + +type + + { TFormAdvOptions } + + TFormAdvOptions = class(TForm) + ActResetOptions: TAction; + AtcBrowseDeskewExe: TAction; + ActionList: TActionList; + BtnBrowseDeskewExePath: TButton; + BtnResetOptions: TButton; + CheckDefaultExecutable: TCheckBox; + ComboOutputFormat: TComboBox; + EdDeskewExePath: TEdit; + LabDeskewExe: TLabel; + LabTitle: TLabel; + LabForcedFormat: TLabel; + LabMaxAngle: TLabel; + LabSkipAngle: TLabel; + Panel1: TPanel; + SpinEditMaxAngle: TFloatSpinEdit; + SpinEditSkipAngle: TFloatSpinEdit; + procedure ActResetOptionsExecute(Sender: TObject); + procedure AtcBrowseDeskewExeExecute(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure FormDestroy(Sender: TObject); + public + procedure ApplyOptions(AOptions: TOptions); + procedure GatherOptions(AOptions: TOptions); + procedure DoIdle; + end; + +var + FormAdvOptions: TFormAdvOptions; + +implementation + +uses + DataModule, MainForm; + +{$R *.lfm} + +{ TFormAdvOptions } + +procedure TFormAdvOptions.FormCreate(Sender: TObject); +begin + ComboOutputFormat.Items.Clear; + ComboOutputFormat.Items.AddObject('Default (usually same as input)', TObject(fofNone)); + ComboOutputFormat.Items.AddObject('1bit black and white', TObject(fofBinary1)); + ComboOutputFormat.Items.AddObject('8bit grayscale', TObject(fofGray8)); + ComboOutputFormat.Items.AddObject('24bit RGB', TObject(fofRgb24)); + ComboOutputFormat.Items.AddObject('32bit RGB + opacity', TObject(fofRgba32)); + ComboOutputFormat.ItemIndex := 0; + + if not Config.ShowDeskewExeOption then + begin + CheckDefaultExecutable.Visible := False; + LabDeskewExe.Visible := False; + EdDeskewExePath.Visible := False; + BtnBrowseDeskewExePath.Visible := False; + Height := EdDeskewExePath.BoundsRect.Bottom; + end; + + ApplyOptions(Module.Options); +end; + +procedure TFormAdvOptions.FormDestroy(Sender: TObject); +begin + GatherOptions(Module.Options); +end; + +procedure TFormAdvOptions.ApplyOptions(AOptions: TOptions); +begin + CheckDefaultExecutable.Checked := AOptions.DefaultExecutable; + EdDeskewExePath.Text := AOptions.CustomExecutablePath; + EdDeskewExePath.SelStart := Length(EdDeskewExePath.Text); + SpinEditMaxAngle.Value := AOptions.MaxAngle; + SpinEditSkipAngle.Value := AOptions.SkipAngle; + ComboOutputFormat.ItemIndex := Integer(AOptions.ForcedOutputFormat); +end; + +procedure TFormAdvOptions.GatherOptions(AOptions: TOptions); +begin + AOptions.MaxAngle := SpinEditMaxAngle.Value; + AOptions.SkipAngle := SpinEditSkipAngle.Value; + AOptions.ForcedOutputFormat := TForcedOutputFormat(PtrUInt(ComboOutputFormat.Items.Objects[ComboOutputFormat.ItemIndex])); + AOptions.DefaultExecutable := CheckDefaultExecutable.Checked; + AOptions.CustomExecutablePath := EdDeskewExePath.Text; +end; + +procedure TFormAdvOptions.DoIdle; +begin + AtcBrowseDeskewExe.Enabled := not CheckDefaultExecutable.Checked; + EdDeskewExePath.Enabled := AtcBrowseDeskewExe.Enabled; +end; + +procedure TFormAdvOptions.AtcBrowseDeskewExeExecute(Sender: TObject); +begin + Module.OpenDialogSingle.Title := 'Select Deskew Binary Executable'; + if Module.OpenDialogSingle.Execute then + begin + EdDeskewExePath.Text := Module.OpenDialogSingle.FileName; + EdDeskewExePath.SelStart := Length(EdDeskewExePath.Text); + end; +end; + +procedure TFormAdvOptions.ActResetOptionsExecute(Sender: TObject); +begin + if Dialogs.QuestionDlg('Reset Options', 'Do you really want to reset the options to default?', + mtConfirmation, [mrOk, 'Reset', mrCancel], 0) = mrOk then + begin + Module.Options.Reset; + ApplyOptions(Module.Options); + FormMain.ApplyOptions(Module.Options); + end; +end; + +end. + diff --git a/resources/libraries/deskew/Gui/config.pas b/resources/libraries/deskew/Gui/config.pas new file mode 100755 index 0000000..bcacb69 --- /dev/null +++ b/resources/libraries/deskew/Gui/config.pas @@ -0,0 +1,24 @@ +unit Config; + +interface + +uses + Interfaces, + MainForm; + +const + ApplicationTitle = 'Deskew GUI'; + WebLink = 'http://galfar.vevb.net/deskew/'; + LogoImageResName = ''; + ShowDeskewExeOption = True; + +procedure AfterMainFormCreation(MainForm: TFormMain); + +implementation + +procedure AfterMainFormCreation(MainForm: TFormMain); +begin +end; + +end. + diff --git a/resources/libraries/deskew/Gui/datamodule.lfm b/resources/libraries/deskew/Gui/datamodule.lfm new file mode 100755 index 0000000..ad0018a --- /dev/null +++ b/resources/libraries/deskew/Gui/datamodule.lfm @@ -0,0 +1,24 @@ +object Module: TModule + OnCreate = DataModuleCreate + OnDestroy = DataModuleDestroy + OldCreateOrder = False + Height = 334 + HorizontalOffset = 613 + VerticalOffset = 84 + Width = 442 + PPI = 96 + object OpenDialogMulti: TOpenDialog + Options = [ofAllowMultiSelect, ofEnableSizing, ofViewDetail, ofAutoPreview] + left = 64 + top = 40 + end + object OpenDialogSingle: TOpenDialog + Options = [ofReadOnly, ofEnableSizing, ofViewDetail, ofAutoPreview] + left = 64 + top = 112 + end + object SelectDirectoryDialog: TSelectDirectoryDialog + left = 64 + top = 192 + end +end diff --git a/resources/libraries/deskew/Gui/datamodule.pas b/resources/libraries/deskew/Gui/datamodule.pas new file mode 100755 index 0000000..941e1f0 --- /dev/null +++ b/resources/libraries/deskew/Gui/datamodule.pas @@ -0,0 +1,117 @@ +unit DataModule; + +{$mode delphi} + +interface + +uses + Classes, SysUtils, FileUtil, Dialogs, ActnList, + // Units needed for file info reading + fileinfo, winpeimagereader, elfreader, machoreader, + // App units + Options; + +type + + { TModule } + + TModule = class(TDataModule) + ActShowAdvOptions: TAction; + OpenDialogMulti: TOpenDialog; + OpenDialogSingle: TOpenDialog; + SelectDirectoryDialog: TSelectDirectoryDialog; + procedure ActShowAdvOptionsExecute(Sender: TObject); + procedure DataModuleCreate(Sender: TObject); + procedure DataModuleDestroy(Sender: TObject); + private + FOptionsFilePath: string; + + procedure SaveOptions; + procedure LoadOptions; + procedure ReadVersionInfo; + public + Options: TOptions; + VersionString: string; + end; + +var + Module: TModule; + +implementation + +uses + IniFiles, Forms, ImagingUtility, AdvOptionsForm, Utils, Config; + +{$R *.lfm} + +const + SOptionsFileName = 'deskewgui.ini'; + +{ TModule } + +procedure TModule.DataModuleCreate(Sender: TObject); +begin + Application.Title := Config.ApplicationTitle; + + ReadVersionInfo; + // Prefers "portable mode": config in the folder as exe if it is writable, + // standard OS location otherwise. + FOptionsFilePath := ConcatPaths([Utils.DetermineConfigFolder, SOptionsFileName]); + + Options := TOptions.Create; + LoadOptions; +end; + +procedure TModule.DataModuleDestroy(Sender: TObject); +begin + SaveOptions; + Options.Free; +end; + +procedure TModule.LoadOptions; +var + Ini: TIniFile; +begin + Ini := TIniFile.Create(FOptionsFilePath, [ifoFormatSettingsActive]); + try + Options.LoadFromIni(Ini); + finally + Ini.Free; + end; +end; + +procedure TModule.SaveOptions; +var + Ini: TIniFile; +begin + Ini := TIniFile.Create(FOptionsFilePath, [ifoFormatSettingsActive]); + try + Options.SaveToIni(Ini); + finally + Ini.Free; + end; +end; + +procedure TModule.ReadVersionInfo; +var + FileVerInfo: TFileVersionInfo; +begin + FileVerInfo := TFileVersionInfo.Create(nil); + try + FileVerInfo.ReadFileInfo; + VersionString := FileVerInfo.VersionStrings.Values['FileVersion']; + VersionString := Copy(VersionString, 1, PosEx('.', VersionString, 3) - 1); + finally + FileVerInfo.Free; + end; +end; + +procedure TModule.ActShowAdvOptionsExecute(Sender: TObject); +begin + FormAdvOptions.ShowModal; +end; + + + +end. + diff --git a/resources/libraries/deskew/Gui/deskewgui.icns b/resources/libraries/deskew/Gui/deskewgui.icns new file mode 100755 index 0000000000000000000000000000000000000000..c3f8d8370a07fec0ff14fe2f0d615cfb558d62a1 GIT binary patch literal 3543 zcmeH|-)j_C6vxla{!Y!}HV=jrAH+OprB=2e7A&SxkSmA;AH+QP;362@20{?>IGb!u z6QLzkg|>uJl)NY<_^611LL*3g3IvG-4Pp>N7=ue)Hq7bw+?{o1oarmpmnyj-6 z1tK+#OC~s7U-(rd56H11Hcdl|bw^0p`a16iL>8g+_uKO$jHVKrU8)M~_Dnyq|#0rBn zPJo=QSDAj6OaAsr#&y^B5UHC#Fr5SFtp#9lJ0|+L%4!t>X%hF71b#>IPL)$Lh*nte z0dDf&QG7&s6C0~vV|B9bqa2d`lEAgMHyuuITu`{ivBO2L9KiAIw|4e*XOQ>Ig3Fd1*GsS-m=d zvgG+?>h0{A?oaMr@$HyrctY@m-g!c@YCYB$|0G}6JheeHczl^ z^TbR%=7|aRz9&QxPoxYyF?JYFG(6GoJkhdjdZKmUiL6_mXmrFq(c12b*6>7Yn)s!ZGJ^eK4Uq)Xv+5^sK4p@*S5Sjqlt~#dWE@5|uaE`-x}- z7qh#zFMHqN;%_*7O})51dlz<;sJG^G>RVa6(WR#3!_hxPYS-RRse5^p+g*Ov-`RQb zIQ2C~IR{){T6(@WIWvZ|SC}@(xD_7mOLz*0r$~7G_9z_Q)S>WFhr>%94li{$e3J*^ zO@BB%MZyCI;VBv(UxV9zy7FcvFYMOC1g`bvV4#;qXl!gg5=+@H!G6I0&zs f;me1vmV|GqcyMwLY`gtXZ?>2oWNFh_p0ewQ1sS zpAdftA$(Sg_x3O$_N;!E#j`|2x%GhWM)W^P5Bw$mp|5;`b?aAR!&b1w_+Is%pPy-I zX^9#e8!0(CnWCekX?1ngtGb_Du=v;4*Hli@ z-`?I*RaKQ?H!d!YIy*aQd3l-k_V#FYc9!bv>nSue)R03g9vvMSw!OUQ_#-1DhFxGU zEG*F5+nZtP{{EiY+uIEv!C#0EH_~x*8RkDdJyAtPh4D@JZaX&XA`VMRN(}kh+FE*f zd0`QwWgLTPy)ggg=7zGevXninH+&w5=H})IavUe{*Y57FqYC5L2bqt(=JWZKEyNnu zI}pJKxw*MY2lE&h7;sc!9Qz>i?ca~}M67*8EiEkuKKAG1Ud-<|YrTmT?s0S4Zc=cf6KpVQXV!!>*#)r6Y$yYNv1m6erIS63HdPasx6pT%druWIFFg82C=T(%Pg z^nY5^+9Ure;eQJ2?+W=3#qa)4;Z`H_0`l5_)Uv$2g&q^~F>)}Tv$HeG&d!!+zkb%R zEVrzW^8)83&Ja8u9UU@1SZ*NwtYAHEp|95_Tn_*{K0aRNJ3ZI49=Fg(eap2nuu#u{ zH#0LM&wAKI?qcFO;~jmj&v6E>tgNt@oEO)CdhKi1XME`69Mfrhd|ZBmet)p*>$RqS zF9LsXa8TuZ+|5CDeeh7Tp{B*%qOY%yc6N3MXJ%?@D&^i~WMLyCBZH=zVTWr1)P2Xt$81{OKVcKK kS0H_*VCx_zpbHy;^fBh;v6=Vu`ta~z+@FM4`15z3p9Y0{A^-pY literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Gui/deskewgui.lpi b/resources/libraries/deskew/Gui/deskewgui.lpi new file mode 100755 index 0000000..51f968d --- /dev/null +++ b/resources/libraries/deskew/Gui/deskewgui.lpi @@ -0,0 +1,236 @@ + + + + + + + + + + <ResourceType Value="res"/> + <UseXPManifest Value="True"/> + <XPManifest> + <TextName Value="GalfarsLair.DeskewGui"/> + <TextDesc Value="Deskew GUI"/> + </XPManifest> + <Icon Value="0"/> + </General> + <VersionInfo> + <UseVersionInfo Value="True"/> + <MinorVersionNr Value="90"/> + <StringTable CompanyName="Galfar's Lair" FileDescription="Deskew GUI" ProductName="Deskew GUI"/> + </VersionInfo> + <BuildModes Count="3"> + <Item1 Name="Debug" Default="True"/> + <Item2 Name="Release"> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="deskewgui"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <OtherUnitFiles Value="..\Imaging"/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Parsing> + <SyntaxOptions> + <SyntaxMode Value="Delphi"/> + </SyntaxOptions> + </Parsing> + <CodeGeneration> + <SmartLinkUnit Value="True"/> + <Optimizations> + <OptimizationLevel Value="3"/> + </Optimizations> + </CodeGeneration> + <Linking> + <Debugging> + <GenerateDebugInfo Value="False"/> + </Debugging> + <LinkSmart Value="True"/> + <Options> + <Win32> + <GraphicApplication Value="True"/> + </Win32> + </Options> + </Linking> + </CompilerOptions> + </Item2> + <Item3 Name="Release-macOS"> + <MacroValues Count="1"> + <Macro1 Name="LCLWidgetType" Value="cocoa"/> + </MacroValues> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="deskewgui"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <OtherUnitFiles Value="..\Imaging"/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Parsing> + <SyntaxOptions> + <SyntaxMode Value="Delphi"/> + </SyntaxOptions> + </Parsing> + <CodeGeneration> + <SmartLinkUnit Value="True"/> + <TargetCPU Value="x86_64"/> + <TargetOS Value="darwin"/> + <Optimizations> + <OptimizationLevel Value="3"/> + </Optimizations> + </CodeGeneration> + <Linking> + <Debugging> + <GenerateDebugInfo Value="False"/> + </Debugging> + <LinkSmart Value="True"/> + <Options> + <Win32> + <GraphicApplication Value="True"/> + </Win32> + </Options> + </Linking> + </CompilerOptions> + </Item3> + <SharedMatrixOptions Count="5"> + <Item1 ID="734952752827" Modes="Release-macOS" Type="IDEMacro" MacroName="LCLWidgetType" Value="cocoa"/> + <Item2 ID="088075076274" Modes="Release-macOS"/> + <Item3 ID="382385632331" Modes="Release-macOS" Value="-WM10.9"/> + <Item4 ID="590039250505" Modes="Debug" Value="-dDEBUG"/> + <Item5 ID="453512566522" Modes="Debug,Release,Release-macOS" Value="-dDONT_LINK_FILE_FORMATS"/> + </SharedMatrixOptions> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + <RunParams> + <FormatVersion Value="2"/> + <Modes Count="0"/> + </RunParams> + <RequiredPackages Count="2"> + <Item1> + <PackageName Value="FCL"/> + </Item1> + <Item2> + <PackageName Value="LCL"/> + </Item2> + </RequiredPackages> + <Units Count="9"> + <Unit0> + <Filename Value="deskewgui.lpr"/> + <IsPartOfProject Value="True"/> + </Unit0> + <Unit1> + <Filename Value="mainform.pas"/> + <IsPartOfProject Value="True"/> + <ComponentName Value="FormMain"/> + <HasResources Value="True"/> + <ResourceBaseClass Value="Form"/> + <UnitName Value="MainForm"/> + </Unit1> + <Unit2> + <Filename Value="..\Imaging\ImagingUtility.pas"/> + <IsPartOfProject Value="True"/> + </Unit2> + <Unit3> + <Filename Value="runner.pas"/> + <IsPartOfProject Value="True"/> + <UnitName Value="Runner"/> + </Unit3> + <Unit4> + <Filename Value="utils.pas"/> + <IsPartOfProject Value="True"/> + <UnitName Value="Utils"/> + </Unit4> + <Unit5> + <Filename Value="options.pas"/> + <IsPartOfProject Value="True"/> + <UnitName Value="Options"/> + </Unit5> + <Unit6> + <Filename Value="advoptionsform.pas"/> + <IsPartOfProject Value="True"/> + <ComponentName Value="FormAdvOptions"/> + <HasResources Value="True"/> + <ResourceBaseClass Value="Form"/> + <UnitName Value="AdvOptionsForm"/> + </Unit6> + <Unit7> + <Filename Value="datamodule.pas"/> + <IsPartOfProject Value="True"/> + <ComponentName Value="Module"/> + <HasResources Value="True"/> + <ResourceBaseClass Value="DataModule"/> + <UnitName Value="DataModule"/> + </Unit7> + <Unit8> + <Filename Value="aboutform.pas"/> + <IsPartOfProject Value="True"/> + <ComponentName Value="FormAbout"/> + <HasResources Value="True"/> + <ResourceBaseClass Value="Form"/> + <UnitName Value="AboutForm"/> + </Unit8> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="deskewgui"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <OtherUnitFiles Value="..\Imaging"/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Parsing> + <SyntaxOptions> + <SyntaxMode Value="Delphi"/> + <IncludeAssertionCode Value="True"/> + </SyntaxOptions> + </Parsing> + <CodeGeneration> + <Checks> + <IOChecks Value="True"/> + <RangeChecks Value="True"/> + <OverflowChecks Value="True"/> + <StackChecks Value="True"/> + </Checks> + <VerifyObjMethodCallValidity Value="True"/> + </CodeGeneration> + <Linking> + <Debugging> + <DebugInfoType Value="dsDwarf2Set"/> + <UseHeaptrc Value="True"/> + <TrashVariables Value="True"/> + <UseExternalDbgSyms Value="True"/> + </Debugging> + <Options> + <Win32> + <GraphicApplication Value="True"/> + </Win32> + </Options> + </Linking> + </CompilerOptions> + <Debugging> + <Exceptions Count="3"> + <Item1> + <Name Value="EAbort"/> + </Item1> + <Item2> + <Name Value="ECodetoolError"/> + </Item2> + <Item3> + <Name Value="EFOpenError"/> + </Item3> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/resources/libraries/deskew/Gui/deskewgui.lpr b/resources/libraries/deskew/Gui/deskewgui.lpr new file mode 100755 index 0000000..fe15fd5 --- /dev/null +++ b/resources/libraries/deskew/Gui/deskewgui.lpr @@ -0,0 +1,28 @@ +program deskewgui; + +uses + {$IFDEF UNIX} + {$IFDEF UseCThreads} + cthreads, + {$ENDIF} + clocale, + {$ENDIF} + Interfaces, // this includes the LCL widgetset + Forms, + { you can add units after this } + DataModule, MainForm, AdvOptionsForm, AboutForm, Runner, Utils, Options, + Config; + +{$R *.res} + +begin + Application.Title := 'Deskew GUI'; + RequireDerivedFormResource:=True; + Application.Initialize; + Application.CreateForm(TModule, Module); + Application.CreateForm(TFormMain, FormMain); + Application.CreateForm(TFormAdvOptions, FormAdvOptions); + Application.CreateForm(TFormAbout, FormAbout); + Application.Run; +end. + diff --git a/resources/libraries/deskew/Gui/mainform.lfm b/resources/libraries/deskew/Gui/mainform.lfm new file mode 100755 index 0000000..7d670f6 --- /dev/null +++ b/resources/libraries/deskew/Gui/mainform.lfm @@ -0,0 +1,441 @@ +object FormMain: TFormMain + Left = 637 + Height = 740 + Top = 231 + Width = 521 + AllowDropFiles = True + Caption = 'Deskew GUI' + ClientHeight = 740 + ClientWidth = 521 + Color = clWhite + Constraints.MinHeight = 520 + Constraints.MinWidth = 520 + OnCreate = FormCreate + OnDestroy = FormDestroy + OnDropFiles = FormDropFiles + Position = poWorkAreaCenter + SessionProperties = 'Position' + LCLVersion = '1.8.4.0' + object Notebook: TNotebook + Left = 8 + Height = 724 + Top = 8 + Width = 505 + PageIndex = 0 + Align = alClient + BorderSpacing.Around = 8 + TabOrder = 0 + object PageIn: TPage + Color = clWhite + object BtnDeskew: TButton + Left = 0 + Height = 57 + Top = 667 + Width = 505 + Action = ActDeskew + Align = alBottom + Anchors = [akLeft] + Font.Height = -32 + ParentFont = False + TabOrder = 2 + end + object PanelFiles: TPanel + Left = 0 + Height = 465 + Top = 0 + Width = 505 + Align = alClient + BevelOuter = bvNone + ClientHeight = 465 + ClientWidth = 505 + TabOrder = 0 + object Label1: TLabel + Left = 0 + Height = 23 + Top = 4 + Width = 505 + Align = alTop + BorderSpacing.Top = 4 + BorderSpacing.Bottom = 10 + Caption = 'Input Files' + Font.Color = 11428096 + Font.Height = 24 + Font.Style = [fsBold] + ParentColor = False + ParentFont = False + end + object MemoFiles: TMemo + Left = 0 + Height = 374 + Top = 37 + Width = 505 + Align = alClient + BorderSpacing.Bottom = 54 + ScrollBars = ssAutoBoth + TabOrder = 0 + WordWrap = False + end + object BtnAddFiles: TButton + Left = 0 + Height = 37 + Top = 417 + Width = 103 + Action = ActAddFiles + Anchors = [akLeft, akBottom] + Default = True + Font.Height = -16 + ParentFont = False + TabOrder = 1 + end + object BtnClear: TButton + Left = 402 + Height = 37 + Top = 417 + Width = 103 + Action = ActClearFiles + Anchors = [akRight, akBottom] + Font.Height = -16 + ParentFont = False + TabOrder = 2 + end + object BtnAbout: TButton + Left = 468 + Height = 25 + Top = 0 + Width = 37 + Action = ActShowAbout + Anchors = [akTop, akRight] + AutoSize = True + TabOrder = 3 + TabStop = False + end + end + object PanelOptions: TPanel + Left = 0 + Height = 182 + Top = 471 + Width = 505 + Align = alBottom + AutoSize = True + BorderSpacing.Top = 6 + BorderSpacing.Bottom = 14 + BevelOuter = bvNone + ClientHeight = 182 + ClientWidth = 505 + TabOrder = 1 + object Label2: TLabel + Left = 0 + Height = 23 + Top = 4 + Width = 505 + Align = alTop + BorderSpacing.Top = 4 + BorderSpacing.Bottom = 4 + Caption = 'Options && Parameters' + Font.Color = 11428096 + Font.Height = 24 + Font.Style = [fsBold] + ParentColor = False + ParentFont = False + end + object Panel1: TPanel + Left = 0 + Height = 143 + Top = 31 + Width = 505 + Align = alClient + BorderSpacing.Bottom = 8 + BevelOuter = bvNone + ClientHeight = 143 + ClientWidth = 505 + TabOrder = 0 + object ColorBtnBackground: TColorButton + AnchorSideLeft.Control = LabBackColor + AnchorSideLeft.Side = asrBottom + AnchorSideTop.Control = LabBackColor + AnchorSideTop.Side = asrCenter + AnchorSideBottom.Side = asrCenter + Left = 117 + Height = 28 + Top = 115 + Width = 64 + BorderSpacing.Left = 12 + BorderWidth = 2 + ButtonColorSize = 16 + ButtonColor = clWhite + Flat = True + end + object LabOptOutputFolder: TLabel + AnchorSideTop.Control = CheckDefaultOutputFileOptions + AnchorSideTop.Side = asrBottom + Left = 8 + Height = 15 + Top = 43 + Width = 75 + BorderSpacing.Top = 12 + Caption = 'Output folder:' + ParentColor = False + end + object LabBackColor: TLabel + Left = 8 + Height = 15 + Top = 122 + Width = 97 + Caption = 'Background color:' + ParentColor = False + end + object CheckDefaultOutputFileOptions: TCheckBox + Left = 8 + Height = 19 + Top = 12 + Width = 180 + Caption = 'Use default output file options' + Checked = True + State = cbChecked + TabOrder = 0 + end + object LabOptFileFormat: TLabel + AnchorSideTop.Control = LabOptOutputFolder + AnchorSideTop.Side = asrBottom + Left = 8 + Height = 15 + Top = 76 + Width = 60 + BorderSpacing.Top = 18 + Caption = 'File format:' + ParentColor = False + end + object ComboFileFormat: TComboBox + AnchorSideLeft.Control = EdDirOutput + AnchorSideTop.Control = LabOptFileFormat + AnchorSideTop.Side = asrCenter + AnchorSideBottom.Side = asrCenter + Left = 95 + Height = 23 + Top = 72 + Width = 256 + ItemHeight = 15 + Style = csDropDownList + TabOrder = 3 + end + object EdDirOutput: TEdit + AnchorSideLeft.Control = LabOptOutputFolder + AnchorSideLeft.Side = asrBottom + AnchorSideTop.Control = LabOptOutputFolder + AnchorSideTop.Side = asrCenter + AnchorSideRight.Control = BtnBrowseOutputDir + Left = 95 + Height = 23 + Top = 39 + Width = 323 + Anchors = [akTop, akLeft, akRight] + BorderSpacing.Left = 12 + BorderSpacing.Right = 4 + TabOrder = 1 + end + object BtnBrowseOutputDir: TButton + AnchorSideLeft.Side = asrBottom + AnchorSideTop.Control = EdDirOutput + AnchorSideTop.Side = asrCenter + AnchorSideRight.Control = Panel1 + AnchorSideRight.Side = asrBottom + AnchorSideBottom.Side = asrCenter + Left = 424 + Height = 25 + Top = 38 + Width = 73 + Action = ActBrowseOutputDir + Anchors = [akTop, akRight] + AutoSize = True + BorderSpacing.Left = 6 + BorderSpacing.Right = 8 + TabOrder = 2 + end + object BtnAdvOptions: TButton + AnchorSideTop.Control = LabBackColor + AnchorSideTop.Side = asrCenter + AnchorSideRight.Control = BtnBrowseOutputDir + AnchorSideRight.Side = asrBottom + Left = 396 + Height = 25 + Top = 117 + Width = 101 + Action = ActShowAdvOptions + Anchors = [akTop, akRight] + TabOrder = 4 + end + end + end + end + object PageOut: TPage + object PanelProgress: TPanel + Left = 0 + Height = 106 + Top = 0 + Width = 505 + Align = alTop + BevelOuter = bvNone + ClientHeight = 106 + ClientWidth = 505 + TabOrder = 0 + object LabDeskewProgressTitle: TLabel + Left = 0 + Height = 28 + Top = 4 + Width = 505 + Align = alTop + BorderSpacing.Top = 4 + BorderSpacing.Bottom = 4 + Caption = 'Deskew in Progress' + Font.Color = 11428096 + Font.Height = 24 + Font.Style = [fsBold] + ParentColor = False + ParentFont = False + end + object ProgressBar: TProgressBar + Left = 8 + Height = 20 + Top = 76 + Width = 489 + Align = alBottom + BorderSpacing.Left = 8 + BorderSpacing.Top = 8 + BorderSpacing.Right = 8 + BorderSpacing.Bottom = 10 + Max = 10 + ParentColor = False + Position = 4 + Smooth = True + TabOrder = 0 + end + object LabProgressTitle: TLabel + Left = 8 + Height = 16 + Top = 52 + Width = 75 + BorderSpacing.Left = 8 + BorderSpacing.Right = 4 + Caption = 'Current file:' + Layout = tlBottom + ParentColor = False + end + object LabCurrentFile: TLabel + AnchorSideLeft.Control = LabProgressTitle + AnchorSideLeft.Side = asrBottom + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = PanelProgress + AnchorSideRight.Side = asrBottom + AnchorSideBottom.Control = LabProgressTitle + AnchorSideBottom.Side = asrBottom + Left = 88 + Height = 18 + Top = 50 + Width = 417 + Anchors = [akLeft, akRight, akBottom] + AutoSize = False + BorderSpacing.Left = 5 + Caption = 'FileName.ext [22/152]' + Font.Height = 16 + Font.Style = [fsBold] + Layout = tlBottom + ParentColor = False + ParentFont = False + OptimalFill = True + end + end + object PanelOut: TPanel + Left = 0 + Height = 595 + Top = 106 + Width = 505 + Align = alClient + BorderSpacing.Bottom = 18 + BevelOuter = bvNone + ClientHeight = 595 + ClientWidth = 505 + TabOrder = 1 + object Label3: TLabel + Left = 0 + Height = 28 + Top = 4 + Width = 505 + Align = alTop + BorderSpacing.Top = 4 + BorderSpacing.Bottom = 10 + Caption = 'Output Log' + Font.Color = 11428096 + Font.Height = 24 + Font.Style = [fsBold] + ParentColor = False + ParentFont = False + end + object MemoOutput: TMemo + Left = 0 + Height = 553 + Top = 42 + Width = 505 + Align = alClient + Font.Height = -12 + Font.Name = 'Courier New' + ParentFont = False + ReadOnly = True + ScrollBars = ssAutoBoth + TabOrder = 0 + WantReturns = False + WordWrap = False + end + end + object BtnFinish: TButton + Left = 0 + Height = 57 + Top = 719 + Width = 505 + Action = ActFinish + Align = alBottom + Anchors = [akLeft] + Font.Height = -32 + ParentFont = False + TabOrder = 2 + end + end + end + object ApplicationProperties: TApplicationProperties + OnIdle = ApplicationPropertiesIdle + left = 176 + top = 200 + end + object ActionList: TActionList + left = 280 + top = 200 + object ActDeskew: TAction + Caption = 'Deskew!' + OnExecute = ActDeskewExecute + OnUpdate = ActDeskewUpdate + end + object ActFinish: TAction + Caption = 'Stop' + OnExecute = ActFinishExecute + end + object ActAddFiles: TAction + Caption = 'Add files...' + OnExecute = ActAddFilesExecute + end + object ActClearFiles: TAction + Caption = 'Clear' + OnExecute = ActClearFilesExecute + end + object ActBrowseOutputDir: TAction + Caption = 'Browse...' + OnExecute = ActBrowseOutputDirExecute + end + object ActShowAdvOptions: TAction + Caption = 'Advanced...' + OnExecute = ActShowAdvOptionsExecute + end + object ActShowAbout: TAction + Caption = ' ? ' + OnExecute = ActShowAboutExecute + end + end +end diff --git a/resources/libraries/deskew/Gui/mainform.pas b/resources/libraries/deskew/Gui/mainform.pas new file mode 100755 index 0000000..96efcf5 --- /dev/null +++ b/resources/libraries/deskew/Gui/mainform.pas @@ -0,0 +1,284 @@ +unit MainForm; + +interface + +uses + Classes, SysUtils, FileUtil, Forms, + Controls, Graphics, Dialogs, ExtCtrls, StdCtrls, + ComCtrls, ActnList, + // App units + Runner, Options; + +type + + { TFormMain } + + TFormMain = class(TForm) + ActDeskew: TAction; + ActFinish: TAction; + ActAddFiles: TAction; + ActClearFiles: TAction; + ActBrowseOutputDir: TAction; + ActShowAbout: TAction; + ActShowAdvOptions: TAction; + ActionList: TActionList; + ApplicationProperties: TApplicationProperties; + BtnAddFiles: TButton; + BtnDeskew: TButton; + BtnClear: TButton; + BtnFinish: TButton; + BtnBrowseOutputDir: TButton; + BtnAdvOptions: TButton; + BtnAbout: TButton; + CheckDefaultOutputFileOptions: TCheckBox; + ColorBtnBackground: TColorButton; + ComboFileFormat: TComboBox; + EdDirOutput: TEdit; + Label1: TLabel; + Label2: TLabel; + Label3: TLabel; + LabOptOutputFolder: TLabel; + LabBackColor: TLabel; + LabDeskewProgressTitle: TLabel; + LabOptFileFormat: TLabel; + LabProgressTitle: TLabel; + LabCurrentFile: TLabel; + MemoOutput: TMemo; + MemoFiles: TMemo; + Notebook: TNotebook; + PageIn: TPage; + PageOut: TPage; + Panel1: TPanel; + PanelProgress: TPanel; + PanelOut: TPanel; + PanelFiles: TPanel; + PanelOptions: TPanel; + ProgressBar: TProgressBar; + procedure ActAddFilesExecute(Sender: TObject); + procedure ActBrowseOutputDirExecute(Sender: TObject); + procedure ActClearFilesExecute(Sender: TObject); + procedure ActDeskewExecute(Sender: TObject); + procedure ActDeskewUpdate(Sender: TObject); + procedure ActFinishExecute(Sender: TObject); + procedure ActShowAboutExecute(Sender: TObject); + procedure ActShowAdvOptionsExecute(Sender: TObject); + procedure ApplicationPropertiesIdle(Sender: TObject; var Done: Boolean); + procedure FormCreate(Sender: TObject); + procedure FormDestroy(Sender: TObject); + procedure FormDropFiles(Sender: TObject; const FileNames: array of String); + private + FRunner: TRunner; + + procedure RunnerFinished(Sender: TObject; Reason: TFinishReason); + procedure RunnerProgress(Sender: TObject; Index: Integer); + + procedure GatherOptions(AOptions: TOptions); + public + procedure ApplyOptions(AOptions: TOptions); + end; + +var + FormMain: TFormMain; + +implementation + +{$R *.lfm} + +uses + ImagingUtility, Imaging, DataModule, AdvOptionsForm, AboutForm, Config; + +{ TFormMain } + +procedure TFormMain.FormCreate(Sender: TObject); +begin + FRunner := TRunner.Create(MemoOutput); + FRunner.OnFinished := RunnerFinished; + FRunner.OnProgress := RunnerProgress; + + Caption := Application.Title + ' v' + Module.VersionString; + + ComboFileFormat.Items.Clear; + ComboFileFormat.Items.AddObject('Same as input', TObject(ffSameAsInput)); + ComboFileFormat.Items.AddObject('PNG', TObject(ffPng)); + ComboFileFormat.Items.AddObject('JPEG', TObject(ffJpeg)); + ComboFileFormat.Items.AddObject('TIFF (support depends on platform)', TObject(ffTiff)); + ComboFileFormat.Items.AddObject('BMP', TObject(ffBmp)); + ComboFileFormat.Items.AddObject('PSD', TObject(ffPsd)); + ComboFileFormat.Items.AddObject('TGA', TObject(ffTga)); + ComboFileFormat.Items.AddObject('JNG', TObject(ffJng)); + ComboFileFormat.Items.AddObject('PPM', TObject(ffPpm)); + ComboFileFormat.ItemIndex := 0; + + ApplyOptions(Module.Options); + + Config.AfterMainFormCreation(Self); + + if Screen.WorkAreaHeight < Height then + Height := Round(Screen.WorkAreaHeight * 0.9); +end; + +procedure TFormMain.FormDestroy(Sender: TObject); +begin + GatherOptions(Module.Options); + FRunner.Free; +end; + +procedure TFormMain.FormDropFiles(Sender: TObject; const FileNames: array of String); +var + I: Integer; +begin + for I := 0 to High(FileNames) do + MemoFiles.Append(FileNames[I]); +end; + +procedure TFormMain.ApplyOptions(AOptions: TOptions); +begin + CheckDefaultOutputFileOptions.Checked := AOptions.DefaultOutputFileOptions; + EdDirOutput.Text := AOptions.OutputFolder; + EdDirOutput.SelStart := Length(EdDirOutput.Text); + ComboFileFormat.ItemIndex := Integer(AOptions.OutputFileFormat); + ColorBtnBackground.ButtonColor := RGBToColor(GetRedValue(AOptions.BackgroundColor), GetGreenValue(AOptions.BackgroundColor), GetBlueValue(AOptions.BackgroundColor)); +end; + +procedure TFormMain.GatherOptions(AOptions: TOptions); +var + LazColor: TColor; + I: Integer; + S: string; +begin + AOptions.Files.Clear; + for I := 0 to MemoFiles.Lines.Count - 1 do + begin + S := Trim(MemoFiles.Lines[I]); + if S <> '' then + AOptions.Files.Add(S); + end; + + AOptions.DefaultOutputFileOptions := CheckDefaultOutputFileOptions.Checked; + AOptions.OutputFolder := EdDirOutput.Text; + AOptions.OutputFileFormat := TFileFormat(PtrUInt(ComboFileFormat.Items.Objects[ComboFileFormat.ItemIndex])); + + LazColor := ColorBtnBackground.ButtonColor; + AOptions.BackgroundColor := Color32(255, Red(LazColor), Green(LazColor), Blue(LazColor)).Color; +end; + +procedure TFormMain.RunnerFinished(Sender: TObject; Reason: TFinishReason); +begin + LabCurrentFile.Hide; + ActFinish.Enabled := True; + ActFinish.Caption := 'Back to Input'; + + case Reason of + frFinished: LabDeskewProgressTitle.Caption := 'Deskewing Finished'; + frFailure: LabDeskewProgressTitle.Caption := 'Deskewing Finished with Failures'; + frStopped: LabDeskewProgressTitle.Caption := 'Deskewing Stopped'; + else + Assert(False); + end; + + LabProgressTitle.Caption := Format('%d files processed', [FRunner.InputPos]); + if FRunner.Failures > 0 then + LabProgressTitle.Caption := LabProgressTitle.Caption + Format(', %d failed', [FRunner.Failures]); +end; + +procedure TFormMain.RunnerProgress(Sender: TObject; Index: Integer); +begin + ProgressBar.Position := Index + 1; + LabCurrentFile.Caption := Format('%s [%d/%d]', [ + ExtractFileName(Module.Options.Files[Index]), Index + 1, Module.Options.Files.Count]); + LabCurrentFile.Visible := True; + LabProgressTitle.Visible := True; +end; + +procedure TFormMain.ActDeskewUpdate(Sender: TObject); +begin + TAction(Sender).Enabled := (MemoFiles.Lines.Count > 0) and (Trim(MemoFiles.Lines[0]) <> ''); +end; + +procedure TFormMain.ApplicationPropertiesIdle(Sender: TObject; var Done: Boolean); +var + NoDefault: Boolean; +begin + NoDefault := not CheckDefaultOutputFileOptions.Checked; + ActBrowseOutputDir.Enabled := NoDefault; + EdDirOutput.Enabled := ActBrowseOutputDir.Enabled; + ComboFileFormat.Enabled := NoDefault; + LabOptOutputFolder.Enabled := NoDefault; + LabOptFileFormat.Enabled := NoDefault; + + FormAdvOptions.DoIdle; +end; + +procedure TFormMain.ActDeskewExecute(Sender: TObject); +begin + GatherOptions(Module.Options); + FormAdvOptions.GatherOptions(Module.Options); + + ActFinish.Caption := 'Stop'; + MemoOutput.Clear; + ProgressBar.Position := 0; + ProgressBar.Max := Module.Options.Files.Count; + LabCurrentFile.Hide; + LabProgressTitle.Hide; + LabProgressTitle.Caption := 'Current file:'; + + Notebook.PageIndex := 1; + Application.ProcessMessages; + + FRunner.Run(Module.Options); +end; + +procedure TFormMain.ActAddFilesExecute(Sender: TObject); +var + I: Integer; +begin + Module.OpenDialogMulti.Title := 'Select Picture Files'; + if Module.OpenDialogMulti.Execute then + begin + for I := 0 to Module.OpenDialogMulti.Files.Count - 1 do + MemoFiles.Append(Module.OpenDialogMulti.Files[I]); + end; +end; + +procedure TFormMain.ActBrowseOutputDirExecute(Sender: TObject); +begin + if Module.SelectDirectoryDialog.Execute then + begin + EdDirOutput.Text := Module.SelectDirectoryDialog.FileName; + EdDirOutput.SelStart := Length(EdDirOutput.Text); + end; +end; + +procedure TFormMain.ActClearFilesExecute(Sender: TObject); +begin + MemoFiles.Clear; +end; + +procedure TFormMain.ActFinishExecute(Sender: TObject); +begin + if FRunner.IsRunning then + begin + ActFinish.Enabled := False; + ActFinish.Caption := 'Stopping'; + FRunner.Stop; + end + else + begin + Notebook.PageIndex := 0; + end; +end; + +procedure TFormMain.ActShowAboutExecute(Sender: TObject); +begin + if not FormAbout.Visible then + FormAbout.ShowModal; +end; + +procedure TFormMain.ActShowAdvOptionsExecute(Sender: TObject); +begin + FormAdvOptions.ShowModal; +end; + + +end. + diff --git a/resources/libraries/deskew/Gui/options.pas b/resources/libraries/deskew/Gui/options.pas new file mode 100755 index 0000000..c640554 --- /dev/null +++ b/resources/libraries/deskew/Gui/options.pas @@ -0,0 +1,231 @@ +unit Options; + +interface + +uses + Classes, SysUtils, ImagingTypes, IniFiles; + +type + TForcedOutputFormat = ( + fofNone, + fofBinary1, + fofGray8, + fofRgb24, + fofRgba32 + ); + + TFileFormat = ( + ffSameAsInput, + ffPng, + ffJpeg, + ffTiff, + ffBmp, + ffPsd, + ffTga, + ffJng, + ffPpm + ); + + { TOptions } + + TOptions = class + private + FFiles: TStrings; + function GetEffectiveExecutablePath: string; + function GetOutputFilePath(const InputFilePath: string): string; + public + // Basic options + DefaultOutputFileOptions: Boolean; + OutputFolder: string; + OutputFileFormat: TFileFormat; + BackgroundColor: TColor32; + + // Advanced options + MaxAngle: Double; + ThresholdLevel: Integer; + ForcedOutputFormat: TForcedOutputFormat; + SkipAngle: Double; + JpegCompressionQuality: Integer; + TiffCompressionScheme: Integer; + DefaultExecutable: Boolean; + CustomExecutablePath: string; + + constructor Create; + destructor Destroy; override; + + procedure ToCmdLineParameters(AParams: TStrings; AFileIndex: Integer); + + procedure SaveToIni(Ini: TIniFile); + procedure LoadFromIni(Ini: TIniFile); + procedure Reset; + + property Files: TStrings read FFiles; + property EffectiveExecutablePath: string read GetEffectiveExecutablePath; + end; + +implementation + +uses + ImagingUtility, Utils; + +const + DefaultBackgroundColor = $FFFFFFFF; // white + DefaultMaxAngle = 10.0; + DefaultSkipAngle = 0.01; + DefaultThresholdLevel = -1; // auto + DefaultJpegCompressionQuality = -1; // use imaginglib default + DefaultTiffCompressionScheme = -1; // use imaginglib default + DefaultOutputFileNamePrefix = 'deskewed-'; + + FileExts: array[TFileFormat] of string = ( + '', // ffSameAsInput + 'png', // ffPng + 'jpg', // ffJpeg + 'tif', // ffTiff + 'bmp', // ffBmp + 'psd', // ffPsd + 'tga', // ffTga + 'jng', // ffJng + 'ppm' // ffPpm + ); + + FormatIds: array[TForcedOutputFormat] of string = ( + '', // fofNone, + 'b1', // fofBinary1 + 'g8', // fofGray8 + 'rgb24', // fofRgb24 + 'rgba32' // fofRgba32 + ); + + IniSectionOptions = 'Options'; + IniSectionAdvanced = 'Advanced'; + +{ TOptions } + +constructor TOptions.Create; +begin + FFiles := TStringList.Create; + Reset; +end; + +destructor TOptions.Destroy; +begin + FFiles.Free; + inherited Destroy; +end; + +function TOptions.GetEffectiveExecutablePath: string; +begin + if DefaultExecutable then + Result := Utils.FindDeskewExePath + else + Result := CustomExecutablePath; +end; + +function TOptions.GetOutputFilePath(const InputFilePath: string): string; +var + FileName: string; +begin + FileName := ExtractFileName(InputFilePath); + + if DefaultOutputFileOptions then + begin + Result := ExtractFilePath(InputFilePath) + DefaultOutputFileNamePrefix + FileName; + end + else + begin + if OutputFileFormat <> ffSameAsInput then + FileName := ChangeFileExt(FileName, '.' + FileExts[OutputFileFormat]); + + Result := IncludeTrailingPathDelimiter(OutputFolder) + FileName; + + // Try to avoid overwriting existing file (in case in-folder = out-folder) + if FileExists(Result) then + Result := IncludeTrailingPathDelimiter(OutputFolder) + DefaultOutputFileNamePrefix + FileName; + end; +end; + +procedure TOptions.ToCmdLineParameters(AParams: TStrings; AFileIndex: Integer); + + function FloatToStrFmt(const F: Double): string; + begin + Result := Format('%.2f', [F], ImagingUtility.GetFormatSettingsForFloats); + end; + +begin + Assert(AFileIndex < FFiles.Count); + + AParams.Clear; + + AParams.AddStrings(['-o', GetOutputFilePath(FFiles[AFileIndex])]); + + if BackgroundColor <> $FF000000 then + AParams.AddStrings(['-b', IntToHex(BackgroundColor, 8)]); + + // Advanced options + if not SameFloat(MaxAngle, DefaultMaxAngle, 0.1) then + AParams.AddStrings(['-a', FloatToStrFmt(MaxAngle)]); + if not SameFloat(SkipAngle, DefaultSkipAngle, 0.01) then + AParams.AddStrings(['-l', FloatToStrFmt(SkipAngle)]); + if ForcedOutputFormat <> fofNone then + AParams.AddStrings(['-f', FormatIds[ForcedOutputFormat]]); + +{$IFDEF DEBUG} + AParams.AddStrings(['-s', 'p']); +{$ENDIF} + AParams.Add(FFiles[AFileIndex]); +end; + +procedure TOptions.SaveToIni(Ini: TIniFile); +begin + Ini.WriteString(IniSectionOptions, 'DefaultOutputFileOptions', BoolToStr(DefaultOutputFileOptions, True)); + Ini.WriteString(IniSectionOptions, 'OutputFolder', OutputFolder); + Ini.WriteString(IniSectionOptions, 'OutputFileFormat', TEnumUtils<TFileFormat>.EnumToStr(OutputFileFormat)); + Ini.WriteString(IniSectionOptions, 'BackgroundColor', ColorToString(BackgroundColor)); + + Ini.WriteFloat(IniSectionAdvanced, 'MaxAngle', MaxAngle); + Ini.WriteInteger(IniSectionAdvanced, 'ThresholdLevel', ThresholdLevel); + Ini.WriteString(IniSectionAdvanced, 'ForcedOutputFormat', TEnumUtils<TForcedOutputFormat>.EnumToStr(ForcedOutputFormat)); + Ini.WriteFloat(IniSectionAdvanced, 'SkipAngle', SkipAngle); + Ini.WriteInteger(IniSectionAdvanced, 'JpegCompressionQuality', JpegCompressionQuality); + Ini.WriteInteger(IniSectionAdvanced, 'TiffCompressionScheme', TiffCompressionScheme); + Ini.WriteString(IniSectionAdvanced, 'DefaultExecutable', BoolToStr(DefaultExecutable, True)); + Ini.WriteString(IniSectionAdvanced, 'CustomExecutablePath', CustomExecutablePath); +end; + +procedure TOptions.LoadFromIni(Ini: TIniFile); +begin + DefaultOutputFileOptions := StrToBoolDef(Ini.ReadString(IniSectionOptions, 'DefaultOutputFileOptions', ''), True); + OutputFolder := Ini.ReadString(IniSectionOptions, 'OutputFolder', ''); + OutputFileFormat := TEnumUtils<TFileFormat>.StrToEnum(Ini.ReadString(IniSectionOptions, 'OutputFileFormat', '')); + BackgroundColor := StringToColorDef(Ini.ReadString(IniSectionOptions, 'BackgroundColor', ''), DefaultBackgroundColor); + + MaxAngle := Ini.ReadFloat(IniSectionAdvanced, 'MaxAngle', DefaultMaxAngle); + ThresholdLevel := Ini.ReadInteger(IniSectionAdvanced, 'ThresholdLevel', DefaultThresholdLevel); + ForcedOutputFormat := TEnumUtils<TForcedOutputFormat>.StrToEnum(Ini.ReadString(IniSectionAdvanced, 'ForcedOutputFormat', '')); + SkipAngle := Ini.ReadFloat(IniSectionAdvanced, 'SkipAngle', DefaultSkipAngle); + JpegCompressionQuality := Ini.ReadInteger(IniSectionAdvanced, 'JpegCompressionQuality', DefaultJpegCompressionQuality); + TiffCompressionScheme := Ini.ReadInteger(IniSectionAdvanced, 'TiffCompressionScheme', DefaultTiffCompressionScheme); + DefaultExecutable := StrToBoolDef(Ini.ReadString(IniSectionAdvanced, 'DefaultExecutable', ''), True); + CustomExecutablePath := Ini.ReadString(IniSectionAdvanced, 'CustomExecutablePath', ''); +end; + +procedure TOptions.Reset; +begin + DefaultOutputFileOptions := True; + OutputFolder := ''; + OutputFileFormat := ffSameAsInput; + BackgroundColor := DefaultBackgroundColor; + + MaxAngle := DefaultMaxAngle; + ThresholdLevel := DefaultThresholdLevel; + ForcedOutputFormat := fofNone; + SkipAngle := DefaultSkipAngle; + JpegCompressionQuality := DefaultJpegCompressionQuality; + TiffCompressionScheme := DefaultTiffCompressionScheme; + DefaultExecutable := True; + CustomExecutablePath := ''; +end; + +end. + diff --git a/resources/libraries/deskew/Gui/runner.pas b/resources/libraries/deskew/Gui/runner.pas new file mode 100755 index 0000000..122757a --- /dev/null +++ b/resources/libraries/deskew/Gui/runner.pas @@ -0,0 +1,174 @@ +unit Runner; + +interface + +uses + Classes, SysUtils, UTF8Process, StdCtrls, ExtCtrls, Options; + +type + TFinishReason = ( + frFinished, + frStopped, + frFailure + ); + + TFinishedEvent = procedure(Sender: TObject; Reason: TFinishReason) of object; + TProgressEvent = procedure(Sender: TObject; Index: Integer) of object; + + { TRunner } + + TRunner = class + private + FProcess: TProcessUTF8; + FTimer: TTimer; + FOutputMemo: TCustomMemo; + FOnFinished: TFinishedEvent; + FOnProgress: TProgressEvent; + + FInputPos: Integer; + FFailures: Integer; + FOptions: TOptions; + FRunning: Boolean; + FStopped: Boolean; + + procedure ReadProcessOutput; + procedure TimerTicked(Sender: TObject); + procedure RunNextItem(IsFirstRun: Boolean = False); + procedure Finish(Reason: TFinishReason); + public + constructor Create(AOutputMemo: TCustomMemo); + destructor Destroy; override; + + procedure Run(AOptions: TOptions); + procedure Stop; + + property IsRunning: Boolean read FRunning; + property Failures: Integer read FFailures; + property InputPos: Integer read FInputPos; + property OnFinished: TFinishedEvent read FOnFinished write FOnFinished; + property OnProgress: TProgressEvent read FOnProgress write FOnProgress; + end; + +implementation + +uses + Process, Dialogs; + +{ TRunner } + +constructor TRunner.Create(AOutputMemo: TCustomMemo); +begin + // Unfortunatelly, we cannot use TAsyncProcess since it does not work reliably on all platforms + FProcess := TProcessUTF8.Create(nil); + FProcess.Options := [poUsePipes, {$IFDEF MSWINDOWS}poNoConsole,{$ENDIF} poStderrToOutPut]; + + FTimer := TTimer.Create(nil); + FTimer.Enabled := False; + FTimer.Interval := 50; + FTimer.OnTimer := TimerTicked; + + FOutputMemo := AOutputMemo; +end; + +destructor TRunner.Destroy; +begin + FProcess.Free; + FTimer.Free; + inherited Destroy; +end; + +procedure TRunner.ReadProcessOutput; +var + BufStr: string; +begin + while FProcess.Output.NumBytesAvailable > 0 do + begin + SetLength(BufStr, FProcess.Output.NumBytesAvailable); + FProcess.Output.Read(BufStr[1], Length(BufStr)); + FOutputMemo.Append(BufStr); + end; +end; + +procedure TRunner.TimerTicked(Sender: TObject); +begin + ReadProcessOutput; + if not FProcess.Running then + RunNextItem; +end; + +procedure TRunner.RunNextItem(IsFirstRun: Boolean); +begin + if not IsFirstRun and (FProcess.ExitCode <> 0) then + Inc(FFailures); + + Inc(FInputPos); + + if FInputPos >= FOptions.Files.Count then + begin + if FFailures = 0 then + Finish(frFinished) + else + Finish(frFailure); + + Exit; + end; + + if FStopped then + begin + Finish(frStopped); + Exit; + end; + + if Assigned(FOnProgress) then + FOnProgress(Self, FInputPos); + + FOptions.ToCmdLineParameters(FProcess.Parameters, FInputPos); + + try + FProcess.Execute; + except + on Ex: Exception do + begin + FOutputMemo.Append(Ex.ClassName + ': ' + Ex.Message); + Dialogs.MessageDlg('Failed to execute Deskew', + 'Deskew command line executable failed to start. Check that it is in the correct location ' + + 'and has the right permissions.' + sLineBreak + sLineBreak + + 'Executable path used: ' + FProcess.Executable, + mtError, [mbOK], ''); + + Finish(frFailure); + Exit; + end; + end; + + if IsFirstRun then + FTimer.Enabled := True; +end; + +procedure TRunner.Finish(Reason: TFinishReason); +begin + FTimer.Enabled := False; + FRunning := False; + if Assigned(FOnFinished) then + FOnFinished(Self, Reason); +end; + +procedure TRunner.Run(AOptions: TOptions); +begin + FInputPos := -1; + FFailures := 0; + FOptions := AOptions; + FStopped := False; + FRunning := True; + + FProcess.Executable := FOptions.EffectiveExecutablePath; + RunNextItem(True); +end; + +procedure TRunner.Stop; +begin + FStopped := True; +end; + +end. + diff --git a/resources/libraries/deskew/Gui/utils.pas b/resources/libraries/deskew/Gui/utils.pas new file mode 100755 index 0000000..93ce267 --- /dev/null +++ b/resources/libraries/deskew/Gui/utils.pas @@ -0,0 +1,133 @@ +unit Utils; + +interface + +uses + Classes, SysUtils, TypInfo, ImagingTypes; + +type + // Workaround for generic functions needing FPC 3.1.1+ + TEnumUtils<T> = class + public + class function EnumToStr(const EnumValue: T): string; + class function StrToEnum(const Str: string): T; + class function GetEnumPrefix: string; + end; + +function FindDeskewExePath: string; +function DetermineConfigFolder: string; +function ColorToString(Color: TColor32): string; +function StringToColorDef(const Str: string; Default: TColor32): TColor32; + +implementation + +uses +{$IF Defined(DARWIN)} + StrUtils, +{$ENDIF} + LazFileUtils, Forms; + +function FindDeskewExePath: string; +var + ExeDir, S: string; +begin + Result := './deskew'; + + ExeDir := Application.Location; + if DirectoryExists(ExeDir) then + begin +{$IF Defined(MSWINDOWS)} + S := ExeDir + 'deskew64.exe'; + if FileExists(S) then + Exit(S); + + S := ExeDir + 'deskew.exe'; + if FileExists(S) then + Exit(S); + + S := ExeDir + 'deskew32.exe'; + if FileExists(S) then + Exit(S); +{$ELSEIF Defined(DARWIN)} + S := ExeDir + 'deskew-mac'; + if FileExists(S) then + Exit(S); + + S := ExeDir + 'deskew'; + if FileExists(S) then + Exit(S); + + if AnsiContainsText(ExeDir, '.app/Contents/MacOS') then + begin + // Get out af the bundle + S := ExtractFileDir(ExtractFileDir(ExtractFileDir(ExcludeTrailingPathDelimiter(ExeDir)))) + '/deskew-mac'; + if FileExists(S) then + Exit(S); + end; +{$ELSEIF Defined(LINUX)} + S := ExeDir + 'deskew'; + if FileExists(S) then + Exit(S); +{$ENDIF} + end; +end; + +function DetermineConfigFolder: string; +var + ExeDir: string; +begin + Result := GetAppConfigDir(False); + + ExeDir := Application.Location; + if DirectoryExists(ExeDir) and DirectoryIsWritable(ExeDir) then + Result := ExeDir; +end; + +function ColorToString(Color: TColor32): string; +begin + Result := '#' + HexStr(Color, 8); +end; + +function StringToColorDef(const Str: string; Default: TColor32): TColor32; +var + S: string; +begin + S := '$' + Copy(Str, 2); + Result := StrToDWordDef(S, Default); +end; + +class function TEnumUtils<T>.EnumToStr(const EnumValue: T): string; +var + S: string; + L: Integer; +begin + S := TypInfo.GetEnumName(TypeInfo(T), Integer(EnumValue)); + L := Length(GetEnumPrefix); + Result := Copy(S, L + 1); +end; + +class function TEnumUtils<T>.StrToEnum(const Str: string): T; +var + S: string; + I: Integer; +begin + S := GetEnumPrefix + Str; + I := TypInfo.GetEnumValue(TypeInfo(T), S); + if I >= 0 then + Result := T(I) + else + Result := Default(T); +end; + +class function TEnumUtils<T>.GetEnumPrefix: string; +var + S: string; +begin + S := TypInfo.GetEnumName(TypeInfo(T), Integer(Default(T))); + Result := Copy(S, 1, 2); + if S[3] in ['a'..'z'] then + Result := Result + S[3]; +end; + +end. + diff --git a/resources/libraries/deskew/ImageUtils.pas b/resources/libraries/deskew/ImageUtils.pas new file mode 100755 index 0000000..6b1ef73 --- /dev/null +++ b/resources/libraries/deskew/ImageUtils.pas @@ -0,0 +1,697 @@ +{ + Deskew + by Marek Mauder + http://galfar.vevb.net/deskew + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +{ + Unit with various image processing functions. Some are taken from + Imaging extensions. +} +unit ImageUtils; + +{$I ImagingOptions.inc} + +interface + +uses + Types, + Math, + SysUtils, + Classes, + ImagingTypes, + Imaging, + ImagingFormats, + ImagingUtility; + +type + TResamplingFilter = ( + rfNearest, + rfLinear, + rfCubic, + rfLanczos + ); + +{ Thresholding using Otsu's method (which chooses the threshold + to minimize the intraclass variance of the black and white pixels!). + Functions returns calculated threshold level value [0..255]. + If BinarizeImage is True then the Image is automatically converted to binary using + computed threshold level.} +function OtsuThresholding(var Image: TImageData; BinarizeImage: Boolean = False): Integer; + +const + SupportedRotationFormats: set of TImageFormat = [ifGray8, ifR8G8B8, ifA8R8G8B8]; + +{ Rotates image with a background (of outside "void" areas) of specified color. The image is resized to fit + the whole rotated image. } +procedure RotateImage(var Image: TImageData; Angle: Double; BackgroundColor: TColor32; + ResamplingFilter: TResamplingFilter; FitRotated: Boolean); + +implementation + +function OtsuThresholding(var Image: TImageData; BinarizeImage: Boolean): Integer; +var + Histogram: array[Byte] of Single; + Level, Max, Min, I, J, NumPixels: Integer; + Pix: PByte; + Mean, Variance: Single; + Mu, Omega, LevelMean, LargestMu: Single; +begin + Assert(Image.Format = ifGray8); + + FillChar(Histogram, SizeOf(Histogram), 0); + Min := 255; + Max := 0; + Level := 0; + NumPixels := Image.Width * Image.Height; + Pix := Image.Bits; + + // Compute histogram and determine min and max pixel values + for I := 0 to NumPixels - 1 do + begin + Histogram[Pix^] := Histogram[Pix^] + 1.0; + if Pix^ < Min then + Min := Pix^; + if Pix^ > Max then + Max := Pix^; + Inc(Pix); + end; + + // Normalize histogram + for I := 0 to 255 do + Histogram[I] := Histogram[I] / NumPixels; + + // Compute image mean and variance + Mean := 0.0; + Variance := 0.0; + for I := 0 to 255 do + Mean := Mean + (I + 1) * Histogram[I]; + for I := 0 to 255 do + Variance := Variance + Sqr(I + 1 - Mean) * Histogram[I]; + + // Now finally compute threshold level + LargestMu := 0; + + for I := 0 to 255 do + begin + Omega := 0.0; + LevelMean := 0.0; + + for J := 0 to I - 1 do + begin + Omega := Omega + Histogram[J]; + LevelMean := LevelMean + (J + 1) * Histogram[J]; + end; + + Mu := Sqr(Mean * Omega - LevelMean); + Omega := Omega * (1.0 - Omega); + + if Omega > 0.0 then + Mu := Mu / Omega + else + Mu := 0; + + if Mu > LargestMu then + begin + LargestMu := Mu; + Level := I; + end; + end; + + if BinarizeImage then + begin + // Do thresholding using computed level + Pix := Image.Bits; + for I := 0 to Image.Width * Image.Height - 1 do + begin + if Pix^ >= Level then + Pix^ := 255 + else + Pix^ := 0; + Inc(Pix); + end; + end; + + Result := Level; +end; + +procedure RotateImage(var Image: TImageData; Angle: Double; BackgroundColor: TColor32; + ResamplingFilter: TResamplingFilter; FitRotated: Boolean); +// Use precomputed weights for bicubic and Lanczos filters +{$DEFINE USE_FILTER_TABLE} + +type + TBufferEntry = record + B, G, R, A: Single; + end; + +const + EmptyBufferEntry: TBufferEntry = (B: 0; G: 0; R: 0; A: 0); + TableSize = 32; + MaxTablePos = TableSize - 1; + MaxKernelRadius = 3; + +var + SrcWidth, SrcHeight: Integer; + SrcWidthHalf, SrcHeightHalf, DstWidthHalf, DstHeightHalf: Single; + DstWidth, DstHeight: Integer; + AngleRad, ForwardSin, ForwardCos, BackwardSin, BackwardCos, SrcX, SrcY, D: Single; + TopLeft, TopRight, BottomLeft, BottomRight: TFloatPoint; + SrcImage, DstImage: TImageData; + FormatInfo: TImageFormatInfo; + X, Y, Bpp: Integer; + DstPixel24: PColor24Rec; + BackColor24: TColor24Rec; + BackColor32, Pixel32: TColor32Rec; + DstByte: PByte; + Filter: TSamplingFilter; + FilterFunction: TFilterFunction; + FilterRadius: Single; + KernelWidth: Integer; + WeightTable: array[-MaxKernelRadius..MaxKernelRadius, 0..TableSize] of Single; + + function FastFloor(X: Single): Integer; inline; + begin + Result := Trunc(X + 65536.0) - 65536; + end; + + function FastCeil(X: Single): Integer; inline; + begin + Result := 65536 - Trunc(65536.0 - X); + end; + + function GetPixelColor24(X, Y: Integer): TColor24Rec; {$IFDEF FPC}inline;{$ENDIF} + begin + if (X >= 0) and (X < SrcWidth) and (Y >= 0) and (Y < SrcHeight) then + Result := PColor24RecArray(SrcImage.Bits)[Y * SrcWidth + X] + else + Result := BackColor24; + end; + + function GetPixelColor8(X, Y: Integer): Byte; {$IFDEF FPC}inline;{$ENDIF} + begin + if (X >= 0) and (X < SrcWidth) and (Y >= 0) and (Y < SrcHeight) then + Result := PByteArray(SrcImage.Bits)[Y * SrcWidth + X] + else + Result := BackColor32.B; + end; + + function GetPixelColor32(X, Y: Integer): TColor32Rec; {$IFDEF FPC}inline;{$ENDIF} + begin + if (X >= 0) and (X < SrcWidth) and (Y >= 0) and (Y < SrcHeight) then + Result := PColor32RecArray(SrcImage.Bits)[Y * SrcWidth + X] + else + Result := BackColor32; + end; + + procedure GetBilinearPixelCoords(X, Y: Single; + out HorzWeight, VertWeight: Single; + out TopLeftPt, BottomLeftPt, TopRightPt, BottomRightPt: TPoint); inline; + begin + TopLeftPt := Point(FastFloor(X), FastFloor(Y)); + + HorzWeight := X - TopLeftPt.X; + VertWeight := Y - TopLeftPt.Y; + + BottomLeftPt := Point(TopLeftPt.X, TopLeftPt.Y + 1); + TopRightPt := Point(TopLeftPt.X + 1, TopLeftPt.Y); + BottomRightPt := Point(TopLeftPt.X + 1, TopLeftPt.Y + 1); + end; + + function InterpolateBytes(HorzWeight, VertWeight: Single; C11, C12, C21, C22: Byte): Byte; inline; + begin + Result := ClampToByte(Trunc( + (1 - HorzWeight) * (1 - VertWeight) * C11 + + (1 - HorzWeight) * VertWeight * C12 + + HorzWeight * (1 - VertWeight) * C21 + + HorzWeight * VertWeight * C22)); + end; + + function Bilinear24(X, Y: Single): TColor24Rec; inline; + var + TopLeftPt, BottomLeftPt, TopRightPt, BottomRightPt: TPoint; + HorzWeight, VertWeight: Single; + TopLeftColor, TopRightColor, BottomLeftColor, BottomRightColor: TColor24Rec; + begin + GetBilinearPixelCoords(X, Y, + HorzWeight, VertWeight, + TopLeftPt, BottomLeftPt, TopRightPt, BottomRightPt); + + TopLeftColor := GetPixelColor24(TopLeftPt.X, TopLeftPt.Y); + BottomLeftColor := GetPixelColor24(BottomLeftPt.X, BottomLeftPt.Y); + TopRightColor := GetPixelColor24(TopRightPt.X, TopRightPt.Y); + BottomRightColor := GetPixelColor24(BottomRightPt.X, BottomRightPt.Y); + + Result.R := InterpolateBytes(HorzWeight, VertWeight, + TopLeftColor.R, BottomLeftColor.R, TopRightColor.R, BottomRightColor.R); + Result.G := InterpolateBytes(HorzWeight, VertWeight, + TopLeftColor.G, BottomLeftColor.G, TopRightColor.G, BottomRightColor.G); + Result.B := InterpolateBytes(HorzWeight, VertWeight, + TopLeftColor.B, BottomLeftColor.B, TopRightColor.B, BottomRightColor.B); + end; + + function Bilinear8(X, Y: Single): Byte; inline; + var + TopLeftPt, BottomLeftPt, TopRightPt, BottomRightPt: TPoint; + HorzWeight, VertWeight: Single; + TopLeftColor, TopRightColor, BottomLeftColor, BottomRightColor: Byte; + begin + GetBilinearPixelCoords(X, Y, + HorzWeight, VertWeight, + TopLeftPt, BottomLeftPt, TopRightPt, BottomRightPt); + + TopLeftColor := GetPixelColor8(TopLeftPt.X, TopLeftPt.Y); + BottomLeftColor := GetPixelColor8(BottomLeftPt.X, BottomLeftPt.Y); + TopRightColor := GetPixelColor8(TopRightPt.X, TopRightPt.Y); + BottomRightColor := GetPixelColor8(BottomRightPt.X, BottomRightPt.Y); + + Result := InterpolateBytes(HorzWeight, VertWeight, + TopLeftColor, BottomLeftColor, TopRightColor, BottomRightColor); + end; + + function Bilinear32(X, Y: Single): TColor32Rec; inline; + var + TopLeftPt, BottomLeftPt, TopRightPt, BottomRightPt: TPoint; + HorzWeight, VertWeight: Single; + TopLeftColor, TopRightColor, BottomLeftColor, BottomRightColor: TColor32Rec; + begin + GetBilinearPixelCoords(X, Y, + HorzWeight, VertWeight, + TopLeftPt, BottomLeftPt, TopRightPt, BottomRightPt); + + TopLeftColor := GetPixelColor32(TopLeftPt.X, TopLeftPt.Y); + BottomLeftColor := GetPixelColor32(BottomLeftPt.X, BottomLeftPt.Y); + TopRightColor := GetPixelColor32(TopRightPt.X, TopRightPt.Y); + BottomRightColor := GetPixelColor32(BottomRightPt.X, BottomRightPt.Y); + + Result.A := InterpolateBytes(HorzWeight, VertWeight, + TopLeftColor.A, BottomLeftColor.A, TopRightColor.A, BottomRightColor.A); + Result.R := InterpolateBytes(HorzWeight, VertWeight, + TopLeftColor.R, BottomLeftColor.R, TopRightColor.R, BottomRightColor.R); + Result.G := InterpolateBytes(HorzWeight, VertWeight, + TopLeftColor.G, BottomLeftColor.G, TopRightColor.G, BottomRightColor.G); + Result.B := InterpolateBytes(HorzWeight, VertWeight, + TopLeftColor.B, BottomLeftColor.B, TopRightColor.B, BottomRightColor.B); + end; + +{$IFDEF USE_FILTER_TABLE} + procedure PrecomputeFilterWeights; + var + I, J: Integer; + Weight: Single; + Fraction: Single; + begin + FillMemoryByte(@WeightTable, SizeOf(WeightTable), 0); + + for I := 0 to TableSize do + begin + Fraction := I / (TableSize - 1); + for J := -KernelWidth to KernelWidth do + begin + Weight := FilterFunction(J + Fraction); + WeightTable[J, I] := Weight; + end; + end; + end; +{$ENDIF} + + function FilterPixel(X, Y: Single; Bpp: Integer): TColor32Rec; + var + HorzEntry, VertEntry: TBufferEntry; + LoX, HiX, LoY, HiY: Integer; + I, J: Integer; + WeightHorz, WeightVert: Single; + CeilX, CeilY: Integer; + {$IFDEF USE_FILTER_TABLE} + XFilterTablePos, YFilterTablePos: Integer; + {$ELSE} + FracXS, FracYS: Single; + {$ENDIF} + SrcPixel: PColor32Rec; + ClipRect: TRect; + Edge: Boolean; + begin + ClipRect := Rect(0, 0, SrcWidth, SrcHeight); + Edge := False; + + CeilX := FastCeil(X); + CeilY := FastCeil(Y); + + with ClipRect do + begin + if not ((CeilX < Left) or (CeilX > Right) or (CeilY < Top) or (CeilY > Bottom)) then + begin + Edge := False; + + if CeilX - KernelWidth < Left then + begin + LoX := Left - CeilX; + Edge := True; + end + else + LoX := -KernelWidth; + + if CeilX + KernelWidth >= Right then + begin + HiX := Right - CeilX - 1; + Edge := True; + end + else + HiX := KernelWidth; + + if CeilY - KernelWidth < Top then + begin + LoY := Top - CeilY; + Edge := True; + end + else + LoY := -KernelWidth; + + if CeilY + KernelWidth >= Bottom then + begin + HiY := Bottom - CeilY - 1; + Edge := True; + end + else + HiY := KernelWidth; + end + else + Exit(BackColor32); + end; + + {$IFDEF USE_FILTER_TABLE} + XFilterTablePos := Round((CeilX - X) * MaxTablePos); + YFilterTablePos := Round((CeilY - Y) * MaxTablePos); + {$ELSE} + FracXS := CeilX - X; + FracYS := CeilY - Y; + {$ENDIF} + + VertEntry := EmptyBufferEntry; + + for I := LoY to HiY do + begin + {$IFDEF USE_FILTER_TABLE} + WeightVert := WeightTable[I, YFilterTablePos]; + {$ELSE} + WeightVert := FilterFunction(I + FracYS); + {$ENDIF} + + SrcPixel := PColor32Rec(@PByteArray(SrcImage.Bits)[(LoX + CeilX + (I + CeilY) * SrcWidth) * Bpp]); + + if WeightVert <> 0 then + begin + HorzEntry := EmptyBufferEntry; + for J := LoX to HiX do + begin + {$IFDEF USE_FILTER_TABLE} + WeightHorz := WeightTable[J, XFilterTablePos]; + {$ELSE} + WeightHorz := FilterFunction(J + FracXS); + {$ENDIF} + + HorzEntry.B := HorzEntry.B + SrcPixel.B * WeightHorz; + if Bpp > 1 then + begin + HorzEntry.R := HorzEntry.R + SrcPixel.R * WeightHorz; + HorzEntry.G := HorzEntry.G + SrcPixel.G * WeightHorz; + if Bpp > 3 then + HorzEntry.A := HorzEntry.A + SrcPixel.A * WeightHorz; + end; + + Inc(PByte(SrcPixel), Bpp); + end; + + VertEntry.A := VertEntry.A + HorzEntry.A * WeightVert; + VertEntry.R := VertEntry.R + HorzEntry.R * WeightVert; + VertEntry.G := VertEntry.G + HorzEntry.G * WeightVert; + VertEntry.B := VertEntry.B + HorzEntry.B * WeightVert; + end; + end; + + if Edge then + begin + for I := -KernelWidth to KernelWidth do + begin + {$IFDEF USE_FILTER_TABLE} + WeightVert := WeightTable[I, YFilterTablePos]; + {$ELSE} + WeightVert := FilterFunction(I + FracYS); + {$ENDIF} + + if WeightVert <> 0 then + begin + HorzEntry := EmptyBufferEntry; + for J := -KernelWidth to KernelWidth do + begin + if (J < LoX) or (J > HiX) or (I < LoY) or (I > HiY) then + begin + {$IFDEF USE_FILTER_TABLE} + WeightHorz := WeightTable[J, XFilterTablePos]; + {$ELSE} + WeightHorz := FilterFunction(J + FracXS); + {$ENDIF} + + HorzEntry.A := HorzEntry.A + BackColor32.A * WeightHorz; + HorzEntry.R := HorzEntry.R + BackColor32.R * WeightHorz; + HorzEntry.G := HorzEntry.G + BackColor32.G * WeightHorz; + HorzEntry.B := HorzEntry.B + BackColor32.B * WeightHorz; + end; + end; + + VertEntry.A := VertEntry.A + HorzEntry.A * WeightVert; + VertEntry.R := VertEntry.R + HorzEntry.R * WeightVert; + VertEntry.G := VertEntry.G + HorzEntry.G * WeightVert; + VertEntry.B := VertEntry.B + HorzEntry.B * WeightVert; + end; + end + end; + + with Result do + begin + A := ClampToByte(Trunc(VertEntry.A + 0.5)); + R := ClampToByte(Trunc(VertEntry.R + 0.5)); + G := ClampToByte(Trunc(VertEntry.G + 0.5)); + B := ClampToByte(Trunc(VertEntry.B + 0.5)); + end; + end; + + function RotatePoint(X, Y: Single): TFloatPoint; + begin + Result.X := ForwardCos * X - ForwardSin * Y; + Result.Y := ForwardSin * X + ForwardCos * Y; + end; + + function Max4(X1, X2, X3, X4: Single): Single; + begin + Result := Math.Max(Math.Max(X1, X2), Math.Max(X3, X4)); + end; + + function Min4(X1, X2, X3, X4: Single): Single; + begin + Result := Math.Min(Math.Min(X1, X2), Math.Min(X3, X4)); + end; + + procedure CalcSourceCoordinates(DstX, DstY: Integer; out SrcX, SrcY: Single); {$IFDEF FPC}inline;{$ENDIF} + var + SrcCoordX, SrcCoordY: Single; + DstCoordX, DstCoordY: Single; + begin + DstCoordX := DstX - DstWidthHalf; + DstCoordY := DstHeightHalf - DstY; + + SrcCoordX := BackwardCos * DstCoordX - BackwardSin * DstCoordY; + SrcCoordY := BackwardSin * DstCoordX + BackwardCos * DstCoordY; + + SrcX := SrcCoordX + SrcWidthHalf; + SrcY := SrcHeightHalf - SrcCoordY; + end; + + function CropToSource(const Pt: TFloatPoint): Single; + var + X, Y: Single; + begin + X := Abs(Pt.X / SrcWidthHalf); + Y := Abs(Pt.Y / SrcHeightHalf); + Result := MaxFloat(X, Y); + end; + +begin + Assert(Image.Format in SupportedRotationFormats); + GetImageFormatInfo(Image.Format, FormatInfo); + + while Angle >= 360 do + Angle := Angle - 360; + while Angle < 0 do + Angle := Angle + 360; + + if (Angle = 0) or (Abs(Angle) = 360) then + Exit; + + AngleRad := Angle * PI / 180; + SinCos(AngleRad, ForwardSin, ForwardCos); + SinCos(-AngleRad, BackwardSin, BackwardCos); + + SrcImage := Image; + SrcWidth := SrcImage.Width; + SrcHeight := SrcImage.Height; + SrcWidthHalf := (SrcWidth - 1) / 2; + SrcHeightHalf := (SrcHeight - 1) / 2; + + // Calculate width and height of the rotated image + TopLeft := RotatePoint(-SrcWidthHalf, SrcHeightHalf); + TopRight := RotatePoint(SrcWidthHalf, SrcHeightHalf); + BottomLeft := RotatePoint(-SrcWidthHalf, -SrcHeightHalf); + BottomRight := RotatePoint(SrcWidthHalf, -SrcHeightHalf); + + if FitRotated then + begin + // Encompass the whole area of rotate image => bounding box + DstWidth := Ceil(Max4(TopLeft.X, TopRight.X, BottomLeft.X, BottomRight.X) - + Min4(TopLeft.X, TopRight.X, BottomLeft.X, BottomRight.X)); + DstHeight := Ceil(Max4(TopLeft.Y, TopRight.Y, BottomLeft.Y, BottomRight.Y) - + Min4(TopLeft.Y, TopRight.Y, BottomLeft.Y, BottomRight.Y)); + + if ResamplingFilter <> rfNearest then + begin + // Account a bit for antialiased edges of the rotated image + Inc(DstWidth); + Inc(DstHeight); + end; + end + else + begin + // Crop to largest proportional rect inside the rotated rect + D := Max4(CropToSource(TopLeft), CropToSource(TopRight), CropToSource(BottomLeft), CropToSource(BottomRight)); + DstWidth := Ceil(SrcWidth / D); + DstHeight := Ceil(SrcHeight / D); + end; + + DstWidthHalf := (DstWidth - 1) / 2; + DstHeightHalf := (DstHeight - 1) / 2; + + InitImage(DstImage); + NewImage(DstWidth, DstHeight, SrcImage.Format, DstImage); + + Bpp := FormatInfo.BytesPerPixel; + DstByte := DstImage.Bits; + BackColor32 := TColor32Rec(BackgroundColor); + + if ResamplingFilter = rfNearest then + begin + for Y := 0 to DstHeight - 1 do + for X := 0 to DstWidth - 1 do + begin + CalcSourceCoordinates(X, Y, SrcX, SrcY); + + if (SrcX >= 0) and (SrcY >= 0) and (SrcX <= SrcWidth - 1) and (SrcY <= SrcHeight - 1) then + begin + if Bpp = 3 then + PColor24Rec(DstByte)^ := PColor24RecArray(SrcImage.Bits)[Round(SrcY) * SrcWidth + Round(SrcX)] + else if Bpp = 1 then + DstByte^ := PByteArray(SrcImage.Bits)[Round(SrcY) * SrcWidth + Round(SrcX)] + else + PColor32Rec(DstByte)^ := PColor32RecArray(SrcImage.Bits)[Round(SrcY) * SrcWidth + Round(SrcX)]; + end + else + CopyPixel(@BackColor32, DstByte, Bpp); + + Inc(DstByte, Bpp); + end; + end + else if ResamplingFilter = rfLinear then + begin + if SrcImage.Format = ifR8G8B8 then + begin + DstPixel24 := DstImage.Bits; + BackColor24 := TColor32Rec(BackgroundColor).Color24Rec; + + // RGB 24bit path + for Y := 0 to DstHeight - 1 do + for X := 0 to DstWidth - 1 do + begin + CalcSourceCoordinates(X, Y, SrcX, SrcY); + + if (SrcX >= -1) and (SrcY >= -1) and (SrcX <= SrcWidth) and (SrcY <= SrcHeight) then + DstPixel24^ := Bilinear24(SrcX, SrcY) + else + DstPixel24^ := BackColor24; + + Inc(DstPixel24); + end; + end + else + begin + // A bit more generic 8+32bit path + for Y := 0 to DstHeight - 1 do + for X := 0 to DstWidth - 1 do + begin + CalcSourceCoordinates(X, Y, SrcX, SrcY); + + if (SrcX >= -1) and (SrcY >= -1) and (SrcX <= SrcWidth) and (SrcY <= SrcHeight) then + begin + if Bpp = 1 then + DstByte^ := Bilinear8(SrcX, SrcY) + else + PColor32Rec(DstByte)^ := Bilinear32(SrcX, SrcY) + end + else + CopyPixel(@BackColor32, DstByte, Bpp); + + Inc(DstByte, Bpp); + end; + end; + end + else + begin + case ResamplingFilter of + rfCubic: Filter := sfCatmullRom; + rfLanczos: Filter := sfLanczos; + else + Assert(False); + end; + + FilterFunction := ImagingFormats.SamplingFilterFunctions[Filter]; + FilterRadius := ImagingFormats.SamplingFilterRadii[Filter]; + + {$IFDEF USE_FILTER_TABLE} + KernelWidth := FastCeil(FilterRadius); + PrecomputeFilterWeights; + {$ENDIF} + + for Y := 0 to DstHeight - 1 do + for X := 0 to DstWidth - 1 do + begin + CalcSourceCoordinates(X, Y, SrcX, SrcY); + Pixel32 := FilterPixel(SrcX, SrcY, Bpp); + CopyPixel(@Pixel32, DstByte, Bpp); + Inc(DstByte, Bpp); + end; + end; + + FreeImage(SrcImage); + Image := DstImage; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/Imaging.pas b/resources/libraries/deskew/Imaging/Imaging.pas new file mode 100755 index 0000000..ccc5897 --- /dev/null +++ b/resources/libraries/deskew/Imaging/Imaging.pas @@ -0,0 +1,4350 @@ +{ + Vampyre Imaging Library + by Marek Mauder + http://imaginglib.sourceforge.net + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +{ This unit is heart of Imaging library. It contains basic functions for + manipulating image data as well as various image file format support.} +unit Imaging; + +{$I ImagingOptions.inc} + +interface + +uses + SysUtils, Classes, Types, ImagingTypes; + +type + { Default Imaging excepton class } + EImagingError = class(Exception); + { Raised when function receives bad image (not passed TestImage).} + EImagingBadImage = class(Exception) + public + constructor Create; + end; + + { Dynamic array of TImageData records } + TDynImageDataArray = array of TImageData; + + +{ ------------------------------------------------------------------------ + Low Level Interface Functions + ------------------------------------------------------------------------} + +{ General Functions } + +{ Initializes image (all is set to zeroes). Call this for each image + before using it (before calling every other function) to be sure there + are no random-filled bytes (which would cause errors later).} +procedure InitImage(var Image: TImageData); +{ Creates empty image of given dimensions and format. Image is filled with + transparent black color (A=0, R=0, G=0, B=0).} +function NewImage(Width, Height: LongInt; Format: TImageFormat; + var Image: TImageData): Boolean; +{ Returns True if given TImageData record is valid.} +function TestImage(const Image: TImageData): Boolean; +{ Frees given image data. Ater this call image is in the same state + as after calling InitImage. If image is not valid (dost not pass TestImage + test) it is only zeroed by calling InitImage.} +procedure FreeImage(var Image: TImageData); +{ Call FreeImage() on all images in given dynamic array and sets its + length to zero.} +procedure FreeImagesInArray(var Images: TDynImageDataArray); +{ Returns True if all TImageData records in given array are valid. Returns False + if at least one is invalid or if array is empty.} +function TestImagesInArray(const Images: TDynImageDataArray): Boolean; +{ Checks given file for every supported image file format and if + the file is in one of them returns its string identifier + (which can be used in LoadFromStream/LoadFromMem type functions). + If file is not in any of the supported formats empty string is returned.} +function DetermineFileFormat(const FileName: string): string; +{ Checks given stream for every supported image file format and if + the stream is in one of them returns its string identifier + (which can be used in LoadFromStream/LoadFromMem type functions). + If stream is not in any of the supported formats empty string is returned.} +function DetermineStreamFormat(Stream: TStream): string; +{ Checks given memory for every supported image file format and if + the memory is in one of them returns its string identifier + (which can be used in LoadFromStream/LoadFromMem type functions). + If memory is not in any of the supported formats empty string is returned.} +function DetermineMemoryFormat(Data: Pointer; Size: LongInt): string; +{ Checks that an apropriate file format is supported purely from inspecting + the given file name's extension (not contents of the file itself). + The file need not exist.} +function IsFileFormatSupported(const FileName: string): Boolean; +{ Enumerates all registered image file formats. Descriptive name, + default extension, masks (like '*.jpg,*.jfif') and some capabilities + of each format are returned. To enumerate all formats start with Index at 0 and + call EnumFileFormats with given Index in loop until it returns False (Index is + automatically increased by 1 in function's body on successful call).} +function EnumFileFormats(var Index: LongInt; var Name, DefaultExt, Masks: string; + var CanSaveImages, IsMultiImageFormat: Boolean): Boolean; + +{ Loading Functions } + +{ Loads single image from given file.} +function LoadImageFromFile(const FileName: string; var Image: TImageData): Boolean; +{ Loads single image from given stream. If function fails stream position + is not changed.} +function LoadImageFromStream(Stream: TStream; var Image: TImageData): Boolean; +{ Loads single image from given memory location.} +function LoadImageFromMemory(Data: Pointer; Size: LongInt; var Image: TImageData): Boolean; +{ Loads multiple images from given file.} +function LoadMultiImageFromFile(const FileName: string; + var Images: TDynImageDataArray): Boolean; +{ Loads multiple images from given stream. If function fails stream position + is not changed.} +function LoadMultiImageFromStream(Stream: TStream; + var Images: TDynImageDataArray): Boolean; +{ Loads multiple images from given memory location.} +function LoadMultiImageFromMemory(Data: Pointer; Size: LongInt; + var Images: TDynImageDataArray): Boolean; + +{ Saving Functions } + +{ Saves single image to given file.} +function SaveImageToFile(const FileName: string; const Image: TImageData): Boolean; +{ Saves single image to given stream. If function fails stream position + is not changed. Ext identifies desired image file format (jpg, png, dds, ...).} +function SaveImageToStream(const Ext: string; Stream: TStream; + const Image: TImageData): Boolean; +{ Saves single image to given memory location. Memory must be allocated and its + size is passed in Size parameter in which number of written bytes is returned. + Ext identifies desired image file format (jpg, png, dds, ...).} +function SaveImageToMemory(const Ext: string; Data: Pointer; var Size: LongInt; + const Image: TImageData): Boolean; +{ Saves multiple images to given file. If format supports + only single level images and there are multiple images to be saved, + they are saved as sequence of files img000.jpg, img001.jpg ....).} +function SaveMultiImageToFile(const FileName: string; + const Images: TDynImageDataArray): Boolean; +{ Saves multiple images to given stream. If format supports + only single level images and there are multiple images to be saved, + they are saved one after another to the stream. If function fails stream + position is not changed. Ext identifies desired image file format (jpg, png, dds, ...).} +function SaveMultiImageToStream(const Ext: string; Stream: TStream; + const Images: TDynImageDataArray): Boolean; +{ Saves multiple images to given memory location. If format supports + only single level images and there are multiple images to be saved, + they are saved one after another to the memory. Memory must be allocated and + its size is passed in Size parameter in which number of written bytes is returned. + Ext identifies desired image file format (jpg, png, dds, ...).} +function SaveMultiImageToMemory(const Ext: string; Data: Pointer; + var Size: LongInt; const Images: TDynImageDataArray): Boolean; + +{ Manipulation Functions } + +{ Creates identical copy of image data. Clone should be initialized + by InitImage or it should be vaild image which will be freed by CloneImage.} +function CloneImage(const Image: TImageData; var Clone: TImageData): Boolean; +{ Converts image to the given format.} +function ConvertImage(var Image: TImageData; DestFormat: TImageFormat): Boolean; +{ Flips given image. Reverses the image along its horizontal axis - the top + becomes the bottom and vice versa.} +function FlipImage(var Image: TImageData): Boolean; +{ Mirrors given image. Reverses the image along its vertical axis — the left + side becomes the right and vice versa.} +function MirrorImage(var Image: TImageData): Boolean; +{ Resizes given image to new dimensions. Nearest, bilinear, or bicubic filtering + can be used. Input Image must already be created - use NewImage to create new images.} +function ResizeImage(var Image: TImageData; NewWidth, NewHeight: LongInt; + Filter: TResizeFilter): Boolean; +{ Swaps SrcChannel and DstChannel color or alpha channels of image. + Use ChannelRed, ChannelBlue, ChannelGreen, ChannelAlpha constants to + identify channels.} +function SwapChannels(var Image: TImageData; SrcChannel, DstChannel: LongInt): Boolean; +{ Reduces the number of colors of the Image. Currently MaxColors must be in + range <2, 4096>. Color reduction works also for alpha channel. Note that for + large images and big number of colors it can be very slow. + Output format of the image is the same as input format.} +function ReduceColors(var Image: TImageData; MaxColors: LongInt): Boolean; +{ Generates mipmaps for image. Levels is the number of desired mipmaps levels + with zero (or some invalid number) meaning all possible levels.} +function GenerateMipMaps(const Image: TImageData; Levels: LongInt; + var MipMaps: TDynImageDataArray): Boolean; +{ Maps image to existing palette producing image in ifIndex8 format. + Pal must be allocated to at least Entries * SizeOf(TColor32Rec) bytes. + As resulting image is in 8bit indexed format Entries must be lower or + equal to 256.} +function MapImageToPalette(var Image: TImageData; Pal: PPalette32; + Entries: LongInt): Boolean; +{ Splits image into XChunks x YChunks subimages. Default size of each chunk is + ChunkWidth x ChunkHeight. If PreserveSize si True chunks at the edges of + the image are also ChunkWidth x ChunkHeight sized and empty space is filled + with optional Fill pixels. After calling this function XChunks contains number of + chunks along x axis and YChunks along y axis. To access chunk [X, Y] use this + index: Chunks[Y * XChunks + X].} +function SplitImage(var Image: TImageData; var Chunks: TDynImageDataArray; + ChunkWidth, ChunkHeight: LongInt; var XChunks, YChunks: LongInt; + PreserveSize: Boolean; Fill: Pointer = nil): Boolean; +{ Creates palette with MaxColors based on the colors of images in Images array. + Use it when you want to convert several images to indexed format using + single palette for all of them. If ConvertImages is True images in array + are converted to indexed format using resulting palette. if it is False + images are left intact and only resulting palatte is returned in Pal. + Pal must be allocated to have at least MaxColors entries.} +function MakePaletteForImages(var Images: TDynImageDataArray; Pal: PPalette32; + MaxColors: LongInt; ConvertImages: Boolean): Boolean; +{ Rotates image by Angle degrees counterclockwise. All angles are allowed.} +procedure RotateImage(var Image: TImageData; Angle: Single); + +{ Drawing/Pixel functions } + +{ Copies rectangular part of SrcImage to DstImage. No blending is performed - + alpha is simply copied to destination image. Operates also with + negative X and Y coordinates. + Note that copying is fastest for images in the same data format + (and slowest for images in special formats).} +function CopyRect(const SrcImage: TImageData; SrcX, SrcY, Width, Height: LongInt; + var DstImage: TImageData; DstX, DstY: LongInt): Boolean; +{ Fills given rectangle of image with given pixel fill data. Fill should point + to the pixel in the same format as the given image is in.} +function FillRect(var Image: TImageData; X, Y, Width, Height: LongInt; FillColor: Pointer): Boolean; +{ Replaces pixels with OldPixel in the given rectangle by NewPixel. + OldPixel and NewPixel should point to the pixels in the same format + as the given image is in.} +function ReplaceColor(var Image: TImageData; X, Y, Width, Height: LongInt; + OldColor, NewColor: Pointer): Boolean; +{ Stretches the contents of the source rectangle to the destination rectangle + with optional resampling. No blending is performed - alpha is + simply copied/resampled to destination image. Note that stretching is + fastest for images in the same data format (and slowest for + images in special formats).} +function StretchRect(const SrcImage: TImageData; SrcX, SrcY, SrcWidth, + SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth, + DstHeight: LongInt; Filter: TResizeFilter): Boolean; +{ Copies pixel of Image at [X, Y] to memory pointed at by Pixel. Doesn't + work with special formats.} +procedure GetPixelDirect(const Image: TImageData; X, Y: LongInt; Pixel: Pointer); +{ Copies pixel from memory pointed at by Pixel to Image at position [X, Y]. + Doesn't work with special formats.} +procedure SetPixelDirect(const Image: TImageData; X, Y: LongInt; Pixel: Pointer); +{ Function for getting pixel colors. Native pixel is read from Image and + then translated to 32 bit ARGB. Works for all image formats (except special) + so it is not very fast.} +function GetPixel32(const Image: TImageData; X, Y: LongInt): TColor32Rec; +{ Procedure for setting pixel colors. Input 32 bit ARGB color is translated to + native format and then written to Image. Works for all image formats (except special) + so it is not very fast.} +procedure SetPixel32(const Image: TImageData; X, Y: LongInt; const Color: TColor32Rec); +{ Function for getting pixel colors. Native pixel is read from Image and + then translated to FP ARGB. Works for all image formats (except special) + so it is not very fast.} +function GetPixelFP(const Image: TImageData; X, Y: LongInt): TColorFPRec; +{ Procedure for setting pixel colors. Input FP ARGB color is translated to + native format and then written to Image. Works for all image formats (except special) + so it is not very fast.} +procedure SetPixelFP(const Image: TImageData; X, Y: LongInt; const Color: TColorFPRec); + +{ Palette Functions } + +{ Allocates new palette with Entries ARGB color entries.} +procedure NewPalette(Entries: LongInt; var Pal: PPalette32); +{ Frees given palette.} +procedure FreePalette(var Pal: PPalette32); +{ Copies Count palette entries from SrcPal starting at index SrcIdx to + DstPal at index DstPal.} +procedure CopyPalette(SrcPal, DstPal: PPalette32; SrcIdx, DstIdx, Count: LongInt); +{ Returns index of color in palette or index of nearest color if exact match + is not found. Pal must have at least Entries color entries.} +function FindColor(Pal: PPalette32; Entries: LongInt; Color: TColor32): LongInt; +{ Creates grayscale palette where each color channel has the same value. + Pal must have at least Entries color entries.} +procedure FillGrayscalePalette(Pal: PPalette32; Entries: LongInt); +{ Creates palette with given bitcount for each channel. + 2^(RBits + GBits + BBits) should be equl to Entries. Examples: + (3, 3, 2) will create palette with all possible colors of R3G3B2 format + and (8, 0, 0) will create palette with 256 shades of red. + Pal must be allocated to at least Entries * SizeOf(TColor32Rec) bytes.} +procedure FillCustomPalette(Pal: PPalette32; Entries: LongInt; RBits, GBits, + BBits: Byte; Alpha: Byte = $FF); +{ Swaps SrcChannel and DstChannel color or alpha channels of palette. + Use ChannelRed, ChannelBlue, ChannelGreen, ChannelAlpha constants to + identify channels. Pal must be allocated to at least + Entries * SizeOf(TColor32Rec) bytes.} +procedure SwapChannelsOfPalette(Pal: PPalette32; Entries, SrcChannel, + DstChannel: LongInt); + +{ Options Functions } + +{ Sets value of integer option specified by OptionId parameter. + Option Ids are constans starting ImagingXXX.} +function SetOption(OptionId, Value: LongInt): Boolean; +{ Returns value of integer option specified by OptionId parameter. If OptionId is + invalid, InvalidOption is returned. Option Ids are constans + starting ImagingXXX.} +function GetOption(OptionId: LongInt): LongInt; +{ Pushes current values of all options on the stack. Returns True + if successfull (max stack depth is 8 now). } +function PushOptions: Boolean; +{ Pops back values of all options from the top of the stack. Returns True + if successfull (max stack depth is 8 now). } +function PopOptions: Boolean; + +{ Image Format Functions } + +{ Returns short information about given image format.} +function GetImageFormatInfo(Format: TImageFormat; out Info: TImageFormatInfo): Boolean; +{ Returns size in bytes of Width x Height area of pixels. Works for all formats.} +function GetPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt; + +{ IO Functions } + +{ User can set his own file IO functions used when loading from/saving to + files by this function.} +procedure SetUserFileIO(OpenProc: TOpenProc; CloseProc: TCloseProc; EofProc: TEofProc; SeekProc: + TSeekProc; TellProc: TTellProc; ReadProc: TReadProc; WriteProc: TWriteProc); +{ Sets file IO functions to Imaging default.} +procedure ResetFileIO; + +{ Raw Image IO Functions } + +procedure ReadRawImageFromFile(const FileName: string; Width, Height: Integer; + Format: TImageFormat; var Image: TImageData; Offset: Integer = 0; RowLength: Integer = 0); +procedure ReadRawImageFromStream(Stream: TStream; Width, Height: Integer; + Format: TImageFormat; var Image: TImageData; Offset: Integer = 0; RowLength: Integer = 0); +procedure ReadRawImageFromMemory(Data: Pointer; DataSize: Integer; Width, Height: Integer; + Format: TImageFormat; var Image: TImageData; Offset: Integer = 0; RowLength: Integer = 0); +procedure ReadRawImageRect(Data: Pointer; Left, Top, Width, Height: Integer; + var Image: TImageData; Offset: Integer = 0; RowLength: Integer = 0); + +procedure WriteRawImageToFile(const FileName: string; const Image: TImageData; + Offset: Integer = 0; RowLength: Integer = 0); +procedure WriteRawImageToStream(Stream: TStream; const Image: TImageData; + Offset: Integer = 0; RowLength: Integer = 0); +procedure WriteRawImageToMemory(Data: Pointer; DataSize: Integer; const Image: TImageData; + Offset: Integer = 0; RowLength: Integer = 0); +procedure WriteRawImageRect(Data: Pointer; Left, Top, Width, Height: Integer; + const Image: TImageData; Offset: Integer = 0; RowLength: Integer = 0); + +{ Convenience/helper Functions } + +procedure ResizeImageToFit(const SrcImage: TImageData; FitWidth, FitHeight: Integer; + Filter: TResizeFilter; var DestImage: TImageData); + +{ Color functions } + +{ Constructs TColor24Rec color.} +function Color24(R, G, B: Byte): TColor24Rec; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Constructs TColor32Rec color.} +function Color32(A, R, G, B: Byte): TColor32Rec; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Constructs TColor48Rec color.} +function Color48(R, G, B: Word): TColor48Rec; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Constructs TColor64Rec color.} +function Color64(A, R, G, B: Word): TColor64Rec; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Constructs TColorFPRec color.} +function ColorFP(A, R, G, B: Single): TColorFPRec; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Constructs TColorHFRec color.} +function ColorHF(A, R, G, B: THalfFloat): TColorHFRec; {$IFDEF USE_INLINE}inline;{$ENDIF} + +{ Convenience function for getting alpha component of TColor32.} +function GetAlphaValue(Color32: TColor32): Byte; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Convenience function for getting red component of TColor32.} +function GetRedValue(Color32: TColor32): Byte; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Convenience function for getting green component of TColor32.} +function GetGreenValue(Color32: TColor32): Byte; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Convenience function for getting blue component of TColor32.} +function GetBlueValue(Color32: TColor32): Byte; {$IFDEF USE_INLINE}inline;{$ENDIF} + + +{ ------------------------------------------------------------------------ + Other Imaging Stuff + ------------------------------------------------------------------------} + +type + { Set of TImageFormat enum.} + TImageFormats = set of TImageFormat; + + { Record containg set of IO functions internaly used by image loaders/savers.} + TIOFunctions = record + Open: TOpenProc; + Close: TCloseProc; + Eof: TEofProc; + Seek: TSeekProc; + Tell: TTellProc; + Read: TReadProc; + Write: TWriteProc; + end; + PIOFunctions = ^TIOFunctions; + +type + TFileFormatFeature = ( + ffLoad, + ffSave, + ffMultiImage, + ffReadOnSave, + ffProgress, + ffReadScanlines); + + TFileFormatFeatures = set of TFileFormatFeature; + + TMetadata = class; + + { Base class for various image file format loaders/savers which + descend from this class. If you want to add support for new image file + format the best way is probably to look at TImageFileFormat descendants' + implementations that are already part of Imaging.} +{$TYPEINFO ON} + TImageFileFormat = class + private + FExtensions: TStringList; + FMasks: TStringList; + function GetCanLoad: Boolean; + function GetCanSave: Boolean; + function GetIsMultiImageFormat: Boolean; + { Does various checks and actions before LoadData method is called.} + function PrepareLoad(Handle: TImagingHandle; var Images: TDynImageDataArray; + OnlyFirstFrame: Boolean): Boolean; + { Processes some actions according to result of LoadData.} + function PostLoadCheck(var Images: TDynImageDataArray; LoadResult: Boolean): Boolean; + { Helper function to be called in SaveData methods of descendants (ensures proper + index and sets FFirstIdx and FLastIdx for multi-images).} + function PrepareSave(Handle: TImagingHandle; const Images: TDynImageDataArray; + var Index: LongInt): Boolean; + { Returns file open mode used for saving images. Depends on defined Features.} + function GetSaveOpenMode: TOpenMode; + protected + FName: string; + FFeatures: TFileFormatFeatures; + FSupportedFormats: TImageFormats; + FFirstIdx, FLastIdx: LongInt; + FMetadata: TMetadata; + { Descendants must override this method and define file format name and + capabilities.} + procedure Define; virtual; + { Defines filename masks for this image file format. AMasks should be + in format '*.ext1,*.ext2,umajo.*'.} + procedure AddMasks(const AMasks: string); + function GetFormatInfo(Format: TImageFormat): TImageFormatInfo; + { Returns set of TImageData formats that can be saved in this file format + without need for conversion.} + function GetSupportedFormats: TImageFormats; virtual; + { Method which must be overrided in descendants if they' are be capable + of loading images. Images are already freed and length is set to zero + whenever this method gets called. Also Handle is assured to be valid + and contains data that passed TestFormat method's check.} + function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; + OnlyFirstFrame: Boolean): Boolean; virtual; + { Method which must be overriden in descendants if they are be capable + of saving images. Images are checked to have length >0 and + that they contain valid images. For single-image file formats + Index contain valid index to Images array (to image which should be saved). + Multi-image formats should use FFirstIdx and FLastIdx fields to + to get all images that are to be saved.} + function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; + Index: LongInt): Boolean; virtual; + { This method is called internaly by MakeCompatible when input image + is in format not supported by this file format. Image is clone of + MakeCompatible's input and Info is its extended format info.} + procedure ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); virtual; + { Returns True if given image is supported for saving by this file format. + Most file formats don't need to override this method. It checks + (in this base class) if Image's format is in SupportedFromats set. + But you may override it if you want further checks + (proper widht and height for example).} + function IsSupported(const Image: TImageData): Boolean; virtual; + public + constructor Create(AMetadata: TMetadata = nil); virtual; + destructor Destroy; override; + + { Loads images from file source.} + function LoadFromFile(const FileName: string; var Images: TDynImageDataArray; + OnlyFirstLevel: Boolean = False): Boolean; + { Loads images from stream source.} + function LoadFromStream(Stream: TStream; var Images: TDynImageDataArray; + OnlyFirstLevel: Boolean = False): Boolean; + { Loads images from memory source.} + function LoadFromMemory(Data: Pointer; Size: LongInt; + var Images: TDynImageDataArray; OnlyFirstLevel: Boolean = False): Boolean; + + { Saves images to file. If format supports only single level images and + there are multiple images to be saved, they are saved as sequence of + independent images (for example SaveToFile saves sequence of + files img000.jpg, img001.jpg ....).} + function SaveToFile(const FileName: string; const Images: TDynImageDataArray; + OnlyFirstLevel: Boolean = False): Boolean; + { Saves images to stream. If format supports only single level images and + there are multiple images to be saved, they are saved as sequence of + independent images.} + function SaveToStream(Stream: TStream; const Images: TDynImageDataArray; + OnlyFirstLevel: Boolean = False): Boolean; + { Saves images to memory. If format supports only single level images and + there are multiple images to be saved, they are saved as sequence of + independent images. Data must be already allocated and their size passed + as Size parameter, number of written bytes is then returned in the same + parameter.} + function SaveToMemory(Data: Pointer; var Size: LongInt; + const Images: TDynImageDataArray; OnlyFirstLevel: Boolean = False): Boolean; + + { Makes Image compatible with this file format (that means it is in one + of data formats in Supported formats set). If input is already + in supported format then Compatible just use value from input + (Compatible := Image) so must not free it after you are done with it + (image bits pointer points to input image's bits). + If input is not in supported format then it is cloned to Compatible + and concerted to one of supported formats (which one dependeds on + this file format). If image is cloned MustBeFreed is set to True + to indicated that you must free Compatible after you are done with it.} + function MakeCompatible(const Image: TImageData; var Compatible: TImageData; + out MustBeFreed: Boolean): Boolean; + { Returns True if data located in source identified by Handle + represent valid image in current format.} + function TestFormat(Handle: TImagingHandle): Boolean; virtual; + { Resturns True if the given FileName matches filter for this file format. + For most formats it just checks filename extensions. + It uses filename masks in from Masks property so it can recognize + filenames like this 'umajoXXXumajo.j0j' if one of themasks is + 'umajo*umajo.j?j'.} + function TestFileName(const FileName: string): Boolean; + { Descendants use this method to check if their options (registered with + constant Ids for SetOption/GetOption interface or accessible as properties + of descendants) have valid values and make necessary changes.} + procedure CheckOptionsValidity; virtual; + + { Description of this format.} + property Name: string read FName; + { Indicates whether images in this format can be loaded.} + property CanLoad: Boolean read GetCanLoad; + { Indicates whether images in this format can be saved.} + property CanSave: Boolean read GetCanSave; + { Indicates whether images in this format can contain multiple image levels.} + property IsMultiImageFormat: Boolean read GetIsMultiImageFormat; + { List of filename extensions for this format.} + property Extensions: TStringList read FExtensions; + { List of filename masks that are used to associate filenames + with TImageFileFormat descendants. Typical mask looks like + '*.bmp' or 'texture.*' (supports file formats which use filename instead + of extension to identify image files).} + property Masks: TStringList read FMasks; + { Set of TImageFormats supported by saving functions of this format. Images + can be saved only in one those formats.} + property SupportedFormats: TImageFormats read GetSupportedFormats; + end; +{$TYPEINFO OFF} + + { Class reference for TImageFileFormat class} + TImageFileFormatClass = class of TImageFileFormat; + + { Physical resolution unit.} + TResolutionUnit = ( + ruSizeInMicroMeters, // value is pixel size in micrometers + ruDpi, // value is pixels/dots per inch + ruDpm, // value is pixels/dots per meter + ruDpcm // value is pixels/dots per centimeter + ); + + { Class for storage of single metadata item.} + TMetadataItem = class + public + Id: string; + ImageIndex: Integer; + Value: Variant; + end; + + { Metadata manager class.} + TMetadata = class + private + FLoadMetaItems: TStringList; + FSaveMetaItems: TStringList; + procedure AddMetaToList(List: TStringList; const Id: string; const Value: Variant; ImageIndex: Integer); + procedure ClearMetaList(List: TStringList); + function GetMetaById(const Id: string): Variant; + function GetMetaByIdMulti(const Id: string; ImageIndex: Integer): Variant; + function GetMetaCount: Integer; + function GetMetaByIdx(Index: Integer): TMetadataItem; + function GetSaveMetaById(const Id: string): Variant; + function GetSaveMetaByIdMulti(const Id: string; ImageIndex: Integer): Variant; + procedure TranslateUnits(ResolutionUnit: TResolutionUnit; var XRes, YRes: Single); + public + constructor Create; + destructor Destroy; override; + + procedure SetMetaItem(const Id: string; const Value: Variant; ImageIndex: Integer = 0); + procedure SetMetaItemForSaving(const Id: string; const Value: Variant; ImageIndex: Integer = 0); + function HasMetaItem(const Id: string; ImageIndex: Integer = 0): Boolean; + function HasMetaItemForSaving(const Id: string; ImageIndex: Integer = 0): Boolean; + + procedure ClearMetaItems; + procedure ClearMetaItemsForSaving; + function GetMetaItemName(const Id: string; ImageIndex: Integer): string; + { Copies loaded meta items to items-for-save stack. Use this when you want to + save metadata that have been just loaded (e.g. resaving image in + different file format but keeping the metadata).} + procedure CopyLoadedMetaItemsForSaving; + + function GetPhysicalPixelSize(ResUnit: TResolutionUnit; var XSize, + YSize: Single; MetaForSave: Boolean = False; ImageIndex: Integer = 0): Boolean; + procedure SetPhysicalPixelSize(ResUnit: TResolutionUnit; XSize, YSize: Single; + MetaForSave: Boolean = False; ImageIndex: Integer = 0); + + property MetaItems[const Id: string]: Variant read GetMetaById; + property MetaItemsMulti[const Id: string; ImageIndex: Integer]: Variant read GetMetaByIdMulti; + { Number of loaded metadata items.} + property MetaItemCount: Integer read GetMetaCount; + property MetaItemsByIdx[Index: Integer]: TMetadataItem read GetMetaByIdx; + property MetaItemsForSaving[const Id: string]: Variant read GetSaveMetaById; + property MetaItemsForSavingMulti[const Id: string; ImageIndex: Integer]: Variant read GetSaveMetaByIdMulti; + end; + +const + { Metadata item id constants } + + { Physical size of one pixel in micrometers. Type of value is Float.} + SMetaPhysicalPixelSizeX = 'PhysicalPixelSizeX'; + SMetaPhysicalPixelSizeY = 'PhysicalPixelSizeY'; + { Delay for frame of animation (how long it should stay visible) in milliseconds. + Type of value is Integer.} + SMetaFrameDelay = 'FrameDelay'; + { Number of times animation should be looped (0 = infinite looping). Type is Int. } + SMetaAnimationLoops = 'AnimationLoops'; + { Gamma correction value. Type is Float.} + SMetaGamma = 'Gamma'; + { Exposure value for HDR etc. Type is Float.} + SMetaExposure = 'Exposure'; + { EXIF image metadata raw blob.} + SMetaExifBlob = 'ExifBlob'; + { XMP image metadata raw blob.} + SMetaXmpBlob = 'XmpBlob'; + { IPTC image metadata raw blob.} + SMetaIptcBlob = 'IptcBlob'; + +var + GlobalMetadata: TMetadata; + +{ Returns symbolic name of given format.} +function GetFormatName(Format: TImageFormat): string; +{ Returns string with information about given Image.} +function ImageToStr(const Image: TImageData): string; +{ Returns Imaging version string in format 'Major.Minor'.} +function GetVersionStr: string; +{ If Condition is True then TruePart is retured, otherwise FalsePart is returned.} +function IffFormat(Condition: Boolean; const TruePart, FalsePart: TImageFormat): TImageFormat; + +{ Registers new option so it can be used by SetOption and GetOption functions. + Returns True if registration was succesful - that is Id is valid and is + not already taken by another option.} +function RegisterOption(OptionId: LongInt; Variable: PLongInt): Boolean; + +{ Registers new image loader/saver so it can be used by LoadFrom/SaveTo + functions.} +procedure RegisterImageFileFormat(AClass: TImageFileFormatClass); +{ Returns image format loader/saver according to given extension + or nil if not found.} +function FindImageFileFormatByExt(const Ext: string): TImageFileFormat; +{ Returns image format loader/saver according to given filename + or nil if not found.} +function FindImageFileFormatByName(const FileName: string): TImageFileFormat; +{ Returns image format loader/saver based on its class + or nil if not found or not registered.} +function FindImageFileFormatByClass(AClass: TImageFileFormatClass): TImageFileFormat; +{ Returns number of registered image file format loaders/saver.} +function GetFileFormatCount: LongInt; +{ Returns image file format loader/saver at given index. Index must be + in range [0..GetFileFormatCount - 1] otherwise nil is returned.} +function GetFileFormatAtIndex(Index: LongInt): TImageFileFormat; +{ Returns filter string for usage with open and save picture dialogs + which contains all registered image file formats. + Set OpenFileFilter to True if you want filter for open dialog + and to False if you want save dialog filter (formats that cannot save to files + are not added then). + For open dialog filter for all known graphic files + (like All(*.jpg;*.png;....) is added too at the first index.} +function GetImageFileFormatsFilter(OpenFileFilter: Boolean): string; +{ Returns file extension (without dot) of image format selected + by given filter index. Used filter string is defined by GetImageFileFormatsFilter + function. This function can be used with save dialogs (with filters created + by GetImageFileFormatsFilter) to get the extension of file format selected + in dialog quickly. Index is in range 1..N (as FilterIndex property + of TOpenDialog/TSaveDialog)} +function GetFilterIndexExtension(Index: LongInt; OpenFileFilter: Boolean): string; +{ Returns filter index of image file format of file specified by FileName. Used filter + string is defined by GetImageFileFormatsFilter function. + Returned index is in range 1..N (as FilterIndex property of TOpenDialog/TSaveDialog)} +function GetFileNameFilterIndex(const FileName: string; OpenFileFilter: Boolean): LongInt; + +{ Returns current IO functions.} +function GetIO: TIOFunctions; +{ Raises EImagingError with given message.} +procedure RaiseImaging(const Msg: string; const Args: array of const); overload; +procedure RaiseImaging(const Msg: string); overload; {$IFDEF USE_INLINE}inline;{$ENDIF} + +const + SImagingLibTitle = 'Vampyre Imaging Library'; + +implementation + +uses +{$IFNDEF DONT_LINK_FILE_FORMATS} +{$IFNDEF DONT_LINK_BITMAP} + ImagingBitmap, +{$ENDIF} +{$IFNDEF DONT_LINK_JPEG} + ImagingJpeg, +{$ENDIF} +{$IF not Defined(DONT_LINK_PNG) or not Defined(DONT_LINK_MNG) or not Defined(DONT_LINK_JNG)} + ImagingNetworkGraphics, +{$IFEND} +{$IFNDEF DONT_LINK_GIF} + ImagingGif, +{$ENDIF} +{$IFNDEF DONT_LINK_DDS} + ImagingDds, +{$ENDIF} +{$IFNDEF DONT_LINK_TARGA} + ImagingTarga, +{$ENDIF} +{$IFNDEF DONT_LINK_PNM} + ImagingPortableMaps, +{$ENDIF} +{$IFNDEF DONT_LINK_RADHDR} + ImagingRadiance, +{$ENDIF} +{$IFNDEF DONT_LINK_EXTRAS} + ImagingExtras, +{$ENDIF} +{$ENDIF} + //ImagingDebug, + ImagingFormats, ImagingUtility, ImagingIO, Variants; + +resourcestring + SExceptMsg = 'Exception Message'; + SAllFilter = 'All Images'; + SUnknownFormat = 'Unknown and unsupported format'; + + SErrorFreeImage = 'Error while freeing image. %s'; + SErrorCloneImage = 'Error while cloning image. %s'; + SErrorFlipImage = 'Error while flipping image. %s'; + SErrorMirrorImage = 'Error while mirroring image. %s'; + SErrorResizeImage = 'Error while resizing image. %s'; + SErrorSwapImage = 'Error while swapping channels of image. %s'; + SFileFormatCanNotLoad = 'Image Format "%s" does not support loading images.'; + SFileFormatCanNotSave = 'Image Format "%s" does not support saving images.'; + SErrorNewImage = 'Error while creating image data with params: Width=%d ' + + 'Height=%d Format=%s.'; + SErrorConvertImage = 'Error while converting image to format "%s". %s'; + SImageInfo = 'Image @%p info: Width = %dpx, Height = %dpx, ' + + 'Format = %s, Size = %.0n %s, Bits @%p, Palette @%p.'; + SImageInfoInvalid = 'Access violation encountered when getting info on ' + + 'image at address %p.'; + SFileNotValid = 'File "%s" is not valid image in "%s" format.'; + SStreamNotValid = 'Stream %p does not contain valid image in "%s" format.'; + SMemoryNotValid = 'Memory %p (%d Bytes) does not contain valid image ' + + 'in "%s" format.'; + SErrorLoadingFile = 'Error while loading images from file "%s" (file format: %s).'; + SErrorLoadingStream = 'Error while loading images from stream %p (file format: %s).'; + SErrorLoadingMemory = 'Error while loading images from memory %p (%d Bytes) (file format: %s).'; + SErrorSavingFile = 'Error while saving images to file "%s" (file format: %s).'; + SErrorSavingStream = 'Error while saving images to stream %p (file format: %s).'; + SErrorSavingMemory = 'Error while saving images to memory %p (%d Bytes) (file format: %s).'; + SErrorFindColor = 'Error while finding color in palette @%p with %d entries.'; + SErrorGrayscalePalette = 'Error while filling grayscale palette @%p with %d entries.'; + SErrorCustomPalette = 'Error while filling custom palette @%p with %d entries.'; + SErrorSwapPalette = 'Error while swapping channels of palette @%p with %d entries.'; + SErrorReduceColors = 'Error while reducing number of colors of image to %d. %s'; + SErrorGenerateMipMaps = 'Error while generating %d mipmap levels for image %s'; + SImagesNotValid = 'One or more images are not valid.'; + SErrorCopyRect = 'Error while copying rect from image %s to image %s.'; + SErrorMapImage = 'Error while mapping image %s to palette.'; + SErrorFillRect = 'Error while filling rectangle X:%d Y:%d W:%d H:%d in image %s'; + SErrorSplitImage = 'Error while splitting image %s to %dx%d sized chunks.'; + SErrorMakePaletteForImages = 'Error while making %d color palette for %d images.'; + SErrorNewPalette = 'Error while creating new palette with %d entries'; + SErrorFreePalette = 'Error while freeing palette @%p'; + SErrorCopyPalette = 'Error while copying %d entries from palette @%p to @%p'; + SErrorReplaceColor = 'Error while replacing colors in rectangle X:%d Y:%d W:%d H:%d of image %s'; + SErrorRotateImage = 'Error while rotating image %s by %.2n degrees'; + SErrorStretchRect = 'Error while stretching rect from image %s to image %s.'; + SErrorEmptyStream = 'Input stream has no data. Check Position property.'; + SErrorInvalidInputImage = 'Invalid input image.'; + + SErrorBadImage = 'Bad image detected.'; + +const + // Initial size of array with options information + InitialOptions = 256; + // Max depth of the option stack + OptionStackDepth = 8; + // Do not change the default format now, its too late + DefaultImageFormat: TImageFormat = ifA8R8G8B8; + // Format used to create metadata IDs for frames loaded form multiimages. + SMetaIdForSubImage = '%s/%d'; + +type + TOptionArray = array of PLongInt; + TOptionValueArray = array of LongInt; + + TOptionStack = class(TObject) + private + FStack: array[0..OptionStackDepth - 1] of TOptionValueArray; + FPosition: LongInt; + public + constructor Create; + destructor Destroy; override; + function Push: Boolean; + function Pop: Boolean; + end; + +var + // Currently set IO functions + IO: TIOFunctions; + // List with all registered TImageFileFormat classes + ImageFileFormats: TList = nil; + // Aarray with registered options (pointers to their values) + Options: TOptionArray = nil; + // Array containing addional infomation about every image format + ImageFormatInfos: TImageFormatInfoArray; + // Stack used by PushOptions/PopOtions functions + OptionStack: TOptionStack = nil; +var + // Variable for ImagingColorReduction option + ColorReductionMask: LongInt = $FF; + // Variable for ImagingLoadOverrideFormat option + LoadOverrideFormat: TImageFormat = ifUnknown; + // Variable for ImagingSaveOverrideFormat option + SaveOverrideFormat: TImageFormat = ifUnknown; + // Variable for ImagingSaveOverrideFormat option + MipMapFilter: TSamplingFilter = sfLinear; + // Variable for ImagingBinaryTreshold option + BinaryTreshold: Integer = 128; + +{ Exceptions } + +constructor EImagingBadImage.Create; +begin + inherited Create(SErrorBadImage); +end; + +{ Internal unit functions } + +{ Modifies option value to be in the allowed range. Works only + for options registered in this unit.} +function CheckOptionValue(OptionId, Value: LongInt): LongInt; forward; +{ Sets IO functions to file IO.} +procedure SetFileIO; forward; +{ Sets IO functions to stream IO.} +procedure SetStreamIO; forward; +{ Sets IO functions to memory IO.} +procedure SetMemoryIO; forward; +{ Inits image format infos array.} +procedure InitImageFormats; forward; +{ Freew image format infos array.} +procedure FreeImageFileFormats; forward; +{ Creates options array and stack.} +procedure InitOptions; forward; +{ Frees options array and stack.} +procedure FreeOptions; forward; + +function UpdateExceptMessage(E: Exception; const MsgToPrepend: string; const Args: array of const): Exception; +begin + Result := E; + E.Message := Format(MsgToPrepend, Args) + ' ' + SExceptMsg + ': ' + E.Message +end; + +{ ------------------------------------------------------------------------ + Low Level Interface Functions + ------------------------------------------------------------------------} + +{ General Functions } + +procedure InitImage(var Image: TImageData); +begin + FillChar(Image, SizeOf(Image), 0); +end; + +function NewImage(Width, Height: LongInt; Format: TImageFormat; var Image: + TImageData): Boolean; +var + FInfo: PImageFormatInfo; +begin + Assert((Width > 0) and (Height >0)); + Assert(IsImageFormatValid(Format)); + Result := False; + FreeImage(Image); + try + Image.Width := Width; + Image.Height := Height; + // Select default data format if selected + if (Format = ifDefault) then + Image.Format := DefaultImageFormat + else + Image.Format := Format; + // Get extended format info + FInfo := ImageFormatInfos[Image.Format]; + if FInfo = nil then + begin + InitImage(Image); + Exit; + end; + // Check image dimensions and calculate its size in bytes + FInfo.CheckDimensions(FInfo.Format, Image.Width, Image.Height); + Image.Size := FInfo.GetPixelsSize(FInfo.Format, Image.Width, Image.Height); + if Image.Size = 0 then + begin + InitImage(Image); + Exit; + end; + // Image bits are allocated and set to zeroes + GetMem(Image.Bits, Image.Size); + FillChar(Image.Bits^, Image.Size, 0); + // Palette is allocated and set to zeroes + if FInfo.PaletteEntries > 0 then + begin + GetMem(Image.Palette, FInfo.PaletteEntries * SizeOf(TColor32Rec)); + FillChar(Image.Palette^, FInfo.PaletteEntries * SizeOf(TColor32Rec), 0); + end; + Result := TestImage(Image); + except + on E: Exception do + begin + FreeMem(Image.Bits); + FreeMem(Image.Palette); + InitImage(Image); + raise UpdateExceptMessage(E, SErrorNewImage, [Width, Height, GetFormatName(Format)]); + end; + end; +end; + +function TestImage(const Image: TImageData): Boolean; +begin + try + Result := (LongInt(Image.Format) >= LongInt(Low(TImageFormat))) and + (LongInt(Image.Format) <= LongInt(High(TImageFormat))) and + (ImageFormatInfos[Image.Format] <> nil) and + (Assigned(ImageFormatInfos[Image.Format].GetPixelsSize) and + (ImageFormatInfos[Image.Format].GetPixelsSize(Image.Format, + Image.Width, Image.Height) = Image.Size)); + except + // Possible int overflows or other errors + Result := False; + end; +end; + +procedure FreeImage(var Image: TImageData); +begin + try + if TestImage(Image) then + begin + FreeMemNil(Image.Bits); + FreeMemNil(Image.Palette); + end; + InitImage(Image); + except + raise UpdateExceptMessage(GetExceptObject, SErrorFreeImage, [ImageToStr(Image)]); + end; +end; + +procedure FreeImagesInArray(var Images: TDynImageDataArray); +var + I: LongInt; +begin + if Length(Images) > 0 then + begin + for I := 0 to Length(Images) - 1 do + FreeImage(Images[I]); + SetLength(Images, 0); + end; +end; + +function TestImagesInArray(const Images: TDynImageDataArray): Boolean; +var + I: LongInt; +begin + if Length(Images) > 0 then + begin + Result := True; + for I := 0 to Length(Images) - 1 do + begin + Result := Result and TestImage(Images[I]); + if not Result then + Break; + end; + end + else + Result := False; +end; + +function DetermineFileFormat(const FileName: string): string; +var + I: LongInt; + Fmt: TImageFileFormat; + Handle: TImagingHandle; +begin + Assert(FileName <> ''); + Result := ''; + SetFileIO; + Handle := IO.Open(PChar(FileName), omReadOnly); + try + // First file format according to FileName and test if the data in + // file is really in that format + for I := 0 to ImageFileFormats.Count - 1 do + begin + Fmt := TImageFileFormat(ImageFileFormats[I]); + if Fmt.TestFileName(FileName) and Fmt.TestFormat(Handle) then + begin + Result := Fmt.Extensions[0]; + Exit; + end; + end; + // No file format was found with filename search so try data-based search + for I := 0 to ImageFileFormats.Count - 1 do + begin + Fmt := TImageFileFormat(ImageFileFormats[I]); + if Fmt.TestFormat(Handle) then + begin + Result := Fmt.Extensions[0]; + Exit; + end; + end; + finally + IO.Close(Handle); + end; +end; + +function DetermineStreamFormat(Stream: TStream): string; +var + I: LongInt; + Fmt: TImageFileFormat; + Handle: TImagingHandle; +begin + Assert(Stream <> nil); + Result := ''; + SetStreamIO; + Handle := IO.Open(Pointer(Stream), omReadOnly); + try + for I := 0 to ImageFileFormats.Count - 1 do + begin + Fmt := TImageFileFormat(ImageFileFormats[I]); + if Fmt.TestFormat(Handle) then + begin + Result := Fmt.Extensions[0]; + Exit; + end; + end; + finally + IO.Close(Handle); + end; +end; + +function DetermineMemoryFormat(Data: Pointer; Size: LongInt): string; +var + I: LongInt; + Fmt: TImageFileFormat; + Handle: TImagingHandle; + IORec: TMemoryIORec; +begin + Assert((Data <> nil) and (Size > 0)); + Result := ''; + SetMemoryIO; + IORec.Data := Data; + IORec.Position := 0; + IORec.Size := Size; + Handle := IO.Open(@IORec, omReadOnly); + try + for I := 0 to ImageFileFormats.Count - 1 do + begin + Fmt := TImageFileFormat(ImageFileFormats[I]); + if Fmt.TestFormat(Handle) then + begin + Result := Fmt.Extensions[0]; + Exit; + end; + end; + finally + IO.Close(Handle); + end; +end; + +function IsFileFormatSupported(const FileName: string): Boolean; +begin + Result := FindImageFileFormatByName(FileName) <> nil; +end; + +function EnumFileFormats(var Index: LongInt; var Name, DefaultExt, Masks: string; + var CanSaveImages, IsMultiImageFormat: Boolean): Boolean; +var + FileFmt: TImageFileFormat; +begin + FileFmt := GetFileFormatAtIndex(Index); + Result := FileFmt <> nil; + if Result then + begin + Name := FileFmt.Name; + DefaultExt := FileFmt.Extensions[0]; + Masks := FileFmt.Masks.DelimitedText; + CanSaveImages := FileFmt.CanSave; + IsMultiImageFormat := FileFmt.IsMultiImageFormat; + Inc(Index); + end + else + begin + Name := ''; + DefaultExt := ''; + Masks := ''; + CanSaveImages := False; + IsMultiImageFormat := False; + end; +end; + +{ Loading Functions } + +function LoadImageFromFile(const FileName: string; var Image: TImageData): + Boolean; +var + Format: TImageFileFormat; + IArray: TDynImageDataArray; + I: LongInt; +begin + Assert(FileName <> ''); + Result := False; + Format := FindImageFileFormatByExt(DetermineFileFormat(FileName)); + if Format <> nil then + begin + FreeImage(Image); + Result := Format.LoadFromFile(FileName, IArray, True); + if Result and (Length(IArray) > 0) then + begin + Image := IArray[0]; + for I := 1 to Length(IArray) - 1 do + FreeImage(IArray[I]); + end + else + Result := False; + end; +end; + +function LoadImageFromStream(Stream: TStream; var Image: TImageData): Boolean; +var + Format: TImageFileFormat; + IArray: TDynImageDataArray; + I: LongInt; +begin + Assert(Stream <> nil); + if Stream.Size - Stream.Position = 0 then + RaiseImaging(SErrorEmptyStream, []); + Result := False; + Format := FindImageFileFormatByExt(DetermineStreamFormat(Stream)); + if Format <> nil then + begin + FreeImage(Image); + Result := Format.LoadFromStream(Stream, IArray, True); + if Result and (Length(IArray) > 0) then + begin + Image := IArray[0]; + for I := 1 to Length(IArray) - 1 do + FreeImage(IArray[I]); + end + else + Result := False; + end; +end; + +function LoadImageFromMemory(Data: Pointer; Size: LongInt; var Image: TImageData): Boolean; +var + Format: TImageFileFormat; + IArray: TDynImageDataArray; + I: LongInt; +begin + Assert((Data <> nil) and (Size > 0)); + Result := False; + Format := FindImageFileFormatByExt(DetermineMemoryFormat(Data, Size)); + if Format <> nil then + begin + FreeImage(Image); + Result := Format.LoadFromMemory(Data, Size, IArray, True); + if Result and (Length(IArray) > 0) then + begin + Image := IArray[0]; + for I := 1 to Length(IArray) - 1 do + FreeImage(IArray[I]); + end + else + Result := False; + end; +end; + +function LoadMultiImageFromFile(const FileName: string; var Images: + TDynImageDataArray): Boolean; +var + Format: TImageFileFormat; +begin + Assert(FileName <> ''); + Result := False; + Format := FindImageFileFormatByExt(DetermineFileFormat(FileName)); + if Format <> nil then + begin + FreeImagesInArray(Images); + Result := Format.LoadFromFile(FileName, Images); + end; +end; + +function LoadMultiImageFromStream(Stream: TStream; var Images: TDynImageDataArray): Boolean; +var + Format: TImageFileFormat; +begin + Assert(Stream <> nil); + if Stream.Size - Stream.Position = 0 then + RaiseImaging(SErrorEmptyStream, []); + Result := False; + Format := FindImageFileFormatByExt(DetermineStreamFormat(Stream)); + if Format <> nil then + begin + FreeImagesInArray(Images); + Result := Format.LoadFromStream(Stream, Images); + end; +end; + +function LoadMultiImageFromMemory(Data: Pointer; Size: LongInt; + var Images: TDynImageDataArray): Boolean; +var + Format: TImageFileFormat; +begin + Assert((Data <> nil) and (Size > 0)); + Result := False; + Format := FindImageFileFormatByExt(DetermineMemoryFormat(Data, Size)); + if Format <> nil then + begin + FreeImagesInArray(Images); + Result := Format.LoadFromMemory(Data, Size, Images); + end; +end; + +{ Saving Functions } + +function SaveImageToFile(const FileName: string; const Image: TImageData): Boolean; +var + Format: TImageFileFormat; + IArray: TDynImageDataArray; +begin + Assert(FileName <> ''); + Result := False; + Format := FindImageFileFormatByName(FileName); + if Format <> nil then + begin + SetLength(IArray, 1); + IArray[0] := Image; + Result := Format.SaveToFile(FileName, IArray, True); + end; +end; + +function SaveImageToStream(const Ext: string; Stream: TStream; + const Image: TImageData): Boolean; +var + Format: TImageFileFormat; + IArray: TDynImageDataArray; +begin + Assert((Ext <> '') and (Stream <> nil)); + Result := False; + Format := FindImageFileFormatByExt(Ext); + if Format <> nil then + begin + SetLength(IArray, 1); + IArray[0] := Image; + Result := Format.SaveToStream(Stream, IArray, True); + end; +end; + +function SaveImageToMemory(const Ext: string; Data: Pointer; var Size: LongInt; + const Image: TImageData): Boolean; +var + Format: TImageFileFormat; + IArray: TDynImageDataArray; +begin + Assert((Ext <> '') and (Data <> nil) and (Size > 0)); + Result := False; + Format := FindImageFileFormatByExt(Ext); + if Format <> nil then + begin + SetLength(IArray, 1); + IArray[0] := Image; + Result := Format.SaveToMemory(Data, Size, IArray, True); + end; +end; + +function SaveMultiImageToFile(const FileName: string; + const Images: TDynImageDataArray): Boolean; +var + Format: TImageFileFormat; +begin + Assert(FileName <> ''); + Result := False; + Format := FindImageFileFormatByName(FileName); + if Format <> nil then + Result := Format.SaveToFile(FileName, Images); +end; + +function SaveMultiImageToStream(const Ext: string; Stream: TStream; + const Images: TDynImageDataArray): Boolean; +var + Format: TImageFileFormat; +begin + Assert((Ext <> '') and (Stream <> nil)); + Result := False; + Format := FindImageFileFormatByExt(Ext); + if Format <> nil then + Result := Format.SaveToStream(Stream, Images); +end; + +function SaveMultiImageToMemory(const Ext: string; Data: Pointer; + var Size: LongInt; const Images: TDynImageDataArray): Boolean; +var + Format: TImageFileFormat; +begin + Assert((Ext <> '') and (Data <> nil) and (Size > 0)); + Result := False; + Format := FindImageFileFormatByExt(Ext); + if Format <> nil then + Result := Format.SaveToMemory(Data, Size, Images); +end; + +{ Manipulation Functions } + +function CloneImage(const Image: TImageData; var Clone: TImageData): Boolean; +var + Info: PImageFormatInfo; +begin + Result := False; + if TestImage(Image) then + try + if TestImage(Clone) and (Image.Bits <> Clone.Bits) then + FreeImage(Clone) + else + InitImage(Clone); + + Info := ImageFormatInfos[Image.Format]; + Clone.Width := Image.Width; + Clone.Height := Image.Height; + Clone.Format := Image.Format; + Clone.Size := Image.Size; + + if Info.PaletteEntries > 0 then + begin + GetMem(Clone.Palette, Info.PaletteEntries * SizeOf(TColor32Rec)); + Move(Image.Palette^, Clone.Palette^, Info.PaletteEntries * + SizeOf(TColor32Rec)); + end; + + GetMem(Clone.Bits, Clone.Size); + Move(Image.Bits^, Clone.Bits^, Clone.Size); + Result := True; + except + raise UpdateExceptMessage(GetExceptObject, SErrorCloneImage, [ImageToStr(Image)]); + end; +end; + +function ConvertImage(var Image: TImageData; DestFormat: TImageFormat): Boolean; +var + NewData: Pointer; + NewPal: PPalette32; + NewSize, NumPixels: LongInt; + SrcInfo, DstInfo: PImageFormatInfo; +begin + Assert(IsImageFormatValid(DestFormat)); + Result := False; + if TestImage(Image) then + with Image do + try + // If default format is set we use DefaultImageFormat + if DestFormat = ifDefault then + DestFormat := DefaultImageFormat; + SrcInfo := ImageFormatInfos[Format]; + DstInfo := ImageFormatInfos[DestFormat]; + if SrcInfo = DstInfo then + begin + // There is nothing to convert - src is alredy in dest format + Result := True; + Exit; + end; + // Exit Src or Dest format is invalid + if (SrcInfo = nil) or (DstInfo = nil) then Exit; + // If dest format is just src with swapped channels we call + // SwapChannels instead + if (SrcInfo.RBSwapFormat = DestFormat) and + (DstInfo.RBSwapFormat = SrcInfo.Format) then + begin + Result := SwapChannels(Image, ChannelRed, ChannelBlue); + Image.Format := SrcInfo.RBSwapFormat; + Exit; + end; + + if (not SrcInfo.IsSpecial) and (not DstInfo.IsSpecial) then + begin + NumPixels := Width * Height; + NewSize := NumPixels * DstInfo.BytesPerPixel; + GetMem(NewData, NewSize); + FillChar(NewData^, NewSize, 0); + GetMem(NewPal, DstInfo.PaletteEntries * SizeOf(TColor32Rec)); + FillChar(NewPal^, DstInfo.PaletteEntries * SizeOf(TColor32Rec), 0); + + if SrcInfo.IsIndexed then + begin + // Source: indexed format + if DstInfo.IsIndexed then + IndexToIndex(NumPixels, Bits, NewData, SrcInfo, DstInfo, Palette, NewPal) + else if DstInfo.HasGrayChannel then + IndexToGray(NumPixels, Bits, NewData, SrcInfo, DstInfo, Palette) + else if DstInfo.IsFloatingPoint then + IndexToFloat(NumPixels, Bits, NewData, SrcInfo, DstInfo, Palette) + else + IndexToChannel(NumPixels, Bits, NewData, SrcInfo, DstInfo, Palette); + end + else if SrcInfo.HasGrayChannel then + begin + // Source: grayscale format + if DstInfo.IsIndexed then + GrayToIndex(NumPixels, Bits, NewData, SrcInfo, DstInfo, NewPal) + else if DstInfo.HasGrayChannel then + GrayToGray(NumPixels, Bits, NewData, SrcInfo, DstInfo) + else if DstInfo.IsFloatingPoint then + GrayToFloat(NumPixels, Bits, NewData, SrcInfo, DstInfo) + else + GrayToChannel(NumPixels, Bits, NewData, SrcInfo, DstInfo); + end + else if SrcInfo.IsFloatingPoint then + begin + // Source: floating point format + if DstInfo.IsIndexed then + FloatToIndex(NumPixels, Bits, NewData, SrcInfo, DstInfo, NewPal) + else if DstInfo.HasGrayChannel then + FloatToGray(NumPixels, Bits, NewData, SrcInfo, DstInfo) + else if DstInfo.IsFloatingPoint then + FloatToFloat(NumPixels, Bits, NewData, SrcInfo, DstInfo) + else + FloatToChannel(NumPixels, Bits, NewData, SrcInfo, DstInfo); + end + else + begin + // Source: standard multi channel image + if DstInfo.IsIndexed then + ChannelToIndex(NumPixels, Bits, NewData, SrcInfo, DstInfo, NewPal) + else if DstInfo.HasGrayChannel then + ChannelToGray(NumPixels, Bits, NewData, SrcInfo, DstInfo) + else if DstInfo.IsFloatingPoint then + ChannelToFloat(NumPixels, Bits, NewData, SrcInfo, DstInfo) + else + ChannelToChannel(NumPixels, Bits, NewData, SrcInfo, DstInfo); + end; + + FreeMemNil(Bits); + FreeMemNil(Palette); + Format := DestFormat; + Bits := NewData; + Size := NewSize; + Palette := NewPal; + end + else + ConvertSpecial(Image, SrcInfo, DstInfo); + + Assert(SrcInfo.Format <> Image.Format); + + Result := True; + except + raise UpdateExceptMessage(GetExceptObject, SErrorConvertImage, [GetFormatName(DestFormat), ImageToStr(Image)]); + end; +end; + +function FlipImage(var Image: TImageData): Boolean; +var + P1, P2, Buff: Pointer; + WidthBytes, I: LongInt; + OldFmt: TImageFormat; +begin + Result := False; + OldFmt := Image.Format; + if TestImage(Image) then + with Image do + try + if ImageFormatInfos[OldFmt].IsSpecial then + ConvertImage(Image, ifDefault); + + WidthBytes := Width * ImageFormatInfos[Format].BytesPerPixel; + GetMem(Buff, WidthBytes); + try + // Swap all scanlines of image + for I := 0 to Height div 2 - 1 do + begin + P1 := @PByteArray(Bits)[I * WidthBytes]; + P2 := @PByteArray(Bits)[(Height - I - 1) * WidthBytes]; + Move(P1^, Buff^, WidthBytes); + Move(P2^, P1^, WidthBytes); + Move(Buff^, P2^, WidthBytes); + end; + finally + FreeMemNil(Buff); + end; + + if OldFmt <> Format then + ConvertImage(Image, OldFmt); + + Result := True; + except + RaiseImaging(SErrorFlipImage, [ImageToStr(Image)]); + end; +end; + +function MirrorImage(var Image: TImageData): Boolean; +var + Scanline: PByte; + Buff: TColorFPRec; + Bpp, Y, X, WidthDiv2, WidthBytes, XLeft, XRight: LongInt; + OldFmt: TImageFormat; +begin + Result := False; + OldFmt := Image.Format; + if TestImage(Image) then + with Image do + try + if ImageFormatInfos[OldFmt].IsSpecial then + ConvertImage(Image, ifDefault); + + Bpp := ImageFormatInfos[Format].BytesPerPixel; + WidthDiv2 := Width div 2; + WidthBytes := Width * Bpp; + // Mirror all pixels on each scanline of image + for Y := 0 to Height - 1 do + begin + Scanline := @PByteArray(Bits)[Y * WidthBytes]; + XLeft := 0; + XRight := (Width - 1) * Bpp; + for X := 0 to WidthDiv2 - 1 do + begin + CopyPixel(@PByteArray(Scanline)[XLeft], @Buff, Bpp); + CopyPixel(@PByteArray(Scanline)[XRight], + @PByteArray(Scanline)[XLeft], Bpp); + CopyPixel(@Buff, @PByteArray(Scanline)[XRight], Bpp); + Inc(XLeft, Bpp); + Dec(XRight, Bpp); + end; + end; + + if OldFmt <> Format then + ConvertImage(Image, OldFmt); + + Result := True; + except + RaiseImaging(SErrorMirrorImage, [ImageToStr(Image)]); + end; +end; + +function ResizeImage(var Image: TImageData; NewWidth, NewHeight: LongInt; + Filter: TResizeFilter): Boolean; +var + WorkImage: TImageData; +begin + Assert((NewWidth > 0) and (NewHeight > 0), 'New width or height is zero.'); + Result := False; + if TestImage(Image) and ((Image.Width <> NewWidth) or (Image.Height <> NewHeight)) then + try + InitImage(WorkImage); + // Create new image with desired dimensions + NewImage(NewWidth, NewHeight, Image.Format, WorkImage); + // Stretch pixels from old image to new one + StretchRect(Image, 0, 0, Image.Width, Image.Height, + WorkImage, 0, 0, WorkImage.Width, WorkImage.Height, Filter); + // Free old image and assign new image to it + FreeMemNil(Image.Bits); + if Image.Palette <> nil then + begin + FreeMem(WorkImage.Palette); + WorkImage.Palette := Image.Palette; + end; + Image := WorkImage; + Result := True; + except + raise UpdateExceptMessage(GetExceptObject, SErrorResizeImage, [ImageToStr(Image)]); + end; +end; + +function SwapChannels(var Image: TImageData; SrcChannel, DstChannel: LongInt): Boolean; +var + I, NumPixels: LongInt; + Info: PImageFormatInfo; + Swap, Alpha: Word; + Data: PByte; + Pix64: TColor64Rec; + PixF: TColorFPRec; + SwapF: Single; +begin + Assert((SrcChannel in [0..3]) and (DstChannel in [0..3])); + Result := False; + if TestImage(Image) and (SrcChannel <> DstChannel) then + with Image do + try + NumPixels := Width * Height; + Info := ImageFormatInfos[Format]; + Data := Bits; + + if (Info.Format = ifR8G8B8) or ((Info.Format = ifA8R8G8B8) and + (SrcChannel <> ChannelAlpha) and (DstChannel <> ChannelAlpha)) then + begin + // Swap channels of most common formats R8G8B8 and A8R8G8B8 (no alpha) + for I := 0 to NumPixels - 1 do + with PColor24Rec(Data)^ do + begin + Swap := Channels[SrcChannel]; + Channels[SrcChannel] := Channels[DstChannel]; + Channels[DstChannel] := Swap; + Inc(Data, Info.BytesPerPixel); + end; + end + else if Info.IsIndexed then + begin + // Swap palette channels of indexed images + SwapChannelsOfPalette(Palette, Info.PaletteEntries, SrcChannel, DstChannel) + end + else if Info.IsFloatingPoint then + begin + // Swap channels of floating point images + for I := 0 to NumPixels - 1 do + begin + FloatGetSrcPixel(Data, Info, PixF); + with PixF do + begin + SwapF := Channels[SrcChannel]; + Channels[SrcChannel] := Channels[DstChannel]; + Channels[DstChannel] := SwapF; + end; + FloatSetDstPixel(Data, Info, PixF); + Inc(Data, Info.BytesPerPixel); + end; + end + else if Info.IsSpecial then + begin + // Swap channels of special format images + ConvertImage(Image, ifDefault); + SwapChannels(Image, SrcChannel, DstChannel); + ConvertImage(Image, Info.Format); + end + else if Info.HasGrayChannel and Info.HasAlphaChannel and + ((SrcChannel = ChannelAlpha) or (DstChannel = ChannelAlpha)) then + begin + for I := 0 to NumPixels - 1 do + begin + // If we have grayscale image with alpha and alpha is channel + // to be swapped, we swap it. No other alternative for gray images, + // just alpha and something + GrayGetSrcPixel(Data, Info, Pix64, Alpha); + Swap := Alpha; + Alpha := Pix64.A; + Pix64.A := Swap; + GraySetDstPixel(Data, Info, Pix64, Alpha); + Inc(Data, Info.BytesPerPixel); + end; + end + else + begin + // Then do general swap on other channel image formats + for I := 0 to NumPixels - 1 do + begin + ChannelGetSrcPixel(Data, Info, Pix64); + with Pix64 do + begin + Swap := Channels[SrcChannel]; + Channels[SrcChannel] := Channels[DstChannel]; + Channels[DstChannel] := Swap; + end; + ChannelSetDstPixel(Data, Info, Pix64); + Inc(Data, Info.BytesPerPixel); + end; + end; + + Result := True; + except + RaiseImaging(SErrorSwapImage, [ImageToStr(Image)]); + end; +end; + +function ReduceColors(var Image: TImageData; MaxColors: LongInt): Boolean; +var + TmpInfo: TImageFormatInfo; + Data, Index: PWord; + I, NumPixels: LongInt; + Pal: PPalette32; + Col:PColor32Rec; + OldFmt: TImageFormat; +begin + Result := False; + if TestImage(Image) then + with Image do + try + // First create temp image info and allocate output bits and palette + MaxColors := ClampInt(MaxColors, 2, High(Word)); + OldFmt := Format; + FillChar(TmpInfo, SizeOf(TmpInfo), 0); + TmpInfo.PaletteEntries := MaxColors; + TmpInfo.BytesPerPixel := 2; + NumPixels := Width * Height; + GetMem(Data, NumPixels * TmpInfo.BytesPerPixel); + GetMem(Pal, MaxColors * SizeOf(TColor32Rec)); + ConvertImage(Image, ifA8R8G8B8); + // We use median cut algorithm to create reduced palette and to + // fill Data with indices to this palette + ReduceColorsMedianCut(NumPixels, Bits, PByte(Data), + ImageFormatInfos[Format], @TmpInfo, MaxColors, ColorReductionMask, Pal); + Col := Bits; + Index := Data; + // Then we write reduced colors to the input image + for I := 0 to NumPixels - 1 do + begin + Col.Color := Pal[Index^].Color; + Inc(Col); + Inc(Index); + end; + FreeMemNil(Data); + FreeMemNil(Pal); + // And convert it to its original format + ConvertImage(Image, OldFmt); + Result := True; + except + RaiseImaging(SErrorReduceColors, [MaxColors, ImageToStr(Image)]); + end; +end; + +function GenerateMipMaps(const Image: TImageData; Levels: LongInt; + var MipMaps: TDynImageDataArray): Boolean; +var + Width, Height, I, Count: LongInt; + Info: TImageFormatInfo; + CompatibleCopy: TImageData; +begin + Result := False; + if TestImage(Image) then + try + Width := Image.Width; + Height := Image.Height; + // We compute number of possible mipmap levels and if + // the given levels are invalid or zero we use this value + Count := GetNumMipMapLevels(Width, Height); + if (Levels <= 0) or (Levels > Count) then + Levels := Count; + + // If we have special format image we create copy to allow pixel access. + // This is also done in FillMipMapLevel which is called for each level + // but then the main big image would be converted to compatible + // for every level. + GetImageFormatInfo(Image.Format, Info); + if Info.IsSpecial then + begin + InitImage(CompatibleCopy); + CloneImage(Image, CompatibleCopy); + ConvertImage(CompatibleCopy, ifDefault); + end + else + CompatibleCopy := Image; + + FreeImagesInArray(MipMaps); + SetLength(MipMaps, Levels); + CloneImage(Image, MipMaps[0]); + + for I := 1 to Levels - 1 do + begin + Width := Width shr 1; + Height := Height shr 1; + if Width < 1 then Width := 1; + if Height < 1 then Height := 1; + FillMipMapLevel(CompatibleCopy, Width, Height, MipMaps[I]); + end; + + if CompatibleCopy.Format <> MipMaps[0].Format then + begin + // Must convert smaller levels to proper format + for I := 1 to High(MipMaps) do + ConvertImage(MipMaps[I], MipMaps[0].Format); + FreeImage(CompatibleCopy); + end; + + Result := True; + except + RaiseImaging(SErrorGenerateMipMaps, [Levels, ImageToStr(Image)]); + end; +end; + +function MapImageToPalette(var Image: TImageData; Pal: PPalette32; + Entries: LongInt): Boolean; + + function FindNearestColor(Pal: PPalette32; Entries: LongInt; Col: TColor32Rec): LongInt; + var + I, MinDif, Dif: LongInt; + begin + Result := 0; + MinDif := 1020; + for I := 0 to Entries - 1 do + with Pal[I] do + begin + Dif := Abs(R - Col.R); + if Dif > MinDif then Continue; + Dif := Dif + Abs(G - Col.G); + if Dif > MinDif then Continue; + Dif := Dif + Abs(B - Col.B); + if Dif > MinDif then Continue; + Dif := Dif + Abs(A - Col.A); + if Dif < MinDif then + begin + MinDif := Dif; + Result := I; + end; + end; + end; + +var + I, MaxEntries: LongInt; + PIndex: PByte; + PColor: PColor32Rec; + CloneARGB: TImageData; + Info: PImageFormatInfo; +begin + Assert((Entries >= 2) and (Entries <= 256)); + Result := False; + + if TestImage(Image) then + try + // We create clone of source image in A8R8G8B8 and + // then recreate source image in ifIndex8 format + // with palette taken from Pal parameter + InitImage(CloneARGB); + CloneImage(Image, CloneARGB); + ConvertImage(CloneARGB, ifA8R8G8B8); + FreeImage(Image); + NewImage(CloneARGB.Width, CloneARGB.Height, ifIndex8, Image); + + Info := ImageFormatInfos[Image.Format]; + MaxEntries := Min(Info.PaletteEntries, Entries); + Move(Pal^, Image.Palette^, MaxEntries * SizeOf(TColor32Rec)); + PIndex := Image.Bits; + PColor := CloneARGB.Bits; + + // For every pixel of ARGB clone we find closest color in + // given palette and assign its index to resulting image's pixel + // procedure used here is very slow but simple and memory usage friendly + // (contrary to other methods) + for I := 0 to Image.Width * Image.Height - 1 do + begin + PIndex^ := Byte(FindNearestColor(Image.Palette, MaxEntries, PColor^)); + Inc(PIndex); + Inc(PColor); + end; + + FreeImage(CloneARGB); + Result := True; + except + raise UpdateExceptMessage(GetExceptObject, SErrorMapImage, [ImageToStr(Image)]); + end; +end; + +function SplitImage(var Image: TImageData; var Chunks: TDynImageDataArray; + ChunkWidth, ChunkHeight: LongInt; var XChunks, YChunks: LongInt; + PreserveSize: Boolean; Fill: Pointer): Boolean; +var + X, Y, XTrunc, YTrunc: LongInt; + NotOnEdge: Boolean; + Info: PImageFormatInfo; + OldFmt: TImageFormat; +begin + Assert((ChunkWidth > 0) and (ChunkHeight > 0)); + Result := False; + OldFmt := Image.Format; + FreeImagesInArray(Chunks); + + if TestImage(Image) then + try + Info := ImageFormatInfos[Image.Format]; + if Info.IsSpecial then + ConvertImage(Image, ifDefault); + + // We compute make sure that chunks are not larger than source image or negative + ChunkWidth := ClampInt(ChunkWidth, 0, Image.Width); + ChunkHeight := ClampInt(ChunkHeight, 0, Image.Height); + // Number of chunks along X and Y axes is computed + XChunks := Trunc(Ceil(Image.Width / ChunkWidth)); + YChunks := Trunc(Ceil(Image.Height / ChunkHeight)); + SetLength(Chunks, XChunks * YChunks); + + // For every chunk we create new image and copy a portion of + // the source image to it. If chunk is on the edge of the source image + // we fill enpty space with Fill pixel data if PreserveSize is set or + // make the chunk smaller if it is not set + for Y := 0 to YChunks - 1 do + for X := 0 to XChunks - 1 do + begin + // Determine if current chunk is on the edge of original image + NotOnEdge := ((X < XChunks - 1) and (Y < YChunks - 1)) or + ((Image.Width mod ChunkWidth = 0) and (Image.Height mod ChunkHeight = 0)); + + if PreserveSize or NotOnEdge then + begin + // We should preserve chunk sizes or we are somewhere inside original image + NewImage(ChunkWidth, ChunkHeight, Image.Format, Chunks[Y * XChunks + X]); + if (not NotOnEdge) and (Fill <> nil) then + FillRect(Chunks[Y * XChunks + X], 0, 0, ChunkWidth, ChunkHeight, Fill); + CopyRect(Image, X * ChunkWidth, Y * ChunkHeight, ChunkWidth, ChunkHeight, + Chunks[Y * XChunks + X], 0, 0); + end + else + begin + // Create smaller edge chunk + XTrunc := Image.Width - X * ChunkWidth; + YTrunc := Image.Height - Y * ChunkHeight; + NewImage(XTrunc, YTrunc, Image.Format, Chunks[Y * XChunks + X]); + CopyRect(Image, X * ChunkWidth, Y * ChunkHeight, XTrunc, YTrunc, + Chunks[Y * XChunks + X], 0, 0); + end; + + // If source image is in indexed format we copy its palette to chunk + if Info.IsIndexed then + begin + Move(Image.Palette^, Chunks[Y * XChunks + X].Palette^, + Info.PaletteEntries * SizeOf(TColor32Rec)); + end; + end; + + if OldFmt <> Image.Format then + begin + ConvertImage(Image, OldFmt); + for X := 0 to Length(Chunks) - 1 do + ConvertImage(Chunks[X], OldFmt); + end; + + Result := True; + except + raise UpdateExceptMessage(GetExceptObject, SErrorSplitImage, + [ImageToStr(Image), ChunkWidth, ChunkHeight]); + end; +end; + +function MakePaletteForImages(var Images: TDynImageDataArray; Pal: PPalette32; + MaxColors: LongInt; ConvertImages: Boolean): Boolean; +var + I: Integer; + SrcInfo, DstInfo: PImageFormatInfo; + Target, TempImage: TImageData; + DstFormat: TImageFormat; +begin + Assert((Pal <> nil) and (MaxColors > 0)); + Result := False; + InitImage(TempImage); + + if TestImagesInArray(Images) then + try + // Null the color histogram + ReduceColorsMedianCut(0, nil, nil, nil, nil, 0, 0, nil, [raCreateHistogram]); + for I := 0 to Length(Images) - 1 do + begin + SrcInfo := ImageFormatInfos[Images[I].Format]; + if SrcInfo.IsIndexed or SrcInfo.IsSpecial then + begin + // create temp image in supported format for updating histogram + CloneImage(Images[I], TempImage); + ConvertImage(TempImage, ifA8R8G8B8); + SrcInfo := ImageFormatInfos[TempImage.Format]; + end + else + TempImage := Images[I]; + + // Update histogram with colors of each input image + ReduceColorsMedianCut(TempImage.Width * TempImage.Height, TempImage.Bits, + nil, SrcInfo, nil, MaxColors, ColorReductionMask, nil, [raUpdateHistogram]); + + if Images[I].Bits <> TempImage.Bits then + FreeImage(TempImage); + end; + // Construct reduced color map from the histogram + ReduceColorsMedianCut(0, nil, nil, nil, nil, MaxColors, ColorReductionMask, + Pal, [raMakeColorMap]); + + if ConvertImages then + begin + DstFormat := ifIndex8; + DstInfo := ImageFormatInfos[DstFormat]; + MaxColors := Min(DstInfo.PaletteEntries, MaxColors); + + for I := 0 to Length(Images) - 1 do + begin + SrcInfo := ImageFormatInfos[Images[I].Format]; + if SrcInfo.IsIndexed or SrcInfo.IsSpecial then + begin + // If source image is in format not supported by ReduceColorsMedianCut + // we convert it + ConvertImage(Images[I], ifA8R8G8B8); + SrcInfo := ImageFormatInfos[Images[I].Format]; + end; + + InitImage(Target); + NewImage(Images[I].Width, Images[I].Height, DstFormat, Target); + // We map each input image to reduced palette and replace + // image in array with mapped image + ReduceColorsMedianCut(Images[I].Width * Images[I].Height, Images[I].Bits, + Target.Bits, SrcInfo, DstInfo, MaxColors, 0, nil, [raMapImage]); + Move(Pal^, Target.Palette^, MaxColors * SizeOf(TColor32Rec)); + + FreeImage(Images[I]); + Images[I] := Target; + end; + end; + Result := True; + except + RaiseImaging(SErrorMakePaletteForImages, [MaxColors, Length(Images)]); + end; +end; + +procedure RotateImage(var Image: TImageData; Angle: Single); +var + OldFmt: TImageFormat; + + procedure XShear(var Src, Dst: TImageData; Row, Offset, Weight, Bpp: Integer); + var + I, J, XPos: Integer; + PixSrc, PixLeft, PixOldLeft: TColor32Rec; + LineDst: PByteArray; + SrcPtr: PColor32; + begin + SrcPtr := @PByteArray(Src.Bits)[Row * Src.Width * Bpp]; + LineDst := @PByteArray(Dst.Bits)[Row * Dst.Width * Bpp]; + PixOldLeft.Color := 0; + + for I := 0 to Src.Width - 1 do + begin + CopyPixel(SrcPtr, @PixSrc, Bpp); + for J := 0 to Bpp - 1 do + PixLeft.Channels[J] := MulDiv(PixSrc.Channels[J], Weight, 256); + + XPos := I + Offset; + if (XPos >= 0) and (XPos < Dst.Width) then + begin + for J := 0 to Bpp - 1 do + PixSrc.Channels[J] := ClampToByte(PixSrc.Channels[J] - (PixLeft.Channels[J] - PixOldLeft.Channels[J])); + CopyPixel(@PixSrc, @LineDst[XPos * Bpp], Bpp); + end; + PixOldLeft := PixLeft; + Inc(PByte(SrcPtr), Bpp); + end; + + XPos := Src.Width + Offset; + if XPos < Dst.Width then + CopyPixel(@PixOldLeft, @LineDst[XPos * Bpp], Bpp); + end; + + procedure YShear(var Src, Dst: TImageData; Col, Offset, Weight, Bpp: Integer); + var + I, J, YPos: Integer; + PixSrc, PixLeft, PixOldLeft: TColor32Rec; + SrcPtr: PByte; + begin + SrcPtr := @PByteArray(Src.Bits)[Col * Bpp]; + PixOldLeft.Color := 0; + + for I := 0 to Src.Height - 1 do + begin + CopyPixel(SrcPtr, @PixSrc, Bpp); + for J := 0 to Bpp - 1 do + PixLeft.Channels[J] := MulDiv(PixSrc.Channels[J], Weight, 256); + + YPos := I + Offset; + if (YPos >= 0) and (YPos < Dst.Height) then + begin + for J := 0 to Bpp - 1 do + PixSrc.Channels[J] := ClampToByte(PixSrc.Channels[J] - (PixLeft.Channels[J] - PixOldLeft.Channels[J])); + CopyPixel(@PixSrc, @PByteArray(Dst.Bits)[(YPos * Dst.Width + Col) * Bpp], Bpp); + end; + PixOldLeft := PixLeft; + Inc(SrcPtr, Src.Width * Bpp); + end; + + YPos := Src.Height + Offset; + if YPos < Dst.Height then + CopyPixel(@PixOldLeft, @PByteArray(Dst.Bits)[(YPos * Dst.Width + Col) * Bpp], Bpp); + end; + + procedure Rotate45(var Image: TImageData; Angle: Single); + var + TempImage1, TempImage2: TImageData; + AngleRad, AngleTan, AngleSin, AngleCos, Shear: Single; + I, DstWidth, DstHeight, SrcWidth, SrcHeight, Bpp: Integer; + SrcFmt, TempFormat: TImageFormat; + Info: TImageFormatInfo; + begin + AngleRad := Angle * Pi / 180; + AngleSin := Sin(AngleRad); + AngleCos := Cos(AngleRad); + AngleTan := Sin(AngleRad / 2) / Cos(AngleRad / 2); + SrcWidth := Image.Width; + SrcHeight := Image.Height; + SrcFmt := Image.Format; + + if not (SrcFmt in [ifR8G8B8..ifX8R8G8B8, ifGray8..ifGray32, ifA16Gray16]) then + ConvertImage(Image, ifA8R8G8B8); + + TempFormat := Image.Format; + GetImageFormatInfo(TempFormat, Info); + Bpp := Info.BytesPerPixel; + + // 1st shear (horizontal) + DstWidth := Trunc(SrcWidth + SrcHeight * Abs(AngleTan) + 0.5); + DstHeight := SrcHeight; + InitImage(TempImage1); + NewImage(DstWidth, DstHeight, TempFormat, TempImage1); + + for I := 0 to DstHeight - 1 do + begin + if AngleTan >= 0 then + Shear := (I + 0.5) * AngleTan + else + Shear := (I - DstHeight + 0.5) * AngleTan; + XShear(Image, TempImage1, I, Floor(Shear), Trunc(255 * (Shear - Floor(Shear)) + 1), Bpp); + end; + + // 2nd shear (vertical) + FreeImage(Image); + DstHeight := Trunc(SrcWidth * Abs(AngleSin) + SrcHeight * AngleCos + 0.5) + 1; + InitImage(TempImage2); + NewImage(DstWidth, DstHeight, TempFormat, TempImage2); + + if AngleSin >= 0 then + Shear := (SrcWidth - 1) * AngleSin + else + Shear := (SrcWidth - DstWidth) * -AngleSin; + + for I := 0 to DstWidth - 1 do + begin + YShear(TempImage1, TempImage2, I, Floor(Shear), Trunc(255 * (Shear - Floor(Shear)) + 1), Bpp); + Shear := Shear - AngleSin; + end; + + // 3rd shear (horizontal) + FreeImage(TempImage1); + DstWidth := Trunc(SrcHeight * Abs(AngleSin) + SrcWidth * AngleCos + 0.5) + 1; + NewImage(DstWidth, DstHeight, TempFormat, Image); + + if AngleSin >= 0 then + Shear := (SrcWidth - 1) * AngleSin * -AngleTan + else + Shear := ((SrcWidth - 1) * -AngleSin + (1 - DstHeight)) * AngleTan; + + for I := 0 to DstHeight - 1 do + begin + XShear(TempImage2, Image, I, Floor(Shear), Trunc(255 * (Shear - Floor(Shear)) + 1), Bpp); + Shear := Shear + AngleTan; + end; + + FreeImage(TempImage2); + if Image.Format <> SrcFmt then + ConvertImage(Image, SrcFmt); + end; + + procedure RotateMul90(var Image: TImageData; Angle: Integer); + var + RotImage: TImageData; + X, Y, BytesPerPixel: Integer; + RotPix, Pix: PByte; + begin + InitImage(RotImage); + BytesPerPixel := ImageFormatInfos[Image.Format].BytesPerPixel; + + if ((Angle = 90) or (Angle = 270)) and (Image.Width <> Image.Height) then + NewImage(Image.Height, Image.Width, Image.Format, RotImage) + else + NewImage(Image.Width, Image.Height, Image.Format, RotImage); + + RotPix := RotImage.Bits; + case Angle of + 90: + begin + for Y := 0 to RotImage.Height - 1 do + begin + Pix := @PByteArray(Image.Bits)[(Image.Width - Y - 1) * BytesPerPixel]; + for X := 0 to RotImage.Width - 1 do + begin + CopyPixel(Pix, RotPix, BytesPerPixel); + Inc(RotPix, BytesPerPixel); + Inc(Pix, Image.Width * BytesPerPixel); + end; + end; + end; + 180: + begin + Pix := @PByteArray(Image.Bits)[((Image.Height - 1) * Image.Width + + (Image.Width - 1)) * BytesPerPixel]; + for Y := 0 to RotImage.Height - 1 do + for X := 0 to RotImage.Width - 1 do + begin + CopyPixel(Pix, RotPix, BytesPerPixel); + Inc(RotPix, BytesPerPixel); + Dec(Pix, BytesPerPixel); + end; + end; + 270: + begin + for Y := 0 to RotImage.Height - 1 do + begin + Pix := @PByteArray(Image.Bits)[((Image.Height - 1) * Image.Width + Y) * BytesPerPixel]; + for X := 0 to RotImage.Width - 1 do + begin + CopyPixel(Pix, RotPix, BytesPerPixel); + Inc(RotPix, BytesPerPixel); + Dec(Pix, Image.Width * BytesPerPixel); + end; + end; + end; + end; + + FreeMemNil(Image.Bits); + RotImage.Palette := Image.Palette; + Image := RotImage; + end; + +begin + if TestImage(Image) then + try + while Angle >= 360 do + Angle := Angle - 360; + while Angle < 0 do + Angle := Angle + 360; + + if (Angle = 0) or (Abs(Angle) = 360) then + Exit; + + OldFmt := Image.Format; + if ImageFormatInfos[Image.Format].IsSpecial then + ConvertImage(Image, ifDefault); + + if (Angle > 45) and (Angle <= 135) then + begin + RotateMul90(Image, 90); + Angle := Angle - 90; + end + else if (Angle > 135) and (Angle <= 225) then + begin + RotateMul90(Image, 180); + Angle := Angle - 180; + end + else if (Angle > 225) and (Angle <= 315) then + begin + RotateMul90(Image, 270); + Angle := Angle - 270; + end; + + if Angle <> 0 then + Rotate45(Image, Angle); + + if OldFmt <> Image.Format then + ConvertImage(Image, OldFmt); + + except + raise UpdateExceptMessage(GetExceptObject, SErrorRotateImage, [ImageToStr(Image), Angle]); + end; +end; + +{ Drawing/Pixel functions } + +function CopyRect(const SrcImage: TImageData; SrcX, SrcY, Width, Height: LongInt; + var DstImage: TImageData; DstX, DstY: LongInt): Boolean; +var + Info: PImageFormatInfo; + I, SrcWidthBytes, DstWidthBytes, MoveBytes: LongInt; + SrcPointer, DstPointer: PByte; + WorkImage: TImageData; + OldFormat: TImageFormat; +begin + Result := False; + OldFormat := ifUnknown; + if TestImage(SrcImage) and TestImage(DstImage) then + try + // Make sure we are still copying image to image, not invalid pointer to protected memory + ClipCopyBounds(SrcX, SrcY, Width, Height, DstX, DstY, SrcImage.Width, SrcImage.Height, + Rect(0, 0, DstImage.Width, DstImage.Height)); + + if (Width > 0) and (Height > 0) then + begin + Info := ImageFormatInfos[DstImage.Format]; + if Info.IsSpecial then + begin + // If dest image is in special format we convert it to default + OldFormat := Info.Format; + ConvertImage(DstImage, ifDefault); + Info := ImageFormatInfos[DstImage.Format]; + end; + if SrcImage.Format <> DstImage.Format then + begin + // If images are in different format source is converted to dest's format + InitImage(WorkImage); + CloneImage(SrcImage, WorkImage); + ConvertImage(WorkImage, DstImage.Format); + end + else + WorkImage := SrcImage; + + MoveBytes := Width * Info.BytesPerPixel; + DstWidthBytes := DstImage.Width * Info.BytesPerPixel; + DstPointer := @PByteArray(DstImage.Bits)[DstY * DstWidthBytes + + DstX * Info.BytesPerPixel]; + SrcWidthBytes := WorkImage.Width * Info.BytesPerPixel; + SrcPointer := @PByteArray(WorkImage.Bits)[SrcY * SrcWidthBytes + + SrcX * Info.BytesPerPixel]; + + for I := 0 to Height - 1 do + begin + Move(SrcPointer^, DstPointer^, MoveBytes); + Inc(SrcPointer, SrcWidthBytes); + Inc(DstPointer, DstWidthBytes); + end; + // If dest image was in special format we convert it back + if OldFormat <> ifUnknown then + ConvertImage(DstImage, OldFormat); + // Working image must be freed if it is not the same as source image + if WorkImage.Bits <> SrcImage.Bits then + FreeImage(WorkImage); + + Result := True; + end; + except + RaiseImaging(SErrorCopyRect, [ImageToStr(SrcImage), ImageToStr(DstImage)]); + end; +end; + +function FillRect(var Image: TImageData; X, Y, Width, Height: LongInt; + FillColor: Pointer): Boolean; +var + Info: PImageFormatInfo; + I, J, ImageWidthBytes, RectWidthBytes, Bpp: Longint; + LinePointer, PixPointer: PByte; + OldFmt: TImageFormat; +begin + Result := False; + if TestImage(Image) then + try + ClipRectBounds(X, Y, Width, Height, Rect(0, 0, Image.Width, Image.Height)); + + if (Width > 0) and (Height > 0) then + begin + OldFmt := Image.Format; + if ImageFormatInfos[OldFmt].IsSpecial then + ConvertImage(Image, ifDefault); + + Info := ImageFormatInfos[Image.Format]; + Bpp := Info.BytesPerPixel; + ImageWidthBytes := Image.Width * Bpp; + RectWidthBytes := Width * Bpp; + LinePointer := @PByteArray(Image.Bits)[Y * ImageWidthBytes + X * Bpp]; + + for I := 0 to Height - 1 do + begin + case Bpp of + 1: FillMemoryByte(LinePointer, RectWidthBytes, PByte(FillColor)^); + 2: FillMemoryWord(LinePointer, RectWidthBytes, PWord(FillColor)^); + 4: FillMemoryLongWord(LinePointer, RectWidthBytes, PLongWord(FillColor)^); + else + PixPointer := LinePointer; + for J := 0 to Width - 1 do + begin + CopyPixel(FillColor, PixPointer, Bpp); + Inc(PixPointer, Bpp); + end; + end; + Inc(LinePointer, ImageWidthBytes); + end; + + if OldFmt <> Image.Format then + ConvertImage(Image, OldFmt); + end; + + Result := True; + except + RaiseImaging(SErrorFillRect, [X, Y, Width, Height, ImageToStr(Image)]); + end; +end; + +function ReplaceColor(var Image: TImageData; X, Y, Width, Height: LongInt; + OldColor, NewColor: Pointer): Boolean; +var + Info: PImageFormatInfo; + I, J, WidthBytes, Bpp: Longint; + LinePointer, PixPointer: PByte; + OldFmt: TImageFormat; +begin + Assert((OldColor <> nil) and (NewColor <> nil)); + Result := False; + if TestImage(Image) then + try + ClipRectBounds(X, Y, Width, Height, Rect(0, 0, Image.Width, Image.Height)); + + if (Width > 0) and (Height > 0) then + begin + OldFmt := Image.Format; + if ImageFormatInfos[OldFmt].IsSpecial then + ConvertImage(Image, ifDefault); + + Info := ImageFormatInfos[Image.Format]; + Bpp := Info.BytesPerPixel; + WidthBytes := Image.Width * Bpp; + LinePointer := @PByteArray(Image.Bits)[Y * WidthBytes + X * Bpp]; + + for I := 0 to Height - 1 do + begin + PixPointer := LinePointer; + for J := 0 to Width - 1 do + begin + if ComparePixels(PixPointer, OldColor, Bpp) then + CopyPixel(NewColor, PixPointer, Bpp); + Inc(PixPointer, Bpp); + end; + Inc(LinePointer, WidthBytes); + end; + + if OldFmt <> Image.Format then + ConvertImage(Image, OldFmt); + end; + + Result := True; + except + RaiseImaging(SErrorReplaceColor, [X, Y, Width, Height, ImageToStr(Image)]); + end; +end; + +function StretchRect(const SrcImage: TImageData; SrcX, SrcY, SrcWidth, + SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth, + DstHeight: LongInt; Filter: TResizeFilter): Boolean; +var + Info: PImageFormatInfo; + WorkImage: TImageData; + OldFormat: TImageFormat; + Resampling: TSamplingFilter; +begin + Result := False; + OldFormat := ifUnknown; + if TestImage(SrcImage) and TestImage(DstImage) then + try + // Make sure we are still copying image to image, not invalid pointer to protected memory + ClipStretchBounds(SrcX, SrcY, SrcWidth, SrcHeight, DstX, DstY, DstWidth, DstHeight, + SrcImage.Width, SrcImage.Height, Rect(0, 0, DstImage.Width, DstImage.Height)); + + if (SrcWidth = DstWidth) and (SrcHeight = DstHeight) then + begin + // If source and dest rectangles have the same size call CopyRect + Result := CopyRect(SrcImage, SrcX, SrcY, SrcWidth, SrcHeight, DstImage, DstX, DstY); + end + else if (SrcWidth > 0) and (SrcHeight > 0) and (DstWidth > 0) and (DstHeight > 0) then + begin + // If source and dest rectangles don't have the same size we do stretch + Info := ImageFormatInfos[DstImage.Format]; + + if Info.IsSpecial then + begin + // If dest image is in special format we convert it to default + OldFormat := Info.Format; + ConvertImage(DstImage, ifDefault); + Info := ImageFormatInfos[DstImage.Format]; + end; + + if SrcImage.Format <> DstImage.Format then + begin + // If images are in different format source is converted to dest's format + InitImage(WorkImage); + CloneImage(SrcImage, WorkImage); + ConvertImage(WorkImage, DstImage.Format); + end + else + WorkImage := SrcImage; + + // Only pixel resize is supported for indexed images + if Info.IsIndexed then + Filter := rfNearest; + + if Filter = rfNearest then + begin + StretchNearest(WorkImage, SrcX, SrcY, SrcWidth, SrcHeight, + DstImage, DstX, DstY, DstWidth, DstHeight); + end + else + begin + Resampling := sfNearest; + case Filter of + rfBilinear: Resampling := sfLinear; + rfBicubic: Resampling := DefaultCubicFilter; + rfLanczos: Resampling := sfLanczos; + end; + StretchResample(WorkImage, SrcX, SrcY, SrcWidth, SrcHeight, + DstImage, DstX, DstY, DstWidth, DstHeight, Resampling); + end; + + // If dest image was in special format we convert it back + if OldFormat <> ifUnknown then + ConvertImage(DstImage, OldFormat); + // Working image must be freed if it is not the same as source image + if WorkImage.Bits <> SrcImage.Bits then + FreeImage(WorkImage); + + Result := True; + end; + except + RaiseImaging(SErrorStretchRect, [ImageToStr(SrcImage), ImageToStr(DstImage)]); + end; +end; + +procedure GetPixelDirect(const Image: TImageData; X, Y: LongInt; Pixel: Pointer); +var + BytesPerPixel: LongInt; +begin + Assert(Pixel <> nil); + BytesPerPixel := ImageFormatInfos[Image.Format].BytesPerPixel; + CopyPixel(@PByteArray(Image.Bits)[(Y * Image.Width + X) * BytesPerPixel], + Pixel, BytesPerPixel); +end; + +procedure SetPixelDirect(const Image: TImageData; X, Y: LongInt; Pixel: Pointer); +var + BytesPerPixel: LongInt; +begin + Assert(Pixel <> nil); + BytesPerPixel := ImageFormatInfos[Image.Format].BytesPerPixel; + CopyPixel(Pixel, @PByteArray(Image.Bits)[(Y * Image.Width + X) * BytesPerPixel], + BytesPerPixel); +end; + +function GetPixel32(const Image: TImageData; X, Y: LongInt): TColor32Rec; +var + Info: PImageFormatInfo; + Data: PByte; +begin + Info := ImageFormatInfos[Image.Format]; + Data := @PByteArray(Image.Bits)[(Y * Image.Width + X) * Info.BytesPerPixel]; + Result := GetPixel32Generic(Data, Info, Image.Palette); +end; + +procedure SetPixel32(const Image: TImageData; X, Y: LongInt; const Color: TColor32Rec); +var + Info: PImageFormatInfo; + Data: PByte; +begin + Info := ImageFormatInfos[Image.Format]; + Data := @PByteArray(Image.Bits)[(Y * Image.Width + X) * Info.BytesPerPixel]; + SetPixel32Generic(Data, Info, Image.Palette, Color); +end; + +function GetPixelFP(const Image: TImageData; X, Y: LongInt): TColorFPRec; +var + Info: PImageFormatInfo; + Data: PByte; +begin + Info := ImageFormatInfos[Image.Format]; + Data := @PByteArray(Image.Bits)[(Y * Image.Width + X) * Info.BytesPerPixel]; + Result := GetPixelFPGeneric(Data, Info, Image.Palette); +end; + +procedure SetPixelFP(const Image: TImageData; X, Y: LongInt; const Color: TColorFPRec); +var + Info: PImageFormatInfo; + Data: PByte; +begin + Info := ImageFormatInfos[Image.Format]; + Data := @PByteArray(Image.Bits)[(Y * Image.Width + X) * Info.BytesPerPixel]; + SetPixelFPGeneric(Data, Info, Image.Palette, Color); +end; + +{ Palette Functions } + +procedure NewPalette(Entries: LongInt; var Pal: PPalette32); +begin + Assert((Entries > 2) and (Entries <= 65535)); + try + GetMem(Pal, Entries * SizeOf(TColor32Rec)); + FillChar(Pal^, Entries * SizeOf(TColor32Rec), $FF); + except + RaiseImaging(SErrorNewPalette, [Entries]); + end; +end; + +procedure FreePalette(var Pal: PPalette32); +begin + try + FreeMemNil(Pal); + except + RaiseImaging(SErrorFreePalette, [Pal]); + end; +end; + +procedure CopyPalette(SrcPal, DstPal: PPalette32; SrcIdx, DstIdx, Count: LongInt); +begin + Assert((SrcPal <> nil) and (DstPal <> nil)); + Assert((SrcIdx >= 0) and (DstIdx >= 0) and (Count >= 0)); + try + Move(SrcPal[SrcIdx], DstPal[DstIdx], Count * SizeOf(TColor32Rec)); + except + RaiseImaging(SErrorCopyPalette, [Count, SrcPal, DstPal]); + end; +end; + +function FindColor(Pal: PPalette32; Entries: LongInt; Color: TColor32): + LongInt; +var + Col: TColor32Rec; + I, MinDif, Dif: LongInt; +begin + Assert(Pal <> nil); + Result := -1; + Col.Color := Color; + try + // First try to find exact match + for I := 0 to Entries - 1 do + with Pal[I] do + begin + if (A = Col.A) and (R = Col.R) and + (G = Col.G) and (B = Col.B) then + begin + Result := I; + Exit; + end; + end; + + // If exact match was not found, find nearest color + MinDif := 1020; + for I := 0 to Entries - 1 do + with Pal[I] do + begin + Dif := Abs(R - Col.R); + if Dif > MinDif then Continue; + Dif := Dif + Abs(G - Col.G); + if Dif > MinDif then Continue; + Dif := Dif + Abs(B - Col.B); + if Dif > MinDif then Continue; + Dif := Dif + Abs(A - Col.A); + if Dif < MinDif then + begin + MinDif := Dif; + Result := I; + end; + end; + except + RaiseImaging(SErrorFindColor, [Pal, Entries]); + end; +end; + +procedure FillGrayscalePalette(Pal: PPalette32; Entries: LongInt); +var + I: LongInt; +begin + Assert(Pal <> nil); + try + for I := 0 to Entries - 1 do + with Pal[I] do + begin + A := $FF; + R := Byte(I); + G := Byte(I); + B := Byte(I); + end; + except + RaiseImaging(SErrorGrayscalePalette, [Pal, Entries]); + end; +end; + +procedure FillCustomPalette(Pal: PPalette32; Entries: LongInt; RBits, GBits, + BBits: Byte; Alpha: Byte = $FF); +var + I, TotalBits, MaxEntries: LongInt; +begin + Assert(Pal <> nil); + TotalBits := RBits + GBits + BBits; + MaxEntries := Min(Pow2Int(TotalBits), Entries); + FillChar(Pal^, Entries * SizeOf(TColor32Rec), 0); + try + for I := 0 to MaxEntries - 1 do + with Pal[I] do + begin + A := Alpha; + if RBits > 0 then + R := ((I shr Max(0, GBits + BBits - 1)) and (1 shl RBits - 1)) * 255 div (1 shl RBits - 1); + if GBits > 0 then + G := ((I shr Max(0, BBits - 1)) and (1 shl GBits - 1)) * 255 div (1 shl GBits - 1); + if BBits > 0 then + B := ((I shr 0) and (1 shl BBits - 1)) * 255 div (1 shl BBits - 1); + end; + except + RaiseImaging(SErrorCustomPalette, [Pal, Entries]); + end; +end; + +procedure SwapChannelsOfPalette(Pal: PPalette32; Entries, SrcChannel, + DstChannel: LongInt); +var + I: LongInt; + Swap: Byte; +begin + Assert(Pal <> nil); + Assert((SrcChannel in [0..3]) and (DstChannel in [0..3])); + try + for I := 0 to Entries - 1 do + with Pal[I] do + begin + Swap := Channels[SrcChannel]; + Channels[SrcChannel] := Channels[DstChannel]; + Channels[DstChannel] := Swap; + end; + except + RaiseImaging(SErrorSwapPalette, [Pal, Entries]); + end; +end; + +{ Options Functions } + +function SetOption(OptionId, Value: LongInt): Boolean; +begin + Result := False; + if (OptionId >= 0) and (OptionId < Length(Options)) and + (Options[OptionID] <> nil) then + begin + Options[OptionID]^ := CheckOptionValue(OptionId, Value); + Result := True; + end; +end; + +function GetOption(OptionId: LongInt): LongInt; +begin + Result := InvalidOption; + if (OptionId >= 0) and (OptionId < Length(Options)) and + (Options[OptionID] <> nil) then + begin + Result := Options[OptionID]^; + end; +end; + +function PushOptions: Boolean; +begin + Result := OptionStack.Push; +end; + +function PopOptions: Boolean; +begin + Result := OptionStack.Pop; +end; + +{ Image Format Functions } + +function GetImageFormatInfo(Format: TImageFormat; out Info: TImageFormatInfo): Boolean; +begin + FillChar(Info, SizeOf(Info), 0); + if ImageFormatInfos[Format] <> nil then + begin + Info := ImageFormatInfos[Format]^; + Result := True; + end + else + Result := False; +end; + +function GetPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt; +begin + if ImageFormatInfos[Format] <> nil then + Result := ImageFormatInfos[Format].GetPixelsSize(Format, Width, Height) + else + Result := 0; +end; + +{ IO Functions } + +procedure SetUserFileIO(OpenProc: TOpenProc; + CloseProc: TCloseProc; EofProc: TEofProc; SeekProc: TSeekProc; TellProc: + TTellProc; ReadProc: TReadProc; WriteProc: TWriteProc); +begin + FileIO.Open := OpenProc; + FileIO.Close := CloseProc; + FileIO.Eof := EofProc; + FileIO.Seek := SeekProc; + FileIO.Tell := TellProc; + FileIO.Read := ReadProc; + FileIO.Write := WriteProc; +end; + +procedure ResetFileIO; +begin + FileIO := OriginalFileIO; +end; + +{ Raw Image IO Functions } + +procedure ReadRawImage(Handle: TImagingHandle; Width, Height: Integer; + Format: TImageFormat; var Image: TImageData; Offset, RowLength: Integer); +var + WidthBytes, I: Integer; + Info: PImageFormatInfo; +begin + Info := ImageFormatInfos[Format]; + // Calc scanline size + WidthBytes := Info.GetPixelsSize(Format, Width, 1); + if RowLength = 0 then + RowLength := WidthBytes; + // Create new image if needed - don't need to allocate new one if there is already + // one with desired size and format + if (Image.Width <> Width) or (Image.Height <> Height) or (Image.Format <> Format) then + NewImage(Width, Height, Format, Image); + // Move past the header + IO.Seek(Handle, Offset, smFromCurrent); + // Read scanlines from input + for I := 0 to Height - 1 do + begin + IO.Read(Handle, @PByteArray(Image.Bits)[I * WidthBytes], WidthBytes); + IO.Seek(Handle, RowLength - WidthBytes, smFromCurrent); + end; +end; + +procedure ReadRawImageFromFile(const FileName: string; Width, Height: Integer; + Format: TImageFormat; var Image: TImageData; Offset, RowLength: Integer); +var + Handle: TImagingHandle; +begin + Assert(FileName <> ''); + // Set IO ops to file ops and open given file + SetFileIO; + Handle := IO.Open(PChar(FileName), omReadOnly); + try + ReadRawImage(Handle, Width, Height, Format, Image, Offset, RowLength); + finally + IO.Close(Handle); + end; +end; + +procedure ReadRawImageFromStream(Stream: TStream; Width, Height: Integer; + Format: TImageFormat; var Image: TImageData; Offset, RowLength: Integer); +var + Handle: TImagingHandle; +begin + Assert(Stream <> nil); + if Stream.Size - Stream.Position = 0 then + RaiseImaging(SErrorEmptyStream, []); + // Set IO ops to stream ops and open given stream + SetStreamIO; + Handle := IO.Open(Pointer(Stream), omReadOnly); + try + ReadRawImage(Handle, Width, Height, Format, Image, Offset, RowLength); + finally + IO.Close(Handle); + end; +end; + +procedure ReadRawImageFromMemory(Data: Pointer; DataSize: Integer; Width, Height: Integer; + Format: TImageFormat; var Image: TImageData; Offset, RowLength: Integer); +var + Handle: TImagingHandle; + MemRec: TMemoryIORec; +begin + Assert((Data <> nil) and (DataSize > 0)); + // Set IO ops to memory ops and open given stream + SetMemoryIO; + MemRec := PrepareMemIO(Data, DataSize); + Handle := IO.Open(@MemRec, omReadOnly); + try + ReadRawImage(Handle, Width, Height, Format, Image, Offset, RowLength); + finally + IO.Close(Handle); + end; +end; + +procedure ReadRawImageRect(Data: Pointer; Left, Top, Width, Height: Integer; + var Image: TImageData; Offset, RowLength: Integer); +var + DestScanBytes, RectBytes, I: Integer; + Info: PImageFormatInfo; + Src, Dest: PByte; +begin + Assert(Data <> nil); + Assert((Left + Width <= Image.Width) and (Top + Height <= Image.Height)); + Info := ImageFormatInfos[Image.Format]; + + // Calc scanline size + DestScanBytes := Info.GetPixelsSize(Info.Format, Image.Width, 1); + RectBytes := Info.GetPixelsSize(Info.Format, Width, 1); + if RowLength = 0 then + RowLength := RectBytes; + + Src := Data; + Dest := @PByteArray(Image.Bits)[Top * DestScanBytes + Info.GetPixelsSize(Info.Format, Left, 1)]; + // Move past the header + Inc(Src, Offset); + + // Read lines into rect in the existing image + for I := 0 to Height - 1 do + begin + Move(Src^, Dest^, RectBytes); + Inc(Src, RowLength); + Inc(Dest, DestScanBytes); + end; +end; + +procedure WriteRawImage(Handle: TImagingHandle; const Image: TImageData; + Offset, RowLength: Integer); +var + WidthBytes, I: Integer; + Info: PImageFormatInfo; +begin + Info := ImageFormatInfos[Image.Format]; + // Calc scanline size + WidthBytes := Info.GetPixelsSize(Image.Format, Image.Width, 1); + if RowLength = 0 then + RowLength := WidthBytes; + // Move past the header + IO.Seek(Handle, Offset, smFromCurrent); + // Write scanlines to output + for I := 0 to Image.Height - 1 do + begin + IO.Write(Handle, @PByteArray(Image.Bits)[I * WidthBytes], WidthBytes); + IO.Seek(Handle, RowLength - WidthBytes, smFromCurrent); + end; +end; + +procedure WriteRawImageToFile(const FileName: string; const Image: TImageData; + Offset, RowLength: Integer); +var + Handle: TImagingHandle; +begin + Assert(FileName <> ''); + // Set IO ops to file ops and open given file + SetFileIO; + Handle := IO.Open(PChar(FileName), omCreate); + try + WriteRawImage(Handle, Image, Offset, RowLength); + finally + IO.Close(Handle); + end; +end; + +procedure WriteRawImageToStream(Stream: TStream; const Image: TImageData; + Offset, RowLength: Integer); +var + Handle: TImagingHandle; +begin + Assert(Stream <> nil); + // Set IO ops to stream ops and open given stream + SetStreamIO; + Handle := IO.Open(Pointer(Stream), omCreate); + try + WriteRawImage(Handle, Image, Offset, RowLength); + finally + IO.Close(Handle); + end; +end; + +procedure WriteRawImageToMemory(Data: Pointer; DataSize: Integer; const Image: TImageData; + Offset, RowLength: Integer); +var + Handle: TImagingHandle; + MemRec: TMemoryIORec; +begin + Assert((Data <> nil) and (DataSize > 0)); + // Set IO ops to memory ops and open given stream + SetMemoryIO; + MemRec := PrepareMemIO(Data, DataSize); + Handle := IO.Open(@MemRec, omCreate); + try + WriteRawImage(Handle, Image, Offset, RowLength); + finally + IO.Close(Handle); + end; +end; + +procedure WriteRawImageRect(Data: Pointer; Left, Top, Width, Height: Integer; + const Image: TImageData; Offset, RowLength: Integer); +var + SrcScanBytes, RectBytes, I: Integer; + Info: PImageFormatInfo; + Src, Dest: PByte; +begin + Assert(Data <> nil); + Assert((Left + Width <= Image.Width) and (Top + Height <= Image.Height)); + Info := ImageFormatInfos[Image.Format]; + + // Calc scanline size + SrcScanBytes := Info.GetPixelsSize(Info.Format, Image.Width, 1); + RectBytes := Info.GetPixelsSize(Info.Format, Width, 1); + if RowLength = 0 then + RowLength := RectBytes; + + Src := @PByteArray(Image.Bits)[Top * SrcScanBytes + Info.GetPixelsSize(Info.Format, Left, 1)]; + Dest := Data; + // Move past the header + Inc(Dest, Offset); + + // Write lines from rect of the existing image + for I := 0 to Height - 1 do + begin + Move(Src^, Dest^, RectBytes); + Inc(Dest, RowLength); + Inc(Src, SrcScanBytes); + end; +end; + +{ Convenience/helper Functions } + +procedure ResizeImageToFit(const SrcImage: TImageData; FitWidth, FitHeight: Integer; + Filter: TResizeFilter; var DestImage: TImageData); +var + CurSize, FitSize, DestSize: TSize; +begin + if not TestImage(SrcImage) then + raise EImagingError.Create(SErrorInvalidInputImage); + + FitSize.CX := FitWidth; + FitSize.CY := FitHeight; + CurSize.CX := SrcImage.Width; + CurSize.CY := SrcImage.Height; + DestSize := ImagingUtility.ScaleSizeToFit(CurSize, FitSize); + + NewImage(Max(DestSize.CX, 1), Max(DestSize.CY, 1), SrcImage.Format, DestImage); + if SrcImage.Palette <> nil then + CopyPalette(SrcImage.Palette, DestImage.Palette, 0, 0, ImageFormatInfos[SrcImage.Format].PaletteEntries); + + StretchRect(SrcImage, 0, 0, CurSize.CX, CurSize.CY, DestImage, 0, 0, + DestSize.CX, DestSize.CY, Filter); +end; + +{ Color constructor functions } + +function Color24(R, G, B: Byte): TColor24Rec; +begin + Result.R := R; + Result.G := G; + Result.B := B; +end; + +function Color32(A, R, G, B: Byte): TColor32Rec; +begin + Result.A := A; + Result.R := R; + Result.G := G; + Result.B := B; +end; + +function Color48(R, G, B: Word): TColor48Rec; +begin + Result.R := R; + Result.G := G; + Result.B := B; +end; + +function Color64(A, R, G, B: Word): TColor64Rec; +begin + Result.A := A; + Result.R := R; + Result.G := G; + Result.B := B; +end; + +function ColorFP(A, R, G, B: Single): TColorFPRec; +begin + Result.A := A; + Result.R := R; + Result.G := G; + Result.B := B; +end; + +function ColorHF(A, R, G, B: THalfFloat): TColorHFRec; +begin + Result.A := A; + Result.R := R; + Result.G := G; + Result.B := B; +end; + +function GetAlphaValue(Color32: TColor32): Byte; +begin + Result := Color32 shr 24; +end; + +function GetRedValue(Color32: TColor32): Byte; +begin + Result := (Color32 shr 16) and $FF; +end; + +function GetGreenValue(Color32: TColor32): Byte; +begin + Result := (Color32 shr 8) and $FF; +end; + +function GetBlueValue(Color32: TColor32): Byte; +begin + Result := Color32 and $FF; +end; + +{ ------------------------------------------------------------------------ + Other Imaging Stuff + ------------------------------------------------------------------------} + +function GetFormatName(Format: TImageFormat): string; +begin + if ImageFormatInfos[Format] <> nil then + Result := ImageFormatInfos[Format].Name + else + Result := SUnknownFormat; +end; + +function ImageToStr(const Image: TImageData): string; +var + ImgSize: Integer; +begin + if TestImage(Image) then + with Image do + begin + ImgSize := Size; + if ImgSize > 8192 then + ImgSize := ImgSize div 1024; + Result := SysUtils.Format(SImageInfo, [@Image, Width, Height, + GetFormatName(Format), ImgSize + 0.0, Iff(ImgSize = Size, 'B', 'KiB'), Bits, + Palette]); + end + else + Result := SysUtils.Format(SImageInfoInvalid, [@Image]); +end; + +function GetVersionStr: string; +begin + Result := Format('%.1d.%.2d', [ImagingVersionMajor, ImagingVersionMinor]); +end; + +function IffFormat(Condition: Boolean; const TruePart, FalsePart: TImageFormat): TImageFormat; +begin + if Condition then + Result := TruePart + else + Result := FalsePart; +end; + +procedure RegisterImageFileFormat(AClass: TImageFileFormatClass); +begin + Assert(AClass <> nil); + if ImageFileFormats = nil then + ImageFileFormats := TList.Create; + if GlobalMetadata = nil then + GlobalMetadata := TMetadata.Create; + if ImageFileFormats <> nil then + ImageFileFormats.Add(AClass.Create); +end; + +function RegisterOption(OptionId: LongInt; Variable: PLongInt): Boolean; +begin + Result := False; + if Options = nil then + InitOptions; + + Assert(Variable <> nil); + + if OptionId >= Length(Options) then + SetLength(Options, OptionId + InitialOptions); + if (OptionId >= 0) and (OptionId < Length(Options)) {and (Options[OptionId] = nil) - must be able to override existing } then + begin + Options[OptionId] := Variable; + Result := True; + end; +end; + +function FindImageFileFormatByExt(const Ext: string): TImageFileFormat; +var + I: LongInt; +begin + Result := nil; + for I := ImageFileFormats.Count - 1 downto 0 do + if TImageFileFormat(ImageFileFormats[I]).Extensions.IndexOf(Ext) >= 0 then + begin + Result := TImageFileFormat(ImageFileFormats[I]); + Exit; + end; +end; + +function FindImageFileFormatByName(const FileName: string): TImageFileFormat; +var + I: LongInt; +begin + Result := nil; + for I := ImageFileFormats.Count - 1 downto 0 do + if TImageFileFormat(ImageFileFormats[I]).TestFileName(FileName) then + begin + Result := TImageFileFormat(ImageFileFormats[I]); + Exit; + end; +end; + +function FindImageFileFormatByClass(AClass: TImageFileFormatClass): TImageFileFormat; +var + I: LongInt; +begin + Result := nil; + for I := 0 to ImageFileFormats.Count - 1 do + if TImageFileFormat(ImageFileFormats[I]) is AClass then + begin + Result := TObject(ImageFileFormats[I]) as TImageFileFormat; + Break; + end; +end; + +function GetFileFormatCount: LongInt; +begin + Result := ImageFileFormats.Count; +end; + +function GetFileFormatAtIndex(Index: LongInt): TImageFileFormat; +begin + if (Index >= 0) and (Index < ImageFileFormats.Count) then + Result := TImageFileFormat(ImageFileFormats[Index]) + else + Result := nil; +end; + +function GetImageFileFormatsFilter(OpenFileFilter: Boolean): string; +var + I, J, Count: LongInt; + Descriptions: string; + Filters, CurFilter: string; + FileFormat: TImageFileFormat; +begin + Descriptions := ''; + Filters := ''; + Count := 0; + + for I := 0 to ImageFileFormats.Count - 1 do + begin + FileFormat := TObject(ImageFileFormats[I]) as TImageFileFormat; + + // If we are creating filter for save dialog and this format cannot save + // files the we skip it + if not OpenFileFilter and not FileFormat.CanSave then + Continue; + + CurFilter := ''; + for J := 0 to FileFormat.Masks.Count - 1 do + begin + CurFilter := CurFilter + FileFormat.Masks[J]; + if J < FileFormat.Masks.Count - 1 then + CurFilter := CurFilter + ';'; + end; + + FmtStr(Descriptions, '%s%s (%s)|%2:s', [Descriptions, FileFormat.Name, CurFilter]); + if Filters <> '' then + FmtStr(Filters, '%s;%s', [Filters, CurFilter]) + else + Filters := CurFilter; + + if I < ImageFileFormats.Count - 1 then + Descriptions := Descriptions + '|'; + + Inc(Count); + end; + + if (Count > 1) and OpenFileFilter then + FmtStr(Descriptions, '%s (%s)|%1:s|%s', [SAllFilter, Filters, Descriptions]); + + Result := Descriptions; +end; + +function GetFilterIndexExtension(Index: LongInt; OpenFileFilter: Boolean): string; +var + I, Count: LongInt; + FileFormat: TImageFileFormat; +begin + // -1 because filter indices are in 1..n range + Index := Index - 1; + Result := ''; + if OpenFileFilter then + begin + if Index > 0 then + Index := Index - 1; + end; + + if (Index >= 0) and (Index < ImageFileFormats.Count) then + begin + Count := 0; + for I := 0 to ImageFileFormats.Count - 1 do + begin + FileFormat := TObject(ImageFileFormats[I]) as TImageFileFormat; + if not OpenFileFilter and not FileFormat.CanSave then + Continue; + if Index = Count then + begin + if FileFormat.Extensions.Count > 0 then + Result := FileFormat.Extensions[0]; + Exit; + end; + Inc(Count); + end; + end; +end; + +function GetFileNameFilterIndex(const FileName: string; OpenFileFilter: Boolean): LongInt; +var + I: LongInt; + FileFormat: TImageFileFormat; +begin + Result := 0; + for I := 0 to ImageFileFormats.Count - 1 do + begin + FileFormat := TObject(ImageFileFormats[I]) as TImageFileFormat; + if not OpenFileFilter and not FileFormat.CanSave then + Continue; + if FileFormat.TestFileName(FileName) then + begin + // +1 because filter indices are in 1..n range + Inc(Result); + if OpenFileFilter then + Inc(Result); + Exit; + end; + Inc(Result); + end; + Result := -1; +end; + +function GetIO: TIOFunctions; +begin + Result := IO; +end; + +procedure RaiseImaging(const Msg: string; const Args: array of const); +var + WholeMsg: string; +begin + WholeMsg := Msg; + if GetExceptObject <> nil then + begin + WholeMsg := WholeMsg + ' ' + SExceptMsg + ': ' + + GetExceptObject.Message; + end; + raise EImagingError.CreateFmt(WholeMsg, Args); +end; + +procedure RaiseImaging(const Msg: string); +begin + RaiseImaging(Msg, []); +end; + +{ Internal unit functions } + +function CheckOptionValue(OptionId, Value: LongInt): LongInt; +begin + case OptionId of + ImagingColorReductionMask: + Result := ClampInt(Value, 0, $FF); + ImagingLoadOverrideFormat, ImagingSaveOverrideFormat: + Result := Iff(ImagingFormats.IsImageFormatValid(TImageFormat(Value)), + Value, LongInt(ifUnknown)); + ImagingMipMapFilter: Result := ClampInt(Value, Ord(Low(TSamplingFilter)), + Ord(High(TSamplingFilter))); + else + Result := Value; + end; +end; + +procedure SetFileIO; +begin + IO := FileIO; +end; + +procedure SetStreamIO; +begin + IO := StreamIO; +end; + +procedure SetMemoryIO; +begin + IO := MemoryIO; +end; + +procedure InitImageFormats; +begin + ImagingFormats.InitImageFormats(ImageFormatInfos); +end; + +procedure FreeImageFileFormats; +var + I: LongInt; +begin + if ImageFileFormats <> nil then + for I := 0 to ImageFileFormats.Count - 1 do + TImageFileFormat(ImageFileFormats[I]).Free; + FreeAndNil(ImageFileFormats); +end; + +procedure InitOptions; +begin + SetLength(Options, InitialOptions); + OptionStack := TOptionStack.Create; +end; + +procedure FreeOptions; +begin + SetLength(Options, 0); + FreeAndNil(OptionStack); +end; + +{ + TImageFileFormat class implementation +} + +constructor TImageFileFormat.Create(AMetadata: TMetadata); +begin + inherited Create; + FName := SUnknownFormat; + FExtensions := TStringList.Create; + FMasks := TStringList.Create; + if AMetadata = nil then + FMetadata := GlobalMetadata + else + FMetadata := AMetadata; + Define; +end; + +destructor TImageFileFormat.Destroy; +begin + FExtensions.Free; + FMasks.Free; + inherited Destroy; +end; + +procedure TImageFileFormat.Define; +begin +end; + +function TImageFileFormat.PrepareLoad(Handle: TImagingHandle; + var Images: TDynImageDataArray; OnlyFirstFrame: Boolean): Boolean; +begin + FMetadata.ClearMetaItems; // Clear old metadata + FreeImagesInArray(Images); + SetLength(Images, 0); + Result := Handle <> nil; +end; + +function TImageFileFormat.PostLoadCheck(var Images: TDynImageDataArray; + LoadResult: Boolean): Boolean; +var + I: LongInt; +begin + if not LoadResult then + begin + FreeImagesInArray(Images); + SetLength(Images, 0); + Result := False; + end + else + begin + Result := (Length(Images) > 0) and TestImagesInArray(Images); + + if Result then + begin + // Convert to overriden format if it is set + if LoadOverrideFormat <> ifUnknown then + for I := Low(Images) to High(Images) do + ConvertImage(Images[I], LoadOverrideFormat); + end; + end; +end; + +function TImageFileFormat.PrepareSave(Handle: TImagingHandle; + const Images: TDynImageDataArray; var Index: Integer): Boolean; +var + Len, I: LongInt; +begin + CheckOptionsValidity; + Result := False; + if CanSave then + begin + Len := Length(Images); + Assert(Len > 0); + + // If there are no images to be saved exit + if Len = 0 then Exit; + + // Check index of image to be saved (-1 as index means save all images) + if IsMultiImageFormat then + begin + if (Index >= Len) then + Index := 0; + + if Index < 0 then + begin + Index := 0; + FFirstIdx := 0; + FLastIdx := Len - 1; + end + else + begin + FFirstIdx := Index; + FLastIdx := Index; + end; + + for I := FFirstIdx to FLastIdx - 1 do + begin + if not TestImage(Images[I]) then + Exit; + end; + end + else + begin + if (Index >= Len) or (Index < 0) then + Index := 0; + if not TestImage(Images[Index]) then + Exit; + end; + + Result := True; + end; +end; + +procedure TImageFileFormat.AddMasks(const AMasks: string); +var + I: LongInt; + Ext: string; +begin + FExtensions.Clear; + FMasks.CommaText := AMasks; + FMasks.Delimiter := ';'; + + for I := 0 to FMasks.Count - 1 do + begin + FMasks[I] := Trim(FMasks[I]); + Ext := GetFileExt(FMasks[I]); + if (Ext <> '') and (Ext <> '*') then + FExtensions.Add(Ext); + end; +end; + +function TImageFileFormat.GetFormatInfo(Format: TImageFormat): TImageFormatInfo; +begin + Result := ImageFormatInfos[Format]^; +end; + +function TImageFileFormat.GetSupportedFormats: TImageFormats; +begin + Result := FSupportedFormats; +end; + +function TImageFileFormat.LoadData(Handle: TImagingHandle; + var Images: TDynImageDataArray; OnlyFirstFrame: Boolean): Boolean; +begin + Result := False; + RaiseImaging(SFileFormatCanNotLoad, [FName]); +end; + +function TImageFileFormat.SaveData(Handle: TImagingHandle; + const Images: TDynImageDataArray; Index: LongInt): Boolean; +begin + Result := False; + RaiseImaging(SFileFormatCanNotSave, [FName]); +end; + +procedure TImageFileFormat.ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); +begin +end; + +function TImageFileFormat.IsSupported(const Image: TImageData): Boolean; +begin + Result := Image.Format in GetSupportedFormats; +end; + +function TImageFileFormat.LoadFromFile(const FileName: string; + var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; +var + Handle: TImagingHandle; +begin + Result := False; + if CanLoad then + try + // Set IO ops to file ops and open given file + SetFileIO; + Handle := IO.Open(PChar(FileName), omReadOnly); + try + // Test if file contains valid image and if so then load it + if TestFormat(Handle) then + begin + Result := PrepareLoad(Handle, Images, OnlyFirstLevel) and + LoadData(Handle, Images, OnlyFirstlevel); + Result := PostLoadCheck(Images, Result); + end + else + RaiseImaging(SFileNotValid, [FileName, Name]); + finally + IO.Close(Handle); + end; + except + RaiseImaging(SErrorLoadingFile, [FileName, FExtensions[0]]); + end; +end; + +function TImageFileFormat.LoadFromStream(Stream: TStream; + var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; +var + Handle: TImagingHandle; + OldPosition: Int64; +begin + Result := False; + OldPosition := Stream.Position; + if CanLoad then + try + // Set IO ops to stream ops and "open" given memory + SetStreamIO; + Handle := IO.Open(Pointer(Stream), omReadOnly); + try + // Test if stream contains valid image and if so then load it + if TestFormat(Handle) then + begin + Result := PrepareLoad(Handle, Images, OnlyFirstLevel) and + LoadData(Handle, Images, OnlyFirstlevel); + Result := PostLoadCheck(Images, Result); + end + else + RaiseImaging(SStreamNotValid, [@Stream, Name]); + finally + IO.Close(Handle); + end; + except + Stream.Position := OldPosition; + FreeImagesInArray(Images); + RaiseImaging(SErrorLoadingStream, [@Stream, FExtensions[0]]); + end; +end; + +function TImageFileFormat.LoadFromMemory(Data: Pointer; Size: LongInt; var + Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; +var + Handle: TImagingHandle; + IORec: TMemoryIORec; +begin + Result := False; + if CanLoad then + try + // Set IO ops to memory ops and "open" given memory + SetMemoryIO; + IORec := PrepareMemIO(Data, Size); + Handle := IO.Open(@IORec,omReadOnly); + try + // Test if memory contains valid image and if so then load it + if TestFormat(Handle) then + begin + Result := PrepareLoad(Handle, Images, OnlyFirstLevel) and + LoadData(Handle, Images, OnlyFirstlevel); + Result := PostLoadCheck(Images, Result); + end + else + RaiseImaging(SMemoryNotValid, [Data, Size, Name]); + finally + IO.Close(Handle); + end; + except + RaiseImaging(SErrorLoadingMemory, [Data, Size, FExtensions[0]]); + end; +end; + +function TImageFileFormat.SaveToFile(const FileName: string; + const Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; +var + Handle: TImagingHandle; + Len, Index, I: LongInt; + Ext, FName: string; +begin + Result := False; + if CanSave and TestImagesInArray(Images) then + try + SetFileIO; + Len := Length(Images); + if IsMultiImageFormat or + (not IsMultiImageFormat and (OnlyFirstLevel or (Len = 1))) then + begin + Handle := IO.Open(PChar(FileName), GetSaveOpenMode); + try + if OnlyFirstLevel then + Index := 0 + else + Index := -1; + // Write multi image to one file + Result := PrepareSave(Handle, Images, Index) and SaveData(Handle, Images, Index); + finally + IO.Close(Handle); + end; + end + else + begin + // Write multi image to file sequence + Ext := ExtractFileExt(FileName); + FName := ChangeFileExt(FileName, ''); + Result := True; + for I := 0 to Len - 1 do + begin + Handle := IO.Open(PChar(Format(FName + '%.3d' + Ext, [I])), GetSaveOpenMode); + try + Index := I; + Result := Result and PrepareSave(Handle, Images, Index) and + SaveData(Handle, Images, Index); + if not Result then + Break; + finally + IO.Close(Handle); + end; + end; + end; + except + raise UpdateExceptMessage(GetExceptObject, SErrorSavingFile, [FileName, FExtensions[0]]); + end; +end; + +function TImageFileFormat.SaveToStream(Stream: TStream; + const Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; +var + Handle: TImagingHandle; + Len, Index, I: LongInt; + OldPosition: Int64; +begin + Result := False; + OldPosition := Stream.Position; + if CanSave and TestImagesInArray(Images) then + try + SetStreamIO; + Handle := IO.Open(PChar(Stream), GetSaveOpenMode); + try + if IsMultiImageFormat or OnlyFirstLevel then + begin + if OnlyFirstLevel then + Index := 0 + else + Index := -1; + // Write multi image in one run + Result := PrepareSave(Handle, Images, Index) and SaveData(Handle, Images, Index); + end + else + begin + // Write multi image to sequence + Result := True; + Len := Length(Images); + for I := 0 to Len - 1 do + begin + Index := I; + Result := Result and PrepareSave(Handle, Images, Index) and + SaveData(Handle, Images, Index); + if not Result then + Break; + end; + end; + finally + IO.Close(Handle); + end; + except + Stream.Position := OldPosition; + raise UpdateExceptMessage(GetExceptObject, SErrorSavingStream, [@Stream, FExtensions[0]]); + end; +end; + +function TImageFileFormat.SaveToMemory(Data: Pointer; var Size: LongInt; + const Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; +var + Handle: TImagingHandle; + Len, Index, I: LongInt; + IORec: TMemoryIORec; +begin + Result := False; + if CanSave and TestImagesInArray(Images) then + try + SetMemoryIO; + IORec := PrepareMemIO(Data, Size); + Handle := IO.Open(PChar(@IORec), GetSaveOpenMode); + try + if IsMultiImageFormat or OnlyFirstLevel then + begin + if OnlyFirstLevel then + Index := 0 + else + Index := -1; + // Write multi image in one run + Result := PrepareSave(Handle, Images, Index) and SaveData(Handle, Images, Index); + end + else + begin + // Write multi image to sequence + Result := True; + Len := Length(Images); + for I := 0 to Len - 1 do + begin + Index := I; + Result := Result and PrepareSave(Handle, Images, Index) and + SaveData(Handle, Images, Index); + if not Result then + Break; + end; + end; + Size := IORec.Position; + finally + IO.Close(Handle); + end; + except + raise UpdateExceptMessage(GetExceptObject, SErrorSavingMemory, [Data, Size, FExtensions[0]]); + end; +end; + +function TImageFileFormat.MakeCompatible(const Image: TImageData; + var Compatible: TImageData; out MustBeFreed: Boolean): Boolean; +begin + InitImage(Compatible); + + if SaveOverrideFormat <> ifUnknown then + begin + // Save format override is active. Clone input and convert it to override format. + CloneImage(Image, Compatible); + ConvertImage(Compatible, SaveOverrideFormat); + // Now check if override format is supported by file format. If it is not + // then file format specific conversion (virtual method) is called. + Result := IsSupported(Compatible); + if not Result then + begin + ConvertToSupported(Compatible, GetFormatInfo(Compatible.Format)); + Result := IsSupported(Compatible); + end; + end // Add IsCompatible function! not only checking by Format + else if IsSupported(Image) then + begin + // No save format override and input is in format supported by this + // file format. Just copy Image's fields to Compatible + Compatible := Image; + Result := True; + end + else + begin + // No override and input's format is not compatible with file format. + // Clone it and the call file format specific conversion (virtual method). + CloneImage(Image, Compatible); + ConvertToSupported(Compatible, GetFormatInfo(Compatible.Format)); + Result := IsSupported(Compatible); + end; + // Tell the user that he must free Compatible after he's done with it + // (if necessary). + MustBeFreed := Image.Bits <> Compatible.Bits; +end; + +function TImageFileFormat.TestFormat(Handle: TImagingHandle): Boolean; +begin + Result := False; +end; + +function TImageFileFormat.TestFileName(const FileName: string): Boolean; +var + I: LongInt; + OnlyName: string; +begin + OnlyName := ExtractFileName(FileName); + // For each mask test if filename matches it + for I := 0 to FMasks.Count - 1 do + if StrMaskMatch(OnlyName, FMasks[I], False) then + begin + Result := True; + Exit; + end; + Result := False; +end; + +procedure TImageFileFormat.CheckOptionsValidity; +begin +end; + +function TImageFileFormat.GetCanLoad: Boolean; +begin + Result := ffLoad in FFeatures; +end; + +function TImageFileFormat.GetCanSave: Boolean; +begin + Result := ffSave in FFeatures; +end; + +function TImageFileFormat.GetIsMultiImageFormat: Boolean; +begin + Result := ffMultiImage in FFeatures; +end; + +function TImageFileFormat.GetSaveOpenMode: TOpenMode; +begin + // TODO: fix + //if ffReadOnSave in FFeatures then + // Result := omReadWrite + //else + Result := omCreate; +end; + +{ TOptionStack class implementation } + +constructor TOptionStack.Create; +begin + inherited Create; + FPosition := -1; +end; + +destructor TOptionStack.Destroy; +var + I: LongInt; +begin + for I := 0 to OptionStackDepth - 1 do + SetLength(FStack[I], 0); + inherited Destroy; +end; + +function TOptionStack.Pop: Boolean; +var + I: LongInt; +begin + Result := False; + if FPosition >= 0 then + begin + SetLength(Options, Length(FStack[FPosition])); + for I := 0 to Length(FStack[FPosition]) - 1 do + if Options[I] <> nil then + Options[I]^ := FStack[FPosition, I]; + Dec(FPosition); + Result := True; + end; +end; + +function TOptionStack.Push: Boolean; +var + I: LongInt; +begin + Result := False; + if FPosition < OptionStackDepth - 1 then + begin + Inc(FPosition); + SetLength(FStack[FPosition], Length(Options)); + for I := 0 to Length(Options) - 1 do + if Options[I] <> nil then + FStack[FPosition, I] := Options[I]^; + Result := True; + end; +end; + +{ TMetadata } + +procedure TMetadata.SetMetaItem(const Id: string; const Value: Variant; + ImageIndex: Integer); +begin + AddMetaToList(FLoadMetaItems, Id, Value, ImageIndex); +end; + +procedure TMetadata.SetMetaItemForSaving(const Id: string; const Value: Variant; + ImageIndex: Integer); +begin + AddMetaToList(FSaveMetaItems, Id, Value, ImageIndex); +end; + +procedure TMetadata.AddMetaToList(List: TStringList; const Id: string; + const Value: Variant; ImageIndex: Integer); +var + Item: TMetadataItem; + Idx: Integer; + FullId: string; +begin + FullId := GetMetaItemName(Id, ImageIndex); + if List.Find(FullId, Idx) then + (List.Objects[Idx] as TMetadataItem).Value := Value + else + begin + Item := TMetadataItem.Create; + Item.Id := Id; + Item.ImageIndex := ImageIndex; + Item.Value := Value; + List.AddObject(FullId, Item); + end; +end; + +procedure TMetadata.ClearMetaItems; +begin + ClearMetaList(FLoadMetaItems); +end; + +procedure TMetadata.ClearMetaItemsForSaving; +begin + ClearMetaList(FSaveMetaItems); +end; + +procedure TMetadata.ClearMetaList(List: TStringList); +var + I: Integer; +begin + for I := 0 to List.Count - 1 do + List.Objects[I].Free; + List.Clear; +end; + +procedure TMetadata.CopyLoadedMetaItemsForSaving; +var + I: Integer; + Copy, Orig: TMetadataItem; +begin + ClearMetaItemsForSaving; + for I := 0 to FLoadMetaItems.Count - 1 do + begin + Orig := TMetadataItem(FLoadMetaItems.Objects[I]); + Copy := TMetadataItem.Create; + Copy.Id := Orig.Id; + Copy.ImageIndex := Orig.ImageIndex; + Copy.Value := Orig.Value; + FSaveMetaItems.AddObject(GetMetaItemName(Copy.Id, Copy.ImageIndex), Copy); + end; +end; + +constructor TMetadata.Create; +begin + inherited; + FLoadMetaItems := TStringList.Create; + FLoadMetaItems.Sorted := True; + FSaveMetaItems := TStringList.Create; + FSaveMetaItems.Sorted := True; +end; + +destructor TMetadata.Destroy; +begin + ClearMetaItems; + ClearMetaItemsForSaving; + FLoadMetaItems.Free; + FSaveMetaItems.Free; + inherited; +end; + +function TMetadata.GetMetaById(const Id: string): Variant; +var + Idx: Integer; +begin + if FLoadMetaItems.Find(Id, Idx) then + Result := (FLoadMetaItems.Objects[Idx] as TMetadataItem).Value + else + Result := Variants.Null; +end; + +function TMetadata.GetMetaByIdMulti(const Id: string; ImageIndex: Integer): Variant; +begin + Result := GetMetaById(GetMetaItemName(Id, ImageIndex)); +end; + +function TMetadata.GetSaveMetaById(const Id: string): Variant; +var + Idx: Integer; +begin + if FSaveMetaItems.Find(Id, Idx) then + Result := (FSaveMetaItems.Objects[Idx] as TMetadataItem).Value + else + Result := Variants.Null; +end; + +function TMetadata.GetSaveMetaByIdMulti(const Id: string; + ImageIndex: Integer): Variant; +begin + Result := GetSaveMetaById(GetMetaItemName(Id, ImageIndex)); +end; + +function TMetadata.GetMetaByIdx(Index: Integer): TMetadataItem; +begin + Result := FLoadMetaItems.Objects[Index] as TMetadataItem; +end; + +function TMetadata.GetMetaCount: Integer; +begin + Result := FLoadMetaItems.Count; +end; + +function TMetadata.GetMetaItemName(const Id: string; + ImageIndex: Integer): string; +begin + Result := Iff(ImageIndex = 0, Id, Format(SMetaIdForSubImage, [Id, ImageIndex])); +end; + +function TMetadata.GetPhysicalPixelSize(ResUnit: TResolutionUnit; var XSize, + YSize: Single; MetaForSave: Boolean; ImageIndex: Integer): Boolean; +type + TGetter = function(const Id: string; ImageIndex: Integer): Variant of object; +var + Getter: TGetter; + XMeta, YMeta: Variant; +begin + if MetaForSave then + Getter := GetSaveMetaByIdMulti + else + Getter := GetMetaByIdMulti; + + XMeta := Getter(SMetaPhysicalPixelSizeX, ImageIndex); + YMeta := Getter(SMetaPhysicalPixelSizeY, ImageIndex); + XSize := -1; + YSize := -1; + + Result := not VarIsNull(XMeta) or not VarIsNull(YMeta); + + if not Result then + Exit; + + if not VarIsNull(XMeta) then + XSize := XMeta; + if not VarIsNull(YMeta) then + YSize := YMeta; + + if XSize < 0 then + XSize := YSize; + if YSize < 0 then + YSize := XSize; + + TranslateUnits(ResUnit, XSize, YSize); +end; + +procedure TMetadata.SetPhysicalPixelSize(ResUnit: TResolutionUnit; XSize, + YSize: Single; MetaForSave: Boolean; ImageIndex: Integer); +type + TAdder = procedure(const Id: string; const Value: Variant; ImageIndex: Integer) of object; +var + Adder: TAdder; +begin + TranslateUnits(ResUnit, XSize, YSize); + + if MetaForSave then + Adder := SetMetaItemForSaving + else + Adder := SetMetaItem; + + Adder(SMetaPhysicalPixelSizeX, XSize, ImageIndex); + Adder(SMetaPhysicalPixelSizeY, YSize, ImageIndex); +end; + +procedure TMetadata.TranslateUnits(ResolutionUnit: TResolutionUnit; var XRes, + YRes: Single); +var + UnitSize: Single; +begin + case ResolutionUnit of + ruDpi: UnitSize := 25400; + ruDpm: UnitSize := 1e06; + ruDpcm: UnitSize := 1e04; + else + UnitSize := 1; + end; + if ResolutionUnit <> ruSizeInMicroMeters then + begin + XRes := UnitSize / XRes; + YRes := UnitSize / YRes; + end; +end; + +function TMetadata.HasMetaItem(const Id: string; ImageIndex: Integer): Boolean; +begin + Result := GetMetaByIdMulti(Id, ImageIndex) <> Variants.Null; +end; + +function TMetadata.HasMetaItemForSaving(const Id: string; ImageIndex: Integer): Boolean; +begin + Result := GetSaveMetaByIdMulti(Id, ImageIndex) <> Variants.Null; +end; + +initialization +{$IFDEF MEMCHECK} + {$IF CompilerVersion >= 18} + System.ReportMemoryLeaksOnShutdown := True; + {$IFEND} +{$ENDIF} + if GlobalMetadata = nil then + GlobalMetadata := TMetadata.Create; + if ImageFileFormats = nil then + ImageFileFormats := TList.Create; + InitImageFormats; + RegisterOption(ImagingColorReductionMask, @ColorReductionMask); + RegisterOption(ImagingLoadOverrideFormat, @LoadOverrideFormat); + RegisterOption(ImagingSaveOverrideFormat, @SaveOverrideFormat); + RegisterOption(ImagingMipMapFilter, @MipMapFilter); + RegisterOption(ImagingBinaryTreshold, @BinaryTreshold); +finalization + FreeOptions; + FreeImageFileFormats; + GlobalMetadata.Free; + +{ + File Notes: + + -- TODOS ---------------------------------------------------- + - nothing now + + -- 0.80 ------------------------------------------------------ + - Added new color records constructor functions (Color24(..), Color32(..)). + - Added convenience channel getters for TColor32 (GetGreenValue, ...). + + -- 0.77.1 --------------------------------------------------- + - Updated IO Open functions according to changes in ImagingTypes. + - Fixed bug in SplitImage that could cause wrong size of edge chunks. + - Metadata support fixes and extensions (frame delays, animation loops). + + -- 0.26.5 Changes/Bug Fixes --------------------------------- + - Started reworking exception raising to keep the original class type + (e.g. in NewImage EOutOfMemory could be raised but was hidden + by EImagingError raised afterwards in NewImage try/except). + - Fixed possible AV in Rotate45 subproc of RotateImage. + - Added ReadRawXXX and WriteRawXXX functions for raw image bits IO. + - Implemented ImagingBinaryTreshold option. + - Added support for simple image metadata loading/saving. + - Moved file format definition (name, exts, caps, ...) from + constructor to new Define method. + - Fixed some memory leaks caused by failures during image loading. + + -- 0.26.3 Changes/Bug Fixes --------------------------------- + - Extended RotateImage to allow arbitrary angle rotations. + - Reversed the order file formats list is searched so + if you register a new one it will be found sooner than + built in formats. + - Fixed memory leak in ResizeImage ocurring when resizing + indexed images. + + -- 0.26.1 Changes/Bug Fixes --------------------------------- + - Added position/size checks to LoadFromStream functions. + - Changed conditional compilation in impl. uses section to reflect changes + in LINK symbols. + + -- 0.24.3 Changes/Bug Fixes --------------------------------- + - GenerateMipMaps now generates all smaller levels from + original big image (better results when using more advanced filters). + Also conversion to compatible image format is now done here not + in FillMipMapLevel (that is called for every mipmap level). + + -- 0.23 Changes/Bug Fixes ----------------------------------- + - MakePaletteForImages now works correctly for indexed and special format images + - Fixed bug in StretchRect: Image was not properly stretched if + src and dst dimensions differed only in height. + - ConvertImage now fills new image with zeroes to avoid random data in + some conversions (RGB->XRGB) + - Changed RegisterOption procedure to function + - Changed bunch of palette functions from low level interface to procedure + (there was no reason for them to be functions). + - Changed FreeImage and FreeImagesInArray functions to procedures. + - Added many assertions, come try-finally, other checks, and small code + and doc changes. + + -- 0.21 Changes/Bug Fixes ----------------------------------- + - GenerateMipMaps threw failed assertion when input was indexed or special, + fixed. + - Added CheckOptionsValidity to TImageFileFormat and its decendants. + - Unit ImagingExtras which registers file formats in Extras package + is now automatically added to uses clause if LINK_EXTRAS symbol is + defined in ImagingOptions.inc file. + - Added EnumFileFormats function to low level interface. + - Fixed bug in SwapChannels which could cause AV when swapping alpha + channel of A8R8G8B8 images. + - Converting loaded images to ImagingOverrideFormat is now done + in PostLoadCheck method to avoid code duplicity. + - Added GetFileFormatCount and GetFileFormatAtIndex functions + - Bug in ConvertImage: if some format was converted to similar format + only with swapped channels (R16G16B16<>B16G16R16) then channels were + swapped correctly but new data format (swapped one) was not set. + - Made TImageFileFormat.MakeCompatible public non-virtual method + (and modified its function). Created new virtual + ConvertToSupported which should be overriden by descendants. + Main reason for doint this is to avoid duplicate code that was in all + TImageFileFormat's descendants. + - Changed TImageFileFormat.GetFormatInfo's result type to TImageFormatInfo. + - Split overloaded FindImageFileFormat functions to + FindImageFileFormatByClass and FindImageFileFormatByExt and created new + FindImageFileFormatByName which operates on whole filenames. + - Function GetExtensionFilterIndex renamed to GetFileNameFilterIndex + (because it now works with filenames not extensions). + - DetermineFileFormat now first searches by filename and if not found + then by data. + - Added TestFileName method to TImageFileFormat. + - Updated GetImageFileFormatsFilter to uses Masks instead of Extensions + property of TImageFileFormat. Also you can now request + OpenDialog and SaveDialog type filters + - Added Masks property and AddMasks method to TImageFileFormat. + AddMasks replaces AddExtensions, it uses filename masks instead + of sime filename extensions to identify supported files. + - Changed TImageFileFormat.LoadData procedure to function and + moved varios duplicate code from its descandats (check index,...) + here to TImageFileFormat helper methods. + - Changed TImageFileFormat.SaveData procedure to function and + moved varios duplicate code from its descandats (check index,...) + here to TImageFileFormat helper methods. + - Removed RAISE_EXCEPTIONS define, exceptions are now raised everytime + - Added MustBeFreed parameter to TImageFileFormat.MakeComptible method + that indicates that compatible image returned by this method must be + freed after its usage. + + -- 0.19 Changes/Bug Fixes ----------------------------------- + - fixed bug in NewImage: if given format was ifDefault it wasn't + replaced with DefaultImageFormat constant which caused problems later + in other units + - fixed bug in RotateImage which caused that rotated special format + images were whole black + - LoadImageFromXXX and LoadMultiImageFromXXX now use DetermineXXXFormat + when choosing proper loader, this eliminated need for Ext parameter + in stream and memory loading functions + - added GetVersionStr function + - fixed bug in ResizeImage which caued indexed images to lose their + palette during process resulting in whole black image + - Clipping in ...Rect functions now uses clipping procs from ImagingUtility, + it also works better + - FillRect optimization for 8, 16, and 32 bit formats + - added pixel set/get functions to low level interface: + GetPixelDirect, SetPixelDirect, GetPixel32, SetPixel32, + GetPixelFP, SetPixelFP + - removed GetPixelBytes low level intf function - redundant + (same data can be obtained by GetImageFormatInfo) + - made small changes in many parts of library to compile + on AMD64 CPU (Linux with FPC) + - changed InitImage to procedure (function was pointless) + - Method TestFormat of TImageFileFormat class made public + (was protected) + - added function IsFileFormatSupported to low level interface + (contributed by Paul Michell) + - fixed some missing format arguments from error strings + which caused Format function to raise exception + - removed forgotten debug code that disabled filtered resizing of images with + channel bitcounts > 8 + + -- 0.17 Changes/Bug Fixes ----------------------------------- + - changed order of parameters of CopyRect function + - GenerateMipMaps now filters mipmap levels + - ResizeImage functions was extended to allow bilinear and bicubic filtering + - added StretchRect function to low level interface + - added functions GetImageFileFormatsFilter, GetFilterIndexExtension, + and GetExtensionFilterIndex + + -- 0.15 Changes/Bug Fixes ----------------------------------- + - added function RotateImage to low level interface + - moved TImageFormatInfo record and types required by it to + ImagingTypes unit, changed GetImageFormatInfo low level + interface function to return TImageFormatInfo instead of short info + - added checking of options values validity before they are used + - fixed possible memory leak in CloneImage + - added ReplaceColor function to low level interface + - new function FindImageFileFormat by class added + + -- 0.13 Changes/Bug Fixes ----------------------------------- + - added DetermineFileFormat, DetermineStreamFormat, DetermineMemoryFormat, + GetPixelsSize functions to low level interface + - added NewPalette, CopyPalette, FreePalette functions + to low level interface + - added MapImageToPalette, FillRect, SplitImage, MakePaletteForImages + functions to low level interface + - fixed buggy FillCustomPalette function (possible div by zero and others) + - added CopyRect function to low level interface + - Member functions of TImageFormatInfo record implemented for all formats + - before saving images TestImagesInArray is called now + - added TestImagesInArray function to low level interface + - added GenerateMipMaps function to low level interface + - stream position in load/save from/to stream is now set to position before + function was called if error occurs + - when error occured during load/save from/to file file handle + was not released + - CloneImage returned always False + +} +end. + diff --git a/resources/libraries/deskew/Imaging/ImagingBitmap.pas b/resources/libraries/deskew/Imaging/ImagingBitmap.pas new file mode 100755 index 0000000..81684e4 --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingBitmap.pas @@ -0,0 +1,856 @@ +{ + Vampyre Imaging Library + by Marek Mauder + http://imaginglib.sourceforge.net + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +{ + This unit contains image format loader/saver for Windows Bitmap images. +} +unit ImagingBitmap; + +{$I ImagingOptions.inc} + +interface + +uses + ImagingTypes, Imaging, ImagingUtility, ImagingFormats, ImagingIO; + +type + { Class for loading and saving Windows Bitmap images. + It can load/save 8bit indexed, 16, 24, 32 bit RGB or ARGB + images with or without RLE compression. It can also load 1/4 bit + indexed images and OS2 bitmaps.} + TBitmapFileFormat = class(TImageFileFormat) + protected + FUseRLE: LongBool; + procedure Define; override; + function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; + OnlyFirstLevel: Boolean): Boolean; override; + function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; + Index: LongInt): Boolean; override; + procedure ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); override; + public + function TestFormat(Handle: TImagingHandle): Boolean; override; + published + { Controls that RLE compression is used during saving. Accessible trough + ImagingBitmapRLE option.} + property UseRLE: LongBool read FUseRLE write FUseRLE; + end; + +implementation + +const + SBitmapFormatName = 'Windows Bitmap Image'; + SBitmapMasks = '*.bmp,*.dib'; + BitmapSupportedFormats: TImageFormats = [ifIndex8, ifA1R5G5B5, ifA4R4G4B4, + ifR5G6B5, ifR8G8B8, ifA8R8G8B8, ifX1R5G5B5, ifX4R4G4B4, ifX8R8G8B8]; + BitmapDefaultRLE = True; + +const + { Bitmap file identifier 'BM'.} + BMMagic: Word = 19778; + + { Constants for the TBitmapInfoHeader.Compression field.} + BI_RGB = 0; + BI_RLE8 = 1; + BI_RLE4 = 2; + BI_BITFIELDS = 3; + + V3InfoHeaderSize = 40; + V4InfoHeaderSize = 108; + +type + { File Header for Windows/OS2 bitmap file.} + TBitmapFileHeader = packed record + ID: Word; // Is always 19778 : 'BM' + Size: LongWord; // Filesize + Reserved1: Word; + Reserved2: Word; + Offset: LongWord; // Offset from start pos to beginning of image bits + end; + + { Info Header for Windows bitmap file version 4.} + TBitmapInfoHeader = packed record + Size: LongWord; + Width: LongInt; + Height: LongInt; + Planes: Word; + BitCount: Word; + Compression: LongWord; + SizeImage: LongWord; + XPelsPerMeter: LongInt; + YPelsPerMeter: LongInt; + ClrUsed: LongInt; + ClrImportant: LongInt; + RedMask: LongWord; + GreenMask: LongWord; + BlueMask: LongWord; + AlphaMask: LongWord; + CSType: LongWord; + EndPoints: array[0..8] of LongWord; + GammaRed: LongWord; + GammaGreen: LongWord; + GammaBlue: LongWord; + end; + + { Info Header for OS2 bitmaps.} + TBitmapCoreHeader = packed record + Size: LongWord; + Width: Word; + Height: Word; + Planes: Word; + BitCount: Word; + end; + + { Used in RLE encoding and decoding.} + TRLEOpcode = packed record + Count: Byte; + Command: Byte; + end; + PRLEOpcode = ^TRLEOpcode; + +{ TBitmapFileFormat class implementation } + +procedure TBitmapFileFormat.Define; +begin + inherited; + FName := SBitmapFormatName; + FFeatures := [ffLoad, ffSave]; + FSupportedFormats := BitmapSupportedFormats; + + FUseRLE := BitmapDefaultRLE; + + AddMasks(SBitmapMasks); + RegisterOption(ImagingBitmapRLE, @FUseRLE); +end; + +function TBitmapFileFormat.LoadData(Handle: TImagingHandle; + var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; +var + BF: TBitmapFileHeader; + BI: TBitmapInfoHeader; + BC: TBitmapCoreHeader; + IsOS2: Boolean; + PalRGB: PPalette24; + I, FPalSize, AlignedSize, StartPos, HeaderSize, AlignedWidthBytes, WidthBytes: LongInt; + Info: TImageFormatInfo; + Data: Pointer; + + procedure LoadRGB; + var + I: LongInt; + LineBuffer: PByte; + begin + with Images[0], GetIO do + begin + // If BI.Height is < 0 then image data are stored non-flipped + // but default in windows is flipped so if Height is positive we must + // flip it + + if BI.BitCount < 8 then + begin + // For 1 and 4 bit images load aligned data, they will be converted to + // 8 bit and unaligned later + GetMem(Data, AlignedSize); + + if BI.Height < 0 then + Read(Handle, Data, AlignedSize) + else + for I := Height - 1 downto 0 do + Read(Handle, @PByteArray(Data)[I * AlignedWidthBytes], AlignedWidthBytes); + end + else + begin + // Images with pixels of size >= 1 Byte are read line by line and + // copied to image bits without padding bytes + GetMem(LineBuffer, AlignedWidthBytes); + try + if BI.Height < 0 then + for I := 0 to Height - 1 do + begin + Read(Handle, LineBuffer, AlignedWidthBytes); + Move(LineBuffer^, PByteArray(Bits)[I * WidthBytes], WidthBytes); + end + else + for I := Height - 1 downto 0 do + begin + Read(Handle, LineBuffer, AlignedWidthBytes); + Move(LineBuffer^, PByteArray(Bits)[I * WidthBytes], WidthBytes); + end; + finally + FreeMemNil(LineBuffer); + end; + end; + end; + end; + + procedure LoadRLE4; + var + RLESrc: PByteArray; + Row, Col, WriteRow, I: LongInt; + SrcPos: LongWord; + DeltaX, DeltaY, Low, High: Byte; + Pixels: PByteArray; + OpCode: TRLEOpcode; + NegHeightBitmap: Boolean; + begin + GetMem(RLESrc, BI.SizeImage); + GetIO.Read(Handle, RLESrc, BI.SizeImage); + with Images[0] do + try + Low := 0; + Pixels := Bits; + SrcPos := 0; + NegHeightBitmap := BI.Height < 0; + Row := 0; // Current row in dest image + Col := 0; // Current column in dest image + // Row in dest image where actuall writting will be done + WriteRow := Iff(NegHeightBitmap, Row, Height - 1 - Row); + while (Row < Height) and (SrcPos < BI.SizeImage) do + begin + // Read RLE op-code + OpCode := PRLEOpcode(@RLESrc[SrcPos])^; + Inc(SrcPos, SizeOf(OpCode)); + if OpCode.Count = 0 then + begin + // A byte Count of zero means that this is a special + // instruction. + case OpCode.Command of + 0: + begin + // Move to next row + Inc(Row); + WriteRow := Iff(NegHeightBitmap, Row, Height - 1 - Row); + Col := 0; + end ; + 1: Break; // Image is finished + 2: + begin + // Move to a new relative position + DeltaX := RLESrc[SrcPos]; + DeltaY := RLESrc[SrcPos + 1]; + Inc(SrcPos, 2); + Inc(Col, DeltaX); + Inc(Row, DeltaY); + end + else + // Do not read data after EOF + if SrcPos + OpCode.Command > BI.SizeImage then + OpCode.Command := BI.SizeImage - SrcPos; + // Take padding bytes and nibbles into account + if Col + OpCode.Command > Width then + OpCode.Command := Width - Col; + // Store absolute data. Command code is the + // number of absolute bytes to store + for I := 0 to OpCode.Command - 1 do + begin + if (I and 1) = 0 then + begin + High := RLESrc[SrcPos] shr 4; + Low := RLESrc[SrcPos] and $F; + Pixels[WriteRow * Width + Col] := High; + Inc(SrcPos); + end + else + Pixels[WriteRow * Width + Col] := Low; + Inc(Col); + end; + // Odd number of bytes is followed by a pad byte + if (OpCode.Command mod 4) in [1, 2] then + Inc(SrcPos); + end; + end + else + begin + // Take padding bytes and nibbles into account + if Col + OpCode.Count > Width then + OpCode.Count := Width - Col; + // Store a run of the same color value + for I := 0 to OpCode.Count - 1 do + begin + if (I and 1) = 0 then + Pixels[WriteRow * Width + Col] := OpCode.Command shr 4 + else + Pixels[WriteRow * Width + Col] := OpCode.Command and $F; + Inc(Col); + end; + end; + end; + finally + FreeMem(RLESrc); + end; + end; + + procedure LoadRLE8; + var + RLESrc: PByteArray; + SrcCount, Row, Col, WriteRow: LongInt; + SrcPos: LongWord; + DeltaX, DeltaY: Byte; + Pixels: PByteArray; + OpCode: TRLEOpcode; + NegHeightBitmap: Boolean; + begin + GetMem(RLESrc, BI.SizeImage); + GetIO.Read(Handle, RLESrc, BI.SizeImage); + with Images[0] do + try + Pixels := Bits; + SrcPos := 0; + NegHeightBitmap := BI.Height < 0; + Row := 0; // Current row in dest image + Col := 0; // Current column in dest image + // Row in dest image where actuall writting will be done + WriteRow := Iff(NegHeightBitmap, Row, Height - 1 - Row); + while (Row < Height) and (SrcPos < BI.SizeImage) do + begin + // Read RLE op-code + OpCode := PRLEOpcode(@RLESrc[SrcPos])^; + Inc(SrcPos, SizeOf(OpCode)); + if OpCode.Count = 0 then + begin + // A byte Count of zero means that this is a special + // instruction. + case OpCode.Command of + 0: + begin + // Move to next row + Inc(Row); + WriteRow := Iff(NegHeightBitmap, Row, Height - 1 - Row); + Col := 0; + end ; + 1: Break; // Image is finished + 2: + begin + // Move to a new relative position + DeltaX := RLESrc[SrcPos]; + DeltaY := RLESrc[SrcPos + 1]; + Inc(SrcPos, 2); + Inc(Col, DeltaX); + Inc(Row, DeltaY); + end + else + SrcCount := OpCode.Command; + // Do not read data after EOF + if SrcPos + OpCode.Command > BI.SizeImage then + OpCode.Command := BI.SizeImage - SrcPos; + // Take padding bytes into account + if Col + OpCode.Command > Width then + OpCode.Command := Width - Col; + // Store absolute data. Command code is the + // number of absolute bytes to store + Move(RLESrc[SrcPos], Pixels[WriteRow * Width + Col], OpCode.Command); + Inc(SrcPos, SrcCount); + Inc(Col, OpCode.Command); + // Odd number of bytes is followed by a pad byte + if (SrcCount mod 2) = 1 then + Inc(SrcPos); + end; + end + else + begin + // Take padding bytes into account + if Col + OpCode.Count > Width then + OpCode.Count := Width - Col; + // Store a run of the same color value. Count is number of bytes to store + FillChar(Pixels [WriteRow * Width + Col], OpCode.Count, OpCode.Command); + Inc(Col, OpCode.Count); + end; + end; + finally + FreeMem(RLESrc); + end; + end; + +begin + Data := nil; + SetLength(Images, 1); + with GetIO, Images[0] do + try + FillChar(BI, SizeOf(BI), 0); + StartPos := Tell(Handle); + Read(Handle, @BF, SizeOf(BF)); + Read(Handle, @BI.Size, SizeOf(BI.Size)); + IsOS2 := BI.Size = SizeOf(TBitmapCoreHeader); + + // Bitmap Info reading + if IsOS2 then + begin + // OS/2 type bitmap, reads info header without 4 already read bytes + Read(Handle, @PByteArray(@BC)[SizeOf(BI.Size)], + SizeOf(TBitmapCoreHeader) - SizeOf(BI.Size)); + with BI do + begin + ClrUsed := 0; + Compression := BI_RGB; + BitCount := BC.BitCount; + Height := BC.Height; + Width := BC.Width; + end; + end + else + begin + // Windows type bitmap + HeaderSize := Min(BI.Size - SizeOf(BI.Size), SizeOf(BI) - SizeOf(BI.Size)); // do not read more than size of BI! + Read(Handle, @PByteArray(@BI)[SizeOf(BI.Size)], HeaderSize); + // SizeImage can be 0 for BI_RGB images, but it is here because of: + // I saved 8bit bitmap in Paint Shop Pro 8 as OS2 RLE compressed. + // It wrote strange 64 Byte Info header with SizeImage set to 0 + // Some progs were able to open it, some were not. + if BI.SizeImage = 0 then + BI.SizeImage := BF.Size - BF.Offset; + end; + // Bit mask reading. Only read it if there is V3 header, V4 header has + // masks laoded already (only masks for RGB in V3). + if (BI.Compression = BI_BITFIELDS) and (BI.Size = V3InfoHeaderSize) then + Read(Handle, @BI.RedMask, SizeOf(BI.RedMask) * 3); + + case BI.BitCount of + 1, 4, 8: Format := ifIndex8; + 16: + if BI.RedMask = $0F00 then + // Set XRGB4 or ARGB4 according to value of alpha mask + Format := IffFormat(BI.AlphaMask = 0, ifX4R4G4B4, ifA4R4G4B4) + else if BI.RedMask = $F800 then + Format := ifR5G6B5 + else + // R5G5B5 is default 16bit format (with Compression = BI_RGB or masks). + // We set it to A1.. and later there is a check if there are any alpha values + // and if not it is changed to X1R5G5B5 + Format := ifA1R5G5B5; + 24: Format := ifR8G8B8; + 32: Format := ifA8R8G8B8; // As with R5G5B5 there is alpha check later + end; + + NewImage(BI.Width, Abs(BI.Height), Format, Images[0]); + Info := GetFormatInfo(Format); + WidthBytes := Width * Info.BytesPerPixel; + AlignedWidthBytes := (((Width * BI.BitCount) + 31) shr 5) * 4; + AlignedSize := Height * LongInt(AlignedWidthBytes); + + // Palette settings and reading + if BI.BitCount <= 8 then + begin + // Seek to the begining of palette + Seek(Handle, StartPos + SizeOf(TBitmapFileHeader) + LongInt(BI.Size), + smFromBeginning); + if IsOS2 then + begin + // OS/2 type + FPalSize := 1 shl BI.BitCount; + GetMem(PalRGB, FPalSize * SizeOf(TColor24Rec)); + try + Read(Handle, PalRGB, FPalSize * SizeOf(TColor24Rec)); + for I := 0 to FPalSize - 1 do + with PalRGB[I] do + begin + Palette[I].R := R; + Palette[I].G := G; + Palette[I].B := B; + end; + finally + FreeMemNil(PalRGB); + end; + end + else + begin + // Windows type + FPalSize := BI.ClrUsed; + if FPalSize = 0 then + FPalSize := 1 shl BI.BitCount; + Read(Handle, Palette, FPalSize * SizeOf(TColor32Rec)); + end; + for I := 0 to Info.PaletteEntries - 1 do + Palette[I].A := $FF; + end; + + // Seek to the beginning of image bits + Seek(Handle, StartPos + LongInt(BF.Offset), smFromBeginning); + + case BI.Compression of + BI_RGB: LoadRGB; + BI_RLE4: LoadRLE4; + BI_RLE8: LoadRLE8; + BI_BITFIELDS: LoadRGB; + end; + + if BI.AlphaMask = 0 then + begin + // Alpha mask is not stored in file (V3) or not defined. + // Check alpha channels of loaded images if they might contain them. + if Format = ifA1R5G5B5 then + begin + // Check if there is alpha channel present in A1R5GB5 images, if it is not + // change format to X1R5G5B5 + if not Has16BitImageAlpha(Width * Height, Bits) then + Format := ifX1R5G5B5; + end + else if Format = ifA8R8G8B8 then + begin + // Check if there is alpha channel present in A8R8G8B8 images, if it is not + // change format to X8R8G8B8 + if not Has32BitImageAlpha(Width * Height, Bits) then + Format := ifX8R8G8B8; + end; + end; + + if BI.BitCount < 8 then + begin + // 1 and 4 bpp images are supported only for loading which is now + // so we now convert them to 8bpp (and unalign scanlines). + case BI.BitCount of + 1: Convert1To8(Data, Bits, Width, Height, AlignedWidthBytes, False); + 4: + begin + // RLE4 bitmaps are translated to 8bit during RLE decoding + if BI.Compression <> BI_RLE4 then + Convert4To8(Data, Bits, Width, Height, AlignedWidthBytes, False); + end; + end; + // Enlarge palette + ReallocMem(Palette, Info.PaletteEntries * SizeOf(TColor32Rec)); + end; + + Result := True; + finally + FreeMemNil(Data); + end; +end; + +function TBitmapFileFormat.SaveData(Handle: TImagingHandle; + const Images: TDynImageDataArray; Index: LongInt): Boolean; +var + StartPos, EndPos, I, Pad, PadSize, WidthBytes: LongInt; + BF: TBitmapFileHeader; + BI: TBitmapInfoHeader; + Info: TImageFormatInfo; + ImageToSave: TImageData; + MustBeFreed: Boolean; + + procedure SaveRLE8; + const + BufferSize = 8 * 1024; + var + X, Y, I, SrcPos: LongInt; + DiffCount, SameCount: Byte; + Pixels: PByteArray; + Buffer: array[0..BufferSize - 1] of Byte; + BufferPos: LongInt; + + procedure WriteByte(ByteToWrite: Byte); + begin + if BufferPos = BufferSize then + begin + // Flush buffer if necessary + GetIO.Write(Handle, @Buffer, BufferPos); + BufferPos := 0; + end; + Buffer[BufferPos] := ByteToWrite; + Inc(BufferPos); + end; + + begin + BufferPos := 0; + with GetIO, ImageToSave do + begin + for Y := Height - 1 downto 0 do + begin + X := 0; + SrcPos := 0; + Pixels := @PByteArray(Bits)[Y * Width]; + + while X < Width do + begin + SameCount := 1; + DiffCount := 0; + // Determine run length + while X + SameCount < Width do + begin + // If we reach max run length or byte with different value + // we end this run + if (SameCount = 255) or (Pixels[SrcPos + SameCount] <> Pixels[SrcPos]) then + Break; + Inc(SameCount); + end; + + if SameCount = 1 then + begin + // If there are not some bytes with the same value we + // compute how many different bytes are there + while X + DiffCount < Width do + begin + // Stop diff byte counting if there two bytes with the same value + // or DiffCount is too big + if (DiffCount = 255) or (Pixels[SrcPos + DiffCount + 1] = + Pixels[SrcPos + DiffCount]) then + Break; + Inc(DiffCount); + end; + end; + + // Now store absolute data (direct copy image->file) or + // store RLE code only (number of repeats + byte to be repeated) + if DiffCount > 2 then + begin + // Save 'Absolute Data' (0 + number of bytes) but only + // if number is >2 because (0+1) and (0+2) are other special commands + WriteByte(0); + WriteByte(DiffCount); + // Write absolute data to buffer + for I := 0 to DiffCount - 1 do + WriteByte(Pixels[SrcPos + I]); + Inc(X, DiffCount); + Inc(SrcPos, DiffCount); + // Odd number of bytes must be padded + if (DiffCount mod 2) = 1 then + WriteByte(0); + end + else + begin + // Save number of repeats and byte that should be repeated + WriteByte(SameCount); + WriteByte(Pixels[SrcPos]); + Inc(X, SameCount); + Inc(SrcPos, SameCount); + end; + end; + // Save 'End Of Line' command + WriteByte(0); + WriteByte(0); + end; + // Save 'End Of Bitmap' command + WriteByte(0); + WriteByte(1); + // Flush buffer + GetIO.Write(Handle, @Buffer, BufferPos); + end; + end; + +begin + Result := False; + if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then + with GetIO, ImageToSave do + try + Info := GetFormatInfo(Format); + StartPos := Tell(Handle); + FillChar(BF, SizeOf(BF), 0); + FillChar(BI, SizeOf(BI), 0); + // Other fields will be filled later - we don't know all values now + BF.ID := BMMagic; + Write(Handle, @BF, SizeOf(BF)); + if Info.HasAlphaChannel and (Info.BytesPerPixel = 2){V4 temp hack} then + // Save images with alpha in V4 format + BI.Size := V4InfoHeaderSize + else + // Save images without alpha in V3 format - for better compatibility + BI.Size := V3InfoHeaderSize; + BI.Width := Width; + BI.Height := Height; + BI.Planes := 1; + BI.BitCount := Info.BytesPerPixel * 8; + BI.XPelsPerMeter := 2835; // 72 dpi + BI.YPelsPerMeter := 2835; // 72 dpi + // Set compression + if (Info.BytesPerPixel = 1) and FUseRLE then + BI.Compression := BI_RLE8 + else if (Info.HasAlphaChannel or + ((BI.BitCount = 16) and (Format <> ifX1R5G5B5))) and (Info.BytesPerPixel = 2){V4 temp hack} then + BI.Compression := BI_BITFIELDS + else + BI.Compression := BI_RGB; + // Write header (first time) + Write(Handle, @BI, BI.Size); + + // Write mask info + if BI.Compression = BI_BITFIELDS then + begin + if BI.BitCount = 16 then + with Info.PixelFormat^ do + begin + BI.RedMask := RBitMask; + BI.GreenMask := GBitMask; + BI.BlueMask := BBitMask; + BI.AlphaMask := ABitMask; + end + else + begin + // Set masks for A8R8G8B8 + BI.RedMask := $00FF0000; + BI.GreenMask := $0000FF00; + BI.BlueMask := $000000FF; + BI.AlphaMask := $FF000000; + end; + // If V3 header is used RGB masks must be written to file separately. + // V4 header has embedded masks (V4 is default for formats with alpha). + if BI.Size = V3InfoHeaderSize then + Write(Handle, @BI.RedMask, SizeOf(BI.RedMask) * 3); + end; + // Write palette + if Palette <> nil then + Write(Handle, Palette, Info.PaletteEntries * SizeOf(TColor32Rec)); + + BF.Offset := Tell(Handle) - StartPos; + + if BI.Compression <> BI_RLE8 then + begin + // Save uncompressed data, scanlines must be filled with pad bytes + // to be multiples of 4, save as bottom-up (Windows native) bitmap + Pad := 0; + WidthBytes := Width * Info.BytesPerPixel; + PadSize := ((Width * BI.BitCount + 31) div 32) * 4 - WidthBytes; + + for I := Height - 1 downto 0 do + begin + Write(Handle, @PByteArray(Bits)[I * WidthBytes], WidthBytes); + if PadSize > 0 then + Write(Handle, @Pad, PadSize); + end; + end + else + begin + // Save data with RLE8 compression + SaveRLE8; + end; + + EndPos := Tell(Handle); + Seek(Handle, StartPos, smFromBeginning); + // Rewrite header with new values + BF.Size := EndPos - StartPos; + BI.SizeImage := BF.Size - BF.Offset; + Write(Handle, @BF, SizeOf(BF)); + Write(Handle, @BI, BI.Size); + Seek(Handle, EndPos, smFromBeginning); + + Result := True; + finally + if MustBeFreed then + FreeImage(ImageToSave); + end; +end; + +procedure TBitmapFileFormat.ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); +var + ConvFormat: TImageFormat; +begin + if Info.IsFloatingPoint then + // Convert FP image to RGB/ARGB according to presence of alpha channel + ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifR8G8B8) + else if Info.HasGrayChannel or Info.IsIndexed then + // Convert all grayscale and indexed images to Index8 unless they have alpha + // (preserve it) + ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifIndex8) + else if Info.HasAlphaChannel then + // Convert images with alpha channel to A8R8G8B8 + ConvFormat := ifA8R8G8B8 + else if Info.UsePixelFormat then + // Convert 16bit RGB images (no alpha) to X1R5G5B5 + ConvFormat := ifX1R5G5B5 + else + // Convert all other formats to R8G8B8 + ConvFormat := ifR8G8B8; + + ConvertImage(Image, ConvFormat); +end; + +function TBitmapFileFormat.TestFormat(Handle: TImagingHandle): Boolean; +var + Hdr: TBitmapFileHeader; + ReadCount: LongInt; +begin + Result := False; + if Handle <> nil then + with GetIO do + begin + ReadCount := Read(Handle, @Hdr, SizeOf(Hdr)); + Seek(Handle, -ReadCount, smFromCurrent); + Result := (Hdr.ID = BMMagic) and (ReadCount = SizeOf(Hdr)); + end; +end; + +initialization + RegisterImageFileFormat(TBitmapFileFormat); + +{ + File Notes: + + -- TODOS ---------------------------------------------------- + - nothing now + - Add option to choose to save V3 or V4 headers. + + -- 0.25.0 Changes/Bug Fixes --------------------------------- + - Fixed problem with indexed BMP loading - some pal entries + could end up with alpha=0. + + -- 0.23 Changes/Bug Fixes ----------------------------------- + - Now saves bitmaps as bottom-up for better compatibility + (mainly Lazarus' TImage!). + - Fixed crash when loading bitmaps with headers larger than V4. + - Temp hacks to disable V4 headers for 32bit images (compatibility with + other soft). + + -- 0.21 Changes/Bug Fixes ----------------------------------- + - Removed temporary data allocation for image with aligned scanlines. + They are now directly written to output so memory requirements are + much lower now. + - Now uses and recognizes BITMAPINFOHEADERV4 when loading/saving. + Mainly for formats with alpha channels. + - Added ifR5G6B5 to supported formats, changed converting to supported + formats little bit. + - Rewritten SaveRLE8 nested procedure. Old code was long and + mysterious - new is short and much more readable. + - MakeCompatible method moved to base class, put ConvertToSupported here. + GetSupportedFormats removed, it is now set in constructor. + - Rewritten LoadRLE4 and LoadRLE8 nested procedures. + Should be less buggy an more readable (load inspired by Colosseum Builders' code). + - Made public properties for options registered to SetOption/GetOption + functions. + - Addded alpha check to 32b bitmap loading too (teh same as in 16b + bitmap loading). + - Moved Convert1To8 and Convert4To8 to ImagingFormats + - Changed extensions to filename masks. + - Changed SaveData, LoadData, and MakeCompatible methods according + to changes in base class in Imaging unit. + + -- 0.19 Changes/Bug Fixes ----------------------------------- + - fixed wrong const that caused A4R4G4B4 BMPs to load as A1R5G5B5 + - fixed the bug that caused 8bit RLE compressed bitmaps to load as + whole black + + -- 0.17 Changes/Bug Fixes ----------------------------------- + - 16 bit images are usually without alpha but some has alpha + channel and there is no indication of it - so I have added + a check: if all pixels of image are with alpha = 0 image is treated + as X1R5G5B5 otherwise as A1R5G5B5 + + -- 0.13 Changes/Bug Fixes ----------------------------------- + - when loading 1/4 bit images with dword aligned dimensions + there was ugly memory rewritting bug causing image corruption + +} + +end. + diff --git a/resources/libraries/deskew/Imaging/ImagingCanvases.pas b/resources/libraries/deskew/Imaging/ImagingCanvases.pas new file mode 100755 index 0000000..a18bfeb --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingCanvases.pas @@ -0,0 +1,2127 @@ +{ + Vampyre Imaging Library + by Marek Mauder + http://imaginglib.sourceforge.net + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +{ This unit contains canvas classes for drawing and applying effects.} +unit ImagingCanvases; + +{$I ImagingOptions.inc} + +interface + +uses + SysUtils, Types, Classes, ImagingTypes, Imaging, ImagingClasses, + ImagingFormats, ImagingUtility; + +const + { Color constants in ifA8R8G8B8 format.} + pcClear = $00000000; + pcBlack = $FF000000; + pcWhite = $FFFFFFFF; + pcMaroon = $FF800000; + pcGreen = $FF008000; + pcOlive = $FF808000; + pcNavy = $FF000080; + pcPurple = $FF800080; + pcTeal = $FF008080; + pcGray = $FF808080; + pcSilver = $FFC0C0C0; + pcRed = $FFFF0000; + pcLime = $FF00FF00; + pcYellow = $FFFFFF00; + pcBlue = $FF0000FF; + pcFuchsia = $FFFF00FF; + pcAqua = $FF00FFFF; + pcLtGray = $FFC0C0C0; + pcDkGray = $FF808080; + + MaxPenWidth = 256; + +type + EImagingCanvasError = class(EImagingError); + EImagingCanvasBlendingError = class(EImagingError); + + { Fill mode used when drawing filled objects on canvas.} + TFillMode = ( + fmSolid, // Solid fill using current fill color + fmClear // No filling done + ); + + { Pen mode used when drawing lines, object outlines, and similar on canvas.} + TPenMode = ( + pmSolid, // Draws solid lines using current pen color. + pmClear // No drawing done + ); + + { Source and destination blending factors for drawing functions with blending. + Blending formula: SrcColor * SrcFactor + DestColor * DestFactor } + TBlendingFactor = ( + bfIgnore, // Don't care + bfZero, // For Src and Dest, Factor = (0, 0, 0, 0) + bfOne, // For Src and Dest, Factor = (1, 1, 1, 1) + bfSrcAlpha, // For Src and Dest, Factor = (Src.A, Src.A, Src.A, Src.A) + bfOneMinusSrcAlpha, // For Src and Dest, Factor = (1 - Src.A, 1 - Src.A, 1 - Src.A, 1 - Src.A) + bfDstAlpha, // For Src and Dest, Factor = (Dest.A, Dest.A, Dest.A, Dest.A) + bfOneMinusDstAlpha, // For Src and Dest, Factor = (1 - Dest.A, 1 - Dest.A, 1 - Dest.A, 1 - Dest.A) + bfSrcColor, // For Dest, Factor = (Src.R, Src.R, Src.B, Src.A) + bfOneMinusSrcColor, // For Dest, Factor = (1 - Src.R, 1 - Src.G, 1 - Src.B, 1 - Src.A) + bfDstColor, // For Src, Factor = (Dest.R, Dest.G, Dest.B, Dest.A) + bfOneMinusDstColor // For Src, Factor = (1 - Dest.R, 1 - Dest.G, 1 - Dest.B, 1 - Dest.A) + ); + + { Procedure for custom pixel write modes with blending.} + TPixelWriteProc = procedure(const SrcPix: TColorFPRec; DestPtr: PByte; + DestInfo: PImageFormatInfo; SrcFactor, DestFactor: TBlendingFactor); + + { Represents 3x3 convolution filter kernel.} + TConvolutionFilter3x3 = record + Kernel: array[0..2, 0..2] of LongInt; + Divisor: LongInt; + Bias: Single; + end; + + { Represents 5x5 convolution filter kernel.} + TConvolutionFilter5x5 = record + Kernel: array[0..4, 0..4] of LongInt; + Divisor: LongInt; + Bias: Single; + end; + + TPointTransformFunction = function(const Pixel: TColorFPRec; + Param1, Param2, Param3: Single): TColorFPRec; + + TDynFPPixelArray = array of TColorFPRec; + + THistogramArray = array[Byte] of Integer; + + TSelectPixelFunction = function(var Pixels: TDynFPPixelArray): TColorFPRec; + + { Base canvas class for drawing objects, applying effects, and other. + Constructor takes TBaseImage (or pointer to TImageData). Source image + bits are not copied but referenced so all canvas functions affect + source image and vice versa. When you change format or resolution of + source image you must call UpdateCanvasState method (so canvas could + recompute some data size related stuff). + + TImagingCanvas works for all image data formats except special ones + (compressed). Because of this its methods are quite slow (they usually work + with colors in ifA32R32G32B32F format). If you want fast drawing you + can use one of fast canvas clases. These descendants of TImagingCanvas + work only for few select formats (or only one) but they are optimized thus + much faster. + } + TImagingCanvas = class(TObject) + private + FDataSizeOnUpdate: LongInt; + FLineRecursion: Boolean; + function GetPixel32(X, Y: LongInt): TColor32; virtual; + function GetPixelFP(X, Y: LongInt): TColorFPRec; virtual; + function GetValid: Boolean; {$IFDEF USE_INLINE}inline;{$ENDIF} + procedure SetPixel32(X, Y: LongInt; const Value: TColor32); virtual; + procedure SetPixelFP(X, Y: LongInt; const Value: TColorFPRec); virtual; + procedure SetPenColor32(const Value: TColor32); {$IFDEF USE_INLINE}inline;{$ENDIF} + procedure SetPenColorFP(const Value: TColorFPRec); {$IFDEF USE_INLINE}inline;{$ENDIF} + procedure SetPenWidth(const Value: LongInt); {$IFDEF USE_INLINE}inline;{$ENDIF} + procedure SetFillColor32(const Value: TColor32); {$IFDEF USE_INLINE}inline;{$ENDIF} + procedure SetFillColorFP(const Value: TColorFPRec); {$IFDEF USE_INLINE}inline;{$ENDIF} + procedure SetClipRect(const Value: TRect); + procedure CheckBeforeBlending(SrcFactor, DestFactor: TBlendingFactor; DestCanvas: TImagingCanvas); + protected + FPData: PImageData; + FClipRect: TRect; + FPenColorFP: TColorFPRec; + FPenColor32: TColor32; + FPenMode: TPenMode; + FPenWidth: LongInt; + FFillColorFP: TColorFPRec; + FFillColor32: TColor32; + FFillMode: TFillMode; + FNativeColor: TColorFPRec; + FFormatInfo: TImageFormatInfo; + + { Returns pointer to pixel at given position.} + function GetPixelPointer(X, Y: LongInt): Pointer; {$IFDEF USE_INLINE}inline;{$ENDIF} + { Translates given FP color to native format of canvas and stores it + in FNativeColor field (its bit copy) or user pointer (in overloaded method).} + procedure TranslateFPToNative(const Color: TColorFPRec); overload; {$IFDEF USE_INLINE}inline;{$ENDIF} + procedure TranslateFPToNative(const Color: TColorFPRec; Native: Pointer); overload; {$IFDEF USE_INLINE}inline;{$ENDIF} + { Clipping function used by horizontal and vertical line drawing functions.} + function ClipAxisParallelLine(var A1, A2, B: LongInt; + AStart, AStop, BStart, BStop: LongInt): Boolean; + { Internal horizontal line drawer used mainly for filling inside of objects + like ellipses and circles.} + procedure HorzLineInternal(X1, X2, Y: LongInt; Color: Pointer; Bpp: LongInt); virtual; + procedure CopyPixelInternal(X, Y: LongInt; Pixel: Pointer; Bpp: LongInt); {$IFDEF USE_INLINE}inline;{$ENDIF} + procedure DrawInternal(const SrcRect: TRect; DestCanvas: TImagingCanvas; + DestX, DestY: Integer; SrcFactor, DestFactor: TBlendingFactor; PixelWriteProc: TPixelWriteProc); + procedure StretchDrawInternal(const SrcRect: TRect; DestCanvas: TImagingCanvas; + const DestRect: TRect; SrcFactor, DestFactor: TBlendingFactor; + Filter: TResizeFilter; PixelWriteProc: TPixelWriteProc); + public + constructor CreateForData(ImageDataPointer: PImageData); + constructor CreateForImage(Image: TBaseImage); + destructor Destroy; override; + + { Call this method when you change size or format of image this canvas + operates on (like calling ResizeImage, ConvertImage, or changing Format + property of TBaseImage descendants).} + procedure UpdateCanvasState; virtual; + { Resets clipping rectangle to Rect(0, 0, ImageWidth, ImageHeight).} + procedure ResetClipRect; + + { Clears entire canvas with current fill color (ignores clipping rectangle + and always uses fmSolid fill mode).} + procedure Clear; + + { Draws horizontal line with current pen settings.} + procedure HorzLine(X1, X2, Y: LongInt); virtual; + { Draws vertical line with current pen settings.} + procedure VertLine(X, Y1, Y2: LongInt); virtual; + { Draws line from [X1, Y1] to [X2, Y2] with current pen settings.} + procedure Line(X1, Y1, X2, Y2: LongInt); virtual; + { Draws a rectangle using current pen settings.} + procedure FrameRect(const Rect: TRect); + { Fills given rectangle with current fill settings.} + procedure FillRect(const Rect: TRect); virtual; + { Fills given rectangle with current fill settings and pixel blending.} + procedure FillRectBlend(const Rect: TRect; SrcFactor, DestFactor: TBlendingFactor); + { Draws rectangle which is outlined by using the current pen settings and + filled by using the current fill settings.} + procedure Rectangle(const Rect: TRect); + { Draws ellipse which is outlined by using the current pen settings and + filled by using the current fill settings. Rect specifies bounding rectangle + of ellipse to be drawn.} + procedure Ellipse(const Rect: TRect); + { Fills area of canvas with current fill color starting at point [X, Y] and + coloring its neighbors. Default flood fill mode changes color of all + neighbors with the same color as pixel [X, Y]. With BoundaryFillMode + set to True neighbors are recolored regardless of their old color, + but area which will be recolored has boundary (specified by current pen color).} + procedure FloodFill(X, Y: Integer; BoundaryFillMode: Boolean = False); + + { Draws contents of this canvas onto another canvas with pixel blending. + Blending factors are chosen using TBlendingFactor parameters. + Resulting destination pixel color is: + SrcColor * SrcFactor + DstColor * DstFactor} + procedure DrawBlend(const SrcRect: TRect; DestCanvas: TImagingCanvas; + DestX, DestY: Integer; SrcFactor, DestFactor: TBlendingFactor); + { Draws contents of this canvas onto another one with typical alpha + blending (Src 'over' Dest, factors are bfSrcAlpha and bfOneMinusSrcAlpha.)} + procedure DrawAlpha(const SrcRect: TRect; DestCanvas: TImagingCanvas; DestX, DestY: Integer); virtual; + { Draws contents of this canvas onto another one using additive blending + (source and dest factors are bfOne).} + procedure DrawAdd(const SrcRect: TRect; DestCanvas: TImagingCanvas; DestX, DestY: Integer); + { Draws stretched and filtered contents of this canvas onto another canvas + with pixel blending. Blending factors are chosen using TBlendingFactor parameters. + Resulting destination pixel color is: + SrcColor * SrcFactor + DstColor * DstFactor} + procedure StretchDrawBlend(const SrcRect: TRect; DestCanvas: TImagingCanvas; + const DestRect: TRect; SrcFactor, DestFactor: TBlendingFactor; + Filter: TResizeFilter = rfBilinear); + { Draws contents of this canvas onto another one with typical alpha + blending (Src 'over' Dest, factors are bfSrcAlpha and bfOneMinusSrcAlpha.)} + procedure StretchDrawAlpha(const SrcRect: TRect; DestCanvas: TImagingCanvas; + const DestRect: TRect; Filter: TResizeFilter = rfBilinear); virtual; + { Draws contents of this canvas onto another one using additive blending + (source and dest factors are bfOne).} + procedure StretchDrawAdd(const SrcRect: TRect; DestCanvas: TImagingCanvas; + const DestRect: TRect; Filter: TResizeFilter = rfBilinear); + + { Convolves canvas' image with given 3x3 filter kernel. You can use + predefined filter kernels or define your own.} + procedure ApplyConvolution3x3(const Filter: TConvolutionFilter3x3); + { Convolves canvas' image with given 5x5 filter kernel. You can use + predefined filter kernels or define your own.} + procedure ApplyConvolution5x5(const Filter: TConvolutionFilter5x5); + { Computes 2D convolution of canvas' image and given filter kernel. + Kernel is in row format and KernelSize must be odd number >= 3. Divisor + is normalizing value based on Kernel (usually sum of all kernel's cells). + The Bias number shifts each color value by a fixed amount (color values + are usually in range [0, 1] during processing). If ClampChannels + is True all output color values are clamped to [0, 1]. You can use + predefined filter kernels or define your own.} + procedure ApplyConvolution(Kernel: PLongInt; KernelSize, Divisor: LongInt; + Bias: Single = 0.0; ClampChannels: Boolean = True); virtual; + + { Applies custom non-linear filter. Filter size is diameter of pixel + neighborhood. Typical values are 3, 5, or 7. } + procedure ApplyNonLinearFilter(FilterSize: Integer; SelectFunc: TSelectPixelFunction); + { Applies median non-linear filter with user defined pixel neighborhood. + Selects median pixel from the neighborhood as new pixel + (current implementation is quite slow).} + procedure ApplyMedianFilter(FilterSize: Integer); + { Applies min non-linear filter with user defined pixel neighborhood. + Selects min pixel from the neighborhood as new pixel.} + procedure ApplyMinFilter(FilterSize: Integer); + { Applies max non-linear filter with user defined pixel neighborhood. + Selects max pixel from the neighborhood as new pixel.} + procedure ApplyMaxFilter(FilterSize: Integer); + + { Transforms pixels one by one by given function. Pixel neighbors are + not taken into account. Param 1-3 are optional parameters + for transform function.} + procedure PointTransform(Transform: TPointTransformFunction; + Param1, Param2, Param3: Single); + { Modifies image contrast and brightness. Parameters should be + in range <-100; 100>.} + procedure ModifyContrastBrightness(Contrast, Brightness: Single); + { Gamma correction of individual color channels. Range is (0, +inf), + 1.0 means no change.} + procedure GammaCorection(Red, Green, Blue: Single); + { Inverts colors of all image pixels, makes negative image. Ignores alpha channel.} + procedure InvertColors; virtual; + { Simple single level thresholding with threshold level (in range [0, 1]) + for each color channel.} + procedure Threshold(Red, Green, Blue: Single); + { Adjusts the color levels of the image by scaling the + colors falling between specified white and black points to full [0, 1] range. + The black point specifies the darkest color in the image, white point + specifies the lightest color, and mid point is gamma aplied to image. + Black and white point must be in range [0, 1].} + procedure AdjustColorLevels(BlackPoint, WhitePoint: Single; MidPoint: Single = 1.0); + { Premultiplies color channel values by alpha. Needed for some platforms/APIs + to display images with alpha properly.} + procedure PremultiplyAlpha; + { Reverses PremultiplyAlpha operation.} + procedure UnPremultiplyAlpha; + + { Calculates image histogram for each channel and also gray values. Each + channel has 256 values available. Channel values of data formats with higher + precision are scaled and rounded. Example: Red[126] specifies number of pixels + in image with red channel = 126.} + procedure GetHistogram(out Red, Green, Blue, Alpha, Gray: THistogramArray); + { Fills image channel with given value leaving other channels intact. + Use ChannelAlpha, ChannelRed, etc. constants from ImagingTypes as + channel identifier.} + procedure FillChannel(ChannelId: Integer; NewChannelValue: Byte); overload; + { Fills image channel with given value leaving other channels intact. + Use ChannelAlpha, ChannelRed, etc. constants from ImagingTypes as + channel identifier.} + procedure FillChannelFP(ChannelId: Integer; NewChannelValue: Single); overload; + + { Color used when drawing lines, frames, and outlines of objects.} + property PenColor32: TColor32 read FPenColor32 write SetPenColor32; + { Color used when drawing lines, frames, and outlines of objects.} + property PenColorFP: TColorFPRec read FPenColorFP write SetPenColorFP; + { Pen mode used when drawing lines, object outlines, and similar on canvas.} + property PenMode: TPenMode read FPenMode write FPenMode; + { Width with which objects like lines, frames, etc. (everything which uses + PenColor) are drawn.} + property PenWidth: LongInt read FPenWidth write SetPenWidth; + { Color used for filling when drawing various objects.} + property FillColor32: TColor32 read FFillColor32 write SetFillColor32; + { Color used for filling when drawing various objects.} + property FillColorFP: TColorFPRec read FFillColorFP write SetFillColorFP; + { Fill mode used when drawing filled objects on canvas.} + property FillMode: TFillMode read FFillMode write FFillMode; + { Specifies the current color of the pixels of canvas. Native pixel is + read from canvas and then translated to 32bit ARGB. Reverse operation + is made when setting pixel color.} + property Pixels32[X, Y: LongInt]: TColor32 read GetPixel32 write SetPixel32; + { Specifies the current color of the pixels of canvas. Native pixel is + read from canvas and then translated to FP ARGB. Reverse operation + is made when setting pixel color.} + property PixelsFP[X, Y: LongInt]: TColorFPRec read GetPixelFP write SetPixelFP; + { Clipping rectangle of this canvas. No pixels outside this rectangle are + altered by canvas methods if Clipping property is True. Clip rect gets + reseted when UpdateCanvasState is called.} + property ClipRect: TRect read FClipRect write SetClipRect; + { Extended format information.} + property FormatInfo: TImageFormatInfo read FFormatInfo; + { Indicates that this canvas is in valid state. If False canvas oprations + may crash.} + property Valid: Boolean read GetValid; + + { Returns all formats supported by this canvas class.} + class function GetSupportedFormats: TImageFormats; virtual; + end; + + TImagingCanvasClass = class of TImagingCanvas; + + TScanlineArray = array[0..MaxInt div SizeOf(Pointer) - 1] of PColor32RecArray; + PScanlineArray = ^TScanlineArray; + + { Fast canvas class for ifA8R8G8B8 format images.} + TFastARGB32Canvas = class(TImagingCanvas) + protected + FScanlines: PScanlineArray; + procedure AlphaBlendPixels(SrcPix, DestPix: PColor32Rec); {$IFDEF USE_INLINE}inline;{$ENDIF} + function GetPixel32(X, Y: LongInt): TColor32; override; + procedure SetPixel32(X, Y: LongInt; const Value: TColor32); override; + public + destructor Destroy; override; + + procedure UpdateCanvasState; override; + + procedure DrawAlpha(const SrcRect: TRect; DestCanvas: TImagingCanvas; DestX, DestY: Integer); override; + procedure StretchDrawAlpha(const SrcRect: TRect; DestCanvas: TImagingCanvas; + const DestRect: TRect; Filter: TResizeFilter = rfBilinear); override; + procedure InvertColors; override; + + property Scanlines: PScanlineArray read FScanlines; + + class function GetSupportedFormats: TImageFormats; override; + end; + +const + { Kernel for 3x3 average smoothing filter.} + FilterAverage3x3: TConvolutionFilter3x3 = ( + Kernel: ((1, 1, 1), + (1, 1, 1), + (1, 1, 1)); + Divisor: 9; + Bias: 0); + + { Kernel for 5x5 average smoothing filter.} + FilterAverage5x5: TConvolutionFilter5x5 = ( + Kernel: ((1, 1, 1, 1, 1), + (1, 1, 1, 1, 1), + (1, 1, 1, 1, 1), + (1, 1, 1, 1, 1), + (1, 1, 1, 1, 1)); + Divisor: 25; + Bias: 0); + + { Kernel for 3x3 Gaussian smoothing filter.} + FilterGaussian3x3: TConvolutionFilter3x3 = ( + Kernel: ((1, 2, 1), + (2, 4, 2), + (1, 2, 1)); + Divisor: 16; + Bias: 0); + + { Kernel for 5x5 Gaussian smoothing filter.} + FilterGaussian5x5: TConvolutionFilter5x5 = ( + Kernel: ((1, 4, 6, 4, 1), + (4, 16, 24, 16, 4), + (6, 24, 36, 24, 6), + (4, 16, 24, 16, 4), + (1, 4, 6, 4, 1)); + Divisor: 256; + Bias: 0); + + { Kernel for 3x3 Sobel horizontal edge detection filter (1st derivative approximation).} + FilterSobelHorz3x3: TConvolutionFilter3x3 = ( + Kernel: (( 1, 2, 1), + ( 0, 0, 0), + (-1, -2, -1)); + Divisor: 1; + Bias: 0); + + { Kernel for 3x3 Sobel vertical edge detection filter (1st derivative approximation).} + FilterSobelVert3x3: TConvolutionFilter3x3 = ( + Kernel: ((-1, 0, 1), + (-2, 0, 2), + (-1, 0, 1)); + Divisor: 1; + Bias: 0); + + { Kernel for 3x3 Prewitt horizontal edge detection filter.} + FilterPrewittHorz3x3: TConvolutionFilter3x3 = ( + Kernel: (( 1, 1, 1), + ( 0, 0, 0), + (-1, -1, -1)); + Divisor: 1; + Bias: 0); + + { Kernel for 3x3 Prewitt vertical edge detection filter.} + FilterPrewittVert3x3: TConvolutionFilter3x3 = ( + Kernel: ((-1, 0, 1), + (-1, 0, 1), + (-1, 0, 1)); + Divisor: 1; + Bias: 0); + + { Kernel for 3x3 Kirsh horizontal edge detection filter.} + FilterKirshHorz3x3: TConvolutionFilter3x3 = ( + Kernel: (( 5, 5, 5), + (-3, 0, -3), + (-3, -3, -3)); + Divisor: 1; + Bias: 0); + + { Kernel for 3x3 Kirsh vertical edge detection filter.} + FilterKirshVert3x3: TConvolutionFilter3x3 = ( + Kernel: ((5, -3, -3), + (5, 0, -3), + (5, -3, -3)); + Divisor: 1; + Bias: 0); + + { Kernel for 3x3 Laplace omni-directional edge detection filter + (2nd derivative approximation).} + FilterLaplace3x3: TConvolutionFilter3x3 = ( + Kernel: ((-1, -1, -1), + (-1, 8, -1), + (-1, -1, -1)); + Divisor: 1; + Bias: 0); + + { Kernel for 5x5 Laplace omni-directional edge detection filter + (2nd derivative approximation).} + FilterLaplace5x5: TConvolutionFilter5x5 = ( + Kernel: ((-1, -1, -1, -1, -1), + (-1, -1, -1, -1, -1), + (-1, -1, 24, -1, -1), + (-1, -1, -1, -1, -1), + (-1, -1, -1, -1, -1)); + Divisor: 1; + Bias: 0); + + { Kernel for 3x3 spharpening filter (Laplacian + original color).} + FilterSharpen3x3: TConvolutionFilter3x3 = ( + Kernel: ((-1, -1, -1), + (-1, 9, -1), + (-1, -1, -1)); + Divisor: 1; + Bias: 0); + + { Kernel for 5x5 spharpening filter (Laplacian + original color).} + FilterSharpen5x5: TConvolutionFilter5x5 = ( + Kernel: ((-1, -1, -1, -1, -1), + (-1, -1, -1, -1, -1), + (-1, -1, 25, -1, -1), + (-1, -1, -1, -1, -1), + (-1, -1, -1, -1, -1)); + Divisor: 1; + Bias: 0); + + { Kernel for 5x5 glow filter.} + FilterGlow5x5: TConvolutionFilter5x5 = ( + Kernel: (( 1, 2, 2, 2, 1), + ( 2, 0, 0, 0, 2), + ( 2, 0, -20, 0, 2), + ( 2, 0, 0, 0, 2), + ( 1, 2, 2, 2, 1)); + Divisor: 8; + Bias: 0); + + { Kernel for 3x3 edge enhancement filter.} + FilterEdgeEnhance3x3: TConvolutionFilter3x3 = ( + Kernel: ((-1, -2, -1), + (-2, 16, -2), + (-1, -2, -1)); + Divisor: 4; + Bias: 0); + + { Kernel for 3x3 contour enhancement filter.} + FilterTraceControur3x3: TConvolutionFilter3x3 = ( + Kernel: ((-6, -6, -2), + (-1, 32, -1), + (-6, -2, -6)); + Divisor: 4; + Bias: 240/255); + + { Kernel for filter that negates all images pixels.} + FilterNegative3x3: TConvolutionFilter3x3 = ( + Kernel: ((0, 0, 0), + (0, -1, 0), + (0, 0, 0)); + Divisor: 1; + Bias: 1); + + { Kernel for 3x3 horz/vert embossing filter.} + FilterEmboss3x3: TConvolutionFilter3x3 = ( + Kernel: ((2, 0, 0), + (0, -1, 0), + (0, 0, -1)); + Divisor: 1; + Bias: 0.5); + + +{ You can register your own canvas class. List of registered canvases is used + by FindBestCanvasForImage functions to find best canvas for given image. + If two different canvases which support the same image data format are + registered then the one that was registered later is returned (so you can + override builtin Imaging canvases).} +procedure RegisterCanvas(CanvasClass: TImagingCanvasClass); +{ Returns best canvas for given TImageFormat.} +function FindBestCanvasForImage(ImageFormat: TImageFormat): TImagingCanvasClass; overload; +{ Returns best canvas for given TImageData.} +function FindBestCanvasForImage(const ImageData: TImageData): TImagingCanvasClass; overload; +{ Returns best canvas for given TBaseImage.} +function FindBestCanvasForImage(Image: TBaseImage): TImagingCanvasClass; overload; + +implementation + +resourcestring + SConstructorInvalidPointer = 'Invalid pointer (%p) to TImageData passed to TImagingCanvas constructor.'; + SConstructorInvalidImage = 'Invalid image data passed to TImagingCanvas constructor (%s).'; + SConstructorUnsupportedFormat = 'Image passed to TImagingCanvas constructor is in unsupported format (%s)'; + +var + // list with all registered TImagingCanvas classes + CanvasClasses: TList = nil; + +procedure RegisterCanvas(CanvasClass: TImagingCanvasClass); +begin + Assert(CanvasClass <> nil); + if CanvasClasses = nil then + CanvasClasses := TList.Create; + if CanvasClasses.IndexOf(CanvasClass) < 0 then + CanvasClasses.Add(CanvasClass); +end; + +function FindBestCanvasForImage(ImageFormat: TImageFormat): TImagingCanvasClass; overload; +var + I: LongInt; +begin + for I := CanvasClasses.Count - 1 downto 0 do + begin + if ImageFormat in TImagingCanvasClass(CanvasClasses[I]).GetSupportedFormats then + begin + Result := TImagingCanvasClass(CanvasClasses[I]); + Exit; + end; + end; + Result := TImagingCanvas; +end; + +function FindBestCanvasForImage(const ImageData: TImageData): TImagingCanvasClass; +begin + Result := FindBestCanvasForImage(ImageData.Format); +end; + +function FindBestCanvasForImage(Image: TBaseImage): TImagingCanvasClass; +begin + Result := FindBestCanvasForImage(Image.Format); +end; + +{ Canvas helper functions } + +procedure PixelBlendProc(const SrcPix: TColorFPRec; DestPtr: PByte; + DestInfo: PImageFormatInfo; SrcFactor, DestFactor: TBlendingFactor); +var + DestPix, FSrc, FDst: TColorFPRec; +begin + // Get set pixel color + DestPix := DestInfo.GetPixelFP(DestPtr, DestInfo, nil); + // Determine current blending factors + case SrcFactor of + bfZero: FSrc := ColorFP(0, 0, 0, 0); + bfOne: FSrc := ColorFP(1, 1, 1, 1); + bfSrcAlpha: FSrc := ColorFP(SrcPix.A, SrcPix.A, SrcPix.A, SrcPix.A); + bfOneMinusSrcAlpha: FSrc := ColorFP(1 - SrcPix.A, 1 - SrcPix.A, 1 - SrcPix.A, 1 - SrcPix.A); + bfDstAlpha: FSrc := ColorFP(DestPix.A, DestPix.A, DestPix.A, DestPix.A); + bfOneMinusDstAlpha: FSrc := ColorFP(1 - DestPix.A, 1 - DestPix.A, 1 - DestPix.A, 1 - DestPix.A); + bfDstColor: FSrc := ColorFP(DestPix.A, DestPix.R, DestPix.G, DestPix.B); + bfOneMinusDstColor: FSrc := ColorFP(1 - DestPix.A, 1 - DestPix.R, 1 - DestPix.G, 1 - DestPix.B); + end; + case DestFactor of + bfZero: FDst := ColorFP(0, 0, 0, 0); + bfOne: FDst := ColorFP(1, 1, 1, 1); + bfSrcAlpha: FDst := ColorFP(SrcPix.A, SrcPix.A, SrcPix.A, SrcPix.A); + bfOneMinusSrcAlpha: FDst := ColorFP(1 - SrcPix.A, 1 - SrcPix.A, 1 - SrcPix.A, 1 - SrcPix.A); + bfDstAlpha: FDst := ColorFP(DestPix.A, DestPix.A, DestPix.A, DestPix.A); + bfOneMinusDstAlpha: FDst := ColorFP(1 - DestPix.A, 1 - DestPix.A, 1 - DestPix.A, 1 - DestPix.A); + bfSrcColor: FDst := ColorFP(SrcPix.A, SrcPix.R, SrcPix.G, SrcPix.B); + bfOneMinusSrcColor: FDst := ColorFP(1 - SrcPix.A, 1 - SrcPix.R, 1 - SrcPix.G, 1 - SrcPix.B); + end; + // Compute blending formula + DestPix.R := SrcPix.R * FSrc.R + DestPix.R * FDst.R; + DestPix.G := SrcPix.G * FSrc.G + DestPix.G * FDst.G; + DestPix.B := SrcPix.B * FSrc.B + DestPix.B * FDst.B; + DestPix.A := SrcPix.A * FSrc.A + DestPix.A * FDst.A; + // Write blended pixel + DestInfo.SetPixelFP(DestPtr, DestInfo, nil, DestPix); +end; + +procedure PixelAlphaProc(const SrcPix: TColorFPRec; DestPtr: PByte; + DestInfo: PImageFormatInfo; SrcFactor, DestFactor: TBlendingFactor); +var + DestPix: TColorFPRec; + SrcAlpha, DestAlpha: Single; +begin + DestPix := DestInfo.GetPixelFP(DestPtr, DestInfo, nil); + // Blend the two pixels (Src 'over' Dest alpha composition operation) + DestPix.A := SrcPix.A + DestPix.A - SrcPix.A * DestPix.A; + if DestPix.A = 0 then + SrcAlpha := 0 + else + SrcAlpha := SrcPix.A / DestPix.A; + DestAlpha := 1.0 - SrcAlpha; + DestPix.R := SrcPix.R * SrcAlpha + DestPix.R * DestAlpha; + DestPix.G := SrcPix.G * SrcAlpha + DestPix.G * DestAlpha; + DestPix.B := SrcPix.B * SrcAlpha + DestPix.B * DestAlpha; + // Write blended pixel + DestInfo.SetPixelFP(DestPtr, DestInfo, nil, DestPix); +end; + +procedure PixelAddProc(const SrcPix: TColorFPRec; DestPtr: PByte; + DestInfo: PImageFormatInfo; SrcFactor, DestFactor: TBlendingFactor); +var + DestPix: TColorFPRec; +begin + // Just add Src and Dest + DestPix := DestInfo.GetPixelFP(DestPtr, DestInfo, nil); + DestPix.R := SrcPix.R + DestPix.R; + DestPix.G := SrcPix.G + DestPix.G; + DestPix.B := SrcPix.B + DestPix.B; + DestPix.A := SrcPix.A + DestPix.A; + DestInfo.SetPixelFP(DestPtr, DestInfo, nil, DestPix); +end; + +function CompareColors(const C1, C2: TColorFPRec): Single; {$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := (C1.R * GrayConv.R + C1.G * GrayConv.G + C1.B * GrayConv.B) - + (C2.R * GrayConv.R + C2.G * GrayConv.G + C2.B * GrayConv.B); +end; + +function MedianSelect(var Pixels: TDynFPPixelArray): TColorFPRec; + + procedure QuickSort(L, R: Integer); + var + I, J: Integer; + P, Temp: TColorFPRec; + begin + repeat + I := L; + J := R; + P := Pixels[(L + R) shr 1]; + repeat + while CompareColors(Pixels[I], P) < 0 do Inc(I); + while CompareColors(Pixels[J], P) > 0 do Dec(J); + if I <= J then + begin + Temp := Pixels[I]; + Pixels[I] := Pixels[J]; + Pixels[J] := Temp; + Inc(I); + Dec(J); + end; + until I > J; + if L < J then + QuickSort(L, J); + L := I; + until I >= R; + end; + +begin + // First sort pixels + QuickSort(0, High(Pixels)); + // Select middle pixel + Result := Pixels[Length(Pixels) div 2]; +end; + +function MinSelect(var Pixels: TDynFPPixelArray): TColorFPRec; +var + I: Integer; +begin + Result := Pixels[0]; + for I := 1 to High(Pixels) do + begin + if CompareColors(Pixels[I], Result) < 0 then + Result := Pixels[I]; + end; +end; + +function MaxSelect(var Pixels: TDynFPPixelArray): TColorFPRec; +var + I: Integer; +begin + Result := Pixels[0]; + for I := 1 to High(Pixels) do + begin + if CompareColors(Pixels[I], Result) > 0 then + Result := Pixels[I]; + end; +end; + +function TransformContrastBrightness(const Pixel: TColorFPRec; C, B, P3: Single): TColorFPRec; +begin + Result.A := Pixel.A; + Result.R := Pixel.R * C + B; + Result.G := Pixel.G * C + B; + Result.B := Pixel.B * C + B; +end; + +function TransformGamma(const Pixel: TColorFPRec; R, G, B: Single): TColorFPRec; +begin + Result.A := Pixel.A; + Result.R := Power(Pixel.R, 1.0 / R); + Result.G := Power(Pixel.G, 1.0 / G); + Result.B := Power(Pixel.B, 1.0 / B); +end; + +function TransformInvert(const Pixel: TColorFPRec; P1, P2, P3: Single): TColorFPRec; +begin + Result.A := Pixel.A; + Result.R := 1.0 - Pixel.R; + Result.G := 1.0 - Pixel.G; + Result.B := 1.0 - Pixel.B; +end; + +function TransformThreshold(const Pixel: TColorFPRec; R, G, B: Single): TColorFPRec; +begin + Result.A := Pixel.A; + Result.R := IffFloat(Pixel.R >= R, 1.0, 0.0); + Result.G := IffFloat(Pixel.G >= G, 1.0, 0.0); + Result.B := IffFloat(Pixel.B >= B, 1.0, 0.0); +end; + +function TransformLevels(const Pixel: TColorFPRec; BlackPoint, WhitePoint, Exp: Single): TColorFPRec; +begin + Result.A := Pixel.A; + if Pixel.R > BlackPoint then + Result.R := Power((Pixel.R - BlackPoint) / (WhitePoint - BlackPoint), Exp) + else + Result.R := 0.0; + if Pixel.G > BlackPoint then + Result.G := Power((Pixel.G - BlackPoint) / (WhitePoint - BlackPoint), Exp) + else + Result.G := 0.0; + if Pixel.B > BlackPoint then + Result.B := Power((Pixel.B - BlackPoint) / (WhitePoint - BlackPoint), Exp) + else + Result.B := 0.0; +end; + +function TransformPremultiplyAlpha(const Pixel: TColorFPRec; P1, P2, P3: Single): TColorFPRec; +begin + Result.A := Pixel.A; + Result.R := Result.R * Pixel.A; + Result.G := Result.G * Pixel.A; + Result.B := Result.B * Pixel.A; +end; + +function TransformUnPremultiplyAlpha(const Pixel: TColorFPRec; P1, P2, P3: Single): TColorFPRec; +begin + Result.A := Pixel.A; + if Pixel.A <> 0.0 then + begin + Result.R := Result.R / Pixel.A; + Result.G := Result.G / Pixel.A; + Result.B := Result.B / Pixel.A; + end + else + begin + Result.R := 0; + Result.G := 0; + Result.B := 0; + end; +end; + + +{ TImagingCanvas class implementation } + +constructor TImagingCanvas.CreateForData(ImageDataPointer: PImageData); +begin + if ImageDataPointer = nil then + raise EImagingCanvasError.CreateFmt(SConstructorInvalidPointer, [ImageDataPointer]); + + if not TestImage(ImageDataPointer^) then + raise EImagingCanvasError.CreateFmt(SConstructorInvalidImage, [Imaging.ImageToStr(ImageDataPointer^)]); + + if not (ImageDataPointer.Format in GetSupportedFormats) then + raise EImagingCanvasError.CreateFmt(SConstructorUnsupportedFormat, [Imaging.ImageToStr(ImageDataPointer^)]); + + FPData := ImageDataPointer; + FPenWidth := 1; + SetPenColor32(pcWhite); + SetFillColor32(pcBlack); + FFillMode := fmSolid; + + UpdateCanvasState; +end; + +constructor TImagingCanvas.CreateForImage(Image: TBaseImage); +begin + CreateForData(Image.ImageDataPointer); +end; + +destructor TImagingCanvas.Destroy; +begin + inherited Destroy; +end; + +function TImagingCanvas.GetPixel32(X, Y: LongInt): TColor32; +begin + Result := Imaging.GetPixel32(FPData^, X, Y).Color; +end; + +function TImagingCanvas.GetPixelFP(X, Y: LongInt): TColorFPRec; +begin + Result := Imaging.GetPixelFP(FPData^, X, Y); +end; + +function TImagingCanvas.GetValid: Boolean; +begin + Result := (FPData <> nil) and (FDataSizeOnUpdate = FPData.Size); +end; + +procedure TImagingCanvas.SetPixel32(X, Y: LongInt; const Value: TColor32); +begin + if (X >= FClipRect.Left) and (Y >= FClipRect.Top) and + (X < FClipRect.Right) and (Y < FClipRect.Bottom) then + begin + Imaging.SetPixel32(FPData^, X, Y, TColor32Rec(Value)); + end; +end; + +procedure TImagingCanvas.SetPixelFP(X, Y: LongInt; const Value: TColorFPRec); +begin + if (X >= FClipRect.Left) and (Y >= FClipRect.Top) and + (X < FClipRect.Right) and (Y < FClipRect.Bottom) then + begin + Imaging.SetPixelFP(FPData^, X, Y, TColorFPRec(Value)); + end; +end; + +procedure TImagingCanvas.SetPenColor32(const Value: TColor32); +begin + FPenColor32 := Value; + TranslatePixel(@FPenColor32, @FPenColorFP, ifA8R8G8B8, ifA32R32G32B32F, nil, nil); +end; + +procedure TImagingCanvas.SetPenColorFP(const Value: TColorFPRec); +begin + FPenColorFP := Value; + TranslatePixel(@FPenColorFP, @FPenColor32, ifA32R32G32B32F, ifA8R8G8B8, nil, nil); +end; + +procedure TImagingCanvas.SetPenWidth(const Value: LongInt); +begin + FPenWidth := ClampInt(Value, 0, MaxPenWidth); +end; + +procedure TImagingCanvas.SetFillColor32(const Value: TColor32); +begin + FFillColor32 := Value; + TranslatePixel(@FFillColor32, @FFillColorFP, ifA8R8G8B8, ifA32R32G32B32F, nil, nil); +end; + +procedure TImagingCanvas.SetFillColorFP(const Value: TColorFPRec); +begin + FFillColorFP := Value; + TranslatePixel(@FFillColorFP, @FFillColor32, ifA32R32G32B32F, ifA8R8G8B8, nil, nil); +end; + +procedure TImagingCanvas.SetClipRect(const Value: TRect); +begin + FClipRect := Value; + SwapMin(FClipRect.Left, FClipRect.Right); + SwapMin(FClipRect.Top, FClipRect.Bottom); + IntersectRect(FClipRect, FClipRect, Rect(0, 0, FPData.Width, FPData.Height)); +end; + +procedure TImagingCanvas.CheckBeforeBlending(SrcFactor, + DestFactor: TBlendingFactor; DestCanvas: TImagingCanvas); +begin + if SrcFactor in [bfSrcColor, bfOneMinusSrcColor] then + raise EImagingCanvasBlendingError.Create('Invalid source blending factor. Check the documentation for TBlendingFactor.'); + if DestFactor in [bfDstColor, bfOneMinusDstColor] then + raise EImagingCanvasBlendingError.Create('Invalid destination blending factor. Check the documentation for TBlendingFactor.'); + if DestCanvas.FormatInfo.IsIndexed then + raise EImagingCanvasBlendingError.Create('Blending destination canvas cannot be in indexed mode.'); +end; + +function TImagingCanvas.GetPixelPointer(X, Y: LongInt): Pointer; +begin + Result := @PByteArray(FPData.Bits)[(Y * FPData.Width + X) * FFormatInfo.BytesPerPixel] +end; + +procedure TImagingCanvas.TranslateFPToNative(const Color: TColorFPRec); +begin + TranslateFPToNative(Color, @FNativeColor); +end; + +procedure TImagingCanvas.TranslateFPToNative(const Color: TColorFPRec; + Native: Pointer); +begin + ImagingFormats.TranslatePixel(@Color, Native, ifA32R32G32B32F, + FPData.Format, nil, FPData.Palette); +end; + +procedure TImagingCanvas.UpdateCanvasState; +begin + FDataSizeOnUpdate := FPData.Size; + ResetClipRect; + Imaging.GetImageFormatInfo(FPData.Format, FFormatInfo) +end; + +procedure TImagingCanvas.ResetClipRect; +begin + FClipRect := Rect(0, 0, FPData.Width, FPData.Height) +end; + +procedure TImagingCanvas.Clear; +begin + TranslateFPToNative(FFillColorFP); + Imaging.FillRect(FPData^, 0, 0, FPData.Width, FPData.Height, @FNativeColor); +end; + +function TImagingCanvas.ClipAxisParallelLine(var A1, A2, B: LongInt; + AStart, AStop, BStart, BStop: LongInt): Boolean; +begin + if (B >= BStart) and (B < BStop) then + begin + SwapMin(A1, A2); + if A1 < AStart then A1 := AStart; + if A2 >= AStop then A2 := AStop - 1; + Result := True; + end + else + Result := False; +end; + +procedure TImagingCanvas.HorzLineInternal(X1, X2, Y: LongInt; Color: Pointer; + Bpp: LongInt); +var + I, WidthBytes: LongInt; + PixelPtr: PByte; +begin + if (Y >= FClipRect.Top) and (Y < FClipRect.Bottom) then + begin + SwapMin(X1, X2); + X1 := Max(X1, FClipRect.Left); + X2 := Min(X2, FClipRect.Right); + PixelPtr := GetPixelPointer(X1, Y); + WidthBytes := (X2 - X1) * Bpp; + case Bpp of + 1: FillMemoryByte(PixelPtr, WidthBytes, PByte(Color)^); + 2: FillMemoryWord(PixelPtr, WidthBytes, PWord(Color)^); + 4: FillMemoryLongWord(PixelPtr, WidthBytes, PLongWord(Color)^); + else + for I := X1 to X2 do + begin + ImagingFormats.CopyPixel(Color, PixelPtr, Bpp); + Inc(PixelPtr, Bpp); + end; + end; + end; +end; + +procedure TImagingCanvas.CopyPixelInternal(X, Y: LongInt; Pixel: Pointer; + Bpp: LongInt); +begin + if (X >= FClipRect.Left) and (Y >= FClipRect.Top) and + (X < FClipRect.Right) and (Y < FClipRect.Bottom) then + begin + ImagingFormats.CopyPixel(Pixel, GetPixelPointer(X, Y), Bpp); + end; +end; + +procedure TImagingCanvas.HorzLine(X1, X2, Y: LongInt); +var + DstRect: TRect; +begin + if FPenMode = pmClear then Exit; + SwapMin(X1, X2); + if IntersectRect(DstRect, Rect(X1, Y - FPenWidth div 2, X2, + Y + FPenWidth div 2 + FPenWidth mod 2), FClipRect) then + begin + TranslateFPToNative(FPenColorFP); + Imaging.FillRect(FPData^, DstRect.Left, DstRect.Top, DstRect.Right - DstRect.Left, + DstRect.Bottom - DstRect.Top, @FNativeColor); + end; +end; + +procedure TImagingCanvas.VertLine(X, Y1, Y2: LongInt); +var + DstRect: TRect; +begin + if FPenMode = pmClear then Exit; + SwapMin(Y1, Y2); + if IntersectRect(DstRect, Rect(X - FPenWidth div 2, Y1, + X + FPenWidth div 2 + FPenWidth mod 2, Y2), FClipRect) then + begin + TranslateFPToNative(FPenColorFP); + Imaging.FillRect(FPData^, DstRect.Left, DstRect.Top, DstRect.Right - DstRect.Left, + DstRect.Bottom - DstRect.Top, @FNativeColor); + end; +end; + +procedure TImagingCanvas.Line(X1, Y1, X2, Y2: LongInt); +var + Steep: Boolean; + Error, YStep, DeltaX, DeltaY, X, Y, I, Bpp, W1, W2, Code1, Code2: LongInt; +begin + if FPenMode = pmClear then Exit; + + // If line is vertical or horizontal just call appropriate method + if X2 = X1 then + begin + VertLine(X1, Y1, Y2); + Exit; + end; + if Y2 = Y1 then + begin + HorzLine(X1, X2, Y1); + Exit; + end; + + // Determine if line is steep (angle with X-axis > 45 degrees) + Steep := Abs(Y2 - Y1) > Abs(X2 - X1); + + // If we need to draw thick line we just draw more 1 pixel lines around + // the one we already drawn. Setting FLineRecursion assures that we + // won't be doing recursions till the end of the world. + if (FPenWidth > 1) and not FLineRecursion then + begin + FLineRecursion := True; + W1 := FPenWidth div 2; + W2 := W1; + if FPenWidth mod 2 = 0 then + Dec(W1); + if Steep then + begin + // Add lines left/right + for I := 1 to W1 do + Line(X1, Y1 - I, X2, Y2 - I); + for I := 1 to W2 do + Line(X1, Y1 + I, X2, Y2 + I); + end + else + begin + // Add lines above/under + for I := 1 to W1 do + Line(X1 - I, Y1, X2 - I, Y2); + for I := 1 to W2 do + Line(X1 + I, Y1, X2 + I, Y2); + end; + FLineRecursion := False; + end; + + with FClipRect do + begin + // Use part of Cohen-Sutherland line clipping to determine if any part of line + // is in ClipRect + Code1 := Ord(X1 < Left) + Ord(X1 > Right) shl 1 + Ord(Y1 < Top) shl 2 + Ord(Y1 > Bottom) shl 3; + Code2 := Ord(X2 < Left) + Ord(X2 > Right) shl 1 + Ord(Y2 < Top) shl 2 + Ord(Y2 > Bottom) shl 3; + end; + + if (Code1 and Code2) = 0 then + begin + TranslateFPToNative(FPenColorFP); + Bpp := FFormatInfo.BytesPerPixel; + + // If line is steep swap X and Y coordinates so later we just have one loop + // of two (where only one is used according to steepness). + if Steep then + begin + SwapValues(X1, Y1); + SwapValues(X2, Y2); + end; + if X1 > X2 then + begin + SwapValues(X1, X2); + SwapValues(Y1, Y2); + end; + + DeltaX := X2 - X1; + DeltaY := Abs(Y2 - Y1); + YStep := Iff(Y2 > Y1, 1, -1); + Error := 0; + Y := Y1; + + // Draw line using Bresenham algorithm. No real line clipping here, + // just don't draw pixels outsize clip rect. + for X := X1 to X2 do + begin + if Steep then + CopyPixelInternal(Y, X, @FNativeColor, Bpp) + else + CopyPixelInternal(X, Y, @FNativeColor, Bpp); + Error := Error + DeltaY; + if Error * 2 >= DeltaX then + begin + Inc(Y, YStep); + Dec(Error, DeltaX); + end; + end; + end; +end; + +procedure TImagingCanvas.FrameRect(const Rect: TRect); +var + HalfPen, PenMod: LongInt; +begin + if FPenMode = pmClear then Exit; + HalfPen := FPenWidth div 2; + PenMod := FPenWidth mod 2; + HorzLine(Rect.Left - HalfPen, Rect.Right + HalfPen + PenMod - 1, Rect.Top); + HorzLine(Rect.Left - HalfPen, Rect.Right + HalfPen + PenMod - 1, Rect.Bottom - 1); + VertLine(Rect.Left, Rect.Top, Rect.Bottom); + VertLine(Rect.Right - 1, Rect.Top, Rect.Bottom); +end; + +procedure TImagingCanvas.FillRect(const Rect: TRect); +var + DstRect: TRect; +begin + if (FFillMode <> fmClear) and IntersectRect(DstRect, Rect, FClipRect) then + begin + TranslateFPToNative(FFillColorFP); + Imaging.FillRect(FPData^, DstRect.Left, DstRect.Top, DstRect.Right - DstRect.Left, + DstRect.Bottom - DstRect.Top, @FNativeColor); + end; +end; + +procedure TImagingCanvas.FillRectBlend(const Rect: TRect; SrcFactor, + DestFactor: TBlendingFactor); +var + DstRect: TRect; + X, Y: Integer; + Line: PByte; +begin + if (FFillMode <> fmClear) and IntersectRect(DstRect, Rect, FClipRect) then + begin + CheckBeforeBlending(SrcFactor, DestFactor, Self); + for Y := DstRect.Top to DstRect.Bottom - 1 do + begin + Line := @PByteArray(FPData.Bits)[(Y * FPData.Width + DstRect.Left) * FFormatInfo.BytesPerPixel]; + for X := DstRect.Left to DstRect.Right - 1 do + begin + PixelBlendProc(FFillColorFP, Line, @FFormatInfo, SrcFactor, DestFactor); + Inc(Line, FFormatInfo.BytesPerPixel); + end; + end; + end; +end; + +procedure TImagingCanvas.Rectangle(const Rect: TRect); +begin + FillRect(Rect); + FrameRect(Rect); +end; + +procedure TImagingCanvas.Ellipse(const Rect: TRect); +var + RadX, RadY, DeltaX, DeltaY, R, RX, RY: LongInt; + X1, X2, Y1, Y2, Bpp, OldY: LongInt; + Fill, Pen: TColorFPRec; +begin + // TODO: Use PenWidth + X1 := Rect.Left; + X2 := Rect.Right; + Y1 := Rect.Top; + Y2 := Rect.Bottom; + + TranslateFPToNative(FPenColorFP, @Pen); + TranslateFPToNative(FFillColorFP, @Fill); + Bpp := FFormatInfo.BytesPerPixel; + + SwapMin(X1, X2); + SwapMin(Y1, Y2); + + RadX := (X2 - X1) div 2; + RadY := (Y2 - Y1) div 2; + + Y1 := Y1 + RadY; + Y2 := Y1; + OldY := Y1; + + DeltaX := (RadX * RadX); + DeltaY := (RadY * RadY); + R := RadX * RadY * RadY; + RX := R; + RY := 0; + + if (FFillMode <> fmClear) then + HorzLineInternal(X1, X2, Y1, @Fill, Bpp); + CopyPixelInternal(X1, Y1, @Pen, Bpp); + CopyPixelInternal(X2, Y1, @Pen, Bpp); + + while RadX > 0 do + begin + if R > 0 then + begin + Inc(Y1); + Dec(Y2); + Inc(RY, DeltaX); + Dec(R, RY); + end; + if R <= 0 then + begin + Dec(RadX); + Inc(X1); + Dec(X2); + Dec(RX, DeltaY); + Inc(R, RX); + end; + + if (OldY <> Y1) and (FFillMode <> fmClear) then + begin + HorzLineInternal(X1, X2, Y1, @Fill, Bpp); + HorzLineInternal(X1, X2, Y2, @Fill, Bpp); + end; + OldY := Y1; + + CopyPixelInternal(X1, Y1, @Pen, Bpp); + CopyPixelInternal(X2, Y1, @Pen, Bpp); + CopyPixelInternal(X1, Y2, @Pen, Bpp); + CopyPixelInternal(X2, Y2, @Pen, Bpp); + end; +end; + +procedure TImagingCanvas.FloodFill(X, Y: Integer; BoundaryFillMode: Boolean); +var + Stack: array of TPoint; + StackPos, Y1: Integer; + OldColor: TColor32; + SpanLeft, SpanRight: Boolean; + + procedure Push(AX, AY: Integer); + begin + if StackPos < High(Stack) then + begin + Inc(StackPos); + Stack[StackPos].X := AX; + Stack[StackPos].Y := AY; + end + else + begin + SetLength(Stack, Length(Stack) + FPData.Width); + Push(AX, AY); + end; + end; + + function Pop(out AX, AY: Integer): Boolean; + begin + if StackPos > 0 then + begin + AX := Stack[StackPos].X; + AY := Stack[StackPos].Y; + Dec(StackPos); + Result := True; + end + else + Result := False; + end; + + function Compare(AX, AY: Integer): Boolean; + var + Color: TColor32; + begin + Color := GetPixel32(AX, AY); + if BoundaryFillMode then + Result := (Color <> FFillColor32) and (Color <> FPenColor32) + else + Result := Color = OldColor; + end; + +begin + // Scanline Floodfill Algorithm With Stack + // http://student.kuleuven.be/~m0216922/CG/floodfill.html + + if not PtInRect(FClipRect, Point(X, Y)) then Exit; + + SetLength(Stack, FPData.Width * 4); + StackPos := 0; + + OldColor := GetPixel32(X, Y); + + Push(X, Y); + + while Pop(X, Y) do + begin + Y1 := Y; + while (Y1 >= FClipRect.Top) and Compare(X, Y1) do + Dec(Y1); + + Inc(Y1); + SpanLeft := False; + SpanRight := False; + + while (Y1 < FClipRect.Bottom) and Compare(X, Y1) do + begin + SetPixel32(X, Y1, FFillColor32); + if not SpanLeft and (X > FClipRect.Left) and Compare(X - 1, Y1) then + begin + Push(X - 1, Y1); + SpanLeft := True; + end + else if SpanLeft and (X > FClipRect.Left) and not Compare(X - 1, Y1) then + SpanLeft := False + else if not SpanRight and (X < FClipRect.Right - 1) and Compare(X + 1, Y1)then + begin + Push(X + 1, Y1); + SpanRight := True; + end + else if SpanRight and (X < FClipRect.Right - 1) and not Compare(X + 1, Y1) then + SpanRight := False; + + Inc(Y1); + end; + end; +end; + +procedure TImagingCanvas.DrawInternal(const SrcRect: TRect; + DestCanvas: TImagingCanvas; DestX, DestY: Integer; SrcFactor, + DestFactor: TBlendingFactor; PixelWriteProc: TPixelWriteProc); +var + X, Y, SrcX, SrcY, Width, Height, SrcBpp, DestBpp: Integer; + PSrc: TColorFPRec; + SrcPointer, DestPointer: PByte; +begin + CheckBeforeBlending(SrcFactor, DestFactor, DestCanvas); + SrcX := SrcRect.Left; + SrcY := SrcRect.Top; + Width := SrcRect.Right - SrcRect.Left; + Height := SrcRect.Bottom - SrcRect.Top; + SrcBpp := FFormatInfo.BytesPerPixel; + DestBpp := DestCanvas.FFormatInfo.BytesPerPixel; + // Clip src and dst rects + ClipCopyBounds(SrcX, SrcY, Width, Height, DestX, DestY, + FPData.Width, FPData.Height, DestCanvas.ClipRect); + + for Y := 0 to Height - 1 do + begin + // Get src and dst scanlines + SrcPointer := @PByteArray(FPData.Bits)[((SrcY + Y) * FPData.Width + SrcX) * SrcBpp]; + DestPointer := @PByteArray(DestCanvas.FPData.Bits)[((DestY + Y) * DestCanvas.FPData.Width + DestX) * DestBpp]; + + for X := 0 to Width - 1 do + begin + PSrc := FFormatInfo.GetPixelFP(SrcPointer, @FFormatInfo, FPData.Palette); + // Call pixel writer procedure - combine source and dest pixels + PixelWriteProc(PSrc, DestPointer, @DestCanvas.FFormatInfo, SrcFactor, DestFactor); + // Increment pixel pointers + Inc(SrcPointer, SrcBpp); + Inc(DestPointer, DestBpp); + end; + end; +end; + +procedure TImagingCanvas.DrawBlend(const SrcRect: TRect; DestCanvas: TImagingCanvas; + DestX, DestY: Integer; SrcFactor, DestFactor: TBlendingFactor); +begin + DrawInternal(SrcRect, DestCanvas, DestX, DestY, SrcFactor, DestFactor, PixelBlendProc); +end; + +procedure TImagingCanvas.DrawAlpha(const SrcRect: TRect; DestCanvas: TImagingCanvas; + DestX, DestY: Integer); +begin + DrawInternal(SrcRect, DestCanvas, DestX, DestY, bfIgnore, bfIgnore, PixelAlphaProc); +end; + +procedure TImagingCanvas.DrawAdd(const SrcRect: TRect; + DestCanvas: TImagingCanvas; DestX, DestY: Integer); +begin + DrawInternal(SrcRect, DestCanvas, DestX, DestY, bfIgnore, bfIgnore, PixelAddProc); +end; + +procedure TImagingCanvas.StretchDrawInternal(const SrcRect: TRect; + DestCanvas: TImagingCanvas; const DestRect: TRect; + SrcFactor, DestFactor: TBlendingFactor; Filter: TResizeFilter; + PixelWriteProc: TPixelWriteProc); +const + FilterMapping: array[TResizeFilter] of TSamplingFilter = + (sfNearest, sfLinear, DefaultCubicFilter, sfLanczos); +var + X, Y, I, J, SrcX, SrcY, SrcWidth, SrcHeight: Integer; + DestX, DestY, DestWidth, DestHeight, SrcBpp, DestBpp: Integer; + SrcPix: TColorFPRec; + MapX, MapY: TMappingTable; + XMinimum, XMaximum: Integer; + LineBuffer: array of TColorFPRec; + ClusterX, ClusterY: TCluster; + Weight, AccumA, AccumR, AccumG, AccumB: Single; + DestLine: PByte; + FilterFunction: TFilterFunction; + Radius: Single; +begin + CheckBeforeBlending(SrcFactor, DestFactor, DestCanvas); + SrcX := SrcRect.Left; + SrcY := SrcRect.Top; + SrcWidth := SrcRect.Right - SrcRect.Left; + SrcHeight := SrcRect.Bottom - SrcRect.Top; + DestX := DestRect.Left; + DestY := DestRect.Top; + DestWidth := DestRect.Right - DestRect.Left; + DestHeight := DestRect.Bottom - DestRect.Top; + SrcBpp := FFormatInfo.BytesPerPixel; + DestBpp := DestCanvas.FFormatInfo.BytesPerPixel; + // Get actual resampling filter and radius + FilterFunction := SamplingFilterFunctions[FilterMapping[Filter]]; + Radius := SamplingFilterRadii[FilterMapping[Filter]]; + // Clip src and dst rects + ClipStretchBounds(SrcX, SrcY, SrcWidth, SrcHeight, DestX, DestY, DestWidth, DestHeight, + FPData.Width, FPData.Height, DestCanvas.ClipRect); + // Generate mapping tables + MapX := BuildMappingTable(DestX, DestX + DestWidth, SrcX, SrcX + SrcWidth, + FPData.Width, FilterFunction, Radius, False); + MapY := BuildMappingTable(DestY, DestY + DestHeight, SrcY, SrcY + SrcHeight, + FPData.Height, FilterFunction, Radius, False); + FindExtremes(MapX, XMinimum, XMaximum); + SetLength(LineBuffer, XMaximum - XMinimum + 1); + + for J := 0 to DestHeight - 1 do + begin + ClusterY := MapY[J]; + for X := XMinimum to XMaximum do + begin + AccumA := 0.0; + AccumR := 0.0; + AccumG := 0.0; + AccumB := 0.0; + for Y := 0 to Length(ClusterY) - 1 do + begin + Weight := ClusterY[Y].Weight; + SrcPix := FFormatInfo.GetPixelFP(@PByteArray(FPData.Bits)[(ClusterY[Y].Pos * FPData.Width + X) * SrcBpp], + @FFormatInfo, FPData.Palette); + AccumB := AccumB + SrcPix.B * Weight; + AccumG := AccumG + SrcPix.G * Weight; + AccumR := AccumR + SrcPix.R * Weight; + AccumA := AccumA + SrcPix.A * Weight; + end; + with LineBuffer[X - XMinimum] do + begin + A := AccumA; + R := AccumR; + G := AccumG; + B := AccumB; + end; + end; + + DestLine := @PByteArray(DestCanvas.FPData.Bits)[((J + DestY) * DestCanvas.FPData.Width + DestX) * DestBpp]; + + for I := 0 to DestWidth - 1 do + begin + ClusterX := MapX[I]; + AccumA := 0.0; + AccumR := 0.0; + AccumG := 0.0; + AccumB := 0.0; + for X := 0 to Length(ClusterX) - 1 do + begin + Weight := ClusterX[X].Weight; + with LineBuffer[ClusterX[X].Pos - XMinimum] do + begin + AccumB := AccumB + B * Weight; + AccumG := AccumG + G * Weight; + AccumR := AccumR + R * Weight; + AccumA := AccumA + A * Weight; + end; + end; + + SrcPix.A := AccumA; + SrcPix.R := AccumR; + SrcPix.G := AccumG; + SrcPix.B := AccumB; + + // Write resulting blended pixel + PixelWriteProc(SrcPix, DestLine, @DestCanvas.FFormatInfo, SrcFactor, DestFactor); + Inc(DestLine, DestBpp); + end; + end; +end; + +procedure TImagingCanvas.StretchDrawBlend(const SrcRect: TRect; + DestCanvas: TImagingCanvas; const DestRect: TRect; + SrcFactor, DestFactor: TBlendingFactor; Filter: TResizeFilter); +begin + StretchDrawInternal(SrcRect, DestCanvas, DestRect, SrcFactor, DestFactor, Filter, PixelBlendProc); +end; + +procedure TImagingCanvas.StretchDrawAlpha(const SrcRect: TRect; + DestCanvas: TImagingCanvas; const DestRect: TRect; Filter: TResizeFilter); +begin + StretchDrawInternal(SrcRect, DestCanvas, DestRect, bfIgnore, bfIgnore, Filter, PixelAlphaProc); +end; + +procedure TImagingCanvas.StretchDrawAdd(const SrcRect: TRect; + DestCanvas: TImagingCanvas; const DestRect: TRect; Filter: TResizeFilter); +begin + StretchDrawInternal(SrcRect, DestCanvas, DestRect, bfIgnore, bfIgnore, Filter, PixelAddProc); +end; + +procedure TImagingCanvas.ApplyConvolution(Kernel: PLongInt; KernelSize, + Divisor: LongInt; Bias: Single; ClampChannels: Boolean); +var + X, Y, I, J, PosY, PosX, SizeDiv2, KernelValue, WidthBytes, Bpp: LongInt; + R, G, B, DivFloat: Single; + Pixel: TColorFPRec; + TempImage: TImageData; + DstPointer, SrcPointer: PByte; +begin + SizeDiv2 := KernelSize div 2; + DivFloat := IffFloat(Divisor > 1, 1.0 / Divisor, 1.0); + Bpp := FFormatInfo.BytesPerPixel; + WidthBytes := FPData.Width * Bpp; + + InitImage(TempImage); + CloneImage(FPData^, TempImage); + + try + // For every pixel in clip rect + for Y := FClipRect.Top to FClipRect.Bottom - 1 do + begin + DstPointer := @PByteArray(FPData.Bits)[Y * WidthBytes + FClipRect.Left * Bpp]; + + for X := FClipRect.Left to FClipRect.Right - 1 do + begin + // Reset accumulators + R := 0.0; + G := 0.0; + B := 0.0; + + for J := 0 to KernelSize - 1 do + begin + PosY := ClampInt(Y + J - SizeDiv2, FClipRect.Top, FClipRect.Bottom - 1); + + for I := 0 to KernelSize - 1 do + begin + PosX := ClampInt(X + I - SizeDiv2, FClipRect.Left, FClipRect.Right - 1); + SrcPointer := @PByteArray(TempImage.Bits)[PosY * WidthBytes + PosX * Bpp]; + + // Get pixels from neighbourhood of current pixel and add their + // colors to accumulators weighted by filter kernel values + Pixel := FFormatInfo.GetPixelFP(SrcPointer, @FFormatInfo, TempImage.Palette); + KernelValue := PLongIntArray(Kernel)[J * KernelSize + I]; + + R := R + Pixel.R * KernelValue; + G := G + Pixel.G * KernelValue; + B := B + Pixel.B * KernelValue; + end; + end; + + Pixel := FFormatInfo.GetPixelFP(DstPointer, @FFormatInfo, FPData.Palette); + + Pixel.R := R * DivFloat + Bias; + Pixel.G := G * DivFloat + Bias; + Pixel.B := B * DivFloat + Bias; + + if ClampChannels then + ClampFloatPixel(Pixel); + + // Set resulting pixel color + FFormatInfo.SetPixelFP(DstPointer, @FFormatInfo, FPData.Palette, Pixel); + + Inc(DstPointer, Bpp); + end; + end; + + finally + FreeImage(TempImage); + end; +end; + +procedure TImagingCanvas.ApplyConvolution3x3(const Filter: TConvolutionFilter3x3); +begin + ApplyConvolution(@Filter.Kernel, 3, Filter.Divisor, Filter.Bias, True); +end; + +procedure TImagingCanvas.ApplyConvolution5x5(const Filter: TConvolutionFilter5x5); +begin + ApplyConvolution(@Filter.Kernel, 5, Filter.Divisor, Filter.Bias, True); +end; + +procedure TImagingCanvas.ApplyNonLinearFilter(FilterSize: Integer; SelectFunc: TSelectPixelFunction); +var + X, Y, I, J, PosY, PosX, SizeDiv2, WidthBytes, Bpp: LongInt; + Pixel: TColorFPRec; + TempImage: TImageData; + DstPointer, SrcPointer: PByte; + NeighPixels: TDynFPPixelArray; +begin + SizeDiv2 := FilterSize div 2; + Bpp := FFormatInfo.BytesPerPixel; + WidthBytes := FPData.Width * Bpp; + SetLength(NeighPixels, FilterSize * FilterSize); + + InitImage(TempImage); + CloneImage(FPData^, TempImage); + + try + // For every pixel in clip rect + for Y := FClipRect.Top to FClipRect.Bottom - 1 do + begin + DstPointer := @PByteArray(FPData.Bits)[Y * WidthBytes + FClipRect.Left * Bpp]; + + for X := FClipRect.Left to FClipRect.Right - 1 do + begin + for J := 0 to FilterSize - 1 do + begin + PosY := ClampInt(Y + J - SizeDiv2, FClipRect.Top, FClipRect.Bottom - 1); + + for I := 0 to FilterSize - 1 do + begin + PosX := ClampInt(X + I - SizeDiv2, FClipRect.Left, FClipRect.Right - 1); + SrcPointer := @PByteArray(TempImage.Bits)[PosY * WidthBytes + PosX * Bpp]; + + // Get pixels from neighbourhood of current pixel and store them + Pixel := FFormatInfo.GetPixelFP(SrcPointer, @FFormatInfo, TempImage.Palette); + NeighPixels[J * FilterSize + I] := Pixel; + end; + end; + + // Choose pixel using custom function + Pixel := SelectFunc(NeighPixels); + // Set resulting pixel color + FFormatInfo.SetPixelFP(DstPointer, @FFormatInfo, FPData.Palette, Pixel); + + Inc(DstPointer, Bpp); + end; + end; + + finally + FreeImage(TempImage); + end; +end; + +procedure TImagingCanvas.ApplyMedianFilter(FilterSize: Integer); +begin + ApplyNonLinearFilter(FilterSize, MedianSelect); +end; + +procedure TImagingCanvas.ApplyMinFilter(FilterSize: Integer); +begin + ApplyNonLinearFilter(FilterSize, MinSelect); +end; + +procedure TImagingCanvas.ApplyMaxFilter(FilterSize: Integer); +begin + ApplyNonLinearFilter(FilterSize, MaxSelect); +end; + +procedure TImagingCanvas.PointTransform(Transform: TPointTransformFunction; + Param1, Param2, Param3: Single); +var + X, Y, Bpp, WidthBytes: Integer; + PixPointer: PByte; + Pixel: TColorFPRec; +begin + Bpp := FFormatInfo.BytesPerPixel; + WidthBytes := FPData.Width * Bpp; + + // For every pixel in clip rect + for Y := FClipRect.Top to FClipRect.Bottom - 1 do + begin + PixPointer := @PByteArray(FPData.Bits)[Y * WidthBytes + FClipRect.Left * Bpp]; + for X := FClipRect.Left to FClipRect.Right - 1 do + begin + Pixel := FFormatInfo.GetPixelFP(PixPointer, @FFormatInfo, FPData.Palette); + + FFormatInfo.SetPixelFP(PixPointer, @FFormatInfo, FPData.Palette, + Transform(Pixel, Param1, Param2, Param3)); + + Inc(PixPointer, Bpp); + end; + end; +end; + +procedure TImagingCanvas.ModifyContrastBrightness(Contrast, Brightness: Single); +begin + PointTransform(TransformContrastBrightness, 1.0 + Contrast / 100, + Brightness / 100, 0); +end; + +procedure TImagingCanvas.GammaCorection(Red, Green, Blue: Single); +begin + PointTransform(TransformGamma, Red, Green, Blue); +end; + +procedure TImagingCanvas.InvertColors; +begin + PointTransform(TransformInvert, 0, 0, 0); +end; + +procedure TImagingCanvas.Threshold(Red, Green, Blue: Single); +begin + PointTransform(TransformThreshold, Red, Green, Blue); +end; + +procedure TImagingCanvas.AdjustColorLevels(BlackPoint, WhitePoint, MidPoint: Single); +begin + PointTransform(TransformLevels, BlackPoint, WhitePoint, 1.0 / MidPoint); +end; + +procedure TImagingCanvas.PremultiplyAlpha; +begin + PointTransform(TransformPremultiplyAlpha, 0, 0, 0); +end; + +procedure TImagingCanvas.UnPremultiplyAlpha; +begin + PointTransform(TransformUnPremultiplyAlpha, 0, 0, 0); +end; + +procedure TImagingCanvas.GetHistogram(out Red, Green, Blue, Alpha, + Gray: THistogramArray); +var + X, Y, Bpp: Integer; + PixPointer: PByte; + Color32: TColor32Rec; +begin + FillChar(Red, SizeOf(Red), 0); + FillChar(Green, SizeOf(Green), 0); + FillChar(Blue, SizeOf(Blue), 0); + FillChar(Alpha, SizeOf(Alpha), 0); + FillChar(Gray, SizeOf(Gray), 0); + + Bpp := FFormatInfo.BytesPerPixel; + + for Y := FClipRect.Top to FClipRect.Bottom - 1 do + begin + PixPointer := @PByteArray(FPData.Bits)[Y * FPData.Width * Bpp + FClipRect.Left * Bpp]; + for X := FClipRect.Left to FClipRect.Right - 1 do + begin + Color32 := FFormatInfo.GetPixel32(PixPointer, @FFormatInfo, FPData.Palette); + + Inc(Red[Color32.R]); + Inc(Green[Color32.G]); + Inc(Blue[Color32.B]); + Inc(Alpha[Color32.A]); + Inc(Gray[Round(GrayConv.R * Color32.R + GrayConv.G * Color32.G + GrayConv.B * Color32.B)]); + + Inc(PixPointer, Bpp); + end; + end; +end; + +procedure TImagingCanvas.FillChannel(ChannelId: Integer; NewChannelValue: Byte); +var + X, Y, Bpp: Integer; + PixPointer: PByte; + Color32: TColor32Rec; +begin + Bpp := FFormatInfo.BytesPerPixel; + + for Y := FClipRect.Top to FClipRect.Bottom - 1 do + begin + PixPointer := @PByteArray(FPData.Bits)[Y * FPData.Width * Bpp + FClipRect.Left * Bpp]; + for X := FClipRect.Left to FClipRect.Right - 1 do + begin + Color32 := FFormatInfo.GetPixel32(PixPointer, @FFormatInfo, FPData.Palette); + Color32.Channels[ChannelId] := NewChannelValue; + FFormatInfo.SetPixel32(PixPointer, @FFormatInfo, FPData.Palette, Color32); + + Inc(PixPointer, Bpp); + end; + end; +end; + +procedure TImagingCanvas.FillChannelFP(ChannelId: Integer; NewChannelValue: Single); +var + X, Y, Bpp: Integer; + PixPointer: PByte; + ColorFP: TColorFPRec; +begin + Bpp := FFormatInfo.BytesPerPixel; + + for Y := FClipRect.Top to FClipRect.Bottom - 1 do + begin + PixPointer := @PByteArray(FPData.Bits)[Y * FPData.Width * Bpp + FClipRect.Left * Bpp]; + for X := FClipRect.Left to FClipRect.Right - 1 do + begin + ColorFP := FFormatInfo.GetPixelFP(PixPointer, @FFormatInfo, FPData.Palette); + ColorFP.Channels[ChannelId] := NewChannelValue; + FFormatInfo.SetPixelFP(PixPointer, @FFormatInfo, FPData.Palette, ColorFP); + + Inc(PixPointer, Bpp); + end; + end; +end; + +class function TImagingCanvas.GetSupportedFormats: TImageFormats; +begin + Result := [ifIndex8..Pred(ifDXT1)]; +end; + +{ TFastARGB32Canvas } + +destructor TFastARGB32Canvas.Destroy; +begin + FreeMem(FScanlines); + inherited Destroy; +end; + +procedure TFastARGB32Canvas.AlphaBlendPixels(SrcPix, DestPix: PColor32Rec); +var + SrcAlpha, DestAlpha, FinalAlpha: Integer; +begin + FinalAlpha := SrcPix.A + 1 + (DestPix.A * (256 - SrcPix.A)) shr 8; + if FinalAlpha = 0 then + SrcAlpha := 0 + else + SrcAlpha := (SrcPix.A shl 8) div FinalAlpha; + DestAlpha := 256 - SrcAlpha; + + DestPix.A := ClampToByte(FinalAlpha); + DestPix.R := (SrcPix.R * SrcAlpha + DestPix.R * DestAlpha) shr 8; + DestPix.G := (SrcPix.G * SrcAlpha + DestPix.G * DestAlpha) shr 8; + DestPix.B := (SrcPix.B * SrcAlpha + DestPix.B * DestAlpha) shr 8; +end; + +procedure TFastARGB32Canvas.DrawAlpha(const SrcRect: TRect; + DestCanvas: TImagingCanvas; DestX, DestY: Integer); +var + X, Y, SrcX, SrcY, Width, Height: Integer; + SrcPix, DestPix: PColor32Rec; +begin + if DestCanvas.ClassType <> Self.ClassType then + begin + inherited; + Exit; + end; + + SrcX := SrcRect.Left; + SrcY := SrcRect.Top; + Width := SrcRect.Right - SrcRect.Left; + Height := SrcRect.Bottom - SrcRect.Top; + ClipCopyBounds(SrcX, SrcY, Width, Height, DestX, DestY, + FPData.Width, FPData.Height, DestCanvas.ClipRect); + + for Y := 0 to Height - 1 do + begin + SrcPix := @FScanlines[SrcY + Y, SrcX]; + DestPix := @TFastARGB32Canvas(DestCanvas).FScanlines[DestY + Y, DestX]; + for X := 0 to Width - 1 do + begin + AlphaBlendPixels(SrcPix, DestPix); + Inc(SrcPix); + Inc(DestPix); + end; + end; +end; + +function TFastARGB32Canvas.GetPixel32(X, Y: LongInt): TColor32; +begin + Result := FScanlines[Y, X].Color; +end; + +procedure TFastARGB32Canvas.SetPixel32(X, Y: LongInt; const Value: TColor32); +begin + if (X >= FClipRect.Left) and (Y >= FClipRect.Top) and + (X < FClipRect.Right) and (Y < FClipRect.Bottom) then + begin + FScanlines[Y, X].Color := Value; + end; +end; + +procedure TFastARGB32Canvas.StretchDrawAlpha(const SrcRect: TRect; + DestCanvas: TImagingCanvas; const DestRect: TRect; Filter: TResizeFilter); +var + X, Y, ScaleX, ScaleY, Yp, Xp, Weight1, Weight2, Weight3, Weight4, InvFracY, T1, T2: Integer; + FracX, FracY: Cardinal; + SrcX, SrcY, SrcWidth, SrcHeight: Integer; + DestX, DestY, DestWidth, DestHeight: Integer; + SrcLine, SrcLine2: PColor32RecArray; + DestPix: PColor32Rec; + Accum: TColor32Rec; +begin + if (Filter = rfBicubic) or (DestCanvas.ClassType <> Self.ClassType) then + begin + inherited; + Exit; + end; + + SrcX := SrcRect.Left; + SrcY := SrcRect.Top; + SrcWidth := SrcRect.Right - SrcRect.Left; + SrcHeight := SrcRect.Bottom - SrcRect.Top; + DestX := DestRect.Left; + DestY := DestRect.Top; + DestWidth := DestRect.Right - DestRect.Left; + DestHeight := DestRect.Bottom - DestRect.Top; + // Clip src and dst rects + ClipStretchBounds(SrcX, SrcY, SrcWidth, SrcHeight, DestX, DestY, DestWidth, DestHeight, + FPData.Width, FPData.Height, DestCanvas.ClipRect); + ScaleX := (SrcWidth shl 16) div DestWidth; + ScaleY := (SrcHeight shl 16) div DestHeight; + + // Nearest and linear filtering using fixed point math + + if Filter = rfNearest then + begin + Yp := 0; + for Y := DestY to DestY + DestHeight - 1 do + begin + Xp := 0; + SrcLine := @FScanlines[SrcY + Yp shr 16, SrcX]; + DestPix := @TFastARGB32Canvas(DestCanvas).FScanlines[Y, DestX]; + for X := 0 to DestWidth - 1 do + begin + AlphaBlendPixels(@SrcLine[Xp shr 16], DestPix); + Inc(DestPix); + Inc(Xp, ScaleX); + end; + Inc(Yp, ScaleY); + end; + end + else + begin + Yp := (ScaleY shr 1) - $8000; + for Y := DestY to DestY + DestHeight - 1 do + begin + DestPix := @TFastARGB32Canvas(DestCanvas).FScanlines[Y, DestX]; + if Yp < 0 then + begin + T1 := 0; + FracY := 0; + InvFracY := $10000; + end + else + begin + T1 := Yp shr 16; + FracY := Yp and $FFFF; + InvFracY := (not Yp and $FFFF) + 1; + end; + + T2 := Iff(T1 < SrcHeight - 1, T1 + 1, T1); + SrcLine := @Scanlines[T1 + SrcY, SrcX]; + SrcLine2 := @Scanlines[T2 + SrcY, SrcX]; + Xp := (ScaleX shr 1) - $8000; + + for X := 0 to DestWidth - 1 do + begin + if Xp < 0 then + begin + T1 := 0; + FracX := 0; + end + else + begin + T1 := Xp shr 16; + FracX := Xp and $FFFF; + end; + + T2 := Iff(T1 < SrcWidth - 1, T1 + 1, T1); + Weight2:= Integer((Cardinal(InvFracY) * FracX) shr 16); // cast to Card, Int can overflow here + Weight1:= InvFracY - Weight2; + Weight4:= Integer((Cardinal(FracY) * FracX) shr 16); + Weight3:= FracY - Weight4; + + Accum.B := (SrcLine[T1].B * Weight1 + SrcLine[T2].B * Weight2 + + SrcLine2[T1].B * Weight3 + SrcLine2[T2].B * Weight4 + $8000) shr 16; + Accum.G := (SrcLine[T1].G * Weight1 + SrcLine[T2].G * Weight2 + + SrcLine2[T1].G * Weight3 + SrcLine2[T2].G * Weight4 + $8000) shr 16; + Accum.R := (SrcLine[T1].R * Weight1 + SrcLine[T2].R * Weight2 + + SrcLine2[T1].R * Weight3 + SrcLine2[T2].R * Weight4 + $8000) shr 16; + Accum.A := (SrcLine[T1].A * Weight1 + SrcLine[T2].A * Weight2 + + SrcLine2[T1].A * Weight3 + SrcLine2[T2].A * Weight4 + $8000) shr 16; + + AlphaBlendPixels(@Accum, DestPix); + + Inc(Xp, ScaleX); + Inc(DestPix); + end; + Inc(Yp, ScaleY); + end; + end; +end; + +procedure TFastARGB32Canvas.UpdateCanvasState; +var + I: LongInt; + ScanPos: PLongWord; +begin + inherited UpdateCanvasState; + + // Realloc and update scanline array + ReallocMem(FScanlines, FPData.Height * SizeOf(PColor32RecArray)); + ScanPos := FPData.Bits; + + for I := 0 to FPData.Height - 1 do + begin + FScanlines[I] := PColor32RecArray(ScanPos); + Inc(ScanPos, FPData.Width); + end; +end; + +class function TFastARGB32Canvas.GetSupportedFormats: TImageFormats; +begin + Result := [ifA8R8G8B8]; +end; + +procedure TFastARGB32Canvas.InvertColors; +var + X, Y: Integer; + PixPtr: PColor32Rec; +begin + for Y := FClipRect.Top to FClipRect.Bottom - 1 do + begin + PixPtr := @FScanlines[Y, FClipRect.Left]; + for X := FClipRect.Left to FClipRect.Right - 1 do + begin + PixPtr.R := not PixPtr.R; + PixPtr.G := not PixPtr.G; + PixPtr.B := not PixPtr.B; + Inc(PixPtr); + end; + end; +end; + +initialization + RegisterCanvas(TFastARGB32Canvas); + +finalization + FreeAndNil(CanvasClasses); + +{ + File Notes: + + -- TODOS ---------------------------------------------------- + - more more more ... + - implement pen width everywhere + - more objects (arc, polygon) + + -- 0.26.5 Changes/Bug Fixes --------------------------------- + - Fixed bug that could raise floating point error in DrawAlpha + and StretchDrawAlpha. + - Fixed bug in TImagingCanvas.Line that caused not drawing + of horz or vert lines. + + -- 0.26.3 Changes/Bug Fixes --------------------------------- + - Added some methods to TFastARGB32Canvas (InvertColors, DrawAlpha/StretchDrawAlpha) + - Fixed DrawAlpha/StretchDrawAlpha destination alpha calculation. + - Added PremultiplyAlpha and UnPremultiplyAlpha methods. + + -- 0.26.1 Changes/Bug Fixes --------------------------------- + - Added FillChannel methods. + - Added FloodFill method. + - Added GetHistogram method. + - Fixed "Invalid FP operation" in AdjustColorLevels in FPC compiled exes + (thanks to Carlos González). + - Added TImagingCanvas.AdjustColorLevels method. + + -- 0.25.0 Changes/Bug Fixes --------------------------------- + - Fixed error that could cause AV in linear and nonlinear filters. + - Added blended rect filling function FillRectBlend. + - Added drawing function with blending (DrawAlpha, StretchDrawAlpha, + StretchDrawAdd, DrawBlend, StretchDrawBlend, ...) + - Added non-linear filters (min, max, median). + - Added point transforms (invert, contrast, gamma, brightness). + + -- 0.21 Changes/Bug Fixes ----------------------------------- + - Added some new filter kernels for convolution. + - Added FillMode and PenMode properties. + - Added FrameRect, Rectangle, Ellipse, and Line methods. + - Removed HorzLine and VertLine from TFastARGB32Canvas - new versions + in general canvas is now as fast as those in TFastARGB32Canvas + (only in case of A8R8G8B8 images of course). + - Added PenWidth property, updated HorzLine and VertLine to use it. + + -- 0.19 Changes/Bug Fixes ----------------------------------- + - added TFastARGB32Canvas + - added convolutions, hline, vline + - unit created, intial stuff added + +} + +end. + diff --git a/resources/libraries/deskew/Imaging/ImagingClasses.pas b/resources/libraries/deskew/Imaging/ImagingClasses.pas new file mode 100755 index 0000000..d7bf33b --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingClasses.pas @@ -0,0 +1,1107 @@ +{ + Vampyre Imaging Library + by Marek Mauder + http://imaginglib.sourceforge.net + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +{ This unit contains class based wrapper to Imaging library.} +unit ImagingClasses; + +{$I ImagingOptions.inc} + +interface + +uses + Types, Classes, ImagingTypes, Imaging, ImagingFormats, ImagingUtility; + +type + { Base abstract high level class wrapper to low level Imaging structures and + functions.} + TBaseImage = class(TPersistent) + private + function GetEmpty: Boolean; + protected + FPData: PImageData; + FOnDataSizeChanged: TNotifyEvent; + FOnPixelsChanged: TNotifyEvent; + function GetFormat: TImageFormat; {$IFDEF USE_INLINE}inline;{$ENDIF} + function GetHeight: Integer; {$IFDEF USE_INLINE}inline;{$ENDIF} + function GetSize: Integer; {$IFDEF USE_INLINE}inline;{$ENDIF} + function GetWidth: Integer; {$IFDEF USE_INLINE}inline;{$ENDIF} + function GetBits: Pointer; {$IFDEF USE_INLINE}inline;{$ENDIF} + function GetPalette: PPalette32; {$IFDEF USE_INLINE}inline;{$ENDIF} + function GetPaletteEntries: Integer; {$IFDEF USE_INLINE}inline;{$ENDIF} + function GetScanline(Index: Integer): Pointer; + function GetPixelPointer(X, Y: Integer): Pointer; {$IFDEF USE_INLINE}inline;{$ENDIF} + function GetScanlineSize: Integer; {$IFDEF USE_INLINE}inline;{$ENDIF} + function GetFormatInfo: TImageFormatInfo; {$IFDEF USE_INLINE}inline;{$ENDIF} + function GetValid: Boolean; {$IFDEF USE_INLINE}inline;{$ENDIF} + function GetBoundsRect: TRect; + procedure SetFormat(const Value: TImageFormat); {$IFDEF USE_INLINE}inline;{$ENDIF} + procedure SetHeight(const Value: Integer); {$IFDEF USE_INLINE}inline;{$ENDIF} + procedure SetWidth(const Value: Integer); {$IFDEF USE_INLINE}inline;{$ENDIF} + procedure SetPointer; virtual; abstract; + procedure DoDataSizeChanged; virtual; + procedure DoPixelsChanged; virtual; + public + constructor Create; virtual; + constructor CreateFromImage(AImage: TBaseImage); + destructor Destroy; override; + { Returns info about current image.} + function ToString: string; {$IF (Defined(DCC) and (CompilerVersion >= 20.0)) or Defined(FPC)}override;{$IFEND} + + { Creates a new image data with the given size and format. Old image + data is lost. Works only for the current image of TMultiImage.} + procedure RecreateImageData(AWidth, AHeight: Integer; AFormat: TImageFormat); + { Maps underlying image data to given TImageData record. Both TBaseImage and + TImageData now share some image memory (bits). So don't call FreeImage + on TImageData afterwards since this TBaseImage would get really broken.} + procedure MapImageData(const ImageData: TImageData); + { Deletes current image.} + procedure Clear; + + { Resizes current image with optional resampling.} + procedure Resize(NewWidth, NewHeight: Integer; Filter: TResizeFilter); + + procedure ResizeToFit(FitWidth, FitHeight: Integer; Filter: TResizeFilter; DstImage: TBaseImage); + { Flips current image. Reverses the image along its horizontal axis the top + becomes the bottom and vice versa.} + procedure Flip; + { Mirrors current image. Reverses the image along its vertical axis the left + side becomes the right and vice versa.} + procedure Mirror; + { Rotates image by Angle degrees counterclockwise.} + procedure Rotate(Angle: Single); + { Copies rectangular part of SrcImage to DstImage. No blending is performed - + alpha is simply copied to destination image. Operates also with + negative X and Y coordinates. + Note that copying is fastest for images in the same data format + (and slowest for images in special formats).} + procedure CopyTo(SrcX, SrcY, Width, Height: Integer; DstImage: TBaseImage; DstX, DstY: Integer); overload; + { Copies whole image to DstImage. No blending is performed - + alpha is simply copied to destination image. Operates also with + negative X and Y coordinates. + Note that copying is fastest for images in the same data format + (and slowest for images in special formats).} + procedure CopyTo(DstImage: TBaseImage; DstX, DstY: Integer); overload; + { Stretches the contents of the source rectangle to the destination rectangle + with optional resampling. No blending is performed - alpha is + simply copied/resampled to destination image. Note that stretching is + fastest for images in the same data format (and slowest for + images in special formats).} + procedure StretchTo(SrcX, SrcY, SrcWidth, SrcHeight: Integer; DstImage: TBaseImage; DstX, DstY, DstWidth, DstHeight: Integer; Filter: TResizeFilter); + { Replaces pixels with OldPixel in the given rectangle by NewPixel. + OldPixel and NewPixel should point to the pixels in the same format + as the given image is in.} + procedure ReplaceColor(X, Y, Width, Height: Integer; OldColor, NewColor: Pointer); + { Swaps SrcChannel and DstChannel color or alpha channels of image. + Use ChannelRed, ChannelBlue, ChannelGreen, ChannelAlpha constants to + identify channels.} + procedure SwapChannels(SrcChannel, DstChannel: Integer); + + { Loads current image data from file.} + procedure LoadFromFile(const FileName: string); virtual; + { Loads current image data from stream.} + procedure LoadFromStream(Stream: TStream); virtual; + + { Saves current image data to file.} + procedure SaveToFile(const FileName: string); + { Saves current image data to stream. Ext identifies desired image file + format (jpg, png, dds, ...)} + procedure SaveToStream(const Ext: string; Stream: TStream); + + { Width of current image in pixels.} + property Width: Integer read GetWidth write SetWidth; + { Height of current image in pixels.} + property Height: Integer read GetHeight write SetHeight; + { Image data format of current image.} + property Format: TImageFormat read GetFormat write SetFormat; + { Size in bytes of current image's data.} + property Size: Integer read GetSize; + { Pointer to memory containing image bits.} + property Bits: Pointer read GetBits; + { Pointer to palette for indexed format images. It is nil for others. + Max palette entry is at index [PaletteEntries - 1].} + property Palette: PPalette32 read GetPalette; + { Number of entries in image's palette} + property PaletteEntries: Integer read GetPaletteEntries; + { Provides indexed access to each line of pixels. Does not work with special + format images (like DXT).} + property Scanline[Index: Integer]: Pointer read GetScanline; + { Returns pointer to image pixel at [X, Y] coordinates.} + property PixelPointer[X, Y: Integer]: Pointer read GetPixelPointer; + { Size/length of one image scanline in bytes.} + property ScanlineSize: Integer read GetScanlineSize; + { Extended image format information.} + property FormatInfo: TImageFormatInfo read GetFormatInfo; + { This gives complete access to underlying TImageData record. + It can be used in functions that take TImageData as parameter + (for example: ReduceColors(SingleImageInstance.ImageData^, 64)).} + property ImageDataPointer: PImageData read FPData; + { Indicates whether the current image is valid (proper format, + allowed dimensions, right size, ...).} + property Valid: Boolean read GetValid; + { Indicates whether image containst any data (size in bytes > 0).} + property Empty: Boolean read GetEmpty; + { Specifies the bounding rectangle of the image.} + property BoundsRect: TRect read GetBoundsRect; + { This event occurs when the image data size has just changed. That means + image width, height, or format has been changed.} + property OnDataSizeChanged: TNotifyEvent read FOnDataSizeChanged write FOnDataSizeChanged; + { This event occurs when some pixels of the image have just changed.} + property OnPixelsChanged: TNotifyEvent read FOnPixelsChanged write FOnPixelsChanged; + end; + + { Extension of TBaseImage which uses single TImageData record to + store image. All methods inherited from TBaseImage work with this record.} + TSingleImage = class(TBaseImage) + protected + FImageData: TImageData; + procedure SetPointer; override; + public + constructor Create; override; + constructor CreateFromParams(AWidth, AHeight: Integer; AFormat: TImageFormat = ifDefault); + constructor CreateFromData(const AData: TImageData); + constructor CreateFromFile(const FileName: string); + constructor CreateFromStream(Stream: TStream); + destructor Destroy; override; + { Assigns single image from another single image or multi image.} + procedure Assign(Source: TPersistent); override; + { Assigns single image from image data record.} + procedure AssignFromImageData(const AImageData: TImageData); + end; + + { Extension of TBaseImage which uses array of TImageData records to + store multiple images. Images are independent on each other and they don't + share any common characteristic. Each can have different size, format, and + palette. All methods inherited from TBaseImage work only with + active image (it could represent mipmap level, animation frame, or whatever). + Methods whose names contain word 'Multi' work with all images in array + (as well as other methods with obvious names).} + TMultiImage = class(TBaseImage) + protected + FDataArray: TDynImageDataArray; + FActiveImage: Integer; + procedure SetActiveImage(Value: Integer); {$IFDEF USE_INLINE}inline;{$ENDIF} + function GetImageCount: Integer; {$IFDEF USE_INLINE}inline;{$ENDIF} + procedure SetImageCount(Value: Integer); + function GetAllImagesValid: Boolean; {$IFDEF USE_INLINE}inline;{$ENDIF} + function GetImage(Index: Integer): TImageData; {$IFDEF USE_INLINE}inline;{$ENDIF} + procedure SetImage(Index: Integer; Value: TImageData); {$IFDEF USE_INLINE}inline;{$ENDIF} + procedure SetPointer; override; + function PrepareInsert(Index, Count: Integer): Boolean; + procedure DoInsertImages(Index: Integer; const Images: TDynImageDataArray); + procedure DoInsertNew(Index: Integer; AWidth, AHeight: Integer; AFormat: TImageFormat); + public + constructor Create; override; + constructor CreateFromParams(AWidth, AHeight: Integer; AFormat: TImageFormat; ImageCount: Integer); + constructor CreateFromArray(const ADataArray: TDynImageDataArray); + constructor CreateFromFile(const FileName: string); + constructor CreateFromStream(Stream: TStream); + destructor Destroy; override; + { Assigns multi image from another multi image or single image.} + procedure Assign(Source: TPersistent); override; + { Assigns multi image from array of image data records.} + procedure AssignFromArray(const ADataArray: TDynImageDataArray); + + { Adds new image at the end of the image array. } + function AddImage(AWidth, AHeight: Integer; AFormat: TImageFormat = ifDefault): Integer; overload; + { Adds existing image at the end of the image array. } + function AddImage(const Image: TImageData): Integer; overload; + { Adds existing image (Active image of a TmultiImage) + at the end of the image array. } + function AddImage(Image: TBaseImage): Integer; overload; + { Adds existing image array ((all images of a multi image)) + at the end of the image array. } + procedure AddImages(const Images: TDynImageDataArray); overload; + { Adds existing MultiImage images at the end of the image array. } + procedure AddImages(Images: TMultiImage); overload; + + { Inserts new image image at the given position in the image array. } + procedure InsertImage(Index, AWidth, AHeight: Integer; AFormat: TImageFormat = ifDefault); overload; + { Inserts existing image at the given position in the image array. } + procedure InsertImage(Index: Integer; const Image: TImageData); overload; + { Inserts existing image (Active image of a TmultiImage) + at the given position in the image array. } + procedure InsertImage(Index: Integer; Image: TBaseImage); overload; + { Inserts existing image at the given position in the image array. } + procedure InsertImages(Index: Integer; const Images: TDynImageDataArray); overload; + { Inserts existing images (all images of a TmultiImage) at + the given position in the image array. } + procedure InsertImages(Index: Integer; Images: TMultiImage); overload; + + { Exchanges two images at the given positions in the image array. } + procedure ExchangeImages(Index1, Index2: Integer); + { Deletes image at the given position in the image array.} + procedure DeleteImage(Index: Integer); + { Rearranges images so that the first image will become last and vice versa.} + procedure ReverseImages; + { Deletes all images.} + procedure ClearAll; + + { Converts all images to another image data format.} + procedure ConvertImages(Format: TImageFormat); + { Resizes all images.} + procedure ResizeImages(NewWidth, NewHeight: Integer; Filter: TResizeFilter); + + { Overloaded loading method that will add new image to multiimage if + image array is empty bero loading. } + procedure LoadFromFile(const FileName: string); override; + { Overloaded loading method that will add new image to multiimage if + image array is empty bero loading. } + procedure LoadFromStream(Stream: TStream); override; + + { Loads whole multi image from file.} + procedure LoadMultiFromFile(const FileName: string); + { Loads whole multi image from stream.} + procedure LoadMultiFromStream(Stream: TStream); + { Saves whole multi image to file.} + procedure SaveMultiToFile(const FileName: string); + { Saves whole multi image to stream. Ext identifies desired + image file format (jpg, png, dds, ...).} + procedure SaveMultiToStream(const Ext: string; Stream: TStream); + + { Indicates active image of this multi image. All methods inherited + from TBaseImage operate on this image only.} + property ActiveImage: Integer read FActiveImage write SetActiveImage; + { Number of images of this multi image.} + property ImageCount: Integer read GetImageCount write SetImageCount; + { This value is True if all images of this TMultiImage are valid.} + property AllImagesValid: Boolean read GetAllImagesValid; + { This gives complete access to underlying TDynImageDataArray. + It can be used in functions that take TDynImageDataArray + as parameter.} + property DataArray: TDynImageDataArray read FDataArray; + { Array property for accessing individual images of TMultiImage. When you + set image at given index the old image is freed and the source is cloned.} + property Images[Index: Integer]: TImageData read GetImage write SetImage; default; + end; + +implementation + +const + DefaultWidth = 16; + Defaultheight = 16; + +function GetArrayFromImageData(const ImageData: TImageData): TDynImageDataArray; +begin + SetLength(Result, 1); + Result[0] := ImageData; +end; + +{ TBaseImage class implementation } + +constructor TBaseImage.Create; +begin + SetPointer; +end; + +constructor TBaseImage.CreateFromImage(AImage: TBaseImage); +begin + Create; + Assign(AImage); +end; + +destructor TBaseImage.Destroy; +begin + inherited Destroy; +end; + +function TBaseImage.GetWidth: Integer; +begin + if Valid then + Result := FPData.Width + else + Result := 0; +end; + +function TBaseImage.GetHeight: Integer; +begin + if Valid then + Result := FPData.Height + else + Result := 0; +end; + +function TBaseImage.GetFormat: TImageFormat; +begin + if Valid then + Result := FPData.Format + else + Result := ifUnknown; +end; + +function TBaseImage.GetScanline(Index: Integer): Pointer; +var + Info: TImageFormatInfo; +begin + if Valid then + begin + Info := GetFormatInfo; + if not Info.IsSpecial then + Result := ImagingFormats.GetScanLine(FPData.Bits, Info, FPData.Width, Index) + else + Result := FPData.Bits; + end + else + Result := nil; +end; + +function TBaseImage.GetScanlineSize: Integer; +begin + if Valid then + Result := FormatInfo.GetPixelsSize(Format, Width, 1) + else + Result := 0; +end; + +function TBaseImage.GetPixelPointer(X, Y: Integer): Pointer; +begin + if Valid then + Result := @PByteArray(FPData.Bits)[(Y * FPData.Width + X) * GetFormatInfo.BytesPerPixel] + else + Result := nil; +end; + +function TBaseImage.GetSize: Integer; +begin + if Valid then + Result := FPData.Size + else + Result := 0; +end; + +function TBaseImage.GetBits: Pointer; +begin + if Valid then + Result := FPData.Bits + else + Result := nil; +end; + +function TBaseImage.GetPalette: PPalette32; +begin + if Valid then + Result := FPData.Palette + else + Result := nil; +end; + +function TBaseImage.GetPaletteEntries: Integer; +begin + Result := GetFormatInfo.PaletteEntries; +end; + +function TBaseImage.GetFormatInfo: TImageFormatInfo; +begin + if Valid then + Imaging.GetImageFormatInfo(FPData.Format, Result) + else + FillChar(Result, SizeOf(Result), 0); +end; + +function TBaseImage.GetValid: Boolean; +begin + Result := Assigned(FPData) and Imaging.TestImage(FPData^); +end; + +function TBaseImage.GetBoundsRect: TRect; +begin + Result := Rect(0, 0, GetWidth, GetHeight); +end; + +function TBaseImage.GetEmpty: Boolean; +begin + Result := FPData.Size = 0; +end; + +procedure TBaseImage.SetWidth(const Value: Integer); +begin + Resize(Value, GetHeight, rfNearest); +end; + +procedure TBaseImage.SetHeight(const Value: Integer); +begin + Resize(GetWidth, Value, rfNearest); +end; + +procedure TBaseImage.SetFormat(const Value: TImageFormat); +begin + if Valid and Imaging.ConvertImage(FPData^, Value) then + DoDataSizeChanged; +end; + +procedure TBaseImage.DoDataSizeChanged; +begin + if Assigned(FOnDataSizeChanged) then + FOnDataSizeChanged(Self); + DoPixelsChanged; +end; + +procedure TBaseImage.DoPixelsChanged; +begin + if Assigned(FOnPixelsChanged) then + FOnPixelsChanged(Self); +end; + +procedure TBaseImage.RecreateImageData(AWidth, AHeight: Integer; AFormat: TImageFormat); +begin + if Assigned(FPData) and Imaging.NewImage(AWidth, AHeight, AFormat, FPData^) then + DoDataSizeChanged; +end; + +procedure TBaseImage.MapImageData(const ImageData: TImageData); +begin + Clear; + FPData.Width := ImageData.Width; + FPData.Height := ImageData.Height; + FPData.Format := ImageData.Format; + FPData.Size := ImageData.Size; + FPData.Bits := ImageData.Bits; + FPData.Palette := ImageData.Palette; +end; + +procedure TBaseImage.Clear; +begin + FreeImage(FPData^); +end; + +procedure TBaseImage.Resize(NewWidth, NewHeight: Integer; Filter: TResizeFilter); +begin + if Valid and Imaging.ResizeImage(FPData^, NewWidth, NewHeight, Filter) then + DoDataSizeChanged; +end; + +procedure TBaseImage.ResizeToFit(FitWidth, FitHeight: Integer; + Filter: TResizeFilter; DstImage: TBaseImage); +begin + if Valid and Assigned(DstImage) then + begin + Imaging.ResizeImageToFit(FPData^, FitWidth, FitHeight, Filter, + DstImage.FPData^); + DstImage.DoDataSizeChanged; + end; +end; + +procedure TBaseImage.Flip; +begin + if Valid and Imaging.FlipImage(FPData^) then + DoPixelsChanged; +end; + +procedure TBaseImage.Mirror; +begin + if Valid and Imaging.MirrorImage(FPData^) then + DoPixelsChanged; +end; + +procedure TBaseImage.Rotate(Angle: Single); +begin + if Valid then + begin + Imaging.RotateImage(FPData^, Angle); + DoPixelsChanged; + end; +end; + +procedure TBaseImage.CopyTo(SrcX, SrcY, Width, Height: Integer; + DstImage: TBaseImage; DstX, DstY: Integer); +begin + if Valid and Assigned(DstImage) and DstImage.Valid then + begin + Imaging.CopyRect(FPData^, SrcX, SrcY, Width, Height, DstImage.FPData^, DstX, DstY); + DstImage.DoPixelsChanged; + end; +end; + +procedure TBaseImage.CopyTo(DstImage: TBaseImage; DstX, DstY: Integer); +begin + if Valid and Assigned(DstImage) and DstImage.Valid then + begin + Imaging.CopyRect(FPData^, 0, 0, Width, Height, DstImage.FPData^, DstX, DstY); + DstImage.DoPixelsChanged; + end; +end; + +procedure TBaseImage.StretchTo(SrcX, SrcY, SrcWidth, SrcHeight: Integer; + DstImage: TBaseImage; DstX, DstY, DstWidth, DstHeight: Integer; Filter: TResizeFilter); +begin + if Valid and Assigned(DstImage) and DstImage.Valid then + begin + Imaging.StretchRect(FPData^, SrcX, SrcY, SrcWidth, SrcHeight, + DstImage.FPData^, DstX, DstY, DstWidth, DstHeight, Filter); + DstImage.DoPixelsChanged; + end; +end; + +procedure TBaseImage.ReplaceColor(X, Y, Width, Height: Integer; OldColor, + NewColor: Pointer); +begin + if Valid then + begin + Imaging.ReplaceColor(FPData^, X, Y, Width, Height, OldColor, NewColor); + DoPixelsChanged; + end; +end; + +procedure TBaseImage.SwapChannels(SrcChannel, DstChannel: Integer); +begin + if Valid then + begin + Imaging.SwapChannels(FPData^, SrcChannel, DstChannel); + DoPixelsChanged; + end; +end; + +function TBaseImage.ToString: string; +begin + Result := Iff(Valid, Imaging.ImageToStr(FPData^), 'empty image'); +end; + +procedure TBaseImage.LoadFromFile(const FileName: string); +begin + if Assigned(FPData) and Imaging.LoadImageFromFile(FileName, FPData^) then + DoDataSizeChanged; +end; + +procedure TBaseImage.LoadFromStream(Stream: TStream); +begin + if Assigned(FPData) and Imaging.LoadImageFromStream(Stream, FPData^) then + DoDataSizeChanged; +end; + +procedure TBaseImage.SaveToFile(const FileName: string); +begin + if Valid then + Imaging.SaveImageToFile(FileName, FPData^); +end; + +procedure TBaseImage.SaveToStream(const Ext: string; Stream: TStream); +begin + if Valid then + Imaging.SaveImageToStream(Ext, Stream, FPData^); +end; + + +{ TSingleImage class implementation } + +constructor TSingleImage.Create; +begin + inherited Create; + Clear; +end; + +constructor TSingleImage.CreateFromParams(AWidth, AHeight: Integer; AFormat: TImageFormat); +begin + inherited Create; + RecreateImageData(AWidth, AHeight, AFormat); +end; + +constructor TSingleImage.CreateFromData(const AData: TImageData); +begin + inherited Create; + AssignFromImageData(AData); +end; + +constructor TSingleImage.CreateFromFile(const FileName: string); +begin + inherited Create; + LoadFromFile(FileName); +end; + +constructor TSingleImage.CreateFromStream(Stream: TStream); +begin + inherited Create; + LoadFromStream(Stream); +end; + +destructor TSingleImage.Destroy; +begin + Imaging.FreeImage(FImageData); + inherited Destroy; +end; + +procedure TSingleImage.SetPointer; +begin + FPData := @FImageData; +end; + +procedure TSingleImage.Assign(Source: TPersistent); +begin + if Source = nil then + begin + Clear; + end + else if Source is TSingleImage then + begin + AssignFromImageData(TSingleImage(Source).FImageData); + end + else if Source is TMultiImage then + begin + if TMultiImage(Source).Valid then + AssignFromImageData(TMultiImage(Source).FPData^) + else + Clear; + end + else + inherited Assign(Source); +end; + +procedure TSingleImage.AssignFromImageData(const AImageData: TImageData); +begin + if Imaging.TestImage(AImageData) then + begin + Imaging.CloneImage(AImageData, FImageData); + DoDataSizeChanged; + end + else + Clear; +end; + +{ TMultiImage class implementation } + +constructor TMultiImage.Create; +begin + inherited Create; +end; + +constructor TMultiImage.CreateFromParams(AWidth, AHeight: Integer; + AFormat: TImageFormat; ImageCount: Integer); +var + I: Integer; +begin + Imaging.FreeImagesInArray(FDataArray); + SetLength(FDataArray, ImageCount); + for I := 0 to GetImageCount - 1 do + Imaging.NewImage(AWidth, AHeight, AFormat, FDataArray[I]); + if GetImageCount > 0 then + SetActiveImage(0); +end; + +constructor TMultiImage.CreateFromArray(const ADataArray: TDynImageDataArray); +begin + AssignFromArray(ADataArray); +end; + +constructor TMultiImage.CreateFromFile(const FileName: string); +begin + LoadMultiFromFile(FileName); +end; + +constructor TMultiImage.CreateFromStream(Stream: TStream); +begin + LoadMultiFromStream(Stream); +end; + +destructor TMultiImage.Destroy; +begin + Imaging.FreeImagesInArray(FDataArray); + inherited Destroy; +end; + +procedure TMultiImage.SetActiveImage(Value: Integer); +begin + FActiveImage := Value; + SetPointer; +end; + +function TMultiImage.GetImageCount: Integer; +begin + Result := Length(FDataArray); +end; + +procedure TMultiImage.SetImageCount(Value: Integer); +var + I, OldCount: Integer; +begin + if Value > GetImageCount then + begin + // Create new empty images if array will be enlarged + OldCount := GetImageCount; + SetLength(FDataArray, Value); + for I := OldCount to Value - 1 do + Imaging.NewImage(DefaultWidth, DefaultHeight, ifDefault, FDataArray[I]); + end + else + begin + // Free images that exceed desired count and shrink array + for I := Value to GetImageCount - 1 do + Imaging.FreeImage(FDataArray[I]); + SetLength(FDataArray, Value); + end; + SetPointer; +end; + +function TMultiImage.GetAllImagesValid: Boolean; +begin + Result := (GetImageCount > 0) and TestImagesInArray(FDataArray); +end; + +function TMultiImage.GetImage(Index: Integer): TImageData; +begin + if (Index >= 0) and (Index < GetImageCount) then + Result := FDataArray[Index]; +end; + +procedure TMultiImage.SetImage(Index: Integer; Value: TImageData); +begin + if (Index >= 0) and (Index < GetImageCount) then + Imaging.CloneImage(Value, FDataArray[Index]); +end; + +procedure TMultiImage.SetPointer; +begin + if GetImageCount > 0 then + begin + FActiveImage := ClampInt(FActiveImage, 0, GetImageCount - 1); + FPData := @FDataArray[FActiveImage]; + end + else + begin + FActiveImage := -1; + FPData := nil + end; +end; + +function TMultiImage.PrepareInsert(Index, Count: Integer): Boolean; +var + I: Integer; +begin + // Inserting to empty image will add image at index 0 + if GetImageCount = 0 then + Index := 0; + + if (Index >= 0) and (Index <= GetImageCount) and (Count > 0) then + begin + SetLength(FDataArray, GetImageCount + Count); + if Index < GetImageCount - 1 then + begin + // Move imges to new position + System.Move(FDataArray[Index], FDataArray[Index + Count], + (GetImageCount - Count - Index) * SizeOf(TImageData)); + // Null old images, not free them! + for I := Index to Index + Count - 1 do + InitImage(FDataArray[I]); + end; + Result := True; + end + else + Result := False; +end; + +procedure TMultiImage.DoInsertImages(Index: Integer; const Images: TDynImageDataArray); +var + I, Len: Integer; +begin + Len := Length(Images); + if PrepareInsert(Index, Len) then + begin + for I := 0 to Len - 1 do + Imaging.CloneImage(Images[I], FDataArray[Index + I]); + end; +end; + +procedure TMultiImage.DoInsertNew(Index, AWidth, AHeight: Integer; + AFormat: TImageFormat); +begin + if PrepareInsert(Index, 1) then + Imaging.NewImage(AWidth, AHeight, AFormat, FDataArray[Index]); +end; + +procedure TMultiImage.Assign(Source: TPersistent); +var + Arr: TDynImageDataArray; +begin + if Source = nil then + begin + ClearAll; + end + else if Source is TMultiImage then + begin + AssignFromArray(TMultiImage(Source).FDataArray); + SetActiveImage(TMultiImage(Source).ActiveImage); + end + else if Source is TSingleImage then + begin + SetLength(Arr, 1); + Arr[0] := TSingleImage(Source).FImageData; + AssignFromArray(Arr); + end + else + inherited Assign(Source); +end; + +procedure TMultiImage.AssignFromArray(const ADataArray: TDynImageDataArray); +var + I: Integer; +begin + Imaging.FreeImagesInArray(FDataArray); + SetLength(FDataArray, Length(ADataArray)); + for I := 0 to GetImageCount - 1 do + begin + // Clone only valid images + if Imaging.TestImage(ADataArray[I]) then + Imaging.CloneImage(ADataArray[I], FDataArray[I]) + else + Imaging.NewImage(DefaultWidth, DefaultHeight, ifDefault, FDataArray[I]); + end; + if GetImageCount > 0 then + SetActiveImage(0); +end; + +function TMultiImage.AddImage(AWidth, AHeight: Integer; AFormat: TImageFormat): Integer; +begin + Result := GetImageCount; + DoInsertNew(Result, AWidth, AHeight, AFormat); +end; + +function TMultiImage.AddImage(const Image: TImageData): Integer; +begin + Result := GetImageCount; + DoInsertImages(Result, GetArrayFromImageData(Image)); +end; + +function TMultiImage.AddImage(Image: TBaseImage): Integer; +begin + if Assigned(Image) and Image.Valid then + begin + Result := GetImageCount; + DoInsertImages(Result, GetArrayFromImageData(Image.FPData^)); + end + else + Result := -1; +end; + +procedure TMultiImage.AddImages(const Images: TDynImageDataArray); +begin + DoInsertImages(GetImageCount, Images); +end; + +procedure TMultiImage.AddImages(Images: TMultiImage); +begin + DoInsertImages(GetImageCount, Images.FDataArray); +end; + +procedure TMultiImage.InsertImage(Index, AWidth, AHeight: Integer; + AFormat: TImageFormat); +begin + DoInsertNew(Index, AWidth, AHeight, AFormat); +end; + +procedure TMultiImage.InsertImage(Index: Integer; const Image: TImageData); +begin + DoInsertImages(Index, GetArrayFromImageData(Image)); +end; + +procedure TMultiImage.InsertImage(Index: Integer; Image: TBaseImage); +begin + if Assigned(Image) and Image.Valid then + DoInsertImages(Index, GetArrayFromImageData(Image.FPData^)); +end; + +procedure TMultiImage.InsertImages(Index: Integer; + const Images: TDynImageDataArray); +begin + DoInsertImages(Index, FDataArray); +end; + +procedure TMultiImage.InsertImages(Index: Integer; Images: TMultiImage); +begin + DoInsertImages(Index, Images.FDataArray); +end; + +procedure TMultiImage.ExchangeImages(Index1, Index2: Integer); +var + TempData: TImageData; +begin + if (Index1 >= 0) and (Index1 < GetImageCount) and + (Index2 >= 0) and (Index2 < GetImageCount) then + begin + TempData := FDataArray[Index1]; + FDataArray[Index1] := FDataArray[Index2]; + FDataArray[Index2] := TempData; + end; +end; + +procedure TMultiImage.DeleteImage(Index: Integer); +var + I: Integer; +begin + if (Index >= 0) and (Index < GetImageCount) then + begin + // Free image at index to be deleted + Imaging.FreeImage(FDataArray[Index]); + if Index < GetImageCount - 1 then + begin + // Move images to new indices if necessary + for I := Index to GetImageCount - 2 do + FDataArray[I] := FDataArray[I + 1]; + end; + // Set new array length and update pointer to active image + SetLength(FDataArray, GetImageCount - 1); + SetPointer; + end; +end; + +procedure TMultiImage.ClearAll; +begin + ImageCount := 0; +end; + +procedure TMultiImage.ConvertImages(Format: TImageFormat); +var + I: Integer; +begin + for I := 0 to GetImageCount - 1 do + Imaging.ConvertImage(FDataArray[I], Format); +end; + +procedure TMultiImage.ResizeImages(NewWidth, NewHeight: Integer; + Filter: TResizeFilter); +var + I: Integer; +begin + for I := 0 to GetImageCount - 1 do + Imaging.ResizeImage(FDataArray[I], NewWidth, NewHeight, Filter); +end; + +procedure TMultiImage.ReverseImages; +var + I: Integer; +begin + for I := 0 to GetImageCount div 2 do + ExchangeImages(I, GetImageCount - 1 - I); +end; + +procedure TMultiImage.LoadFromFile(const FileName: string); +begin + if GetImageCount = 0 then + ImageCount := 1; + inherited LoadFromFile(FileName); +end; + +procedure TMultiImage.LoadFromStream(Stream: TStream); +begin + if GetImageCount = 0 then + ImageCount := 1; + inherited LoadFromStream(Stream); +end; + +procedure TMultiImage.LoadMultiFromFile(const FileName: string); +begin + Imaging.LoadMultiImageFromFile(FileName, FDataArray); + SetActiveImage(0); +end; + +procedure TMultiImage.LoadMultiFromStream(Stream: TStream); +begin + Imaging.LoadMultiImageFromStream(Stream, FDataArray); + SetActiveImage(0); +end; + +procedure TMultiImage.SaveMultiToFile(const FileName: string); +begin + Imaging.SaveMultiImageToFile(FileName, FDataArray); +end; + +procedure TMultiImage.SaveMultiToStream(const Ext: string; Stream: TStream); +begin + Imaging.SaveMultiImageToStream(Ext, Stream, FDataArray); +end; + +{ + File Notes: + + -- TODOS ---------------------------------------------------- + - nothing now + + -- 0.77.1 --------------------------------------------------- + - Added TSingleImage.AssignFromData and TMultiImage.AssigntFromArray + as a replacement for constructors used as methods (that is + compiler error in Delphi XE3). + - Added TBaseImage.ResizeToFit method. + - Changed TMultiImage to have default state with no images. + - TMultiImage.AddImage now returns index of newly added image. + - Fixed img index bug in TMultiImage.ResizeImages + + -- 0.26.5 Changes/Bug Fixes --------------------------------- + - Added MapImageData method to TBaseImage + - Added Empty property to TBaseImage. + - Added Clear method to TBaseImage. + - Added ScanlineSize property to TBaseImage. + + -- 0.24.3 Changes/Bug Fixes --------------------------------- + - Added TMultiImage.ReverseImages method. + + -- 0.23 Changes/Bug Fixes ----------------------------------- + - Added SwapChannels method to TBaseImage. + - Added ReplaceColor method to TBaseImage. + - Added ToString method to TBaseImage. + + -- 0.21 Changes/Bug Fixes ----------------------------------- + - Inserting images to empty MultiImage will act as Add method. + - MultiImages with empty arrays will now create one image when + LoadFromFile or LoadFromStream is called. + - Fixed bug that caused AVs when getting props like Width, Height, asn Size + and when inlining was off. There was call to Iff but with inlining disabled + params like FPData.Size were evaluated and when FPData was nil => AV. + - Added many FPData validity checks to many methods. There were AVs + when calling most methods on empty TMultiImage. + - Added AllImagesValid property to TMultiImage. + - Fixed memory leak in TMultiImage.CreateFromParams. + + -- 0.19 Changes/Bug Fixes ----------------------------------- + - added ResizeImages method to TMultiImage + - removed Ext parameter from various LoadFromStream methods, no + longer needed + - fixed various issues concerning ActiveImage of TMultiImage + (it pointed to invalid location after some operations) + - most of property set/get methods are now inline + - added PixelPointers property to TBaseImage + - added Images default array property to TMultiImage + - renamed methods in TMultiImage to contain 'Image' instead of 'Level' + - added canvas support + - added OnDataSizeChanged and OnPixelsChanged event to TBaseImage + - renamed TSingleImage.NewImage to RecreateImageData, made public, and + moved to TBaseImage + + -- 0.17 Changes/Bug Fixes ----------------------------------- + - added props PaletteEntries and ScanLine to TBaseImage + - aded new constructor to TBaseImage that take TBaseImage source + - TMultiImage levels adding and inserting rewritten internally + - added some new functions to TMultiImage: AddLevels, InsertLevels + - added some new functions to TBaseImage: Flip, Mirror, Rotate, + CopyRect, StretchRect + - TBasicImage.Resize has now filter parameter + - new stuff added to TMultiImage (DataArray prop, ConvertLevels) + + -- 0.13 Changes/Bug Fixes ----------------------------------- + - added AddLevel, InsertLevel, ExchangeLevels and DeleteLevel + methods to TMultiImage + - added TBaseImage, TSingleImage and TMultiImage with initial + members +} + +end. + diff --git a/resources/libraries/deskew/Imaging/ImagingColors.pas b/resources/libraries/deskew/Imaging/ImagingColors.pas new file mode 100755 index 0000000..c7fc811 --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingColors.pas @@ -0,0 +1,246 @@ +{ + Vampyre Imaging Library + by Marek Mauder + http://imaginglib.sourceforge.net + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +{ This unit contains functions for manipulating and converting color values.} +unit ImagingColors; + +interface + +{$I ImagingOptions.inc} + +uses + SysUtils, ImagingTypes, ImagingUtility; + +{ Converts RGB color to YUV.} +procedure RGBToYUV(R, G, B: Byte; var Y, U, V: Byte); +{ Converts YIV to RGB color.} +procedure YUVToRGB(Y, U, V: Byte; var R, G, B: Byte); + +{ Converts RGB color to YCbCr as used in JPEG.} +procedure RGBToYCbCr(R, G, B: Byte; var Y, Cb, Cr: Byte); +{ Converts YCbCr as used in JPEG to RGB color.} +procedure YCbCrToRGB(Y, Cb, Cr: Byte; var R, G, B: Byte); +{ Converts RGB color to YCbCr as used in JPEG.} +procedure RGBToYCbCr16(R, G, B: Word; var Y, Cb, Cr: Word); +{ Converts YCbCr as used in JPEG to RGB color.} +procedure YCbCrToRGB16(Y, Cb, Cr: Word; var R, G, B: Word); + +{ Converts RGB color to CMY.} +procedure RGBToCMY(R, G, B: Byte; var C, M, Y: Byte); +{ Converts CMY to RGB color.} +procedure CMYToRGB(C, M, Y: Byte; var R, G, B: Byte); +{ Converts RGB color to CMY.} +procedure RGBToCMY16(R, G, B: Word; var C, M, Y: Word); +{ Converts CMY to RGB color.} +procedure CMYToRGB16(C, M, Y: Word; var R, G, B: Word); + +{ Converts RGB color to CMYK.} +procedure RGBToCMYK(R, G, B: Byte; var C, M, Y, K: Byte); +{ Converts CMYK to RGB color.} +procedure CMYKToRGB(C, M, Y, K: Byte; var R, G, B: Byte); +{ Converts RGB color to CMYK.} +procedure RGBToCMYK16(R, G, B: Word; var C, M, Y, K: Word); +{ Converts CMYK to RGB color.} +procedure CMYKToRGB16(C, M, Y, K: Word; var R, G, B: Word); + +{ Converts RGB color to YCoCg.} +procedure RGBToYCoCg(R, G, B: Byte; var Y, Co, Cg: Byte); +{ Converts YCoCg to RGB color.} +procedure YCoCgToRGB(Y, Co, Cg: Byte; var R, G, B: Byte); + +//procedure RGBToHSL(R, G, B: Byte; var H, S, L: Byte); +//procedure HSLToRGB(H, S, L: Byte; var R, G, B: Byte); + +implementation + +procedure RGBToYUV(R, G, B: Byte; var Y, U, V: Byte); +begin + Y := ClampToByte(Round( 0.257 * R + 0.504 * G + 0.098 * B) + 16); + V := ClampToByte(Round( 0.439 * R - 0.368 * G - 0.071 * B) + 128); + U := ClampToByte(Round(-0.148 * R - 0.291 * G + 0.439 * B) + 128); +end; + +procedure YUVToRGB(Y, U, V: Byte; var R, G, B: Byte); +var + CY, CU, CV: LongInt; +begin + CY := Y - 16; + CU := U - 128; + CV := V - 128; + R := ClampToByte(Round(1.164 * CY - 0.002 * CU + 1.596 * CV)); + G := ClampToByte(Round(1.164 * CY - 0.391 * CU - 0.813 * CV)); + B := ClampToByte(Round(1.164 * CY + 2.018 * CU - 0.001 * CV)); +end; + +procedure RGBToYCbCr(R, G, B: Byte; var Y, Cb, Cr: Byte); +begin + Y := ClampToByte(Round( 0.29900 * R + 0.58700 * G + 0.11400 * B)); + Cb := ClampToByte(Round(-0.16874 * R - 0.33126 * G + 0.50000 * B + 128)); + Cr := ClampToByte(Round( 0.50000 * R - 0.41869 * G - 0.08131 * B + 128)); +end; + +procedure YCbCrToRGB(Y, Cb, Cr: Byte; var R, G, B: Byte); +begin + R := ClampToByte(Round(Y + 1.40200 * (Cr - 128))); + G := ClampToByte(Round(Y - 0.34414 * (Cb - 128) - 0.71414 * (Cr - 128))); + B := ClampToByte(Round(Y + 1.77200 * (Cb - 128))); +end; + +procedure RGBToYCbCr16(R, G, B: Word; var Y, Cb, Cr: Word); +begin + Y := ClampToWord(Round( 0.29900 * R + 0.58700 * G + 0.11400 * B)); + Cb := ClampToWord(Round(-0.16874 * R - 0.33126 * G + 0.50000 * B + 32768)); + Cr := ClampToWord(Round( 0.50000 * R - 0.41869 * G - 0.08131 * B + 32768)); +end; + +procedure YCbCrToRGB16(Y, Cb, Cr: Word; var R, G, B: Word); +begin + R := ClampToWord(Round(Y + 1.40200 * (Cr - 32768))); + G := ClampToWord(Round(Y - 0.34414 * (Cb - 32768) - 0.71414 * (Cr - 32768))); + B := ClampToWord(Round(Y + 1.77200 * (Cb - 32768))); +end; + +procedure RGBToCMY(R, G, B: Byte; var C, M, Y: Byte); +begin + C := 255 - R; + M := 255 - G; + Y := 255 - B; +end; + +procedure CMYToRGB(C, M, Y: Byte; var R, G, B: Byte); +begin + R := 255 - C; + G := 255 - M; + B := 255 - Y; +end; + +procedure RGBToCMY16(R, G, B: Word; var C, M, Y: Word); +begin + C := 65535 - R; + M := 65535 - G; + Y := 65535 - B; +end; + +procedure CMYToRGB16(C, M, Y: Word; var R, G, B: Word); +begin + R := 65535 - C; + G := 65535 - M; + B := 65535 - Y; +end; + +procedure RGBToCMYK(R, G, B: Byte; var C, M, Y, K: Byte); +begin + RGBToCMY(R, G, B, C, M, Y); + K := Min(C, Min(M, Y)); + if K = 255 then + begin + C := 0; + M := 0; + Y := 0; + end + else + begin + C := ClampToByte(Round((C - K) / (255 - K) * 255)); + M := ClampToByte(Round((M - K) / (255 - K) * 255)); + Y := ClampToByte(Round((Y - K) / (255 - K) * 255)); + end; +end; + +procedure CMYKToRGB(C, M, Y, K: Byte; var R, G, B: Byte); +begin + R := (255 - (C - MulDiv(C, K, 255) + K)); + G := (255 - (M - MulDiv(M, K, 255) + K)); + B := (255 - (Y - MulDiv(Y, K, 255) + K)); +end; + +procedure RGBToCMYK16(R, G, B: Word; var C, M, Y, K: Word); +begin + RGBToCMY16(R, G, B, C, M, Y); + K := Min(C, Min(M, Y)); + if K = 65535 then + begin + C := 0; + M := 0; + Y := 0; + end + else + begin + C := ClampToWord(Round((C - K) / (65535 - K) * 65535)); + M := ClampToWord(Round((M - K) / (65535 - K) * 65535)); + Y := ClampToWord(Round((Y - K) / (65535 - K) * 65535)); + end; +end; + +procedure CMYKToRGB16(C, M, Y, K: Word; var R, G, B: Word); +begin + R := 65535 - (C - MulDiv(C, K, 65535) + K); + G := 65535 - (M - MulDiv(M, K, 65535) + K); + B := 65535 - (Y - MulDiv(Y, K, 65535) + K); +end; + +procedure RGBToYCoCg(R, G, B: Byte; var Y, Co, Cg: Byte); +begin + // C and Delphi's SHR behaviour differs for negative numbers, use div instead. + Y := ClampToByte(( R + G shl 1 + B + 2) div 4); + Co := ClampToByte(( R shl 1 - B shl 1 + 2) div 4 + 128); + Cg := ClampToByte((-R + G shl 1 - B + 2) div 4 + 128); +end; + +procedure YCoCgToRGB(Y, Co, Cg: Byte; var R, G, B: Byte); +var + CoInt, CgInt: Integer; +begin + CoInt := Co - 128; + CgInt := Cg - 128; + R := ClampToByte(Y + CoInt - CgInt); + G := ClampToByte(Y + CgInt); + B := ClampToByte(Y - CoInt - CgInt); +end; + +{ + File Notes: + + -- TODOS ---------------------------------------------------- + - nothing now + + -- 0.26.3 Changes/Bug Fixes --------------------------------- + - Added RGB<>YCoCg conversion functions. + - Fixed RGB>>CMYK conversions. + + -- 0.23 Changes/Bug Fixes ----------------------------------- + - Added RGB<>CMY(K) converion functions for 16 bit channels + (needed by PSD loading code). + + -- 0.21 Changes/Bug Fixes ----------------------------------- + - Added some color space conversion functions and LUTs + (RGB/YUV/YCrCb/CMY/CMYK). + + -- 0.17 Changes/Bug Fixes ----------------------------------- + - unit created (empty!) +} + +end. diff --git a/resources/libraries/deskew/Imaging/ImagingComponents.pas b/resources/libraries/deskew/Imaging/ImagingComponents.pas new file mode 100755 index 0000000..c80fd01 --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingComponents.pas @@ -0,0 +1,1308 @@ +{ + Vampyre Imaging Library + by Marek Mauder + http://imaginglib.sourceforge.net + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +{ This unit contains VCL/LCL TGraphic descendant which uses Imaging library + for saving and loading.} +unit ImagingComponents; + +{$I ImagingOptions.inc} + +interface + +{$IFDEF LCL} + {$DEFINE COMPONENT_SET_LCL} + {$UNDEF COMPONENT_SET_VCL} +{$ENDIF} + +{$IF not Defined(COMPONENT_SET_LCL) and not Defined(COMPONENT_SET_VCL)} +// If no component sets should be used just include empty unit. +//DOC-IGNORE-BEGIN +implementation +//DOC-IGNORE-END +{$ELSE} + +uses + SysUtils, Types, Classes, +{$IFDEF MSWINDOWS} + Windows, +{$ENDIF} +{$IFDEF COMPONENT_SET_VCL} + Graphics, +{$ENDIF} +{$IFDEF COMPONENT_SET_LCL} + InterfaceBase, + GraphType, + Graphics, + LCLType, + LCLIntf, +{$ENDIF} + ImagingTypes, Imaging, ImagingClasses; + +type + { Graphic class which uses Imaging to load images. + It has standard TBitmap class as ancestor and it can + Assign also to/from TImageData structres and TBaseImage + classes. For saving is uses inherited TBitmap methods. + This class is automatically registered to TPicture for all + file extensions supported by Imaging (useful only for loading). + If you just want to load images in various formats you can use this + class or simply use TPicture.LoadFromXXX which will create this class + automatically. For TGraphic class that saves with Imaging look + at TImagingGraphicForSave class.} + TImagingGraphic = class(TBitmap) + protected + procedure ReadDataFromStream(Stream: TStream); virtual; + procedure AssignTo(Dest: TPersistent); override; + public + constructor Create; override; + + { Loads new image from the stream. It can load all image + file formats supported by Imaging (and enabled of course) + even though it is called by descendant class capable of + saving only one file format.} + procedure LoadFromStream(Stream: TStream); override; + { Copies the image contained in Source to this graphic object. + Supports also TBaseImage descendants from ImagingClasses unit. } + procedure Assign(Source: TPersistent); override; + { Copies the image contained in TBaseImage to this graphic object.} + procedure AssignFromImage(Image: TBaseImage); + { Copies the current image to TBaseImage object.} + procedure AssignToImage(Image: TBaseImage); + { Copies the image contained in TImageData structure to this graphic object.} + procedure AssignFromImageData(const ImageData: TImageData); + { Copies the current image to TImageData structure.} + procedure AssignToImageData(var ImageData: TImageData); + + {$IFDEF FPC} + class function IsStreamFormatSupported(Stream: TStream): boolean; override; + {$ENDIF} + end; + + TImagingGraphicClass = class of TImagingGraphic; + + { Base class for file format specific TGraphic classes that use + Imaging for saving. Each descendant class can load all file formats + supported by Imaging but save only one format (TImagingBitmap + for *.bmp, TImagingJpeg for *.jpg). Format specific classes also + allow easy access to Imaging options that affect saving of files + (they are properties here).} + TImagingGraphicForSave = class(TImagingGraphic) + protected + FDefaultFileExt: string; + FSavingFormat: TImageFormat; + procedure WriteDataToStream(Stream: TStream); virtual; + public + constructor Create; override; + { Saves the current image to the stream. It is saved in the + file format according to the DefaultFileExt property. + So each descendant class can save some other file format.} + procedure SaveToStream(Stream: TStream); override; + { Returns TImageFileFormat descendant for this graphic class.} + class function GetFileFormat: TImageFileFormat; virtual; abstract; + {$IFDEF COMPONENT_SET_LCL} + { Returns file extensions of this graphic class.} + class function GetFileExtensions: string; override; + { Returns default MIME type of this graphic class.} + function GetMimeType: string; override; + {$ENDIF} + { Default (the most common) file extension of this graphic class.} + property DefaultFileExt: string read FDefaultFileExt; + end; + + TImagingGraphicForSaveClass = class of TImagingGraphicForSave; + +{$IFNDEF DONT_LINK_BITMAP} + { TImagingGraphic descendant for loading/saving Windows bitmaps. + VCL/CLX/LCL all have native support for bitmaps so you might + want to disable this class (although you can save bitmaps with + RLE compression with this class).} + TImagingBitmap = class(TImagingGraphicForSave) + protected + FUseRLE: Boolean; + public + constructor Create; override; + procedure SaveToStream(Stream: TStream); override; + class function GetFileFormat: TImageFileFormat; override; + { See ImagingBitmapRLE option for details.} + property UseRLE: Boolean read FUseRLE write FUseRLE; + end; +{$ENDIF} + +{$IFNDEF DONT_LINK_JPEG} + { TImagingGraphic descendant for loading/saving JPEG images.} + TImagingJpeg = class(TImagingGraphicForSave) + protected + FQuality: LongInt; + FProgressive: Boolean; + public + constructor Create; override; + procedure SaveToStream(Stream: TStream); override; + class function GetFileFormat: TImageFileFormat; override; + {$IFDEF COMPONENT_SET_LCL} + function GetMimeType: string; override; + {$ENDIF} + { See ImagingJpegQuality option for details.} + property Quality: LongInt read FQuality write FQuality; + { See ImagingJpegProgressive option for details.} + property Progressive: Boolean read FProgressive write FProgressive; + end; +{$ENDIF} + +{$IFNDEF DONT_LINK_PNG} + { TImagingGraphic descendant for loading/saving PNG images.} + TImagingPNG = class(TImagingGraphicForSave) + protected + FPreFilter: LongInt; + FCompressLevel: LongInt; + public + constructor Create; override; + procedure SaveToStream(Stream: TStream); override; + class function GetFileFormat: TImageFileFormat; override; + { See ImagingPNGPreFilter option for details.} + property PreFilter: LongInt read FPreFilter write FPreFilter; + { See ImagingPNGCompressLevel option for details.} + property CompressLevel: LongInt read FCompressLevel write FCompressLevel; + end; +{$ENDIF} + +{$IFNDEF DONT_LINK_GIF} + { TImagingGraphic descendant for loading/saving GIF images.} + TImagingGIF = class(TImagingGraphicForSave) + public + class function GetFileFormat: TImageFileFormat; override; + end; +{$ENDIF} + +{$IFNDEF DONT_LINK_TARGA} + { TImagingGraphic descendant for loading/saving Targa images.} + TImagingTarga = class(TImagingGraphicForSave) + protected + FUseRLE: Boolean; + public + constructor Create; override; + procedure SaveToStream(Stream: TStream); override; + class function GetFileFormat: TImageFileFormat; override; + { See ImagingTargaRLE option for details.} + property UseRLE: Boolean read FUseRLE write FUseRLE; + end; +{$ENDIF} + +{$IFNDEF DONT_LINK_DDS} + { Compresssion type used when saving DDS files by TImagingDds.} + TDDSCompresion = (dcNone, dcDXT1, dcDXT3, dcDXT5); + + { TImagingGraphic descendant for loading/saving DDS images.} + TImagingDDS = class(TImagingGraphicForSave) + protected + FCompression: TDDSCompresion; + public + constructor Create; override; + procedure SaveToStream(Stream: TStream); override; + class function GetFileFormat: TImageFileFormat; override; + { You can choose compression type used when saving DDS file. + dcNone means that file will be saved in the current bitmaps pixel format.} + property Compression: TDDSCompresion read FCompression write FCompression; + end; +{$ENDIF} + +{$IFNDEF DONT_LINK_MNG} + { TImagingGraphic descendant for loading/saving MNG images.} + TImagingMNG = class(TImagingGraphicForSave) + protected + FLossyCompression: Boolean; + FLossyAlpha: Boolean; + FPreFilter: LongInt; + FCompressLevel: LongInt; + FQuality: LongInt; + FProgressive: Boolean; + public + constructor Create; override; + procedure SaveToStream(Stream: TStream); override; + class function GetFileFormat: TImageFileFormat; override; + {$IFDEF COMPONENT_SET_LCL} + function GetMimeType: string; override; + {$ENDIF} + { See ImagingMNGLossyCompression option for details.} + property LossyCompression: Boolean read FLossyCompression write FLossyCompression; + { See ImagingMNGLossyAlpha option for details.} + property LossyAlpha: Boolean read FLossyAlpha write FLossyAlpha; + { See ImagingMNGPreFilter option for details.} + property PreFilter: LongInt read FPreFilter write FPreFilter; + { See ImagingMNGCompressLevel option for details.} + property CompressLevel: LongInt read FCompressLevel write FCompressLevel; + { See ImagingMNGQuality option for details.} + property Quality: LongInt read FQuality write FQuality; + { See ImagingMNGProgressive option for details.} + property Progressive: Boolean read FProgressive write FProgressive; + end; +{$ENDIF} + +{$IFNDEF DONT_LINK_JNG} + { TImagingGraphic descendant for loading/saving JNG images.} + TImagingJNG = class(TImagingGraphicForSave) + protected + FLossyAlpha: Boolean; + FAlphaPreFilter: LongInt; + FAlphaCompressLevel: LongInt; + FQuality: LongInt; + FProgressive: Boolean; + public + constructor Create; override; + procedure SaveToStream(Stream: TStream); override; + class function GetFileFormat: TImageFileFormat; override; + { See ImagingJNGLossyAlpha option for details.} + property LossyAlpha: Boolean read FLossyAlpha write FLossyAlpha; + { See ImagingJNGPreFilter option for details.} + property AlphaPreFilter: LongInt read FAlphaPreFilter write FAlphaPreFilter; + { See ImagingJNGCompressLevel option for details.} + property AlphaCompressLevel: LongInt read FAlphaCompressLevel write FAlphaCompressLevel; + { See ImagingJNGQuality option for details.} + property Quality: LongInt read FQuality write FQuality; + { See ImagingJNGProgressive option for details.} + property Progressive: Boolean read FProgressive write FProgressive; + end; +{$ENDIF} + +{ Returns bitmap pixel format with the closest match with given data format.} +function DataFormatToPixelFormat(Format: TImageFormat): TPixelFormat; +{ Returns data format with closest match with given bitmap pixel format.} +function PixelFormatToDataFormat(Format: TPixelFormat): TImageFormat; + +{ Converts TImageData structure to VCL/CLX/LCL bitmap.} +procedure ConvertDataToBitmap(const Data: TImageData; Bitmap: TBitmap); +{ Converts VCL/CLX/LCL bitmap to TImageData structure.} +procedure ConvertBitmapToData(Bitmap: TBitmap; var Data: TImageData); +{ Converts TBaseImage instance to VCL/CLX/LCL bitmap.} +procedure ConvertImageToBitmap(Image: TBaseImage; Bitmap: TBitmap); +{ Converts VCL/CLX/LCL bitmap to TBaseImage. Image must exist before + procedure is called. It overwrites its current image data. + When Image is TMultiImage only the current image level is overwritten.} +procedure ConvertBitmapToImage(Bitmap: TBitmap; Image: TBaseImage); + +{ Displays image stored in TImageData structure onto TCanvas. This procedure + draws image without converting from Imaging format to TBitmap. + Only [ifA8R8G8B8, ifX8R8G8B8] image formats are supported. Use this + when you want displaying images that change frequently (because converting to + TBitmap by ConvertImageDataToBitmap is generally slow). Dest and Src + rectangles represent coordinates in the form (X1, Y1, X2, Y2).} +procedure DisplayImageData(DstCanvas: TCanvas; const DstRect: TRect; const ImageData: TImageData; const SrcRect: TRect); +{ Displays image onto TCanvas at position [DstX, DstY]. This procedure + draws image without converting from Imaging format to TBitmap. + Only [ifA8R8G8B8, ifX8R8G8B8] image formats are supported. Use this + when you want displaying images that change frequently (because converting to + TBitmap by ConvertImageDataToBitmap is generally slow).} +procedure DisplayImage(DstCanvas: TCanvas; DstX, DstY: LongInt; Image: TBaseImage); overload; +{ Displays image onto TCanvas to rectangle DstRect. This procedure + draws image without converting from Imaging format to TBitmap. + Only [ifA8R8G8B8, ifX8R8G8B8] image formats are supported. Use this + when you want displaying images that change frequently (because converting to + TBitmap by ConvertImageDataToBitmap is generally slow).} +procedure DisplayImage(DstCanvas: TCanvas; const DstRect: TRect; Image: TBaseImage); overload; +{ Displays part of the image specified by SrcRect onto TCanvas to rectangle DstRect. + This procedure draws image without converting from Imaging format to TBitmap. + Only [ifA8R8G8B8, ifX8R8G8B8] image formats are supported. Use this + when you want displaying images that change frequently (because converting to + TBitmap by ConvertImageDataToBitmap is generally slow).} +procedure DisplayImage(DstCanvas: TCanvas; const DstRect: TRect; Image: TBaseImage; const SrcRect: TRect); overload; + +{$IFDEF MSWINDOWS} +{ Displays image stored in TImageData structure onto Windows device context. + Behaviour is the same as of DisplayImageData.} +procedure DisplayImageDataOnDC(DC: HDC; const DstRect: TRect; const ImageData: TImageData; const SrcRect: TRect); +{$ENDIF} + +implementation + +uses +{$IF Defined(LCL)} + {$IF Defined(LCLGTK2)} + GLib2, GDK2, GTK2, GTK2Def, GTK2Proc, + {$IFEND} +{$IFEND} +{$IFNDEF DONT_LINK_BITMAP} + ImagingBitmap, +{$ENDIF} +{$IFNDEF DONT_LINK_JPEG} + ImagingJpeg, +{$ENDIF} +{$IFNDEF DONT_LINK_GIF} + ImagingGif, +{$ENDIF} +{$IFNDEF DONT_LINK_TARGA} + ImagingTarga, +{$ENDIF} +{$IFNDEF DONT_LINK_DDS} + ImagingDds, +{$ENDIF} +{$IF not Defined(DONT_LINK_PNG) or not Defined(DONT_LINK_MNG) or not Defined(DONT_LINK_JNG)} + ImagingNetworkGraphics, +{$IFEND} + ImagingFormats, ImagingUtility; + +resourcestring + SBadFormatDataToBitmap = 'Cannot find compatible bitmap format for image %s'; + SBadFormatBitmapToData = 'Cannot find compatible data format for bitmap %p'; + SBadFormatDisplay = 'Unsupported image format passed'; + SUnsupportedLCLWidgetSet = 'This function is not implemented for current LCL widget set'; + SImagingGraphicName = 'Imaging Graphic AllInOne'; + +{ Registers types to VCL/LCL.} +procedure RegisterTypes; +var + I: LongInt; + + procedure RegisterFileFormatAllInOne(Format: TImageFileFormat); + var + I: LongInt; + begin + for I := 0 to Format.Extensions.Count - 1 do + TPicture.RegisterFileFormat(Format.Extensions[I], SImagingGraphicName, + TImagingGraphic); + end; + + procedure RegisterFileFormat(AClass: TImagingGraphicForSaveClass); + var + I: LongInt; + begin + for I := 0 to AClass.GetFileFormat.Extensions.Count - 1 do + TPicture.RegisterFileFormat(AClass.GetFileFormat.Extensions[I], + AClass.GetFileFormat.Name, AClass); + end; + +begin + for I := Imaging.GetFileFormatCount - 1 downto 0 do + RegisterFileFormatAllInOne(Imaging.GetFileFormatAtIndex(I)); + Classes.RegisterClass(TImagingGraphic); + +{$IFNDEF DONT_LINK_TARGA} + RegisterFileFormat(TImagingTarga); + Classes.RegisterClass(TImagingTarga); +{$ENDIF} +{$IFNDEF DONT_LINK_DDS} + RegisterFileFormat(TImagingDDS); + Classes.RegisterClass(TImagingDDS); +{$ENDIF} +{$IFNDEF DONT_LINK_JNG} + RegisterFileFormat(TImagingJNG); + Classes.RegisterClass(TImagingJNG); +{$ENDIF} +{$IFNDEF DONT_LINK_MNG} + RegisterFileFormat(TImagingMNG); + Classes.RegisterClass(TImagingMNG); +{$ENDIF} +{$IFNDEF DONT_LINK_GIF} + RegisterFileFormat(TImagingGIF); + Classes.RegisterClass(TImagingGIF); +{$ENDIF} +{$IFNDEF DONT_LINK_PNG} + {$IFDEF COMPONENT_SET_LCL} + // Unregister Lazarus´ default PNG loader which crashes on some PNG files + TPicture.UnregisterGraphicClass(TPortableNetworkGraphic); + {$ENDIF} + RegisterFileFormat(TImagingPNG); + Classes.RegisterClass(TImagingPNG); +{$ENDIF} +{$IFNDEF DONT_LINK_JPEG} + RegisterFileFormat(TImagingJpeg); + Classes.RegisterClass(TImagingJpeg); +{$ENDIF} +{$IFNDEF DONT_LINK_BITMAP} + RegisterFileFormat(TImagingBitmap); + Classes.RegisterClass(TImagingBitmap); +{$ENDIF} +end; + +{ Unregisters types from VCL/LCL.} +procedure UnRegisterTypes; +begin +{$IFNDEF DONT_LINK_BITMAP} + TPicture.UnregisterGraphicClass(TImagingBitmap); + Classes.UnRegisterClass(TImagingBitmap); +{$ENDIF} +{$IFNDEF DONT_LINK_JPEG} + TPicture.UnregisterGraphicClass(TImagingJpeg); + Classes.UnRegisterClass(TImagingJpeg); +{$ENDIF} +{$IFNDEF DONT_LINK_PNG} + TPicture.UnregisterGraphicClass(TImagingPNG); + Classes.UnRegisterClass(TImagingPNG); +{$ENDIF} +{$IFNDEF DONT_LINK_GIF} + TPicture.UnregisterGraphicClass(TImagingGIF); + Classes.UnRegisterClass(TImagingGIF); +{$ENDIF} +{$IFNDEF DONT_LINK_TARGA} + TPicture.UnregisterGraphicClass(TImagingTarga); + Classes.UnRegisterClass(TImagingTarga); +{$ENDIF} +{$IFNDEF DONT_LINK_DDS} + TPicture.UnregisterGraphicClass(TImagingDDS); + Classes.UnRegisterClass(TImagingDDS); +{$ENDIF} + TPicture.UnregisterGraphicClass(TImagingGraphic); + Classes.UnRegisterClass(TImagingGraphic); +end; + +function DataFormatToPixelFormat(Format: TImageFormat): TPixelFormat; +begin + case Format of +{$IFDEF COMPONENT_SET_VCL} + ifIndex8: Result := pf8bit; + ifR5G6B5: Result := pf16bit; + ifR8G8B8: Result := pf24bit; +{$ENDIF} + ifA8R8G8B8, + ifX8R8G8B8: Result := pf32bit; + else + Result := pfCustom; + end; +end; + +function PixelFormatToDataFormat(Format: TPixelFormat): TImageFormat; +begin + case Format of + pf8bit: Result := ifIndex8; + pf15bit: Result := ifA1R5G5B5; + pf16bit: Result := ifR5G6B5; + pf24bit: Result := ifR8G8B8; + pf32bit: Result := ifA8R8G8B8; + else + Result := ifUnknown; + end; +end; + +procedure ConvertDataToBitmap(const Data: TImageData; Bitmap: TBitmap); +var + I, LineBytes: LongInt; + PF: TPixelFormat; + Info: TImageFormatInfo; + WorkData: TImageData; +{$IFDEF COMPONENT_SET_VCL} + LogPalette: TMaxLogPalette; +{$ENDIF} +{$IFDEF COMPONENT_SET_LCL} + RawImage: TRawImage; + ImgHandle, ImgMaskHandle: HBitmap; +{$ENDIF} +begin + PF := DataFormatToPixelFormat(Data.Format); + GetImageFormatInfo(Data.Format, Info); + + if (PF = pf8bit) and PaletteHasAlpha(Data.Palette, Info.PaletteEntries) then + begin + // Some indexed images may have valid alpha data, dont lose it! + // (e.g. transparent 8bit PNG or GIF images) + PF := pfCustom; + end; + + if PF = pfCustom then + begin + // Convert from formats not supported by Graphics unit + Imaging.InitImage(WorkData); + Imaging.CloneImage(Data, WorkData); + if Info.IsFloatingPoint or Info.HasAlphaChannel or Info.IsSpecial then + Imaging.ConvertImage(WorkData, ifA8R8G8B8) + else + begin +{$IFDEF COMPONENT_SET_VCL} + if Info.IsIndexed or Info.HasGrayChannel then + Imaging.ConvertImage(WorkData, ifIndex8) + else if Info.UsePixelFormat then + Imaging.ConvertImage(WorkData, ifR5G6B5) + else + Imaging.ConvertImage(WorkData, ifR8G8B8); +{$ELSE} + Imaging.ConvertImage(WorkData, ifA8R8G8B8); +{$ENDIF} + end; + + PF := DataFormatToPixelFormat(WorkData.Format); + GetImageFormatInfo(WorkData.Format, Info); + end + else + WorkData := Data; + + if PF = pfCustom then + RaiseImaging(SBadFormatDataToBitmap, [ImageToStr(WorkData)]); + + LineBytes := WorkData.Width * Info.BytesPerPixel; + +{$IFDEF COMPONENT_SET_VCL} + Bitmap.Width := WorkData.Width; + Bitmap.Height := WorkData.Height; + Bitmap.PixelFormat := PF; + + if (PF = pf8bit) and (WorkData.Palette <> nil) then + begin + // Copy palette, this must be done before copying bits + FillChar(LogPalette, SizeOf(LogPalette), 0); + LogPalette.palVersion := $300; + LogPalette.palNumEntries := Info.PaletteEntries; + for I := 0 to Info.PaletteEntries - 1 do + with LogPalette do + begin + palPalEntry[I].peRed := WorkData.Palette[I].R; + palPalEntry[I].peGreen := WorkData.Palette[I].G; + palPalEntry[I].peBlue := WorkData.Palette[I].B; + end; + Bitmap.Palette := CreatePalette(PLogPalette(@LogPalette)^); + end; + // Copy scanlines + for I := 0 to WorkData.Height - 1 do + Move(PByteArray(WorkData.Bits)[I * LineBytes], Bitmap.Scanline[I]^, LineBytes); + + // Delphi 2009 and newer support alpha transparency fro TBitmap +{$IF Defined(DELPHI) and (CompilerVersion >= 20.0)} + if Bitmap.PixelFormat = pf32bit then + Bitmap.AlphaFormat := afDefined; +{$IFEND} + +{$ENDIF} +{$IFDEF COMPONENT_SET_LCL} + // Create 32bit raw image from image data + FillChar(RawImage, SizeOf(RawImage), 0); + with RawImage.Description do + begin + Width := WorkData.Width; + Height := WorkData.Height; + BitsPerPixel := 32; + Format := ricfRGBA; + LineEnd := rileDWordBoundary; + BitOrder := riboBitsInOrder; + ByteOrder := riboLSBFirst; + LineOrder := riloTopToBottom; + AlphaPrec := 8; + RedPrec := 8; + GreenPrec := 8; + BluePrec := 8; + AlphaShift := 24; + RedShift := 16; + GreenShift := 8; + BlueShift := 0; + Depth := 32; // Must be 32 for alpha blending (and for working in MacOSX Carbon) + end; + RawImage.Data := WorkData.Bits; + RawImage.DataSize := WorkData.Size; + + // Create bitmap from raw image + if RawImage_CreateBitmaps(RawImage, ImgHandle, ImgMaskHandle) then + begin + Bitmap.Handle := ImgHandle; + Bitmap.MaskHandle := ImgMaskHandle; + end; +{$ENDIF} + if WorkData.Bits <> Data.Bits then + Imaging.FreeImage(WorkData); +end; + +procedure ConvertBitmapToData(Bitmap: TBitmap; var Data: TImageData); +var + I, LineBytes: LongInt; + Format: TImageFormat; + Info: TImageFormatInfo; +{$IFDEF COMPONENT_SET_VCL} + Colors: Word; + LogPalette: TMaxLogPalette; +{$ENDIF} +{$IFDEF COMPONENT_SET_LCL} + RawImage: TRawImage; + LineLazBytes: LongInt; +{$ENDIF} +begin +{$IFDEF COMPONENT_SET_LCL} + // In the current Lazarus 0.9.10 Bitmap.PixelFormat property is useless. + // We cannot change bitmap's format by changing it (it will just release + // old image but not convert it to new format) nor we can determine bitmaps's + // current format (it is usually set to pfDevice). So bitmap's format is obtained + // trough RawImage api and cannot be changed to mirror some Imaging format + // (so formats with no coresponding Imaging format cannot be saved now). + + if RawImage_DescriptionFromBitmap(Bitmap.Handle, RawImage.Description) then + case RawImage.Description.BitsPerPixel of + 8: Format := ifIndex8; + 16: + if RawImage.Description.Depth = 15 then + Format := ifA1R5G5B5 + else + Format := ifR5G6B5; + 24: Format := ifR8G8B8; + 32: Format := ifA8R8G8B8; + 48: Format := ifR16G16B16; + 64: Format := ifA16R16G16B16; + else + Format := ifUnknown; + end; +{$ELSE} + Format := PixelFormatToDataFormat(Bitmap.PixelFormat); + if Format = ifUnknown then + begin + // Convert from formats not supported by Imaging (1/4 bit) + if Bitmap.PixelFormat < pf8bit then + Bitmap.PixelFormat := pf8bit + else + Bitmap.PixelFormat := pf32bit; + Format := PixelFormatToDataFormat(Bitmap.PixelFormat); + end; +{$ENDIF} + + if Format = ifUnknown then + RaiseImaging(SBadFormatBitmapToData, []); + + Imaging.NewImage(Bitmap.Width, Bitmap.Height, Format, Data); + GetImageFormatInfo(Data.Format, Info); + LineBytes := Data.Width * Info.BytesPerPixel; + +{$IFDEF COMPONENT_SET_VCL} + if (Format = ifIndex8) and (GetObject(Bitmap.Palette, SizeOf(Colors), + @Colors) <> 0) then + begin + // Copy palette + GetPaletteEntries(Bitmap.Palette, 0, Colors, LogPalette.palPalEntry); + if Colors > Info.PaletteEntries then + Colors := Info.PaletteEntries; + for I := 0 to Colors - 1 do + with LogPalette do + begin + Data.Palette[I].A := $FF; + Data.Palette[I].R := palPalEntry[I].peRed; + Data.Palette[I].G := palPalEntry[I].peGreen; + Data.Palette[I].B := palPalEntry[I].peBlue; + end; + end; + // Copy scanlines + for I := 0 to Data.Height - 1 do + Move(Bitmap.ScanLine[I]^, PByteArray(Data.Bits)[I * LineBytes], LineBytes); +{$ENDIF} +{$IFDEF COMPONENT_SET_LCL} + // Get raw image from bitmap (mask handle must be 0 or expect violations) + if RawImage_FromBitmap(RawImage, Bitmap.Handle, 0, nil) then + begin + LineLazBytes := GetBytesPerLine(Data.Width, RawImage.Description.BitsPerPixel, + RawImage.Description.LineEnd); + // Copy scanlines + for I := 0 to Data.Height - 1 do + begin + Move(PByteArray(RawImage.Data)[I * LineLazBytes], + PByteArray(Data.Bits)[I * LineBytes], LineBytes); + end; + // May need to swap RB order, depends on wifget set + if RawImage.Description.BlueShift > RawImage.Description.RedShift then + SwapChannels(Data, ChannelRed, ChannelBlue); + + RawImage.FreeData; + end; +{$ENDIF} +end; + +procedure ConvertImageToBitmap(Image: TBaseImage; Bitmap: TBitmap); +begin + ConvertDataToBitmap(Image.ImageDataPointer^, Bitmap); +end; + +procedure ConvertBitmapToImage(Bitmap: TBitmap; Image: TBaseImage); +begin + ConvertBitmapToData(Bitmap, Image.ImageDataPointer^); +end; + +{$IFDEF MSWINDOWS} +procedure DisplayImageDataOnDC(DC: HDC; const DstRect: TRect; const ImageData: TImageData; const SrcRect: TRect); +var + OldMode: Integer; + BitmapInfo: Windows.TBitmapInfo; + Bmp: TBitmap; +begin + if TestImage(ImageData) then + begin + Assert(ImageData.Format in [ifA8R8G8B8, ifX8R8G8B8], SBadFormatDisplay); + OldMode := Windows.SetStretchBltMode(DC, COLORONCOLOR); + + FillChar(BitmapInfo, SizeOf(BitmapInfo), 0); + with BitmapInfo.bmiHeader do + begin + biSize := SizeOf(TBitmapInfoHeader); + biPlanes := 1; + biBitCount := 32; + biCompression := BI_RGB; + biWidth := ImageData.Width; + biHeight := -ImageData.Height; + biSizeImage := ImageData.Size; + biXPelsPerMeter := 0; + biYPelsPerMeter := 0; + biClrUsed := 0; + biClrImportant := 0; + end; + + try + with SrcRect, ImageData do + if Windows.StretchDIBits(DC, DstRect.Left, DstRect.Top, + DstRect.Right - DstRect.Left, DstRect.Bottom - DstRect.Top, Left, + Top, Right - Left, Bottom - Top, Bits, BitmapInfo, DIB_RGB_COLORS, SRCCOPY) <> Height then + begin + // StretchDIBits may fail on some ocassions (error 487, http://support.microsoft.com/kb/269585). + // This fallback is slow but works every time. Thanks to Sergey Galezdinov for the fix. + Bmp := TBitmap.Create; + try + ConvertDataToBitmap(ImageData, Bmp); + StretchBlt(DC, DstRect.Left, DstRect.Top, DstRect.Right - DstRect.Left, DstRect.Bottom - DstRect.Top, + Bmp.Canvas.Handle, 0, 0, Width, Height, SRCCOPY); + finally + Bmp.Free; + end; + end; + finally + Windows.SetStretchBltMode(DC, OldMode); + end; + end; +end; +{$ENDIF} + +procedure DisplayImageData(DstCanvas: TCanvas; const DstRect: TRect; const ImageData: TImageData; const SrcRect: TRect); +{$IF Defined(DCC) or Defined(LCLWIN32)} // Delphi or LCL Win32 +begin + DisplayImageDataOnDC(DstCanvas.Handle, DstRect, ImageData, SrcRect); +end; +{$ELSEIF Defined(LCLGTK2)} + type + TDeviceContext = TGtk2DeviceContext; + + procedure GDKDrawBitmap(Dest: HDC; DstX, DstY: Integer; SrcX, SrcY, + SrcWidth, SrcHeight: Integer; ImageData: TImageData); + var + P: TPoint; + begin + P := TDeviceContext(Dest).Offset; + Inc(DstX, P.X); + Inc(DstY, P.Y); + gdk_draw_rgb_32_image(TDeviceContext(Dest).Drawable, TDeviceContext(Dest).GC, + DstX, DstY, SrcWidth, SrcHeight, GDK_RGB_DITHER_NONE, + @PLongWordArray(ImageData.Bits)[SrcY * ImageData.Width + SrcX], ImageData.Width * 4); + end; + +var + DisplayImage: TImageData; + NewWidth, NewHeight: Integer; + SrcBounds, DstBounds, DstClip: TRect; +begin + if TestImage(ImageData) then + begin + Assert(ImageData.Format in [ifA8R8G8B8, ifX8R8G8B8], SBadFormatDisplay); + InitImage(DisplayImage); + + SrcBounds := RectToBounds(SrcRect); + DstBounds := RectToBounds(DstRect); + WidgetSet.GetClipBox(DstCanvas.Handle, @DstClip); + + ClipStretchBounds(SrcBounds.Left, SrcBounds.Top, SrcBounds.Right, SrcBounds.Bottom, + DstBounds.Left, DstBounds.Top, DstBounds.Right, DstBounds.Bottom, ImageData.Width, + ImageData.Height, DstClip); + + NewWidth := DstBounds.Right; + NewHeight := DstBounds.Bottom; + + if (NewWidth > 0) and (NewHeight > 0) then + begin + if (SrcBounds.Right = NewWidth) and (SrcBounds.Bottom = NewHeight) then + try + CloneImage(ImageData, DisplayImage); + // Swap R-B channels for GTK display compatability! + SwapChannels(DisplayImage, ChannelRed, ChannelBlue); + GDKDrawBitmap(DstCanvas.Handle, DstBounds.Left, DstBounds.Top, + SrcBounds.Left, SrcBounds.Top, NewWidth, NewHeight, DisplayImage); + finally + FreeImage(DisplayImage); + end + else + try + // Create new image with desired dimensions + NewImage(NewWidth, NewHeight, ImageData.Format, DisplayImage); + // Stretch pixels from old image to new one TResizeFilter = (rfNearest, rfBilinear, rfBicubic); + StretchRect(ImageData, SrcBounds.Left, SrcBounds.Top, SrcBounds.Right, + SrcBounds.Bottom, DisplayImage, 0, 0, NewWidth, NewHeight, rfNearest); + // Swap R-B channels for GTK display compatability! + SwapChannels(DisplayImage, ChannelRed, ChannelBlue); + GDKDrawBitmap(DstCanvas.Handle, DstBounds.Left, DstBounds.Top, 0, 0, + NewWidth, NewHeight, DisplayImage); + finally + FreeImage(DisplayImage); + end + end; + end; +end; +{$ELSE} +begin + raise Exception.Create(SUnsupportedLCLWidgetSet); +end; +{$IFEND} + +procedure DisplayImage(DstCanvas: TCanvas; DstX, DstY: LongInt; Image: TBaseImage); +begin + DisplayImageData(DstCanvas, BoundsToRect(DstX, DstY, Image.Width, Image.Height), + Image.ImageDataPointer^, Image.BoundsRect); +end; + +procedure DisplayImage(DstCanvas: TCanvas; const DstRect: TRect; Image: TBaseImage); +begin + DisplayImageData(DstCanvas, DstRect, Image.ImageDataPointer^, Image.BoundsRect); +end; + +procedure DisplayImage(DstCanvas: TCanvas; const DstRect: TRect; Image: TBaseImage; const SrcRect: TRect); +begin + DisplayImageData(DstCanvas, DstRect, Image.ImageDataPointer^, SrcRect); +end; + + +{ TImagingGraphic class implementation } + +constructor TImagingGraphic.Create; +begin + inherited Create; + PixelFormat := pf24Bit; +end; + +procedure TImagingGraphic.LoadFromStream(Stream: TStream); +begin + ReadDataFromStream(Stream); +end; + +procedure TImagingGraphic.ReadDataFromStream(Stream: TStream); +var + Image: TSingleImage; +begin + Image := TSingleImage.Create; + try + Image.LoadFromStream(Stream); + Assign(Image); + finally + Image.Free; + end; +end; + +procedure TImagingGraphic.AssignTo(Dest: TPersistent); +var + Arr: TDynImageDataArray; +begin + if Dest is TSingleImage then + begin + AssignToImage(TSingleImage(Dest)) + end + else if Dest is TMultiImage then + begin + SetLength(Arr, 1); + AssignToImageData(Arr[0]); + TMultiImage(Dest).CreateFromArray(Arr); + Imaging.FreeImagesInArray(Arr); + end + else + inherited AssignTo(Dest); +end; + +{$IFDEF FPC} +class function TImagingGraphic.IsStreamFormatSupported(Stream: TStream): Boolean; +begin + Result := DetermineStreamFormat(Stream) <> ''; +end; +{$ENDIF} + +procedure TImagingGraphic.Assign(Source: TPersistent); +begin + if Source is TBaseImage then + AssignFromImage(TBaseImage(Source)) + else + inherited Assign(Source); +end; + +procedure TImagingGraphic.AssignFromImage(Image: TBaseImage); +begin + if (Image <> nil) and Image.Valid then + AssignFromImageData(Image.ImageDataPointer^); +end; + +procedure TImagingGraphic.AssignToImage(Image: TBaseImage); +begin + if (Image <> nil) and (Image.ImageDataPointer <> nil) then + AssignToImageData(Image.ImageDataPointer^); +end; + +procedure TImagingGraphic.AssignFromImageData(const ImageData: TImageData); +begin + if Imaging.TestImage(ImageData) then + ConvertDataToBitmap(ImageData, Self); +end; + +procedure TImagingGraphic.AssignToImageData(var ImageData: TImageData); +begin + Imaging.FreeImage(ImageData); + ConvertBitmapToData(Self, ImageData); +end; + + +{ TImagingGraphicForSave class implementation } + +constructor TImagingGraphicForSave.Create; +begin + inherited Create; + FDefaultFileExt := GetFileFormat.Extensions[0]; + FSavingFormat := ifUnknown; + GetFileFormat.CheckOptionsValidity; +end; + +procedure TImagingGraphicForSave.WriteDataToStream(Stream: TStream); +var + Image: TSingleImage; +begin + if FDefaultFileExt <> '' then + begin + Image := TSingleImage.Create; + try + Image.Assign(Self); + if FSavingFormat <> ifUnknown then + Image.Format := FSavingFormat; + Image.SaveToStream(FDefaultFileExt, Stream); + finally + Image.Free; + end; + end; +end; + +procedure TImagingGraphicForSave.SaveToStream(Stream: TStream); +begin + WriteDataToStream(Stream); +end; + +{$IFDEF COMPONENT_SET_LCL} +class function TImagingGraphicForSave.GetFileExtensions: string; +begin + Result := StringReplace(GetFileFormat.Extensions.CommaText, ',', ';', [rfReplaceAll]); +end; + +function TImagingGraphicForSave.GetMimeType: string; +begin + Result := 'image/' + FDefaultFileExt; +end; +{$ENDIF} + +{$IFNDEF DONT_LINK_BITMAP} + +{ TImagingBitmap class implementation } + +constructor TImagingBitmap.Create; +begin + inherited Create; + FUseRLE := (GetFileFormat as TBitmapFileFormat).UseRLE; +end; + +class function TImagingBitmap.GetFileFormat: TImageFileFormat; +begin + Result := FindImageFileFormatByClass(TBitmapFileFormat); +end; + +procedure TImagingBitmap.SaveToStream(Stream: TStream); +begin + Imaging.PushOptions; + Imaging.SetOption(ImagingBitmapRLE, Ord(FUseRLE)); + inherited SaveToStream(Stream); + Imaging.PopOptions; +end; +{$ENDIF} + +{$IFNDEF DONT_LINK_JPEG} + +{ TImagingJpeg class implementation } + +constructor TImagingJpeg.Create; +begin + inherited Create; + FQuality := (GetFileFormat as TJpegFileFormat).Quality; + FProgressive := (GetFileFormat as TJpegFileFormat).Progressive; +end; + +class function TImagingJpeg.GetFileFormat: TImageFileFormat; +begin + Result := FindImageFileFormatByClass(TJpegFileFormat); +end; + +{$IFDEF COMPONENT_SET_LCL} +function TImagingJpeg.GetMimeType: string; +begin + Result := 'image/jpeg'; +end; +{$ENDIF} + +procedure TImagingJpeg.SaveToStream(Stream: TStream); +begin + Imaging.PushOptions; + Imaging.SetOption(ImagingJpegQuality, FQuality); + Imaging.SetOption(ImagingJpegProgressive, Ord(FProgressive)); + inherited SaveToStream(Stream); + Imaging.PopOptions; +end; + +{$ENDIF} + +{$IFNDEF DONT_LINK_PNG} + +{ TImagingPNG class implementation } + +constructor TImagingPNG.Create; +begin + inherited Create; + FPreFilter := (GetFileFormat as TPNGFileFormat).PreFilter; + FCompressLevel := (GetFileFormat as TPNGFileFormat).CompressLevel; +end; + +class function TImagingPNG.GetFileFormat: TImageFileFormat; +begin + Result := FindImageFileFormatByClass(TPNGFileFormat); +end; + +procedure TImagingPNG.SaveToStream(Stream: TStream); +begin + Imaging.PushOptions; + Imaging.SetOption(ImagingPNGPreFilter, FPreFilter); + Imaging.SetOption(ImagingPNGCompressLevel, FCompressLevel); + inherited SaveToStream(Stream); + Imaging.PopOptions; +end; +{$ENDIF} + +{$IFNDEF DONT_LINK_GIF} + +{ TImagingGIF class implementation} + +class function TImagingGIF.GetFileFormat: TImageFileFormat; +begin + Result := FindImageFileFormatByClass(TGIFFileFormat); +end; + +{$ENDIF} + +{$IFNDEF DONT_LINK_TARGA} + +{ TImagingTarga class implementation } + +constructor TImagingTarga.Create; +begin + inherited Create; + FUseRLE := (GetFileFormat as TTargaFileFormat).UseRLE; +end; + +class function TImagingTarga.GetFileFormat: TImageFileFormat; +begin + Result := FindImageFileFormatByClass(TTargaFileFormat); +end; + +procedure TImagingTarga.SaveToStream(Stream: TStream); +begin + Imaging.PushOptions; + Imaging.SetOption(ImagingTargaRLE, Ord(FUseRLE)); + inherited SaveToStream(Stream); + Imaging.PopOptions; +end; +{$ENDIF} + +{$IFNDEF DONT_LINK_DDS} + +{ TImagingDDS class implementation } + +constructor TImagingDDS.Create; +begin + inherited Create; + FCompression := dcNone; +end; + +class function TImagingDDS.GetFileFormat: TImageFileFormat; +begin + Result := FindImageFileFormatByClass(TDDSFileFormat); +end; + +procedure TImagingDDS.SaveToStream(Stream: TStream); +begin + case FCompression of + dcNone: FSavingFormat := ifUnknown; + dcDXT1: FSavingFormat := ifDXT1; + dcDXT3: FSavingFormat := ifDXT3; + dcDXT5: FSavingFormat := ifDXT5; + end; + Imaging.PushOptions; + Imaging.SetOption(ImagingDDSSaveCubeMap, Ord(False)); + Imaging.SetOption(ImagingDDSSaveVolume, Ord(False)); + Imaging.SetOption(ImagingDDSSaveMipMapCount, 1); + Imaging.SetOption(ImagingDDSSaveDepth, 1); + inherited SaveToStream(Stream); + Imaging.PopOptions; +end; +{$ENDIF} + +{$IFNDEF DONT_LINK_MNG} + +{ TImagingMNG class implementation } + +constructor TImagingMNG.Create; +begin + inherited Create; + FLossyCompression := (GetFileFormat as TMNGFileFormat).LossyCompression; + FLossyAlpha := (GetFileFormat as TMNGFileFormat).LossyAlpha; + FPreFilter := (GetFileFormat as TMNGFileFormat).PreFilter; + FCompressLevel := (GetFileFormat as TMNGFileFormat).CompressLevel; + FQuality := (GetFileFormat as TMNGFileFormat).Quality; + FProgressive := (GetFileFormat as TMNGFileFormat).Progressive; +end; + +class function TImagingMNG.GetFileFormat: TImageFileFormat; +begin + Result := FindImageFileFormatByClass(TMNGFileFormat); +end; + +{$IFDEF COMPONENT_SET_LCL} +function TImagingMNG.GetMimeType: string; +begin + Result := 'video/mng'; +end; +{$ENDIF} + +procedure TImagingMNG.SaveToStream(Stream: TStream); +begin + Imaging.PushOptions; + Imaging.SetOption(ImagingMNGLossyCompression, Ord(FLossyCompression)); + Imaging.SetOption(ImagingMNGLossyAlpha, Ord(FLossyAlpha)); + Imaging.SetOption(ImagingMNGPreFilter, FPreFilter); + Imaging.SetOption(ImagingMNGCompressLevel, FCompressLevel); + Imaging.SetOption(ImagingMNGQuality, FQuality); + Imaging.SetOption(ImagingMNGProgressive, Ord(FProgressive)); + inherited SaveToStream(Stream); + Imaging.PopOptions; +end; +{$ENDIF} + +{$IFNDEF DONT_LINK_JNG} + +{ TImagingJNG class implementation } + +constructor TImagingJNG.Create; +begin + inherited Create; + FLossyAlpha := (GetFileFormat as TJNGFileFormat).LossyAlpha; + FAlphaPreFilter := (GetFileFormat as TJNGFileFormat).PreFilter; + FAlphaCompressLevel := (GetFileFormat as TJNGFileFormat).CompressLevel; + FQuality := (GetFileFormat as TJNGFileFormat).Quality; + FProgressive := (GetFileFormat as TJNGFileFormat).Progressive; +end; + +class function TImagingJNG.GetFileFormat: TImageFileFormat; +begin + Result := FindImageFileFormatByClass(TJNGFileFormat); +end; + +procedure TImagingJNG.SaveToStream(Stream: TStream); +begin + Imaging.PushOptions; + Imaging.SetOption(ImagingJNGLossyALpha, Ord(FLossyAlpha)); + Imaging.SetOption(ImagingJNGAlphaPreFilter, FAlphaPreFilter); + Imaging.SetOption(ImagingJNGAlphaCompressLevel, FAlphaCompressLevel); + Imaging.SetOption(ImagingJNGQuality, FQuality); + Imaging.SetOption(ImagingJNGProgressive, Ord(FProgressive)); + inherited SaveToStream(Stream); + Imaging.PopOptions; +end; +{$ENDIF} + +initialization + RegisterTypes; +finalization + UnRegisterTypes; + +{$IFEND} // {$IF not Defined(COMPONENT_SET_LCL) and not Defined(COMPONENT_SET_VCL)} + +{ + File Notes: + + -- TODOS ---------------------------------------------------- + - nothing now + + -- 0.77.1 --------------------------------------------------- + - Fixed bug in ConvertBitmapToData causing images from GTK2 bitmaps + to have swapped RB channels. + - LCL: Removed GTK1 support (deprecated). + + -- 0.26.3 Changes/Bug Fixes --------------------------------- + - Transparency of 8bit images (like loaded from 8bit PNG or GIF) is + kept intact during conversion to TBitmap in ConvertDataToBitmap + (32bit bitmap is created). + + -- 0.26.3 Changes/Bug Fixes --------------------------------- + - Setting AlphaFormat property of TBitmap in ConvertDataToBitmap + when using Delphi 2009+. + - Fixed garbled LCL TBitmaps created by ConvertDataToBitmap + in Mac OS X (Carbon). + + -- 0.26.1 Changes/Bug Fixes --------------------------------- + - Added some more IFDEFs for Lazarus widget sets. + - Removed CLX code. + - GTK version of Unix DisplayImageData only used with LCL GTK so the + the rest of the unit can be used with Qt or other LCL interfaces. + - Fallback mechanism for DisplayImageDataOnDC, it may fail on occasions. + - Changed file format conditional compilation to reflect changes + in LINK symbols. + - Lazarus 0.9.26 compatibility changes. + + -- 0.24.1 Changes/Bug Fixes --------------------------------- + - Fixed wrong IFDEF causing that Imaging wouldn't compile in Lazarus + with GTK2 target. + - Added commnets with code for Lazarus rev. 11861+ regarding + RawImage interface. Replace current code with that in comments + if you use Lazarus from SVN. New RawImage interface will be used by + default after next Lazarus release. + + -- 0.23 Changes/Bug Fixes ----------------------------------- + - Added TImagingGIF. + + -- 0.21 Changes/Bug Fixes ----------------------------------- + - Uses only high level interface now (except for saving options). + - Slightly changed class hierarchy. TImagingGraphic is now only for loading + and base class for savers is new TImagingGraphicForSave. Also + TImagingGraphic is now registered with all supported file formats + by TPicture's format support. + + -- 0.19 Changes/Bug Fixes ----------------------------------- + - added DisplayImage procedures (thanks to Paul Michell, modified) + - removed RegisterTypes and UnRegisterTypes from interface section, + they are called automatically + - added procedures: ConvertImageToBitmap and ConvertBitmapToImage + + -- 0.17 Changes/Bug Fixes ----------------------------------- + - LCL data to bitmap conversion didn´t work in Linux, fixed + - added MNG file format + - added JNG file format + + -- 0.15 Changes/Bug Fixes ----------------------------------- + - made it LCL compatible + - made it CLX compatible + - added all initial stuff +} + +end. + diff --git a/resources/libraries/deskew/Imaging/ImagingDds.pas b/resources/libraries/deskew/Imaging/ImagingDds.pas new file mode 100755 index 0000000..9bcee5a --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingDds.pas @@ -0,0 +1,1145 @@ +{ + Vampyre Imaging Library + by Marek Mauder + http://imaginglib.sourceforge.net + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +{ This unit contains image format loader/saver for DirectDraw Surface images.} +unit ImagingDds; + +{$I ImagingOptions.inc} + +interface + +uses + ImagingTypes, Imaging, ImagingUtility, ImagingFormats; + +type + { Class for loading and saving Microsoft DirectDraw surfaces. + It can load/save all D3D formats which have coresponding + TImageFormat. It supports plain textures, cube textures and + volume textures, all of these can have mipmaps. It can also + load some formats which have no exact TImageFormat, but can be easily + converted to one (bump map formats, etc.). + You can get some information about last loaded DDS file by calling + GetOption with ImagingDDSLoadedXXX options and you can set some + saving options by calling SetOption with ImagingDDSSaveXXX or you can + simply use properties of this class. + Note that when saving cube maps and volumes input image array must contain + at least number of images to build cube/volume based on current + Depth and MipMapCount settings.} + TDDSFileFormat = class(TImageFileFormat) + private + FLoadedCubeMap: LongBool; + FLoadedVolume: LongBool; + FLoadedMipMapCount: LongInt; + FLoadedDepth: LongInt; + FSaveCubeMap: LongBool; + FSaveVolume: LongBool; + FSaveMipMapCount: LongInt; + FSaveDepth: LongInt; + procedure ComputeSubDimensions(Idx, Width, Height, MipMaps, Depth: LongInt; + IsCubeMap, IsVolume: Boolean; var CurWidth, CurHeight: LongInt); + protected + procedure Define; override; + function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; + OnlyFirstLevel: Boolean): Boolean; override; + function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; + Index: LongInt): Boolean; override; + procedure ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); override; + public + function TestFormat(Handle: TImagingHandle): Boolean; override; + procedure CheckOptionsValidity; override; + published + { True if last loaded DDS file was cube map.} + property LoadedCubeMap: LongBool read FLoadedCubeMap write FLoadedCubeMap; + { True if last loaded DDS file was volume texture.} + property LoadedVolume: LongBool read FLoadedVolume write FLoadedVolume; + { Number of mipmap levels of last loaded DDS image.} + property LoadedMipMapCount: LongInt read FLoadedMipMapCount write FLoadedMipMapCount; + { Depth (slices of volume texture or faces of cube map) of last loaded DDS image.} + property LoadedDepth: LongInt read FLoadedDepth write FLoadedDepth; + { True if next DDS file to be saved should be stored as cube map.} + property SaveCubeMap: LongBool read FSaveCubeMap write FSaveCubeMap; + { True if next DDS file to be saved should be stored as volume texture.} + property SaveVolume: LongBool read FSaveVolume write FSaveVolume; + { Sets the number of mipmaps which should be stored in the next saved DDS file. + Only applies to cube maps and volumes, ordinary 2D textures save all + levels present in input.} + property SaveMipMapCount: LongInt read FSaveMipMapCount write FSaveMipMapCount; + { Sets the depth (slices of volume texture or faces of cube map) + of the next saved DDS file.} + property SaveDepth: LongInt read FSaveDepth write FSaveDepth; + end; + +const + { DDS related metadata Ids } + + { DXGI format of textures stored in DDS files with DX10 extension. Type is + Enum (value corresponding to DXGI_FORMAT enum from DX SDK).} + SMetaDdsDxgiFormat = 'DdsDxgiFormat'; + { Number of mipmaps for each main image in DDS file.} + SMetaDdsMipMapCount = 'DdsMipMapCount'; + { Texture array size stored in DDS file (DX10 extension).} + SMetaDdsArraySize = 'DdsArraySize'; + +implementation + +const + SDDSFormatName = 'DirectDraw Surface'; + SDDSMasks = '*.dds'; + DDSSupportedFormats: TImageFormats = [ifR8G8B8, ifA8R8G8B8, ifX8R8G8B8, + ifA1R5G5B5, ifA4R4G4B4, ifX1R5G5B5, ifX4R4G4B4, ifR5G6B5, ifA16B16G16R16, + ifR32F, ifA32B32G32R32F, ifR16F, ifA16B16G16R16F, ifR3G3B2, ifGray8, ifA8Gray8, + ifGray16, ifDXT1, ifDXT3, ifDXT5, ifATI1N, ifATI2N]; + +const + { Four character codes.} + DDSMagic = LongWord(Byte('D') or (Byte('D') shl 8) or (Byte('S') shl 16) or + (Byte(' ') shl 24)); + FOURCC_DXT1 = LongWord(Byte('D') or (Byte('X') shl 8) or (Byte('T') shl 16) or + (Byte('1') shl 24)); + FOURCC_DXT3 = LongWord(Byte('D') or (Byte('X') shl 8) or (Byte('T') shl 16) or + (Byte('3') shl 24)); + FOURCC_DXT5 = LongWord(Byte('D') or (Byte('X') shl 8) or (Byte('T') shl 16) or + (Byte('5') shl 24)); + FOURCC_ATI1 = LongWord(Byte('A') or (Byte('T') shl 8) or (Byte('I') shl 16) or + (Byte('1') shl 24)); + FOURCC_ATI2 = LongWord(Byte('A') or (Byte('T') shl 8) or (Byte('I') shl 16) or + (Byte('2') shl 24)); + FOURCC_DX10 = LongWord(Byte('D') or (Byte('X') shl 8) or (Byte('1') shl 16) or + (Byte('0') shl 24)); + + { Some D3DFORMAT values used in DDS files as FourCC value.} + D3DFMT_A16B16G16R16 = 36; + D3DFMT_R32F = 114; + D3DFMT_A32B32G32R32F = 116; + D3DFMT_R16F = 111; + D3DFMT_A16B16G16R16F = 113; + + { Constans used by TDDSurfaceDesc2.Flags.} + DDSD_CAPS = $00000001; + DDSD_HEIGHT = $00000002; + DDSD_WIDTH = $00000004; + DDSD_PITCH = $00000008; + DDSD_PIXELFORMAT = $00001000; + DDSD_MIPMAPCOUNT = $00020000; + DDSD_LINEARSIZE = $00080000; + DDSD_DEPTH = $00800000; + + { Constans used by TDDSPixelFormat.Flags.} + DDPF_ALPHAPIXELS = $00000001; // used by formats which contain alpha + DDPF_FOURCC = $00000004; // used by DXT and large ARGB formats + DDPF_RGB = $00000040; // used by RGB formats + DDPF_LUMINANCE = $00020000; // used by formats like D3DFMT_L16 + DDPF_BUMPLUMINANCE = $00040000; // used by mixed signed-unsigned formats + DDPF_BUMPDUDV = $00080000; // used by signed formats + + { Constans used by TDDSCaps.Caps1.} + DDSCAPS_COMPLEX = $00000008; + DDSCAPS_TEXTURE = $00001000; + DDSCAPS_MIPMAP = $00400000; + + { Constans used by TDDSCaps.Caps2.} + DDSCAPS2_CUBEMAP = $00000200; + DDSCAPS2_POSITIVEX = $00000400; + DDSCAPS2_NEGATIVEX = $00000800; + DDSCAPS2_POSITIVEY = $00001000; + DDSCAPS2_NEGATIVEY = $00002000; + DDSCAPS2_POSITIVEZ = $00004000; + DDSCAPS2_NEGATIVEZ = $00008000; + DDSCAPS2_VOLUME = $00200000; + + { Flags for TDDSurfaceDesc2.Flags used when saving DDS file.} + DDS_SAVE_FLAGS = DDSD_CAPS or DDSD_PIXELFORMAT or DDSD_WIDTH or + DDSD_HEIGHT or DDSD_LINEARSIZE; + +type + { Stores the pixel format information.} + TDDPixelFormat = packed record + Size: LongWord; // Size of the structure = 32 bytes + Flags: LongWord; // Flags to indicate valid fields + FourCC: LongWord; // Four-char code for compressed textures (DXT) + BitCount: LongWord; // Bits per pixel if uncomp. usually 16,24 or 32 + RedMask: LongWord; // Bit mask for the Red component + GreenMask: LongWord; // Bit mask for the Green component + BlueMask: LongWord; // Bit mask for the Blue component + AlphaMask: LongWord; // Bit mask for the Alpha component + end; + + { Specifies capabilities of surface.} + TDDSCaps = packed record + Caps1: LongWord; // Should always include DDSCAPS_TEXTURE + Caps2: LongWord; // For cubic environment maps + Reserved: array[0..1] of LongWord; // Reserved + end; + + { Record describing DDS file contents.} + TDDSurfaceDesc2 = packed record + Size: LongWord; // Size of the structure = 124 Bytes + Flags: LongWord; // Flags to indicate valid fields + Height: LongWord; // Height of the main image in pixels + Width: LongWord; // Width of the main image in pixels + PitchOrLinearSize: LongWord; // For uncomp formats number of bytes per + // scanline. For comp it is the size in + // bytes of the main image + Depth: LongWord; // Only for volume text depth of the volume + MipMaps: LongInt; // Total number of levels in the mipmap chain + Reserved1: array[0..10] of LongWord; // Reserved + PixelFormat: TDDPixelFormat; // Format of the pixel data + Caps: TDDSCaps; // Capabilities + Reserved2: LongWord; // Reserved + end; + + { DDS file header.} + TDDSFileHeader = packed record + Magic: LongWord; // File format magic + Desc: TDDSurfaceDesc2; // Surface description + end; + + { Resoirce types for D3D 10+ } + TD3D10ResourceDimension = ( + D3D10_RESOURCE_DIMENSION_UNKNOWN = 0, + D3D10_RESOURCE_DIMENSION_BUFFER = 1, + D3D10_RESOURCE_DIMENSION_TEXTURE1D = 2, + D3D10_RESOURCE_DIMENSION_TEXTURE2D = 3, + D3D10_RESOURCE_DIMENSION_TEXTURE3D = 4 + ); + + { Texture formats for D3D 10+ } + TDXGIFormat = ( + DXGI_FORMAT_UNKNOWN = 0, + DXGI_FORMAT_R32G32B32A32_TYPELESS = 1, + DXGI_FORMAT_R32G32B32A32_FLOAT = 2, + DXGI_FORMAT_R32G32B32A32_UINT = 3, + DXGI_FORMAT_R32G32B32A32_SINT = 4, + DXGI_FORMAT_R32G32B32_TYPELESS = 5, + DXGI_FORMAT_R32G32B32_FLOAT = 6, + DXGI_FORMAT_R32G32B32_UINT = 7, + DXGI_FORMAT_R32G32B32_SINT = 8, + DXGI_FORMAT_R16G16B16A16_TYPELESS = 9, + DXGI_FORMAT_R16G16B16A16_FLOAT = 10, + DXGI_FORMAT_R16G16B16A16_UNORM = 11, + DXGI_FORMAT_R16G16B16A16_UINT = 12, + DXGI_FORMAT_R16G16B16A16_SNORM = 13, + DXGI_FORMAT_R16G16B16A16_SINT = 14, + DXGI_FORMAT_R32G32_TYPELESS = 15, + DXGI_FORMAT_R32G32_FLOAT = 16, + DXGI_FORMAT_R32G32_UINT = 17, + DXGI_FORMAT_R32G32_SINT = 18, + DXGI_FORMAT_R32G8X24_TYPELESS = 19, + DXGI_FORMAT_D32_FLOAT_S8X24_UINT = 20, + DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = 21, + DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = 22, + DXGI_FORMAT_R10G10B10A2_TYPELESS = 23, + DXGI_FORMAT_R10G10B10A2_UNORM = 24, + DXGI_FORMAT_R10G10B10A2_UINT = 25, + DXGI_FORMAT_R11G11B10_FLOAT = 26, + DXGI_FORMAT_R8G8B8A8_TYPELESS = 27, + DXGI_FORMAT_R8G8B8A8_UNORM = 28, + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29, + DXGI_FORMAT_R8G8B8A8_UINT = 30, + DXGI_FORMAT_R8G8B8A8_SNORM = 31, + DXGI_FORMAT_R8G8B8A8_SINT = 32, + DXGI_FORMAT_R16G16_TYPELESS = 33, + DXGI_FORMAT_R16G16_FLOAT = 34, + DXGI_FORMAT_R16G16_UNORM = 35, + DXGI_FORMAT_R16G16_UINT = 36, + DXGI_FORMAT_R16G16_SNORM = 37, + DXGI_FORMAT_R16G16_SINT = 38, + DXGI_FORMAT_R32_TYPELESS = 39, + DXGI_FORMAT_D32_FLOAT = 40, + DXGI_FORMAT_R32_FLOAT = 41, + DXGI_FORMAT_R32_UINT = 42, + DXGI_FORMAT_R32_SINT = 43, + DXGI_FORMAT_R24G8_TYPELESS = 44, + DXGI_FORMAT_D24_UNORM_S8_UINT = 45, + DXGI_FORMAT_R24_UNORM_X8_TYPELESS = 46, + DXGI_FORMAT_X24_TYPELESS_G8_UINT = 47, + DXGI_FORMAT_R8G8_TYPELESS = 48, + DXGI_FORMAT_R8G8_UNORM = 49, + DXGI_FORMAT_R8G8_UINT = 50, + DXGI_FORMAT_R8G8_SNORM = 51, + DXGI_FORMAT_R8G8_SINT = 52, + DXGI_FORMAT_R16_TYPELESS = 53, + DXGI_FORMAT_R16_FLOAT = 54, + DXGI_FORMAT_D16_UNORM = 55, + DXGI_FORMAT_R16_UNORM = 56, + DXGI_FORMAT_R16_UINT = 57, + DXGI_FORMAT_R16_SNORM = 58, + DXGI_FORMAT_R16_SINT = 59, + DXGI_FORMAT_R8_TYPELESS = 60, + DXGI_FORMAT_R8_UNORM = 61, + DXGI_FORMAT_R8_UINT = 62, + DXGI_FORMAT_R8_SNORM = 63, + DXGI_FORMAT_R8_SINT = 64, + DXGI_FORMAT_A8_UNORM = 65, + DXGI_FORMAT_R1_UNORM = 66, + DXGI_FORMAT_R9G9B9E5_SHAREDEXP = 67, + DXGI_FORMAT_R8G8_B8G8_UNORM = 68, + DXGI_FORMAT_G8R8_G8B8_UNORM = 69, + DXGI_FORMAT_BC1_TYPELESS = 70, + DXGI_FORMAT_BC1_UNORM = 71, + DXGI_FORMAT_BC1_UNORM_SRGB = 72, + DXGI_FORMAT_BC2_TYPELESS = 73, + DXGI_FORMAT_BC2_UNORM = 74, + DXGI_FORMAT_BC2_UNORM_SRGB = 75, + DXGI_FORMAT_BC3_TYPELESS = 76, + DXGI_FORMAT_BC3_UNORM = 77, + DXGI_FORMAT_BC3_UNORM_SRGB = 78, + DXGI_FORMAT_BC4_TYPELESS = 79, + DXGI_FORMAT_BC4_UNORM = 80, + DXGI_FORMAT_BC4_SNORM = 81, + DXGI_FORMAT_BC5_TYPELESS = 82, + DXGI_FORMAT_BC5_UNORM = 83, + DXGI_FORMAT_BC5_SNORM = 84, + DXGI_FORMAT_B5G6R5_UNORM = 85, + DXGI_FORMAT_B5G5R5A1_UNORM = 86, + DXGI_FORMAT_B8G8R8A8_UNORM = 87, + DXGI_FORMAT_B8G8R8X8_UNORM = 88, + DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89, + DXGI_FORMAT_B8G8R8A8_TYPELESS = 90, + DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 91, + DXGI_FORMAT_B8G8R8X8_TYPELESS = 92, + DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = 93, + DXGI_FORMAT_BC6H_TYPELESS = 94, + DXGI_FORMAT_BC6H_UF16 = 95, + DXGI_FORMAT_BC6H_SF16 = 96, + DXGI_FORMAT_BC7_TYPELESS = 97, + DXGI_FORMAT_BC7_UNORM = 98, + DXGI_FORMAT_BC7_UNORM_SRGB = 99, + DXGI_FORMAT_AYUV = 100, + DXGI_FORMAT_Y410 = 101, + DXGI_FORMAT_Y416 = 102, + DXGI_FORMAT_NV12 = 103, + DXGI_FORMAT_P010 = 104, + DXGI_FORMAT_P016 = 105, + DXGI_FORMAT_420_OPAQUE = 106, + DXGI_FORMAT_YUY2 = 107, + DXGI_FORMAT_Y210 = 108, + DXGI_FORMAT_Y216 = 109, + DXGI_FORMAT_NV11 = 110, + DXGI_FORMAT_AI44 = 111, + DXGI_FORMAT_IA44 = 112, + DXGI_FORMAT_P8 = 113, + DXGI_FORMAT_A8P8 = 114, + DXGI_FORMAT_B4G4R4A4_UNORM = 115 + ); + + { DX10 extension header for DDS file format } + TDX10Header = packed record + DXGIFormat: TDXGIFormat; + ResourceDimension: TD3D10ResourceDimension; + MiscFlags: LongWord; + ArraySize: LongWord; + Reserved: LongWord; + end; + +{ TDDSFileFormat class implementation } + +procedure TDDSFileFormat.Define; +begin + inherited; + FName := SDDSFormatName; + FFeatures := [ffLoad, ffSave, ffMultiImage]; + FSupportedFormats := DDSSupportedFormats; + + FSaveCubeMap := False; + FSaveVolume := False; + FSaveMipMapCount := 1; + FSaveDepth := 1; + + AddMasks(SDDSMasks); + + RegisterOption(ImagingDDSLoadedCubeMap, @FLoadedCubeMap); + RegisterOption(ImagingDDSLoadedVolume, @FLoadedVolume); + RegisterOption(ImagingDDSLoadedMipMapCount, @FLoadedMipMapCount); + RegisterOption(ImagingDDSLoadedDepth, @FLoadedDepth); + RegisterOption(ImagingDDSSaveCubeMap, @FSaveCubeMap); + RegisterOption(ImagingDDSSaveVolume, @FSaveVolume); + RegisterOption(ImagingDDSSaveMipMapCount, @FSaveMipMapCount); + RegisterOption(ImagingDDSSaveDepth, @FSaveDepth); +end; + +procedure TDDSFileFormat.CheckOptionsValidity; +begin + if FSaveCubeMap then + FSaveVolume := False; + if FSaveVolume then + FSaveCubeMap := False; + if FSaveDepth < 1 then + FSaveDepth := 1; + if FSaveMipMapCount < 1 then + FSaveMipMapCount := 1; +end; + +procedure TDDSFileFormat.ComputeSubDimensions(Idx, Width, Height, MipMaps, Depth: LongInt; + IsCubeMap, IsVolume: Boolean; var CurWidth, CurHeight: LongInt); +var + I, Last, Shift: LongInt; +begin + CurWidth := Width; + CurHeight := Height; + if MipMaps > 1 then + begin + if not IsVolume then + begin + if IsCubeMap then + begin + // Cube maps are stored like this + // Face 0 mimap 0 + // Face 0 mipmap 1 + // ... + // Face 1 mipmap 0 + // Face 1 mipmap 1 + // ... + + // Modify index so later in for loop we iterate less times + Idx := Idx - ((Idx div MipMaps) * MipMaps); + end; + for I := 0 to Idx - 1 do + begin + CurWidth := ClampInt(CurWidth shr 1, 1, CurWidth); + CurHeight := ClampInt(CurHeight shr 1, 1, CurHeight); + end; + end + else + begin + // Volume textures are stored in DDS files like this: + // Slice 0 mipmap 0 + // Slice 1 mipmap 0 + // Slice 2 mipmap 0 + // Slice 3 mipmap 0 + // Slice 0 mipmap 1 + // Slice 1 mipmap 1 + // Slice 0 mipmap 2 + // Slice 0 mipmap 3 ... + Shift := 0; + Last := Depth; + while Idx > Last - 1 do + begin + CurWidth := ClampInt(CurWidth shr 1, 1, CurWidth); + CurHeight := ClampInt(CurHeight shr 1, 1, CurHeight); + if (CurWidth = 1) and (CurHeight = 1) then + Break; + Inc(Shift); + Inc(Last, ClampInt(Depth shr Shift, 1, Depth)); + end; + end; + end; +end; + +function TDDSFileFormat.LoadData(Handle: TImagingHandle; + var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; +var + Hdr: TDDSFileHeader; + HdrDX10: TDX10Header; + SrcFormat: TImageFormat; + FmtInfo: TImageFormatInfo; + NeedsSwapChannels: Boolean; + CurrentWidth, CurrentHeight, ImageCount, LoadSize, I, + PitchOrLinear, MainImageLinearSize: Integer; + Data: PByte; + UseAsPitch: Boolean; + UseAsLinear: Boolean; + + function MasksEqual(const DDPF: TDDPixelFormat; PF: PPixelFormatInfo): Boolean; + begin + Result := (DDPF.AlphaMask = PF.ABitMask) and + (DDPF.RedMask = PF.RBitMask) and (DDPF.GreenMask = PF.GBitMask) and + (DDPF.BlueMask = PF.BBitMask); + end; + + function FindFourCCFormat(FourCC: LongWord): TImageFormat; + begin + // Handle FourCC and large ARGB formats + case FourCC of + D3DFMT_A16B16G16R16: Result := ifA16B16G16R16; + D3DFMT_R32F: Result := ifR32F; + D3DFMT_A32B32G32R32F: Result := ifA32B32G32R32F; + D3DFMT_R16F: Result := ifR16F; + D3DFMT_A16B16G16R16F: Result := ifA16B16G16R16F; + FOURCC_DXT1: Result := ifDXT1; + FOURCC_DXT3: Result := ifDXT3; + FOURCC_DXT5: Result := ifDXT5; + FOURCC_ATI1: Result := ifATI1N; + FOURCC_ATI2: Result := ifATI2N; + else + Result := ifUnknown; + end; + end; + + function FindDX10Format(DXGIFormat: TDXGIFormat; var NeedsSwapChannels: Boolean): TImageFormat; + begin + Result := ifUnknown; + NeedsSwapChannels := False; + + case DXGIFormat of + DXGI_FORMAT_UNKNOWN: ; + DXGI_FORMAT_R32G32B32A32_TYPELESS, DXGI_FORMAT_R32G32B32A32_FLOAT: + Result := ifA32B32G32R32F; + DXGI_FORMAT_R32G32B32A32_UINT: ; + DXGI_FORMAT_R32G32B32A32_SINT: ; + DXGI_FORMAT_R32G32B32_TYPELESS, DXGI_FORMAT_R32G32B32_FLOAT: + Result := ifB32G32R32F; + DXGI_FORMAT_R32G32B32_UINT: ; + DXGI_FORMAT_R32G32B32_SINT: ; + DXGI_FORMAT_R16G16B16A16_FLOAT: + Result := ifA16B16G16R16F; + DXGI_FORMAT_R16G16B16A16_TYPELESS, DXGI_FORMAT_R16G16B16A16_UNORM, + DXGI_FORMAT_R16G16B16A16_UINT, DXGI_FORMAT_R16G16B16A16_SNORM, + DXGI_FORMAT_R16G16B16A16_SINT: + Result := ifA16B16G16R16; + DXGI_FORMAT_R32G32_TYPELESS: ; + DXGI_FORMAT_R32G32_FLOAT: ; + DXGI_FORMAT_R32G32_UINT: ; + DXGI_FORMAT_R32G32_SINT: ; + DXGI_FORMAT_R32G8X24_TYPELESS: ; + DXGI_FORMAT_D32_FLOAT_S8X24_UINT: ; + DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: ; + DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: ; + DXGI_FORMAT_R10G10B10A2_TYPELESS: ; + DXGI_FORMAT_R10G10B10A2_UNORM: ; + DXGI_FORMAT_R10G10B10A2_UINT: ; + DXGI_FORMAT_R11G11B10_FLOAT: ; + DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UINT, DXGI_FORMAT_R8G8B8A8_SNORM,DXGI_FORMAT_R8G8B8A8_SINT, + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + begin + Result := ifA8R8G8B8; + NeedsSwapChannels := True; + end; + DXGI_FORMAT_R16G16_TYPELESS: ; + DXGI_FORMAT_R16G16_FLOAT: ; + DXGI_FORMAT_R16G16_UNORM: ; + DXGI_FORMAT_R16G16_UINT: ; + DXGI_FORMAT_R16G16_SNORM: ; + DXGI_FORMAT_R16G16_SINT: ; + DXGI_FORMAT_R32_TYPELESS, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_SINT: + Result := ifGray32; + DXGI_FORMAT_D32_FLOAT, DXGI_FORMAT_R32_FLOAT: + Result := ifR32F; + DXGI_FORMAT_R24G8_TYPELESS: ; + DXGI_FORMAT_D24_UNORM_S8_UINT: ; + DXGI_FORMAT_R24_UNORM_X8_TYPELESS: ; + DXGI_FORMAT_X24_TYPELESS_G8_UINT: ; + DXGI_FORMAT_R8G8_TYPELESS, DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_R8G8_UINT, + DXGI_FORMAT_R8G8_SNORM, DXGI_FORMAT_R8G8_SINT: + Result := ifA8Gray8; + DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_D16_UNORM, DXGI_FORMAT_R16_UNORM, + DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_SNORM, DXGI_FORMAT_R16_SINT: + Result := ifGray16; + DXGI_FORMAT_R16_FLOAT: + Result := ifR16F; + DXGI_FORMAT_R8_TYPELESS, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8_UINT, + DXGI_FORMAT_R8_SNORM, DXGI_FORMAT_R8_SINT, DXGI_FORMAT_A8_UNORM: + Result := ifGray8; + DXGI_FORMAT_R1_UNORM: ; + DXGI_FORMAT_R9G9B9E5_SHAREDEXP: ; + DXGI_FORMAT_R8G8_B8G8_UNORM: ; + DXGI_FORMAT_G8R8_G8B8_UNORM: ; + DXGI_FORMAT_BC1_TYPELESS, DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_BC1_UNORM_SRGB: + Result := ifDXT1; + DXGI_FORMAT_BC2_TYPELESS, DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_BC2_UNORM_SRGB: + Result := ifDXT3; + DXGI_FORMAT_BC3_TYPELESS, DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_BC3_UNORM_SRGB: + Result := ifDXT5; + DXGI_FORMAT_BC4_TYPELESS, DXGI_FORMAT_BC4_UNORM, DXGI_FORMAT_BC4_SNORM: + Result := ifATI1N; + DXGI_FORMAT_BC5_TYPELESS, DXGI_FORMAT_BC5_UNORM, DXGI_FORMAT_BC5_SNORM: + Result := ifATI2N; + DXGI_FORMAT_B5G6R5_UNORM: + Result := ifR5G6B5; + DXGI_FORMAT_B5G5R5A1_UNORM: + Result := ifA1R5G5B5; + DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_TYPELESS: + Result := ifA8R8G8B8; + DXGI_FORMAT_B8G8R8X8_UNORM, DXGI_FORMAT_B8G8R8X8_TYPELESS: + Result := ifX8R8G8B8; + DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: ; + DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: ; + DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: ; + DXGI_FORMAT_BC6H_TYPELESS: ; + DXGI_FORMAT_BC6H_UF16: ; + DXGI_FORMAT_BC6H_SF16: ; + DXGI_FORMAT_BC7_TYPELESS: ; + DXGI_FORMAT_BC7_UNORM: ; + DXGI_FORMAT_BC7_UNORM_SRGB: ; + DXGI_FORMAT_P8: ; + DXGI_FORMAT_A8P8: ; + DXGI_FORMAT_B4G4R4A4_UNORM: + Result := ifA4R4G4B4; + end; + end; + +begin + Result := False; + ImageCount := 1; + FLoadedMipMapCount := 1; + FLoadedDepth := 1; + FLoadedVolume := False; + FLoadedCubeMap := False; + ZeroMemory(@HdrDX10, SizeOf(HdrDX10)); + + with GetIO, Hdr, Hdr.Desc.PixelFormat do + begin + Read(Handle, @Hdr, SizeOf(Hdr)); + + SrcFormat := ifUnknown; + NeedsSwapChannels := False; + + // Get image data format + if (Flags and DDPF_FOURCC) = DDPF_FOURCC then + begin + if FourCC = FOURCC_DX10 then + begin + Read(Handle, @HdrDX10, SizeOf(HdrDX10)); + SrcFormat := FindDX10Format(HdrDX10.DXGIFormat, NeedsSwapChannels); + FMetadata.SetMetaItem(SMetaDdsDxgiFormat, HdrDX10.DXGIFormat); + FMetadata.SetMetaItem(SMetaDdsArraySize, HdrDX10.ArraySize); + end + else + SrcFormat := FindFourCCFormat(FourCC); + end + else if (Flags and DDPF_RGB) = DDPF_RGB then + begin + // Handle RGB formats + if (Flags and DDPF_ALPHAPIXELS) = DDPF_ALPHAPIXELS then + begin + // Handle RGB with alpha formats + case BitCount of + 16: + begin + if MasksEqual(Desc.PixelFormat, GetFormatInfo(ifA4R4G4B4).PixelFormat) then + SrcFormat := ifA4R4G4B4; + if MasksEqual(Desc.PixelFormat, GetFormatInfo(ifA1R5G5B5).PixelFormat) then + SrcFormat := ifA1R5G5B5; + end; + 32: + begin + SrcFormat := ifA8R8G8B8; + if BlueMask = $00FF0000 then + NeedsSwapChannels := True; + end; + end; + end + else + begin + // Handle RGB without alpha formats + case BitCount of + 8: + if MasksEqual(Desc.PixelFormat, + GetFormatInfo(ifR3G3B2).PixelFormat) then + SrcFormat := ifR3G3B2; + 16: + begin + if MasksEqual(Desc.PixelFormat, + GetFormatInfo(ifX4R4G4B4).PixelFormat) then + SrcFormat := ifX4R4G4B4; + if MasksEqual(Desc.PixelFormat, + GetFormatInfo(ifX1R5G5B5).PixelFormat) then + SrcFormat := ifX1R5G5B5; + if MasksEqual(Desc.PixelFormat, + GetFormatInfo(ifR5G6B5).PixelFormat) then + SrcFormat := ifR5G6B5; + end; + 24: SrcFormat := ifR8G8B8; + 32: + begin + SrcFormat := ifX8R8G8B8; + if BlueMask = $00FF0000 then + NeedsSwapChannels := True; + end; + end; + end; + end + else if (Flags and DDPF_LUMINANCE) = DDPF_LUMINANCE then + begin + // Handle luminance formats + if (Flags and DDPF_ALPHAPIXELS) = DDPF_ALPHAPIXELS then + begin + // Handle luminance with alpha formats + if BitCount = 16 then + SrcFormat := ifA8Gray8; + end + else + begin + // Handle luminance without alpha formats + case BitCount of + 8: SrcFormat := ifGray8; + 16: SrcFormat := ifGray16; + end; + end; + end + else if (Flags and DDPF_BUMPLUMINANCE) = DDPF_BUMPLUMINANCE then + begin + // Handle mixed bump-luminance formats like D3DFMT_X8L8V8U8 + case BitCount of + 32: + if BlueMask = $00FF0000 then + begin + SrcFormat := ifX8R8G8B8; // D3DFMT_X8L8V8U8 + NeedsSwapChannels := True; + end; + end; + end + else if (Flags and DDPF_BUMPDUDV) = DDPF_BUMPDUDV then + begin + // Handle bumpmap formats like D3DFMT_Q8W8V8U8 + case BitCount of + 16: SrcFormat := ifA8Gray8; // D3DFMT_V8U8 + 32: + if AlphaMask = $FF000000 then + begin + SrcFormat := ifA8R8G8B8; // D3DFMT_Q8W8V8U8 + NeedsSwapChannels := True; + end; + 64: SrcFormat := ifA16B16G16R16; // D3DFMT_Q16W16V16U16 + end; + end; + + // If DDS format is not supported we will exit + if SrcFormat = ifUnknown then + Exit; + + // File contains mipmaps for each subimage. + { Some DDS writers ignore setting proper Caps and Flags so + this check is not usable: + if ((Desc.Caps.Caps1 and DDSCAPS_MIPMAP) = DDSCAPS_MIPMAP) and + ((Desc.Flags and DDSD_MIPMAPCOUNT) = DDSD_MIPMAPCOUNT) then} + if Desc.MipMaps > 1 then + begin + FLoadedMipMapCount := Desc.MipMaps; + FMetadata.SetMetaItem(SMetaDdsMipMapCount, Desc.MipMaps); + ImageCount := Desc.MipMaps; + end; + + // File stores volume texture + if ((Desc.Caps.Caps2 and DDSCAPS2_VOLUME) = DDSCAPS2_VOLUME) and + ((Desc.Flags and DDSD_DEPTH) = DDSD_DEPTH) then + begin + FLoadedVolume := True; + FLoadedDepth := Desc.Depth; + ImageCount := GetVolumeLevelCount(Desc.Depth, ImageCount); + end; + + // File stores cube texture + if (Desc.Caps.Caps2 and DDSCAPS2_CUBEMAP) = DDSCAPS2_CUBEMAP then + begin + FLoadedCubeMap := True; + I := 0; + if (Desc.Caps.Caps2 and DDSCAPS2_POSITIVEX) = DDSCAPS2_POSITIVEX then Inc(I); + if (Desc.Caps.Caps2 and DDSCAPS2_POSITIVEY) = DDSCAPS2_POSITIVEY then Inc(I); + if (Desc.Caps.Caps2 and DDSCAPS2_POSITIVEZ) = DDSCAPS2_POSITIVEZ then Inc(I); + if (Desc.Caps.Caps2 and DDSCAPS2_NEGATIVEX) = DDSCAPS2_NEGATIVEX then Inc(I); + if (Desc.Caps.Caps2 and DDSCAPS2_NEGATIVEY) = DDSCAPS2_NEGATIVEY then Inc(I); + if (Desc.Caps.Caps2 and DDSCAPS2_NEGATIVEZ) = DDSCAPS2_NEGATIVEZ then Inc(I); + FLoadedDepth := I; + ImageCount := ImageCount * I; + end; + + // Allocate and load all images in file + FmtInfo := GetFormatInfo(SrcFormat); + SetLength(Images, ImageCount); + + // Compute the pitch or get if from file if present + UseAsPitch := (Desc.Flags and DDSD_PITCH) = DDSD_PITCH; + UseAsLinear := (Desc.Flags and DDSD_LINEARSIZE) = DDSD_LINEARSIZE; + // Use linear as default if none is set + if not UseAsPitch and not UseAsLinear then + UseAsLinear := True; + // Main image pitch or linear size + PitchOrLinear := Desc.PitchOrLinearSize; + + // Check: some writers just write garbage to pitch/linear size fields and flags + MainImageLinearSize := FmtInfo.GetPixelsSize(SrcFormat, Desc.Width, Desc.Height); + if UseAsLinear and ((PitchOrLinear < MainImageLinearSize) or + (PitchOrLinear * Integer(Desc.Height) = MainImageLinearSize)) then + begin + // Explicitly set linear size + PitchOrLinear := MainImageLinearSize; + end; + + for I := 0 to ImageCount - 1 do + begin + // Compute dimensions of surrent subimage based on texture type and + // number of mipmaps + ComputeSubDimensions(I, Desc.Width, Desc.Height, Desc.MipMaps, Desc.Depth, + FLoadedCubeMap, FLoadedVolume, CurrentWidth, CurrentHeight); + NewImage(CurrentWidth, CurrentHeight, SrcFormat, Images[I]); + + if (I > 0) or (PitchOrLinear = 0) then + begin + // Compute pitch or linear size for mipmap levels, or even for main image + // since some formats do not fill pitch nor size + if UseAsLinear then + PitchOrLinear := FmtInfo.GetPixelsSize(SrcFormat, CurrentWidth, CurrentHeight) + else + PitchOrLinear := (CurrentWidth * FmtInfo.BytesPerPixel + 3) div 4 * 4; // must be DWORD aligned + end; + + if UseAsLinear then + LoadSize := PitchOrLinear + else + LoadSize := CurrentHeight * PitchOrLinear; + + if UseAsLinear or (LoadSize = Images[I].Size) then + begin + // If DDS does not use Pitch we can simply copy data + Read(Handle, Images[I].Bits, LoadSize) + end + else + begin + // If DDS uses Pitch we must load aligned scanlines + // and then remove padding + GetMem(Data, LoadSize); + try + Read(Handle, Data, LoadSize); + RemovePadBytes(Data, Images[I].Bits, CurrentWidth, CurrentHeight, + FmtInfo.BytesPerPixel, PitchOrLinear); + finally + FreeMem(Data); + end; + end; + + if NeedsSwapChannels then + SwapChannels(Images[I], ChannelRed, ChannelBlue); + end; + Result := True; + end; +end; + +function TDDSFileFormat.SaveData(Handle: TImagingHandle; + const Images: TDynImageDataArray; Index: LongInt): Boolean; +var + Hdr: TDDSFileHeader; + MainImage, ImageToSave: TImageData; + I, MainIdx, Len, ImageCount: LongInt; + J: LongWord; + FmtInfo: TImageFormatInfo; + MustBeFreed: Boolean; + Is2DTexture, IsCubeMap, IsVolume: Boolean; + MipMapCount, CurrentWidth, CurrentHeight: LongInt; + NeedsResize: Boolean; + NeedsConvert: Boolean; +begin + Result := False; + FillChar(Hdr, Sizeof(Hdr), 0); + + MainIdx := FFirstIdx; + Len := FLastIdx - MainIdx + 1; + // Some DDS saving rules: + // 2D textures: Len is used as mipmap count (FSaveMipMapCount not used!). + // Cube maps: FSaveDepth * FSaveMipMapCount images are used, if Len is + // smaller than this file is saved as regular 2D texture. + // Volume maps: GetVolumeLevelCount(FSaveDepth, FSaveMipMapCount) images are + // used, if Len is smaller than this file is + // saved as regular 2D texture. + + IsCubeMap := FSaveCubeMap; + IsVolume := FSaveVolume; + MipMapCount := FSaveMipMapCount; + + if IsCubeMap then + begin + // Check if we have enough images on Input to save cube map + if Len < FSaveDepth * FSaveMipMapCount then + IsCubeMap := False; + end + else if IsVolume then + begin + // Check if we have enough images on Input to save volume texture + if Len < GetVolumeLevelCount(FSaveDepth, FSaveMipMapCount) then + IsVolume := False; + end; + + Is2DTexture := not IsCubeMap and not IsVolume; + if Is2DTexture then + begin + // Get number of mipmaps used with 2D texture + MipMapCount := Min(Len, GetNumMipMapLevels(Images[MainIdx].Width, Images[MainIdx].Height)); + end; + + // we create compatible main image and fill headers + if MakeCompatible(Images[MainIdx], MainImage, MustBeFreed) then + with GetIO, MainImage, Hdr do + try + FmtInfo := GetFormatInfo(Format); + Magic := DDSMagic; + Desc.Size := SizeOf(Desc); + Desc.Width := Width; + Desc.Height := Height; + Desc.Flags := DDS_SAVE_FLAGS; + Desc.Caps.Caps1 := DDSCAPS_TEXTURE; + Desc.PixelFormat.Size := SizeOf(Desc.PixelFormat); + Desc.PitchOrLinearSize := MainImage.Size; + ImageCount := MipMapCount; + + if MipMapCount > 1 then + begin + // Set proper flags if we have some mipmaps to be saved + Desc.Flags := Desc.Flags or DDSD_MIPMAPCOUNT; + Desc.Caps.Caps1 := Desc.Caps.Caps1 or DDSCAPS_MIPMAP or DDSCAPS_COMPLEX; + Desc.MipMaps := MipMapCount; + end; + + if IsCubeMap then + begin + // Set proper cube map flags - number of stored faces is taken + // from FSaveDepth + Desc.Caps.Caps1 := Desc.Caps.Caps1 or DDSCAPS_COMPLEX; + Desc.Caps.Caps2 := Desc.Caps.Caps2 or DDSCAPS2_CUBEMAP; + J := DDSCAPS2_POSITIVEX; + for I := 0 to FSaveDepth - 1 do + begin + Desc.Caps.Caps2 := Desc.Caps.Caps2 or J; + J := J shl 1; + end; + ImageCount := FSaveDepth * FSaveMipMapCount; + end + else if IsVolume then + begin + // Set proper flags for volume texture + Desc.Flags := Desc.Flags or DDSD_DEPTH; + Desc.Caps.Caps1 := Desc.Caps.Caps1 or DDSCAPS_COMPLEX; + Desc.Caps.Caps2 := Desc.Caps.Caps2 or DDSCAPS2_VOLUME; + Desc.Depth := FSaveDepth; + ImageCount := GetVolumeLevelCount(FSaveDepth, FSaveMipMapCount); + end; + + // Now we set DDS pixel format for main image + if FmtInfo.IsSpecial or FmtInfo.IsFloatingPoint or + (FmtInfo.BytesPerPixel > 4) then + begin + Desc.PixelFormat.Flags := DDPF_FOURCC; + case Format of + ifA16B16G16R16: Desc.PixelFormat.FourCC := D3DFMT_A16B16G16R16; + ifR32F: Desc.PixelFormat.FourCC := D3DFMT_R32F; + ifA32B32G32R32F: Desc.PixelFormat.FourCC := D3DFMT_A32B32G32R32F; + ifR16F: Desc.PixelFormat.FourCC := D3DFMT_R16F; + ifA16B16G16R16F: Desc.PixelFormat.FourCC := D3DFMT_A16B16G16R16F; + ifDXT1: Desc.PixelFormat.FourCC := FOURCC_DXT1; + ifDXT3: Desc.PixelFormat.FourCC := FOURCC_DXT3; + ifDXT5: Desc.PixelFormat.FourCC := FOURCC_DXT5; + ifATI1N: Desc.PixelFormat.FourCC := FOURCC_ATI1; + ifATI2N: Desc.PixelFormat.FourCC := FOURCC_ATI2; + end; + end + else if FmtInfo.HasGrayChannel then + begin + Desc.PixelFormat.Flags := DDPF_LUMINANCE; + Desc.PixelFormat.BitCount := FmtInfo.BytesPerPixel * 8; + case Format of + ifGray8: Desc.PixelFormat.RedMask := 255; + ifGray16: Desc.PixelFormat.RedMask := 65535; + ifA8Gray8: + begin + Desc.PixelFormat.Flags := Desc.PixelFormat.Flags or DDPF_ALPHAPIXELS; + Desc.PixelFormat.RedMask := 255; + Desc.PixelFormat.AlphaMask := 65280; + end; + end; + end + else + begin + Desc.PixelFormat.Flags := DDPF_RGB; + Desc.PixelFormat.BitCount := FmtInfo.BytesPerPixel * 8; + if FmtInfo.HasAlphaChannel then + begin + Desc.PixelFormat.Flags := Desc.PixelFormat.Flags or DDPF_ALPHAPIXELS; + Desc.PixelFormat.AlphaMask := $FF000000; + end; + if FmtInfo.BytesPerPixel > 2 then + begin + Desc.PixelFormat.RedMask := $00FF0000; + Desc.PixelFormat.GreenMask := $0000FF00; + Desc.PixelFormat.BlueMask := $000000FF; + end + else + begin + Desc.PixelFormat.AlphaMask := FmtInfo.PixelFormat.ABitMask; + Desc.PixelFormat.RedMask := FmtInfo.PixelFormat.RBitMask; + Desc.PixelFormat.GreenMask := FmtInfo.PixelFormat.GBitMask; + Desc.PixelFormat.BlueMask := FmtInfo.PixelFormat.BBitMask; + end; + end; + + // Header and main image are written to output + Write(Handle, @Hdr, SizeOf(Hdr)); + Write(Handle, MainImage.Bits, MainImage.Size); + + // Write the rest of the images and convert them to + // the same format as main image if necessary and ensure proper mipmap + // simensions too. + for I := MainIdx + 1 to MainIdx + ImageCount - 1 do + begin + // Get proper dimensions for this level + ComputeSubDimensions(I, Desc.Width, Desc.Height, Desc.MipMaps, Desc.Depth, + IsCubeMap, IsVolume, CurrentWidth, CurrentHeight); + + // Check if input image for this level has the right size and format + NeedsResize := not ((Images[I].Width = CurrentWidth) and (Images[I].Height = CurrentHeight)); + NeedsConvert := not (Images[I].Format = Format); + + if NeedsResize or NeedsConvert then + begin + // Input image must be resized or converted to different format + // to become valid mipmap level + InitImage(ImageToSave); + CloneImage(Images[I], ImageToSave); + if NeedsConvert then + ConvertImage(ImageToSave, Format); + if NeedsResize then + ResizeImage(ImageToSave, CurrentWidth, CurrentHeight, rfBilinear); + end + else + // Input image can be used without any changes + ImageToSave := Images[I]; + + // Write level data and release temp image if necessary + Write(Handle, ImageToSave.Bits, ImageToSave.Size); + if Images[I].Bits <> ImageToSave.Bits then + FreeImage(ImageToSave); + end; + + Result := True; + finally + if MustBeFreed then + FreeImage(MainImage); + end; +end; + +procedure TDDSFileFormat.ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); +var + ConvFormat: TImageFormat; +begin + if Info.IsIndexed or Info.IsSpecial then + // convert indexed and unsupported special formatd to A8R8G8B8 + ConvFormat := ifA8R8G8B8 + else if Info.IsFloatingPoint then + begin + if Info.Format = ifA16R16G16B16F then + // only swap channels here + ConvFormat := ifA16B16G16R16F + else + // convert other floating point formats to A32B32G32R32F + ConvFormat := ifA32B32G32R32F + end + else if Info.HasGrayChannel then + begin + if Info.HasAlphaChannel then + // convert grayscale with alpha to A8Gray8 + ConvFormat := ifA8Gray8 + else if Info.BytesPerPixel = 1 then + // convert 8bit grayscale to Gray8 + ConvFormat := ifGray8 + else + // convert 16-64bit grayscales to Gray16 + ConvFormat := ifGray16; + end + else if Info.BytesPerPixel > 4 then + ConvFormat := ifA16B16G16R16 + else if Info.HasAlphaChannel then + // convert the other images with alpha channel to A8R8G8B8 + ConvFormat := ifA8R8G8B8 + else + // convert the other formats to X8R8G8B8 + ConvFormat := ifX8R8G8B8; + + ConvertImage(Image, ConvFormat); +end; + +function TDDSFileFormat.TestFormat(Handle: TImagingHandle): Boolean; +var + Hdr: TDDSFileHeader; + ReadCount: LongInt; +begin + Result := False; + if Handle <> nil then + with GetIO do + begin + ReadCount := Read(Handle, @Hdr, SizeOf(Hdr)); + Seek(Handle, -ReadCount, smFromCurrent); + Result := (Hdr.Magic = DDSMagic) and (ReadCount = SizeOf(Hdr)) and + ((Hdr.Desc.Caps.Caps1 and DDSCAPS_TEXTURE) = DDSCAPS_TEXTURE); + end; +end; + +initialization + RegisterImageFileFormat(TDDSFileFormat); + +{ + File Notes: + + -- TODOS ---------------------------------------------------- + - nothing now + + -- 0.77.1 ---------------------------------------------------- + - Texture and D3D specific info stored in DDS is now available as metadata + (loading). + - Added support for loading DDS files with DX10 extension + (http://msdn.microsoft.com/en-us/library/windows/desktop/bb943991(v=vs.85).aspx) + and few compatibility fixes. + + -- 0.25.0 Changes/Bug Fixes --------------------------------- + - Added support for 3Dc ATI1/2 formats. + + -- 0.23 Changes/Bug Fixes ----------------------------------- + - Saved DDS with mipmaps now correctly defineds COMPLEX flag. + - Fixed loading of RGB DDS files that use pitch and have mipmaps - + mipmaps were loaded wrongly. + + -- 0.21 Changes/Bug Fixes ----------------------------------- + - Changed saving behaviour a bit: mipmaps are inlcuded automatically for + 2D textures if input image array has more than 1 image (no need to + set SaveMipMapCount manually). + - Mipmap levels are now saved with proper dimensions when saving DDS files. + - Made some changes to not be so strict when loading DDS files. + Many programs seem to save them in non-standard format + (by MS DDS File Reference). + - Added missing ifX8R8G8B8 to SupportedFormats, MakeCompatible failed + when image was converted to this format (inside). + - MakeCompatible method moved to base class, put ConvertToSupported here. + GetSupportedFormats removed, it is now set in constructor. + - Fixed bug that sometimes saved non-standard DDS files and another + one that caused crash when these files were loaded. + - Changed extensions to filename masks. + - Changed SaveData, LoadData, and MakeCompatible methods according + to changes in base class in Imaging unit. + + -- 0.19 Changes/Bug Fixes ----------------------------------- + - added support for half-float image formats + - change in LoadData to allow support for more images + in one stream loading + + -- 0.17 Changes/Bug Fixes ----------------------------------- + - fixed bug in TestFormat which does not recognize many DDS files + - changed pitch/linearsize handling in DDS loading code to + load DDS files produced by NVidia's Photoshop plugin +} + +end. + diff --git a/resources/libraries/deskew/Imaging/ImagingExtras.pas b/resources/libraries/deskew/Imaging/ImagingExtras.pas new file mode 100755 index 0000000..f88e7df --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingExtras.pas @@ -0,0 +1,148 @@ +{ + Vampyre Imaging Library + by Marek Mauder + http://imaginglib.sourceforge.net + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +{ This is helper unit that registers all image file formats in Extras package + to Imaging core loading and saving functions. Just put this unit in your uses + clause instead of adding every unit that provides new file format support. + Also new constants for SetOption/GetOption functions for new file formats + are located here.} +unit ImagingExtras; + +{$I ImagingOptions.inc} + +{$DEFINE DONT_LINK_JPEG2000} // link support for JPEG2000 images +//{$DEFINE DONT_LINK_TIFF} // link support for TIFF images +//{$DEFINE DONT_LINK_PSD} // link support for PSD images +{$DEFINE DONT_LINK_PCX} // link support for PCX images +{$DEFINE DONT_LINK_XPM} // link support for XPM images + +{$IFNDEF FULL_FEATURE_SET} + {$DEFINE DONT_LINK_ELDER} // link support for Elder Imagery images +{$ENDIF} + +{$IF not ( + (Defined(DCC) and Defined(CPUX86) and not Defined(MACOS)) or + (Defined(FPC) and not Defined(MSDOS) and + ((Defined(CPUX86) and (Defined(LINUX) or Defined(WIN32) or Defined(MACOS)) or + (Defined(CPUX64) and Defined(LINUX))))) + )} + // JPEG2000 only for 32bit Windows/Linux/OSX and for 64bit Unix with FPC + {$DEFINE DONT_LINK_JPEG2000} +{$IFEND} + +interface + +const + { Those are new options for GetOption/SetOption interface. } + + { Controls JPEG 2000 lossy compression quality. It is number in range 1..100. + 1 means small/ugly file, 100 means large/nice file. Default is 80.} + ImagingJpeg2000Quality = 55; + { Controls whether JPEG 2000 image is saved with full file headers or just + as code stream. Default value is False (0).} + ImagingJpeg2000CodeStreamOnly = 56; + { Specifies JPEG 2000 image compression type. If True (1), saved JPEG 2000 files + will be losslessly compressed. Otherwise lossy compression is used. + Default value is False (0).} + ImagingJpeg2000LosslessCompression = 57; + { Specifies compression scheme used when saving TIFF images. Supported values + are 0 (Uncompressed), 1 (LZW), 2 (PackBits RLE), 3 (Deflate - ZLib), 4 (JPEG), + 5 (CCITT Group 4 fax encoding - for binary images only). + Default is 1 (LZW). Note that not all images can be stored with + JPEG compression - these images will be saved with default compression if + JPEG is set.} + ImagingTiffCompression = 65; + { Controls compression quality when selected TIFF compression is Jpeg. + It is number in range 1..100. 1 means small/ugly file, + 100 means large/nice file. Accessible trough ImagingTiffJpegQuality option.} + ImagingTiffJpegQuality = 66; + { If enabled image data is saved as layer of PSD file. This is required + to get proper transparency when opened in Photoshop for images with + alpha data (will be opened with one layer, RGB color channels, and transparency). + If you don't need this Photoshop compatibility turn this option off as you'll get + smaller file (will be opened in PS as background raster with RGBA channels). + Default value is True (1). } + ImagingPSDSaveAsLayer = 70; + +implementation + +uses +{$IFNDEF DONT_LINK_FILE_FORMATS} +{$IFNDEF DONT_LINK_JPEG2000} + ImagingJpeg2000, +{$ENDIF} +{$IFNDEF DONT_LINK_TIFF} + ImagingTiff, +{$ENDIF} +{$IFNDEF DONT_LINK_PSD} + ImagingPsd, +{$ENDIF} +{$IFNDEF DONT_LINK_PCX} + ImagingPcx, +{$ENDIF} +{$IFNDEF DONT_LINK_XPM} + ImagingXpm, +{$ENDIF} +{$IFNDEF DONT_LINK_ELDER} + ElderImagery, +{$ENDIF} +{$ENDIF} + Imaging; + +{ + File Notes: + + -- TODOS ----------------------------------------------------- + - nothing now + + -- 0.77 ----------------------------------------------------- + - .. + + -- 0.26.5 Changes/Bug Fixes --------------------------------- + - Added Group 4 Fax encoding as compression for TIFF files. + - Added ImagingTiffJpegQuality option. + + -- 0.26.3 Changes/Bug Fixes --------------------------------- + - Allowed JPEG2000 for Mac OS X x86 + + -- 0.26.1 Changes/Bug Fixes --------------------------------- + - ElderImagery formats are disabled by default, TIFF enabled. + - Changed _LINK_ symbols according to changes in ImagingOptions.inc. + + -- 0.24.1 Changes/Bug Fixes --------------------------------- + - Allowed JPEG2000 for x86_64 CPUS in Linux + + -- 0.23 Changes/Bug Fixes ----------------------------------- + - Better IF conditional to disable JPEG2000 on unsupported platforms. + - Added PSD and TIFF related stuff. + + -- 0.21 Changes/Bug Fixes ----------------------------------- + - Created with initial stuff. + +} + +end. diff --git a/resources/libraries/deskew/Imaging/ImagingFormats.pas b/resources/libraries/deskew/Imaging/ImagingFormats.pas new file mode 100755 index 0000000..6f57118 --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingFormats.pas @@ -0,0 +1,4464 @@ +{ + Vampyre Imaging Library + by Marek Mauder + http://imaginglib.sourceforge.net + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +{ This unit manages information about all image data formats and contains + low level format conversion, manipulation, and other related functions.} +unit ImagingFormats; + +{$I ImagingOptions.inc} + +interface + +uses + ImagingTypes, Imaging, ImagingUtility; + +type + TImageFormatInfoArray = array[TImageFormat] of PImageFormatInfo; + PImageFormatInfoArray = ^TImageFormatInfoArray; + + +{ Additional image manipulation functions (usually used internally by Imaging unit) } + +type + { Color reduction operations.} + TReduceColorsAction = (raCreateHistogram, raUpdateHistogram, raMakeColorMap, + raMapImage); + TReduceColorsActions = set of TReduceColorsAction; +const + AllReduceColorsActions = [raCreateHistogram, raUpdateHistogram, + raMakeColorMap, raMapImage]; +{ Reduces the number of colors of source. Src is bits of source image + (ARGB or floating point) and Dst is in some indexed format. MaxColors + is the number of colors to which reduce and DstPal is palette to which + the resulting colors are written and it must be allocated to at least + MaxColors entries. ChannelMask is 'anded' with every pixel's channel value + when creating color histogram. If $FF is used all 8bits of color channels + are used which can be slow for large images with many colors so you can + use lower masks to speed it up.} +procedure ReduceColorsMedianCut(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo; MaxColors: LongInt; ChannelMask: Byte; + DstPal: PPalette32; Actions: TReduceColorsActions = AllReduceColorsActions); +{ Stretches rectangle in source image to rectangle in destination image + using nearest neighbor filtering. It is fast but results look blocky + because there is no interpolation used. SrcImage and DstImage must be + in the same data format. Works for all data formats except special formats.} +procedure StretchNearest(const SrcImage: TImageData; SrcX, SrcY, SrcWidth, + SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth, + DstHeight: LongInt); +type + { Built-in sampling filters.} + TSamplingFilter = (sfNearest, sfLinear, sfCosine, sfHermite, sfQuadratic, + sfGaussian, sfSpline, sfLanczos, sfMitchell, sfCatmullRom); + { Type of custom sampling function} + TFilterFunction = function(Value: Single): Single; +const + { Default resampling filter used for bicubic resizing.} + DefaultCubicFilter = sfCatmullRom; +var + { Built-in filter functions.} + SamplingFilterFunctions: array[TSamplingFilter] of TFilterFunction; + { Default radii of built-in filter functions.} + SamplingFilterRadii: array[TSamplingFilter] of Single; + +{ Stretches rectangle in source image to rectangle in destination image + with resampling. One of built-in resampling filters defined by + Filter is used. Set WrapEdges to True for seamlessly tileable images. + SrcImage and DstImage must be in the same data format. + Works for all data formats except special and indexed formats.} +procedure StretchResample(const SrcImage: TImageData; SrcX, SrcY, SrcWidth, + SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth, + DstHeight: LongInt; Filter: TSamplingFilter; WrapEdges: Boolean = False); overload; +{ Stretches rectangle in source image to rectangle in destination image + with resampling. You can use custom sampling function and filter radius. + Set WrapEdges to True for seamlessly tileable images. SrcImage and DstImage + must be in the same data format. + Works for all data formats except special and indexed formats.} +procedure StretchResample(const SrcImage: TImageData; SrcX, SrcY, SrcWidth, + SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth, + DstHeight: LongInt; Filter: TFilterFunction; Radius: Single; + WrapEdges: Boolean = False); overload; +{ Helper for functions that create mipmap levels. BiggerLevel is + valid image and SmallerLevel is empty zeroed image. SmallerLevel is created + with Width and Height dimensions and it is filled with pixels of BiggerLevel + using resampling filter specified by ImagingMipMapFilter option. + Uses StretchNearest and StretchResample internally so the same image data format + limitations apply.} +procedure FillMipMapLevel(const BiggerLevel: TImageData; Width, Height: LongInt; + var SmallerLevel: TImageData); + + +{ Various helper & support functions } + +{ Copies Src pixel to Dest pixel. It is faster than System.Move procedure.} +procedure CopyPixel(Src, Dest: Pointer; BytesPerPixel: LongInt); {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Compares Src pixel and Dest pixel. It is faster than SysUtils.CompareMem function.} +function ComparePixels(PixelA, PixelB: Pointer; BytesPerPixel: LongInt): Boolean; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Translates pixel color in SrcFormat to DstFormat.} +procedure TranslatePixel(SrcPixel, DstPixel: Pointer; SrcFormat, + DstFormat: TImageFormat; SrcPalette, DstPalette: PPalette32); +{ Clamps floating point pixel channel values to [0.0, 1.0] range.} +procedure ClampFloatPixel(var PixF: TColorFPRec); {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Helper function that converts pixel in any format to 32bit ARGB pixel. + For common formats it's faster than calling GetPixel32 etc.} +procedure ConvertToPixel32(SrcPix: PByte; DestPix: PColor32Rec; + const SrcInfo: TImageFormatInfo; SrcPalette: PPalette32 = nil); {$IFDEF USE_INLINE}inline;{$ENDIF} + +{ Adds padding bytes at the ends of scanlines. Bpp is the number of bytes per + pixel of source and WidthBytes is the number of bytes per scanlines of dest.} +procedure AddPadBytes(DataIn: Pointer; DataOut: Pointer; Width, Height, + Bpp, WidthBytes: LongInt); +{ Removes padding from image with scanlines that have aligned sizes. Bpp is + the number of bytes per pixel of dest and WidthBytes is the number of bytes + per scanlines of source.} +procedure RemovePadBytes(DataIn: Pointer; DataOut: Pointer; Width, Height, + Bpp, WidthBytes: LongInt); + +{ Converts 1bit image data to 8bit. Used mostly by file loaders for formats + supporting 1bit images. Scaling of pixel values to 8bits is optional + (indexed formats don't need this).} +procedure Convert1To8(DataIn, DataOut: PByte; Width, Height, + WidthBytes: LongInt; ScaleTo8Bits: Boolean); +{ Converts 2bit image data to 8bit. Used mostly by file loaders for formats + supporting 2bit images. Scaling of pixel values to 8bits is optional + (indexed formats don't need this).} +procedure Convert2To8(DataIn, DataOut: PByte; Width, Height, + WidthBytes: LongInt; ScaleTo8Bits: Boolean); +{ Converts 4bit image data to 8bit. Used mostly by file loaders for formats + supporting 4bit images. Scaling of pixel values to 8bits is optional + (indexed formats don't need this).} +procedure Convert4To8(DataIn, DataOut: PByte; Width, Height, + WidthBytes: LongInt; ScaleTo8Bits: Boolean); + +{ Helper function for image file loaders. Some 15 bit images (targas, bitmaps) + may contain 1 bit alpha but there is no indication of it. This function checks + all 16 bit(should be X1R5G5B5 or A1R5G5B5 format) pixels and some of them have + alpha bit set it returns True, otherwise False.} +function Has16BitImageAlpha(NumPixels: LongInt; Data: PWord): Boolean; +{ Helper function for image file loaders. This function checks is similar + to Has16BitImageAlpha but works with A8R8G8B8/X8R8G8B8 format.} +function Has32BitImageAlpha(NumPixels: LongInt; Data: PLongWord): Boolean; +{ Checks if there is any relevant alpha data (any entry has alpha <> 255) + in the given palette.} +function PaletteHasAlpha(Palette: PPalette32; PaletteEntries: Integer): Boolean; +{ Checks if given palette has only grayscale entries.} +function PaletteIsGrayScale(Palette: PPalette32; PaletteEntries: Integer): Boolean; + +{ Provides indexed access to each line of pixels. Does not work with special + format images.} +function GetScanLine(ImageBits: Pointer; const FormatInfo: TImageFormatInfo; + LineWidth, Index: LongInt): Pointer; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Returns True if Format is valid image data format identifier.} +function IsImageFormatValid(Format: TImageFormat): Boolean; + +{ Converts 16bit half floating point value to 32bit Single.} +function HalfToFloat(Half: THalfFloat): Single; +{ Converts 32bit Single to 16bit half floating point.} +function FloatToHalf(Float: Single): THalfFloat; + +{ Converts half float color value to single-precision floating point color.} +function ColorHalfToFloat(ColorHF: TColorHFRec): TColorFPRec; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Converts single-precision floating point color to half float color.} +function ColorFloatToHalf(ColorFP: TColorFPRec): TColorHFRec; {$IFDEF USE_INLINE}inline;{$ENDIF} + +{ Converts ARGB color to grayscale. } +function Color32ToGray(Color32: TColor32): Byte; {$IFDEF USE_INLINE}inline;{$ENDIF} + +{ Makes image PalEntries x 1 big where each pixel has color of one pal entry.} +procedure VisualizePalette(Pal: PPalette32; Entries: Integer; out PalImage: TImageData); + +type + TPointRec = record + Pos: LongInt; + Weight: Single; + end; + TCluster = array of TPointRec; + TMappingTable = array of TCluster; + +{ Helper function for resampling.} +function BuildMappingTable(DstLow, DstHigh, SrcLow, SrcHigh, SrcImageWidth: LongInt; + Filter: TFilterFunction; Radius: Single; WrapEdges: Boolean): TMappingTable; +{ Helper function for resampling.} +procedure FindExtremes(const Map: TMappingTable; var MinPos, MaxPos: LongInt); + + +{ Pixel readers/writers for different image formats } + +{ Returns pixel of image in any ARGB format. Channel values are scaled to 16 bits.} +procedure ChannelGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo; + var Pix: TColor64Rec); +{ Sets pixel of image in any ARGB format. Channel values must be scaled to 16 bits.} +procedure ChannelSetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo; + const Pix: TColor64Rec); + +{ Returns pixel of image in any grayscale format. Gray value is scaled to 64 bits + and alpha to 16 bits.} +procedure GrayGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo; + var Gray: TColor64Rec; var Alpha: Word); +{ Sets pixel of image in any grayscale format. Gray value must be scaled to 64 bits + and alpha to 16 bits.} +procedure GraySetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo; + const Gray: TColor64Rec; Alpha: Word); + +{ Returns pixel of image in any floating point format. Channel values are + in range <0.0, 1.0>.} +procedure FloatGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo; + var Pix: TColorFPRec); +{ Sets pixel of image in any floating point format. Channel values must be + in range <0.0, 1.0>.} +procedure FloatSetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo; + const Pix: TColorFPRec); + +{ Returns pixel of image in any indexed format. Returned value is index to + the palette.} +procedure IndexGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo; + var Index: LongWord); +{ Sets pixel of image in any indexed format. Index is index to the palette.} +procedure IndexSetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo; + Index: LongWord); + + +{ Pixel readers/writers for 32bit and FP colors} + +{ Function for getting pixel colors. Native pixel is read from Image and + then translated to 32 bit ARGB.} +function GetPixel32Generic(Bits: Pointer; Info: PImageFormatInfo; + Palette: PPalette32): TColor32Rec; +{ Procedure for setting pixel colors. Input 32 bit ARGB color is translated to + native format and then written to Image.} +procedure SetPixel32Generic(Bits: Pointer; Info: PImageFormatInfo; + Palette: PPalette32; const Color: TColor32Rec); +{ Function for getting pixel colors. Native pixel is read from Image and + then translated to FP ARGB.} +function GetPixelFPGeneric(Bits: Pointer; Info: PImageFormatInfo; + Palette: PPalette32): TColorFPRec; +{ Procedure for setting pixel colors. Input FP ARGB color is translated to + native format and then written to Image.} +procedure SetPixelFPGeneric(Bits: Pointer; Info: PImageFormatInfo; + Palette: PPalette32; const Color: TColorFPRec); + + +{ Image format conversion functions } + +{ Converts any ARGB format to any ARGB format.} +procedure ChannelToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo); +{ Converts any ARGB format to any grayscale format.} +procedure ChannelToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo); +{ Converts any ARGB format to any floating point format.} +procedure ChannelToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo); +{ Converts any ARGB format to any indexed format.} +procedure ChannelToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo; DstPal: PPalette32); + +{ Converts any grayscale format to any grayscale format.} +procedure GrayToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo); +{ Converts any grayscale format to any ARGB format.} +procedure GrayToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo); +{ Converts any grayscale format to any floating point format.} +procedure GrayToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo); +{ Converts any grayscale format to any indexed format.} +procedure GrayToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo; DstPal: PPalette32); + +{ Converts any floating point format to any floating point format.} +procedure FloatToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo); +{ Converts any floating point format to any ARGB format.} +procedure FloatToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo); +{ Converts any floating point format to any grayscale format.} +procedure FloatToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo); +{ Converts any floating point format to any indexed format.} +procedure FloatToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo; DstPal: PPalette32); + +{ Converts any indexed format to any indexed format.} +procedure IndexToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo; SrcPal, DstPal: PPalette32); +{ Converts any indexed format to any ARGB format.} +procedure IndexToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo; SrcPal: PPalette32); +{ Converts any indexed format to any grayscale format.} +procedure IndexToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo; SrcPal: PPalette32); +{ Converts any indexed format to any floating point format.} +procedure IndexToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo; SrcPal: PPalette32); + +{ Special formats conversion functions } + +{ Converts image to/from/between special image formats (dxtc, ...).} +procedure ConvertSpecial(var Image: TImageData; SrcInfo, + DstInfo: PImageFormatInfo); + + +{ Inits all image format information. Called internally on startup.} +procedure InitImageFormats(var Infos: TImageFormatInfoArray); + +const + // Grayscale conversion channel weights + GrayConv: TColorFPRec = (B: 0.114; G: 0.587; R: 0.299; A: 0.0); + + // Contants for converting integer colors to floating point + OneDiv8Bit: Single = 1.0 / 255.0; + OneDiv16Bit: Single = 1.0 / 65535.0; + +implementation + +{ TImageFormatInfo member functions } + +{ Returns size in bytes of image in given standard format where + Size = Width * Height * Bpp.} +function GetStdPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt; forward; +{ Checks if Width and Height are valid for given standard format.} +procedure CheckStdDimensions(Format: TImageFormat; var Width, Height: LongInt); forward; +{ Returns size in bytes of image in given DXT format.} +function GetDXTPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt; forward; +{ Checks if Width and Height are valid for given DXT format. If they are + not valid, they are changed to pass the check.} +procedure CheckDXTDimensions(Format: TImageFormat; var Width, Height: LongInt); forward; +{ Returns size in bytes of image in BTC format.} +function GetBTCPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt; forward; +{ Returns size in bytes of image in binary format (1bit image).} +function GetBinaryPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt; forward; + +function GetBCPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt; forward; +procedure CheckBCDimensions(Format: TImageFormat; var Width, Height: LongInt); forward; + + +{ Optimized pixel readers/writers for 32bit and FP colors to be stored in TImageFormatInfo } + +function GetPixel32ifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColor32Rec; forward; +procedure SetPixel32ifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColor32Rec); forward; +function GetPixelFPifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColorFPRec; forward; +procedure SetPixelFPifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColorFPRec); forward; + +function GetPixel32Channel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColor32Rec; forward; +procedure SetPixel32Channel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColor32Rec); forward; +function GetPixelFPChannel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColorFPRec; forward; +procedure SetPixelFPChannel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColorFPRec); forward; + +function GetPixelFPFloat32(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColorFPRec; forward; +procedure SetPixelFPFloat32(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColorFPRec); forward; + +var + PFR3G3B2: TPixelFormatInfo; + PFX5R1G1B1: TPixelFormatInfo; + PFR5G6B5: TPixelFormatInfo; + PFA1R5G5B5: TPixelFormatInfo; + PFA4R4G4B4: TPixelFormatInfo; + PFX1R5G5B5: TPixelFormatInfo; + PFX4R4G4B4: TPixelFormatInfo; + FInfos: PImageFormatInfoArray; + +var + // Free Pascal generates hundreds of warnings here +{$WARNINGS OFF} + + // indexed formats + Index8Info: TImageFormatInfo = ( + Format: ifIndex8; + Name: 'Index8'; + BytesPerPixel: 1; + ChannelCount: 1; + PaletteEntries: 256; + HasAlphaChannel: True; + IsIndexed: True; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPGeneric; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPGeneric); + + // grayscale formats + Gray8Info: TImageFormatInfo = ( + Format: ifGray8; + Name: 'Gray8'; + BytesPerPixel: 1; + ChannelCount: 1; + HasGrayChannel: True; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Channel8Bit; + GetPixelFP: GetPixelFPChannel8Bit; + SetPixel32: SetPixel32Channel8Bit; + SetPixelFP: SetPixelFPChannel8Bit); + + A8Gray8Info: TImageFormatInfo = ( + Format: ifA8Gray8; + Name: 'A8Gray8'; + BytesPerPixel: 2; + ChannelCount: 2; + HasGrayChannel: True; + HasAlphaChannel: True; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Channel8Bit; + GetPixelFP: GetPixelFPChannel8Bit; + SetPixel32: SetPixel32Channel8Bit; + SetPixelFP: SetPixelFPChannel8Bit); + + Gray16Info: TImageFormatInfo = ( + Format: ifGray16; + Name: 'Gray16'; + BytesPerPixel: 2; + ChannelCount: 1; + HasGrayChannel: True; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPGeneric; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPGeneric); + + Gray32Info: TImageFormatInfo = ( + Format: ifGray32; + Name: 'Gray32'; + BytesPerPixel: 4; + ChannelCount: 1; + HasGrayChannel: True; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPGeneric; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPGeneric); + + Gray64Info: TImageFormatInfo = ( + Format: ifGray64; + Name: 'Gray64'; + BytesPerPixel: 8; + ChannelCount: 1; + HasGrayChannel: True; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPGeneric; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPGeneric); + + A16Gray16Info: TImageFormatInfo = ( + Format: ifA16Gray16; + Name: 'A16Gray16'; + BytesPerPixel: 4; + ChannelCount: 2; + HasGrayChannel: True; + HasAlphaChannel: True; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPGeneric; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPGeneric); + + // ARGB formats + X5R1G1B1Info: TImageFormatInfo = ( + Format: ifX5R1G1B1; + Name: 'X5R1G1B1'; + BytesPerPixel: 1; + ChannelCount: 3; + UsePixelFormat: True; + PixelFormat: @PFX5R1G1B1; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPGeneric; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPGeneric); + + R3G3B2Info: TImageFormatInfo = ( + Format: ifR3G3B2; + Name: 'R3G3B2'; + BytesPerPixel: 1; + ChannelCount: 3; + UsePixelFormat: True; + PixelFormat: @PFR3G3B2; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPGeneric; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPGeneric); + + R5G6B5Info: TImageFormatInfo = ( + Format: ifR5G6B5; + Name: 'R5G6B5'; + BytesPerPixel: 2; + ChannelCount: 3; + UsePixelFormat: True; + PixelFormat: @PFR5G6B5; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPGeneric; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPGeneric); + + A1R5G5B5Info: TImageFormatInfo = ( + Format: ifA1R5G5B5; + Name: 'A1R5G5B5'; + BytesPerPixel: 2; + ChannelCount: 4; + HasAlphaChannel: True; + UsePixelFormat: True; + PixelFormat: @PFA1R5G5B5; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPGeneric; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPGeneric); + + A4R4G4B4Info: TImageFormatInfo = ( + Format: ifA4R4G4B4; + Name: 'A4R4G4B4'; + BytesPerPixel: 2; + ChannelCount: 4; + HasAlphaChannel: True; + UsePixelFormat: True; + PixelFormat: @PFA4R4G4B4; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPGeneric; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPGeneric); + + X1R5G5B5Info: TImageFormatInfo = ( + Format: ifX1R5G5B5; + Name: 'X1R5G5B5'; + BytesPerPixel: 2; + ChannelCount: 3; + UsePixelFormat: True; + PixelFormat: @PFX1R5G5B5; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPGeneric; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPGeneric); + + X4R4G4B4Info: TImageFormatInfo = ( + Format: ifX4R4G4B4; + Name: 'X4R4G4B4'; + BytesPerPixel: 2; + ChannelCount: 3; + UsePixelFormat: True; + PixelFormat: @PFX4R4G4B4; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPGeneric; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPGeneric); + + R8G8B8Info: TImageFormatInfo = ( + Format: ifR8G8B8; + Name: 'R8G8B8'; + BytesPerPixel: 3; + ChannelCount: 3; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Channel8Bit; + GetPixelFP: GetPixelFPChannel8Bit; + SetPixel32: SetPixel32Channel8Bit; + SetPixelFP: SetPixelFPChannel8Bit); + + A8R8G8B8Info: TImageFormatInfo = ( + Format: ifA8R8G8B8; + Name: 'A8R8G8B8'; + BytesPerPixel: 4; + ChannelCount: 4; + HasAlphaChannel: True; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32ifA8R8G8B8; + GetPixelFP: GetPixelFPifA8R8G8B8; + SetPixel32: SetPixel32ifA8R8G8B8; + SetPixelFP: SetPixelFPifA8R8G8B8); + + X8R8G8B8Info: TImageFormatInfo = ( + Format: ifX8R8G8B8; + Name: 'X8R8G8B8'; + BytesPerPixel: 4; + ChannelCount: 3; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Channel8Bit; + GetPixelFP: GetPixelFPChannel8Bit; + SetPixel32: SetPixel32Channel8Bit; + SetPixelFP: SetPixelFPChannel8Bit); + + R16G16B16Info: TImageFormatInfo = ( + Format: ifR16G16B16; + Name: 'R16G16B16'; + BytesPerPixel: 6; + ChannelCount: 3; + RBSwapFormat: ifB16G16R16; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPGeneric; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPGeneric); + + A16R16G16B16Info: TImageFormatInfo = ( + Format: ifA16R16G16B16; + Name: 'A16R16G16B16'; + BytesPerPixel: 8; + ChannelCount: 4; + HasAlphaChannel: True; + RBSwapFormat: ifA16B16G16R16; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPGeneric; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPGeneric); + + B16G16R16Info: TImageFormatInfo = ( + Format: ifB16G16R16; + Name: 'B16G16R16'; + BytesPerPixel: 6; + ChannelCount: 3; + IsRBSwapped: True; + RBSwapFormat: ifR16G16B16; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPGeneric; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPGeneric); + + A16B16G16R16Info: TImageFormatInfo = ( + Format: ifA16B16G16R16; + Name: 'A16B16G16R16'; + BytesPerPixel: 8; + ChannelCount: 4; + HasAlphaChannel: True; + IsRBSwapped: True; + RBSwapFormat: ifA16R16G16B16; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPGeneric; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPGeneric); + + // floating point formats + R32FInfo: TImageFormatInfo = ( + Format: ifR32F; + Name: 'R32F'; + BytesPerPixel: 4; + ChannelCount: 1; + IsFloatingPoint: True; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPFloat32; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPFloat32); + + A32R32G32B32FInfo: TImageFormatInfo = ( + Format: ifA32R32G32B32F; + Name: 'A32R32G32B32F'; + BytesPerPixel: 16; + ChannelCount: 4; + HasAlphaChannel: True; + IsFloatingPoint: True; + RBSwapFormat: ifA32B32G32R32F; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPFloat32; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPFloat32); + + A32B32G32R32FInfo: TImageFormatInfo = ( + Format: ifA32B32G32R32F; + Name: 'A32B32G32R32F'; + BytesPerPixel: 16; + ChannelCount: 4; + HasAlphaChannel: True; + IsFloatingPoint: True; + IsRBSwapped: True; + RBSwapFormat: ifA32R32G32B32F; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPFloat32; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPFloat32); + + R16FInfo: TImageFormatInfo = ( + Format: ifR16F; + Name: 'R16F'; + BytesPerPixel: 2; + ChannelCount: 1; + IsFloatingPoint: True; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPGeneric; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPGeneric); + + A16R16G16B16FInfo: TImageFormatInfo = ( + Format: ifA16R16G16B16F; + Name: 'A16R16G16B16F'; + BytesPerPixel: 8; + ChannelCount: 4; + HasAlphaChannel: True; + IsFloatingPoint: True; + RBSwapFormat: ifA16B16G16R16F; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPGeneric; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPGeneric); + + A16B16G16R16FInfo: TImageFormatInfo = ( + Format: ifA16B16G16R16F; + Name: 'A16B16G16R16F'; + BytesPerPixel: 8; + ChannelCount: 4; + HasAlphaChannel: True; + IsFloatingPoint: True; + IsRBSwapped: True; + RBSwapFormat: ifA16R16G16B16F; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPGeneric; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPGeneric); + + R32G32B32FInfo: TImageFormatInfo = ( + Format: ifR32G32B32F; + Name: 'R32G32B32F'; + BytesPerPixel: 12; + ChannelCount: 3; + IsFloatingPoint: True; + RBSwapFormat: ifB32G32R32F; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPFloat32; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPFloat32); + + B32G32R32FInfo: TImageFormatInfo = ( + Format: ifB32G32R32F; + Name: 'B32G32R32F'; + BytesPerPixel: 12; + ChannelCount: 3; + IsFloatingPoint: True; + IsRBSwapped: True; + RBSwapFormat: ifR32G32B32F; + GetPixelsSize: GetStdPixelsSize; + CheckDimensions: CheckStdDimensions; + GetPixel32: GetPixel32Generic; + GetPixelFP: GetPixelFPFloat32; + SetPixel32: SetPixel32Generic; + SetPixelFP: SetPixelFPFloat32); + + // special formats + DXT1Info: TImageFormatInfo = ( + Format: ifDXT1; + Name: 'DXT1'; + ChannelCount: 4; + HasAlphaChannel: True; + IsSpecial: True; + GetPixelsSize: GetDXTPixelsSize; + CheckDimensions: CheckDXTDimensions; + SpecialNearestFormat: ifA8R8G8B8); + + DXT3Info: TImageFormatInfo = ( + Format: ifDXT3; + Name: 'DXT3'; + ChannelCount: 4; + HasAlphaChannel: True; + IsSpecial: True; + GetPixelsSize: GetDXTPixelsSize; + CheckDimensions: CheckDXTDimensions; + SpecialNearestFormat: ifA8R8G8B8); + + DXT5Info: TImageFormatInfo = ( + Format: ifDXT5; + Name: 'DXT5'; + ChannelCount: 4; + HasAlphaChannel: True; + IsSpecial: True; + GetPixelsSize: GetDXTPixelsSize; + CheckDimensions: CheckDXTDimensions; + SpecialNearestFormat: ifA8R8G8B8); + + BTCInfo: TImageFormatInfo = ( + Format: ifBTC; + Name: 'BTC'; + ChannelCount: 1; + HasAlphaChannel: False; + IsSpecial: True; + GetPixelsSize: GetBTCPixelsSize; + CheckDimensions: CheckDXTDimensions; + SpecialNearestFormat: ifGray8); + + ATI1NInfo: TImageFormatInfo = ( + Format: ifATI1N; + Name: 'ATI1N'; + ChannelCount: 1; + HasAlphaChannel: False; + IsSpecial: True; + GetPixelsSize: GetDXTPixelsSize; + CheckDimensions: CheckDXTDimensions; + SpecialNearestFormat: ifGray8); + + ATI2NInfo: TImageFormatInfo = ( + Format: ifATI2N; + Name: 'ATI2N'; + ChannelCount: 2; + HasAlphaChannel: False; + IsSpecial: True; + GetPixelsSize: GetDXTPixelsSize; + CheckDimensions: CheckDXTDimensions; + SpecialNearestFormat: ifA8R8G8B8); + + BinaryInfo: TImageFormatInfo = ( + Format: ifBinary; + Name: 'Binary'; + ChannelCount: 1; + HasAlphaChannel: False; + IsSpecial: True; + GetPixelsSize: GetBinaryPixelsSize; + CheckDimensions: CheckStdDimensions; + SpecialNearestFormat: ifGray8); + + { Passtrough formats } + + {ETC1Info: TImageFormatInfo = ( + Format: ifETC1; + Name: 'ETC1'; + ChannelCount: 3; + HasAlphaChannel: False; + IsSpecial: True; + IsPasstrough: True; + GetPixelsSize: GetBCPixelsSize; + CheckDimensions: CheckBCDimensions; + SpecialNearestFormat: ifR8G8B8); + + ETC2RGBInfo: TImageFormatInfo = ( + Format: ifETC2RGB; + Name: 'ETC2RGB'; + ChannelCount: 3; + HasAlphaChannel: False; + IsSpecial: True; + IsPasstrough: True; + GetPixelsSize: GetBCPixelsSize; + CheckDimensions: CheckBCDimensions; + SpecialNearestFormat: ifR8G8B8); + + ETC2RGBAInfo: TImageFormatInfo = ( + Format: ifETC2RGBA; + Name: 'ETC2RGBA'; + ChannelCount: 4; + HasAlphaChannel: True; + IsSpecial: True; + IsPasstrough: True; + GetPixelsSize: GetBCPixelsSize; + CheckDimensions: CheckBCDimensions; + SpecialNearestFormat: ifA8R8G8B8); + + ETC2PAInfo: TImageFormatInfo = ( + Format: ifETC2PA; + Name: 'ETC2PA'; + ChannelCount: 4; + HasAlphaChannel: True; + IsSpecial: True; + IsPasstrough: True; + GetPixelsSize: GetBCPixelsSize; + CheckDimensions: CheckBCDimensions; + SpecialNearestFormat: ifA8R8G8B8); + + DXBC6Info: TImageFormatInfo = ( + Format: ifDXBC6; + Name: 'DXBC6'; + ChannelCount: 4; + HasAlphaChannel: True; + IsSpecial: True; + IsPasstrough: True; + GetPixelsSize: GetBCPixelsSize; + CheckDimensions: CheckBCDimensions; + SpecialNearestFormat: ifA8R8G8B8); + + DXBC7Info: TImageFormatInfo = ( + Format: ifDXBC6; + Name: 'DXBC7'; + ChannelCount: 4; + HasAlphaChannel: True; + IsSpecial: True; + IsPasstrough: True; + GetPixelsSize: GetBCPixelsSize; + CheckDimensions: CheckBCDimensions; + SpecialNearestFormat: ifA8R8G8B8); + + PVRTCInfo: TImageFormatInfo = ( + Format: ifPVRTC; + Name: 'PVRTC'; + ChannelCount: 4; + HasAlphaChannel: True; + IsSpecial: True; + IsPasstrough: True; + GetPixelsSize: GetBCPixelsSize; + CheckDimensions: CheckBCDimensions; + SpecialNearestFormat: ifA8R8G8B8);} + +{$WARNINGS ON} + +function PixelFormat(ABitCount, RBitCount, GBitCount, BBitCount: Byte): TPixelFormatInfo; forward; + +procedure InitImageFormats(var Infos: TImageFormatInfoArray); +begin + FInfos := @Infos; + + Infos[ifDefault] := @A8R8G8B8Info; + // indexed formats + Infos[ifIndex8] := @Index8Info; + // grayscale formats + Infos[ifGray8] := @Gray8Info; + Infos[ifA8Gray8] := @A8Gray8Info; + Infos[ifGray16] := @Gray16Info; + Infos[ifGray32] := @Gray32Info; + Infos[ifGray64] := @Gray64Info; + Infos[ifA16Gray16] := @A16Gray16Info; + // ARGB formats + Infos[ifX5R1G1B1] := @X5R1G1B1Info; + Infos[ifR3G3B2] := @R3G3B2Info; + Infos[ifR5G6B5] := @R5G6B5Info; + Infos[ifA1R5G5B5] := @A1R5G5B5Info; + Infos[ifA4R4G4B4] := @A4R4G4B4Info; + Infos[ifX1R5G5B5] := @X1R5G5B5Info; + Infos[ifX4R4G4B4] := @X4R4G4B4Info; + Infos[ifR8G8B8] := @R8G8B8Info; + Infos[ifA8R8G8B8] := @A8R8G8B8Info; + Infos[ifX8R8G8B8] := @X8R8G8B8Info; + Infos[ifR16G16B16] := @R16G16B16Info; + Infos[ifA16R16G16B16] := @A16R16G16B16Info; + Infos[ifB16G16R16] := @B16G16R16Info; + Infos[ifA16B16G16R16] := @A16B16G16R16Info; + // floating point formats + Infos[ifR32F] := @R32FInfo; + Infos[ifA32R32G32B32F] := @A32R32G32B32FInfo; + Infos[ifA32B32G32R32F] := @A32B32G32R32FInfo; + Infos[ifR16F] := @R16FInfo; + Infos[ifA16R16G16B16F] := @A16R16G16B16FInfo; + Infos[ifA16B16G16R16F] := @A16B16G16R16FInfo; + Infos[ifR32G32B32F] := @R32G32B32FInfo; + Infos[ifB32G32R32F] := @B32G32R32FInfo; + // special formats + Infos[ifDXT1] := @DXT1Info; + Infos[ifDXT3] := @DXT3Info; + Infos[ifDXT5] := @DXT5Info; + Infos[ifBTC] := @BTCInfo; + Infos[ifATI1N] := @ATI1NInfo; + Infos[ifATI2N] := @ATI2NInfo; + Infos[ifBinary] := @BinaryInfo; + + PFR3G3B2 := PixelFormat(0, 3, 3, 2); + PFX5R1G1B1 := PixelFormat(0, 1, 1, 1); + PFR5G6B5 := PixelFormat(0, 5, 6, 5); + PFA1R5G5B5 := PixelFormat(1, 5, 5, 5); + PFA4R4G4B4 := PixelFormat(4, 4, 4, 4); + PFX1R5G5B5 := PixelFormat(0, 5, 5, 5); + PFX4R4G4B4 := PixelFormat(0, 4, 4, 4); +end; + + +{ Internal unit helper functions } + +function PixelFormat(ABitCount, RBitCount, GBitCount, BBitCount: Byte): TPixelFormatInfo; +begin + Result.ABitMask := ((1 shl ABitCount) - 1) shl (RBitCount + GBitCount + + BBitCount); + Result.RBitMask := ((1 shl RBitCount) - 1) shl (GBitCount + BBitCount); + Result.GBitMask := ((1 shl GBitCount) - 1) shl (BBitCount); + Result.BBitMask := (1 shl BBitCount) - 1; + Result.ABitCount := ABitCount; + Result.RBitCount := RBitCount; + Result.GBitCount := GBitCount; + Result.BBitCount := BBitCount; + Result.AShift := RBitCount + GBitCount + BBitCount; + Result.RShift := GBitCount + BBitCount; + Result.GShift := BBitCount; + Result.BShift := 0; + Result.ARecDiv := Max(1, Pow2Int(Result.ABitCount) - 1); + Result.RRecDiv := Max(1, Pow2Int(Result.RBitCount) - 1); + Result.GRecDiv := Max(1, Pow2Int(Result.GBitCount) - 1); + Result.BRecDiv := Max(1, Pow2Int(Result.BBitCount) - 1); +end; + +function PixelFormatMask(ABitMask, RBitMask, GBitMask, BBitMask: LongWord): TPixelFormatInfo; + + function GetBitCount(B: LongWord): LongWord; + var + I: LongWord; + begin + I := 0; + while (I < 31) and (((1 shl I) and B) = 0) do + Inc(I); + Result := 0; + while ((1 shl I) and B) <> 0 do + begin + Inc(I); + Inc(Result); + end; + end; + +begin + Result := PixelFormat(GetBitCount(ABitMask), GetBitCount(RBitMask), + GetBitCount(GBitMask), GetBitCount(BBitMask)); +end; + +function PFSetARGB(const PF: TPixelFormatInfo; A, R, G, B: Byte): TColor32; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + with PF do + Result := + (A shl ABitCount shr 8 shl AShift) or + (R shl RBitCount shr 8 shl RShift) or + (G shl GBitCount shr 8 shl GShift) or + (B shl BBitCount shr 8 shl BShift); +end; + +procedure PFGetARGB(const PF: TPixelFormatInfo; Color: LongWord; + var A, R, G, B: Byte); {$IFDEF USE_INLINE}inline;{$ENDIF} +begin + with PF do + begin + A := (Color and ABitMask shr AShift) * 255 div ARecDiv; + R := (Color and RBitMask shr RShift) * 255 div RRecDiv; + G := (Color and GBitMask shr GShift) * 255 div GRecDiv; + B := (Color and BBitMask shl BShift) * 255 div BRecDiv; + end; +end; + +function PFSetColor(const PF: TPixelFormatInfo; ARGB: TColor32): LongWord; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + with PF do + Result := + (Byte(ARGB shr 24) shl ABitCount shr 8 shl AShift) or + (Byte(ARGB shr 16) shl RBitCount shr 8 shl RShift) or + (Byte(ARGB shr 8) shl GBitCount shr 8 shl GShift) or + (Byte(ARGB) shl BBitCount shr 8 shl BShift); +end; + +function PFGetColor(const PF: TPixelFormatInfo; Color: LongWord): TColor32; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + with PF, TColor32Rec(Result) do + begin + A := (Color and ABitMask shr AShift) * 255 div ARecDiv; + R := (Color and RBitMask shr RShift) * 255 div RRecDiv; + G := (Color and GBitMask shr GShift) * 255 div GRecDiv; + B := (Color and BBitMask shl BShift) * 255 div BRecDiv; + end; +end; + +{ Additional image manipulation functions (usually used internally by Imaging unit) } + +const + MaxPossibleColors = 4096; + HashSize = 32768; + AlphaWeight = 1024; + RedWeight = 612; + GreenWeight = 1202; + BlueWeight = 234; + +type + PColorBin = ^TColorBin; + TColorBin = record + Color: TColor32Rec; + Number: LongInt; + Next: PColorBin; + end; + + THashTable = array[0..HashSize - 1] of PColorBin; + + TColorBox = record + AMin, AMax, + RMin, RMax, + GMin, GMax, + BMin, BMax: LongInt; + Total: LongInt; + Represented: TColor32Rec; + List: PColorBin; + end; + +var + Table: THashTable; + Box: array[0..MaxPossibleColors - 1] of TColorBox; + Boxes: LongInt; + +procedure ReduceColorsMedianCut(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo; MaxColors: LongInt; ChannelMask: Byte; + DstPal: PPalette32; Actions: TReduceColorsActions); + + procedure CreateHistogram (Src: PByte; SrcInfo: PImageFormatInfo; + ChannelMask: Byte); + var + A, R, G, B: Byte; + I, Addr: LongInt; + PC: PColorBin; + Col: TColor32Rec; + begin + for I := 0 to NumPixels - 1 do + begin + Col := GetPixel32Generic(Src, SrcInfo, nil); + A := Col.A and ChannelMask; + R := Col.R and ChannelMask; + G := Col.G and ChannelMask; + B := Col.B and ChannelMask; + + Addr := (A + 11 * B + 59 * R + 119 * G) mod HashSize; + PC := Table[Addr]; + + while (PC <> nil) and ((PC.Color.R <> R) or (PC.Color.G <> G) or + (PC.Color.B <> B) or (PC.Color.A <> A)) do + PC := PC.Next; + + if PC = nil then + begin + New(PC); + PC.Color.R := R; + PC.Color.G := G; + PC.Color.B := B; + PC.Color.A := A; + PC.Number := 1; + PC.Next := Table[Addr]; + Table[Addr] := PC; + end + else + Inc(PC^.Number); + Inc(Src, SrcInfo.BytesPerPixel); + end; + end; + + procedure InitBox (var Box : TColorBox); + begin + Box.AMin := 256; + Box.RMin := 256; + Box.GMin := 256; + Box.BMin := 256; + Box.AMax := -1; + Box.RMax := -1; + Box.GMax := -1; + Box.BMax := -1; + Box.Total := 0; + Box.List := nil; + end; + + procedure ChangeBox (var Box: TColorBox; const C: TColorBin); + begin + with C.Color do + begin + if A < Box.AMin then Box.AMin := A; + if A > Box.AMax then Box.AMax := A; + if B < Box.BMin then Box.BMin := B; + if B > Box.BMax then Box.BMax := B; + if G < Box.GMin then Box.GMin := G; + if G > Box.GMax then Box.GMax := G; + if R < Box.RMin then Box.RMin := R; + if R > Box.RMax then Box.RMax := R; + end; + Inc(Box.Total, C.Number); + end; + + procedure MakeColormap; + var + I, J: LongInt; + CP, Pom: PColorBin; + Cut, LargestIdx, Largest, Size, S: LongInt; + CutA, CutR, CutG, CutB: Boolean; + SumA, SumR, SumG, SumB: LongInt; + Temp: TColorBox; + begin + I := 0; + Boxes := 1; + LargestIdx := 0; + while (I < HashSize) and (Table[I] = nil) do + Inc(i); + if I < HashSize then + begin + // put all colors into Box[0] + InitBox(Box[0]); + repeat + CP := Table[I]; + while CP.Next <> nil do + begin + ChangeBox(Box[0], CP^); + CP := CP.Next; + end; + ChangeBox(Box[0], CP^); + CP.Next := Box[0].List; + Box[0].List := Table[I]; + Table[I] := nil; + repeat + Inc(I) + until (I = HashSize) or (Table[I] <> nil); + until I = HashSize; + // now all colors are in Box[0] + repeat + // cut one color box + Largest := 0; + for I := 0 to Boxes - 1 do + with Box[I] do + begin + Size := (AMax - AMin) * AlphaWeight; + S := (RMax - RMin) * RedWeight; + if S > Size then + Size := S; + S := (GMax - GMin) * GreenWeight; + if S > Size then + Size := S; + S := (BMax - BMin) * BlueWeight; + if S > Size then + Size := S; + if Size > Largest then + begin + Largest := Size; + LargestIdx := I; + end; + end; + if Largest > 0 then + begin + // cutting Box[LargestIdx] into Box[LargestIdx] and Box[Boxes] + CutR := False; + CutG := False; + CutB := False; + CutA := False; + with Box[LargestIdx] do + begin + if (AMax - AMin) * AlphaWeight = Largest then + begin + Cut := (AMax + AMin) shr 1; + CutA := True; + end + else + if (RMax - RMin) * RedWeight = Largest then + begin + Cut := (RMax + RMin) shr 1; + CutR := True; + end + else + if (GMax - GMin) * GreenWeight = Largest then + begin + Cut := (GMax + GMin) shr 1; + CutG := True; + end + else + begin + Cut := (BMax + BMin) shr 1; + CutB := True; + end; + CP := List; + end; + InitBox(Box[LargestIdx]); + InitBox(Box[Boxes]); + repeat + // distribute one color + Pom := CP.Next; + with CP.Color do + begin + if (CutA and (A <= Cut)) or (CutR and (R <= Cut)) or + (CutG and (G <= Cut)) or (CutB and (B <= Cut)) then + I := LargestIdx + else + I := Boxes; + end; + CP.Next := Box[i].List; + Box[i].List := CP; + ChangeBox(Box[i], CP^); + CP := Pom; + until CP = nil; + Inc(Boxes); + end; + until (Boxes = MaxColors) or (Largest = 0); + // compute box representation + for I := 0 to Boxes - 1 do + begin + SumR := 0; + SumG := 0; + SumB := 0; + SumA := 0; + repeat + CP := Box[I].List; + Inc(SumR, CP.Color.R * CP.Number); + Inc(SumG, CP.Color.G * CP.Number); + Inc(SumB, CP.Color.B * CP.Number); + Inc(SumA, CP.Color.A * CP.Number); + Box[I].List := CP.Next; + Dispose(CP); + until Box[I].List = nil; + with Box[I] do + begin + Represented.A := SumA div Total; + Represented.R := SumR div Total; + Represented.G := SumG div Total; + Represented.B := SumB div Total; + AMin := AMin and ChannelMask; + RMin := RMin and ChannelMask; + GMin := GMin and ChannelMask; + BMin := BMin and ChannelMask; + AMax := (AMax and ChannelMask) + (not ChannelMask); + RMax := (RMax and ChannelMask) + (not ChannelMask); + GMax := (GMax and ChannelMask) + (not ChannelMask); + BMax := (BMax and ChannelMask) + (not ChannelMask); + end; + end; + // sort color boxes + for I := 0 to Boxes - 2 do + begin + Largest := 0; + for J := I to Boxes - 1 do + if Box[J].Total > Largest then + begin + Largest := Box[J].Total; + LargestIdx := J; + end; + if LargestIdx <> I then + begin + Temp := Box[I]; + Box[I] := Box[LargestIdx]; + Box[LargestIdx] := Temp; + end; + end; + end; + end; + + procedure FillOutputPalette; + var + I: LongInt; + begin + FillChar(DstPal^, SizeOf(TColor32Rec) * MaxColors, $FF); + for I := 0 to MaxColors - 1 do + begin + if I < Boxes then + with Box[I].Represented do + begin + DstPal[I].A := A; + DstPal[I].R := R; + DstPal[I].G := G; + DstPal[I].B := B; + end + else + DstPal[I].Color := $FF000000; + end; + end; + + function MapColor(const Col: TColor32Rec) : LongInt; + var + I: LongInt; + begin + I := 0; + with Col do + while (I < Boxes) and ((Box[I].AMin > A) or (Box[I].AMax < A) or + (Box[I].RMin > R) or (Box[I].RMax < R) or (Box[I].GMin > G) or + (Box[I].GMax < G) or (Box[I].BMin > B) or (Box[I].BMax < B)) do + Inc(I); + if I = Boxes then + MapColor := 0 + else + MapColor := I; + end; + + procedure MapImage(Src, Dst: PByte; SrcInfo, DstInfo: PImageFormatInfo); + var + I: LongInt; + Col: TColor32Rec; + begin + for I := 0 to NumPixels - 1 do + begin + Col := GetPixel32Generic(Src, SrcInfo, nil); + IndexSetDstPixel(Dst, DstInfo, MapColor(Col)); + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end; + end; + +begin + MaxColors := ClampInt(MaxColors, 2, MaxPossibleColors); + + if (raUpdateHistogram in Actions) or (raMapImage in Actions) then + begin + Assert(not SrcInfo.IsSpecial); + Assert(not SrcInfo.IsIndexed); + end; + + if raCreateHistogram in Actions then + FillChar(Table, SizeOf(Table), 0); + + if raUpdateHistogram in Actions then + CreateHistogram(Src, SrcInfo, ChannelMask); + + if raMakeColorMap in Actions then + begin + MakeColorMap; + FillOutputPalette; + end; + + if raMapImage in Actions then + MapImage(Src, Dst, SrcInfo, DstInfo); +end; + +procedure StretchNearest(const SrcImage: TImageData; SrcX, SrcY, SrcWidth, + SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth, + DstHeight: LongInt); +var + Info: TImageFormatInfo; + ScaleX, ScaleY, X, Y, Xp, Yp: LongInt; + DstPixel, SrcLine: PByte; +begin + GetImageFormatInfo(SrcImage.Format, Info); + Assert(SrcImage.Format = DstImage.Format); + Assert(not Info.IsSpecial); + // Use integers instead of floats for source image pixel coords + // Xp and Yp coords must be shifted right to get read source image coords + ScaleX := (SrcWidth shl 16) div DstWidth; + ScaleY := (SrcHeight shl 16) div DstHeight; + Yp := 0; + for Y := 0 to DstHeight - 1 do + begin + Xp := 0; + SrcLine := @PByteArray(SrcImage.Bits)[((SrcY + Yp shr 16) * SrcImage.Width + SrcX) * Info.BytesPerPixel]; + DstPixel := @PByteArray(DstImage.Bits)[((DstY + Y) * DstImage.Width + DstX) * Info.BytesPerPixel]; + for X := 0 to DstWidth - 1 do + begin + case Info.BytesPerPixel of + 1: PByte(DstPixel)^ := PByteArray(SrcLine)[Xp shr 16]; + 2: PWord(DstPixel)^ := PWordArray(SrcLine)[Xp shr 16]; + 3: PColor24Rec(DstPixel)^ := PPalette24(SrcLine)[Xp shr 16]; + 4: PColor32(DstPixel)^ := PLongWordArray(SrcLine)[Xp shr 16]; + 6: PColor48Rec(DstPixel)^ := PColor48RecArray(SrcLine)[Xp shr 16]; + 8: PColor64(DstPixel)^ := PInt64Array(SrcLine)[Xp shr 16]; + 16: PColorFPRec(DstPixel)^ := PColorFPRecArray(SrcLine)[Xp shr 16]; + end; + Inc(DstPixel, Info.BytesPerPixel); + Inc(Xp, ScaleX); + end; + Inc(Yp, ScaleY); + end; +end; + +{ Filter function for nearest filtering. Also known as box filter.} +function FilterNearest(Value: Single): Single; +begin + if (Value > -0.5) and (Value <= 0.5) then + Result := 1 + else + Result := 0; +end; + +{ Filter function for linear filtering. Also known as triangle or Bartlett filter.} +function FilterLinear(Value: Single): Single; +begin + if Value < 0.0 then + Value := -Value; + if Value < 1.0 then + Result := 1.0 - Value + else + Result := 0.0; +end; + +{ Cosine filter.} +function FilterCosine(Value: Single): Single; +begin + Result := 0; + if Abs(Value) < 1 then + Result := (Cos(Value * Pi) + 1) / 2; +end; + +{ f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 } +function FilterHermite(Value: Single): Single; +begin + if Value < 0.0 then + Value := -Value; + if Value < 1 then + Result := (2 * Value - 3) * Sqr(Value) + 1 + else + Result := 0; +end; + +{ Quadratic filter. Also known as Bell.} +function FilterQuadratic(Value: Single): Single; +begin + if Value < 0.0 then + Value := -Value; + if Value < 0.5 then + Result := 0.75 - Sqr(Value) + else + if Value < 1.5 then + begin + Value := Value - 1.5; + Result := 0.5 * Sqr(Value); + end + else + Result := 0.0; +end; + +{ Gaussian filter.} +function FilterGaussian(Value: Single): Single; +begin + Result := Exp(-2.0 * Sqr(Value)) * Sqrt(2.0 / Pi); +end; + +{ 4th order (cubic) b-spline filter.} +function FilterSpline(Value: Single): Single; +var + Temp: Single; +begin + if Value < 0.0 then + Value := -Value; + if Value < 1.0 then + begin + Temp := Sqr(Value); + Result := 0.5 * Temp * Value - Temp + 2.0 / 3.0; + end + else + if Value < 2.0 then + begin + Value := 2.0 - Value; + Result := Sqr(Value) * Value / 6.0; + end + else + Result := 0.0; +end; + +{ Lanczos-windowed sinc filter.} +function FilterLanczos(Value: Single): Single; + + function SinC(Value: Single): Single; + begin + if Value <> 0.0 then + begin + Value := Value * Pi; + Result := Sin(Value) / Value; + end + else + Result := 1.0; + end; + +begin + if Value < 0.0 then + Value := -Value; + if Value < 3.0 then + Result := SinC(Value) * SinC(Value / 3.0) + else + Result := 0.0; +end; + +{ Micthell cubic filter.} +function FilterMitchell(Value: Single): Single; +const + B = 1.0 / 3.0; + C = 1.0 / 3.0; +var + Temp: Single; +begin + if Value < 0.0 then + Value := -Value; + Temp := Sqr(Value); + if Value < 1.0 then + begin + Value := (((12.0 - 9.0 * B - 6.0 * C) * (Value * Temp)) + + ((-18.0 + 12.0 * B + 6.0 * C) * Temp) + + (6.0 - 2.0 * B)); + Result := Value / 6.0; + end + else + if Value < 2.0 then + begin + Value := (((-B - 6.0 * C) * (Value * Temp)) + + ((6.0 * B + 30.0 * C) * Temp) + + ((-12.0 * B - 48.0 * C) * Value) + + (8.0 * B + 24.0 * C)); + Result := Value / 6.0; + end + else + Result := 0.0; +end; + +{ CatmullRom spline filter.} +function FilterCatmullRom(Value: Single): Single; +begin + if Value < 0.0 then + Value := -Value; + if Value < 1.0 then + Result := 0.5 * (2.0 + Sqr(Value) * (-5.0 + 3.0 * Value)) + else + if Value < 2.0 then + Result := 0.5 * (4.0 + Value * (-8.0 + Value * (5.0 - Value))) + else + Result := 0.0; +end; + +procedure StretchResample(const SrcImage: TImageData; SrcX, SrcY, SrcWidth, + SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth, + DstHeight: LongInt; Filter: TSamplingFilter; WrapEdges: Boolean); +begin + // Calls the other function with filter function and radius defined by Filter + StretchResample(SrcImage, SrcX, SrcY, SrcWidth, SrcHeight, DstImage, DstX, DstY, + DstWidth, DstHeight, SamplingFilterFunctions[Filter], SamplingFilterRadii[Filter], + WrapEdges); +end; + +var + FullEdge: Boolean = True; + +{ The following resampling code is modified and extended code from Graphics32 + library by Alex A. Denisov.} +function BuildMappingTable(DstLow, DstHigh, SrcLow, SrcHigh, SrcImageWidth: LongInt; + Filter: TFilterFunction; Radius: Single; WrapEdges: Boolean): TMappingTable; +var + I, J, K, N: LongInt; + Left, Right, SrcWidth, DstWidth: LongInt; + Weight, Scale, Center, Count: Single; +begin + Result := nil; + K := 0; + SrcWidth := SrcHigh - SrcLow; + DstWidth := DstHigh - DstLow; + + // Check some special cases + if SrcWidth = 1 then + begin + SetLength(Result, DstWidth); + for I := 0 to DstWidth - 1 do + begin + SetLength(Result[I], 1); + Result[I][0].Pos := 0; + Result[I][0].Weight := 1.0; + end; + Exit; + end + else + if (SrcWidth = 0) or (DstWidth = 0) then + Exit; + + if FullEdge then + Scale := DstWidth / SrcWidth + else + Scale := (DstWidth - 1) / (SrcWidth - 1); + + SetLength(Result, DstWidth); + + // Pre-calculate filter contributions for a row or column + if Scale = 0.0 then + begin + Assert(Length(Result) = 1); + SetLength(Result[0], 1); + Result[0][0].Pos := (SrcLow + SrcHigh) div 2; + Result[0][0].Weight := 1.0; + end + else if Scale < 1.0 then + begin + // Sub-sampling - scales from bigger to smaller + Radius := Radius / Scale; + for I := 0 to DstWidth - 1 do + begin + if FullEdge then + Center := SrcLow - 0.5 + (I + 0.5) / Scale + else + Center := SrcLow + I / Scale; + Left := Floor(Center - Radius); + Right := Ceil(Center + Radius); + Count := -1.0; + for J := Left to Right do + begin + Weight := Filter((Center - J) * Scale) * Scale; + if Weight <> 0.0 then + begin + Count := Count + Weight; + K := Length(Result[I]); + SetLength(Result[I], K + 1); + Result[I][K].Pos := ClampInt(J, SrcLow, SrcHigh - 1); + Result[I][K].Weight := Weight; + end; + end; + if Length(Result[I]) = 0 then + begin + SetLength(Result[I], 1); + Result[I][0].Pos := Floor(Center); + Result[I][0].Weight := 1.0; + end + else if Count <> 0.0 then + Result[I][K div 2].Weight := Result[I][K div 2].Weight - Count; + end; + end + else // if Scale > 1.0 then + begin + // Super-sampling - scales from smaller to bigger + Scale := 1.0 / Scale; + for I := 0 to DstWidth - 1 do + begin + if FullEdge then + Center := SrcLow - 0.5 + (I + 0.5) * Scale + else + Center := SrcLow + I * Scale; + Left := Floor(Center - Radius); + Right := Ceil(Center + Radius); + Count := -1.0; + for J := Left to Right do + begin + Weight := Filter(Center - J); + if Weight <> 0.0 then + begin + Count := Count + Weight; + K := Length(Result[I]); + SetLength(Result[I], K + 1); + + if WrapEdges then + begin + if J < 0 then + N := SrcImageWidth + J + else if J >= SrcImageWidth then + N := J - SrcImageWidth + else + N := ClampInt(J, SrcLow, SrcHigh - 1); + end + else + N := ClampInt(J, SrcLow, SrcHigh - 1); + + Result[I][K].Pos := N; + Result[I][K].Weight := Weight; + end; + end; + if Count <> 0.0 then + Result[I][K div 2].Weight := Result[I][K div 2].Weight - Count; + end; + end; +end; + +procedure FindExtremes(const Map: TMappingTable; var MinPos, MaxPos: LongInt); +var + I, J: LongInt; +begin + if Length(Map) > 0 then + begin + MinPos := Map[0][0].Pos; + MaxPos := MinPos; + for I := 0 to Length(Map) - 1 do + for J := 0 to Length(Map[I]) - 1 do + begin + if MinPos > Map[I][J].Pos then + MinPos := Map[I][J].Pos; + if MaxPos < Map[I][J].Pos then + MaxPos := Map[I][J].Pos; + end; + end; +end; + +procedure StretchResample(const SrcImage: TImageData; SrcX, SrcY, SrcWidth, + SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth, + DstHeight: LongInt; Filter: TFilterFunction; Radius: Single; WrapEdges: Boolean); +var + MapX, MapY: TMappingTable; + I, J, X, Y: LongInt; + XMinimum, XMaximum: LongInt; + LineBufferFP: array of TColorFPRec; + ClusterX, ClusterY: TCluster; + Weight, AccumA, AccumR, AccumG, AccumB: Single; + DstLine: PByte; + SrcFloat: TColorFPRec; + Info: TImageFormatInfo; + BytesPerChannel: Integer; +begin + GetImageFormatInfo(SrcImage.Format, Info); + Assert(SrcImage.Format = DstImage.Format); + Assert(not Info.IsSpecial and not Info.IsIndexed); + BytesPerChannel := Info.BytesPerPixel div Info.ChannelCount; + + // Create horizontal and vertical mapping tables + MapX := BuildMappingTable(DstX, DstX + DstWidth, SrcX, SrcX + SrcWidth, + SrcImage.Width, Filter, Radius, WrapEdges); + MapY := BuildMappingTable(DstY, DstY + DstHeight, SrcY, SrcY + SrcHeight, + SrcImage.Height, Filter, Radius, WrapEdges); + + if (MapX = nil) or (MapY = nil) then + Exit; + + ClusterX := nil; + ClusterY := nil; + + try + // Find min and max X coords of pixels that will contribute to target image + FindExtremes(MapX, XMinimum, XMaximum); + + SetLength(LineBufferFP, XMaximum - XMinimum + 1); + // Following code works for the rest of data formats + for J := 0 to DstHeight - 1 do + begin + // First for each pixel in the current line sample vertically + // and store results in LineBuffer. Then sample horizontally + // using values in LineBuffer. + ClusterY := MapY[J]; + for X := XMinimum to XMaximum do + begin + // Clear accumulators + AccumA := 0; + AccumR := 0; + AccumG := 0; + AccumB := 0; + // For each pixel in line compute weighted sum of pixels + // in source column that will contribute to this pixel + for Y := 0 to Length(ClusterY) - 1 do + begin + // Accumulate this pixel's weighted value + Weight := ClusterY[Y].Weight; + SrcFloat := Info.GetPixelFP(@PByteArray(SrcImage.Bits)[(ClusterY[Y].Pos * SrcImage.Width + X) * Info.BytesPerPixel], @Info, nil); + AccumB := AccumB + SrcFloat.B * Weight; + AccumG := AccumG + SrcFloat.G * Weight; + AccumR := AccumR + SrcFloat.R * Weight; + AccumA := AccumA + SrcFloat.A * Weight; + end; + // Store accumulated value for this pixel in buffer + with LineBufferFP[X - XMinimum] do + begin + A := AccumA; + R := AccumR; + G := AccumG; + B := AccumB; + end; + end; + + DstLine := @PByteArray(DstImage.Bits)[((J + DstY) * DstImage.Width + DstX) * Info.BytesPerPixel]; + // Now compute final colors for targte pixels in the current row + // by sampling horizontally + for I := 0 to DstWidth - 1 do + begin + ClusterX := MapX[I]; + // Clear accumulator + AccumA := 0; + AccumR := 0; + AccumG := 0; + AccumB := 0; + // Compute weighted sum of values (which are already + // computed weighted sums of pixels in source columns stored in LineBuffer) + // that will contribute to the current target pixel + for X := 0 to Length(ClusterX) - 1 do + begin + Weight := ClusterX[X].Weight; + with LineBufferFP[ClusterX[X].Pos - XMinimum] do + begin + AccumB := AccumB + B * Weight; + AccumG := AccumG + G * Weight; + AccumR := AccumR + R * Weight; + AccumA := AccumA + A * Weight; + end; + end; + + // Now compute final color to be written to dest image + SrcFloat.A := AccumA; + SrcFloat.R := AccumR; + SrcFloat.G := AccumG; + SrcFloat.B := AccumB; + + Info.SetPixelFP(DstLine, @Info, nil, SrcFloat); + Inc(DstLine, Info.BytesPerPixel); + end; + end; + + finally + MapX := nil; + MapY := nil; + end; +end; + +procedure FillMipMapLevel(const BiggerLevel: TImageData; Width, Height: LongInt; + var SmallerLevel: TImageData); +var + Filter: TSamplingFilter; + Info: TImageFormatInfo; + CompatibleCopy: TImageData; +begin + Assert(TestImage(BiggerLevel)); + Filter := TSamplingFilter(GetOption(ImagingMipMapFilter)); + + // If we have special format image we must create copy to allow pixel access + GetImageFormatInfo(BiggerLevel.Format, Info); + if Info.IsSpecial then + begin + InitImage(CompatibleCopy); + CloneImage(BiggerLevel, CompatibleCopy); + ConvertImage(CompatibleCopy, ifDefault); + end + else + CompatibleCopy := BiggerLevel; + + // Create new smaller image + NewImage(Width, Height, CompatibleCopy.Format, SmallerLevel); + GetImageFormatInfo(CompatibleCopy.Format, Info); + // If input is indexed we must copy its palette + if Info.IsIndexed then + CopyPalette(CompatibleCopy.Palette, SmallerLevel.Palette, 0, 0, Info.PaletteEntries); + + if (Filter = sfNearest) or Info.IsIndexed then + begin + StretchNearest(CompatibleCopy, 0, 0, CompatibleCopy.Width, CompatibleCopy.Height, + SmallerLevel, 0, 0, Width, Height); + end + else + begin + StretchResample(CompatibleCopy, 0, 0, CompatibleCopy.Width, CompatibleCopy.Height, + SmallerLevel, 0, 0, Width, Height, Filter); + end; + + // Free copy and convert result to special format if necessary + if CompatibleCopy.Format <> BiggerLevel.Format then + begin + ConvertImage(SmallerLevel, BiggerLevel.Format); + FreeImage(CompatibleCopy); + end; +end; + + +{ Various format support functions } + +procedure CopyPixel(Src, Dest: Pointer; BytesPerPixel: LongInt); +begin + case BytesPerPixel of + 1: PByte(Dest)^ := PByte(Src)^; + 2: PWord(Dest)^ := PWord(Src)^; + 3: PColor24Rec(Dest)^ := PColor24Rec(Src)^; + 4: PLongWord(Dest)^ := PLongWord(Src)^; + 6: PColor48Rec(Dest)^ := PColor48Rec(Src)^; + 8: PInt64(Dest)^ := PInt64(Src)^; + 12: PColor96FPRec(Dest)^ := PColor96FPRec(Src)^; + 16: PColorFPRec(Dest)^ := PColorFPRec(Src)^; + end; +end; + +function ComparePixels(PixelA, PixelB: Pointer; BytesPerPixel: LongInt): Boolean; +begin + case BytesPerPixel of + 1: Result := PByte(PixelA)^ = PByte(PixelB)^; + 2: Result := PWord(PixelA)^ = PWord(PixelB)^; + 3: Result := (PWord(PixelA)^ = PWord(PixelB)^) and (PColor24Rec(PixelA).R = PColor24Rec(PixelB).R); + 4: Result := PLongWord(PixelA)^ = PLongWord(PixelB)^; + 6: Result := (PLongWord(PixelA)^ = PLongWord(PixelB)^) and (PColor48Rec(PixelA).R = PColor48Rec(PixelB).R); + 8: Result := PInt64(PixelA)^ = PInt64(PixelB)^; + 12: Result := (PFloatHelper(PixelA).Data = PFloatHelper(PixelB).Data) and + (PFloatHelper(PixelA).Data32 = PFloatHelper(PixelB).Data32); + 16: Result := (PFloatHelper(PixelA).Data = PFloatHelper(PixelB).Data) and + (PFloatHelper(PixelA).Data64 = PFloatHelper(PixelB).Data64); + else + Result := False; + end; +end; + +procedure TranslatePixel(SrcPixel, DstPixel: Pointer; SrcFormat, + DstFormat: TImageFormat; SrcPalette, DstPalette: PPalette32); +var + SrcInfo, DstInfo: PImageFormatInfo; + PixFP: TColorFPRec; +begin + SrcInfo := FInfos[SrcFormat]; + DstInfo := FInfos[DstFormat]; + + PixFP := GetPixelFPGeneric(SrcPixel, SrcInfo, SrcPalette); + SetPixelFPGeneric(DstPixel, DstInfo, DstPalette, PixFP); +end; + +procedure ClampFloatPixel(var PixF: TColorFPRec); +begin + if PixF.A > 1.0 then + PixF.A := 1.0; + if PixF.R > 1.0 then + PixF.R := 1.0; + if PixF.G > 1.0 then + PixF.G := 1.0; + if PixF.B > 1.0 then + PixF.B := 1.0; + + if PixF.A < 0.0 then + PixF.A := 0.0; + if PixF.R < 0.0 then + PixF.R := 0.0; + if PixF.G < 0.0 then + PixF.G := 0.0; + if PixF.B < 0.0 then + PixF.B := 0.0; +end; + +procedure ConvertToPixel32(SrcPix: PByte; DestPix: PColor32Rec; + const SrcInfo: TImageFormatInfo; SrcPalette: PPalette32); +begin + case SrcInfo.Format of + ifIndex8: + begin + DestPix^ := SrcPalette[SrcPix^]; + end; + ifGray8: + begin + DestPix.R := SrcPix^; + DestPix.G := SrcPix^; + DestPix.B := SrcPix^; + DestPix.A := 255; + end; + ifA8Gray8: + begin + DestPix.R := SrcPix^; + DestPix.G := SrcPix^; + DestPix.B := SrcPix^; + DestPix.A := PWordRec(SrcPix).High; + end; + ifGray16: + begin + DestPix.R := PWord(SrcPix)^ shr 8; + DestPix.G := DestPix.R; + DestPix.B := DestPix.R; + DestPix.A := 255; + end; + ifR8G8B8: + begin + DestPix.Color24Rec := PColor24Rec(SrcPix)^; + DestPix.A := 255; + end; + ifA8R8G8B8: + begin + DestPix^ := PColor32Rec(SrcPix)^; + end; + ifR16G16B16: + begin + DestPix.R := PColor48Rec(SrcPix).R shr 8; + DestPix.G := PColor48Rec(SrcPix).G shr 8; + DestPix.B := PColor48Rec(SrcPix).B shr 8; + DestPix.A := 255; + end; + ifA16R16G16B16: + begin + DestPix.R := PColor64Rec(SrcPix).R shr 8; + DestPix.G := PColor64Rec(SrcPix).G shr 8; + DestPix.B := PColor64Rec(SrcPix).B shr 8; + DestPix.A := PColor64Rec(SrcPix).A shr 8; + end; + else + DestPix^ := SrcInfo.GetPixel32(SrcPix, @SrcInfo, SrcPalette); + end; +end; + +procedure AddPadBytes(DataIn: Pointer; DataOut: Pointer; Width, Height, + Bpp, WidthBytes: LongInt); +var + I, W: LongInt; +begin + W := Width * Bpp; + for I := 0 to Height - 1 do + Move(PByteArray(DataIn)[I * W], PByteArray(DataOut)[I * WidthBytes], W); +end; + +procedure RemovePadBytes(DataIn: Pointer; DataOut: Pointer; Width, Height, + Bpp, WidthBytes: LongInt); +var + I, W: LongInt; +begin + W := Width * Bpp; + for I := 0 to Height - 1 do + Move(PByteArray(DataIn)[I * WidthBytes], PByteArray(DataOut)[I * W], W); +end; + +procedure Convert1To8(DataIn, DataOut: PByte; Width, Height, + WidthBytes: LongInt; ScaleTo8Bits: Boolean); +const + Mask1: array[0..7] of Byte = ($80, $40, $20, $10, $08, $04, $02, $01); + Shift1: array[0..7] of Byte = (7, 6, 5, 4, 3, 2, 1, 0); + Scaling: Byte = 255; +var + X, Y: LongInt; + InArray: PByteArray absolute DataIn; +begin + for Y := 0 to Height - 1 do + for X := 0 to Width - 1 do + begin + DataOut^ := (InArray[Y * WidthBytes + X shr 3] and Mask1[X and 7]) shr Shift1[X and 7]; + if ScaleTo8Bits then + DataOut^ := DataOut^ * Scaling; + Inc(DataOut); + end; +end; + +procedure Convert2To8(DataIn, DataOut: PByte; Width, Height, + WidthBytes: LongInt; ScaleTo8Bits: Boolean); +const + Mask2: array[0..3] of Byte = ($C0, $30, $0C, $03); + Shift2: array[0..3] of Byte = (6, 4, 2, 0); + Scaling: Byte = 85; +var + X, Y: LongInt; + InArray: PByteArray absolute DataIn; +begin + for Y := 0 to Height - 1 do + for X := 0 to Width - 1 do + begin + DataOut^ := (InArray[Y * WidthBytes + X shr 2] and Mask2[X and 3]) shr Shift2[X and 3]; + if ScaleTo8Bits then + DataOut^ := DataOut^ * Scaling; + Inc(DataOut); + end; +end; + +procedure Convert4To8(DataIn, DataOut: PByte; Width, Height, + WidthBytes: LongInt; ScaleTo8Bits: Boolean); +const + Mask4: array[0..1] of Byte = ($F0, $0F); + Shift4: array[0..1] of Byte = (4, 0); + Scaling: Byte = 17; +var + X, Y: LongInt; + InArray: PByteArray absolute DataIn; +begin + for Y := 0 to Height - 1 do + for X := 0 to Width - 1 do + begin + DataOut^ := (InArray[Y * WidthBytes + X shr 1] and Mask4[X and 1]) shr Shift4[X and 1]; + if ScaleTo8Bits then + DataOut^ := DataOut^ * Scaling; + Inc(DataOut); + end; +end; + +function Has16BitImageAlpha(NumPixels: LongInt; Data: PWord): Boolean; +var + I: LongInt; +begin + Result := False; + for I := 0 to NumPixels - 1 do + begin + if Data^ >= 1 shl 15 then + begin + Result := True; + Exit; + end; + Inc(Data); + end; +end; + +function Has32BitImageAlpha(NumPixels: LongInt; Data: PLongWord): Boolean; +var + I: LongInt; +begin + Result := False; + for I := 0 to NumPixels - 1 do + begin + if Data^ >= 1 shl 24 then + begin + Result := True; + Exit; + end; + Inc(Data); + end; +end; + +function PaletteHasAlpha(Palette: PPalette32; PaletteEntries: Integer): Boolean; +var + I: Integer; +begin + for I := 0 to PaletteEntries - 1 do + begin + if Palette[I].A <> 255 then + begin + Result := True; + Exit; + end; + end; + Result := False; +end; + +function PaletteIsGrayScale(Palette: PPalette32; PaletteEntries: Integer): Boolean; +var + I: Integer; +begin + for I := 0 to PaletteEntries - 1 do + begin + if (Palette[I].R <> Palette[I].G) or (Palette[I].R <> Palette[I].B) then + begin + Result := False; + Exit; + end; + end; + Result := True; +end; + +function GetScanLine(ImageBits: Pointer; const FormatInfo: TImageFormatInfo; + LineWidth, Index: LongInt): Pointer; +var + LineBytes: LongInt; +begin + Assert(not FormatInfo.IsSpecial); + LineBytes := FormatInfo.GetPixelsSize(FormatInfo.Format, LineWidth, 1); + Result := @PByteArray(ImageBits)[Index * LineBytes]; +end; + +function IsImageFormatValid(Format: TImageFormat): Boolean; +begin + Result := FInfos[Format] <> nil; +end; + +const + HalfMin: Single = 5.96046448e-08; // Smallest positive half + HalfMinNorm: Single = 6.10351562e-05; // Smallest positive normalized half + HalfMax: Single = 65504.0; // Largest positive half + HalfEpsilon: Single = 0.00097656; // Smallest positive e for which half (1.0 + e) != half (1.0) + HalfNaN: THalfFloat = 65535; + HalfPosInf: THalfFloat = 31744; + HalfNegInf: THalfFloat = 64512; + + +{ + Half/Float conversions inspired by half class from OpenEXR library. + + Float (Pascal Single type) is an IEEE 754 single-precision + floating point number. + + Bit layout of Single: + + 31 (msb) + | + | 30 23 + | | | + | | | 22 0 (lsb) + | | | | | + X XXXXXXXX XXXXXXXXXXXXXXXXXXXXXXX + s e m + + Bit layout of half: + + 15 (msb) + | + | 14 10 + | | | + | | | 9 0 (lsb) + | | | | | + X XXXXX XXXXXXXXXX + s e m + + S is the sign-bit, e is the exponent and m is the significand (mantissa). +} + +function HalfToFloat(Half: THalfFloat): Single; +var + Dst, Sign, Mantissa: LongWord; + Exp: LongInt; +begin + // Extract sign, exponent, and mantissa from half number + Sign := Half shr 15; + Exp := (Half and $7C00) shr 10; + Mantissa := Half and 1023; + + if (Exp > 0) and (Exp < 31) then + begin + // Common normalized number + Exp := Exp + (127 - 15); + Mantissa := Mantissa shl 13; + Dst := (Sign shl 31) or (LongWord(Exp) shl 23) or Mantissa; + // Result := Power(-1, Sign) * Power(2, Exp - 15) * (1 + Mantissa / 1024); + end + else if (Exp = 0) and (Mantissa = 0) then + begin + // Zero - preserve sign + Dst := Sign shl 31; + end + else if (Exp = 0) and (Mantissa <> 0) then + begin + // Denormalized number - renormalize it + while (Mantissa and $00000400) = 0 do + begin + Mantissa := Mantissa shl 1; + Dec(Exp); + end; + Inc(Exp); + Mantissa := Mantissa and not $00000400; + // Now assemble normalized number + Exp := Exp + (127 - 15); + Mantissa := Mantissa shl 13; + Dst := (Sign shl 31) or (LongWord(Exp) shl 23) or Mantissa; + // Result := Power(-1, Sign) * Power(2, -14) * (Mantissa / 1024); + end + else if (Exp = 31) and (Mantissa = 0) then + begin + // +/- infinity + Dst := (Sign shl 31) or $7F800000; + end + else //if (Exp = 31) and (Mantisa <> 0) then + begin + // Not a number - preserve sign and mantissa + Dst := (Sign shl 31) or $7F800000 or (Mantissa shl 13); + end; + + // Reinterpret LongWord as Single + Result := PSingle(@Dst)^; +end; + +function FloatToHalf(Float: Single): THalfFloat; +var + Src: LongWord; + Sign, Exp, Mantissa: LongInt; +begin + Src := PLongWord(@Float)^; + // Extract sign, exponent, and mantissa from Single number + Sign := Src shr 31; + Exp := LongInt((Src and $7F800000) shr 23) - 127 + 15; + Mantissa := Src and $007FFFFF; + + if (Exp > 0) and (Exp < 30) then + begin + // Simple case - round the significand and combine it with the sign and exponent + Result := (Sign shl 15) or (Exp shl 10) or ((Mantissa + $00001000) shr 13); + end + else if Src = 0 then + begin + // Input float is zero - return zero + Result := 0; + end + else + begin + // Difficult case - lengthy conversion + if Exp <= 0 then + begin + if Exp < -10 then + begin + // Input float's value is less than HalfMin, return zero + Result := 0; + end + else + begin + // Float is a normalized Single whose magnitude is less than HalfNormMin. + // We convert it to denormalized half. + Mantissa := (Mantissa or $00800000) shr (1 - Exp); + // Round to nearest + if (Mantissa and $00001000) > 0 then + Mantissa := Mantissa + $00002000; + // Assemble Sign and Mantissa (Exp is zero to get denormalized number) + Result := (Sign shl 15) or (Mantissa shr 13); + end; + end + else if Exp = 255 - 127 + 15 then + begin + if Mantissa = 0 then + begin + // Input float is infinity, create infinity half with original sign + Result := (Sign shl 15) or $7C00; + end + else + begin + // Input float is NaN, create half NaN with original sign and mantissa + Result := (Sign shl 15) or $7C00 or (Mantissa shr 13); + end; + end + else + begin + // Exp is > 0 so input float is normalized Single + + // Round to nearest + if (Mantissa and $00001000) > 0 then + begin + Mantissa := Mantissa + $00002000; + if (Mantissa and $00800000) > 0 then + begin + Mantissa := 0; + Exp := Exp + 1; + end; + end; + + if Exp > 30 then + begin + // Exponent overflow - return infinity half + Result := (Sign shl 15) or $7C00; + end + else + // Assemble normalized half + Result := (Sign shl 15) or (Exp shl 10) or (Mantissa shr 13); + end; + end; +end; + +function ColorHalfToFloat(ColorHF: TColorHFRec): TColorFPRec; +begin + Result.A := HalfToFloat(ColorHF.A); + Result.R := HalfToFloat(ColorHF.R); + Result.G := HalfToFloat(ColorHF.G); + Result.B := HalfToFloat(ColorHF.B); +end; + +function ColorFloatToHalf(ColorFP: TColorFPRec): TColorHFRec; +begin + Result.A := FloatToHalf(ColorFP.A); + Result.R := FloatToHalf(ColorFP.R); + Result.G := FloatToHalf(ColorFP.G); + Result.B := FloatToHalf(ColorFP.B); +end; + +function Color32ToGray(Color32: TColor32): Byte; +begin + Result := Round(GrayConv.R * TColor32Rec(Color32).R + + GrayConv.G * TColor32Rec(Color32).G + + GrayConv.B * TColor32Rec(Color32).B); +end; + +procedure VisualizePalette(Pal: PPalette32; Entries: Integer; out PalImage: TImageData); +var + I: Integer; + Pix: PColor32; +begin + InitImage(PalImage); + NewImage(Entries, 1, ifA8R8G8B8, PalImage); + Pix := PalImage.Bits; + for I := 0 to Entries - 1 do + begin + Pix^ := Pal[I].Color; + Inc(Pix); + end; +end; + + +{ Pixel readers/writers for different image formats } + +procedure ChannelGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo; + var Pix: TColor64Rec); +var + A, R, G, B: Byte; +begin + FillChar(Pix, SizeOf(Pix), 0); + // returns 64 bit color value with 16 bits for each channel + case SrcInfo.BytesPerPixel of + 1: + begin + PFGetARGB(SrcInfo.PixelFormat^, Src^, A, R, G, B); + Pix.A := A shl 8; + Pix.R := R shl 8; + Pix.G := G shl 8; + Pix.B := B shl 8; + end; + 2: + begin + PFGetARGB(SrcInfo.PixelFormat^, PWord(Src)^, A, R, G, B); + Pix.A := A shl 8; + Pix.R := R shl 8; + Pix.G := G shl 8; + Pix.B := B shl 8; + end; + 3: + with Pix do + begin + R := MulDiv(PColor24Rec(Src).R, 65535, 255); + G := MulDiv(PColor24Rec(Src).G, 65535, 255); + B := MulDiv(PColor24Rec(Src).B, 65535, 255); + end; + 4: + with Pix do + begin + A := MulDiv(PColor32Rec(Src).A, 65535, 255); + R := MulDiv(PColor32Rec(Src).R, 65535, 255); + G := MulDiv(PColor32Rec(Src).G, 65535, 255); + B := MulDiv(PColor32Rec(Src).B, 65535, 255); + end; + 6: + with Pix do + begin + R := PColor48Rec(Src).R; + G := PColor48Rec(Src).G; + B := PColor48Rec(Src).B; + end; + 8: Pix.Color := PColor64(Src)^; + end; + // if src has no alpha, we set it to max (otherwise we would have to + // test if dest has alpha or not in each ChannelToXXX function) + if not SrcInfo.HasAlphaChannel then + Pix.A := 65535; + + if SrcInfo.IsRBSwapped then + SwapValues(Pix.R, Pix.B); +end; + +procedure ChannelSetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo; + const Pix: TColor64Rec); +var + PixW: TColor64Rec; +begin + PixW := Pix; + if DstInfo.IsRBSwapped then + SwapValues(PixW.R, PixW.B); + // Pix contains 64 bit color value with 16 bit for each channel + case DstInfo.BytesPerPixel of + 1: Dst^ := PFSetARGB(DstInfo.PixelFormat^, PixW.A shr 8, + PixW.R shr 8, PixW.G shr 8, PixW.B shr 8); + 2: PWord(Dst)^ := PFSetARGB(DstInfo.PixelFormat^, PixW.A shr 8, + PixW.R shr 8, PixW.G shr 8, PixW.B shr 8); + 3: + with PColor24Rec(Dst)^ do + begin + R := MulDiv(PixW.R, 255, 65535); + G := MulDiv(PixW.G, 255, 65535); + B := MulDiv(PixW.B, 255, 65535); + end; + 4: + with PColor32Rec(Dst)^ do + begin + A := MulDiv(PixW.A, 255, 65535); + R := MulDiv(PixW.R, 255, 65535); + G := MulDiv(PixW.G, 255, 65535); + B := MulDiv(PixW.B, 255, 65535); + end; + 6: + with PColor48Rec(Dst)^ do + begin + R := PixW.R; + G := PixW.G; + B := PixW.B; + end; + 8: PColor64(Dst)^ := PixW.Color; + end; +end; + +procedure GrayGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo; + var Gray: TColor64Rec; var Alpha: Word); +begin + FillChar(Gray, SizeOf(Gray), 0); + // Source alpha is scaled to 16 bits and stored in Alpha, + // grayscale value is scaled to 64 bits and stored in Gray + case SrcInfo.BytesPerPixel of + 1: Gray.A := MulDiv(Src^, 65535, 255); + 2: + if SrcInfo.HasAlphaChannel then + with PWordRec(Src)^ do + begin + Alpha := MulDiv(High, 65535, 255); + Gray.A := MulDiv(Low, 65535, 255); + end + else + Gray.A := PWord(Src)^; + 4: + if SrcInfo.HasAlphaChannel then + with PLongWordRec(Src)^ do + begin + Alpha := High; + Gray.A := Low; + end + else + with PLongWordRec(Src)^ do + begin + Gray.A := High; + Gray.R := Low; + end; + 8: Gray.Color := PColor64(Src)^; + end; + // if src has no alpha, we set it to max (otherwise we would have to + // test if dest has alpha or not in each GrayToXXX function) + if not SrcInfo.HasAlphaChannel then + Alpha := 65535; +end; + +procedure GraySetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo; + const Gray: TColor64Rec; Alpha: Word); +begin + // Gray contains grayscale value scaled to 64 bits, Alpha contains + // alpha value scaled to 16 bits + case DstInfo.BytesPerPixel of + 1: Dst^ := MulDiv(Gray.A, 255, 65535); + 2: + if DstInfo.HasAlphaChannel then + with PWordRec(Dst)^ do + begin + High := MulDiv(Alpha, 255, 65535); + Low := MulDiv(Gray.A, 255, 65535); + end + else + PWord(Dst)^ := Gray.A; + 4: + if DstInfo.HasAlphaChannel then + with PLongWordRec(Dst)^ do + begin + High := Alpha; + Low := Gray.A; + end + else + with PLongWordRec(Dst)^ do + begin + High := Gray.A; + Low := Gray.R; + end; + 8: PColor64(Dst)^ := Gray.Color; + end; +end; + +procedure FloatGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo; + var Pix: TColorFPRec); +var + PixHF: TColorHFRec; +begin + Assert(SrcInfo.BytesPerPixel in [2, 4, 8, 12, 16]); + + if SrcInfo.BytesPerPixel in [4, 12, 16] then + begin + // IEEE 754 single-precision channels + FillChar(Pix, SizeOf(Pix), 0); + case SrcInfo.BytesPerPixel of + 4: Pix.R := PSingle(Src)^; + 12: Pix.Color96Rec := PColor96FPRec(Src)^; + 16: Pix := PColorFPRec(Src)^; + end; + end + else + begin + // Half float channels + FillChar(PixHF, SizeOf(PixHF), 0); + case SrcInfo.BytesPerPixel of + 2: PixHF.R := PHalfFloat(Src)^; + 8: PixHF := PColorHFRec(Src)^; + end; + Pix := ColorHalfToFloat(PixHF); + end; + + // If src has no alpha, we set it to max (otherwise we would have to + // test if dest has alpha or not in each FloatToXXX function) + if not SrcInfo.HasAlphaChannel then + Pix.A := 1.0; + if SrcInfo.IsRBSwapped then + SwapValues(Pix.R, Pix.B); +end; + +procedure FloatSetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo; + const Pix: TColorFPRec); +var + PixW: TColorFPRec; + PixHF: TColorHFRec; +begin + Assert(DstInfo.BytesPerPixel in [2, 4, 8, 12, 16]); + + PixW := Pix; + if DstInfo.IsRBSwapped then + SwapValues(PixW.R, PixW.B); + + if DstInfo.BytesPerPixel in [4, 12, 16] then + begin + case DstInfo.BytesPerPixel of + 4: PSingle(Dst)^ := PixW.R; + 12: PColor96FPRec(Dst)^:= PixW.Color96Rec; + 16: PColorFPRec(Dst)^ := PixW; + end; + end + else + begin + PixHF := ColorFloatToHalf(PixW); + case DstInfo.BytesPerPixel of + 2: PHalfFloat(Dst)^ := PixHF.R; + 8: PColorHFRec(Dst)^ := PixHF; + end; + end; +end; + +procedure IndexGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo; + var Index: LongWord); +begin + case SrcInfo.BytesPerPixel of + 1: Index := Src^; + end; +end; + +procedure IndexSetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo; + Index: LongWord); +begin + case DstInfo.BytesPerPixel of + 1: Dst^ := Byte(Index); + 2: PWord(Dst)^ := Word(Index); + 4: PLongWord(Dst)^ := Index; + end; +end; + + +{ Pixel readers/writers for 32bit and FP colors} + +function GetPixel32Generic(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColor32Rec; +var + Pix64: TColor64Rec; + PixF: TColorFPRec; + Alpha: Word; + Index: LongWord; +begin + if Info.Format = ifA8R8G8B8 then + begin + Result := PColor32Rec(Bits)^ + end + else if Info.Format = ifR8G8B8 then + begin + PColor24Rec(@Result)^ := PColor24Rec(Bits)^; + Result.A := $FF; + end + else if Info.IsFloatingPoint then + begin + FloatGetSrcPixel(Bits, Info, PixF); + Result.A := ClampToByte(Round(PixF.A * 255.0)); + Result.R := ClampToByte(Round(PixF.R * 255.0)); + Result.G := ClampToByte(Round(PixF.G * 255.0)); + Result.B := ClampToByte(Round(PixF.B * 255.0)); + end + else if Info.HasGrayChannel then + begin + GrayGetSrcPixel(Bits, Info, Pix64, Alpha); + Result.A := MulDiv(Alpha, 255, 65535); + Result.R := MulDiv(Pix64.A, 255, 65535); + Result.G := MulDiv(Pix64.A, 255, 65535); + Result.B := MulDiv(Pix64.A, 255, 65535); + end + else if Info.IsIndexed then + begin + IndexGetSrcPixel(Bits, Info, Index); + Result := Palette[Index]; + end + else + begin + ChannelGetSrcPixel(Bits, Info, Pix64); + Result.A := MulDiv(Pix64.A, 255, 65535); + Result.R := MulDiv(Pix64.R, 255, 65535); + Result.G := MulDiv(Pix64.G, 255, 65535); + Result.B := MulDiv(Pix64.B, 255, 65535); + end; +end; + +procedure SetPixel32Generic(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColor32Rec); +var + Pix64: TColor64Rec; + PixF: TColorFPRec; + Alpha: Word; + Index: LongWord; +begin + if Info.Format = ifA8R8G8B8 then + begin + PColor32Rec(Bits)^ := Color + end + else if Info.Format = ifR8G8B8 then + begin + PColor24Rec(Bits)^ := Color.Color24Rec; + end + else if Info.IsFloatingPoint then + begin + PixF.A := Color.A * OneDiv8Bit; + PixF.R := Color.R * OneDiv8Bit; + PixF.G := Color.G * OneDiv8Bit; + PixF.B := Color.B * OneDiv8Bit; + FloatSetDstPixel(Bits, Info, PixF); + end + else if Info.HasGrayChannel then + begin + Alpha := MulDiv(Color.A, 65535, 255); + Pix64.Color := 0; + Pix64.A := MulDiv(Round(GrayConv.R * Color.R + GrayConv.G * Color.G + + GrayConv.B * Color.B), 65535, 255); + GraySetDstPixel(Bits, Info, Pix64, Alpha); + end + else if Info.IsIndexed then + begin + Index := FindColor(Palette, Info.PaletteEntries, Color.Color); + IndexSetDstPixel(Bits, Info, Index); + end + else + begin + Pix64.A := MulDiv(Color.A, 65535, 255); + Pix64.R := MulDiv(Color.R, 65535, 255); + Pix64.G := MulDiv(Color.G, 65535, 255); + Pix64.B := MulDiv(Color.B, 65535, 255); + ChannelSetDstPixel(Bits, Info, Pix64); + end; +end; + +function GetPixelFPGeneric(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColorFPRec; +var + Pix32: TColor32Rec; + Pix64: TColor64Rec; + Alpha: Word; + Index: LongWord; +begin + if Info.IsFloatingPoint then + begin + FloatGetSrcPixel(Bits, Info, Result); + end + else if Info.HasGrayChannel then + begin + GrayGetSrcPixel(Bits, Info, Pix64, Alpha); + Result.A := Alpha * OneDiv16Bit; + Result.R := Pix64.A * OneDiv16Bit; + Result.G := Pix64.A * OneDiv16Bit; + Result.B := Pix64.A * OneDiv16Bit; + end + else if Info.IsIndexed then + begin + IndexGetSrcPixel(Bits, Info, Index); + Pix32 := Palette[Index]; + Result.A := Pix32.A * OneDiv8Bit; + Result.R := Pix32.R * OneDiv8Bit; + Result.G := Pix32.G * OneDiv8Bit; + Result.B := Pix32.B * OneDiv8Bit; + end + else + begin + ChannelGetSrcPixel(Bits, Info, Pix64); + Result.A := Pix64.A * OneDiv16Bit; + Result.R := Pix64.R * OneDiv16Bit; + Result.G := Pix64.G * OneDiv16Bit; + Result.B := Pix64.B * OneDiv16Bit; + end; +end; + +procedure SetPixelFPGeneric(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColorFPRec); +var + Pix32: TColor32Rec; + Pix64: TColor64Rec; + Alpha: Word; + Index: LongWord; +begin + if Info.IsFloatingPoint then + begin + FloatSetDstPixel(Bits, Info, Color); + end + else if Info.HasGrayChannel then + begin + Alpha := ClampToWord(Round(Color.A * 65535.0)); + Pix64.Color := 0; + Pix64.A := ClampToWord(Round((GrayConv.R * Color.R + GrayConv.G * Color.G + + GrayConv.B * Color.B) * 65535.0)); + GraySetDstPixel(Bits, Info, Pix64, Alpha); + end + else if Info.IsIndexed then + begin + Pix32.A := ClampToByte(Round(Color.A * 255.0)); + Pix32.R := ClampToByte(Round(Color.R * 255.0)); + Pix32.G := ClampToByte(Round(Color.G * 255.0)); + Pix32.B := ClampToByte(Round(Color.B * 255.0)); + Index := FindColor(Palette, Info.PaletteEntries, Pix32.Color); + IndexSetDstPixel(Bits, Info, Index); + end + else + begin + Pix64.A := ClampToWord(Round(Color.A * 65535.0)); + Pix64.R := ClampToWord(Round(Color.R * 65535.0)); + Pix64.G := ClampToWord(Round(Color.G * 65535.0)); + Pix64.B := ClampToWord(Round(Color.B * 65535.0)); + ChannelSetDstPixel(Bits, Info, Pix64); + end; +end; + + +{ Image format conversion functions } + +procedure ChannelToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo); +var + I: LongInt; + Pix64: TColor64Rec; +begin + // two most common conversions (RGB->ARGB and ARGB->RGB for 24/32 bit + // images) are made separately from general ARGB conversion to + // make them faster + if (SrcInfo.BytesPerPixel = 3) and (DstInfo.BytesPerPixel = 4) then + for I := 0 to NumPixels - 1 do + begin + PColor24Rec(Dst)^ := PColor24Rec(Src)^; + if DstInfo.HasAlphaChannel then + PColor32Rec(Dst).A := 255; + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end + else + if (SrcInfo.BytesPerPixel = 4) and (DstInfo.BytesPerPixel = 3) then + for I := 0 to NumPixels - 1 do + begin + PColor24Rec(Dst)^ := PColor24Rec(Src)^; + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end + else + for I := 0 to NumPixels - 1 do + begin + // general ARGB conversion + ChannelGetSrcPixel(Src, SrcInfo, Pix64); + ChannelSetDstPixel(Dst, DstInfo, Pix64); + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end; +end; + +procedure ChannelToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo); +var + I: LongInt; + Pix64: TColor64Rec; + Alpha: Word; +begin + // two most common conversions (R8G8B8->Gray8 nad A8R8G8B8->Gray8) + // are made separately from general conversions to make them faster + if (SrcInfo.BytesPerPixel in [3, 4]) and (DstInfo.Format = ifGray8) then + for I := 0 to NumPixels - 1 do + begin + Dst^ := Round(GrayConv.R * PColor24Rec(Src).R + GrayConv.G * PColor24Rec(Src).G + + GrayConv.B * PColor24Rec(Src).B); + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end + else + for I := 0 to NumPixels - 1 do + begin + ChannelGetSrcPixel(Src, SrcInfo, Pix64); + + // alpha is saved from source pixel to Alpha, + // Gray value is computed and set to highest word of Pix64 so + // Pix64.Color contains grayscale value scaled to 64 bits + Alpha := Pix64.A; + with GrayConv do + Pix64.A := Round(R * Pix64.R + G * Pix64.G + B * Pix64.B); + + GraySetDstPixel(Dst, DstInfo, Pix64, Alpha); + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end; +end; + +procedure ChannelToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo); +var + I: LongInt; + Pix64: TColor64Rec; + PixF: TColorFPRec; +begin + for I := 0 to NumPixels - 1 do + begin + ChannelGetSrcPixel(Src, SrcInfo, Pix64); + + // floating point channel values are scaled to 1.0 + PixF.A := Pix64.A * OneDiv16Bit; + PixF.R := Pix64.R * OneDiv16Bit; + PixF.G := Pix64.G * OneDiv16Bit; + PixF.B := Pix64.B * OneDiv16Bit; + + FloatSetDstPixel(Dst, DstInfo, PixF); + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end; +end; + +procedure ChannelToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo; DstPal: PPalette32); +begin + ReduceColorsMedianCut(NumPixels, Src, Dst, SrcInfo, DstInfo, DstInfo.PaletteEntries, + GetOption(ImagingColorReductionMask), DstPal); +end; + +procedure GrayToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo); +var + I: LongInt; + Gray: TColor64Rec; + Alpha: Word; +begin + // two most common conversions (Gray8->Gray16 nad Gray16->Gray8) + // are made separately from general conversions to make them faster + if (SrcInfo.Format = ifGray8) and (DstInfo.Format = ifGray16) then + begin + for I := 0 to NumPixels - 1 do + PWordArray(Dst)[I] := PByteArray(Src)[I] shl 8; + end + else + begin + if (DstInfo.Format = ifGray8) and (SrcInfo.Format = ifGray16) then + begin + for I := 0 to NumPixels - 1 do + PByteArray(Dst)[I] := PWordArray(Src)[I] shr 8; + end + else + for I := 0 to NumPixels - 1 do + begin + // general grayscale conversion + GrayGetSrcPixel(Src, SrcInfo, Gray, Alpha); + GraySetDstPixel(Dst, DstInfo, Gray, Alpha); + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end; + end; +end; + +procedure GrayToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo); +var + I: LongInt; + Pix64: TColor64Rec; + Alpha: Word; +begin + // two most common conversions (Gray8->R8G8B8 nad Gray8->A8R8G8B8) + // are made separately from general conversions to make them faster + if (DstInfo.BytesPerPixel in [3, 4]) and (SrcInfo.Format = ifGray8) then + for I := 0 to NumPixels - 1 do + begin + PColor24Rec(Dst).R := Src^; + PColor24Rec(Dst).G := Src^; + PColor24Rec(Dst).B := Src^; + if DstInfo.HasAlphaChannel then + PColor32Rec(Dst).A := $FF; + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end + else + for I := 0 to NumPixels - 1 do + begin + GrayGetSrcPixel(Src, SrcInfo, Pix64, Alpha); + + // most significant word of grayscale value is used for + // each channel and alpha channel is set to Alpha + Pix64.R := Pix64.A; + Pix64.G := Pix64.A; + Pix64.B := Pix64.A; + Pix64.A := Alpha; + + ChannelSetDstPixel(Dst, DstInfo, Pix64); + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end; +end; + +procedure GrayToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo); +var + I: LongInt; + Gray: TColor64Rec; + PixF: TColorFPRec; + Alpha: Word; +begin + for I := 0 to NumPixels - 1 do + begin + GrayGetSrcPixel(Src, SrcInfo, Gray, Alpha); + // most significant word of grayscale value is used for + // each channel and alpha channel is set to Alpha + // then all is scaled to 0..1 + PixF.R := Gray.A * OneDiv16Bit; + PixF.G := Gray.A * OneDiv16Bit; + PixF.B := Gray.A * OneDiv16Bit; + PixF.A := Alpha * OneDiv16Bit; + + FloatSetDstPixel(Dst, DstInfo, PixF); + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end; +end; + +procedure GrayToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo; DstPal: PPalette32); +var + I: LongInt; + Idx: LongWord; + Gray: TColor64Rec; + Alpha, Shift: Word; +begin + FillGrayscalePalette(DstPal, DstInfo.PaletteEntries); + Shift := Log2Int(DstInfo.PaletteEntries); + // most common conversion (Gray8->Index8) + // is made separately from general conversions to make it faster + if (SrcInfo.Format = ifGray8) and (DstInfo.Format = ifIndex8) then + for I := 0 to NumPixels - 1 do + begin + Dst^ := Src^; + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end + else + for I := 0 to NumPixels - 1 do + begin + // gray value is read from src and index to precomputed + // grayscale palette is computed and written to dst + // (we assume here that there will be no more than 65536 palette + // entries in dst format, gray value is shifted so the highest + // gray value match the highest possible index in palette) + GrayGetSrcPixel(Src, SrcInfo, Gray, Alpha); + Idx := Gray.A shr (16 - Shift); + IndexSetDstPixel(Dst, DstInfo, Idx); + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end; +end; + +procedure FloatToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo); +var + I: LongInt; + PixF: TColorFPRec; +begin + for I := 0 to NumPixels - 1 do + begin + // general floating point conversion + FloatGetSrcPixel(Src, SrcInfo, PixF); + FloatSetDstPixel(Dst, DstInfo, PixF); + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end; +end; + +procedure FloatToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo); +var + I: LongInt; + Pix64: TColor64Rec; + PixF: TColorFPRec; +begin + for I := 0 to NumPixels - 1 do + begin + FloatGetSrcPixel(Src, SrcInfo, PixF); + ClampFloatPixel(PixF); + + // floating point channel values are scaled to 1.0 + Pix64.A := ClampToWord(Round(PixF.A * 65535)); + Pix64.R := ClampToWord(Round(PixF.R * 65535)); + Pix64.G := ClampToWord(Round(PixF.G * 65535)); + Pix64.B := ClampToWord(Round(PixF.B * 65535)); + + ChannelSetDstPixel(Dst, DstInfo, Pix64); + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end; +end; + +procedure FloatToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo); +var + I: LongInt; + PixF: TColorFPRec; + Gray: TColor64Rec; + Alpha: Word; +begin + for I := 0 to NumPixels - 1 do + begin + FloatGetSrcPixel(Src, SrcInfo, PixF); + ClampFloatPixel(PixF); + + // alpha is saved from source pixel to Alpha, + // Gray value is computed and set to highest word of Pix64 so + // Pix64.Color contains grayscale value scaled to 64 bits + Alpha := ClampToWord(Round(PixF.A * 65535.0)); + Gray.A := ClampToWord(Round((GrayConv.R * PixF.R + GrayConv.G * PixF.G + + GrayConv.B * PixF.B) * 65535.0)); + + GraySetDstPixel(Dst, DstInfo, Gray, Alpha); + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end; +end; + +procedure FloatToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo; DstPal: PPalette32); +begin + ReduceColorsMedianCut(NumPixels, Src, Dst, SrcInfo, DstInfo, DstInfo.PaletteEntries, + GetOption(ImagingColorReductionMask), DstPal); +end; + +procedure IndexToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo; SrcPal, DstPal: PPalette32); +var + I: LongInt; +begin + // there is only one indexed format now, so it is just a copy + for I := 0 to NumPixels - 1 do + begin + Dst^ := Src^; + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end; + for I := 0 to SrcInfo.PaletteEntries - 1 do + DstPal[I] := SrcPal[I]; +end; + +procedure IndexToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo; SrcPal: PPalette32); +var + I: LongInt; + Pix64: TColor64Rec; + Idx: LongWord; +begin + // two most common conversions (Index8->R8G8B8 nad Index8->A8R8G8B8) + // are made separately from general conversions to make them faster + if (SrcInfo.Format = ifIndex8) and (DstInfo.Format in [ifR8G8B8, ifA8R8G8B8]) then + for I := 0 to NumPixels - 1 do + begin + with PColor24Rec(Dst)^ do + begin + R := SrcPal[Src^].R; + G := SrcPal[Src^].G; + B := SrcPal[Src^].B; + end; + if DstInfo.Format = ifA8R8G8B8 then + PColor32Rec(Dst).A := SrcPal[Src^].A; + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end + else + for I := 0 to NumPixels - 1 do + begin + // index to palette is read from source and color + // is retrieved from palette entry. Color is then + // scaled to 16bits and written to dest + IndexGetSrcPixel(Src, SrcInfo, Idx); + with Pix64 do + begin + A := SrcPal[Idx].A shl 8; + R := SrcPal[Idx].R shl 8; + G := SrcPal[Idx].G shl 8; + B := SrcPal[Idx].B shl 8; + end; + ChannelSetDstPixel(Dst, DstInfo, Pix64); + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end; +end; + +procedure IndexToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo; SrcPal: PPalette32); +var + I: LongInt; + Gray: TColor64Rec; + Alpha: Word; + Idx: LongWord; +begin + // most common conversion (Index8->Gray8) + // is made separately from general conversions to make it faster + if (SrcInfo.Format = ifIndex8) and (DstInfo.Format = ifGray8) then + begin + for I := 0 to NumPixels - 1 do + begin + Dst^ := Round(GrayConv.R * SrcPal[Src^].R + GrayConv.G * SrcPal[Src^].G + + GrayConv.B * SrcPal[Src^].B); + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end + end + else + for I := 0 to NumPixels - 1 do + begin + // index to palette is read from source and color + // is retrieved from palette entry. Color is then + // transformed to grayscale and assigned to the highest + // byte of Gray value + IndexGetSrcPixel(Src, SrcInfo, Idx); + Alpha := SrcPal[Idx].A shl 8; + Gray.A := MulDiv(Round(GrayConv.R * SrcPal[Idx].R + GrayConv.G * SrcPal[Idx].G + + GrayConv.B * SrcPal[Idx].B), 65535, 255); + GraySetDstPixel(Dst, DstInfo, Gray, Alpha); + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end; +end; + +procedure IndexToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo, + DstInfo: PImageFormatInfo; SrcPal: PPalette32); +var + I: LongInt; + Idx: LongWord; + PixF: TColorFPRec; +begin + for I := 0 to NumPixels - 1 do + begin + // index to palette is read from source and color + // is retrieved from palette entry. Color is then + // scaled to 0..1 and written to dest + IndexGetSrcPixel(Src, SrcInfo, Idx); + with PixF do + begin + A := SrcPal[Idx].A * OneDiv8Bit; + R := SrcPal[Idx].R * OneDiv8Bit; + G := SrcPal[Idx].G * OneDiv8Bit; + B := SrcPal[Idx].B * OneDiv8Bit; + end; + FloatSetDstPixel(Dst, DstInfo, PixF); + Inc(Src, SrcInfo.BytesPerPixel); + Inc(Dst, DstInfo.BytesPerPixel); + end; +end; + + +{ Special formats conversion functions } + +type + // DXT RGB color block + TDXTColorBlock = packed record + Color0, Color1: Word; + Mask: LongWord; + end; + PDXTColorBlock = ^TDXTColorBlock; + + // DXT explicit alpha for a block + TDXTAlphaBlockExp = packed record + Alphas: array[0..3] of Word; + end; + PDXTAlphaBlockExp = ^TDXTAlphaBlockExp; + + // DXT interpolated alpha for a block + TDXTAlphaBlockInt = packed record + Alphas: array[0..7] of Byte; + end; + PDXTAlphaBlockInt = ^TDXTAlphaBlockInt; + + TPixelInfo = record + Color: Word; + Alpha: Byte; + Orig: TColor32Rec; + end; + + TPixelBlock = array[0..15] of TPixelInfo; + +function DecodeCol(Color: Word): TColor32Rec; +{$IFDEF USE_INLINE} inline; {$ENDIF} +begin + Result.A := $FF; +{ Result.R := ((Color and $F800) shr 11) shl 3; + Result.G := ((Color and $07E0) shr 5) shl 2; + Result.B := (Color and $001F) shl 3;} + // this color expansion is slower but gives better results + Result.R := (Color shr 11) * 255 div 31; + Result.G := ((Color shr 5) and $3F) * 255 div 63; + Result.B := (Color and $1F) * 255 div 31; +end; + +procedure DecodeDXT1(SrcBits, DestBits: PByte; Width, Height: LongInt); +var + Sel, X, Y, I, J, K: LongInt; + Block: TDXTColorBlock; + Colors: array[0..3] of TColor32Rec; +begin + for Y := 0 to Height div 4 - 1 do + for X := 0 to Width div 4 - 1 do + begin + Block := PDXTColorBlock(SrcBits)^; + Inc(SrcBits, SizeOf(Block)); + // we read and decode endpoint colors + Colors[0] := DecodeCol(Block.Color0); + Colors[1] := DecodeCol(Block.Color1); + // and interpolate between them + if Block.Color0 > Block.Color1 then + begin + // interpolation for block without alpha + Colors[2].A := $FF; + Colors[2].R := (Colors[0].R shl 1 + Colors[1].R + 1) div 3; + Colors[2].G := (Colors[0].G shl 1 + Colors[1].G + 1) div 3; + Colors[2].B := (Colors[0].B shl 1 + Colors[1].B + 1) div 3; + Colors[3].A := $FF; + Colors[3].R := (Colors[0].R + Colors[1].R shl 1 + 1) div 3; + Colors[3].G := (Colors[0].G + Colors[1].G shl 1 + 1) div 3; + Colors[3].B := (Colors[0].B + Colors[1].B shl 1 + 1) div 3; + end + else + begin + // interpolation for block with alpha + Colors[2].A := $FF; + Colors[2].R := (Colors[0].R + Colors[1].R) shr 1; + Colors[2].G := (Colors[0].G + Colors[1].G) shr 1; + Colors[2].B := (Colors[0].B + Colors[1].B) shr 1; + Colors[3].A := 0; + Colors[3].R := (Colors[0].R + Colors[1].R shl 1 + 1) div 3; + Colors[3].G := (Colors[0].G + Colors[1].G shl 1 + 1) div 3; + Colors[3].B := (Colors[0].B + Colors[1].B shl 1 + 1) div 3; + end; + + // we distribute the dxt block colors across the 4x4 block of the + // destination image accroding to the dxt block mask + K := 0; + for J := 0 to 3 do + for I := 0 to 3 do + begin + Sel := (Block.Mask and (3 shl (K shl 1))) shr (K shl 1); + if ((X shl 2 + I) < Width) and ((Y shl 2 + J) < Height) then + PPalette32(DestBits)[(Y shl 2 + J) * Width + X shl 2 + I] := + Colors[Sel]; + Inc(K); + end; + end; +end; + +procedure DecodeDXT3(SrcBits, DestBits: PByte; Width, Height: LongInt); +var + Sel, X, Y, I, J, K: LongInt; + Block: TDXTColorBlock; + AlphaBlock: TDXTAlphaBlockExp; + Colors: array[0..3] of TColor32Rec; + AWord: Word; +begin + for Y := 0 to Height div 4 - 1 do + for X := 0 to Width div 4 - 1 do + begin + AlphaBlock := PDXTAlphaBlockExp(SrcBits)^; + Inc(SrcBits, SizeOf(AlphaBlock)); + Block := PDXTColorBlock(SrcBits)^; + Inc(SrcBits, SizeOf(Block)); + // we read and decode endpoint colors + Colors[0] := DecodeCol(Block.Color0); + Colors[1] := DecodeCol(Block.Color1); + // and interpolate between them + Colors[2].R := (Colors[0].R shl 1 + Colors[1].R + 1) div 3; + Colors[2].G := (Colors[0].G shl 1 + Colors[1].G + 1) div 3; + Colors[2].B := (Colors[0].B shl 1 + Colors[1].B + 1) div 3; + Colors[3].R := (Colors[0].R + Colors[1].R shl 1 + 1) div 3; + Colors[3].G := (Colors[0].G + Colors[1].G shl 1 + 1) div 3; + Colors[3].B := (Colors[0].B + Colors[1].B shl 1 + 1) div 3; + + // we distribute the dxt block colors and alphas + // across the 4x4 block of the destination image + // accroding to the dxt block mask and alpha block + K := 0; + for J := 0 to 3 do + begin + AWord := AlphaBlock.Alphas[J]; + for I := 0 to 3 do + begin + Sel := (Block.Mask and (3 shl (K shl 1))) shr (K shl 1); + if (X shl 2 + I < Width) and (Y shl 2 + J < Height) then + begin + Colors[Sel].A := AWord and $0F; + Colors[Sel].A := Colors[Sel].A or (Colors[Sel].A shl 4); + PPalette32(DestBits)[(Y shl 2 + J) * Width + X shl 2 + I] := + Colors[Sel]; + end; + Inc(K); + AWord := AWord shr 4; + end; + end; + end; +end; + +procedure GetInterpolatedAlphas(var AlphaBlock: TDXTAlphaBlockInt); +begin + with AlphaBlock do + if Alphas[0] > Alphas[1] then + begin + // Interpolation of six alphas + Alphas[2] := (6 * Alphas[0] + 1 * Alphas[1] + 3) div 7; + Alphas[3] := (5 * Alphas[0] + 2 * Alphas[1] + 3) div 7; + Alphas[4] := (4 * Alphas[0] + 3 * Alphas[1] + 3) div 7; + Alphas[5] := (3 * Alphas[0] + 4 * Alphas[1] + 3) div 7; + Alphas[6] := (2 * Alphas[0] + 5 * Alphas[1] + 3) div 7; + Alphas[7] := (1 * Alphas[0] + 6 * Alphas[1] + 3) div 7; + end + else + begin + // Interpolation of four alphas, two alphas are set directly + Alphas[2] := (4 * Alphas[0] + 1 * Alphas[1] + 2) div 5; + Alphas[3] := (3 * Alphas[0] + 2 * Alphas[1] + 2) div 5; + Alphas[4] := (2 * Alphas[0] + 3 * Alphas[1] + 2) div 5; + Alphas[5] := (1 * Alphas[0] + 4 * Alphas[1] + 2) div 5; + Alphas[6] := 0; + Alphas[7] := $FF; + end; +end; + +procedure DecodeDXT5(SrcBits, DestBits: PByte; Width, Height: LongInt); +var + Sel, X, Y, I, J, K: LongInt; + Block: TDXTColorBlock; + AlphaBlock: TDXTAlphaBlockInt; + Colors: array[0..3] of TColor32Rec; + AMask: array[0..1] of LongWord; +begin + for Y := 0 to Height div 4 - 1 do + for X := 0 to Width div 4 - 1 do + begin + AlphaBlock := PDXTAlphaBlockInt(SrcBits)^; + Inc(SrcBits, SizeOf(AlphaBlock)); + Block := PDXTColorBlock(SrcBits)^; + Inc(SrcBits, SizeOf(Block)); + // we read and decode endpoint colors + Colors[0] := DecodeCol(Block.Color0); + Colors[1] := DecodeCol(Block.Color1); + // and interpolate between them + Colors[2].R := (Colors[0].R shl 1 + Colors[1].R + 1) div 3; + Colors[2].G := (Colors[0].G shl 1 + Colors[1].G + 1) div 3; + Colors[2].B := (Colors[0].B shl 1 + Colors[1].B + 1) div 3; + Colors[3].R := (Colors[0].R + Colors[1].R shl 1 + 1) div 3; + Colors[3].G := (Colors[0].G + Colors[1].G shl 1 + 1) div 3; + Colors[3].B := (Colors[0].B + Colors[1].B shl 1 + 1) div 3; + // 6 bit alpha mask is copied into two long words for + // easier usage + AMask[0] := PLongWord(@AlphaBlock.Alphas[2])^ and $00FFFFFF; + AMask[1] := PLongWord(@AlphaBlock.Alphas[5])^ and $00FFFFFF; + // alpha interpolation between two endpoint alphas + GetInterpolatedAlphas(AlphaBlock); + + // we distribute the dxt block colors and alphas + // across the 4x4 block of the destination image + // accroding to the dxt block mask and alpha block mask + K := 0; + for J := 0 to 3 do + for I := 0 to 3 do + begin + Sel := (Block.Mask and (3 shl (K shl 1))) shr (K shl 1); + if ((X shl 2 + I) < Width) and ((Y shl 2 + J) < Height) then + begin + Colors[Sel].A := AlphaBlock.Alphas[AMask[J shr 1] and 7]; + PPalette32(DestBits)[(Y shl 2 + J) * Width + (X shl 2 + I)] := + Colors[Sel]; + end; + Inc(K); + AMask[J shr 1] := AMask[J shr 1] shr 3; + end; + end; +end; + +procedure GetBlock(var Block: TPixelBlock; SrcBits: Pointer; XPos, YPos, + Width, Height: LongInt); +var + X, Y, I: LongInt; + Src: PColor32Rec; +begin + I := 0; + // 4x4 pixel block is filled with information about every + // pixel in the block: alpha, original color, 565 color + for Y := 0 to 3 do + for X := 0 to 3 do + begin + Src := @PPalette32(SrcBits)[(YPos shl 2 + Y) * Width + XPos shl 2 + X]; + Block[I].Color := ((Src.R shr 3) shl 11) or ((Src.G shr 2) shl 5) or + (Src.B shr 3); + Block[I].Alpha := Src.A; + Block[I].Orig := Src^; + Inc(I); + end; +end; + +function ColorDistance(const C1, C2: TColor32Rec): LongInt; +{$IFDEF USE_INLINE} inline;{$ENDIF} +begin + Result := (C1.R - C2.R) * (C1.R - C2.R) + + (C1.G - C2.G) * (C1.G - C2.G) + (C1.B - C2.B) * (C1.B - C2.B); +end; + +procedure GetEndpoints(const Block: TPixelBlock; var Ep0, Ep1: Word); +var + I, J, Farthest, Dist: LongInt; + Colors: array[0..15] of TColor32Rec; +begin + // we choose two colors from the pixel block which has the + // largest distance between them + for I := 0 to 15 do + Colors[I] := Block[I].Orig; + Farthest := -1; + for I := 0 to 15 do + for J := I + 1 to 15 do + begin + Dist := ColorDistance(Colors[I], Colors[J]); + if Dist > Farthest then + begin + Farthest := Dist; + Ep0 := Block[I].Color; + Ep1 := Block[J].Color; + end; + end; +end; + +procedure GetAlphaEndpoints(const Block: TPixelBlock; var Min, Max: Byte); +var + I: LongInt; +begin + Min := 255; + Max := 0; + // we choose the lowest and the highest alpha values + for I := 0 to 15 do + begin + if Block[I].Alpha < Min then + Min := Block[I].Alpha; + if Block[I].Alpha > Max then + Max := Block[I].Alpha; + end; +end; + +procedure FixEndpoints(var Ep0, Ep1: Word; HasAlpha: Boolean); +var + Temp: Word; +begin + // if dxt block has alpha information, Ep0 must be smaller + // than Ep1, if the block has no alpha Ep1 must be smaller + if HasAlpha then + begin + if Ep0 > Ep1 then + begin + Temp := Ep0; + Ep0 := Ep1; + Ep1 := Temp; + end; + end + else + if Ep0 < Ep1 then + begin + Temp := Ep0; + Ep0 := Ep1; + Ep1 := Temp; + end; +end; + +function GetColorMask(Ep0, Ep1: Word; NumCols: LongInt; + const Block: TPixelBlock): LongWord; +var + I, J, Closest, Dist: LongInt; + Colors: array[0..3] of TColor32Rec; + Mask: array[0..15] of Byte; +begin + // we decode endpoint colors + Colors[0] := DecodeCol(Ep0); + Colors[1] := DecodeCol(Ep1); + // and interpolate colors between (3 for DXT1 with alpha, 4 for the others) + if NumCols = 3 then + begin + Colors[2].R := (Colors[0].R + Colors[1].R) shr 1; + Colors[2].G := (Colors[0].G + Colors[1].G) shr 1; + Colors[2].B := (Colors[0].B + Colors[1].B) shr 1; + Colors[3].R := (Colors[0].R + Colors[1].R) shr 1; + Colors[3].G := (Colors[0].G + Colors[1].G) shr 1; + Colors[3].B := (Colors[0].B + Colors[1].B) shr 1; + end + else + begin + Colors[2].R := (Colors[0].R shl 1 + Colors[1].R + 1) div 3; + Colors[2].G := (Colors[0].G shl 1 + Colors[1].G + 1) div 3; + Colors[2].B := (Colors[0].B shl 1 + Colors[1].B + 1) div 3; + Colors[3].R := (Colors[0].R + Colors[1].R shl 1 + 1) div 3; + Colors[3].G := (Colors[0].G + Colors[1].G shl 1 + 1) div 3; + Colors[3].B := (Colors[0].B + Colors[1].B shl 1 + 1) div 3; + end; + + for I := 0 to 15 do + begin + // this is only for DXT1 with alpha + if (Block[I].Alpha < 128) and (NumCols = 3) then + begin + Mask[I] := 3; + Continue; + end; + // for each of the 16 input pixels the nearest color in the + // 4 dxt colors is found + Closest := MaxInt; + for J := 0 to NumCols - 1 do + begin + Dist := ColorDistance(Block[I].Orig, Colors[J]); + if Dist < Closest then + begin + Closest := Dist; + Mask[I] := J; + end; + end; + end; + + Result := 0; + for I := 0 to 15 do + Result := Result or (Mask[I] shl (I shl 1)); +end; + +procedure GetAlphaMask(Ep0, Ep1: Byte; var Block: TPixelBlock; Mask: PByteArray); +var + Alphas: array[0..7] of Byte; + M: array[0..15] of Byte; + I, J, Closest, Dist: LongInt; +begin + Alphas[0] := Ep0; + Alphas[1] := Ep1; + // interpolation between two given alpha endpoints + // (I use 6 interpolated values mode) + Alphas[2] := (6 * Alphas[0] + 1 * Alphas[1] + 3) div 7; + Alphas[3] := (5 * Alphas[0] + 2 * Alphas[1] + 3) div 7; + Alphas[4] := (4 * Alphas[0] + 3 * Alphas[1] + 3) div 7; + Alphas[5] := (3 * Alphas[0] + 4 * Alphas[1] + 3) div 7; + Alphas[6] := (2 * Alphas[0] + 5 * Alphas[1] + 3) div 7; + Alphas[7] := (1 * Alphas[0] + 6 * Alphas[1] + 3) div 7; + + // the closest interpolated values for each of the input alpha + // is found + for I := 0 to 15 do + begin + Closest := MaxInt; + for J := 0 to 7 do + begin + Dist := Abs(Alphas[J] - Block[I].Alpha); + if Dist < Closest then + begin + Closest := Dist; + M[I] := J; + end; + end; + end; + + Mask[0] := M[0] or (M[1] shl 3) or ((M[2] and 3) shl 6); + Mask[1] := ((M[2] and 4) shr 2) or (M[3] shl 1) or (M[4] shl 4) or + ((M[5] and 1) shl 7); + Mask[2] := ((M[5] and 6) shr 1) or (M[6] shl 2) or (M[7] shl 5); + Mask[3] := M[8] or (M[9] shl 3) or ((M[10] and 3) shl 6); + Mask[4] := ((M[10] and 4) shr 2) or (M[11] shl 1) or (M[12] shl 4) or + ((M[13] and 1) shl 7); + Mask[5] := ((M[13] and 6) shr 1) or (M[14] shl 2) or (M[15] shl 5); +end; + + +procedure EncodeDXT1(SrcBits: PByte; DestBits: PByte; Width, Height: LongInt); +var + X, Y, I: LongInt; + HasAlpha: Boolean; + Block: TDXTColorBlock; + Pixels: TPixelBlock; +begin + for Y := 0 to Height div 4 - 1 do + for X := 0 to Width div 4 - 1 do + begin + GetBlock(Pixels, SrcBits, X, Y, Width, Height); + HasAlpha := False; + for I := 0 to 15 do + if Pixels[I].Alpha < 128 then + begin + HasAlpha := True; + Break; + end; + GetEndpoints(Pixels, Block.Color0, Block.Color1); + FixEndpoints(Block.Color0, Block.Color1, HasAlpha); + if HasAlpha then + Block.Mask := GetColorMask(Block.Color0, Block.Color1, 3, Pixels) + else + Block.Mask := GetColorMask(Block.Color0, Block.Color1, 4, Pixels); + PDXTColorBlock(DestBits)^ := Block; + Inc(DestBits, SizeOf(Block)); + end; +end; + +procedure EncodeDXT3(SrcBits: Pointer; DestBits: PByte; Width, Height: LongInt); +var + X, Y, I: LongInt; + Block: TDXTColorBlock; + AlphaBlock: TDXTAlphaBlockExp; + Pixels: TPixelBlock; +begin + for Y := 0 to Height div 4 - 1 do + for X := 0 to Width div 4 - 1 do + begin + GetBlock(Pixels, SrcBits, X, Y, Width, Height); + for I := 0 to 7 do + PByteArray(@AlphaBlock.Alphas)[I] := + (Pixels[I shl 1].Alpha shr 4) or ((Pixels[I shl 1 + 1].Alpha shr 4) shl 4); + GetEndpoints(Pixels, Block.Color0, Block.Color1); + FixEndpoints(Block.Color0, Block.Color1, False); + Block.Mask := GetColorMask(Block.Color0, Block.Color1, 4, Pixels); + PDXTAlphaBlockExp(DestBits)^ := AlphaBlock; + Inc(DestBits, SizeOf(AlphaBlock)); + PDXTColorBlock(DestBits)^ := Block; + Inc(DestBits, SizeOf(Block)); + end; +end; + +procedure EncodeDXT5(SrcBits: Pointer; DestBits: PByte; Width, Height: LongInt); +var + X, Y: LongInt; + Block: TDXTColorBlock; + AlphaBlock: TDXTAlphaBlockInt; + Pixels: TPixelBlock; +begin + for Y := 0 to Height div 4 - 1 do + for X := 0 to Width div 4 - 1 do + begin + GetBlock(Pixels, SrcBits, X, Y, Width, Height); + GetEndpoints(Pixels, Block.Color0, Block.Color1); + FixEndpoints(Block.Color0, Block.Color1, False); + Block.Mask := GetColorMask(Block.Color0, Block.Color1, 4, Pixels); + GetAlphaEndPoints(Pixels, AlphaBlock.Alphas[1], AlphaBlock.Alphas[0]); + GetAlphaMask(AlphaBlock.Alphas[0], AlphaBlock.Alphas[1], Pixels, + PByteArray(@AlphaBlock.Alphas[2])); + PDXTAlphaBlockInt(DestBits)^ := AlphaBlock; + Inc(DestBits, SizeOf(AlphaBlock)); + PDXTColorBlock(DestBits)^ := Block; + Inc(DestBits, SizeOf(Block)); + end; +end; + +type + TBTCBlock = packed record + MLower, MUpper: Byte; + BitField: Word; + end; + PBTCBlock = ^TBTCBlock; + +procedure EncodeBTC(SrcBits: Pointer; DestBits: PByte; Width, Height: Integer); +var + X, Y, I, J: Integer; + Block: TBTCBlock; + M, MLower, MUpper, K: Integer; + Pixels: array[0..15] of Byte; +begin + for Y := 0 to Height div 4 - 1 do + for X := 0 to Width div 4 - 1 do + begin + M := 0; + MLower := 0; + MUpper := 0; + FillChar(Block, SizeOf(Block), 0); + K := 0; + + // Store 4x4 pixels and compute average, lower, and upper intensity levels + for I := 0 to 3 do + for J := 0 to 3 do + begin + Pixels[K] := PByteArray(SrcBits)[(Y shl 2 + I) * Width + X shl 2 + J]; + Inc(M, Pixels[K]); + Inc(K); + end; + + M := M div 16; + K := 0; + + // Now compute upper and lower levels, number of upper pixels, + // and update bit field (1 when pixel is above avg. level M) + for I := 0 to 15 do + begin + if Pixels[I] > M then + begin + Inc(MUpper, Pixels[I]); + Inc(K); + Block.BitField := Block.BitField or (1 shl I); + end + else + Inc(MLower, Pixels[I]); + end; + + // Scale levels and save them to block + if K > 0 then + Block.MUpper := ClampToByte(MUpper div K) + else + Block.MUpper := 0; + Block.MLower := ClampToByte(MLower div (16 - K)); + + // Finally save block to dest data + PBTCBlock(DestBits)^ := Block; + Inc(DestBits, SizeOf(Block)); + end; +end; + +procedure GetOneChannelBlock(var Block: TPixelBlock; SrcBits: Pointer; XPos, YPos, + Width, Height, BytesPP, ChannelIdx: Integer); +var + X, Y, I: Integer; + Src: PByte; +begin + I := 0; + // 4x4 pixel block is filled with information about every pixel in the block, + // but only one channel value is stored in Alpha field + for Y := 0 to 3 do + for X := 0 to 3 do + begin + Src := @PByteArray(SrcBits)[(YPos * 4 + Y) * Width * BytesPP + + (XPos * 4 + X) * BytesPP + ChannelIdx]; + Block[I].Alpha := Src^; + Inc(I); + end; +end; + +procedure EncodeATI1N(SrcBits: Pointer; DestBits: PByte; Width, Height: Integer); +var + X, Y: Integer; + AlphaBlock: TDXTAlphaBlockInt; + Pixels: TPixelBlock; +begin + for Y := 0 to Height div 4 - 1 do + for X := 0 to Width div 4 - 1 do + begin + // Encode one channel + GetOneChannelBlock(Pixels, SrcBits, X, Y, Width, Height, 1, 0); + GetAlphaEndPoints(Pixels, AlphaBlock.Alphas[1], AlphaBlock.Alphas[0]); + GetAlphaMask(AlphaBlock.Alphas[0], AlphaBlock.Alphas[1], Pixels, + PByteArray(@AlphaBlock.Alphas[2])); + PDXTAlphaBlockInt(DestBits)^ := AlphaBlock; + Inc(DestBits, SizeOf(AlphaBlock)); + end; +end; + +procedure EncodeATI2N(SrcBits: Pointer; DestBits: PByte; Width, Height: Integer); +var + X, Y: Integer; + AlphaBlock: TDXTAlphaBlockInt; + Pixels: TPixelBlock; +begin + for Y := 0 to Height div 4 - 1 do + for X := 0 to Width div 4 - 1 do + begin + // Encode Red/X channel + GetOneChannelBlock(Pixels, SrcBits, X, Y, Width, Height, 4, ChannelRed); + GetAlphaEndPoints(Pixels, AlphaBlock.Alphas[1], AlphaBlock.Alphas[0]); + GetAlphaMask(AlphaBlock.Alphas[0], AlphaBlock.Alphas[1], Pixels, + PByteArray(@AlphaBlock.Alphas[2])); + PDXTAlphaBlockInt(DestBits)^ := AlphaBlock; + Inc(DestBits, SizeOf(AlphaBlock)); + // Encode Green/Y channel + GetOneChannelBlock(Pixels, SrcBits, X, Y, Width, Height, 4, ChannelGreen); + GetAlphaEndPoints(Pixels, AlphaBlock.Alphas[1], AlphaBlock.Alphas[0]); + GetAlphaMask(AlphaBlock.Alphas[0], AlphaBlock.Alphas[1], Pixels, + PByteArray(@AlphaBlock.Alphas[2])); + PDXTAlphaBlockInt(DestBits)^ := AlphaBlock; + Inc(DestBits, SizeOf(AlphaBlock)); + end; +end; + +procedure EncodeBinary(SrcBits: Pointer; DestBits: PByte; Width, Height: Integer); +var + Src: PByte absolute SrcBits; + Bitmap: PByteArray absolute DestBits; + X, Y, WidthBytes: Integer; + PixelTresholded, Treshold: Byte; +begin + Treshold := ClampToByte(GetOption(ImagingBinaryTreshold)); + WidthBytes := (Width + 7) div 8; + + for Y := 0 to Height - 1 do + for X := 0 to Width - 1 do + begin + if Src^ > Treshold then + PixelTresholded := 255 + else + PixelTresholded := 0; + + Bitmap[Y * WidthBytes + X div 8] := Bitmap[Y * WidthBytes + X div 8] or // OR current value of byte with following: + (PixelTresholded and 1) // To make 1 from 255, 0 remains 0 + shl (7 - (X mod 8)); // Put current bit to proper place in byte + + Inc(Src); + end; +end; + +procedure DecodeBTC(SrcBits, DestBits: PByte; Width, Height: Integer); +var + X, Y, I, J, K: Integer; + Block: TBTCBlock; + Dest: PByte; +begin + for Y := 0 to Height div 4 - 1 do + for X := 0 to Width div 4 - 1 do + begin + Block := PBTCBlock(SrcBits)^; + Inc(SrcBits, SizeOf(Block)); + K := 0; + + // Just write MUpper when there is '1' in bit field and MLower + // when there is '0' + for I := 0 to 3 do + for J := 0 to 3 do + begin + Dest := @PByteArray(DestBits)[(Y shl 2 + I) * Width + X shl 2 + J]; + if Block.BitField and (1 shl K) <> 0 then + Dest^ := Block.MUpper + else + Dest^ := Block.MLower; + Inc(K); + end; + end; +end; + +procedure DecodeATI1N(SrcBits, DestBits: PByte; Width, Height: Integer); +var + X, Y, I, J: Integer; + AlphaBlock: TDXTAlphaBlockInt; + AMask: array[0..1] of LongWord; +begin + for Y := 0 to Height div 4 - 1 do + for X := 0 to Width div 4 - 1 do + begin + AlphaBlock := PDXTAlphaBlockInt(SrcBits)^; + Inc(SrcBits, SizeOf(AlphaBlock)); + // 6 bit alpha mask is copied into two long words for + // easier usage + AMask[0] := PLongWord(@AlphaBlock.Alphas[2])^ and $00FFFFFF; + AMask[1] := PLongWord(@AlphaBlock.Alphas[5])^ and $00FFFFFF; + // alpha interpolation between two endpoint alphas + GetInterpolatedAlphas(AlphaBlock); + + // we distribute the dxt block alphas + // across the 4x4 block of the destination image + for J := 0 to 3 do + for I := 0 to 3 do + begin + PByteArray(DestBits)[(Y shl 2 + J) * Width + (X shl 2 + I)] := + AlphaBlock.Alphas[AMask[J shr 1] and 7]; + AMask[J shr 1] := AMask[J shr 1] shr 3; + end; + end; +end; + +procedure DecodeATI2N(SrcBits, DestBits: PByte; Width, Height: Integer); +var + X, Y, I, J: Integer; + Color: TColor32Rec; + AlphaBlock1, AlphaBlock2: TDXTAlphaBlockInt; + AMask1: array[0..1] of LongWord; + AMask2: array[0..1] of LongWord; +begin + for Y := 0 to Height div 4 - 1 do + for X := 0 to Width div 4 - 1 do + begin + // Read the first alpha block and get masks + AlphaBlock1 := PDXTAlphaBlockInt(SrcBits)^; + Inc(SrcBits, SizeOf(AlphaBlock1)); + AMask1[0] := PLongWord(@AlphaBlock1.Alphas[2])^ and $00FFFFFF; + AMask1[1] := PLongWord(@AlphaBlock1.Alphas[5])^ and $00FFFFFF; + // Read the secind alpha block and get masks + AlphaBlock2 := PDXTAlphaBlockInt(SrcBits)^; + Inc(SrcBits, SizeOf(AlphaBlock2)); + AMask2[0] := PLongWord(@AlphaBlock2.Alphas[2])^ and $00FFFFFF; + AMask2[1] := PLongWord(@AlphaBlock2.Alphas[5])^ and $00FFFFFF; + // alpha interpolation between two endpoint alphas + GetInterpolatedAlphas(AlphaBlock1); + GetInterpolatedAlphas(AlphaBlock2); + + Color.A := $FF; + Color.B := 0; + + // Distribute alpha block values across 4x4 pixel block, + // first alpha block represents Red channel, second is Green. + for J := 0 to 3 do + for I := 0 to 3 do + begin + Color.R := AlphaBlock1.Alphas[AMask1[J shr 1] and 7]; + Color.G := AlphaBlock2.Alphas[AMask2[J shr 1] and 7]; + PColor32RecArray(DestBits)[(Y shl 2 + J) * Width + (X shl 2 + I)] := Color; + AMask1[J shr 1] := AMask1[J shr 1] shr 3; + AMask2[J shr 1] := AMask2[J shr 1] shr 3; + end; + end; +end; + +procedure DecodeBinary(SrcBits, DestBits: PByte; Width, Height: Integer); {$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Convert1To8(SrcBits, DestBits, Width, Height, (Width + 7) div 8, True); +end; + +procedure SpecialToUnSpecial(const SrcImage: TImageData; DestBits: Pointer; + SpecialFormat: TImageFormat); +begin + case SpecialFormat of + ifDXT1: DecodeDXT1(SrcImage.Bits, DestBits, SrcImage.Width, SrcImage.Height); + ifDXT3: DecodeDXT3(SrcImage.Bits, DestBits, SrcImage.Width, SrcImage.Height); + ifDXT5: DecodeDXT5(SrcImage.Bits, DestBits, SrcImage.Width, SrcImage.Height); + ifBTC: DecodeBTC (SrcImage.Bits, DestBits, SrcImage.Width, SrcImage.Height); + ifATI1N: DecodeATI1N(SrcImage.Bits, DestBits, SrcImage.Width, SrcImage.Height); + ifATI2N: DecodeATI2N(SrcImage.Bits, DestBits, SrcImage.Width, SrcImage.Height); + ifBinary: DecodeBinary(SrcImage.Bits, DestBits, SrcImage.Width, SrcImage.Height); + end; +end; + +procedure UnSpecialToSpecial(SrcBits: Pointer; const DestImage: TImageData; + SpecialFormat: TImageFormat); +begin + case SpecialFormat of + ifDXT1: EncodeDXT1(SrcBits, DestImage.Bits, DestImage.Width, DestImage.Height); + ifDXT3: EncodeDXT3(SrcBits, DestImage.Bits, DestImage.Width, DestImage.Height); + ifDXT5: EncodeDXT5(SrcBits, DestImage.Bits, DestImage.Width, DestImage.Height); + ifBTC: EncodeBTC (SrcBits, DestImage.Bits, DestImage.Width, DestImage.Height); + ifATI1N: EncodeATI1N(SrcBits, DestImage.Bits, DestImage.Width, DestImage.Height); + ifATI2N: EncodeATI2N(SrcBits, DestImage.Bits, DestImage.Width, DestImage.Height); + ifBinary: EncodeBinary(SrcBits, DestImage.Bits, DestImage.Width, DestImage.Height); + end; +end; + +procedure ConvertSpecial(var Image: TImageData; + SrcInfo, DstInfo: PImageFormatInfo); +var + WorkImage: TImageData; + + procedure CheckSize(var Img: TImageData; Info: PImageFormatInfo); + var + Width, Height: Integer; + begin + Width := Img.Width; + Height := Img.Height; + DstInfo.CheckDimensions(Info.Format, Width, Height); + ResizeImage(Img, Width, Height, rfNearest); + end; + +begin + if SrcInfo.IsSpecial and DstInfo.IsSpecial then + begin + // Convert source to nearest 'normal' format + InitImage(WorkImage); + NewImage(Image.Width, Image.Height, SrcInfo.SpecialNearestFormat, WorkImage); + SpecialToUnSpecial(Image, WorkImage.Bits, SrcInfo.Format); + FreeImage(Image); + // Make sure output of SpecialToUnSpecial is the same as input of + // UnSpecialToSpecial + if SrcInfo.SpecialNearestFormat <> DstInfo.SpecialNearestFormat then + ConvertImage(WorkImage, DstInfo.SpecialNearestFormat); + // Convert work image to dest special format + CheckSize(WorkImage, DstInfo); + NewImage(WorkImage.Width, WorkImage.Height, DstInfo.Format, Image); + UnSpecialToSpecial(WorkImage.Bits, Image, DstInfo.Format); + FreeImage(WorkImage); + end + else if SrcInfo.IsSpecial and not DstInfo.IsSpecial then + begin + // Convert source to nearest 'normal' format + InitImage(WorkImage); + NewImage(Image.Width, Image.Height, SrcInfo.SpecialNearestFormat, WorkImage); + SpecialToUnSpecial(Image, WorkImage.Bits, SrcInfo.Format); + FreeImage(Image); + // Now convert to dest format + ConvertImage(WorkImage, DstInfo.Format); + Image := WorkImage; + end + else if not SrcInfo.IsSpecial and DstInfo.IsSpecial then + begin + // Convert source to nearest format + WorkImage := Image; + ConvertImage(WorkImage, DstInfo.SpecialNearestFormat); + // Now convert from nearest to dest + CheckSize(WorkImage, DstInfo); + InitImage(Image); + NewImage(WorkImage.Width, WorkImage.Height, DstInfo.Format, Image); + UnSpecialToSpecial(WorkImage.Bits, Image, DstInfo.Format); + FreeImage(WorkImage); + end; +end; + +function GetStdPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt; +begin + if FInfos[Format] <> nil then + Result := Width * Height * FInfos[Format].BytesPerPixel + else + Result := 0; +end; + +procedure CheckStdDimensions(Format: TImageFormat; var Width, Height: LongInt); +begin +end; + +function GetDXTPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt; +begin + // DXT can be used only for images with dimensions that are + // multiples of four + CheckDXTDimensions(Format, Width, Height); + Result := Width * Height; + if Format in [ifDXT1, ifATI1N] then + Result := Result div 2; +end; + +procedure CheckDXTDimensions(Format: TImageFormat; var Width, Height: LongInt); +begin + // DXT image dimensions must be multiples of four + Width := (Width + 3) and not 3; // div 4 * 4; + Height := (Height + 3) and not 3; // div 4 * 4; +end; + +function GetBTCPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt; +begin + // BTC can be used only for images with dimensions that are + // multiples of four + CheckDXTDimensions(Format, Width, Height); + Result := Width * Height div 4; // 2bits/pixel +end; + +function GetBCPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt; +begin + raise ENotImplemented.Create(); +end; + +procedure CheckBCDimensions(Format: TImageFormat; var Width, Height: LongInt); +begin + raise ENotImplemented.Create(); +end; + +function GetBinaryPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt; +begin + // Binary images are aligned on BYTE boundary + Result := ((Width + 7) div 8) * Height; // 1bit/pixel +end; + +{ Optimized pixel readers/writers for 32bit and FP colors to be stored in TImageFormatInfo } + +function GetPixel32ifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColor32Rec; +begin + Result.Color := PLongWord(Bits)^; +end; + +procedure SetPixel32ifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColor32Rec); +begin + PLongWord(Bits)^ := Color.Color; +end; + +function GetPixelFPifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColorFPRec; +begin + Result.A := PColor32Rec(Bits).A * OneDiv8Bit; + Result.R := PColor32Rec(Bits).R * OneDiv8Bit; + Result.G := PColor32Rec(Bits).G * OneDiv8Bit; + Result.B := PColor32Rec(Bits).B * OneDiv8Bit; +end; + +procedure SetPixelFPifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColorFPRec); +begin + PColor32Rec(Bits).A := ClampToByte(Round(Color.A * 255.0)); + PColor32Rec(Bits).R := ClampToByte(Round(Color.R * 255.0)); + PColor32Rec(Bits).G := ClampToByte(Round(Color.G * 255.0)); + PColor32Rec(Bits).B := ClampToByte(Round(Color.B * 255.0)); +end; + +function GetPixel32Channel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColor32Rec; +begin + case Info.Format of + ifR8G8B8, ifX8R8G8B8: + begin + Result.A := $FF; + PColor24Rec(@Result)^ := PColor24Rec(Bits)^; + end; + ifGray8, ifA8Gray8: + begin + if Info.HasAlphaChannel then + Result.A := PWordRec(Bits).High + else + Result.A := $FF; + Result.R := PWordRec(Bits).Low; + Result.G := PWordRec(Bits).Low; + Result.B := PWordRec(Bits).Low; + end; + end; +end; + +procedure SetPixel32Channel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColor32Rec); +begin + case Info.Format of + ifR8G8B8, ifX8R8G8B8: + begin + PColor24Rec(Bits)^ := PColor24Rec(@Color)^; + end; + ifGray8, ifA8Gray8: + begin + if Info.HasAlphaChannel then + PWordRec(Bits).High := Color.A; + PWordRec(Bits).Low := Round(GrayConv.R * Color.R + GrayConv.G * Color.G + + GrayConv.B * Color.B); + end; + end; +end; + +function GetPixelFPChannel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColorFPRec; +begin + case Info.Format of + ifR8G8B8, ifX8R8G8B8: + begin + Result.A := 1.0; + Result.R := PColor24Rec(Bits).R * OneDiv8Bit; + Result.G := PColor24Rec(Bits).G * OneDiv8Bit; + Result.B := PColor24Rec(Bits).B * OneDiv8Bit; + end; + ifGray8, ifA8Gray8: + begin + if Info.HasAlphaChannel then + Result.A := PWordRec(Bits).High * OneDiv8Bit + else + Result.A := 1.0; + Result.R := PWordRec(Bits).Low * OneDiv8Bit; + Result.G := PWordRec(Bits).Low * OneDiv8Bit; + Result.B := PWordRec(Bits).Low * OneDiv8Bit; + end; + end; +end; + +procedure SetPixelFPChannel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColorFPRec); +begin + case Info.Format of + ifR8G8B8, ifX8R8G8B8: + begin + PColor24Rec(Bits).R := ClampToByte(Round(Color.R * 255.0)); + PColor24Rec(Bits).G := ClampToByte(Round(Color.G * 255.0)); + PColor24Rec(Bits).B := ClampToByte(Round(Color.B * 255.0)); + end; + ifGray8, ifA8Gray8: + begin + if Info.HasAlphaChannel then + PWordRec(Bits).High := ClampToByte(Round(Color.A * 255.0)); + PWordRec(Bits).Low := ClampToByte(Round((GrayConv.R * Color.R + GrayConv.G * Color.G + + GrayConv.B * Color.B) * 255.0)); + end; + end; +end; + +function GetPixelFPFloat32(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColorFPRec; +begin + case Info.Format of + ifA32R32G32B32F, ifA32B32G32R32F: + begin + Result := PColorFPRec(Bits)^; + end; + ifR32G32B32F, ifB32G32R32F: + begin + Result.A := 1.0; + Result.Color96Rec := PColor96FPRec(Bits)^; + end; + ifR32F: + begin + Result.A := 1.0; + Result.R := PSingle(Bits)^; + Result.G := 0.0; + Result.B := 0.0; + end; + end; + if Info.IsRBSwapped then + SwapValues(Result.R, Result.B); +end; + +procedure SetPixelFPFloat32(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColorFPRec); +begin + case Info.Format of + ifA32R32G32B32F, ifA32B32G32R32F: + begin + PColorFPRec(Bits)^ := Color; + end; + ifR32G32B32F, ifB32G32R32F: + begin + PColor96FPRec(Bits)^ := Color.Color96Rec; + end; + ifR32F: + begin + PSingle(Bits)^ := Color.R; + end; + end; + if Info.IsRBSwapped then + SwapValues(PColor96FPRec(Bits).R, PColor96FPRec(Bits).B); +end; + +initialization + // Initialize default sampling filter function pointers and radii + SamplingFilterFunctions[sfNearest] := FilterNearest; + SamplingFilterFunctions[sfLinear] := FilterLinear; + SamplingFilterFunctions[sfCosine] := FilterCosine; + SamplingFilterFunctions[sfHermite] := FilterHermite; + SamplingFilterFunctions[sfQuadratic] := FilterQuadratic; + SamplingFilterFunctions[sfGaussian] := FilterGaussian; + SamplingFilterFunctions[sfSpline] := FilterSpline; + SamplingFilterFunctions[sfLanczos] := FilterLanczos; + SamplingFilterFunctions[sfMitchell] := FilterMitchell; + SamplingFilterFunctions[sfCatmullRom] := FilterCatmullRom; + SamplingFilterRadii[sfNearest] := 1.0; + SamplingFilterRadii[sfLinear] := 1.0; + SamplingFilterRadii[sfCosine] := 1.0; + SamplingFilterRadii[sfHermite] := 1.0; + SamplingFilterRadii[sfQuadratic] := 1.5; + SamplingFilterRadii[sfGaussian] := 1.25; + SamplingFilterRadii[sfSpline] := 2.0; + SamplingFilterRadii[sfLanczos] := 3.0; + SamplingFilterRadii[sfMitchell] := 2.0; + SamplingFilterRadii[sfCatmullRom] := 2.0; + +{ + File Notes: + + -- TODOS ---------------------------------------------------- + - nothing now + + -- 0.80 ------------------------------------------------------- + - Added PaletteIsGrayScale and Color32ToGray functions. + + -- 0.77 Changes/Bug Fixes ------------------------------------- + - NOT YET: Added support for Passtrough image data formats. + - Added ConvertToPixel32 helper function. + + -- 0.26.5 Changes/Bug Fixes ----------------------------------- + - Removed optimized codepatch for few data formats from StretchResample + function. It was quite buggy and not so much faster anyway. + - Added PaletteHasAlpha function. + - Added support functions for ifBinary data format. + - Added optional pixel scaling to Convert1To8, Convert2To8, + abd Convert4To8 functions. + + -- 0.26.3 Changes/Bug Fixes ----------------------------------- + - Filtered resampling ~10% faster now. + - Fixed DXT3 alpha encoding. + - ifIndex8 format now has HasAlphaChannel=True. + + -- 0.25.0 Changes/Bug Fixes ----------------------------------- + - Made some resampling stuff public so that it can be used in canvas class. + - Added some color constructors. + - Added VisualizePalette helper function. + - Fixed ConvertSpecial, not very readable before and error when + converting special->special. + + -- 0.24.3 Changes/Bug Fixes ----------------------------------- + - Some refactorings a changes to DXT based formats. + - Added ifATI1N and ifATI2N image data formats support structures and functions. + + -- 0.23 Changes/Bug Fixes ----------------------------------- + - Added ifBTC image format support structures and functions. + + -- 0.21 Changes/Bug Fixes ----------------------------------- + - FillMipMapLevel now works well with indexed and special formats too. + - Moved Convert1To8 and Convert4To8 functions from ImagingBitmaps here + and created new Convert2To8 function. They are now used by more than one + file format loader. + + -- 0.19 Changes/Bug Fixes ----------------------------------- + - StretchResample now uses pixel get/set functions stored in + TImageFormatInfo so it is much faster for formats that override + them with optimized ones + - added pixel set/get functions optimized for various image formats + (to be stored in TImageFormatInfo) + - bug in ConvertSpecial caused problems when converting DXTC images + to bitmaps in ImagingCoponents + - bug in StretchRect caused that it didn't work with ifR32F and + ifR16F formats + - removed leftover code in FillMipMapLevel which disabled + filtered resizing of images witch ChannelSize <> 8bits + - added half float converting functions and support for half based + image formats where needed + - added TranslatePixel and IsImageFormatValid functions + - fixed possible range overflows when converting from FP to integer images + - added pixel set/get functions: GetPixel32Generic, GetPixelFPGeneric, + SetPixel32Generic, SetPixelFPGeneric + - fixed occasional range overflows in StretchResample + + -- 0.17 Changes/Bug Fixes ----------------------------------- + - added StretchNearest, StretchResample and some sampling functions + - added ChannelCount values to TImageFormatInfo constants + - added resolution validity check to GetDXTPixelsSize + + -- 0.15 Changes/Bug Fixes ----------------------------------- + - added RBSwapFormat values to some TImageFromatInfo definitions + - fixed bug in ConvertSpecial (causing DXT images to convert only to 32bit) + - added CopyPixel, ComparePixels helper functions + + -- 0.13 Changes/Bug Fixes ----------------------------------- + - replaced pixel format conversions for colors not to be + darkened when converting from low bit counts + - ReduceColorsMedianCut was updated to support creating one + optimal palette for more images and it is somewhat faster + now too + - there was ugly bug in DXTC dimensions checking +} + +end. + diff --git a/resources/libraries/deskew/Imaging/ImagingGif.pas b/resources/libraries/deskew/Imaging/ImagingGif.pas new file mode 100755 index 0000000..13a6555 --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingGif.pas @@ -0,0 +1,1291 @@ +{ + Vampyre Imaging Library + by Marek Mauder + http://imaginglib.sourceforge.net + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +{ This unit contains image format loader/saver for GIF images.} +unit ImagingGif; + +{$I ImagingOptions.inc} + +interface + +uses + SysUtils, Classes, Imaging, ImagingTypes, ImagingIO, ImagingUtility; + +type + { GIF (Graphics Interchange Format) loader/saver class. GIF was + (and is still used) popular format for storing images supporting + multiple images per file and single color transparency. + Pixel format is 8 bit indexed where each image frame can have + its own color palette. GIF uses lossless LZW compression + (patent expired few years ago). + Imaging can load and save all GIFs with all frames and supports + transparency. Imaging can load just raw ifIndex8 frames or + also animate them in ifA8R8G8B8 format. See ImagingGIFLoadAnimated option.} + TGIFFileFormat = class(TImageFileFormat) + private + FLoadAnimated: LongBool; + function InterlaceStep(Y, Height: Integer; var Pass: Integer): Integer; + procedure LZWDecompress(Stream: TStream; Handle: TImagingHandle; + Width, Height: Integer; Interlaced: Boolean; Data: Pointer); + procedure LZWCompress(const IO: TIOFunctions; Handle: TImagingHandle; + Width, Height, BitCount: Integer; Interlaced: Boolean; Data: Pointer); + protected + procedure Define; override; + function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; + OnlyFirstLevel: Boolean): Boolean; override; + function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; + Index: LongInt): Boolean; override; + procedure ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); override; + public + function TestFormat(Handle: TImagingHandle): Boolean; override; + published + property LoadAnimated: LongBool read FLoadAnimated write FLoadAnimated; + end; + +implementation + +const + SGIFFormatName = 'Graphics Interchange Format'; + SGIFMasks = '*.gif'; + GIFSupportedFormats: TImageFormats = [ifIndex8]; + GIFDefaultLoadAnimated = True; + +type + TGIFVersion = (gv87, gv89); + TDisposalMethod = (dmNoRemoval, dmLeave, dmRestoreBackground, + dmRestorePrevious, dmReserved4, dmReserved5, dmReserved6, dmReserved7); + +const + GIFSignature: TChar3 = 'GIF'; + GIFVersions: array[TGIFVersion] of TChar3 = ('87a', '89a'); + GIFDefaultDelay = 65; + + // Masks for accessing fields in PackedFields of TGIFHeader + GIFGlobalColorTable = $80; + GIFColorResolution = $70; + GIFColorTableSorted = $08; + GIFColorTableSize = $07; + + // Masks for accessing fields in PackedFields of TImageDescriptor + GIFLocalColorTable = $80; + GIFInterlaced = $40; + GIFLocalTableSorted = $20; + + // Block identifiers + GIFPlainText: Byte = $01; + GIFGraphicControlExtension: Byte = $F9; + GIFCommentExtension: Byte = $FE; + GIFApplicationExtension: Byte = $FF; + GIFImageDescriptor: Byte = Ord(','); + GIFExtensionIntroducer: Byte = Ord('!'); + GIFTrailer: Byte = Ord(';'); + GIFBlockTerminator: Byte = $00; + + // Masks for accessing fields in PackedFields of TGraphicControlExtension + GIFTransparent = $01; + GIFUserInput = $02; + GIFDisposalMethod = $1C; + +const + // Netscape sub block types + GIFAppLoopExtension = 1; + GIFAppBufferExtension = 2; + +type + TGIFHeader = packed record + // File header part + Signature: TChar3; // Header Signature (always "GIF") + Version: TChar3; // GIF format version("87a" or "89a") + // Logical Screen Descriptor part + ScreenWidth: Word; // Width of Display Screen in Pixels + ScreenHeight: Word; // Height of Display Screen in Pixels + PackedFields: Byte; // Screen and color map information + BackgroundColorIndex: Byte; // Background color index (in global color table) + AspectRatio: Byte; // Pixel aspect ratio, ratio = (AspectRatio + 15) / 64 + end; + + TImageDescriptor = packed record + //Separator: Byte; // leave that out since we always read one bye ahead + Left: Word; // X position of image with respect to logical screen + Top: Word; // Y position + Width: Word; + Height: Word; + PackedFields: Byte; + end; + +const + // GIF extension labels + GIFExtTypeGraphic = $F9; + GIFExtTypePlainText = $01; + GIFExtTypeApplication = $FF; + GIFExtTypeComment = $FE; + +type + TGraphicControlExtension = packed record + BlockSize: Byte; + PackedFields: Byte; + DelayTime: Word; + TransparentColorIndex: Byte; + Terminator: Byte; + end; + +type + TGIFIdentifierCode = array[0..7] of AnsiChar; + TGIFAuthenticationCode = array[0..2] of AnsiChar; + TGIFApplicationRec = packed record + Identifier: TGIFIdentifierCode; + Authentication: TGIFAuthenticationCode; + end; + +const + CodeTableSize = 4096; + HashTableSize = 17777; + +type + TReadContext = record + Inx: Integer; + Size: Integer; + Buf: array [0..255 + 4] of Byte; + CodeSize: Integer; + ReadMask: Integer; + end; + PReadContext = ^TReadContext; + + TWriteContext = record + Inx: Integer; + CodeSize: Integer; + Buf: array [0..255 + 4] of Byte; + end; + PWriteContext = ^TWriteContext; + + TOutputContext = record + W: Integer; + H: Integer; + X: Integer; + Y: Integer; + BitsPerPixel: Integer; + Pass: Integer; + Interlace: Boolean; + LineIdent: Integer; + Data: Pointer; + CurrLineData: Pointer; + end; + + TImageDict = record + Tail: Word; + Index: Word; + Col: Byte; + end; + PImageDict = ^TImageDict; + + PIntCodeTable = ^TIntCodeTable; + TIntCodeTable = array [0..CodeTableSize - 1] of Word; + + TDictTable = array [0..CodeTableSize - 1] of TImageDict; + PDictTable = ^TDictTable; + +resourcestring + SGIFDecodingError = 'Error when decoding GIF LZW data'; + +{ + TGIFFileFormat implementation +} + +procedure TGIFFileFormat.Define; +begin + inherited; + FName := SGIFFormatName; + FFeatures := [ffLoad, ffSave, ffMultiImage]; + FSupportedFormats := GIFSupportedFormats; + FLoadAnimated := GIFDefaultLoadAnimated; + + AddMasks(SGIFMasks); + RegisterOption(ImagingGIFLoadAnimated, @FLoadAnimated); +end; + +function TGIFFileFormat.InterlaceStep(Y, Height: Integer; var Pass: Integer): Integer; +begin + Result := Y; + case Pass of + 0, 1: + Inc(Result, 8); + 2: + Inc(Result, 4); + 3: + Inc(Result, 2); + end; + if Result >= Height then + begin + if Pass = 0 then + begin + Pass := 1; + Result := 4; + if Result < Height then + Exit; + end; + if Pass = 1 then + begin + Pass := 2; + Result := 2; + if Result < Height then + Exit; + end; + if Pass = 2 then + begin + Pass := 3; + Result := 1; + end; + end; +end; + +{ GIF LZW decompresion code is from JVCL JvGIF.pas unit.} +procedure TGIFFileFormat.LZWDecompress(Stream: TStream; Handle: TImagingHandle; Width, Height: Integer; + Interlaced: Boolean; Data: Pointer); +var + MinCodeSize: Byte; + MaxCode, BitMask, InitCodeSize: Integer; + ClearCode, EndingCode, FirstFreeCode, FreeCode: Word; + I, OutCount, Code: Integer; + CurCode, OldCode, InCode, FinalChar: Word; + Prefix, Suffix, OutCode: PIntCodeTable; + ReadCtxt: TReadContext; + OutCtxt: TOutputContext; + TableFull: Boolean; + + function ReadCode(var Context: TReadContext): Integer; + var + RawCode: Integer; + ByteIndex: Integer; + Bytes: Byte; + BytesToLose: Integer; + begin + while (Context.Inx + Context.CodeSize > Context.Size) and + (Stream.Position < Stream.Size) do + begin + // Not enough bits in buffer - refill it - Not very efficient, but infrequently called + BytesToLose := Context.Inx shr 3; + // Note biggest Code Size is 12 bits. And this can at worst span 3 Bytes + Move(Context.Buf[Word(BytesToLose)], Context.Buf[0], 3); + Context.Inx := Context.Inx and 7; + Context.Size := Context.Size - (BytesToLose shl 3); + Stream.Read(Bytes, 1); + if Bytes > 0 then + Stream.Read(Context.Buf[Word(Context.Size shr 3)], Bytes); + Context.Size := Context.Size + (Bytes shl 3); + end; + ByteIndex := Context.Inx shr 3; + RawCode := Context.Buf[Word(ByteIndex)] + + (Word(Context.Buf[Word(ByteIndex + 1)]) shl 8); + if Context.CodeSize > 8 then + RawCode := RawCode + (Integer(Context.Buf[ByteIndex + 2]) shl 16); + RawCode := RawCode shr (Context.Inx and 7); + Context.Inx := Context.Inx + Byte(Context.CodeSize); + Result := RawCode and Context.ReadMask; + end; + + procedure Output(Value: Byte; var Context: TOutputContext); + var + P: PByte; + begin + if Context.Y >= Context.H then + Exit; + + // Only ifIndex8 supported + P := @PByteArray(Context.CurrLineData)[Context.X]; + P^ := Value; + + {case Context.BitsPerPixel of + 1: + begin + P := @PByteArray(Context.CurrLineData)[Context.X shr 3]; + if (Context.X and $07) <> 0 then + P^ := P^ or Word(Value shl (7 - (Word(Context.X and 7)))) + else + P^ := Byte(Value shl 7); + end; + 4: + begin + P := @PByteArray(Context.CurrLineData)[Context.X shr 1]; + if (Context.X and 1) <> 0 then + P^ := P^ or Value + else + P^ := Byte(Value shl 4); + end; + 8: + begin + P := @PByteArray(Context.CurrLineData)[Context.X]; + P^ := Value; + end; + end;} + Inc(Context.X); + + if Context.X < Context.W then + Exit; + Context.X := 0; + if Context.Interlace then + Context.Y := InterlaceStep(Context.Y, Context.H, Context.Pass) + else + Inc(Context.Y); + + Context.CurrLineData := @PByteArray(Context.Data)[Context.Y * Context.LineIdent]; + end; + +begin + OutCount := 0; + OldCode := 0; + FinalChar := 0; + TableFull := False; + GetMem(Prefix, SizeOf(TIntCodeTable)); + GetMem(Suffix, SizeOf(TIntCodeTable)); + GetMem(OutCode, SizeOf(TIntCodeTable) + SizeOf(Word)); + try + Stream.Read(MinCodeSize, 1); + if (MinCodeSize < 2) or (MinCodeSize > 9) then + RaiseImaging(SGIFDecodingError, []); + // Initial read context + ReadCtxt.Inx := 0; + ReadCtxt.Size := 0; + ReadCtxt.CodeSize := MinCodeSize + 1; + ReadCtxt.ReadMask := (1 shl ReadCtxt.CodeSize) - 1; + // Initialise pixel-output context + OutCtxt.X := 0; + OutCtxt.Y := 0; + OutCtxt.Pass := 0; + OutCtxt.W := Width; + OutCtxt.H := Height; + OutCtxt.BitsPerPixel := MinCodeSize; + OutCtxt.Interlace := Interlaced; + OutCtxt.LineIdent := Width; + OutCtxt.Data := Data; + OutCtxt.CurrLineData := Data; + BitMask := (1 shl OutCtxt.BitsPerPixel) - 1; + // 2 ^ MinCodeSize accounts for all colours in file + ClearCode := 1 shl MinCodeSize; + EndingCode := ClearCode + 1; + FreeCode := ClearCode + 2; + FirstFreeCode := FreeCode; + // 2^ (MinCodeSize + 1) includes clear and eoi Code and space too + InitCodeSize := ReadCtxt.CodeSize; + MaxCode := 1 shl ReadCtxt.CodeSize; + Code := ReadCode(ReadCtxt); + while (Code <> EndingCode) and (Code <> $FFFF) and + (OutCtxt.Y < OutCtxt.H) do + begin + if Code = ClearCode then + begin + ReadCtxt.CodeSize := InitCodeSize; + MaxCode := 1 shl ReadCtxt.CodeSize; + ReadCtxt.ReadMask := MaxCode - 1; + FreeCode := FirstFreeCode; + Code := ReadCode(ReadCtxt); + CurCode := Code; + OldCode := Code; + if Code = $FFFF then + Break; + FinalChar := (CurCode and BitMask); + Output(Byte(FinalChar), OutCtxt); + TableFull := False; + end + else + begin + CurCode := Code; + InCode := Code; + if CurCode >= FreeCode then + begin + CurCode := OldCode; + OutCode^[OutCount] := FinalChar; + Inc(OutCount); + end; + while CurCode > BitMask do + begin + if OutCount > CodeTableSize then + RaiseImaging(SGIFDecodingError, []); + OutCode^[OutCount] := Suffix^[CurCode]; + Inc(OutCount); + CurCode := Prefix^[CurCode]; + end; + + FinalChar := CurCode and BitMask; + OutCode^[OutCount] := FinalChar; + Inc(OutCount); + for I := OutCount - 1 downto 0 do + Output(Byte(OutCode^[I]), OutCtxt); + OutCount := 0; + // Update dictionary + if not TableFull then + begin + Prefix^[FreeCode] := OldCode; + Suffix^[FreeCode] := FinalChar; + // Advance to next free slot + Inc(FreeCode); + if FreeCode >= MaxCode then + begin + if ReadCtxt.CodeSize < 12 then + begin + Inc(ReadCtxt.CodeSize); + MaxCode := MaxCode shl 1; + ReadCtxt.ReadMask := (1 shl ReadCtxt.CodeSize) - 1; + end + else + TableFull := True; + end; + end; + OldCode := InCode; + end; + Code := ReadCode(ReadCtxt); + end; + if Code = $FFFF then + RaiseImaging(SGIFDecodingError, []); + finally + FreeMem(Prefix); + FreeMem(OutCode); + FreeMem(Suffix); + end; +end; + +{ GIF LZW compresion code is from JVCL JvGIF.pas unit.} +procedure TGIFFileFormat.LZWCompress(const IO: TIOFunctions; Handle: TImagingHandle; Width, Height, BitCount: Integer; + Interlaced: Boolean; Data: Pointer); +var + LineIdent: Integer; + MinCodeSize, Col: Byte; + InitCodeSize, X, Y: Integer; + Pass: Integer; + MaxCode: Integer; { 1 shl CodeSize } + ClearCode, EndingCode, LastCode, Tail: Integer; + I, HashValue: Integer; + LenString: Word; + Dict: PDictTable; + HashTable: TList; + PData: PByte; + WriteCtxt: TWriteContext; + + function InitHash(P: Integer): Integer; + begin + Result := (P + 3) * 301; + end; + + procedure WriteCode(Code: Integer; var Context: TWriteContext); + var + BufIndex: Integer; + Bytes: Byte; + begin + BufIndex := Context.Inx shr 3; + Code := Code shl (Context.Inx and 7); + Context.Buf[BufIndex] := Context.Buf[BufIndex] or Byte(Code); + Context.Buf[BufIndex + 1] := Byte(Code shr 8); + Context.Buf[BufIndex + 2] := Byte(Code shr 16); + Context.Inx := Context.Inx + Context.CodeSize; + if Context.Inx >= 255 * 8 then + begin + // Flush out full buffer + Bytes := 255; + IO.Write(Handle, @Bytes, 1); + IO.Write(Handle, @Context.Buf, Bytes); + Move(Context.Buf[255], Context.Buf[0], 2); + FillChar(Context.Buf[2], 255, 0); + Context.Inx := Context.Inx - (255 * 8); + end; + end; + + procedure FlushCode(var Context: TWriteContext); + var + Bytes: Byte; + begin + Bytes := (Context.Inx + 7) shr 3; + if Bytes > 0 then + begin + IO.Write(Handle, @Bytes, 1); + IO.Write(Handle, @Context.Buf, Bytes); + end; + // Data block terminator - a block of zero Size + Bytes := 0; + IO.Write(Handle, @Bytes, 1); + end; + +begin + LineIdent := Width; + Tail := 0; + HashValue := 0; + Col := 0; + HashTable := TList.Create; + GetMem(Dict, SizeOf(TDictTable)); + try + for I := 0 to HashTableSize - 1 do + HashTable.Add(nil); + + // Initialise encoder variables + InitCodeSize := BitCount + 1; + if InitCodeSize = 2 then + Inc(InitCodeSize); + MinCodeSize := InitCodeSize - 1; + IO.Write(Handle, @MinCodeSize, 1); + ClearCode := 1 shl MinCodeSize; + EndingCode := ClearCode + 1; + LastCode := EndingCode; + MaxCode := 1 shl InitCodeSize; + LenString := 0; + // Setup write context + WriteCtxt.Inx := 0; + WriteCtxt.CodeSize := InitCodeSize; + FillChar(WriteCtxt.Buf, SizeOf(WriteCtxt.Buf), 0); + WriteCode(ClearCode, WriteCtxt); + Y := 0; + Pass := 0; + + while Y < Height do + begin + PData := @PByteArray(Data)[Y * LineIdent]; + for X := 0 to Width - 1 do + begin + // Only ifIndex8 support + case BitCount of + 8: + begin + Col := PData^; + PData := @PByteArray(PData)[1]; + end; + {4: + begin + if X and 1 <> 0 then + begin + Col := PData^ and $0F; + PData := @PByteArray(PData)[1]; + end + else + Col := PData^ shr 4; + end; + 1: + begin + if X and 7 = 7 then + begin + Col := PData^ and 1; + PData := @PByteArray(PData)[1]; + end + else + Col := (PData^ shr (7 - (X and $07))) and $01; + end;} + end; + Inc(LenString); + if LenString = 1 then + begin + Tail := Col; + HashValue := InitHash(Col); + end + else + begin + HashValue := HashValue * (Col + LenString + 4); + I := HashValue mod HashTableSize; + HashValue := HashValue mod HashTableSize; + while (HashTable[I] <> nil) and + ((PImageDict(HashTable[I])^.Tail <> Tail) or + (PImageDict(HashTable[I])^.Col <> Col)) do + begin + Inc(I); + if I >= HashTableSize then + I := 0; + end; + if HashTable[I] <> nil then // Found in the strings table + Tail := PImageDict(HashTable[I])^.Index + else + begin + // Not found + WriteCode(Tail, WriteCtxt); + Inc(LastCode); + HashTable[I] := @Dict^[LastCode]; + PImageDict(HashTable[I])^.Index := LastCode; + PImageDict(HashTable[I])^.Tail := Tail; + PImageDict(HashTable[I])^.Col := Col; + Tail := Col; + HashValue := InitHash(Col); + LenString := 1; + if LastCode >= MaxCode then + begin + // Next Code will be written longer + MaxCode := MaxCode shl 1; + Inc(WriteCtxt.CodeSize); + end + else + if LastCode >= CodeTableSize - 2 then + begin + // Reset tables + WriteCode(Tail, WriteCtxt); + WriteCode(ClearCode, WriteCtxt); + LenString := 0; + LastCode := EndingCode; + WriteCtxt.CodeSize := InitCodeSize; + MaxCode := 1 shl InitCodeSize; + for I := 0 to HashTableSize - 1 do + HashTable[I] := nil; + end; + end; + end; + end; + if Interlaced then + Y := InterlaceStep(Y, Height, Pass) + else + Inc(Y); + end; + WriteCode(Tail, WriteCtxt); + WriteCode(EndingCode, WriteCtxt); + FlushCode(WriteCtxt); + finally + HashTable.Free; + FreeMem(Dict); + end; +end; + +function TGIFFileFormat.LoadData(Handle: TImagingHandle; + var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; +type + TFrameInfo = record + Left, Top: Integer; + Width, Height: Integer; + Disposal: TDisposalMethod; + HasTransparency: Boolean; + HasLocalPal: Boolean; + TransIndex: Integer; + BackIndex: Integer; + end; +var + Header: TGIFHeader; + HasGlobalPal: Boolean; + GlobalPalLength: Integer; + GlobalPal: TPalette32Size256; + ScreenWidth, ScreenHeight, I, CachedIndex: Integer; + BlockID: Byte; + HasGraphicExt: Boolean; + GraphicExt: TGraphicControlExtension; + FrameInfos: array of TFrameInfo; + AppRead: Boolean; + CachedFrame: TImageData; + AnimFrames: TDynImageDataArray; + + function ReadBlockID: Byte; + begin + Result := GIFTrailer; + if GetIO.Read(Handle, @Result, SizeOf(Result)) < SizeOf(Result) then + Result := GIFTrailer; + end; + + procedure ReadExtensions; + var + BlockSize, BlockType, ExtType: Byte; + AppRec: TGIFApplicationRec; + LoopCount: SmallInt; + + procedure SkipBytes; + begin + with GetIO do + repeat + // Read block sizes and skip them + Read(Handle, @BlockSize, SizeOf(BlockSize)); + Seek(Handle, BlockSize, smFromCurrent); + until BlockSize = 0; + end; + + begin + HasGraphicExt := False; + AppRead := False; + + // Read extensions until image descriptor is found. Only graphic extension + // is stored now (for transparency), others are skipped. + while BlockID = GIFExtensionIntroducer do + with GetIO do + begin + Read(Handle, @ExtType, SizeOf(ExtType)); + + while ExtType in [GIFGraphicControlExtension, GIFCommentExtension, GIFApplicationExtension, GIFPlainText] do + begin + if ExtType = GIFGraphicControlExtension then + begin + HasGraphicExt := True; + Read(Handle, @GraphicExt, SizeOf(GraphicExt)); + end + else if (ExtType = GIFApplicationExtension) and not AppRead then + begin + Read(Handle, @BlockSize, SizeOf(BlockSize)); + if BlockSize >= SizeOf(AppRec) then + begin + Read(Handle, @AppRec, SizeOf(AppRec)); + if ((AppRec.Identifier = 'NETSCAPE') and (AppRec.Authentication = '2.0')) or + ((AppRec.Identifier = 'ANIMEXTS') and (AppRec.Authentication = '1.0')) then + begin + Read(Handle, @BlockSize, SizeOf(BlockSize)); + while BlockSize <> 0 do + begin + BlockType := ReadBlockID; + Dec(BlockSize); + + case BlockType of + GIFAppLoopExtension: + if (BlockSize >= SizeOf(LoopCount)) then + begin + // Read loop count + Read(Handle, @LoopCount, SizeOf(LoopCount)); + Dec(BlockSize, SizeOf(LoopCount)); + if LoopCount > 0 then + Inc(LoopCount); // Netscape extension is really "repeats" not "loops" + FMetadata.SetMetaItem(SMetaAnimationLoops, LoopCount); + end; + GIFAppBufferExtension: + begin + Dec(BlockSize, SizeOf(Word)); + Seek(Handle, SizeOf(Word), smFromCurrent); + end; + end; + end; + SkipBytes; + AppRead := True; + end + else + begin + // Revert all bytes reading + Seek(Handle, - SizeOf(AppRec) - SizeOf(BlockSize), smFromCurrent); + SkipBytes; + end; + end + else + begin + Seek(Handle, - BlockSize - SizeOf(BlockSize), smFromCurrent); + SkipBytes; + end; + end + else if ExtType in [GIFCommentExtension, GIFApplicationExtension, GIFPlainText] then + repeat + // Read block sizes and skip them + Read(Handle, @BlockSize, SizeOf(BlockSize)); + Seek(Handle, BlockSize, smFromCurrent); + until BlockSize = 0; + + // Read ID of following block + BlockID := ReadBlockID; + ExtType := BlockID; + end + end; + end; + + procedure CopyLZWData(Dest: TStream); + var + CodeSize, BlockSize: Byte; + InputSize: Integer; + Buff: array[Byte] of Byte; + begin + InputSize := ImagingIO.GetInputSize(GetIO, Handle); + // Copy codesize to stream + GetIO.Read(Handle, @CodeSize, 1); + Dest.Write(CodeSize, 1); + repeat + // Read and write data blocks, last is block term value of 0 + GetIO.Read(Handle, @BlockSize, 1); + Dest.Write(BlockSize, 1); + if BlockSize > 0 then + begin + GetIO.Read(Handle, @Buff[0], BlockSize); + Dest.Write(Buff[0], BlockSize); + end; + until (BlockSize = 0) or (GetIO.Tell(Handle) >= InputSize); + end; + + procedure ReadFrame; + var + ImageDesc: TImageDescriptor; + Interlaced: Boolean; + I, Idx, LocalPalLength: Integer; + LocalPal: TPalette32Size256; + LZWStream: TMemoryStream; + + procedure RemoveBadFrame; + begin + FreeImage(Images[Idx]); + SetLength(Images, Length(Images) - 1); + end; + + begin + Idx := Length(Images); + SetLength(Images, Idx + 1); + SetLength(FrameInfos, Idx + 1); + FillChar(LocalPal, SizeOf(LocalPal), 0); + + with GetIO do + begin + // Read and parse image descriptor + Read(Handle, @ImageDesc, SizeOf(ImageDesc)); + FrameInfos[Idx].HasLocalPal := (ImageDesc.PackedFields and GIFLocalColorTable) = GIFLocalColorTable; + Interlaced := (ImageDesc.PackedFields and GIFInterlaced) = GIFInterlaced; + LocalPalLength := ImageDesc.PackedFields and GIFColorTableSize; + LocalPalLength := 1 shl (LocalPalLength + 1); // Total pal length is 2^(n+1) + + // From Mozilla source + if (ImageDesc.Width = 0) or (ImageDesc.Width > Header.ScreenWidth) then + ImageDesc.Width := Header.ScreenWidth; + if (ImageDesc.Height = 0) or (ImageDesc.Height > Header.ScreenHeight) then + ImageDesc.Height := Header.ScreenHeight; + + FrameInfos[Idx].Left := ImageDesc.Left; + FrameInfos[Idx].Top := ImageDesc.Top; + FrameInfos[Idx].Width := ImageDesc.Width; + FrameInfos[Idx].Height := ImageDesc.Height; + FrameInfos[Idx].BackIndex := Header.BackgroundColorIndex; + + // Create new image for this frame which would be later pasted onto logical screen + NewImage(ImageDesc.Width, ImageDesc.Height, ifIndex8, Images[Idx]); + + // Load local palette if there is any + if FrameInfos[Idx].HasLocalPal then + for I := 0 to LocalPalLength - 1 do + begin + LocalPal[I].A := 255; + Read(Handle, @LocalPal[I].R, SizeOf(LocalPal[I].R)); + Read(Handle, @LocalPal[I].G, SizeOf(LocalPal[I].G)); + Read(Handle, @LocalPal[I].B, SizeOf(LocalPal[I].B)); + end; + + // Use local pal if present or global pal if present or create + // default pal if neither of them is present + if FrameInfos[Idx].HasLocalPal then + Move(LocalPal, Images[Idx].Palette^, SizeOf(LocalPal)) + else if HasGlobalPal then + Move(GlobalPal, Images[Idx].Palette^, SizeOf(GlobalPal)) + else + FillCustomPalette(Images[Idx].Palette, GlobalPalLength, 3, 3, 2); + + if (ImageDesc.Left <= Header.ScreenWidth + 1) and (ImageDesc.Top <= Header.ScreenHeight + 1) then + begin + // Resize the screen if needed to fit the frame + ScreenWidth := Max(ScreenWidth, ImageDesc.Width + ImageDesc.Left); + ScreenHeight := Max(ScreenHeight, ImageDesc.Height + ImageDesc.Top); + end + else + begin + // Remove frame outside logical screen + RemoveBadFrame; + Exit; + end; + + // If Grahic Control Extension is present make use of it + if HasGraphicExt then + begin + FrameInfos[Idx].HasTransparency := (GraphicExt.PackedFields and GIFTransparent) = GIFTransparent; + FrameInfos[Idx].Disposal := TDisposalMethod((GraphicExt.PackedFields and GIFDisposalMethod) shr 2); + if FrameInfos[Idx].HasTransparency then + begin + FrameInfos[Idx].TransIndex := GraphicExt.TransparentColorIndex; + Images[Idx].Palette[FrameInfos[Idx].TransIndex].A := 0; + end; + FMetadata.SetMetaItem(SMetaFrameDelay, Integer(GraphicExt.DelayTime * 10), Idx); + end + else + FrameInfos[Idx].HasTransparency := False; + + LZWStream := TMemoryStream.Create; + try + try + // Copy LZW data to temp stream, needed for correct decompression + CopyLZWData(LZWStream); + LZWStream.Position := 0; + // Data decompression finally + LZWDecompress(LZWStream, Handle, ImageDesc.Width, ImageDesc.Height, Interlaced, Images[Idx].Bits); + except + RemoveBadFrame; + Exit; + end; + finally + LZWStream.Free; + end; + end; + end; + + procedure CopyFrameTransparent32(const Image, Frame: TImageData; Left, Top: Integer); + var + X, Y: Integer; + Src: PByte; + Dst: PColor32; + begin + Src := Frame.Bits; + + // Copy all pixels from frame to log screen but ignore the transparent ones + for Y := 0 to Frame.Height - 1 do + begin + Dst := @PColor32RecArray(Image.Bits)[(Top + Y) * Image.Width + Left]; + for X := 0 to Frame.Width - 1 do + begin + if (Frame.Palette[Src^].A <> 0) then + Dst^ := Frame.Palette[Src^].Color; + Inc(Src); + Inc(Dst); + end; + end; + end; + + procedure AnimateFrame(Index: Integer; var AnimFrame: TImageData); + var + I, First, Last: Integer; + UseCache: Boolean; + BGColor: TColor32; + begin + // We may need to use raw frame 0 to n to correctly animate n-th frame + Last := Index; + First := Max(0, Last); + // See if we can use last animate frame as a basis for this one + // (so we don't have to use previous raw frames). + UseCache := TestImage(CachedFrame) and (CachedIndex = Index - 1) and (CachedIndex >= 0) and + (FrameInfos[CachedIndex].Disposal <> dmRestorePrevious); + + // Reuse or release cache + if UseCache then + CloneImage(CachedFrame, AnimFrame) + else + FreeImage(CachedFrame); + + // Default color for clearing of the screen + BGColor := Images[Index].Palette[FrameInfos[Index].BackIndex].Color; + + // Now prepare logical screen for drawing of raw frame at Index. + // We may need to use all previous raw frames to get the screen + // to proper state (according to their disposal methods). + + if not UseCache then + begin + if FrameInfos[Index].HasTransparency then + BGColor := Images[Index].Palette[FrameInfos[Index].TransIndex].Color; + // Clear whole screen + FillMemoryLongWord(AnimFrame.Bits, AnimFrame.Size, BGColor); + + // Try to maximize First so we don't have to use all 0 to n raw frames + while First > 0 do + begin + if (ScreenWidth = Images[First].Width) and (ScreenHeight = Images[First].Height) then + begin + if (FrameInfos[First].Disposal = dmRestoreBackground) and (First < Last) then + Break; + end; + Dec(First); + end; + + for I := First to Last - 1 do + begin + case FrameInfos[I].Disposal of + dmNoRemoval, dmLeave: + begin + // Copy previous raw frame onto screen + CopyFrameTransparent32(AnimFrame, Images[I], FrameInfos[I].Left, FrameInfos[I].Top); + end; + dmRestoreBackground: + if (I > First) then + begin + // Restore background color + FillRect(AnimFrame, FrameInfos[I].Left, FrameInfos[I].Top, + FrameInfos[I].Width, FrameInfos[I].Height, @BGColor); + end; + dmRestorePrevious: ; // Do nothing - previous state is already on screen + end; + end; + end + else if FrameInfos[CachedIndex].Disposal = dmRestoreBackground then + begin + // We have our cached result but also need to restore + // background in a place of cached frame + if FrameInfos[CachedIndex].HasTransparency then + BGColor := Images[CachedIndex].Palette[FrameInfos[CachedIndex].TransIndex].Color; + FillRect(AnimFrame, FrameInfos[CachedIndex].Left, FrameInfos[CachedIndex].Top, + FrameInfos[CachedIndex].Width, FrameInfos[CachedIndex].Height, @BGColor); + end; + + // Copy current raw frame to prepared screen + CopyFrameTransparent32(AnimFrame, Images[Index], FrameInfos[Index].Left, FrameInfos[Index].Top); + + // Cache animated result + CloneImage(AnimFrame, CachedFrame); + CachedIndex := Index; + end; + +begin + AppRead := False; + + SetLength(Images, 0); + FillChar(GlobalPal, SizeOf(GlobalPal), 0); + + with GetIO do + begin + // Read GIF header + Read(Handle, @Header, SizeOf(Header)); + ScreenWidth := Header.ScreenWidth; + ScreenHeight := Header.ScreenHeight; + HasGlobalPal := Header.PackedFields and GIFGlobalColorTable = GIFGlobalColorTable; // Bit 7 + GlobalPalLength := Header.PackedFields and GIFColorTableSize; // Bits 0-2 + GlobalPalLength := 1 shl (GlobalPalLength + 1); // Total pal length is 2^(n+1) + + // Read global palette from file if present + if HasGlobalPal then + begin + for I := 0 to GlobalPalLength - 1 do + begin + GlobalPal[I].A := 255; + Read(Handle, @GlobalPal[I].R, SizeOf(GlobalPal[I].R)); + Read(Handle, @GlobalPal[I].G, SizeOf(GlobalPal[I].G)); + Read(Handle, @GlobalPal[I].B, SizeOf(GlobalPal[I].B)); + end; + end; + + // Read ID of the first block + BlockID := ReadBlockID; + + // Now read all data blocks in the file until file trailer is reached + while BlockID <> GIFTrailer do + begin + // Read blocks until we find the one of known type + while not (BlockID in [GIFTrailer, GIFExtensionIntroducer, GIFImageDescriptor]) do + BlockID := ReadBlockID; + // Read supported and skip unsupported extensions + ReadExtensions; + // If image frame is found read it + if BlockID = GIFImageDescriptor then + ReadFrame; + // Read next block's ID + BlockID := ReadBlockID; + // If block ID is unknown set it to end-of-GIF marker + if not (BlockID in [GIFExtensionIntroducer, GIFTrailer, GIFImageDescriptor]) then + BlockID := GIFTrailer; + end; + + if FLoadAnimated then + begin + // Aniated frames will be stored in AnimFrames + SetLength(AnimFrames, Length(Images)); + InitImage(CachedFrame); + CachedIndex := -1; + + for I := 0 to High(Images) do + begin + // Create new logical screen + NewImage(ScreenWidth, ScreenHeight, ifA8R8G8B8, AnimFrames[I]); + // Animate frames to current log screen + AnimateFrame(I, AnimFrames[I]); + end; + + // Now release raw 8bit frames and put animated 32bit ones + // to output array + FreeImage(CachedFrame); + for I := 0 to High(AnimFrames) do + begin + FreeImage(Images[I]); + Images[I] := AnimFrames[I]; + end; + end; + + Result := True; + end; +end; + +function TGIFFileFormat.SaveData(Handle: TImagingHandle; + const Images: TDynImageDataArray; Index: Integer): Boolean; +var + Header: TGIFHeader; + ImageDesc: TImageDescriptor; + ImageToSave: TImageData; + MustBeFreed: Boolean; + I, J: Integer; + GraphicExt: TGraphicControlExtension; + + procedure FindMaxDimensions(var MaxWidth, MaxHeight: Word); + var + I: Integer; + begin + MaxWidth := Images[FFirstIdx].Width; + MaxHeight := Images[FFirstIdx].Height; + + for I := FFirstIdx + 1 to FLastIdx do + begin + MaxWidth := Iff(Images[I].Width > MaxWidth, Images[I].Width, MaxWidth); + MaxHeight := Iff(Images[I].Height > MaxWidth, Images[I].Height, MaxHeight); + end; + end; + + procedure SetFrameDelay(Idx: Integer; var Ext: TGraphicControlExtension); + begin + if FMetadata.HasMetaItemForSaving(SMetaFrameDelay, Idx) then + Ext.DelayTime := FMetadata.MetaItemsForSavingMulti[SMetaFrameDelay, Idx] div 10 + else + Ext.DelayTime := GIFDefaultDelay; + end; + + procedure SaveGlobalMetadata; + var + AppExt: TGIFApplicationRec; + BlockSize, LoopExtId: Byte; + Repeats: Word; + begin + if FMetadata.HasMetaItemForSaving(SMetaAnimationLoops) then + with GetIO do + begin + FillChar(AppExt, SizeOf(AppExt), 0); + AppExt.Identifier := 'NETSCAPE'; + AppExt.Authentication := '2.0'; + Repeats := FMetadata.MetaItemsForSaving[SMetaAnimationLoops]; + if Repeats > 0 then + Dec(Repeats); + LoopExtId := GIFAppLoopExtension; + + Write(Handle, @GIFExtensionIntroducer, SizeOf(GIFExtensionIntroducer)); + Write(Handle, @GIFApplicationExtension, SizeOf(GIFApplicationExtension)); + BlockSize := 11; + Write(Handle, @BlockSize, SizeOf(BlockSize)); + Write(Handle, @AppExt, SizeOf(AppExt)); + BlockSize := 3; + Write(Handle, @BlockSize, SizeOf(BlockSize)); + Write(Handle, @LoopExtId, SizeOf(LoopExtId)); + Write(Handle, @Repeats, SizeOf(Repeats)); + Write(Handle, @GIFBlockTerminator, SizeOf(GIFBlockTerminator)); + end; + end; + +begin + // Fill header with data, select size of largest image in array as + // logical screen size + FillChar(Header, Sizeof(Header), 0); + Header.Signature := GIFSignature; + Header.Version := GIFVersions[gv89]; + FindMaxDimensions(Header.ScreenWidth, Header.ScreenHeight); + Header.PackedFields := GIFColorResolution; // Color resolution is 256 + GetIO.Write(Handle, @Header, SizeOf(Header)); + + // Prepare default GC extension with delay + FillChar(GraphicExt, Sizeof(GraphicExt), 0); + GraphicExt.DelayTime := GIFDefaultDelay; + GraphicExt.BlockSize := 4; + + SaveGlobalMetadata; + + for I := FFirstIdx to FLastIdx do + begin + if MakeCompatible(Images[I], ImageToSave, MustBeFreed) then + with GetIO, ImageToSave do + try + // Write Graphic Control Extension with default delay + Write(Handle, @GIFExtensionIntroducer, SizeOf(GIFExtensionIntroducer)); + Write(Handle, @GIFGraphicControlExtension, SizeOf(GIFGraphicControlExtension)); + SetFrameDelay(I, GraphicExt); + Write(Handle, @GraphicExt, SizeOf(GraphicExt)); + // Write frame marker and fill and write image descriptor for this frame + Write(Handle, @GIFImageDescriptor, SizeOf(GIFImageDescriptor)); + FillChar(ImageDesc, Sizeof(ImageDesc), 0); + ImageDesc.Width := Width; + ImageDesc.Height := Height; + ImageDesc.PackedFields := GIFLocalColorTable or GIFColorTableSize; // Use lccal color table with 256 entries + Write(Handle, @ImageDesc, SizeOf(ImageDesc)); + + // Write local color table for each frame + for J := 0 to 255 do + begin + Write(Handle, @Palette[J].R, SizeOf(Palette[J].R)); + Write(Handle, @Palette[J].G, SizeOf(Palette[J].G)); + Write(Handle, @Palette[J].B, SizeOf(Palette[J].B)); + end; + + // Finally compress image data + LZWCompress(GetIO, Handle, Width, Height, 8, False, Bits); + + finally + if MustBeFreed then + FreeImage(ImageToSave); + end; + end; + + GetIO.Write(Handle, @GIFTrailer, SizeOf(GIFTrailer)); + Result := True; +end; + +procedure TGIFFileFormat.ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); +begin + ConvertImage(Image, ifIndex8); +end; + +function TGIFFileFormat.TestFormat(Handle: TImagingHandle): Boolean; +var + Header: TGIFHeader; + ReadCount: Integer; +begin + Result := False; + if Handle <> nil then + begin + ReadCount := GetIO.Read(Handle, @Header, SizeOf(Header)); + GetIO.Seek(Handle, -ReadCount, smFromCurrent); + Result := (ReadCount >= SizeOf(Header)) and + (Header.Signature = GIFSignature) and + ((Header.Version = GIFVersions[gv87]) or (Header.Version = GIFVersions[gv89])); + end; +end; + +initialization + RegisterImageFileFormat(TGIFFileFormat); + +{ + File Notes: + + -- TODOS ---------------------------------------------------- + - nothing now + + -- 0.77 Changes/Bug Fixes ----------------------------------- + - Fixed crash when resaving GIF with animation metadata. + - Writes frame delays of GIF animations from metadata. + - Reads and writes looping of GIF animations stored into/from metadata. + + -- 0.26.5 Changes/Bug Fixes --------------------------------- + - Reads frame delays from GIF animations into metadata. + + -- 0.26.3 Changes/Bug Fixes --------------------------------- + - Fixed bug - loading of GIF with NETSCAPE app extensions + failed with Delphi 2009. + + -- 0.26.1 Changes/Bug Fixes --------------------------------- + - GIF loading and animation mostly rewritten, based on + modification by Sergey Galezdinov (ExtraGIF in Extras/Contrib). + + -- 0.25.0 Changes/Bug Fixes --------------------------------- + - Fixed loading of some rare GIFs, problems with LZW + decompression. + + -- 0.24.3 Changes/Bug Fixes --------------------------------- + - Better solution to transparency for some GIFs. Background not + transparent by default. + + -- 0.24.1 Changes/Bug Fixes --------------------------------- + - Made backround color transparent by default (alpha = 0). + + -- 0.23 Changes/Bug Fixes ----------------------------------- + - Fixed other loading bugs (local pal size, transparency). + - Added GIF saving. + - Fixed bug when loading multiframe GIFs and implemented few animation + features (disposal methods, ...). + - Loading of GIFs working. + - Unit created with initial stuff! +} + +end. diff --git a/resources/libraries/deskew/Imaging/ImagingIO.pas b/resources/libraries/deskew/Imaging/ImagingIO.pas new file mode 100755 index 0000000..6a58e4b --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingIO.pas @@ -0,0 +1,685 @@ +{ + Vampyre Imaging Library + by Marek Mauder + http://imaginglib.sourceforge.net + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +{ This unit contains default IO functions for reading from/writting to + files, streams and memory.} +unit ImagingIO; + +{$I ImagingOptions.inc} + +interface + +uses + SysUtils, Classes, ImagingTypes, Imaging, ImagingUtility; + +type + TMemoryIORec = record + Data: ImagingUtility.PByteArray; + Position: LongInt; + Size: LongInt; + end; + PMemoryIORec = ^TMemoryIORec; + +var + OriginalFileIO: TIOFunctions; + FileIO: TIOFunctions; + StreamIO: TIOFunctions; + MemoryIO: TIOFunctions; + +{ Helper function that returns size of input (from current position to the end) + represented by Handle (and opened and operated on by members of IOFunctions).} +function GetInputSize(const IOFunctions: TIOFunctions; Handle: TImagingHandle): Int64; +{ Helper function that initializes TMemoryIORec with given params.} +function PrepareMemIO(Data: Pointer; Size: LongInt): TMemoryIORec; +{ Reads one text line from input (CR+LF, CR, or LF as line delimiter).} +function ReadLine(const IOFunctions: TIOFunctions; Handle: TImagingHandle; + out Line: AnsiString; FailOnControlChars: Boolean = False): Boolean; +{ Writes one text line to input with optional line delimiter.} +procedure WriteLine(const IOFunctions: TIOFunctions; Handle: TImagingHandle; + const Line: AnsiString; const LineEnding: AnsiString = sLineBreak); + +type + TReadMemoryStream = class(TCustomMemoryStream) + public + constructor Create(Data: Pointer; Size: Integer); + class function CreateFromIOHandle(const IOFunctions: TIOFunctions; Handle: TImagingHandle): TReadMemoryStream; + end; + + TImagingIOStream = class(TStream) + private + FIO: TIOFunctions; + FHandle: TImagingHandle; + public + constructor Create(const IOFunctions: TIOFunctions; Handle: TImagingHandle); + end; + +implementation + +const + DefaultBufferSize = 16 * 1024; + +type + { Based on TaaBufferedStream + Copyright (c) Julian M Bucknall 1997, 1999 } + TBufferedStream = class + private + FBuffer: PByteArray; + FBufSize: Integer; + FBufStart: Integer; + FBufPos: Integer; + FBytesInBuf: Integer; + FSize: Integer; + FDirty: Boolean; + FStream: TStream; + function GetPosition: Integer; + function GetSize: Integer; + procedure ReadBuffer; + procedure WriteBuffer; + procedure SetPosition(const Value: Integer); + public + constructor Create(AStream: TStream); + destructor Destroy; override; + function Read(var Buffer; Count: Integer): Integer; + function Write(const Buffer; Count: Integer): Integer; + function Seek(Offset: Integer; Origin: Word): Integer; + procedure Commit; + property Stream: TStream read FStream; + property Position: Integer read GetPosition write SetPosition; + property Size: Integer read GetSize; + end; + +constructor TBufferedStream.Create(AStream: TStream); +begin + inherited Create; + FStream := AStream; + FBufSize := DefaultBufferSize; + GetMem(FBuffer, FBufSize); + FBufPos := 0; + FBytesInBuf := 0; + FBufStart := 0; + FDirty := False; + FSize := AStream.Size; +end; + +destructor TBufferedStream.Destroy; +begin + if FBuffer <> nil then + begin + Commit; + FreeMem(FBuffer); + end; + FStream.Position := Position; // Make sure source stream has right position + inherited Destroy; +end; + +function TBufferedStream.GetPosition: Integer; +begin + Result := FBufStart + FBufPos; +end; + +procedure TBufferedStream.SetPosition(const Value: Integer); +begin + Seek(Value, soFromCurrent); +end; + +function TBufferedStream.GetSize: Integer; +begin + Result := FSize; +end; + +procedure TBufferedStream.ReadBuffer; +var + SeekResult: Integer; +begin + SeekResult := FStream.Seek(FBufStart, 0); + if SeekResult = -1 then + raise Exception.Create('TBufferedStream.ReadBuffer: seek failed'); + FBytesInBuf := FStream.Read(FBuffer^, FBufSize); + if FBytesInBuf <= 0 then + raise Exception.Create('TBufferedStream.ReadBuffer: read failed'); +end; + +procedure TBufferedStream.WriteBuffer; +var + SeekResult: Integer; + BytesWritten: Integer; +begin + SeekResult := FStream.Seek(FBufStart, 0); + if SeekResult = -1 then + raise Exception.Create('TBufferedStream.WriteBuffer: seek failed'); + BytesWritten := FStream.Write(FBuffer^, FBytesInBuf); + if BytesWritten <> FBytesInBuf then + raise Exception.Create('TBufferedStream.WriteBuffer: write failed'); +end; + +procedure TBufferedStream.Commit; +begin + if FDirty then + begin + WriteBuffer; + FDirty := False; + end; +end; + +function TBufferedStream.Read(var Buffer; Count: Integer): Integer; +var + BufAsBytes : TByteArray absolute Buffer; + BufIdx, BytesToGo, BytesToRead: Integer; +begin + // Calculate the actual number of bytes we can read - this depends on + // the current position and size of the stream as well as the number + // of bytes requested. + BytesToGo := Count; + if FSize < (FBufStart + FBufPos + Count) then + BytesToGo := FSize - (FBufStart + FBufPos); + + if BytesToGo <= 0 then + begin + Result := 0; + Exit; + end; + // Remember to return the result of our calculation + Result := BytesToGo; + + BufIdx := 0; + if FBytesInBuf = 0 then + ReadBuffer; + // Calculate the number of bytes we can read prior to the loop + BytesToRead := FBytesInBuf - FBufPos; + if BytesToRead > BytesToGo then + BytesToRead := BytesToGo; + // Copy from the stream buffer to the caller's buffer + Move(FBuffer^[FBufPos], BufAsBytes[BufIdx], BytesToRead); + // Calculate the number of bytes still to read} + Dec(BytesToGo, BytesToRead); + + // while we have bytes to read, read them + while BytesToGo > 0 do + begin + Inc(BufIdx, BytesToRead); + // As we've exhausted this buffer-full, advance to the next, check + // to see whether we need to write the buffer out first + if FDirty then + begin + WriteBuffer; + FDirty := false; + end; + Inc(FBufStart, FBufSize); + FBufPos := 0; + ReadBuffer; + // Calculate the number of bytes we can read in this cycle + BytesToRead := FBytesInBuf; + if BytesToRead > BytesToGo then + BytesToRead := BytesToGo; + // Ccopy from the stream buffer to the caller's buffer + Move(FBuffer^, BufAsBytes[BufIdx], BytesToRead); + // Calculate the number of bytes still to read + Dec(BytesToGo, BytesToRead); + end; + // Remember our new position + Inc(FBufPos, BytesToRead); + if FBufPos = FBufSize then + begin + Inc(FBufStart, FBufSize); + FBufPos := 0; + FBytesInBuf := 0; + end; +end; + +function TBufferedStream.Seek(Offset: Integer; Origin: Word): Integer; +var + NewBufStart, NewPos: Integer; +begin + // Calculate the new position + case Origin of + soFromBeginning : NewPos := Offset; + soFromCurrent : NewPos := FBufStart + FBufPos + Offset; + soFromEnd : NewPos := FSize + Offset; + else + raise Exception.Create('TBufferedStream.Seek: invalid origin'); + end; + + if (NewPos < 0) or (NewPos > FSize) then + begin + //NewPos := ClampInt(NewPos, 0, FSize); don't do this - for writing + end; + // Calculate which page of the file we need to be at + NewBufStart := NewPos and not Pred(FBufSize); + // If the new page is different than the old, mark the buffer as being + // ready to be replenished, and if need be write out any dirty data + if NewBufStart <> FBufStart then + begin + if FDirty then + begin + WriteBuffer; + FDirty := False; + end; + FBufStart := NewBufStart; + FBytesInBuf := 0; + end; + // Save the new position + FBufPos := NewPos - NewBufStart; + Result := NewPos; +end; + +function TBufferedStream.Write(const Buffer; Count: Integer): Integer; +var + BufAsBytes: TByteArray absolute Buffer; + BufIdx, BytesToGo, BytesToWrite: Integer; +begin + // When we write to this stream we always assume that we can write the + // requested number of bytes: if we can't (eg, the disk is full) we'll + // get an exception somewhere eventually. + BytesToGo := Count; + // Remember to return the result of our calculation + Result := BytesToGo; + + BufIdx := 0; + if (FBytesInBuf = 0) and (FSize > FBufStart) then + ReadBuffer; + // Calculate the number of bytes we can write prior to the loop + BytesToWrite := FBufSize - FBufPos; + if BytesToWrite > BytesToGo then + BytesToWrite := BytesToGo; + // Copy from the caller's buffer to the stream buffer + Move(BufAsBytes[BufIdx], FBuffer^[FBufPos], BytesToWrite); + // Mark our stream buffer as requiring a save to the actual stream, + // note that this will suffice for the rest of the routine as well: no + // inner routine will turn off the dirty flag. + FDirty := True; + // Calculate the number of bytes still to write + Dec(BytesToGo, BytesToWrite); + + // While we have bytes to write, write them + while BytesToGo > 0 do + begin + Inc(BufIdx, BytesToWrite); + // As we've filled this buffer, write it out to the actual stream + // and advance to the next buffer, reading it if required + FBytesInBuf := FBufSize; + WriteBuffer; + Inc(FBufStart, FBufSize); + FBufPos := 0; + FBytesInBuf := 0; + if FSize > FBufStart then + ReadBuffer; + // Calculate the number of bytes we can write in this cycle + BytesToWrite := FBufSize; + if BytesToWrite > BytesToGo then + BytesToWrite := BytesToGo; + // Copy from the caller's buffer to our buffer + Move(BufAsBytes[BufIdx], FBuffer^, BytesToWrite); + // Calculate the number of bytes still to write + Dec(BytesToGo, BytesToWrite); + end; + // Remember our new position + Inc(FBufPos, BytesToWrite); + // Make sure the count of valid bytes is correct + if FBytesInBuf < FBufPos then + FBytesInBuf := FBufPos; + // Make sure the stream size is correct + if FSize < (FBufStart + FBytesInBuf) then + FSize := FBufStart + FBytesInBuf; + // If we're at the end of the buffer, write it out and advance to the + // start of the next page + if FBufPos = FBufSize then + begin + WriteBuffer; + FDirty := False; + Inc(FBufStart, FBufSize); + FBufPos := 0; + FBytesInBuf := 0; + end; +end; + +{ File IO functions } + +function FileOpen(FileName: PChar; Mode: TOpenMode): TImagingHandle; cdecl; +var + Stream: TStream; +begin + Stream := nil; + + case Mode of + omReadOnly: Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); + omCreate: Stream := TFileStream.Create(FileName, fmCreate); + omReadWrite: + begin + if FileExists(FileName) then + Stream := TFileStream.Create(FileName, fmOpenReadWrite or fmShareExclusive) + else + Stream := TFileStream.Create(FileName, fmCreate); + end; + end; + + Assert(Stream <> nil); + Result := TBufferedStream.Create(Stream); +end; + +procedure FileClose(Handle: TImagingHandle); cdecl; +var + Stream: TStream; +begin + Stream := TBufferedStream(Handle).Stream; + TBufferedStream(Handle).Free; + Stream.Free; +end; + +function FileEof(Handle: TImagingHandle): Boolean; cdecl; +begin + Result := TBufferedStream(Handle).Position = TBufferedStream(Handle).Size; +end; + +function FileSeek(Handle: TImagingHandle; Offset: Int64; Mode: TSeekMode): Int64; cdecl; +begin + Result := TBufferedStream(Handle).Seek(Offset, LongInt(Mode)); +end; + +function FileTell(Handle: TImagingHandle): Int64; cdecl; +begin + Result := TBufferedStream(Handle).Position; +end; + +function FileRead(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt): LongInt; cdecl; +begin + Result := TBufferedStream(Handle).Read(Buffer^, Count); +end; + +function FileWrite(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt): LongInt; cdecl; +begin + Result := TBufferedStream(Handle).Write(Buffer^, Count); +end; + +{ Stream IO functions } + +function StreamOpen(FileName: PChar; Mode: TOpenMode): TImagingHandle; cdecl; +begin + Result := FileName; +end; + +procedure StreamClose(Handle: TImagingHandle); cdecl; +begin +end; + +function StreamEof(Handle: TImagingHandle): Boolean; cdecl; +begin + Result := TStream(Handle).Position = TStream(Handle).Size; +end; + +function StreamSeek(Handle: TImagingHandle; Offset: Int64; Mode: TSeekMode): Int64; cdecl; +begin + Result := TStream(Handle).Seek(Offset, LongInt(Mode)); +end; + +function StreamTell(Handle: TImagingHandle): Int64; cdecl; +begin + Result := TStream(Handle).Position; +end; + +function StreamRead(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt): + LongInt; cdecl; +begin + Result := TStream(Handle).Read(Buffer^, Count); +end; + +function StreamWrite(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt): LongInt; cdecl; +begin + Result := TStream(Handle).Write(Buffer^, Count); +end; + +{ Memory IO functions } + +function MemoryOpen(FileName: PChar; Mode: TOpenMode): TImagingHandle; cdecl; +begin + Result := FileName; +end; + +procedure MemoryClose(Handle: TImagingHandle); cdecl; +begin +end; + +function MemoryEof(Handle: TImagingHandle): Boolean; cdecl; +begin + Result := PMemoryIORec(Handle).Position = PMemoryIORec(Handle).Size; +end; + +function MemorySeek(Handle: TImagingHandle; Offset: Int64; Mode: TSeekMode): Int64; cdecl; +begin + Result := PMemoryIORec(Handle).Position; + case Mode of + smFromBeginning: Result := Offset; + smFromCurrent: Result := PMemoryIORec(Handle).Position + Offset; + smFromEnd: Result := PMemoryIORec(Handle).Size + Offset; + end; + //Result := ClampInt(Result, 0, PMemoryIORec(Handle).Size); don't do this - some file formats use it + PMemoryIORec(Handle).Position := Result; +end; + +function MemoryTell(Handle: TImagingHandle): Int64; cdecl; +begin + Result := PMemoryIORec(Handle).Position; +end; + +function MemoryRead(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt): + LongInt; cdecl; +var + Rec: PMemoryIORec; +begin + Rec := PMemoryIORec(Handle); + Result := Count; + if Rec.Position + Count > Rec.Size then + Result := Rec.Size - Rec.Position; + Move(Rec.Data[Rec.Position], Buffer^, Result); + Rec.Position := Rec.Position + Result; +end; + +function MemoryWrite(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt): LongInt; cdecl; +var + Rec: PMemoryIORec; +begin + Rec := PMemoryIORec(Handle); + Result := Count; + if Rec.Position + Count > Rec.Size then + Result := Rec.Size - Rec.Position; + Move(Buffer^, Rec.Data[Rec.Position], Result); + Rec.Position := Rec.Position + Result; +end; + +{ Helper IO functions } + +function GetInputSize(const IOFunctions: TIOFunctions; Handle: TImagingHandle): Int64; +var + OldPos: Int64; +begin + OldPos := IOFunctions.Tell(Handle); + IOFunctions.Seek(Handle, 0, smFromEnd); + Result := IOFunctions.Tell(Handle); + IOFunctions.Seek(Handle, OldPos, smFromBeginning); +end; + +function PrepareMemIO(Data: Pointer; Size: LongInt): TMemoryIORec; +begin + Result.Data := Data; + Result.Position := 0; + Result.Size := Size; +end; + +function ReadLine(const IOFunctions: TIOFunctions; Handle: TImagingHandle; + out Line: AnsiString; FailOnControlChars: Boolean): Boolean; +const + MaxLine = 1024; +var + EolPos, Pos: Integer; + C: AnsiChar; + EolReached: Boolean; + Endings: set of AnsiChar; +begin + Line := ''; + Pos := 0; + EolPos := 0; + EolReached := False; + Endings := [#10, #13]; + Result := True; + + while not IOFunctions.Eof(Handle) do + begin + IOFunctions.Read(Handle, @C, SizeOf(C)); + + if FailOnControlChars and (Byte(C) < $20) then + begin + Break; + end; + + if not (C in Endings) then + begin + if EolReached then + begin + IOFunctions.Seek(Handle, EolPos, smFromBeginning); + Exit; + end + else + begin + SetLength(Line, Length(Line) + 1); + Line[Length(Line)] := C; + end; + end + else if not EolReached then + begin + EolReached := True; + EolPos := IOFunctions.Tell(Handle); + end; + + Inc(Pos); + if Pos >= MaxLine then + begin + Break; + end; + end; + + Result := False; + IOFunctions.Seek(Handle, -Pos, smFromCurrent); +end; + +procedure WriteLine(const IOFunctions: TIOFunctions; Handle: TImagingHandle; + const Line: AnsiString; const LineEnding: AnsiString); +var + ToWrite: AnsiString; +begin + ToWrite := Line + LineEnding; + IOFunctions.Write(Handle, @ToWrite[1], Length(ToWrite)); +end; + +{ TReadMemoryStream } + +constructor TReadMemoryStream.Create(Data: Pointer; Size: Integer); +begin + SetPointer(Data, Size); +end; + +class function TReadMemoryStream.CreateFromIOHandle(const IOFunctions: TIOFunctions; Handle: TImagingHandle): TReadMemoryStream; +var + Data: Pointer; + Size: Integer; +begin + Size := GetInputSize(IOFunctions, Handle); + GetMem(Data, Size); + IOFunctions.Read(Handle, Data, Size); + Result := TReadMemoryStream.Create(Data, Size); +end; + +{ TImagingIOStream } + +constructor TImagingIOStream.Create(const IOFunctions: TIOFunctions; + Handle: TImagingHandle); +begin + +end; + +initialization + OriginalFileIO.Open := FileOpen; + OriginalFileIO.Close := FileClose; + OriginalFileIO.Eof := FileEof; + OriginalFileIO.Seek := FileSeek; + OriginalFileIO.Tell := FileTell; + OriginalFileIO.Read := FileRead; + OriginalFileIO.Write := FileWrite; + + StreamIO.Open := StreamOpen; + StreamIO.Close := StreamClose; + StreamIO.Eof := StreamEof; + StreamIO.Seek := StreamSeek; + StreamIO.Tell := StreamTell; + StreamIO.Read := StreamRead; + StreamIO.Write := StreamWrite; + + MemoryIO.Open := MemoryOpen; + MemoryIO.Close := MemoryClose; + MemoryIO.Eof := MemoryEof; + MemoryIO.Seek := MemorySeek; + MemoryIO.Tell := MemoryTell; + MemoryIO.Read := MemoryRead; + MemoryIO.Write := MemoryWrite; + + ResetFileIO; + +{ + File Notes: + + -- TODOS ---------------------------------------------------- + - nothing now + + -- 0.77.3 --------------------------------------------------- + - IO functions now have 64bit sizes and offsets. + - Added helper classes TReadMemoryStream and TImagingIOStream. + + -- 0.77.1 --------------------------------------------------- + - Updated IO Open functions according to changes in ImagingTypes. + - Added ReadLine and WriteLine functions. + + -- 0.23 Changes/Bug Fixes ----------------------------------- + - Added merge between buffered read-only and write-only file + stream adapters - TIFF saving needed both reading and writing. + - Fixed bug causing wrong value of TBufferedWriteFile.Size + (needed to add buffer pos to size). + + -- 0.21 Changes/Bug Fixes ----------------------------------- + - Removed TMemoryIORec.Written, use Position to get proper memory + position (Written didn't take Seeks into account). + - Added TBufferedReadFile and TBufferedWriteFile classes for + buffered file reading/writting. File IO functions now use these + classes resulting in performance increase mainly in file formats + that read/write many small chunks. + - Added fmShareDenyWrite to FileOpenRead. You can now read + files opened for reading by Imaging from other apps. + - Added GetInputSize and PrepareMemIO helper functions. + + -- 0.19 Changes/Bug Fixes ----------------------------------- + - changed behaviour of MemorySeek to act as TStream + based Seeks +} +end. + diff --git a/resources/libraries/deskew/Imaging/ImagingJpeg.pas b/resources/libraries/deskew/Imaging/ImagingJpeg.pas new file mode 100755 index 0000000..94f5371 --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingJpeg.pas @@ -0,0 +1,769 @@ +{ + Vampyre Imaging Library + by Marek Mauder + http://imaginglib.sourceforge.net + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +{ This unit contains image format loader/saver for Jpeg images.} +unit ImagingJpeg; + +{$I ImagingOptions.inc} + +{ You can choose which Pascal JpegLib implementation will be used. + IMJPEGLIB is version bundled with Imaging which works with all supported + compilers and platforms. + PASJPEG is original JpegLib translation or version modified for FPC + (and shipped with it). You can use PASJPEG if this version is already + linked with another part of your program and you don't want to have + two quite large almost the same libraries linked to your exe. + This is the case with Lazarus applications for example.} + +{$DEFINE IMJPEGLIB} +{ $DEFINE PASJPEG} + +{ Automatically use FPC's PasJpeg when compiling with Lazarus. But not when + WINDOWS is defined. See http://galfar.vevb.net/imaging/smf/index.php/topic,90.0.html. + Fixed in FPC revision 13963: http://bugs.freepascal.org/view.php?id=14928 } +{$IF Defined(LCL) and not Defined(WINDOWS)} + {$UNDEF IMJPEGLIB} + {$DEFINE PASJPEG} +{$IFEND} + +{ We usually want to skip the rest of the corrupted file when loading JEPG files + instead of getting exception. JpegLib's error handler can only be + exited using setjmp/longjmp ("non-local goto") functions to get error + recovery when loading corrupted JPEG files. This is implemented in assembler + and currently available only for 32bit Delphi targets and FPC.} +{$DEFINE ErrorJmpRecovery} +{$IF Defined(DCC) and not Defined(CPUX86)} + {$UNDEF ErrorJmpRecovery} +{$IFEND} + +interface + +uses + SysUtils, ImagingTypes, Imaging, ImagingColors, +{$IF Defined(IMJPEGLIB)} + imjpeglib, imjmorecfg, imjcomapi, imjdapimin, imjdeferr, imjerror, + imjdapistd, imjcapimin, imjcapistd, imjdmarker, imjcparam, +{$ELSEIF Defined(PASJPEG)} + jpeglib, jmorecfg, jcomapi, jdapimin, jdeferr, jerror, + jdapistd, jcapimin, jcapistd, jdmarker, jcparam, +{$IFEND} + ImagingUtility; + +{$IF Defined(FPC) and Defined(PASJPEG)} + { When using FPC's pasjpeg in FPC the channel order is BGR instead of RGB} + {$DEFINE RGBSWAPPED} +{$IFEND} + +type + { Class for loading/saving Jpeg images. Supports load/save of + 8 bit grayscale and 24 bit RGB images. Jpegs can be saved with optional + progressive encoding. + Based on IJG's JpegLib so doesn't support alpha channels and lossless + coding.} + TJpegFileFormat = class(TImageFileFormat) + private + FGrayScale: Boolean; + protected + FQuality: LongInt; + FProgressive: LongBool; + procedure SetJpegIO(const JpegIO: TIOFunctions); virtual; + procedure Define; override; + function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; + OnlyFirstLevel: Boolean): Boolean; override; + function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; + Index: LongInt): Boolean; override; + procedure ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); override; + public + function TestFormat(Handle: TImagingHandle): Boolean; override; + procedure CheckOptionsValidity; override; + published + { Controls Jpeg save compression quality. It is number in range 1..100. + 1 means small/ugly file, 100 means large/nice file. Accessible trough + ImagingJpegQuality option.} + property Quality: LongInt read FQuality write FQuality; + { If True Jpeg images are saved in progressive format. Accessible trough + ImagingJpegProgressive option.} + property Progressive: LongBool read FProgressive write FProgressive; + end; + +implementation + +const + SJpegFormatName = 'Joint Photographic Experts Group Image'; + SJpegMasks = '*.jpg,*.jpeg,*.jfif,*.jpe,*.jif'; + JpegSupportedFormats: TImageFormats = [ifR8G8B8, ifGray8]; + JpegDefaultQuality = 90; + JpegDefaultProgressive = False; + +const + { Jpeg file identifiers.} + JpegMagic: TChar2 = #$FF#$D8; + BufferSize = 16384; + +resourcestring + SJpegError = 'JPEG Error'; + +type + TJpegContext = record + case Byte of + 0: (common: jpeg_common_struct); + 1: (d: jpeg_decompress_struct); + 2: (c: jpeg_compress_struct); + end; + + TSourceMgr = record + Pub: jpeg_source_mgr; + Input: TImagingHandle; + Buffer: JOCTETPTR; + StartOfFile: Boolean; + end; + PSourceMgr = ^TSourceMgr; + + TDestMgr = record + Pub: jpeg_destination_mgr; + Output: TImagingHandle; + Buffer: JOCTETPTR; + end; + PDestMgr = ^TDestMgr; + +var + JIO: TIOFunctions; + JpegErrorMgr: jpeg_error_mgr; + +{ Intenal unit jpeglib support functions } + +{$IFDEF ErrorJmpRecovery} + {$IFDEF DCC} + type + jmp_buf = record + EBX, + ESI, + EDI, + ESP, + EBP, + EIP: LongWord; + end; + pjmp_buf = ^jmp_buf; + + { JmpLib SetJmp/LongJmp Library + (C)Copyright 2003, 2004 Will DeWitt Jr. <edge@boink.net> } + function SetJmp(out jmpb: jmp_buf): Integer; + asm + { -> EAX jmpb } + { <- EAX Result } + MOV EDX, [ESP] // Fetch return address (EIP) + // Save task state + MOV [EAX+jmp_buf.&EBX], EBX + MOV [EAX+jmp_buf.&ESI], ESI + MOV [EAX+jmp_buf.&EDI], EDI + MOV [EAX+jmp_buf.&ESP], ESP + MOV [EAX+jmp_buf.&EBP], EBP + MOV [EAX+jmp_buf.&EIP], EDX + + SUB EAX, EAX + @@1: + end; + + procedure LongJmp(const jmpb: jmp_buf; retval: Integer); + asm + { -> EAX jmpb } + { EDX retval } + { <- EAX Result } + XCHG EDX, EAX + + MOV ECX, [EDX+jmp_buf.&EIP] + // Restore task state + MOV EBX, [EDX+jmp_buf.&EBX] + MOV ESI, [EDX+jmp_buf.&ESI] + MOV EDI, [EDX+jmp_buf.&EDI] + MOV ESP, [EDX+jmp_buf.&ESP] + MOV EBP, [EDX+jmp_buf.&EBP] + MOV [ESP], ECX // Restore return address (EIP) + + TEST EAX, EAX // Ensure retval is <> 0 + JNZ @@1 + MOV EAX, 1 + @@1: + end; + {$ENDIF} + +type + TJmpBuf = jmp_buf; + TErrorClientData = record + JmpBuf: TJmpBuf; + ScanlineReadReached: Boolean; + end; + PErrorClientData = ^TErrorClientData; +{$ENDIF} + +procedure JpegError(CInfo: j_common_ptr); + + procedure RaiseError; + var + Buffer: AnsiString; + begin + // Create the message and raise exception + CInfo.err.format_message(CInfo, Buffer); + // Warning: you can get "Invalid argument index in format" exception when + // using FPC (see http://bugs.freepascal.org/view.php?id=21229). + // Fixed in FPC 2.7.1 + {$IF Defined(FPC) and (FPC_FULLVERSION <= 20701)} + raise EImagingError.CreateFmt(SJPEGError + ' %d', [CInfo.err.msg_code]); + {$ELSE} + raise EImagingError.CreateFmt(SJPEGError + ' %d: ' + string(Buffer), [CInfo.err.msg_code]); + {$IFEND} + end; + +begin +{$IFDEF ErrorJmpRecovery} + // Only recovers on loads and when header is sucessfully loaded + // (error occurs when reading scanlines) + if (CInfo.client_data <> nil) and + PErrorClientData(CInfo.client_data).ScanlineReadReached then + begin + // Non-local jump to error handler in TJpegFileFormat.LoadData + longjmp(PErrorClientData(CInfo.client_data).JmpBuf, 1) + end + else + RaiseError; +{$ELSE} + RaiseError; +{$ENDIF} +end; + +procedure OutputMessage(CurInfo: j_common_ptr); +begin +end; + +procedure ReleaseContext(var jc: TJpegContext); +begin + if jc.common.err = nil then + Exit; + jpeg_destroy(@jc.common); + jpeg_destroy_decompress(@jc.d); + jpeg_destroy_compress(@jc.c); + jc.common.err := nil; +end; + +procedure InitSource(cinfo: j_decompress_ptr); +begin + PSourceMgr(cinfo.src).StartOfFile := True; +end; + +function FillInputBuffer(cinfo: j_decompress_ptr): Boolean; +var + NBytes: LongInt; + Src: PSourceMgr; +begin + Src := PSourceMgr(cinfo.src); + NBytes := JIO.Read(Src.Input, Src.Buffer, BufferSize); + + if NBytes <= 0 then + begin + PByteArray(Src.Buffer)[0] := $FF; + PByteArray(Src.Buffer)[1] := JPEG_EOI; + NBytes := 2; + end; + Src.Pub.next_input_byte := Src.Buffer; + Src.Pub.bytes_in_buffer := NBytes; + Src.StartOfFile := False; + Result := True; +end; + +procedure SkipInputData(cinfo: j_decompress_ptr; num_bytes: LongInt); +var + Src: PSourceMgr; +begin + Src := PSourceMgr(cinfo.src); + if num_bytes > 0 then + begin + while num_bytes > Src.Pub.bytes_in_buffer do + begin + Dec(num_bytes, Src.Pub.bytes_in_buffer); + FillInputBuffer(cinfo); + end; + Src.Pub.next_input_byte := @PByteArray(Src.Pub.next_input_byte)[num_bytes]; + //Inc(LongInt(Src.Pub.next_input_byte), num_bytes); + Dec(Src.Pub.bytes_in_buffer, num_bytes); + end; +end; + +procedure TermSource(cinfo: j_decompress_ptr); +var + Src: PSourceMgr; +begin + Src := PSourceMgr(cinfo.src); + // Move stream position back just after EOI marker so that more that one + // JPEG images can be loaded from one stream + JIO.Seek(Src.Input, -Src.Pub.bytes_in_buffer, smFromCurrent); +end; + +procedure JpegStdioSrc(var cinfo: jpeg_decompress_struct; Handle: + TImagingHandle); +var + Src: PSourceMgr; +begin + if cinfo.src = nil then + begin + cinfo.src := cinfo.mem.alloc_small(j_common_ptr(@cinfo), JPOOL_PERMANENT, + SizeOf(TSourceMgr)); + Src := PSourceMgr(cinfo.src); + Src.Buffer := cinfo.mem.alloc_small(j_common_ptr(@cinfo), JPOOL_PERMANENT, + BufferSize * SizeOf(JOCTET)); + end; + Src := PSourceMgr(cinfo.src); + Src.Pub.init_source := InitSource; + Src.Pub.fill_input_buffer := FillInputBuffer; + Src.Pub.skip_input_data := SkipInputData; + Src.Pub.resync_to_restart := jpeg_resync_to_restart; + Src.Pub.term_source := TermSource; + Src.Input := Handle; + Src.Pub.bytes_in_buffer := 0; + Src.Pub.next_input_byte := nil; +end; + +procedure InitDest(cinfo: j_compress_ptr); +var + Dest: PDestMgr; +begin + Dest := PDestMgr(cinfo.dest); + Dest.Pub.next_output_byte := Dest.Buffer; + Dest.Pub.free_in_buffer := BufferSize; +end; + +function EmptyOutput(cinfo: j_compress_ptr): Boolean; +var + Dest: PDestMgr; +begin + Dest := PDestMgr(cinfo.dest); + JIO.Write(Dest.Output, Dest.Buffer, BufferSize); + Dest.Pub.next_output_byte := Dest.Buffer; + Dest.Pub.free_in_buffer := BufferSize; + Result := True; +end; + +procedure TermDest(cinfo: j_compress_ptr); +var + Dest: PDestMgr; + DataCount: LongInt; +begin + Dest := PDestMgr(cinfo.dest); + DataCount := BufferSize - Dest.Pub.free_in_buffer; + if DataCount > 0 then + JIO.Write(Dest.Output, Dest.Buffer, DataCount); +end; + +procedure JpegStdioDest(var cinfo: jpeg_compress_struct; Handle: + TImagingHandle); +var + Dest: PDestMgr; +begin + if cinfo.dest = nil then + cinfo.dest := cinfo.mem.alloc_small(j_common_ptr(@cinfo), + JPOOL_PERMANENT, SizeOf(TDestMgr)); + Dest := PDestMgr(cinfo.dest); + Dest.Buffer := cinfo.mem.alloc_small(j_common_ptr(@cinfo), JPOOL_IMAGE, + BufferSize * SIZEOF(JOCTET)); + Dest.Pub.init_destination := InitDest; + Dest.Pub.empty_output_buffer := EmptyOutput; + Dest.Pub.term_destination := TermDest; + Dest.Output := Handle; +end; + +procedure SetupErrorMgr(var jc: TJpegContext); +begin + // Set standard error handlers and then override some + jc.common.err := jpeg_std_error(JpegErrorMgr); + jc.common.err.error_exit := JpegError; + jc.common.err.output_message := OutputMessage; +end; + +procedure InitDecompressor(Handle: TImagingHandle; var jc: TJpegContext); +begin + jpeg_CreateDecompress(@jc.d, JPEG_LIB_VERSION, sizeof(jc.d)); + JpegStdioSrc(jc.d, Handle); + jpeg_read_header(@jc.d, True); + jc.d.scale_num := 1; + jc.d.scale_denom := 1; + jc.d.do_block_smoothing := True; + if jc.d.out_color_space = JCS_GRAYSCALE then + begin + jc.d.quantize_colors := True; + jc.d.desired_number_of_colors := 256; + end; +end; + +procedure InitCompressor(Handle: TImagingHandle; var jc: TJpegContext; + Saver: TJpegFileFormat); +begin + jpeg_CreateCompress(@jc.c, JPEG_LIB_VERSION, sizeof(jc.c)); + JpegStdioDest(jc.c, Handle); + if Saver.FGrayScale then + jc.c.in_color_space := JCS_GRAYSCALE + else + jc.c.in_color_space := JCS_RGB; + jpeg_set_defaults(@jc.c); + jpeg_set_quality(@jc.c, Saver.FQuality, True); + if Saver.FProgressive then + jpeg_simple_progression(@jc.c); +end; + +{ TJpegFileFormat class implementation } + +procedure TJpegFileFormat.Define; +begin + FName := SJpegFormatName; + FFeatures := [ffLoad, ffSave]; + FSupportedFormats := JpegSupportedFormats; + + FQuality := JpegDefaultQuality; + FProgressive := JpegDefaultProgressive; + + AddMasks(SJpegMasks); + RegisterOption(ImagingJpegQuality, @FQuality); + RegisterOption(ImagingJpegProgressive, @FProgressive); +end; + +procedure TJpegFileFormat.CheckOptionsValidity; +begin + // Check if option values are valid + if not (FQuality in [1..100]) then + FQuality := JpegDefaultQuality; +end; + +function TJpegFileFormat.LoadData(Handle: TImagingHandle; + var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; +var + PtrInc, LinesPerCall, LinesRead, I: Integer; + Dest: PByte; + jc: TJpegContext; + Info: TImageFormatInfo; + Col32: PColor32Rec; + NeedsRedBlueSwap: Boolean; + Pix: PColor24Rec; +{$IFDEF ErrorJmpRecovery} + ErrorClient: TErrorClientData; +{$ENDIF} + + procedure LoadMetaData; + var + XDensity, YDensity: Single; + ResUnit: TResolutionUnit; + begin + // Density unit: 0 - undef, 1 - inch, 2 - cm + if jc.d.saw_JFIF_marker and (jc.d.density_unit > 0) and + (jc.d.X_density > 0) and (jc.d.Y_density > 0) then + begin + XDensity := jc.d.X_density; + YDensity := jc.d.Y_density; + ResUnit := ruDpi; + if jc.d.density_unit = 2 then + ResUnit := ruDpcm; + FMetadata.SetPhysicalPixelSize(ResUnit, XDensity, YDensity); + end; + end; + +begin + // Copy IO functions to global var used in JpegLib callbacks + Result := False; + SetJpegIO(GetIO); + SetLength(Images, 1); + + with JIO, Images[0] do + try + ZeroMemory(@jc, SizeOf(jc)); + SetupErrorMgr(jc); + {$IFDEF ErrorJmpRecovery} + ZeroMemory(@ErrorClient, SizeOf(ErrorClient)); + jc.common.client_data := @ErrorClient; + if setjmp(ErrorClient.JmpBuf) <> 0 then + begin + Result := True; + Exit; + end; + {$ENDIF} + InitDecompressor(Handle, jc); + + case jc.d.out_color_space of + JCS_GRAYSCALE: Format := ifGray8; + JCS_RGB: Format := ifR8G8B8; + JCS_CMYK: Format := ifA8R8G8B8; + else + Exit; + end; + + NewImage(jc.d.image_width, jc.d.image_height, Format, Images[0]); + jpeg_start_decompress(@jc.d); + GetImageFormatInfo(Format, Info); + PtrInc := Width * Info.BytesPerPixel; + LinesPerCall := 1; + Dest := Bits; + + // If Jpeg's colorspace is RGB and not YCbCr we need to swap + // R and B to get Imaging's native order + NeedsRedBlueSwap := jc.d.jpeg_color_space = JCS_RGB; + {$IFDEF RGBSWAPPED} + // Force R-B swap for FPC's PasJpeg + NeedsRedBlueSwap := True; + {$ENDIF} + + {$IFDEF ErrorJmpRecovery} + ErrorClient.ScanlineReadReached := True; + {$ENDIF} + + while jc.d.output_scanline < jc.d.output_height do + begin + LinesRead := jpeg_read_scanlines(@jc.d, @Dest, LinesPerCall); + if NeedsRedBlueSwap and (Format = ifR8G8B8) then + begin + Pix := PColor24Rec(Dest); + for I := 0 to Width - 1 do + begin + SwapValues(Pix.R, Pix.B); + Inc(Pix); + end; + end; + Inc(Dest, PtrInc * LinesRead); + end; + + if jc.d.out_color_space = JCS_CMYK then + begin + Col32 := Bits; + // Translate from CMYK to RGB + for I := 0 to Width * Height - 1 do + begin + CMYKToRGB(255 - Col32.B, 255 - Col32.G, 255 - Col32.R, 255 - Col32.A, + Col32.R, Col32.G, Col32.B); + Col32.A := 255; + Inc(Col32); + end; + end; + + // Store supported metadata + LoadMetaData; + + jpeg_finish_output(@jc.d); + jpeg_finish_decompress(@jc.d); + Result := True; + finally + ReleaseContext(jc); + end; +end; + +function TJpegFileFormat.SaveData(Handle: TImagingHandle; + const Images: TDynImageDataArray; Index: LongInt): Boolean; +var + PtrInc, LinesWritten: LongInt; + Src, Line: PByte; + jc: TJpegContext; + ImageToSave: TImageData; + Info: TImageFormatInfo; + MustBeFreed: Boolean; +{$IFDEF RGBSWAPPED} + I: LongInt; + Pix: PColor24Rec; +{$ENDIF} + + procedure SaveMetaData; + var + XRes, YRes: Single; + begin + if FMetadata.GetPhysicalPixelSize(ruDpcm, XRes, YRes, True) then + begin + jc.c.density_unit := 2; // Dots per cm + jc.c.X_density := Round(XRes); + jc.c.Y_density := Round(YRes) + end; + end; + +begin + Result := False; + // Copy IO functions to global var used in JpegLib callbacks + SetJpegIO(GetIO); + + // Makes image to save compatible with Jpeg saving capabilities + if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then + with JIO, ImageToSave do + try + ZeroMemory(@jc, SizeOf(jc)); + SetupErrorMgr(jc); + + GetImageFormatInfo(Format, Info); + FGrayScale := Format = ifGray8; + InitCompressor(Handle, jc, Self); + jc.c.image_width := Width; + jc.c.image_height := Height; + if FGrayScale then + begin + jc.c.input_components := 1; + jc.c.in_color_space := JCS_GRAYSCALE; + end + else + begin + jc.c.input_components := 3; + jc.c.in_color_space := JCS_RGB; + end; + + PtrInc := Width * Info.BytesPerPixel; + Src := Bits; + + {$IFDEF RGBSWAPPED} + GetMem(Line, PtrInc); + {$ENDIF} + + // Save supported metadata + SaveMetaData; + + jpeg_start_compress(@jc.c, True); + while (jc.c.next_scanline < jc.c.image_height) do + begin + {$IFDEF RGBSWAPPED} + if Format = ifR8G8B8 then + begin + Move(Src^, Line^, PtrInc); + Pix := PColor24Rec(Line); + for I := 0 to Width - 1 do + begin + SwapValues(Pix.R, Pix.B); + Inc(Pix, 1); + end; + end; + {$ELSE} + Line := Src; + {$ENDIF} + + LinesWritten := jpeg_write_scanlines(@jc.c, @Line, 1); + Inc(Src, PtrInc * LinesWritten); + end; + + jpeg_finish_compress(@jc.c); + Result := True; + finally + ReleaseContext(jc); + if MustBeFreed then + FreeImage(ImageToSave); + {$IFDEF RGBSWAPPED} + FreeMem(Line); + {$ENDIF} + end; +end; + +procedure TJpegFileFormat.ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); +begin + if Info.HasGrayChannel then + ConvertImage(Image, ifGray8) + else + ConvertImage(Image, ifR8G8B8); +end; + +function TJpegFileFormat.TestFormat(Handle: TImagingHandle): Boolean; +var + ReadCount: LongInt; + ID: array[0..9] of AnsiChar; +begin + Result := False; + if Handle <> nil then + with GetIO do + begin + FillChar(ID, SizeOf(ID), 0); + ReadCount := Read(Handle, @ID, SizeOf(ID)); + Seek(Handle, -ReadCount, smFromCurrent); + Result := (ReadCount = SizeOf(ID)) and + CompareMem(@ID, @JpegMagic, SizeOf(JpegMagic)); + end; +end; + +procedure TJpegFileFormat.SetJpegIO(const JpegIO: TIOFunctions); +begin + JIO := JpegIO; +end; + +initialization + RegisterImageFileFormat(TJpegFileFormat); + +{ + File Notes: + + -- TODOS ---------------------------------------------------- + - nothing now + + -- 0.77.1 --------------------------------------------------- + - Able to read corrupted JPEG files - loads partial image + and skips the corrupted parts (FPC and x86 Delphi). + - Fixed reading of physical resolution metadata, could cause + "divided by zero" later on for some files. + + -- 0.26.5 Changes/Bug Fixes --------------------------------- + - Fixed loading of some JPEGs with certain APPN markers (bug in JpegLib). + - Fixed swapped Red-Blue order when loading Jpegs with + jc.d.jpeg_color_space = JCS_RGB. + - Added loading and saving of physical pixel size metadata. + + -- 0.26.3 Changes/Bug Fixes --------------------------------- + - Changed the Jpeg error manager, messages were not properly formated. + + -- 0.26.1 Changes/Bug Fixes --------------------------------- + - Fixed wrong color space setting in InitCompressor. + - Fixed problem with progressive Jpegs in FPC (modified JpegLib, + can't use FPC's PasJpeg in Windows). + + -- 0.25.0 Changes/Bug Fixes --------------------------------- + - FPC's PasJpeg wasn't really used in last version, fixed. + + -- 0.24.1 Changes/Bug Fixes --------------------------------- + - Fixed loading of CMYK jpeg images. Could cause heap corruption + and loaded image looked wrong. + + -- 0.23 Changes/Bug Fixes ----------------------------------- + - Removed JFIF/EXIF detection from TestFormat. Found JPEGs + with different headers (Lavc) which weren't recognized. + + -- 0.21 Changes/Bug Fixes ----------------------------------- + - MakeCompatible method moved to base class, put ConvertToSupported here. + GetSupportedFormats removed, it is now set in constructor. + - Made public properties for options registered to SetOption/GetOption + functions. + - Changed extensions to filename masks. + - Changed SaveData, LoadData, and MakeCompatible methods according + to changes in base class in Imaging unit. + - Changes in TestFormat, now reads JFIF and EXIF signatures too. + + -- 0.19 Changes/Bug Fixes ----------------------------------- + - input position is now set correctly to the end of the image + after loading is done. Loading of sequence of JPEG files stored in + single stream works now + - when loading and saving images in FPC with PASJPEG read and + blue channels are swapped to have the same chanel order as IMJPEGLIB + - you can now choose between IMJPEGLIB and PASJPEG implementations + + -- 0.17 Changes/Bug Fixes ----------------------------------- + - added SetJpegIO method which is used by JNG image format +} +end. + diff --git a/resources/libraries/deskew/Imaging/ImagingNetworkGraphics.pas b/resources/libraries/deskew/Imaging/ImagingNetworkGraphics.pas new file mode 100755 index 0000000..95ffdfa --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingNetworkGraphics.pas @@ -0,0 +1,2714 @@ +{ + Vampyre Imaging Library + by Marek Mauder + http://imaginglib.sourceforge.net + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +{ This unit contains image format loaders/savers for Network Graphics image + file formats PNG, MNG, and JNG.} +unit ImagingNetworkGraphics; + +interface + +{$I ImagingOptions.inc} + +{ If MNG support is enabled we must make sure PNG and JNG are enabled too.} +{$IFNDEF DONT_LINK_MNG} + {$UNDEF DONT_LINK_PNG} + {$UNDEF DONT_LINK_JNG} +{$ENDIF} + +uses + Types, SysUtils, Classes, ImagingTypes, Imaging, ImagingUtility, ImagingFormats, dzlib; + +type + { Basic class for Network Graphics file formats loaders/savers.} + TNetworkGraphicsFileFormat = class(TImageFileFormat) + protected + FSignature: TChar8; + FPreFilter: LongInt; + FCompressLevel: LongInt; + FLossyCompression: LongBool; + FLossyAlpha: LongBool; + FQuality: LongInt; + FProgressive: LongBool; + FZLibStategy: Integer; + function GetSupportedFormats: TImageFormats; override; + procedure ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); override; + procedure Define; override; + public + function TestFormat(Handle: TImagingHandle): Boolean; override; + procedure CheckOptionsValidity; override; + published + { Sets precompression filter used when saving images with lossless compression. + Allowed values are: 0 (none), 1 (sub), 2 (up), 3 (average), 4 (paeth), + 5 (use 0 for indexed/gray images and 4 for RGB/ARGB images), + 6 (adaptive filtering - use best filter for each scanline - very slow). + Note that filters 3 and 4 are much slower than filters 1 and 2. + Default value is 5.} + property PreFilter: LongInt read FPreFilter write FPreFilter; + { Sets ZLib compression level used when saving images with lossless compression. + Allowed values are in range 0 (no compresstion) to 9 (best compression). + Default value is 5.} + property CompressLevel: LongInt read FCompressLevel write FCompressLevel; + { Specifies whether MNG animation frames are saved with lossy or lossless + compression. Lossless frames are saved as PNG images and lossy frames are + saved as JNG images. Allowed values are 0 (False) and 1 (True). + Default value is 0.} + property LossyCompression: LongBool read FLossyCompression write FLossyCompression; + { Defines whether alpha channel of lossy MNG frames or JNG images + is lossy compressed too. Allowed values are 0 (False) and 1 (True). + Default value is 0.} + property LossyAlpha: LongBool read FLossyAlpha write FLossyAlpha; + { Specifies compression quality used when saving lossy MNG frames or JNG images. + For details look at ImagingJpegQuality option.} + property Quality: LongInt read FQuality write FQuality; + { Specifies whether images are saved in progressive format when saving lossy + MNG frames or JNG images. For details look at ImagingJpegProgressive.} + property Progressive: LongBool read FProgressive write FProgressive; + end; + + { Class for loading Portable Network Graphics Images. + Loads all types of this image format (all images in png test suite) + and saves all types with bitcount >= 8 (non-interlaced only). + Compression level and filtering can be set by options interface. + + Supported ancillary chunks (loading): + tRNS, bKGD + (for indexed images transparency contains alpha values for palette, + RGB/Gray images with transparency are converted to formats with alpha + and pixels with transparent color are replaced with background color + with alpha = 0).} + TPNGFileFormat = class(TNetworkGraphicsFileFormat) + private + FLoadAnimated: LongBool; + protected + procedure Define; override; + function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; + OnlyFirstLevel: Boolean): Boolean; override; + function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; + Index: LongInt): Boolean; override; + published + property LoadAnimated: LongBool read FLoadAnimated write FLoadAnimated; + end; + +{$IFNDEF DONT_LINK_MNG} + { Class for loading Multiple Network Graphics files. + This format has complex animation capabilities but Imaging only + extracts frames. Individual frames are stored as standard PNG or JNG + images. Loads all types of these frames stored in IHDR-IEND and + JHDR-IEND streams (Note that there are MNG chunks + like BASI which define images but does not contain image data itself, + those are ignored). + Imaging saves MNG files as MNG-VLC (very low complexity) so it is basicaly + an array of image frames without MNG animation chunks. Frames can be saved + as lossless PNG or lossy JNG images (look at TPNGFileFormat and + TJNGFileFormat for info). Every frame can be in different data format. + + Many frame compression settings can be modified by options interface.} + TMNGFileFormat = class(TNetworkGraphicsFileFormat) + protected + procedure Define; override; + function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; + OnlyFirstLevel: Boolean): Boolean; override; + function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; + Index: LongInt): Boolean; override; + end; +{$ENDIF} + +{$IFNDEF DONT_LINK_JNG} + { Class for loading JPEG Network Graphics Images. + Loads all types of this image format (all images in jng test suite) + and saves all types except 12 bit JPEGs. + Alpha channel in JNG images is stored separately from color/gray data and + can be lossy (as JPEG image) or lossless (as PNG image) compressed. + Type of alpha compression, compression level and quality, + and filtering can be set by options interface. + + Supported ancillary chunks (loading): + tRNS, bKGD + (Images with transparency are converted to formats with alpha + and pixels with transparent color are replaced with background color + with alpha = 0).} + TJNGFileFormat = class(TNetworkGraphicsFileFormat) + protected + procedure Define; override; + function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; + OnlyFirstLevel: Boolean): Boolean; override; + function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; + Index: LongInt): Boolean; override; + end; +{$ENDIF} + + +implementation + +uses +{$IFNDEF DONT_LINK_JNG} + ImagingJpeg, ImagingIO, +{$ENDIF} + ImagingCanvases; + +const + NGDefaultPreFilter = 5; + NGDefaultCompressLevel = 5; + NGDefaultLossyAlpha = False; + NGDefaultLossyCompression = False; + NGDefaultProgressive = False; + NGDefaultQuality = 90; + NGLosslessFormats: TImageFormats = [ifIndex8, ifGray8, ifA8Gray8, ifGray16, + ifA16Gray16, ifR8G8B8, ifA8R8G8B8, ifR16G16B16, ifA16R16G16B16, ifB16G16R16, + ifA16B16G16R16, ifBinary]; + NGLossyFormats: TImageFormats = [ifGray8, ifA8Gray8, ifR8G8B8, ifA8R8G8B8]; + PNGDefaultLoadAnimated = True; + NGDefaultZLibStartegy = 1; // Z_FILTERED + + SPNGFormatName = 'Portable Network Graphics'; + SPNGMasks = '*.png'; + SMNGFormatName = 'Multiple Network Graphics'; + SMNGMasks = '*.mng'; + SJNGFormatName = 'JPEG Network Graphics'; + SJNGMasks = '*.jng'; + +resourcestring + SErrorLoadingChunk = 'Error when reading %s chunk data. File may be corrupted.'; + +type + { Chunk header.} + TChunkHeader = packed record + DataSize: LongWord; + ChunkID: TChar4; + end; + + { IHDR chunk format - PNG header.} + TIHDR = packed record + Width: LongWord; // Image width + Height: LongWord; // Image height + BitDepth: Byte; // Bits per pixel or bits per sample (for truecolor) + ColorType: Byte; // 0 = grayscale, 2 = truecolor, 3 = palette, + // 4 = gray + alpha, 6 = truecolor + alpha + Compression: Byte; // Compression type: 0 = ZLib + Filter: Byte; // Used precompress filter + Interlacing: Byte; // Used interlacing: 0 = no int, 1 = Adam7 + end; + PIHDR = ^TIHDR; + + { MHDR chunk format - MNG header.} + TMHDR = packed record + FrameWidth: LongWord; // Frame width + FrameHeight: LongWord; // Frame height + TicksPerSecond: LongWord; // FPS of animation + NominalLayerCount: LongWord; // Number of layers in file + NominalFrameCount: LongWord; // Number of frames in file + NominalPlayTime: LongWord; // Play time of animation in ticks + SimplicityProfile: LongWord; // Defines which MNG features are used in this file + end; + PMHDR = ^TMHDR; + + { JHDR chunk format - JNG header.} + TJHDR = packed record + Width: LongWord; // Image width + Height: LongWord; // Image height + ColorType: Byte; // 8 = grayscale (Y), 10 = color (YCbCr), + // 12 = gray + alpha (Y-alpha), 14 = color + alpha (YCbCr-alpha) + SampleDepth: Byte; // 8, 12 or 20 (8 and 12 samples together) bit + Compression: Byte; // Compression type: 8 = Huffman coding + Interlacing: Byte; // 0 = single scan, 8 = progressive + AlphaSampleDepth: Byte; // 0, 1, 2, 4, 8, 16 if alpha compression is 0 (PNG) + // 8 if alpha compression is 8 (JNG) + AlphaCompression: Byte; // 0 = PNG graysscale IDAT, 8 = grayscale 8-bit JPEG + AlphaFilter: Byte; // 0 = PNG filter or no filter (JPEG) + AlphaInterlacing: Byte; // 0 = non interlaced + end; + PJHDR = ^TJHDR; + + { acTL chunk format - APNG animation control.} + TacTL = packed record + NumFrames: LongWord; // Number of frames + NumPlay: LongWord; // Number of times to loop the animation (0 = inf) + end; + PacTL =^TacTL; + + { fcTL chunk format - APNG frame control.} + TfcTL = packed record + SeqNumber: LongWord; // Sequence number of the animation chunk, starting from 0 + Width: LongWord; // Width of the following frame + Height: LongWord; // Height of the following frame + XOffset: LongWord; // X position at which to render the following frame + YOffset: LongWord; // Y position at which to render the following frame + DelayNumer: Word; // Frame delay fraction numerator + DelayDenom: Word; // Frame delay fraction denominator + DisposeOp: Byte; // Type of frame area disposal to be done after rendering this frame + BlendOp: Byte; // Type of frame area rendering for this frame + end; + PfcTL = ^TfcTL; + + { pHYs chunk format - encodes the absolute or relative dimensions of pixels.} + TpHYs = packed record + PixelsPerUnitX: LongWord; + PixelsPerUnitY: LongWord; + UnitSpecifier: Byte; + end; + PpHYs = ^TpHYs; + +const + { PNG file identifier.} + PNGSignature: TChar8 = #$89'PNG'#$0D#$0A#$1A#$0A; + { MNG file identifier.} + MNGSignature: TChar8 = #$8A'MNG'#$0D#$0A#$1A#$0A; + { JNG file identifier.} + JNGSignature: TChar8 = #$8B'JNG'#$0D#$0A#$1A#$0A; + + { Constants for chunk identifiers and signature identifiers. + They are in big-endian format.} + IHDRChunk: TChar4 = 'IHDR'; + IENDChunk: TChar4 = 'IEND'; + MHDRChunk: TChar4 = 'MHDR'; + MENDChunk: TChar4 = 'MEND'; + JHDRChunk: TChar4 = 'JHDR'; + IDATChunk: TChar4 = 'IDAT'; + JDATChunk: TChar4 = 'JDAT'; + JDAAChunk: TChar4 = 'JDAA'; + JSEPChunk: TChar4 = 'JSEP'; + PLTEChunk: TChar4 = 'PLTE'; + BACKChunk: TChar4 = 'BACK'; + DEFIChunk: TChar4 = 'DEFI'; + TERMChunk: TChar4 = 'TERM'; + tRNSChunk: TChar4 = 'tRNS'; + bKGDChunk: TChar4 = 'bKGD'; + gAMAChunk: TChar4 = 'gAMA'; + acTLChunk: TChar4 = 'acTL'; + fcTLChunk: TChar4 = 'fcTL'; + fdATChunk: TChar4 = 'fdAT'; + pHYsChunk: TChar4 = 'pHYs'; + + { APNG frame dispose operations.} + DisposeOpNone = 0; + DisposeOpBackground = 1; + DisposeOpPrevious = 2; + + { APNG frame blending modes} + BlendOpSource = 0; + BlendOpOver = 1; + + { Interlace start and offsets.} + RowStart: array[0..6] of LongInt = (0, 0, 4, 0, 2, 0, 1); + ColumnStart: array[0..6] of LongInt = (0, 4, 0, 2, 0, 1, 0); + RowIncrement: array[0..6] of LongInt = (8, 8, 8, 4, 4, 2, 2); + ColumnIncrement: array[0..6] of LongInt = (8, 8, 4, 4, 2, 2, 1); + +type + { Helper class that holds information about MNG frame in PNG or JNG format.} + TFrameInfo = class + public + Index: Integer; + FrameWidth, FrameHeight: LongInt; + IsJpegFrame: Boolean; + IHDR: TIHDR; + JHDR: TJHDR; + fcTL: TfcTL; + pHYs: TpHYs; + Palette: PPalette24; + PaletteEntries: LongInt; + Transparency: Pointer; + TransparencySize: LongInt; + Background: Pointer; + BackgroundSize: LongInt; + IDATMemory: TMemoryStream; + JDATMemory: TMemoryStream; + JDAAMemory: TMemoryStream; + constructor Create(AIndex: Integer); + destructor Destroy; override; + procedure AssignSharedProps(Source: TFrameInfo); + end; + + { Defines type of Network Graphics file.} + TNGFileType = (ngPNG, ngAPNG, ngMNG, ngJNG); + + TNGFileHandler = class + public + FileFormat: TNetworkGraphicsFileFormat; + FileType: TNGFileType; + Frames: array of TFrameInfo; + MHDR: TMHDR; // Main header for MNG files + acTL: TacTL; // Global anim control for APNG files + GlobalPalette: PPalette24; + GlobalPaletteEntries: LongInt; + GlobalTransparency: Pointer; + GlobalTransparencySize: LongInt; + constructor Create(AFileFormat: TNetworkGraphicsFileFormat); + destructor Destroy; override; + procedure Clear; + function GetLastFrame: TFrameInfo; + function AddFrameInfo: TFrameInfo; + procedure LoadMetaData; + end; + + { Network Graphics file parser and frame converter.} + TNGFileLoader = class(TNGFileHandler) + public + function LoadFile(Handle: TImagingHandle): Boolean; + procedure LoadImageFromPNGFrame(FrameWidth, FrameHeight: LongInt; const IHDR: TIHDR; IDATStream: TMemoryStream; var Image: TImageData); +{$IFNDEF DONT_LINK_JNG} + procedure LoadImageFromJNGFrame(FrameWidth, FrameHeight: LongInt; const JHDR: TJHDR; IDATStream, JDATStream, JDAAStream: TMemoryStream; var Image: TImageData); +{$ENDIF} + procedure ApplyFrameSettings(Frame: TFrameInfo; var Image: TImageData); + end; + + TNGFileSaver = class(TNGFileHandler) + public + PreFilter: LongInt; + CompressLevel: LongInt; + LossyAlpha: Boolean; + Quality: LongInt; + Progressive: Boolean; + ZLibStrategy: Integer; + function SaveFile(Handle: TImagingHandle): Boolean; + procedure AddFrame(const Image: TImageData; IsJpegFrame: Boolean); + procedure StoreImageToPNGFrame(const IHDR: TIHDR; Bits: Pointer; FmtInfo: TImageFormatInfo; IDATStream: TMemoryStream); +{$IFNDEF DONT_LINK_JNG} + procedure StoreImageToJNGFrame(const JHDR: TJHDR; const Image: TImageData; IDATStream, JDATStream, JDAAStream: TMemoryStream); +{$ENDIF} + procedure SetFileOptions; + end; + +{$IFNDEF DONT_LINK_JNG} + TCustomIOJpegFileFormat = class(TJpegFileFormat) + protected + FCustomIO: TIOFunctions; + procedure SetJpegIO(const JpegIO: TIOFunctions); override; + procedure SetCustomIO(const CustomIO: TIOFunctions); + end; +{$ENDIF} + + TAPNGAnimator = class + public + class procedure Animate(var Images: TDynImageDataArray; const acTL: TacTL; const SrcFrames: array of TFrameInfo); + end; + +{ Helper routines } + +function PaethPredictor(A, B, C: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} +var + P, PA, PB, PC: LongInt; +begin + P := A + B - C; + PA := Abs(P - A); + PB := Abs(P - B); + PC := Abs(P - C); + if (PA <= PB) and (PA <= PC) then + Result := A + else + if PB <= PC then + Result := B + else + Result := C; +end; + +procedure SwapRGB(Line: PByte; Width, SampleDepth, BytesPerPixel: LongInt); +var + I: LongInt; + Tmp: Word; +begin + case SampleDepth of + 8: + for I := 0 to Width - 1 do + with PColor24Rec(Line)^ do + begin + Tmp := R; + R := B; + B := Tmp; + Inc(Line, BytesPerPixel); + end; + 16: + for I := 0 to Width - 1 do + with PColor48Rec(Line)^ do + begin + Tmp := R; + R := B; + B := Tmp; + Inc(Line, BytesPerPixel); + end; + end; + end; + +{$IFNDEF DONT_LINK_JNG} + +{ TCustomIOJpegFileFormat class implementation } + +procedure TCustomIOJpegFileFormat.SetCustomIO(const CustomIO: TIOFunctions); +begin + FCustomIO := CustomIO; +end; + +procedure TCustomIOJpegFileFormat.SetJpegIO(const JpegIO: TIOFunctions); +begin + inherited SetJpegIO(FCustomIO); +end; + +{$ENDIF} + +{ TFrameInfo class implementation } + +constructor TFrameInfo.Create(AIndex: Integer); +begin + Index := AIndex; + IDATMemory := TMemoryStream.Create; + JDATMemory := TMemoryStream.Create; + JDAAMemory := TMemoryStream.Create; +end; + +destructor TFrameInfo.Destroy; +begin + FreeMem(Palette); + FreeMem(Transparency); + FreeMem(Background); + IDATMemory.Free; + JDATMemory.Free; + JDAAMemory.Free; + inherited Destroy; +end; + +procedure TFrameInfo.AssignSharedProps(Source: TFrameInfo); +begin + IHDR := Source.IHDR; + JHDR := Source.JHDR; + PaletteEntries := Source.PaletteEntries; + GetMem(Palette, PaletteEntries * SizeOf(TColor24Rec)); + Move(Source.Palette^, Palette^, PaletteEntries * SizeOf(TColor24Rec)); + TransparencySize := Source.TransparencySize; + GetMem(Transparency, TransparencySize); + Move(Source.Transparency^, Transparency^, TransparencySize); +end; + +{ TNGFileHandler class implementation} + +destructor TNGFileHandler.Destroy; +begin + Clear; + inherited Destroy; +end; + +procedure TNGFileHandler.Clear; +var + I: LongInt; +begin + for I := 0 to Length(Frames) - 1 do + Frames[I].Free; + SetLength(Frames, 0); + FreeMemNil(GlobalPalette); + GlobalPaletteEntries := 0; + FreeMemNil(GlobalTransparency); + GlobalTransparencySize := 0; +end; + +constructor TNGFileHandler.Create(AFileFormat: TNetworkGraphicsFileFormat); +begin + FileFormat := AFileFormat; +end; + +function TNGFileHandler.GetLastFrame: TFrameInfo; +var + Len: LongInt; +begin + Len := Length(Frames); + if Len > 0 then + Result := Frames[Len - 1] + else + Result := nil; +end; + +procedure TNGFileHandler.LoadMetaData; +var + I: Integer; + Delay, Denom: Integer; +begin + if FileType = ngAPNG then + begin + // Num plays of APNG animation + FileFormat.FMetadata.SetMetaItem(SMetaAnimationLoops, acTL.NumPlay); + end; + + for I := 0 to High(Frames) do + begin + if Frames[I].pHYs.UnitSpecifier = 1 then + begin + // Store physical pixel dimensions, in PNG stored as pixels per meter DPM + FileFormat.FMetadata.SetPhysicalPixelSize(ruDpm, Frames[I].pHYs.PixelsPerUnitX, + Frames[I].pHYs.PixelsPerUnitY); + end; + if FileType = ngAPNG then + begin + // Store frame delay of APNG file frame + Denom := Frames[I].fcTL.DelayDenom; + if Denom = 0 then + Denom := 100; + Delay := Round(1000 * (Frames[I].fcTL.DelayNumer / Denom)); + FileFormat.FMetadata.SetMetaItem(SMetaFrameDelay, Delay, I); + end; + end; +end; + +function TNGFileHandler.AddFrameInfo: TFrameInfo; +var + Len: LongInt; +begin + Len := Length(Frames); + SetLength(Frames, Len + 1); + Result := TFrameInfo.Create(Len); + Frames[Len] := Result; +end; + +{ TNGFileLoader class implementation} + +function TNGFileLoader.LoadFile(Handle: TImagingHandle): Boolean; +var + Sig: TChar8; + Chunk: TChunkHeader; + ChunkData: Pointer; + ChunkCrc: LongWord; + + procedure ReadChunk; + begin + GetIO.Read(Handle, @Chunk, SizeOf(Chunk)); + Chunk.DataSize := SwapEndianLongWord(Chunk.DataSize); + end; + + procedure ReadChunkData; + var + ReadBytes: LongWord; + begin + FreeMemNil(ChunkData); + GetMem(ChunkData, Chunk.DataSize); + ReadBytes := GetIO.Read(Handle, ChunkData, Chunk.DataSize); + GetIO.Read(Handle, @ChunkCrc, SizeOf(ChunkCrc)); + if ReadBytes <> Chunk.DataSize then + RaiseImaging(SErrorLoadingChunk, [string(Chunk.ChunkID)]); + end; + + procedure SkipChunkData; + begin + GetIO.Seek(Handle, Chunk.DataSize + SizeOf(ChunkCrc), smFromCurrent); + end; + + procedure StartNewPNGImage; + var + Frame: TFrameInfo; + begin + ReadChunkData; + + if Chunk.ChunkID = fcTLChunk then + begin + if (Length(Frames) = 1) and (Frames[0].IDATMemory.Size = 0) then + begin + // First fcTL chunk maybe for first IDAT frame which is alredy created + Frame := Frames[0]; + end + else + begin + // Subsequent APNG frames with data in fdAT + Frame := AddFrameInfo; + // Copy some shared props from first frame (IHDR is the same for all APNG frames, palette etc) + Frame.AssignSharedProps(Frames[0]); + end; + Frame.fcTL := PfcTL(ChunkData)^; + SwapEndianLongWord(@Frame.fcTL, 5); + Frame.fcTL.DelayNumer := SwapEndianWord(Frame.fcTL.DelayNumer); + Frame.fcTL.DelayDenom := SwapEndianWord(Frame.fcTL.DelayDenom); + Frame.FrameWidth := Frame.fcTL.Width; + Frame.FrameHeight := Frame.fcTL.Height; + end + else + begin + // This is frame defined by IHDR chunk + Frame := AddFrameInfo; + Frame.IHDR := PIHDR(ChunkData)^; + SwapEndianLongWord(@Frame.IHDR, 2); + Frame.FrameWidth := Frame.IHDR.Width; + Frame.FrameHeight := Frame.IHDR.Height; + end; + Frame.IsJpegFrame := False; + end; + + procedure StartNewJNGImage; + var + Frame: TFrameInfo; + begin + ReadChunkData; + Frame := AddFrameInfo; + Frame.IsJpegFrame := True; + Frame.JHDR := PJHDR(ChunkData)^; + SwapEndianLongWord(@Frame.JHDR, 2); + Frame.FrameWidth := Frame.JHDR.Width; + Frame.FrameHeight := Frame.JHDR.Height; + end; + + procedure AppendIDAT; + begin + ReadChunkData; + // Append current IDAT/fdAT chunk to storage stream + if Chunk.ChunkID = IDATChunk then + GetLastFrame.IDATMemory.Write(ChunkData^, Chunk.DataSize) + else if Chunk.ChunkID = fdATChunk then + GetLastFrame.IDATMemory.Write(PByteArray(ChunkData)[4], Chunk.DataSize - SizeOf(LongWord)); + end; + + procedure AppendJDAT; + begin + ReadChunkData; + // Append current JDAT chunk to storage stream + GetLastFrame.JDATMemory.Write(ChunkData^, Chunk.DataSize); + end; + + procedure AppendJDAA; + begin + ReadChunkData; + // Append current JDAA chunk to storage stream + GetLastFrame.JDAAMemory.Write(ChunkData^, Chunk.DataSize); + end; + + procedure LoadPLTE; + begin + ReadChunkData; + if GetLastFrame = nil then + begin + // Load global palette + GetMem(GlobalPalette, Chunk.DataSize); + Move(ChunkData^, GlobalPalette^, Chunk.DataSize); + GlobalPaletteEntries := Chunk.DataSize div 3; + end + else if GetLastFrame.Palette = nil then + begin + if (Chunk.DataSize = 0) and (GlobalPalette <> nil) then + begin + // Use global palette + GetMem(GetLastFrame.Palette, GlobalPaletteEntries * SizeOf(TColor24Rec)); + Move(GlobalPalette^, GetLastFrame.Palette^, GlobalPaletteEntries * SizeOf(TColor24Rec)); + GetLastFrame.PaletteEntries := GlobalPaletteEntries; + end + else + begin + // Load pal from PLTE chunk + GetMem(GetLastFrame.Palette, Chunk.DataSize); + Move(ChunkData^, GetLastFrame.Palette^, Chunk.DataSize); + GetLastFrame.PaletteEntries := Chunk.DataSize div 3; + end; + end; + end; + + procedure LoadtRNS; + begin + ReadChunkData; + if GetLastFrame = nil then + begin + // Load global transparency + GetMem(GlobalTransparency, Chunk.DataSize); + Move(ChunkData^, GlobalTransparency^, Chunk.DataSize); + GlobalTransparencySize := Chunk.DataSize; + end + else if GetLastFrame.Transparency = nil then + begin + if (Chunk.DataSize = 0) and (GlobalTransparency <> nil) then + begin + // Use global transparency + GetMem(GetLastFrame.Transparency, GlobalTransparencySize); + Move(GlobalTransparency^, GetLastFrame.Transparency^, Chunk.DataSize); + GetLastFrame.TransparencySize := GlobalTransparencySize; + end + else + begin + // Load pal from tRNS chunk + GetMem(GetLastFrame.Transparency, Chunk.DataSize); + Move(ChunkData^, GetLastFrame.Transparency^, Chunk.DataSize); + GetLastFrame.TransparencySize := Chunk.DataSize; + end; + end; + end; + + procedure LoadbKGD; + begin + ReadChunkData; + if GetLastFrame.Background = nil then + begin + GetMem(GetLastFrame.Background, Chunk.DataSize); + Move(ChunkData^, GetLastFrame.Background^, Chunk.DataSize); + GetLastFrame.BackgroundSize := Chunk.DataSize; + end; + end; + + procedure HandleacTL; + begin + FileType := ngAPNG; + ReadChunkData; + acTL := PacTL(ChunkData)^; + SwapEndianLongWord(@acTL, SizeOf(acTL) div SizeOf(LongWord)); + end; + + procedure LoadpHYs; + begin + ReadChunkData; + with GetLastFrame do + begin + pHYs := PpHYs(ChunkData)^; + SwapEndianLongWord(@pHYs, SizeOf(pHYs) div SizeOf(LongWord)); + end; + end; + +begin + Result := False; + Clear; + ChunkData := nil; + with GetIO do + try + Read(Handle, @Sig, SizeOf(Sig)); + // Set file type according to the signature + if Sig = PNGSignature then FileType := ngPNG + else if Sig = MNGSignature then FileType := ngMNG + else if Sig = JNGSignature then FileType := ngJNG + else Exit; + + if FileType = ngMNG then + begin + // Store MNG header if present + ReadChunk; + ReadChunkData; + MHDR := PMHDR(ChunkData)^; + SwapEndianLongWord(@MHDR, SizeOf(MHDR) div SizeOf(LongWord)); + end; + + // Read chunks until ending chunk or EOF is reached + repeat + ReadChunk; + if (Chunk.ChunkID = IHDRChunk) or (Chunk.ChunkID = fcTLChunk) then StartNewPNGImage + else if Chunk.ChunkID = JHDRChunk then StartNewJNGImage + else if (Chunk.ChunkID = IDATChunk) or (Chunk.ChunkID = fdATChunk) then AppendIDAT + else if Chunk.ChunkID = JDATChunk then AppendJDAT + else if Chunk.ChunkID = JDAAChunk then AppendJDAA + else if Chunk.ChunkID = PLTEChunk then LoadPLTE + else if Chunk.ChunkID = tRNSChunk then LoadtRNS + else if Chunk.ChunkID = bKGDChunk then LoadbKGD + else if Chunk.ChunkID = acTLChunk then HandleacTL + else if Chunk.ChunkID = pHYsChunk then LoadpHYs + else SkipChunkData; + until Eof(Handle) or (Chunk.ChunkID = MENDChunk) or + ((FileType <> ngMNG) and (Chunk.ChunkID = IENDChunk)); + + Result := True; + finally + FreeMemNil(ChunkData); + end; +end; + +procedure TNGFileLoader.LoadImageFromPNGFrame(FrameWidth, FrameHeight: LongInt; const IHDR: TIHDR; + IDATStream: TMemoryStream; var Image: TImageData); +type + TGetPixelFunc = function(Line: PByteArray; X: LongInt): Byte; +var + LineBuffer: array[Boolean] of PByteArray; + ActLine: Boolean; + Data, TotalBuffer, ZeroLine, PrevLine: Pointer; + BitCount, TotalSize, TotalPos, BytesPerPixel, I, Pass, + SrcDataSize, BytesPerLine, InterlaceLineBytes, InterlaceWidth: LongInt; + Info: TImageFormatInfo; + + procedure DecodeAdam7; + const + BitTable: array[1..8] of LongInt = ($1, $3, 0, $F, 0, 0, 0, $FF); + StartBit: array[1..8] of LongInt = (7, 6, 0, 4, 0, 0, 0, 0); + var + Src, Dst, Dst2: PByte; + CurBit, Col: LongInt; + begin + Src := @LineBuffer[ActLine][1]; + Col := ColumnStart[Pass]; + with Image do + case BitCount of + 1, 2, 4: + begin + Dst := @PByteArray(Data)[I * BytesPerLine]; + repeat + CurBit := StartBit[BitCount]; + repeat + Dst2 := @PByteArray(Dst)[(BitCount * Col) shr 3]; + Dst2^ := Dst2^ or ((Src^ shr CurBit) and BitTable[BitCount]) + shl (StartBit[BitCount] - (Col * BitCount mod 8)); + Inc(Col, ColumnIncrement[Pass]); + Dec(CurBit, BitCount); + until CurBit < 0; + Inc(Src); + until Col >= Width; + end; + else + begin + Dst := @PByteArray(Data)[I * BytesPerLine + Col * BytesPerPixel]; + repeat + CopyPixel(Src, Dst, BytesPerPixel); + Inc(Dst, BytesPerPixel); + Inc(Src, BytesPerPixel); + Inc(Dst, ColumnIncrement[Pass] * BytesPerPixel - BytesPerPixel); + Inc(Col, ColumnIncrement[Pass]); + until Col >= Width; + end; + end; + end; + + procedure FilterScanline(Filter: Byte; BytesPerPixel: LongInt; Line, PrevLine, Target: PByteArray; + BytesPerLine: LongInt); + var + I: LongInt; + begin + case Filter of + 0: + begin + // No filter + Move(Line^, Target^, BytesPerLine); + end; + 1: + begin + // Sub filter + Move(Line^, Target^, BytesPerPixel); + for I := BytesPerPixel to BytesPerLine - 1 do + Target[I] := (Line[I] + Target[I - BytesPerPixel]) and $FF; + end; + 2: + begin + // Up filter + for I := 0 to BytesPerLine - 1 do + Target[I] := (Line[I] + PrevLine[I]) and $FF; + end; + 3: + begin + // Average filter + for I := 0 to BytesPerPixel - 1 do + Target[I] := (Line[I] + PrevLine[I] shr 1) and $FF; + for I := BytesPerPixel to BytesPerLine - 1 do + Target[I] := (Line[I] + (Target[I - BytesPerPixel] + PrevLine[I]) shr 1) and $FF; + end; + 4: + begin + // Paeth filter + for I := 0 to BytesPerPixel - 1 do + Target[I] := (Line[I] + PaethPredictor(0, PrevLine[I], 0)) and $FF; + for I := BytesPerPixel to BytesPerLine - 1 do + Target[I] := (Line[I] + PaethPredictor(Target[I - BytesPerPixel], PrevLine[I], PrevLine[I - BytesPerPixel])) and $FF; + end; + end; + end; + + procedure TransformLOCOToRGB(Data: PByte; NumPixels, BytesPerPixel: LongInt); + var + I: LongInt; + begin + for I := 0 to NumPixels - 1 do + begin + if IHDR.BitDepth = 8 then + begin + PColor32Rec(Data).R := Byte(PColor32Rec(Data).R + PColor32Rec(Data).G); + PColor32Rec(Data).B := Byte(PColor32Rec(Data).B + PColor32Rec(Data).G); + end + else + begin + PColor64Rec(Data).R := Word(PColor64Rec(Data).R + PColor64Rec(Data).G); + PColor64Rec(Data).B := Word(PColor64Rec(Data).B + PColor64Rec(Data).G); + end; + Inc(Data, BytesPerPixel); + end; + end; + + function CheckBinaryPalette: Boolean; + begin + with GetLastFrame do + Result := (PaletteEntries = 2) and + (Palette[0].R = 0) and (Palette[0].G = 0) and (Palette[0].B = 0) and + (Palette[1].R = 255) and (Palette[1].G = 255) and (Palette[1].B = 255); + end; + +begin + Image.Width := FrameWidth; + Image.Height := FrameHeight; + Image.Format := ifUnknown; + + case IHDR.ColorType of + 0: + begin + // Gray scale image + case IHDR.BitDepth of + 1: Image.Format := ifBinary; + 2, 4, 8: Image.Format := ifGray8; + 16: Image.Format := ifGray16; + end; + BitCount := IHDR.BitDepth; + end; + 2: + begin + // RGB image + case IHDR.BitDepth of + 8: Image.Format := ifR8G8B8; + 16: Image.Format := ifR16G16B16; + end; + BitCount := IHDR.BitDepth * 3; + end; + 3: + begin + // Indexed image + if (IHDR.BitDepth = 1) and CheckBinaryPalette then + Image.Format := ifBinary + else + Image.Format := ifIndex8; + BitCount := IHDR.BitDepth; + end; + 4: + begin + // Grayscale + alpha image + case IHDR.BitDepth of + 8: Image.Format := ifA8Gray8; + 16: Image.Format := ifA16Gray16; + end; + BitCount := IHDR.BitDepth * 2; + end; + 6: + begin + // ARGB image + case IHDR.BitDepth of + 8: Image.Format := ifA8R8G8B8; + 16: Image.Format := ifA16R16G16B16; + end; + BitCount := IHDR.BitDepth * 4; + end; + end; + + GetImageFormatInfo(Image.Format, Info); + BytesPerPixel := (BitCount + 7) div 8; + + LineBuffer[True] := nil; + LineBuffer[False] := nil; + TotalBuffer := nil; + ZeroLine := nil; + ActLine := True; + + // Start decoding + with Image do + try + BytesPerLine := (Width * BitCount + 7) div 8; + SrcDataSize := Height * BytesPerLine; + GetMem(Data, SrcDataSize); + FillChar(Data^, SrcDataSize, 0); + GetMem(ZeroLine, BytesPerLine); + FillChar(ZeroLine^, BytesPerLine, 0); + + if IHDR.Interlacing = 1 then + begin + // Decode interlaced images + TotalPos := 0; + DecompressBuf(IDATStream.Memory, IDATStream.Size, 0, + Pointer(TotalBuffer), TotalSize); + GetMem(LineBuffer[True], BytesPerLine + 1); + GetMem(LineBuffer[False], BytesPerLine + 1); + for Pass := 0 to 6 do + begin + // Prepare next interlace run + if Width <= ColumnStart[Pass] then + Continue; + InterlaceWidth := (Width + ColumnIncrement[Pass] - 1 - + ColumnStart[Pass]) div ColumnIncrement[Pass]; + InterlaceLineBytes := (InterlaceWidth * BitCount + 7) shr 3; + I := RowStart[Pass]; + FillChar(LineBuffer[True][0], BytesPerLine + 1, 0); + FillChar(LineBuffer[False][0], BytesPerLine + 1, 0); + while I < Height do + begin + // Copy line from decompressed data to working buffer + Move(PByteArray(TotalBuffer)[TotalPos], + LineBuffer[ActLine][0], InterlaceLineBytes + 1); + Inc(TotalPos, InterlaceLineBytes + 1); + // Swap red and blue channels if necessary + if (IHDR.ColorType in [2, 6]) then + SwapRGB(@LineBuffer[ActLine][1], InterlaceWidth, IHDR.BitDepth, BytesPerPixel); + // Reverse-filter current scanline + FilterScanline(LineBuffer[ActLine][0], BytesPerPixel, + @LineBuffer[ActLine][1], @LineBuffer[not ActLine][1], + @LineBuffer[ActLine][1], InterlaceLineBytes); + // Decode Adam7 interlacing + DecodeAdam7; + ActLine := not ActLine; + // Continue with next row in interlaced order + Inc(I, RowIncrement[Pass]); + end; + end; + end + else + begin + // Decode non-interlaced images + PrevLine := ZeroLine; + DecompressBuf(IDATStream.Memory, IDATStream.Size, SrcDataSize + Height, + Pointer(TotalBuffer), TotalSize); + for I := 0 to Height - 1 do + begin + // Swap red and blue channels if necessary + if IHDR.ColorType in [2, 6] then + SwapRGB(@PByteArray(TotalBuffer)[I * (BytesPerLine + 1) + 1], Width, + IHDR.BitDepth, BytesPerPixel); + // reverse-filter current scanline + FilterScanline(PByteArray(TotalBuffer)[I * (BytesPerLine + 1)], + BytesPerPixel, @PByteArray(TotalBuffer)[I * (BytesPerLine + 1) + 1], + PrevLine, @PByteArray(Data)[I * BytesPerLine], BytesPerLine); + PrevLine := @PByteArray(Data)[I * BytesPerLine]; + end; + end; + + Size := Info.GetPixelsSize(Info.Format, Width, Height); + + if Size <> SrcDataSize then + begin + // If source data size is different from size of image in assigned + // format we must convert it (it is in 1/2/4 bit count) + GetMem(Bits, Size); + case IHDR.BitDepth of + 1: + begin + // Convert only indexed, keep black and white in ifBinary + if IHDR.ColorType <> 0 then + Convert1To8(Data, Bits, Width, Height, BytesPerLine, False); + end; + 2: Convert2To8(Data, Bits, Width, Height, BytesPerLine, IHDR.ColorType = 0); + 4: Convert4To8(Data, Bits, Width, Height, BytesPerLine, IHDR.ColorType = 0); + end; + FreeMem(Data); + end + else + begin + // If source data size is the same as size of + // image Bits in assigned format we simply copy pointer reference + Bits := Data; + end; + + // LOCO transformation was used too (only for color types 2 and 6) + if (IHDR.Filter = 64) and (IHDR.ColorType in [2, 6]) then + TransformLOCOToRGB(Bits, Width * Height, BytesPerPixel); + + // Images with 16 bit channels must be swapped because of PNG's big endianity + if IHDR.BitDepth = 16 then + SwapEndianWord(Bits, Width * Height * BytesPerPixel div SizeOf(Word)); + finally + FreeMem(LineBuffer[True]); + FreeMem(LineBuffer[False]); + FreeMem(TotalBuffer); + FreeMem(ZeroLine); + end; +end; + +{$IFNDEF DONT_LINK_JNG} + +procedure TNGFileLoader.LoadImageFromJNGFrame(FrameWidth, FrameHeight: LongInt; const JHDR: TJHDR; IDATStream, + JDATStream, JDAAStream: TMemoryStream; var Image: TImageData); +var + AlphaImage: TImageData; + FakeIHDR: TIHDR; + FmtInfo: TImageFormatInfo; + I: LongInt; + AlphaPtr: PByte; + GrayPtr: PWordRec; + ColorPtr: PColor32Rec; + + procedure LoadJpegFromStream(Stream: TStream; var DestImage: TImageData); + var + JpegFormat: TCustomIOJpegFileFormat; + Handle: TImagingHandle; + DynImages: TDynImageDataArray; + begin + if JHDR.SampleDepth <> 12 then + begin + JpegFormat := TCustomIOJpegFileFormat.Create; + JpegFormat.SetCustomIO(StreamIO); + Stream.Position := 0; + Handle := StreamIO.Open(Pointer(Stream), omReadOnly); + try + JpegFormat.LoadData(Handle, DynImages, True); + DestImage := DynImages[0]; + finally + StreamIO.Close(Handle); + JpegFormat.Free; + SetLength(DynImages, 0); + end; + end + else + NewImage(FrameWidth, FrameHeight, ifR8G8B8, DestImage); + end; + +begin + LoadJpegFromStream(JDATStream, Image); + + // If present separate alpha channel is processed + if (JHDR.ColorType in [12, 14]) and (Image.Format in [ifGray8, ifR8G8B8]) then + begin + InitImage(AlphaImage); + if JHDR.AlphaCompression = 0 then + begin + // Alpha channel is PNG compressed + FakeIHDR.Width := JHDR.Width; + FakeIHDR.Height := JHDR.Height; + FakeIHDR.ColorType := 0; + FakeIHDR.BitDepth := JHDR.AlphaSampleDepth; + FakeIHDR.Filter := JHDR.AlphaFilter; + FakeIHDR.Interlacing := JHDR.AlphaInterlacing; + + LoadImageFromPNGFrame(FrameWidth, FrameHeight, FakeIHDR, IDATStream, AlphaImage); + end + else + begin + // Alpha channel is JPEG compressed + LoadJpegFromStream(JDAAStream, AlphaImage); + end; + + // Check if alpha channel is the same size as image + if (Image.Width <> AlphaImage.Width) and (Image.Height <> AlphaImage.Height) then + ResizeImage(AlphaImage, Image.Width, Image.Height, rfNearest); + + // Check alpha channels data format + GetImageFormatInfo(AlphaImage.Format, FmtInfo); + if (FmtInfo.BytesPerPixel > 1) or (not FmtInfo.HasGrayChannel) then + ConvertImage(AlphaImage, ifGray8); + + // Convert image to fromat with alpha channel + if Image.Format = ifGray8 then + ConvertImage(Image, ifA8Gray8) + else + ConvertImage(Image, ifA8R8G8B8); + + // Combine alpha channel with image + AlphaPtr := AlphaImage.Bits; + if Image.Format = ifA8Gray8 then + begin + GrayPtr := Image.Bits; + for I := 0 to Image.Width * Image.Height - 1 do + begin + GrayPtr.High := AlphaPtr^; + Inc(GrayPtr); + Inc(AlphaPtr); + end; + end + else + begin + ColorPtr := Image.Bits; + for I := 0 to Image.Width * Image.Height - 1 do + begin + ColorPtr.A := AlphaPtr^; + Inc(ColorPtr); + Inc(AlphaPtr); + end; + end; + + FreeImage(AlphaImage); + end; +end; + +{$ENDIF} + +procedure TNGFileLoader.ApplyFrameSettings(Frame: TFrameInfo; var Image: TImageData); +var + FmtInfo: TImageFormatInfo; + BackGroundColor: TColor64Rec; + ColorKey: TColor64Rec; + Alphas: PByteArray; + AlphasSize: LongInt; + IsColorKeyPresent: Boolean; + IsBackGroundPresent: Boolean; + IsColorFormat: Boolean; + + procedure ConverttRNS; + begin + if FmtInfo.IsIndexed then + begin + if Alphas = nil then + begin + GetMem(Alphas, Frame.TransparencySize); + Move(Frame.Transparency^, Alphas^, Frame.TransparencySize); + AlphasSize := Frame.TransparencySize; + end; + end + else if not FmtInfo.HasAlphaChannel then + begin + FillChar(ColorKey, SizeOf(ColorKey), 0); + Move(Frame.Transparency^, ColorKey, Min(Frame.TransparencySize, SizeOf(ColorKey))); + if IsColorFormat then + SwapValues(ColorKey.R, ColorKey.B); + SwapEndianWord(@ColorKey, 3); + // 1/2/4 bit images were converted to 8 bit so we must convert color key too + if (not Frame.IsJpegFrame) and (Frame.IHDR.ColorType in [0, 4]) then + case Frame.IHDR.BitDepth of + 1: ColorKey.B := Word(ColorKey.B * 255); + 2: ColorKey.B := Word(ColorKey.B * 85); + 4: ColorKey.B := Word(ColorKey.B * 17); + end; + IsColorKeyPresent := True; + end; + end; + + procedure ConvertbKGD; + begin + FillChar(BackGroundColor, SizeOf(BackGroundColor), 0); + Move(Frame.Background^, BackGroundColor, Min(Frame.BackgroundSize, SizeOf(BackGroundColor))); + if IsColorFormat then + SwapValues(BackGroundColor.R, BackGroundColor.B); + SwapEndianWord(@BackGroundColor, 3); + // 1/2/4 bit images were converted to 8 bit so we must convert back color too + if (not Frame.IsJpegFrame) and (Frame.IHDR.ColorType in [0, 4]) then + case Frame.IHDR.BitDepth of + 1: BackGroundColor.B := Word(BackGroundColor.B * 255); + 2: BackGroundColor.B := Word(BackGroundColor.B * 85); + 4: BackGroundColor.B := Word(BackGroundColor.B * 17); + end; + IsBackGroundPresent := True; + end; + + procedure ReconstructPalette; + var + I: LongInt; + begin + with Image do + begin + GetMem(Palette, FmtInfo.PaletteEntries * SizeOf(TColor32Rec)); + FillChar(Palette^, FmtInfo.PaletteEntries * SizeOf(TColor32Rec), $FF); + // if RGB palette was loaded from file then use it + if Frame.Palette <> nil then + for I := 0 to Min(Frame.PaletteEntries, FmtInfo.PaletteEntries) - 1 do + with Palette[I] do + begin + R := Frame.Palette[I].B; + G := Frame.Palette[I].G; + B := Frame.Palette[I].R; + end; + // if palette alphas were loaded from file then use them + if Alphas <> nil then + begin + for I := 0 to Min(AlphasSize, FmtInfo.PaletteEntries) - 1 do + Palette[I].A := Alphas[I]; + end; + end; + end; + + procedure ApplyColorKey; + var + DestFmt: TImageFormat; + Col32, Bkg32: TColor32Rec; + OldPixel, NewPixel: Pointer; + begin + case Image.Format of + ifGray8: DestFmt := ifA8Gray8; + ifGray16: DestFmt := ifA16Gray16; + ifR8G8B8: DestFmt := ifA8R8G8B8; + ifR16G16B16: DestFmt := ifA16R16G16B16; + else + DestFmt := ifUnknown; + end; + + if DestFmt <> ifUnknown then + begin + if not IsBackGroundPresent then + BackGroundColor := ColorKey; + ConvertImage(Image, DestFmt); + + // Now back color and color key must be converted to image's data format, looks ugly + case Image.Format of + ifA8Gray8: + begin + Col32 := Color32(0, 0, $FF, Byte(ColorKey.B)); + Bkg32 := Color32(0, 0, 0, Byte(BackGroundColor.B)); + end; + ifA16Gray16: + begin + ColorKey.G := $FFFF; + end; + ifA8R8G8B8: + begin + Col32 := Color32($FF, Byte(ColorKey.R), Byte(ColorKey.G), Byte(ColorKey.B)); + Bkg32 := Color32(0, Byte(BackGroundColor.R), Byte(BackGroundColor.G), Byte(BackGroundColor.B)); + end; + ifA16R16G16B16: + begin + ColorKey.A := $FFFF; + end; + end; + + if Image.Format in [ifA8Gray8, ifA8R8G8B8] then + begin + OldPixel := @Col32; + NewPixel := @Bkg32; + end + else + begin + OldPixel := @ColorKey; + NewPixel := @BackGroundColor; + end; + + ReplaceColor(Image, 0, 0, Image.Width, Image.Height, OldPixel, NewPixel); + end; + end; + +begin + Alphas := nil; + IsColorKeyPresent := False; + IsBackGroundPresent := False; + GetImageFormatInfo(Image.Format, FmtInfo); + + IsColorFormat := (Frame.IsJpegFrame and (Frame.JHDR.ColorType in [10, 14])) or + (not Frame.IsJpegFrame and (Frame.IHDR.ColorType in [2, 6])); + + // Convert some chunk data to useful format + if Frame.TransparencySize > 0 then + ConverttRNS; + if Frame.BackgroundSize > 0 then + ConvertbKGD; + + // Build palette for indexed images + if FmtInfo.IsIndexed then + ReconstructPalette; + + // Apply color keying + if IsColorKeyPresent and not FmtInfo.HasAlphaChannel then + ApplyColorKey; + + FreeMemNil(Alphas); +end; + +{ TNGFileSaver class implementation } + +procedure TNGFileSaver.StoreImageToPNGFrame(const IHDR: TIHDR; Bits: Pointer; + FmtInfo: TImageFormatInfo; IDATStream: TMemoryStream); +var + TotalBuffer, CompBuffer, ZeroLine, PrevLine: Pointer; + FilterLines: array[0..4] of PByteArray; + TotalSize, CompSize, I, BytesPerLine, BytesPerPixel: LongInt; + Filter: Byte; + Adaptive: Boolean; + + procedure FilterScanline(Filter: Byte; BytesPerPixel: LongInt; Line, PrevLine, Target: PByteArray); + var + I: LongInt; + begin + case Filter of + 0: + begin + // No filter + Move(Line^, Target^, BytesPerLine); + end; + 1: + begin + // Sub filter + Move(Line^, Target^, BytesPerPixel); + for I := BytesPerPixel to BytesPerLine - 1 do + Target[I] := (Line[I] - Line[I - BytesPerPixel]) and $FF; + end; + 2: + begin + // Up filter + for I := 0 to BytesPerLine - 1 do + Target[I] := (Line[I] - PrevLine[I]) and $FF; + end; + 3: + begin + // Average filter + for I := 0 to BytesPerPixel - 1 do + Target[I] := (Line[I] - PrevLine[I] shr 1) and $FF; + for I := BytesPerPixel to BytesPerLine - 1 do + Target[I] := (Line[I] - (Line[I - BytesPerPixel] + PrevLine[I]) shr 1) and $FF; + end; + 4: + begin + // Paeth filter + for I := 0 to BytesPerPixel - 1 do + Target[I] := (Line[I] - PaethPredictor(0, PrevLine[I], 0)) and $FF; + for I := BytesPerPixel to BytesPerLine - 1 do + Target[I] := (Line[I] - PaethPredictor(Line[I - BytesPerPixel], PrevLine[I], PrevLine[I - BytesPerPixel])) and $FF; + end; + end; + end; + + procedure AdaptiveFilter(var Filter: Byte; BytesPerPixel: LongInt; Line, PrevLine, Target: PByteArray); + var + I, J, BestTest: LongInt; + Sums: array[0..4] of LongInt; + begin + // Compute the output scanline using all five filters, + // and select the filter that gives the smallest sum of + // absolute values of outputs + FillChar(Sums, SizeOf(Sums), 0); + BestTest := MaxInt; + for I := 0 to 4 do + begin + FilterScanline(I, BytesPerPixel, Line, PrevLine, FilterLines[I]); + for J := 0 to BytesPerLine - 1 do + Sums[I] := Sums[I] + Abs(ShortInt(FilterLines[I][J])); + if Sums[I] < BestTest then + begin + Filter := I; + BestTest := Sums[I]; + end; + end; + Move(FilterLines[Filter]^, Target^, BytesPerLine); + end; + +begin + // Select precompression filter and compression level + Adaptive := False; + Filter := 0; + case PreFilter of + 6: + if not ((IHDR.BitDepth < 8) or (IHDR.ColorType = 3)) then + Adaptive := True; + 0..4: Filter := PreFilter; + else + if IHDR.ColorType in [2, 6] then + Filter := 4 + end; + + // Prepare data for compression + CompBuffer := nil; + FillChar(FilterLines, SizeOf(FilterLines), 0); + BytesPerPixel := Max(1, FmtInfo.BytesPerPixel); + BytesPerLine := FmtInfo.GetPixelsSize(FmtInfo.Format, LongInt(IHDR.Width), 1); + TotalSize := (BytesPerLine + 1) * LongInt(IHDR.Height); + GetMem(TotalBuffer, TotalSize); + GetMem(ZeroLine, BytesPerLine); + FillChar(ZeroLine^, BytesPerLine, 0); + PrevLine := ZeroLine; + + if Adaptive then + begin + for I := 0 to 4 do + GetMem(FilterLines[I], BytesPerLine); + end; + + try + // Process next scanlines + for I := 0 to IHDR.Height - 1 do + begin + // Filter scanline + if Adaptive then + begin + AdaptiveFilter(Filter, BytesPerPixel, @PByteArray(Bits)[I * BytesPerLine], + PrevLine, @PByteArray(TotalBuffer)[I * (BytesPerLine + 1) + 1]); + end + else + begin + FilterScanline(Filter, BytesPerPixel, @PByteArray(Bits)[I * BytesPerLine], + PrevLine, @PByteArray(TotalBuffer)[I * (BytesPerLine + 1) + 1]); + end; + PrevLine := @PByteArray(Bits)[I * BytesPerLine]; + // Swap red and blue if necessary + if (IHDR.ColorType in [2, 6]) and not FmtInfo.IsRBSwapped then + begin + SwapRGB(@PByteArray(TotalBuffer)[I * (BytesPerLine + 1) + 1], + IHDR.Width, IHDR.BitDepth, BytesPerPixel); + end; + // Images with 16 bit channels must be swapped because of PNG's big endianess + if IHDR.BitDepth = 16 then + begin + SwapEndianWord(@PByteArray(TotalBuffer)[I * (BytesPerLine + 1) + 1], + BytesPerLine div SizeOf(Word)); + end; + // Set filter used for this scanline + PByteArray(TotalBuffer)[I * (BytesPerLine + 1)] := Filter; + end; + // Compress IDAT data + CompressBuf(TotalBuffer, TotalSize, CompBuffer, CompSize, + CompressLevel, ZLibStrategy); + // Write IDAT data to stream + IDATStream.WriteBuffer(CompBuffer^, CompSize); + finally + FreeMem(TotalBuffer); + FreeMem(CompBuffer); + FreeMem(ZeroLine); + if Adaptive then + for I := 0 to 4 do + FreeMem(FilterLines[I]); + end; +end; + +{$IFNDEF DONT_LINK_JNG} + +procedure TNGFileSaver.StoreImageToJNGFrame(const JHDR: TJHDR; + const Image: TImageData; IDATStream, JDATStream, + JDAAStream: TMemoryStream); +var + ColorImage, AlphaImage: TImageData; + FmtInfo: TImageFormatInfo; + AlphaPtr: PByte; + GrayPtr: PWordRec; + ColorPtr: PColor32Rec; + I: LongInt; + FakeIHDR: TIHDR; + + procedure SaveJpegToStream(Stream: TStream; const Image: TImageData); + var + JpegFormat: TCustomIOJpegFileFormat; + Handle: TImagingHandle; + DynImages: TDynImageDataArray; + begin + JpegFormat := TCustomIOJpegFileFormat.Create; + JpegFormat.SetCustomIO(StreamIO); + // Only JDAT stream can be saved progressive + if Stream = JDATStream then + JpegFormat.FProgressive := Progressive + else + JpegFormat.FProgressive := False; + JpegFormat.FQuality := Quality; + SetLength(DynImages, 1); + DynImages[0] := Image; + Handle := StreamIO.Open(Pointer(Stream), omCreate); + try + JpegFormat.SaveData(Handle, DynImages, 0); + finally + StreamIO.Close(Handle); + SetLength(DynImages, 0); + JpegFormat.Free; + end; + end; + +begin + GetImageFormatInfo(Image.Format, FmtInfo); + InitImage(ColorImage); + InitImage(AlphaImage); + + if FmtInfo.HasAlphaChannel then + begin + // Create new image for alpha channel and color image without alpha + CloneImage(Image, ColorImage); + NewImage(Image.Width, Image.Height, ifGray8, AlphaImage); + case Image.Format of + ifA8Gray8: ConvertImage(ColorImage, ifGray8); + ifA8R8G8B8: ConvertImage(ColorImage, ifR8G8B8); + end; + + // Store source image's alpha to separate image + AlphaPtr := AlphaImage.Bits; + if Image.Format = ifA8Gray8 then + begin + GrayPtr := Image.Bits; + for I := 0 to Image.Width * Image.Height - 1 do + begin + AlphaPtr^ := GrayPtr.High; + Inc(GrayPtr); + Inc(AlphaPtr); + end; + end + else + begin + ColorPtr := Image.Bits; + for I := 0 to Image.Width * Image.Height - 1 do + begin + AlphaPtr^ := ColorPtr.A; + Inc(ColorPtr); + Inc(AlphaPtr); + end; + end; + + // Write color image to stream as JPEG + SaveJpegToStream(JDATStream, ColorImage); + + if LossyAlpha then + begin + // Write alpha image to stream as JPEG + SaveJpegToStream(JDAAStream, AlphaImage); + end + else + begin + // Alpha channel is PNG compressed + FakeIHDR.Width := JHDR.Width; + FakeIHDR.Height := JHDR.Height; + FakeIHDR.ColorType := 0; + FakeIHDR.BitDepth := JHDR.AlphaSampleDepth; + FakeIHDR.Filter := JHDR.AlphaFilter; + FakeIHDR.Interlacing := JHDR.AlphaInterlacing; + + GetImageFormatInfo(AlphaImage.Format, FmtInfo); + StoreImageToPNGFrame(FakeIHDR, AlphaImage.Bits, FmtInfo, IDATStream); + end; + + FreeImage(ColorImage); + FreeImage(AlphaImage); + end + else + begin + // Simply write JPEG to stream + SaveJpegToStream(JDATStream, Image); + end; +end; + +{$ENDIF} + +procedure TNGFileSaver.AddFrame(const Image: TImageData; IsJpegFrame: Boolean); +var + Frame: TFrameInfo; + FmtInfo: TImageFormatInfo; + Index: Integer; + + procedure StorePalette; + var + Pal: PPalette24; + Alphas: PByteArray; + I, PalBytes: LongInt; + AlphasDiffer: Boolean; + begin + // Fill and save RGB part of palette to PLTE chunk + PalBytes := FmtInfo.PaletteEntries * SizeOf(TColor24Rec); + GetMem(Pal, PalBytes); + AlphasDiffer := False; + for I := 0 to FmtInfo.PaletteEntries - 1 do + begin + Pal[I].B := Image.Palette[I].R; + Pal[I].G := Image.Palette[I].G; + Pal[I].R := Image.Palette[I].B; + if Image.Palette[I].A < 255 then + AlphasDiffer := True; + end; + Frame.Palette := Pal; + Frame.PaletteEntries := FmtInfo.PaletteEntries; + // Fill and save alpha part (if there are any alphas < 255) of palette to tRNS chunk + if AlphasDiffer then + begin + PalBytes := FmtInfo.PaletteEntries * SizeOf(Byte); + GetMem(Alphas, PalBytes); + for I := 0 to FmtInfo.PaletteEntries - 1 do + Alphas[I] := Image.Palette[I].A; + Frame.Transparency := Alphas; + Frame.TransparencySize := PalBytes; + end; + end; + + procedure FillFrameControlChunk(const IHDR: TIHDR; var fcTL: TfcTL); + var + Delay: Integer; + begin + fcTL.SeqNumber := 0; // Decided when writing to file + fcTL.Width := IHDR.Width; + fcTL.Height := IHDR.Height; + fcTL.XOffset := 0; + fcTL.YOffset := 0; + fcTL.DelayNumer := 1; + fcTL.DelayDenom := 3; + if FileFormat.FMetadata.HasMetaItemForSaving(SMetaFrameDelay, Index) then + begin + // Metadata contains frame delay information in milliseconds + Delay := FileFormat.FMetadata.MetaItemsForSavingMulti[SMetaFrameDelay, Index]; + fcTL.DelayNumer := Delay; + fcTL.DelayDenom := 1000; + end; + fcTL.DisposeOp := DisposeOpNone; + fcTL.BlendOp := BlendOpSource; + SwapEndianLongWord(@fcTL, 5); + fcTL.DelayNumer := SwapEndianWord(fcTL.DelayNumer); + fcTL.DelayDenom := SwapEndianWord(fcTL.DelayDenom); + end; + +begin + // Add new frame + Frame := AddFrameInfo; + Frame.IsJpegFrame := IsJpegFrame; + Index := Length(Frames) - 1; + + with Frame do + begin + GetImageFormatInfo(Image.Format, FmtInfo); + + if IsJpegFrame then + begin +{$IFNDEF DONT_LINK_JNG} + // Fill JNG header + JHDR.Width := Image.Width; + JHDR.Height := Image.Height; + case Image.Format of + ifGray8: JHDR.ColorType := 8; + ifR8G8B8: JHDR.ColorType := 10; + ifA8Gray8: JHDR.ColorType := 12; + ifA8R8G8B8: JHDR.ColorType := 14; + end; + JHDR.SampleDepth := 8; // 8-bit samples and quantization tables + JHDR.Compression := 8; // Huffman coding + JHDR.Interlacing := Iff(Progressive, 8, 0); + JHDR.AlphaSampleDepth := Iff(FmtInfo.HasAlphaChannel, 8, 0); + JHDR.AlphaCompression := Iff(LossyAlpha, 8, 0); + JHDR.AlphaFilter := 0; + JHDR.AlphaInterlacing := 0; + + StoreImageToJNGFrame(JHDR, Image, IDATMemory, JDATMemory, JDAAMemory); + + // Finally swap endian + SwapEndianLongWord(@JHDR, 2); +{$ENDIF} + end + else + begin + // Fill PNG header + IHDR.Width := Image.Width; + IHDR.Height := Image.Height; + IHDR.Compression := 0; + IHDR.Filter := 0; + IHDR.Interlacing := 0; + IHDR.BitDepth := FmtInfo.BytesPerPixel * 8; + + // Select appropiate PNG color type and modify bitdepth + if FmtInfo.HasGrayChannel then + begin + IHDR.ColorType := 0; + if FmtInfo.HasAlphaChannel then + begin + IHDR.ColorType := 4; + IHDR.BitDepth := IHDR.BitDepth div 2; + end; + end + else if FmtInfo.Format = ifBinary then + begin + IHDR.ColorType := 0; + IHDR.BitDepth := 1; + end + else if FmtInfo.IsIndexed then + IHDR.ColorType := 3 + else if FmtInfo.HasAlphaChannel then + begin + IHDR.ColorType := 6; + IHDR.BitDepth := IHDR.BitDepth div 4; + end + else + begin + IHDR.ColorType := 2; + IHDR.BitDepth := IHDR.BitDepth div 3; + end; + + if FileType = ngAPNG then + begin + // Fill fcTL chunk of APNG file + FillFrameControlChunk(IHDR, fcTL); + end; + + // Compress PNG image and store it to stream + StoreImageToPNGFrame(IHDR, Image.Bits, FmtInfo, IDATMemory); + // Store palette if necesary + if FmtInfo.IsIndexed then + StorePalette; + + // Finally swap endian + SwapEndianLongWord(@IHDR, 2); + end; + end; +end; + +function TNGFileSaver.SaveFile(Handle: TImagingHandle): Boolean; +var + I: LongInt; + Chunk: TChunkHeader; + SeqNo: LongWord; + + function GetNextSeqNo: LongWord; + begin + // Seq numbers of fcTL and fdAT are "interleaved" as they share the counter. + // Example: first fcTL for IDAT has seq=0, next is fcTL for seond frame with + // seq=1, then first fdAT with seq=2, fcTL seq=3, fdAT=4, ... + Result := SwapEndianLongWord(SeqNo); + Inc(SeqNo); + end; + + function CalcChunkCrc(const ChunkHdr: TChunkHeader; Data: Pointer; + Size: LongInt): LongWord; + begin + Result := $FFFFFFFF; + CalcCrc32(Result, @ChunkHdr.ChunkID, SizeOf(ChunkHdr.ChunkID)); + CalcCrc32(Result, Data, Size); + Result := SwapEndianLongWord(Result xor $FFFFFFFF); + end; + + procedure WriteChunk(var Chunk: TChunkHeader; ChunkData: Pointer); + var + ChunkCrc: LongWord; + SizeToWrite: LongInt; + begin + SizeToWrite := Chunk.DataSize; + Chunk.DataSize := SwapEndianLongWord(Chunk.DataSize); + ChunkCrc := CalcChunkCrc(Chunk, ChunkData, SizeToWrite); + GetIO.Write(Handle, @Chunk, SizeOf(Chunk)); + if SizeToWrite <> 0 then + GetIO.Write(Handle, ChunkData, SizeToWrite); + GetIO.Write(Handle, @ChunkCrc, SizeOf(ChunkCrc)); + end; + + procedure WritefdAT(Frame: TFrameInfo); + var + ChunkCrc: LongWord; + ChunkSeqNo: LongWord; + begin + Chunk.ChunkID := fdATChunk; + ChunkSeqNo := GetNextSeqNo; + // fdAT saves seq number LongWord before compressed pixels + Chunk.DataSize := Frame.IDATMemory.Size + SizeOf(LongWord); + Chunk.DataSize := SwapEndianLongWord(Chunk.DataSize); + // Calc CRC + ChunkCrc := $FFFFFFFF; + CalcCrc32(ChunkCrc, @Chunk.ChunkID, SizeOf(Chunk.ChunkID)); + CalcCrc32(ChunkCrc, @ChunkSeqNo, SizeOf(ChunkSeqNo)); + CalcCrc32(ChunkCrc, Frame.IDATMemory.Memory, Frame.IDATMemory.Size); + ChunkCrc := SwapEndianLongWord(ChunkCrc xor $FFFFFFFF); + // Write out all fdAT data + GetIO.Write(Handle, @Chunk, SizeOf(Chunk)); + GetIO.Write(Handle, @ChunkSeqNo, SizeOf(ChunkSeqNo)); + GetIO.Write(Handle, Frame.IDATMemory.Memory, Frame.IDATMemory.Size); + GetIO.Write(Handle, @ChunkCrc, SizeOf(ChunkCrc)); + end; + + procedure WriteGlobalMetaDataChunks(Frame: TFrameInfo); + var + XRes, YRes: Single; + begin + if FileFormat.FMetadata.GetPhysicalPixelSize(ruDpm, XRes, YRes, True) then + begin + // Save pHYs chunk + Frame.pHYs.UnitSpecifier := 1; + // PNG stores physical resolution as dots per meter + Frame.pHYs.PixelsPerUnitX := Round(XRes); + Frame.pHYs.PixelsPerUnitY := Round(YRes); + + Chunk.DataSize := SizeOf(Frame.pHYs); + Chunk.ChunkID := pHYsChunk; + SwapEndianLongWord(@Frame.pHYs, SizeOf(Frame.pHYs) div SizeOf(LongWord)); + WriteChunk(Chunk, @Frame.pHYs); + end; + end; + + procedure WritePNGMainImageChunks(Frame: TFrameInfo); + begin + with Frame do + begin + // Write IHDR chunk + Chunk.DataSize := SizeOf(IHDR); + Chunk.ChunkID := IHDRChunk; + WriteChunk(Chunk, @IHDR); + // Write PLTE chunk if data is present + if Palette <> nil then + begin + Chunk.DataSize := PaletteEntries * SizeOf(TColor24Rec); + Chunk.ChunkID := PLTEChunk; + WriteChunk(Chunk, Palette); + end; + // Write tRNS chunk if data is present + if Transparency <> nil then + begin + Chunk.DataSize := TransparencySize; + Chunk.ChunkID := tRNSChunk; + WriteChunk(Chunk, Transparency); + end; + end; + // Write metadata related chunks + WriteGlobalMetaDataChunks(Frame); + end; + +begin + Result := False; + SeqNo := 0; + + case FileType of + ngPNG, ngAPNG: GetIO.Write(Handle, @PNGSignature, SizeOf(TChar8)); + ngMNG: GetIO.Write(Handle, @MNGSignature, SizeOf(TChar8)); + ngJNG: GetIO.Write(Handle, @JNGSignature, SizeOf(TChar8)); + end; + + if FileType = ngMNG then + begin + // MNG - main header before frames + SwapEndianLongWord(@MHDR, SizeOf(MHDR) div SizeOf(LongWord)); + Chunk.DataSize := SizeOf(MHDR); + Chunk.ChunkID := MHDRChunk; + WriteChunk(Chunk, @MHDR); + end + else if FileType = ngAPNG then + begin + // APNG - IHDR and global chunks for all frames, then acTL chunk, then frames + // (fcTL+IDAT, fcTL+fdAT, fcTL+fdAT, fcTL+fdAT, ....) + WritePNGMainImageChunks(Frames[0]); + + // Animation control chunk + acTL.NumFrames := Length(Frames); + if FileFormat.FMetadata.HasMetaItemForSaving(SMetaAnimationLoops) then + begin + // Number of plays of APNG animation + acTL.NumPlay:= FileFormat.FMetadata.MetaItemsForSaving[SMetaAnimationLoops]; + end + else + acTL.NumPlay := 0; + SwapEndianLongWord(@acTL, SizeOf(acTL) div SizeOf(LongWord)); + + Chunk.DataSize := SizeOf(acTL); + Chunk.ChunkID := acTLChunk; + WriteChunk(Chunk, @acTL); + end; + + for I := 0 to Length(Frames) - 1 do + with Frames[I] do + begin + if IsJpegFrame then + begin + // Write JHDR chunk + Chunk.DataSize := SizeOf(JHDR); + Chunk.ChunkID := JHDRChunk; + WriteChunk(Chunk, @JHDR); + // Write metadata related chunks + WriteGlobalMetaDataChunks(Frames[I]); + // Write JNG image data + Chunk.DataSize := JDATMemory.Size; + Chunk.ChunkID := JDATChunk; + WriteChunk(Chunk, JDATMemory.Memory); + // Write alpha channel if present + if JHDR.AlphaSampleDepth > 0 then + begin + if JHDR.AlphaCompression = 0 then + begin + // Alpha is PNG compressed + Chunk.DataSize := IDATMemory.Size; + Chunk.ChunkID := IDATChunk; + WriteChunk(Chunk, IDATMemory.Memory); + end + else + begin + // Alpha is JNG compressed + Chunk.DataSize := JDAAMemory.Size; + Chunk.ChunkID := JDAAChunk; + WriteChunk(Chunk, JDAAMemory.Memory); + end; + end; + // Write image end + Chunk.DataSize := 0; + Chunk.ChunkID := IENDChunk; + WriteChunk(Chunk, nil); + end + else if FileType <> ngAPNG then + begin + // Regular PNG frame (single PNG image or MNG frame) + WritePNGMainImageChunks(Frames[I]); + // Write PNG image data + Chunk.DataSize := IDATMemory.Size; + Chunk.ChunkID := IDATChunk; + WriteChunk(Chunk, IDATMemory.Memory); + // Write image end + Chunk.DataSize := 0; + Chunk.ChunkID := IENDChunk; + WriteChunk(Chunk, nil); + end + else if FileType = ngAPNG then + begin + // APNG frame - Write fcTL before frame data + Chunk.DataSize := SizeOf(fcTL); + Chunk.ChunkID := fcTLChunk; + fcTl.SeqNumber := GetNextSeqNo; + WriteChunk(Chunk, @fcTL); + // Write data - IDAT for first frame and fdAT for following ones + if I = 0 then + begin + Chunk.DataSize := IDATMemory.Size; + Chunk.ChunkID := IDATChunk; + WriteChunk(Chunk, IDATMemory.Memory); + end + else + WritefdAT(Frames[I]); + // Write image end after last frame + if I = Length(Frames) - 1 then + begin + Chunk.DataSize := 0; + Chunk.ChunkID := IENDChunk; + WriteChunk(Chunk, nil); + end; + end; + end; + + if FileType = ngMNG then + begin + Chunk.DataSize := 0; + Chunk.ChunkID := MENDChunk; + WriteChunk(Chunk, nil); + end; +end; + +procedure TNGFileSaver.SetFileOptions; +begin + PreFilter := FileFormat.FPreFilter; + CompressLevel := FileFormat.FCompressLevel; + LossyAlpha := FileFormat.FLossyAlpha; + Quality := FileFormat.FQuality; + Progressive := FileFormat.FProgressive; + ZLibStrategy := FileFormat.FZLibStategy; +end; + +{ TAPNGAnimator class implementation } + +class procedure TAPNGAnimator.Animate(var Images: TDynImageDataArray; + const acTL: TacTL; const SrcFrames: array of TFrameInfo); +var + I, SrcIdx, Offset, Len: Integer; + DestFrames: TDynImageDataArray; + SrcCanvas, DestCanvas: TImagingCanvas; + PreviousCache: TImageData; + DestFormat: TImageFormat; + FormatInfo: TImageFormatInfo; + AnimatingNeeded, BlendingNeeded: Boolean; + + procedure CheckFrames; + var + I: Integer; + begin + for I := 0 to Len - 1 do + with SrcFrames[I] do + begin + if (FrameWidth <> Integer(IHDR.Width)) or (FrameHeight <> Integer(IHDR.Height)) or (Len <> Integer(acTL.NumFrames)) or + (not ((fcTL.DisposeOp = DisposeOpNone) and (fcTL.BlendOp = BlendOpSource)) and + not ((fcTL.DisposeOp = DisposeOpBackground) and (fcTL.BlendOp = BlendOpSource)) and + not ((fcTL.DisposeOp = DisposeOpBackground) and (fcTL.BlendOp = BlendOpOver))) then + begin + AnimatingNeeded := True; + end; + + if fcTL.BlendOp = BlendOpOver then + BlendingNeeded := True; + + if AnimatingNeeded and BlendingNeeded then + Exit; + end; + end; + +begin + AnimatingNeeded := False; + BlendingNeeded := False; + Len := Length(SrcFrames); + + CheckFrames; + + if (Len = 0) or not AnimatingNeeded then + Exit; + + if (Len = Integer(acTL.NumFrames) + 1) and (SrcFrames[0].fcTL.Width = 0) then + begin + // If default image (stored in IDAT chunk) isn't part of animation we ignore it + Offset := 1; + Len := Len - 1; + end + else + Offset := 0; + + DestFormat := Images[0].Format; + GetImageFormatInfo(DestFormat, FormatInfo); + if BlendingNeeded and FormatInfo.IsIndexed then // alpha blending needed -> destination cannot be indexed + DestFormat := ifA8R8G8B8; + + SetLength(DestFrames, Len); + DestCanvas := ImagingCanvases.FindBestCanvasForImage(DestFormat).Create; + SrcCanvas := ImagingCanvases.FindBestCanvasForImage(Images[0]).Create; + InitImage(PreviousCache); + NewImage(SrcFrames[0].IHDR.Width, SrcFrames[0].IHDR.Height, DestFormat, PreviousCache); + + for I := 0 to Len - 1 do + begin + SrcIdx := I + Offset; + + NewImage(SrcFrames[SrcIdx].IHDR.Width, SrcFrames[SrcIdx].IHDR.Height, + DestFormat, DestFrames[I]); + if DestFrames[I].Format = ifIndex8 then + Move(Images[SrcIdx].Palette^, DestFrames[I].Palette^, 256 * SizeOf(TColor32)); + + DestCanvas.CreateForData(@DestFrames[I]); + + if (SrcFrames[SrcIdx].fcTL.DisposeOp = DisposeOpPrevious) and (SrcFrames[SrcIdx - 1].fcTL.DisposeOp <> DisposeOpPrevious) then + begin + // Cache current output buffer so we may return to it later (previous dispose op) + CopyRect(DestFrames[I - 1], 0, 0, DestFrames[I - 1].Width, DestFrames[I - 1].Height, + PreviousCache, 0, 0); + end; + + if (I = 0) or (SrcIdx = 0) then + begin + // Clear whole frame with transparent black color (default for first frame) + DestCanvas.FillColor32 := pcClear; + DestCanvas.Clear; + end + else if SrcFrames[SrcIdx - 1].fcTL.DisposeOp = DisposeOpBackground then + begin + // Restore background color (clear) on previous frame's area and leave previous content outside of it + CopyRect(DestFrames[I - 1], 0, 0, DestFrames[I - 1].Width, DestFrames[I - 1].Height, + DestFrames[I], 0, 0); + DestCanvas.FillColor32 := pcClear; + DestCanvas.FillRect(BoundsToRect(SrcFrames[SrcIdx - 1].fcTL.XOffset, SrcFrames[SrcIdx - 1].fcTL.YOffset, + SrcFrames[SrcIdx - 1].FrameWidth, SrcFrames[SrcIdx - 1].FrameHeight)); + end + else if SrcFrames[SrcIdx - 1].fcTL.DisposeOp = DisposeOpNone then + begin + // Clone previous frame - no change to output buffer + CopyRect(DestFrames[I - 1], 0, 0, DestFrames[I - 1].Width, DestFrames[I - 1].Height, + DestFrames[I], 0, 0); + end + else if SrcFrames[SrcIdx - 1].fcTL.DisposeOp = DisposeOpPrevious then + begin + // Revert to previous frame (cached, can't just restore DestFrames[I - 2]) + CopyRect(PreviousCache, 0, 0, PreviousCache.Width, PreviousCache.Height, + DestFrames[I], 0, 0); + end; + + // Copy pixels or alpha blend them over + if SrcFrames[SrcIdx].fcTL.BlendOp = BlendOpSource then + begin + CopyRect(Images[SrcIdx], 0, 0, Images[SrcIdx].Width, Images[SrcIdx].Height, + DestFrames[I], SrcFrames[SrcIdx].fcTL.XOffset, SrcFrames[SrcIdx].fcTL.YOffset); + end + else if SrcFrames[SrcIdx].fcTL.BlendOp = BlendOpOver then + begin + SrcCanvas.CreateForData(@Images[SrcIdx]); + SrcCanvas.DrawAlpha(SrcCanvas.ClipRect, DestCanvas, + SrcFrames[SrcIdx].fcTL.XOffset, SrcFrames[SrcIdx].fcTL.YOffset); + end; + + FreeImage(Images[SrcIdx]); + end; + + DestCanvas.Free; + SrcCanvas.Free; + FreeImage(PreviousCache); + + // Assign dest frames to final output images + Images := DestFrames; +end; + +{ TNetworkGraphicsFileFormat class implementation } + +procedure TNetworkGraphicsFileFormat.Define; +begin + inherited; + FFeatures := [ffLoad, ffSave]; + + FPreFilter := NGDefaultPreFilter; + FCompressLevel := NGDefaultCompressLevel; + FLossyAlpha := NGDefaultLossyAlpha; + FLossyCompression := NGDefaultLossyCompression; + FQuality := NGDefaultQuality; + FProgressive := NGDefaultProgressive; + FZLibStategy := NGDefaultZLibStartegy; +end; + +procedure TNetworkGraphicsFileFormat.CheckOptionsValidity; +begin + // Just check if save options has valid values + if not (FPreFilter in [0..6]) then + FPreFilter := NGDefaultPreFilter; + if not (FCompressLevel in [0..9]) then + FCompressLevel := NGDefaultCompressLevel; + if not (FQuality in [1..100]) then + FQuality := NGDefaultQuality; +end; + +function TNetworkGraphicsFileFormat.GetSupportedFormats: TImageFormats; +begin + if FLossyCompression then + Result := NGLossyFormats + else + Result := NGLosslessFormats; +end; + +procedure TNetworkGraphicsFileFormat.ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); +var + ConvFormat: TImageFormat; +begin + if not FLossyCompression then + begin + // Convert formats for lossless compression + if Info.HasGrayChannel then + begin + if Info.HasAlphaChannel then + begin + if Info.BytesPerPixel <= 2 then + // Convert <= 16bit grayscale images with alpha to ifA8Gray8 + ConvFormat := ifA8Gray8 + else + // Convert > 16bit grayscale images with alpha to ifA16Gray16 + ConvFormat := ifA16Gray16 + end + else + // Convert grayscale images without alpha to ifGray16 + ConvFormat := ifGray16; + end + else + if Info.IsFloatingPoint then + // Convert floating point images to 64 bit ARGB (or RGB if no alpha) + ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16B16G16R16, ifB16G16R16) + else if Info.HasAlphaChannel or Info.IsSpecial then + // Convert all other images with alpha or special images to A8R8G8B8 + ConvFormat := ifA8R8G8B8 + else + // Convert images without alpha to R8G8B8 + ConvFormat := ifR8G8B8; + end + else + begin + // Convert formats for lossy compression + if Info.HasGrayChannel then + ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8Gray8, ifGray8) + else + ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifR8G8B8); + end; + + ConvertImage(Image, ConvFormat); +end; + +function TNetworkGraphicsFileFormat.TestFormat(Handle: TImagingHandle): Boolean; +var + ReadCount: LongInt; + Sig: TChar8; +begin + Result := False; + if Handle <> nil then + with GetIO do + begin + FillChar(Sig, SizeOf(Sig), 0); + ReadCount := Read(Handle, @Sig, SizeOf(Sig)); + Seek(Handle, -ReadCount, smFromCurrent); + Result := (ReadCount = SizeOf(Sig)) and (Sig = FSignature); + end; +end; + +{ TPNGFileFormat class implementation } + +procedure TPNGFileFormat.Define; +begin + inherited; + FName := SPNGFormatName; + FFeatures := FFeatures + [ffMultiImage]; + FLoadAnimated := PNGDefaultLoadAnimated; + AddMasks(SPNGMasks); + + FSignature := PNGSignature; + + RegisterOption(ImagingPNGPreFilter, @FPreFilter); + RegisterOption(ImagingPNGCompressLevel, @FCompressLevel); + RegisterOption(ImagingPNGLoadAnimated, @FLoadAnimated); + RegisterOption(ImagingPNGZLibStrategy, @FZLibStategy); +end; + +function TPNGFileFormat.LoadData(Handle: TImagingHandle; + var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; +var + I, Len: LongInt; + NGFileLoader: TNGFileLoader; +begin + Result := False; + NGFileLoader := TNGFileLoader.Create(Self); + try + // Use NG file parser to load file + if NGFileLoader.LoadFile(Handle) and (Length(NGFileLoader.Frames) > 0) then + begin + Len := Length(NGFileLoader.Frames); + SetLength(Images, Len); + for I := 0 to Len - 1 do + with NGFileLoader.Frames[I] do + begin + // Build actual image bits + if not IsJpegFrame then + NGFileLoader.LoadImageFromPNGFrame(FrameWidth, FrameHeight, IHDR, IDATMemory, Images[I]); + // Build palette, aply color key or background + + NGFileLoader.ApplyFrameSettings(NGFileLoader.Frames[I], Images[I]); + Result := True; + end; + // Animate APNG images + if (NGFileLoader.FileType = ngAPNG) and FLoadAnimated then + TAPNGAnimator.Animate(Images, NGFileLoader.acTL, NGFileLoader.Frames); + end; + finally + NGFileLoader.LoadMetaData; // Store metadata + NGFileLoader.Free; + end; +end; + +function TPNGFileFormat.SaveData(Handle: TImagingHandle; + const Images: TDynImageDataArray; Index: LongInt): Boolean; +var + I: Integer; + ImageToSave: TImageData; + MustBeFreed: Boolean; + NGFileSaver: TNGFileSaver; + DefaultFormat: TImageFormat; + Screen: TImageData; + AnimWidth, AnimHeight: Integer; +begin + Result := False; + DefaultFormat := ifDefault; + AnimWidth := 0; + AnimHeight := 0; + NGFileSaver := TNGFileSaver.Create(Self); + + // Save images with more frames as APNG format + if Length(Images) > 1 then + begin + NGFileSaver.FileType := ngAPNG; + // Get max dimensions of frames + AnimWidth := Images[FFirstIdx].Width; + AnimHeight := Images[FFirstIdx].Height; + for I := FFirstIdx + 1 to FLastIdx do + begin + AnimWidth := Max(AnimWidth, Images[I].Width); + AnimHeight := Max(AnimHeight, Images[I].Height); + end; + end + else + NGFileSaver.FileType := ngPNG; + + NGFileSaver.SetFileOptions; + + with NGFileSaver do + try + // Store all frames to be saved frames file saver + for I := FFirstIdx to FLastIdx do + begin + if MakeCompatible(Images[I], ImageToSave, MustBeFreed) then + try + if FileType = ngAPNG then + begin + // IHDR chunk is shared for all frames so all frames must have the + // same data format as the first image. + if I = FFirstIdx then + begin + DefaultFormat := ImageToSave.Format; + // Subsequenet frames may be bigger than the first one. + // APNG doens't support this - max allowed size is what's written in + // IHDR - size of main/default/first image. If some frame is + // bigger than the first one we need to resize (create empty bigger + // image and copy) the first frame so all following frames could fit to + // its area. + if (ImageToSave.Width <> AnimWidth) or (ImageToSave.Height <> AnimHeight) then + begin + InitImage(Screen); + NewImage(AnimWidth, AnimHeight, ImageToSave.Format, Screen); + CopyRect(ImageToSave, 0, 0, ImageToSave.Width, ImageToSave.Height, Screen, 0, 0); + if MustBeFreed then + FreeImage(ImageToSave); + ImageToSave := Screen; + end; + end + else if ImageToSave.Format <> DefaultFormat then + begin + if MustBeFreed then + ConvertImage(ImageToSave, DefaultFormat) + else + begin + CloneImage(Images[I], ImageToSave); + ConvertImage(ImageToSave, DefaultFormat); + MustBeFreed := True; + end; + end; + end; + + // Add image as PNG frame + AddFrame(ImageToSave, False); + finally + if MustBeFreed then + FreeImage(ImageToSave); + end + else + Exit; + end; + + // Finally save PNG file + SaveFile(Handle); + Result := True; + finally + NGFileSaver.Free; + end; +end; + +{$IFNDEF DONT_LINK_MNG} + +{ TMNGFileFormat class implementation } + +procedure TMNGFileFormat.Define; +begin + inherited; + FName := SMNGFormatName; + FFeatures := FFeatures + [ffMultiImage]; + AddMasks(SMNGMasks); + + FSignature := MNGSignature; + + RegisterOption(ImagingMNGLossyCompression, @FLossyCompression); + RegisterOption(ImagingMNGLossyAlpha, @FLossyAlpha); + RegisterOption(ImagingMNGPreFilter, @FPreFilter); + RegisterOption(ImagingMNGCompressLevel, @FCompressLevel); + RegisterOption(ImagingMNGQuality, @FQuality); + RegisterOption(ImagingMNGProgressive, @FProgressive); +end; + +function TMNGFileFormat.LoadData(Handle: TImagingHandle; + var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; +var + NGFileLoader: TNGFileLoader; + I, Len: LongInt; +begin + Result := False; + NGFileLoader := TNGFileLoader.Create(Self); + try + // Use NG file parser to load file + if NGFileLoader.LoadFile(Handle) then + begin + Len := Length(NGFileLoader.Frames); + if Len > 0 then + begin + SetLength(Images, Len); + for I := 0 to Len - 1 do + with NGFileLoader.Frames[I] do + begin + // Build actual image bits + if IsJpegFrame then + NGFileLoader.LoadImageFromJNGFrame(FrameWidth, FrameHeight, JHDR, IDATMemory, JDATMemory, JDAAMemory, Images[I]) + else + NGFileLoader.LoadImageFromPNGFrame(FrameWidth, FrameHeight, IHDR, IDATMemory, Images[I]); + // Build palette, aply color key or background + NGFileLoader.ApplyFrameSettings(NGFileLoader.Frames[I], Images[I]); + end; + end + else + begin + // Some MNG files (with BASI-IEND streams) dont have actual pixel data + SetLength(Images, 1); + NewImage(NGFileLoader.MHDR.FrameWidth, NGFileLoader.MHDR.FrameWidth, ifDefault, Images[0]); + end; + Result := True; + end; + finally + NGFileLoader.LoadMetaData; // Store metadata + NGFileLoader.Free; + end; +end; + +function TMNGFileFormat.SaveData(Handle: TImagingHandle; + const Images: TDynImageDataArray; Index: LongInt): Boolean; +var + NGFileSaver: TNGFileSaver; + I, LargestWidth, LargestHeight: LongInt; + ImageToSave: TImageData; + MustBeFreed: Boolean; +begin + Result := False; + LargestWidth := 0; + LargestHeight := 0; + + NGFileSaver := TNGFileSaver.Create(Self); + NGFileSaver.FileType := ngMNG; + NGFileSaver.SetFileOptions; + + with NGFileSaver do + try + // Store all frames to be saved frames file saver + for I := FFirstIdx to FLastIdx do + begin + if MakeCompatible(Images[I], ImageToSave, MustBeFreed) then + try + // Add image as PNG or JNG frame + AddFrame(ImageToSave, FLossyCompression); + // Remember largest frame width and height + LargestWidth := Iff(LargestWidth < ImageToSave.Width, ImageToSave.Width, LargestWidth); + LargestHeight := Iff(LargestHeight < ImageToSave.Height, ImageToSave.Height, LargestHeight); + finally + if MustBeFreed then + FreeImage(ImageToSave); + end + else + Exit; + end; + + // Fill MNG header + MHDR.FrameWidth := LargestWidth; + MHDR.FrameHeight := LargestHeight; + MHDR.TicksPerSecond := 0; + MHDR.NominalLayerCount := 0; + MHDR.NominalFrameCount := Length(Frames); + MHDR.NominalPlayTime := 0; + MHDR.SimplicityProfile := 473; // 111011001 binary, defines MNG-VLC with transparency and JNG support + + // Finally save MNG file + SaveFile(Handle); + Result := True; + finally + NGFileSaver.Free; + end; +end; + +{$ENDIF} + +{$IFNDEF DONT_LINK_JNG} + +{ TJNGFileFormat class implementation } + +procedure TJNGFileFormat.Define; +begin + inherited; + FName := SJNGFormatName; + AddMasks(SJNGMasks); + + FSignature := JNGSignature; + FLossyCompression := True; + + RegisterOption(ImagingJNGLossyAlpha, @FLossyAlpha); + RegisterOption(ImagingJNGAlphaPreFilter, @FPreFilter); + RegisterOption(ImagingJNGAlphaCompressLevel, @FCompressLevel); + RegisterOption(ImagingJNGQuality, @FQuality); + RegisterOption(ImagingJNGProgressive, @FProgressive); + +end; + +function TJNGFileFormat.LoadData(Handle: TImagingHandle; + var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; +var + NGFileLoader: TNGFileLoader; +begin + Result := False; + NGFileLoader := TNGFileLoader.Create(Self); + try + // Use NG file parser to load file + if NGFileLoader.LoadFile(Handle) and (Length(NGFileLoader.Frames) > 0) then + with NGFileLoader.Frames[0] do + begin + SetLength(Images, 1); + // Build actual image bits + if IsJpegFrame then + NGFileLoader.LoadImageFromJNGFrame(FrameWidth, FrameHeight, JHDR, IDATMemory, JDATMemory, JDAAMemory, Images[0]); + // Build palette, aply color key or background + NGFileLoader.ApplyFrameSettings(NGFileLoader.Frames[0], Images[0]); + Result := True; + end; + finally + NGFileLoader.LoadMetaData; // Store metadata + NGFileLoader.Free; + end; +end; + +function TJNGFileFormat.SaveData(Handle: TImagingHandle; + const Images: TDynImageDataArray; Index: LongInt): Boolean; +var + NGFileSaver: TNGFileSaver; + ImageToSave: TImageData; + MustBeFreed: Boolean; +begin + // Make image JNG compatible, store it in saver, and save it to file + Result := MakeCompatible(Images[Index], ImageToSave, MustBeFreed); + if Result then + begin + NGFileSaver := TNGFileSaver.Create(Self); + with NGFileSaver do + try + FileType := ngJNG; + SetFileOptions; + AddFrame(ImageToSave, True); + SaveFile(Handle); + finally + // Free NG saver and compatible image + NGFileSaver.Free; + if MustBeFreed then + FreeImage(ImageToSave); + end; + end; +end; + +{$ENDIF} + +initialization + RegisterImageFileFormat(TPNGFileFormat); +{$IFNDEF DONT_LINK_MNG} + RegisterImageFileFormat(TMNGFileFormat); +{$ENDIF} +{$IFNDEF DONT_LINK_JNG} + RegisterImageFileFormat(TJNGFileFormat); +{$ENDIF} +finalization + +{ + File Notes: + + -- TODOS ---------------------------------------------------- + - nothing now + + -- 0.77 Changes/Bug Fixes ----------------------------------- + - Reads and writes APNG animation loop count metadata. + - Writes frame delays of APNG from metadata. + - Fixed color keys in 8bit depth PNG/MNG loading. + - Fixed needless (and sometimes buggy) conversion to format with alpha + channel in FPC (GetMem(0) <> nil!). + - Added support for optional ZLib compression strategy. + - Added loading and saving of ifBinary (1bit black and white) + format images. During loading grayscale 1bpp and indexed 1bpp + (with only black and white colors in palette) are treated as ifBinary. + ifBinary are saved as 1bpp grayscale PNGs. + + -- 0.26.5 Changes/Bug Fixes --------------------------------- + - Reads frame delays from APNG files into metadata. + - Added loading and saving of metadata from these chunks: pHYs. + - Simplified decoding of 1/2/4 bit images a bit (less code). + + -- 0.26.3 Changes/Bug Fixes --------------------------------- + - Added APNG saving support. + - Added APNG support to NG loader and animating to PNG loader. + + -- 0.26.1 Changes/Bug Fixes --------------------------------- + - Changed file format conditional compilation to reflect changes + in LINK symbols. + + -- 0.24.3 Changes/Bug Fixes --------------------------------- + - Changes for better thread safety. + + -- 0.23 Changes/Bug Fixes ----------------------------------- + - Added loading of global palettes and transparencies in MNG files + (and by doing so fixed crash when loading images with global PLTE or tRNS). + + -- 0.21 Changes/Bug Fixes ----------------------------------- + - Small changes in converting to supported formats. + - MakeCompatible method moved to base class, put ConvertToSupported here. + GetSupportedFormats removed, it is now set in constructor. + - Made public properties for options registered to SetOption/GetOption + functions. + - Changed extensions to filename masks. + - Changed SaveData, LoadData, and MakeCompatible methods according + to changes in base class in Imaging unit. + + -- 0.17 Changes/Bug Fixes ----------------------------------- + - MNG and JNG support added, PNG support redesigned to support NG file handlers + - added classes for working with NG file formats + - stuff from old ImagingPng unit added and that unit was deleted + - unit created and initial stuff added + + -- 0.15 Changes/Bug Fixes ----------------------------------- + - when saving indexed images save alpha to tRNS? + - added some defines and ifdefs to dzlib unit to allow choosing + impaszlib, fpc's paszlib, zlibex or other zlib implementation + - added colorkeying support + - fixed 16bit channel image handling - pixels were not swapped + - fixed arithmetic overflow (in paeth filter) in FPC + - data of unknown chunks are skipped and not needlesly loaded + + -- 0.13 Changes/Bug Fixes ----------------------------------- + - adaptive filtering added to PNG saving + - TPNGFileFormat class added +} + +end. diff --git a/resources/libraries/deskew/Imaging/ImagingOptions.inc b/resources/libraries/deskew/Imaging/ImagingOptions.inc new file mode 100755 index 0000000..018349e --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingOptions.inc @@ -0,0 +1,219 @@ +{ + User Options + Following defines and options can be changed by user. +} + +{ Source options } + +{$DEFINE USE_INLINE} // Use function inlining for some functions + // works in Free Pascal and Delphi 9+. +{$DEFINE USE_ASM} // Ff defined, assembler versions of some + // functions will be used (only for x86). + + // Debug options: If none of these two are defined + // your project settings are used. +{ $DEFINE IMAGING_DEBUG} // If defined, debug info, range/IO/overflow + // checking, stack frames, assertions, and + // other debugging options will be turned on. +{ $DEFINE IMAGING_RELEASE} // If defined, all debug info is off. + + + +(* File format support linking options. + Define formats which you don't want to be registred automatically (by adding + Imaging.pas unit to your uses clause). + Default: all formats are registered = no symbols defined. + Example: If you want to disable JPEG support just uncomment //{$DEFINE DONT_LINK_JPEG} line +*) + +//{$DEFINE DONT_LINK_JPEG} // link support for Jpeg images +//{$DEFINE DONT_LINK_PNG} // link support for PNG images +//{$DEFINE DONT_LINK_TARGA} // link support for Targa images +//{$DEFINE DONT_LINK_BITMAP} // link support for Windows Bitmap images +//{$DEFINE DONT_LINK_DDS} // link support for DDS images +//{$DEFINE DONT_LINK_GIF} // link support for GIF images +{$DEFINE DONT_LINK_MNG} // link support for MNG images +//{$DEFINE DONT_LINK_JNG} // link support for JNG images +//{$DEFINE DONT_LINK_PNM} // link support for PortableMap images (PBM, PGM, PPM, PAM, PFM) +{$DEFINE DONT_LINK_RADHDR} // link support for Radiance HDR/RGBE file format + +{$DEFINE DONT_LINK_EXTRAS} // link support for file formats defined in + // Extras package. Exactly which formats will be + // registered depends on settings in + // ImagingExtras.pas unit. + +{.$DEFINE DONT_LINK_FILE_FORMATS} // no auto link support of any file format + +{ Component set used in ImagignComponents.pas unit. You usually don't need + to be concerned with this - proper component library is selected automatically + according to your compiler. } + +{$DEFINE COMPONENT_SET_VCL} // use Delphi VCL +{ $DEFINE COMPONENT_SET_LCL} // use Lazarus LCL (set automatically when compiling with FPC) + +{ + Auto Options + Following options and defines are set automatically and some + are required for Imaging to compile successfully. Do not change + anything here if you don't know what you are doing. +} + +{ Compiler options } + +{$ALIGN ON} // Field alignment: 8 Bytes (in D6+) +{$BOOLEVAL OFF} // Boolean eval: off +{$EXTENDEDSYNTAX ON} // Extended syntax: on +{$LONGSTRINGS ON} // string = AnsiString: on +{$MINENUMSIZE 4} // Min enum size: 4 B +{$TYPEDADDRESS OFF} // Typed pointers: off +{$WRITEABLECONST OFF} // Writeable constants: off + +{$IFNDEF FPC} + {$DEFINE DCC} // if not using FPC then DCC compiler is used (Delphi/BCB) + // others are not supported +{$ENDIF} + +{$IFDEF DCC} + {$DEFINE DELPHI} +{$ENDIF} + +{$IF (Defined(DCC) and (CompilerVersion >= 18.5))} + {$IFDEF RELEASE} + {$UNDEF DEBUG} // If we are using Delphi 2007+ where you can set + // DEBUG/RELEASE mode in project options and RELEASE + // is currently set we undef DEBUG mode + {$ENDIF} +{$IFEND} + +{$IF Defined(IMAGING_DEBUG)} + {$ASSERTIONS ON} + {$DEBUGINFO ON} + {$RANGECHECKS ON} + {$IOCHECKS ON} + {$OVERFLOWCHECKS ON} + {$IFDEF DCC} + {$OPTIMIZATION OFF} + {$STACKFRAMES ON} + {$LOCALSYMBOLS ON} + {$DEFINE MEMCHECK} + {$ENDIF} + {$IFDEF FPC} + {$S+} + {$CHECKPOINTER ON} + {$ENDIF} +{$ELSEIF Defined(IMAGING_RELEASE)} + {$ASSERTIONS OFF} + {$DEBUGINFO OFF} + {$RANGECHECKS OFF} + {$IOCHECKS OFF} + {$OVERFLOWCHECKS OFF} + {$IFDEF DCC} + {$OPTIMIZATION ON} + {$STACKFRAMES OFF} + {$LOCALSYMBOLS OFF} + {$ENDIF} + {$IFDEF FPC} + {$S-} + {$ENDIF} +{$IFEND} + +{$IF Defined (CPU86) and not Defined(CPUX86)} + {$DEFINE CPUX86} // Compatibility with Delphi +{$IFEND} + +{$IF Defined (CPUX86_64) and not Defined(CPUX64)} + {$DEFINE CPUX64} // Compatibility with Delphi +{$IFEND} + +{$IF Defined (DARWIN) and not Defined(MACOS)} + {$DEFINE MACOS} // Compatibility with Delphi +{$IFEND} +{$IF Defined(MACOS)} + {$DEFINE MACOSX} +{$IFEND} + +{$IF Defined(DCC) and (CompilerVersion < 23)} + {$DEFINE CPUX86} // Compatibility with older Delphi +{$IFEND} + +{$IF Defined(WIN32) or Defined(WIN64)} + {$DEFINE MSWINDOWS} // Compatibility with Delphi +{$IFEND} + +{$IF Defined(UNIX) and not Defined(POSIX)} + {$DEFINE POSIX} // Compatibility with Delphi +{$IFEND} + +{ Compiler capabilities } + +// Define if compiler supports inlining of functions and procedures +{$IF (Defined(DCC) and (CompilerVersion >= 17)) or Defined(FPC)} + {$DEFINE HAS_INLINE} +{$IFEND} + +// Define if compiler supports advanced records with methods +{$IF (Defined(DCC) and (CompilerVersion >= 18)) or + (Defined(FPC) and (FPC_FULLVERSION >= 20600))} + {$DEFINE HAS_ADVANCED_RECORDS} +{$IFEND} + +// Define if compiler supports operator overloading +// (unfortunately Delphi and FPC operator overloading is not compatible). +// FPC supports Delphi compatible operator overloads since 2.6.0 +{$IF (Defined(DCC) and (CompilerVersion >= 18)) or + (Defined(FPC) and (FPC_FULLVERSION >= 20600))} + {$DEFINE HAS_OPERATOR_OVERLOADING} +{$IFEND} + +// Anonymous methods +{$IF Defined(DCC) and (CompilerVersion >= 20) } + {$DEFINE HAS_ANON_METHODS} +{$IFEND} + +// Generic types (Delphi and FPC implementations incompatible). +// Update: FPC supports Delphi compatible generics since 2.6.0 +{$IF (Defined(DCC) and (CompilerVersion >= 20)) or + (Defined(FPC) and (FPC_FULLVERSION >= 20600))} + {$DEFINE HAS_GENERICS} +{$IFEND} + +{ Imaging options check} + +{$IFNDEF HAS_INLINE} + {$UNDEF USE_INLINE} +{$ENDIF} + +{$IF not Defined(CPUX86)} + {$UNDEF USE_ASM} +{$IFEND} + +{$IFDEF FPC} + {$DEFINE COMPONENT_SET_LCL} + {$UNDEF COMPONENT_SET_VCL} +{$ENDIF} + +{$IFDEF DELPHI} + {$UNDEF COMPONENT_SET_LCL} + {$DEFINE COMPONENT_SET_VCL} +{$ENDIF} + +{ More compiler options } + +{$IFDEF FPC} // Free Pascal options - some options set above (like min enum size) + // are reset to defaults by setting {$MODE} so they are + // redeclared here + {$MODE DELPHI} // compatible with delphi + {$GOTO ON} // alow goto + {$PACKRECORDS 8} // same as ALING 8 for Delphi + {$PACKENUM 4} // Min enum size: 4 B + {$CALLING REGISTER} // default calling convention is register + {$IFDEF CPU86} + {$ASMMODE INTEL} // intel assembler mode + {$ENDIF} +{$ENDIF} + +{$IFDEF HAS_INLINE} + {$INLINE ON} // turns inlining on for compilers that support it +{$ENDIF} + + diff --git a/resources/libraries/deskew/Imaging/ImagingPortableMaps.pas b/resources/libraries/deskew/Imaging/ImagingPortableMaps.pas new file mode 100755 index 0000000..531c1b2 --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingPortableMaps.pas @@ -0,0 +1,977 @@ +{ + Vampyre Imaging Library + by Marek Mauder + http://imaginglib.sourceforge.net + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +{ This unit contains loader/saver for Portable Maps file format family (or PNM). + That includes PBM, PGM, PPM, PAM, and PFM formats.} +unit ImagingPortableMaps; + +{$I ImagingOptions.inc} + +interface + +uses + SysUtils, ImagingTypes, Imaging, ImagingFormats, ImagingUtility; + +type + { Types of pixels of PNM images.} + TTupleType = (ttInvalid, ttBlackAndWhite, ttGrayScale, ttRGB, ttBlackAndWhiteAlpha, + ttGrayScaleAlpha, ttRGBAlpha, ttGrayScaleFP, ttRGBFP); + + { Record with info about PNM image used in both loading and saving functions.} + TPortableMapInfo = record + Width: LongInt; + Height: LongInt; + FormatId: AnsiChar; + MaxVal: LongInt; + BitCount: LongInt; + Depth: LongInt; + TupleType: TTupleType; + Binary: Boolean; + HasPAMHeader: Boolean; + IsBigEndian: Boolean; + end; + + { Base class for Portable Map file formats (or Portable AnyMaps or PNM). + There are several types of PNM file formats that share common + (simple) structure. This class can actually load all supported PNM formats. + Saving is also done by this class but descendants (each for different PNM + format) control it.} + TPortableMapFileFormat = class(TImageFileFormat) + protected + FIdNumbers: TChar2; + FSaveBinary: LongBool; + FUSFormat: TFormatSettings; + procedure Define; override; + function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; + OnlyFirstLevel: Boolean): Boolean; override; + function SaveDataInternal(Handle: TImagingHandle; const Images: TDynImageDataArray; + Index: LongInt; var MapInfo: TPortableMapInfo): Boolean; + public + function TestFormat(Handle: TImagingHandle): Boolean; override; + published + { If set to True images will be saved in binary format. If it is False + they will be saved in text format (which could result in 5-10x bigger file). + Default is value True. Note that PAM and PFM files are always saved in binary.} + property SaveBinary: LongBool read FSaveBinary write FSaveBinary; + end; + + { Portable Bit Map is used to store monochrome 1bit images. Raster data + can be saved as text or binary data. Either way value of 0 represents white + and 1 is black. As Imaging does not have support for 1bit data formats + PBM images can be loaded but not saved. Loaded images are returned in + ifGray8 format (witch pixel values scaled from 1bit to 8bit).} + TPBMFileFormat = class(TPortableMapFileFormat) + protected + procedure Define; override; + end; + + { Portable Gray Map is used to store grayscale 8bit or 16bit images. + Raster data can be saved as text or binary data.} + TPGMFileFormat = class(TPortableMapFileFormat) + protected + procedure Define; override; + function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; + Index: LongInt): Boolean; override; + procedure ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); override; + end; + + { Portable Pixel Map is used to store RGB images with 8bit or 16bit channels. + Raster data can be saved as text or binary data.} + TPPMFileFormat = class(TPortableMapFileFormat) + protected + procedure Define; override; + function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; + Index: LongInt): Boolean; override; + procedure ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); override; + end; + + { Portable Arbitrary Map is format that can store image data formats + of PBM, PGM, and PPM formats with optional alpha channel. Raster data + can be stored only in binary format. All data formats supported + by this format are ifGray8, ifGray16, ifA8Gray8, ifA16Gray16, + ifR8G8B8, ifR16G16R16, ifA8R8G8B8, and ifA16R16G16B16.} + TPAMFileFormat = class(TPortableMapFileFormat) + protected + procedure Define; override; + function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; + Index: LongInt): Boolean; override; + procedure ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); override; + end; + + { Portable Float Map is unofficial extension of PNM format family which + can store images with floating point pixels. Raster data is saved in + binary format as array of IEEE 32 bit floating point numbers. One channel + or RGB images are supported by PFM format (so no alpha).} + TPFMFileFormat = class(TPortableMapFileFormat) + protected + procedure Define; override; + function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; + Index: LongInt): Boolean; override; + procedure ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); override; + end; + +implementation + +const + PortableMapDefaultBinary = True; + + SPBMFormatName = 'Portable Bit Map'; + SPBMMasks = '*.pbm'; + SPGMFormatName = 'Portable Gray Map'; + SPGMMasks = '*.pgm'; + PGMSupportedFormats = [ifGray8, ifGray16]; + SPPMFormatName = 'Portable Pixel Map'; + SPPMMasks = '*.ppm'; + PPMSupportedFormats = [ifR8G8B8, ifR16G16B16]; + SPAMFormatName = 'Portable Arbitrary Map'; + SPAMMasks = '*.pam'; + PAMSupportedFormats = [ifGray8, ifGray16, ifA8Gray8, ifA16Gray16, + ifR8G8B8, ifR16G16B16, ifA8R8G8B8, ifA16R16G16B16]; + SPFMFormatName = 'Portable Float Map'; + SPFMMasks = '*.pfm'; + PFMSupportedFormats = [ifR32F, ifB32G32R32F]; + +const + { TAB, CR, LF, and Space are used as seperators in Portable map headers and data.} + WhiteSpaces = [#9, #10, #13, #32]; + SPAMWidth = 'WIDTH'; + SPAMHeight = 'HEIGHT'; + SPAMDepth = 'DEPTH'; + SPAMMaxVal = 'MAXVAL'; + SPAMTupleType = 'TUPLTYPE'; + SPAMEndHdr = 'ENDHDR'; + + { Size of buffer used to speed up text PNM loading/saving.} + LineBufferCapacity = 16 * 1024; + + TupleTypeNames: array[TTupleType] of string = ( + 'INVALID', 'BLACKANDWHITE', 'GRAYSCALE', 'RGB', + 'BLACKANDWHITE_ALPHA', 'GRAYSCALE_ALPHA', 'RGB_ALPHA', 'GRAYSCALEFP', + 'RGBFP'); + +{ TPortableMapFileFormat } + +procedure TPortableMapFileFormat.Define; +begin + inherited; + FFeatures := [ffLoad, ffSave]; + FSaveBinary := PortableMapDefaultBinary; + FUSFormat := GetFormatSettingsForFloats; +end; + +function TPortableMapFileFormat.LoadData(Handle: TImagingHandle; + var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; +var + I, ScanLineSize, MonoSize: LongInt; + Dest: PByte; + MonoData: Pointer; + Info: TImageFormatInfo; + LineBuffer: array[0..LineBufferCapacity - 1] of AnsiChar; + LineEnd, LinePos: LongInt; + MapInfo: TPortableMapInfo; + LineBreak: string; + + procedure CheckBuffer; + begin + if (LineEnd = 0) or (LinePos = LineEnd) then + begin + // Reload buffer if its is empty or its end was reached + LineEnd := GetIO.Read(Handle, @LineBuffer[0], LineBufferCapacity); + LinePos := 0; + end; + end; + + procedure FixInputPos; + begin + // Sets input's position to its real pos as it would be without buffering + if LineEnd > 0 then + begin + GetIO.Seek(Handle, -LineEnd + LinePos, smFromCurrent); + LineEnd := 0; + end; + end; + + function ReadString: string; + var + S: AnsiString; + C: AnsiChar; + begin + // First skip all whitespace chars + SetLength(S, 1); + repeat + CheckBuffer; + S[1] := LineBuffer[LinePos]; + Inc(LinePos); + if S[1] = '#' then + repeat + // Comment detected, skip everything until next line is reached + CheckBuffer; + S[1] := LineBuffer[LinePos]; + Inc(LinePos); + until S[1] = #10; + until not(S[1] in WhiteSpaces); + // Now we have reached some chars other than white space, read them until + // there is whitespace again + repeat + SetLength(S, Length(S) + 1); + CheckBuffer; + S[Length(S)] := LineBuffer[LinePos]; + Inc(LinePos); + // Repeat until current char is whitespace or end of file is reached + // (Line buffer has 0 bytes which happens only on EOF) + until (S[Length(S)] in WhiteSpaces) or (LineEnd = 0); + // Get rid of last char - whitespace or null + SetLength(S, Length(S) - 1); + // Move position to the beginning of next string (skip white space - needed + // to make the loader stop at the right input position) + repeat + CheckBuffer; + C := LineBuffer[LinePos]; + Inc(LinePos); + until not (C in WhiteSpaces) or (LineEnd = 0); + // Dec pos, current is the begining of the the string + Dec(LinePos); + + Result := string(S); + end; + + function ReadIntValue: LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} + begin + Result := StrToInt(ReadString); + end; + + procedure FindLineBreak; + var + C: AnsiChar; + begin + LineBreak := #10; + repeat + CheckBuffer; + C := LineBuffer[LinePos]; + Inc(LinePos); + + if C = #13 then + LineBreak := #13#10; + + until C = #10; + end; + + function ParseHeader: Boolean; + var + Id: TChar2; + I: TTupleType; + TupleTypeName: string; + Scale: Single; + begin + Result := False; + with GetIO do + begin + FillChar(MapInfo, SizeOf(MapInfo), 0); + Read(Handle, @Id, SizeOf(Id)); + FindLineBreak; + + if Id[1] in ['1'..'6'] then + begin + // Read header for PBM, PGM, and PPM files + MapInfo.Width := ReadIntValue; + MapInfo.Height := ReadIntValue; + + if Id[1] in ['1', '4'] then + begin + MapInfo.MaxVal := 1; + MapInfo.BitCount := 1 + end + else + begin + // Read channel max value, <=255 for 8bit images, >255 for 16bit images + // but some programs think its max colors so put <=256 here + MapInfo.MaxVal := ReadIntValue; + MapInfo.BitCount := Iff(MapInfo.MaxVal <= 256, 8, 16); + end; + + MapInfo.Depth := 1; + case Id[1] of + '1', '4': MapInfo.TupleType := ttBlackAndWhite; + '2', '5': MapInfo.TupleType := ttGrayScale; + '3', '6': + begin + MapInfo.TupleType := ttRGB; + MapInfo.Depth := 3; + end; + end; + end + else if Id[1] = '7' then + begin + // Read values from PAM header + // WIDTH + if (ReadString <> SPAMWidth) then Exit; + MapInfo.Width := ReadIntValue; + // HEIGHT + if (ReadString <> SPAMheight) then Exit; + MapInfo.Height := ReadIntValue; + // DEPTH + if (ReadString <> SPAMDepth) then Exit; + MapInfo.Depth := ReadIntValue; + // MAXVAL + if (ReadString <> SPAMMaxVal) then Exit; + MapInfo.MaxVal := ReadIntValue; + MapInfo.BitCount := Iff(MapInfo.MaxVal <= 256, 8, 16); + // TUPLETYPE + if (ReadString <> SPAMTupleType) then Exit; + TupleTypeName := ReadString; + for I := Low(TTupleType) to High(TTupleType) do + if SameText(TupleTypeName, TupleTypeNames[I]) then + begin + MapInfo.TupleType := I; + Break; + end; + // ENDHDR + if (ReadString <> SPAMEndHdr) then Exit; + end + else if Id[1] in ['F', 'f'] then + begin + // Read header of PFM file + MapInfo.Width := ReadIntValue; + MapInfo.Height := ReadIntValue; + Scale := StrToFloatDef(ReadString, 0, FUSFormat); + MapInfo.IsBigEndian := Scale > 0.0; + if Id[1] = 'F' then + MapInfo.TupleType := ttRGBFP + else + MapInfo.TupleType := ttGrayScaleFP; + MapInfo.Depth := Iff(MapInfo.TupleType = ttRGBFP, 3, 1); + MapInfo.BitCount := Iff(MapInfo.TupleType = ttRGBFP, 96, 32); + end; + + FixInputPos; + MapInfo.Binary := (Id[1] in ['4', '5', '6', '7', 'F', 'f']); + + if MapInfo.Binary and not (Id[1] in ['F', 'f']) then + begin + // Mimic the behaviour of Photoshop and other editors/viewers: + // If linenreaks in file are DOS CR/LF 16bit binary values are + // little endian, Unix LF only linebreak indicates big endian. + MapInfo.IsBigEndian := LineBreak = #10; + end; + + // Check if values found in header are valid + Result := (MapInfo.Width > 0) and (MapInfo.Height > 0) and + (MapInfo.BitCount in [1, 8, 16, 32, 96]) and (MapInfo.TupleType <> ttInvalid); + // Now check if image has proper number of channels (PAM) + if Result then + case MapInfo.TupleType of + ttBlackAndWhite, ttGrayScale: Result := MapInfo.Depth = 1; + ttBlackAndWhiteAlpha, ttGrayScaleAlpha: Result := MapInfo.Depth = 2; + ttRGB: Result := MapInfo.Depth = 3; + ttRGBAlpha: Result := MapInfo.Depth = 4; + end; + end; + end; + +begin + Result := False; + LineEnd := 0; + LinePos := 0; + SetLength(Images, 1); + + with GetIO, Images[0] do + begin + Format := ifUnknown; + // Try to parse file header + if not ParseHeader then Exit; + // Select appropriate data format based on values read from file header + case MapInfo.TupleType of + ttBlackAndWhite: Format := ifGray8; + ttBlackAndWhiteAlpha: Format := ifA8Gray8; + ttGrayScale: Format := IffFormat(MapInfo.BitCount = 8, ifGray8, ifGray16); + ttGrayScaleAlpha: Format := IffFormat(MapInfo.BitCount = 8, ifA8Gray8, ifA16Gray16); + ttRGB: Format := IffFormat(MapInfo.BitCount = 8, ifR8G8B8, ifR16G16B16); + ttRGBAlpha: Format := IffFormat(MapInfo.BitCount = 8, ifA8R8G8B8, ifA16R16G16B16); + ttGrayScaleFP: Format := ifR32F; + ttRGBFP: Format := ifB32G32R32F; + end; + // Exit if no matching data format was found + if Format = ifUnknown then Exit; + + NewImage(MapInfo.Width, MapInfo.Height, Format, Images[0]); + Info := GetFormatInfo(Format); + + // Now read pixels from file to dest image + if not MapInfo.Binary then + begin + Dest := Bits; + for I := 0 to Width * Height - 1 do + begin + case Format of + ifGray8: + begin + Dest^ := ReadIntValue; + if MapInfo.BitCount = 1 then + // If source is 1bit mono image (where 0=white, 1=black) + // we must scale it to 8bits + Dest^ := 255 - Dest^ * 255; + end; + ifGray16: PWord(Dest)^ := ReadIntValue; + ifR8G8B8: + with PColor24Rec(Dest)^ do + begin + R := ReadIntValue; + G := ReadIntValue; + B := ReadIntValue; + end; + ifR16G16B16: + with PColor48Rec(Dest)^ do + begin + R := ReadIntValue; + G := ReadIntValue; + B := ReadIntValue; + end; + end; + Inc(Dest, Info.BytesPerPixel); + end; + end + else + begin + if MapInfo.BitCount > 1 then + begin + if not (MapInfo.TupleType in [ttGrayScaleFP, ttRGBFP]) then + begin + // Just copy bytes from binary Portable Maps (non 1bit, non FP) + Read(Handle, Bits, Size); + end + else + begin + Dest := Bits; + // FP images are in BGR order and endian swap maybe needed. + // Some programs store scanlines in bottom-up order but + // I will stick with Photoshops behaviour here + Read(Handle, Bits, Size); + if MapInfo.IsBigEndian then + SwapEndianLongWord(PLongWord(Dest), Size div SizeOf(LongWord)); + end; + + if MapInfo.TupleType in [ttBlackAndWhite, ttBlackAndWhiteAlpha] then + begin + // Black and white PAM files must be scaled to 8bits. Note that + // in PAM files 1=white, 0=black (reverse of PBM) + for I := 0 to Width * Height * Iff(MapInfo.TupleType = ttBlackAndWhiteAlpha, 2, 1) - 1 do + PByteArray(Bits)[I] := PByteArray(Bits)[I] * 255; + end + else if MapInfo.TupleType in [ttRGB, ttRGBAlpha] then + begin + // Swap channels of RGB/ARGB images. Binary RGB image files use BGR order. + SwapChannels(Images[0], ChannelBlue, ChannelRed); + end; + + // Swap byte order if needed + if (MapInfo.BitCount = 16) and MapInfo.IsBigEndian then + SwapEndianWord(Bits, Width * Height * Info.BytesPerPixel div SizeOf(Word)); + end + else + begin + // Handle binary PBM files (ttBlackAndWhite 1bit) + ScanLineSize := (Width + 7) div 8; + // Get total binary data size, read it from file to temp + // buffer and convert the data to Gray8 + MonoSize := ScanLineSize * Height; + GetMem(MonoData, MonoSize); + try + Read(Handle, MonoData, MonoSize); + Convert1To8(MonoData, Bits, Width, Height, ScanLineSize, False); + // 1bit mono images must be scaled to 8bit, but inverted (where 0=white, 1=black) + for I := 0 to Width * Height - 1 do + PByteArray(Bits)[I] := 255 - PByteArray(Bits)[I] * 255; + finally + FreeMem(MonoData); + end; + end; + end; + + FixInputPos; + + if (MapInfo.MaxVal <> Pow2Int(MapInfo.BitCount) - 1) and + (MapInfo.TupleType in [ttGrayScale, ttGrayScaleAlpha, ttRGB, ttRGBAlpha]) then + begin + Dest := Bits; + // Scale color values according to MaxVal we got from header + // if necessary. + for I := 0 to Width * Height * Info.BytesPerPixel div (MapInfo.BitCount shr 3) - 1 do + begin + if MapInfo.BitCount = 8 then + Dest^ := Dest^ * 255 div MapInfo.MaxVal + else + PWord(Dest)^ := PWord(Dest)^ * 65535 div MapInfo.MaxVal; + Inc(Dest, MapInfo.BitCount shr 3); + end; + end; + + Result := True; + end; +end; + +function TPortableMapFileFormat.SaveDataInternal(Handle: TImagingHandle; + const Images: TDynImageDataArray; Index: Integer; var MapInfo: TPortableMapInfo): Boolean; +const + // Use Unix linebreak, for many viewers/editors it means that + // 16bit samples are stored as big endian - so we need to swap byte order + // before saving + LineDelimiter = #10; + PixelDelimiter = #32; +var + ImageToSave: TImageData; + MustBeFreed: Boolean; + Info: TImageFormatInfo; + I, LineLength: LongInt; + Src: PByte; + Pixel32: TColor32Rec; + Pixel64: TColor64Rec; + W: Word; + + procedure WriteString(S: string; Delimiter: Char = LineDelimiter); + begin + SetLength(S, Length(S) + 1); + S[Length(S)] := Delimiter; + {$IF Defined(DCC) and Defined(UNICODE)} + GetIO.Write(Handle, @AnsiString(S)[1], Length(S)); + {$ELSE} + GetIO.Write(Handle, @S[1], Length(S)); + {$IFEND} + Inc(LineLength, Length(S)); + end; + + procedure WriteHeader; + begin + WriteString('P' + MapInfo.FormatId); + if not MapInfo.HasPAMHeader then + begin + // Write header of PGM, PPM, and PFM files + WriteString(IntToStr(ImageToSave.Width)); + WriteString(IntToStr(ImageToSave.Height)); + case MapInfo.TupleType of + ttGrayScale, ttRGB: WriteString(IntToStr(Pow2Int(MapInfo.BitCount) - 1)); + ttGrayScaleFP, ttRGBFP: + begin + // Negative value indicates that raster data is saved in little endian + WriteString(FloatToStr(-1.0, FUSFormat)); + end; + end; + end + else + begin + // Write PAM file header + WriteString(Format('%s %d', [SPAMWidth, ImageToSave.Width])); + WriteString(Format('%s %d', [SPAMHeight, ImageToSave.Height])); + WriteString(Format('%s %d', [SPAMDepth, MapInfo.Depth])); + WriteString(Format('%s %d', [SPAMMaxVal, Pow2Int(MapInfo.BitCount) - 1])); + WriteString(Format('%s %s', [SPAMTupleType, TupleTypeNames[MapInfo.TupleType]])); + WriteString(SPAMEndHdr); + end; + end; + +begin + Result := False; + if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then + with GetIO, ImageToSave do + try + Info := GetFormatInfo(Format); + // Fill values of MapInfo record that were not filled by + // descendants in their SaveData methods + MapInfo.BitCount := (Info.BytesPerPixel div Info.ChannelCount) * 8; + MapInfo.Depth := Info.ChannelCount; + if MapInfo.TupleType = ttInvalid then + begin + if Info.HasGrayChannel then + begin + if Info.HasAlphaChannel then + MapInfo.TupleType := ttGrayScaleAlpha + else + MapInfo.TupleType := ttGrayScale; + end + else + begin + if Info.HasAlphaChannel then + MapInfo.TupleType := ttRGBAlpha + else + MapInfo.TupleType := ttRGB; + end; + end; + // Write file header + WriteHeader; + + if not MapInfo.Binary then + begin + Src := Bits; + LineLength := 0; + // For each pixel find its text representation and write it to file + for I := 0 to Width * Height - 1 do + begin + case Format of + ifGray8: WriteString(IntToStr(Src^), PixelDelimiter); + ifGray16: WriteString(IntToStr(PWord(Src)^), PixelDelimiter); + ifR8G8B8: + with PColor24Rec(Src)^ do + WriteString(SysUtils.Format('%d %d %d', [R, G, B]), PixelDelimiter); + ifR16G16B16: + with PColor48Rec(Src)^ do + WriteString(SysUtils.Format('%d %d %d', [R, G, B]), PixelDelimiter); + end; + // Lines in text PNM images should have length <70 + if LineLength > 65 then + begin + LineLength := 0; + WriteString('', LineDelimiter); + end; + Inc(Src, Info.BytesPerPixel); + end; + end + else + begin + // Write binary images + if not (MapInfo.TupleType in [ttGrayScaleFP, ttRGBFP]) then + begin + // Save integer binary images + if MapInfo.BitCount = 8 then + begin + if MapInfo.TupleType in [ttGrayScale, ttGrayScaleAlpha] then + begin + // 8bit grayscale images can be written in one Write call + Write(Handle, Bits, Size); + end + else + begin + // 8bit RGB/ARGB images: red and blue must be swapped and + // 3 or 4 bytes must be written + Src := Bits; + for I := 0 to Width * Height - 1 do + with PColor32Rec(Src)^ do + begin + if MapInfo.TupleType = ttRGBAlpha then + Pixel32.A := A; + Pixel32.R := B; + Pixel32.G := G; + Pixel32.B := R; + Write(Handle, @Pixel32, Info.BytesPerPixel); + Inc(Src, Info.BytesPerPixel); + end; + end; + end + else + begin + // Images with 16bit channels: make sure that channel values are saved in big endian + Src := Bits; + if MapInfo.TupleType in [ttGrayScale, ttGrayScaleAlpha] then + begin + // 16bit grayscale image + for I := 0 to Width * Height * Info.BytesPerPixel div SizeOf(Word) - 1 do + begin + W := SwapEndianWord(PWord(Src)^); + Write(Handle, @W, SizeOf(Word)); + Inc(Src, SizeOf(Word)); + end; + end + else + begin + // RGB images with 16bit channels: swap RB and endian too + for I := 0 to Width * Height - 1 do + with PColor64Rec(Src)^ do + begin + if MapInfo.TupleType = ttRGBAlpha then + Pixel64.A := SwapEndianWord(A); + Pixel64.R := SwapEndianWord(B); + Pixel64.G := SwapEndianWord(G); + Pixel64.B := SwapEndianWord(R); + Write(Handle, @Pixel64, Info.BytesPerPixel); + Inc(Src, Info.BytesPerPixel); + end; + end; + end; + end + else + begin + // Floating point images (no need to swap endian here - little + // endian is specified in file header) + Write(Handle, Bits, Size); + end; + end; + Result := True; + finally + if MustBeFreed then + FreeImage(ImageToSave); + end; +end; + +function TPortableMapFileFormat.TestFormat(Handle: TImagingHandle): Boolean; +var + Id: TChar4; + ReadCount: LongInt; +begin + Result := False; + if Handle <> nil then + with GetIO do + begin + ReadCount := Read(Handle, @Id, SizeOf(Id)); + Seek(Handle, -ReadCount, smFromCurrent); + Result := (Id[0] = 'P') and (Id[1] in [FIdNumbers[0], FIdNumbers[1]]) and + (Id[2] in WhiteSpaces); + end; +end; + +{ TPBMFileFormat } + +procedure TPBMFileFormat.Define; +begin + inherited; + FName := SPBMFormatName; + FFeatures := [ffLoad]; + AddMasks(SPBMMasks); + FIdNumbers := '14'; +end; + +{ TPGMFileFormat } + +procedure TPGMFileFormat.Define; +begin + inherited; + FName := SPGMFormatName; + FSupportedFormats := PGMSupportedFormats; + AddMasks(SPGMMasks); + RegisterOption(ImagingPGMSaveBinary, @FSaveBinary); + FIdNumbers := '25'; +end; + +function TPGMFileFormat.SaveData(Handle: TImagingHandle; + const Images: TDynImageDataArray; Index: Integer): Boolean; +var + MapInfo: TPortableMapInfo; +begin + FillChar(MapInfo, SizeOf(MapInfo), 0); + if FSaveBinary then + MapInfo.FormatId := FIdNumbers[1] + else + MapInfo.FormatId := FIdNumbers[0]; + MapInfo.Binary := FSaveBinary; + Result := SaveDataInternal(Handle, Images, Index, MapInfo); +end; + +procedure TPGMFileFormat.ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); +var + ConvFormat: TImageFormat; +begin + if Info.IsFloatingPoint then + // All FP images go to 16bit + ConvFormat := ifGray16 + else if Info.HasGrayChannel then + // Grayscale will be 8 or 16 bit - depends on input's bitcount + ConvFormat := IffFormat(Info.BytesPerPixel div Info.ChannelCount > 1, + ifGray16, ifGray8) + else if Info.BytesPerPixel > 4 then + // Large bitcounts -> 16bit + ConvFormat := ifGray16 + else + // Rest of the formats -> 8bit + ConvFormat := ifGray8; + + ConvertImage(Image, ConvFormat); +end; + +{ TPPMFileFormat } + +procedure TPPMFileFormat.Define; +begin + inherited; + FName := SPPMFormatName; + FSupportedFormats := PPMSupportedFormats; + AddMasks(SPPMMasks); + RegisterOption(ImagingPPMSaveBinary, @FSaveBinary); + FIdNumbers := '36'; +end; + +function TPPMFileFormat.SaveData(Handle: TImagingHandle; + const Images: TDynImageDataArray; Index: Integer): Boolean; +var + MapInfo: TPortableMapInfo; +begin + FillChar(MapInfo, SizeOf(MapInfo), 0); + if FSaveBinary then + MapInfo.FormatId := FIdNumbers[1] + else + MapInfo.FormatId := FIdNumbers[0]; + MapInfo.Binary := FSaveBinary; + Result := SaveDataInternal(Handle, Images, Index, MapInfo); +end; + +procedure TPPMFileFormat.ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); +var + ConvFormat: TImageFormat; +begin + if Info.IsFloatingPoint then + // All FP images go to 48bit RGB + ConvFormat := ifR16G16B16 + else if Info.HasGrayChannel then + // Grayscale will be 24 or 48 bit RGB - depends on input's bitcount + ConvFormat := IffFormat(Info.BytesPerPixel div Info.ChannelCount > 1, + ifR16G16B16, ifR8G8B8) + else if Info.BytesPerPixel > 4 then + // Large bitcounts -> 48bit RGB + ConvFormat := ifR16G16B16 + else + // Rest of the formats -> 24bit RGB + ConvFormat := ifR8G8B8; + + ConvertImage(Image, ConvFormat); +end; + +{ TPAMFileFormat } + +procedure TPAMFileFormat.Define; +begin + inherited; + FName := SPAMFormatName; + FSupportedFormats := PAMSupportedFormats; + AddMasks(SPAMMasks); + FIdNumbers := '77'; +end; + +function TPAMFileFormat.SaveData(Handle: TImagingHandle; + const Images: TDynImageDataArray; Index: Integer): Boolean; +var + MapInfo: TPortableMapInfo; +begin + FillChar(MapInfo, SizeOf(MapInfo), 0); + MapInfo.FormatId := FIdNumbers[0]; + MapInfo.Binary := True; + MapInfo.HasPAMHeader := True; + Result := SaveDataInternal(Handle, Images, Index, MapInfo); +end; + +procedure TPAMFileFormat.ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); +var + ConvFormat: TImageFormat; +begin + if Info.IsFloatingPoint then + ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16R16G16B16, ifR16G16B16) + else if Info.HasGrayChannel then + ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16Gray16, ifGray16) + else + begin + if Info.BytesPerPixel <= 4 then + ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifR8G8B8) + else + ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16R16G16B16, ifR16G16B16); + end; + ConvertImage(Image, ConvFormat); +end; + +{ TPFMFileFormat } + +procedure TPFMFileFormat.Define; +begin + inherited; + FName := SPFMFormatName; + AddMasks(SPFMMasks); + FIdNumbers := 'Ff'; + FSupportedFormats := PFMSupportedFormats; +end; + +function TPFMFileFormat.SaveData(Handle: TImagingHandle; + const Images: TDynImageDataArray; Index: Integer): Boolean; +var + Info: TImageFormatInfo; + MapInfo: TPortableMapInfo; +begin + FillChar(MapInfo, SizeOf(MapInfo), 0); + Info := GetFormatInfo(Images[Index].Format); + + if (Info.ChannelCount > 1) or Info.IsIndexed then + MapInfo.TupleType := ttRGBFP + else + MapInfo.TupleType := ttGrayScaleFP; + + if MapInfo.TupleType = ttGrayScaleFP then + MapInfo.FormatId := FIdNumbers[1] + else + MapInfo.FormatId := FIdNumbers[0]; + + MapInfo.Binary := True; + Result := SaveDataInternal(Handle, Images, Index, MapInfo); +end; + +procedure TPFMFileFormat.ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); +begin + if (Info.ChannelCount > 1) or Info.IsIndexed then + ConvertImage(Image, ifB32G32R32F) + else + ConvertImage(Image, ifR32F); +end; + +initialization + RegisterImageFileFormat(TPBMFileFormat); + RegisterImageFileFormat(TPGMFileFormat); + RegisterImageFileFormat(TPPMFileFormat); + RegisterImageFileFormat(TPAMFileFormat); + RegisterImageFileFormat(TPFMFileFormat); + +{ + File Notes: + + -- TODOS ---------------------------------------------------- + - nothing now + + -- 0.77.1 Changes/Bug Fixes ----------------------------------- + - Native RGB floating point format of PFM is now supported by Imaging + so we use it now for saving instead of A32B32G32B32. + - String to float formatting changes (don't change global settings). + + -- 0.26.3 Changes/Bug Fixes ----------------------------------- + - Fixed D2009 Unicode related bug in PNM saving. + + -- 0.24.3 Changes/Bug Fixes ----------------------------------- + - Improved compatibility of 16bit/component image loading. + - Changes for better thread safety. + + -- 0.21 Changes/Bug Fixes ----------------------------------- + - Made modifications to ASCII PNM loading to be more "stream-safe". + - Fixed bug: indexed images saved as grayscale in PFM. + - Changed converting to supported formats little bit. + - Added scaling of channel values (non-FP and non-mono images) according + to MaxVal. + - Added buffering to loading of PNM files. More than 10x faster now + for text files. + - Added saving support to PGM, PPM, PAM, and PFM format. + - Added PFM file format. + - Initial version created. +} + +end. diff --git a/resources/libraries/deskew/Imaging/ImagingPsd.pas b/resources/libraries/deskew/Imaging/ImagingPsd.pas new file mode 100755 index 0000000..96ce9c9 --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingPsd.pas @@ -0,0 +1,801 @@ +{ + Vampyre Imaging Library + by Marek Mauder + http://imaginglib.sourceforge.net + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +{ This unit contains image format loader/saver for Photoshop PSD image format.} +unit ImagingPsd; + +{$I ImagingOptions.inc} + +interface + +uses + SysUtils, ImagingTypes, Imaging, ImagingColors, ImagingUtility; + +type + { Class for loading and saving Adobe Photoshop PSD images. + Loading and saving of indexed, grayscale, RGB(A), HDR (FP32), and CMYK + (auto converted to RGB) images is supported. Non-HDR gray, RGB, + and CMYK images can have 8bit or 16bit color channels. + There is no support for loading mono images, duotone images are treated + like grayscale images, and multichannel and CIE Lab images are loaded as + RGB images but without actual conversion to RGB color space. + Also no layer information is loaded.} + TPSDFileFormat = class(TImageFileFormat) + private + FSaveAsLayer: LongBool; + protected + procedure Define; override; + function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; + OnlyFirstLevel: Boolean): Boolean; override; + function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; + Index: LongInt): Boolean; override; + procedure ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); override; + public + function TestFormat(Handle: TImagingHandle): Boolean; override; + published + property SaveAsLayer: LongBool read FSaveAsLayer write FSaveAsLayer; + end; + +implementation + +uses + ImagingExtras; + +const + SPSDFormatName = 'Photoshop Image'; + SPSDMasks = '*.psd,*.pdd'; + PSDSupportedFormats: TImageFormats = [ifIndex8, ifGray8, ifA8Gray8, + ifR8G8B8, ifA8R8G8B8, ifGray16, ifA16Gray16, ifR16G16B16, ifA16R16G16B16, + ifR32F, ifR32G32B32F, ifA32R32G32B32F]; + PSDDefaultSaveAsLayer = True; + +const + SPSDMagic = '8BPS'; + CompressionNone: Word = 0; + CompressionRLE: Word = 1; + +type + {$MINENUMSIZE 2} + { PSD Image color mode.} + TPSDColorMode = ( + cmMono = 0, + cmGrayscale = 1, + cmIndexed = 2, + cmRGB = 3, + cmCMYK = 4, + cmMultiChannel = 7, + cmDuoTone = 8, + cmLab = 9 + ); + + { PSD image main header.} + TPSDHeader = packed record + Signature: TChar4; // Format ID '8BPS' + Version: Word; // Always 1 + Reserved: array[0..5] of Byte; // Reserved, all zero + Channels: Word; // Number of color channels (1-24) including alpha channels + Rows : LongWord; // Height of image in pixels (1-30000) + Columns: LongWord; // Width of image in pixels (1-30000) + Depth: Word; // Number of bits per channel (1, 8, and 16) + Mode: TPSDColorMode; // Color mode + end; + + TPSDChannelInfo = packed record + ChannelID: Word; // 0 = Red, 1 = Green, 2 = Blue etc., -1 = Transparency mask, -2 = User mask + Size: LongWord; // Size of channel data. + end; + +procedure SwapHeader(var Header: TPSDHeader); +begin + Header.Version := SwapEndianWord(Header.Version); + Header.Channels := SwapEndianWord(Header.Channels); + Header.Depth := SwapEndianWord(Header.Depth); + Header.Rows := SwapEndianLongWord(Header.Rows); + Header.Columns := SwapEndianLongWord(Header.Columns); + Header.Mode := TPSDColorMode(SwapEndianWord(Word(Header.Mode))); +end; + +{ + TPSDFileFormat class implementation +} + +procedure TPSDFileFormat.Define; +begin + inherited; + FName := SPSDFormatName; + FFeatures := [ffLoad, ffSave]; + FSupportedFormats := PSDSupportedFormats; + AddMasks(SPSDMasks); + + FSaveAsLayer := PSDDefaultSaveAsLayer; + RegisterOption(ImagingPSDSaveAsLayer, @FSaveAsLayer); +end; + +function TPSDFileFormat.LoadData(Handle: TImagingHandle; + var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; +var + Header: TPSDHeader; + ByteCount: LongWord; + RawPal: array[0..767] of Byte; + Compression, PackedSize: Word; + LineSize, ChannelPixelSize, WidthBytes, + CurrChannel, MaxRLESize, I, Y, X: LongInt; + Info: TImageFormatInfo; + PackedLine, LineBuffer: PByte; + RLELineSizes: array of Word; + Col32: TColor32Rec; + Col64: TColor64Rec; + PCol32: PColor32Rec; + PCol64: PColor64Rec; + + { PackBits RLE decode code from Mike Lischke's GraphicEx library.} + procedure DecodeRLE(Source, Dest: PByte; PackedSize, UnpackedSize: LongInt); + var + Count: LongInt; + begin + while (UnpackedSize > 0) and (PackedSize > 0) do + begin + Count := ShortInt(Source^); + Inc(Source); + Dec(PackedSize); + if Count < 0 then + begin + // Replicate next byte -Count + 1 times + if Count = -128 then + Continue; + Count := -Count + 1; + if Count > UnpackedSize then + Count := UnpackedSize; + FillChar(Dest^, Count, Source^); + Inc(Source); + Dec(PackedSize); + Inc(Dest, Count); + Dec(UnpackedSize, Count); + end + else + begin + // Copy next Count + 1 bytes from input + Inc(Count); + if Count > UnpackedSize then + Count := UnpackedSize; + if Count > PackedSize then + Count := PackedSize; + Move(Source^, Dest^, Count); + Inc(Dest, Count); + Inc(Source, Count); + Dec(PackedSize, Count); + Dec(UnpackedSize, Count); + end; + end; + end; + +begin + Result := False; + SetLength(Images, 1); + with GetIO, Images[0] do + begin + // Read PSD header + Read(Handle, @Header, SizeOf(Header)); + SwapHeader(Header); + + // Determine image data format + Format := ifUnknown; + case Header.Mode of + cmGrayscale, cmDuoTone: + begin + if Header.Depth in [8, 16] then + begin + if Header.Channels = 1 then + Format := IffFormat(Header.Depth = 8, ifGray8, ifGray16) + else if Header.Channels >= 2 then + Format := IffFormat(Header.Depth = 8, ifA8Gray8, ifA16Gray16); + end + else if (Header.Depth = 32) and (Header.Channels = 1) then + Format := ifR32F; + end; + cmIndexed: + begin + if Header.Depth = 8 then + Format := ifIndex8; + end; + cmRGB, cmMultiChannel, cmCMYK, cmLab: + begin + if Header.Depth in [8, 16] then + begin + if Header.Channels = 3 then + Format := IffFormat(Header.Depth = 8, ifR8G8B8, ifR16G16B16) + else if Header.Channels >= 4 then + Format := IffFormat(Header.Depth = 8, ifA8R8G8B8, ifA16R16G16B16); + end + else if Header.Depth = 32 then + begin + if Header.Channels = 3 then + Format := ifR32G32B32F + else if Header.Channels >= 4 then + Format := ifA32R32G32B32F; + end; + end; + cmMono:; // Not supported + end; + + // Exit if no compatible format was found + if Format = ifUnknown then + Exit; + + NewImage(Header.Columns, Header.Rows, Format, Images[0]); + Info := GetFormatInfo(Format); + + // Read or skip Color Mode Data Block (palette) + Read(Handle, @ByteCount, SizeOf(ByteCount)); + ByteCount := SwapEndianLongWord(ByteCount); + if Format = ifIndex8 then + begin + // Read palette only for indexed images + Read(Handle, @RawPal, SizeOf(RawPal)); + for I := 0 to 255 do + begin + Palette[I].A := $FF; + Palette[I].R := RawPal[I + 0]; + Palette[I].G := RawPal[I + 256]; + Palette[I].B := RawPal[I + 512]; + end; + end + else + Seek(Handle, ByteCount, smFromCurrent); + + // Skip Image Resources Block + Read(Handle, @ByteCount, SizeOf(ByteCount)); + ByteCount := SwapEndianLongWord(ByteCount); + Seek(Handle, ByteCount, smFromCurrent); + // Now there is Layer and Mask Information Block + Read(Handle, @ByteCount, SizeOf(ByteCount)); + ByteCount := SwapEndianLongWord(ByteCount); + // Skip Layer and Mask Information Block + Seek(Handle, ByteCount, smFromCurrent); + + // Read compression flag + Read(Handle, @Compression, SizeOf(Compression)); + Compression := SwapEndianWord(Compression); + + if Compression = CompressionRLE then + begin + // RLE compressed PSDs (most) have first lengths of compressed scanlines + // for each channel stored + SetLength(RLELineSizes, Height * Header.Channels); + Read(Handle, @RLELineSizes[0], Length(RLELineSizes) * SizeOf(Word)); + SwapEndianWord(@RLELineSizes[0], Height * Header.Channels); + MaxRLESize := RLELineSizes[0]; + for I := 1 to High(RLELineSizes) do + begin + if MaxRLESize < RLELineSizes[I] then + MaxRLESize := RLELineSizes[I]; + end; + end + else + MaxRLESize := 0; + + ChannelPixelSize := Info.BytesPerPixel div Info.ChannelCount; + LineSize := Width * ChannelPixelSize; + WidthBytes := Width * Info.BytesPerPixel; + GetMem(LineBuffer, LineSize); + GetMem(PackedLine, MaxRLESize); + + try + // Image color chanels are stored separately in PSDs so we will load + // one by one and copy their data to appropriate addresses of dest image. + for I := 0 to Header.Channels - 1 do + begin + // Now determine to which color channel of destination image we are going + // to write pixels. + if I <= 4 then + begin + // If PSD has alpha channel we need to switch current channel order - + // PSDs have alpha stored after blue channel but Imaging has alpha + // before red. + if Info.HasAlphaChannel and (Header.Mode <> cmCMYK) then + begin + if I = Info.ChannelCount - 1 then + CurrChannel := I + else + CurrChannel := Info.ChannelCount - 2 - I; + end + else + CurrChannel := Info.ChannelCount - 1 - I; + end + else + begin + // No valid channel remains + CurrChannel := -1; + end; + + if CurrChannel >= 0 then + begin + for Y := 0 to Height - 1 do + begin + if Compression = CompressionRLE then + begin + // Read RLE line and decompress it + PackedSize := RLELineSizes[I * Height + Y]; + Read(Handle, PackedLine, PackedSize); + DecodeRLE(PackedLine, LineBuffer, PackedSize, LineSize); + end + else + begin + // Just read uncompressed line + Read(Handle, LineBuffer, LineSize); + end; + + // Swap endian if needed + if ChannelPixelSize = 4 then + SwapEndianLongWord(PLongWord(LineBuffer), Width) + else if ChannelPixelSize = 2 then + SwapEndianWord(PWordArray(LineBuffer), Width); + + if Info.ChannelCount > 1 then + begin + // Copy each pixel fragment to its right place in destination image + for X := 0 to Width - 1 do + begin + Move(PByteArray(LineBuffer)[X * ChannelPixelSize], + PByteArray(Bits)[Y * WidthBytes + X * Info.BytesPerPixel + CurrChannel * ChannelPixelSize], + ChannelPixelSize); + end; + end + else + begin + // Just copy the line + Move(LineBuffer^, PByteArray(Bits)[Y * LineSize], LineSize); + end; + end; + end + else + begin + // Skip current color channel, not needed for image loading - just to + // get stream's position to the end of PSD + if Compression = CompressionRLE then + begin + for Y := 0 to Height - 1 do + Seek(Handle, RLELineSizes[I * Height + Y], smFromCurrent); + end + else + Seek(Handle, LineSize * Height, smFromCurrent); + end; + end; + + if Header.Mode = cmCMYK then + begin + // Convert CMYK images to RGB (alpha is ignored here). PSD stores CMYK + // channels in the way that first requires substraction from max channel value + if ChannelPixelSize = 1 then + begin + PCol32 := Bits; + for X := 0 to Width * Height - 1 do + begin + Col32.A := 255 - PCol32.A; + Col32.R := 255 - PCol32.R; + Col32.G := 255 - PCol32.G; + Col32.B := 255 - PCol32.B; + CMYKToRGB(Col32.A, Col32.R, Col32.G, Col32.B, PCol32.R, PCol32.G, PCol32.B); + PCol32.A := 255; + Inc(PCol32); + end; + end + else + begin + PCol64 := Bits; + for X := 0 to Width * Height - 1 do + begin + Col64.A := 65535 - PCol64.A; + Col64.R := 65535 - PCol64.R; + Col64.G := 65535 - PCol64.G; + Col64.B := 65535 - PCol64.B; + CMYKToRGB16(Col64.A, Col64.R, Col64.G, Col64.B, PCol64.R, PCol64.G, PCol64.B); + PCol64.A := 65535; + Inc(PCol64); + end; + end; + end; + + Result := True; + finally + FreeMem(LineBuffer); + FreeMem(PackedLine); + end; + end; +end; + +function TPSDFileFormat.SaveData(Handle: TImagingHandle; + const Images: TDynImageDataArray; Index: LongInt): Boolean; +type + TURect = packed record + Top, Left, Bottom, Right: LongWord; + end; +const + BlendMode: TChar8 = '8BIMnorm'; + LayerOptions: array[0..3] of Byte = (255, 0, 0, 0); + LayerName: array[0..7] of AnsiChar = #7'Layer 0'; +var + MustBeFreed: Boolean; + ImageToSave: TImageData; + Info: TImageFormatInfo; + Header: TPSDHeader; + I, CurrChannel, ChannelPixelSize: LongInt; + LayerBlockOffset, SaveOffset, ChannelInfoOffset: Integer; + ChannelInfo: TPSDChannelInfo; + R: TURect; + LongVal: LongWord; + WordVal, LayerCount: Word; + RawPal: array[0..767] of Byte; + ChannelDataSizes: array of Integer; + + function PackLine(Src, Dest: PByteArray; Length: Integer): Integer; + var + I, Remaining: Integer; + begin + Remaining := Length; + Result := 0; + while Remaining > 0 do + begin + I := 0; + // Look for characters same as the first + while (I < 128) and (Remaining - I > 0) and (Src[0] = Src[I]) do + Inc(I); + + if I > 2 then + begin + Dest[0] := Byte(-(I - 1)); + Dest[1] := Src[0]; + Dest := PByteArray(@Dest[2]); + + Src := PByteArray(@Src[I]); + Dec(Remaining, I); + Inc(Result, 2); + end + else + begin + // Look for different characters + I := 0; + while (I < 128) and (Remaining - (I + 1) > 0) and + ((Src[I] <> Src[I + 1]) or (Remaining - (I + 2) <= 0) or + (Src[I] <> Src[I + 2])) do + begin + Inc(I); + end; + // If there's only 1 remaining, the previous WHILE doesn't catch it + if Remaining = 1 then + I := 1; + + if I > 0 then + begin + // Some distinct ones found + Dest[0] := I - 1; + Move(Src[0], Dest[1], I); + Dest := PByteArray(@Dest[1 + I]); + Src := PByteArray(@Src[I]); + Dec(Remaining, I); + Inc(Result, I + 1); + end; + end; + end; + end; + + procedure WriteChannelData(SeparateChannelStorage: Boolean); + var + I, X, Y, LineSize, WidthBytes, RLETableOffset, CurrentOffset, WrittenLineSize: Integer; + LineBuffer, RLEBuffer: PByteArray; + RLELengths: array of Word; + Compression: Word; + begin + LineSize := ImageToSave.Width * ChannelPixelSize; + WidthBytes := ImageToSave.Width * Info.BytesPerPixel; + GetMem(LineBuffer, LineSize); + GetMem(RLEBuffer, LineSize * 3); + SetLength(RLELengths, ImageToSave.Height * Info.ChannelCount); + RLETableOffset := 0; + // No compression for FP32, Photoshop won't open them + Compression := Iff(Info.IsFloatingPoint, CompressionNone, CompressionRLE); + + if not SeparateChannelStorage then + begin + // This is for storing background merged image. There's only one + // compression flag and one RLE lenghts table for all channels + WordVal := Swap(Compression); + GetIO.Write(Handle, @WordVal, SizeOf(WordVal)); + if Compression = CompressionRLE then + begin + RLETableOffset := GetIO.Tell(Handle); + GetIO.Write(Handle, @RLELengths[0], SizeOf(Word) * ImageToSave.Height * Info.ChannelCount); + end; + end; + + for I := 0 to Info.ChannelCount - 1 do + begin + if SeparateChannelStorage then + begin + // Layer image data has compression flag and RLE lenghts table + // independent for each channel + WordVal := Swap(CompressionRLE); + GetIO.Write(Handle, @WordVal, SizeOf(WordVal)); + if Compression = CompressionRLE then + begin + RLETableOffset := GetIO.Tell(Handle); + GetIO.Write(Handle, @RLELengths[0], SizeOf(Word) * ImageToSave.Height); + ChannelDataSizes[I] := 0; + end; + end; + + // Now determine which color channel we are going to write to file. + if Info.HasAlphaChannel then + begin + if I = Info.ChannelCount - 1 then + CurrChannel := I + else + CurrChannel := Info.ChannelCount - 2 - I; + end + else + CurrChannel := Info.ChannelCount - 1 - I; + + for Y := 0 to ImageToSave.Height - 1 do + begin + if Info.ChannelCount > 1 then + begin + // Copy each pixel fragment to its right place in destination image + for X := 0 to ImageToSave.Width - 1 do + begin + Move(PByteArray(ImageToSave.Bits)[Y * WidthBytes + X * Info.BytesPerPixel + CurrChannel * ChannelPixelSize], + PByteArray(LineBuffer)[X * ChannelPixelSize], ChannelPixelSize); + end; + end + else + Move(PByteArray(ImageToSave.Bits)[Y * LineSize], LineBuffer^, LineSize); + + // Write current channel line to file (swap endian if needed first) + if ChannelPixelSize = 4 then + SwapEndianLongWord(PLongWord(LineBuffer), ImageToSave.Width) + else if ChannelPixelSize = 2 then + SwapEndianWord(PWordArray(LineBuffer), ImageToSave.Width); + + if Compression = CompressionRLE then + begin + // Compress and write line + WrittenLineSize := PackLine(LineBuffer, RLEBuffer, LineSize); + RLELengths[ImageToSave.Height * I + Y] := SwapEndianWord(WrittenLineSize); + GetIO.Write(Handle, RLEBuffer, WrittenLineSize); + end + else + begin + WrittenLineSize := LineSize; + GetIO.Write(Handle, LineBuffer, WrittenLineSize); + end; + + if SeparateChannelStorage then + Inc(ChannelDataSizes[I], WrittenLineSize); + end; + + if SeparateChannelStorage and (Compression = CompressionRLE) then + begin + // Update channel RLE lengths + CurrentOffset := GetIO.Tell(Handle); + GetIO.Seek(Handle, RLETableOffset, smFromBeginning); + GetIO.Write(Handle, @RLELengths[ImageToSave.Height * I], SizeOf(Word) * ImageToSave.Height); + GetIO.Seek(Handle, CurrentOffset, smFromBeginning); + Inc(ChannelDataSizes[I], SizeOf(Word) * ImageToSave.Height); + end; + end; + + if not SeparateChannelStorage and (Compression = CompressionRLE) then + begin + // Update channel RLE lengths + CurrentOffset := GetIO.Tell(Handle); + GetIO.Seek(Handle, RLETableOffset, smFromBeginning); + GetIO.Write(Handle, @RLELengths[0], SizeOf(Word) * ImageToSave.Height * Info.ChannelCount); + GetIO.Seek(Handle, CurrentOffset, smFromBeginning); + end; + + FreeMem(LineBuffer); + FreeMem(RLEBuffer); + end; + +begin + Result := False; + if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then + with GetIO, ImageToSave do + try + Info := GetFormatInfo(Format); + ChannelPixelSize := Info.BytesPerPixel div Info.ChannelCount; + + // Fill header with proper info and save it + FillChar(Header, SizeOf(Header), 0); + Header.Signature := SPSDMagic; + Header.Version := 1; + Header.Channels := Info.ChannelCount; + Header.Rows := Height; + Header.Columns := Width; + Header.Depth := Info.BytesPerPixel div Info.ChannelCount * 8; + if Info.IsIndexed then + Header.Mode := cmIndexed + else if Info.HasGrayChannel or (Info.ChannelCount = 1) then + Header.Mode := cmGrayscale + else + Header.Mode := cmRGB; + + SwapHeader(Header); + Write(Handle, @Header, SizeOf(Header)); + + // Write palette size and data + LongVal := SwapEndianLongWord(IffUnsigned(Info.IsIndexed, SizeOf(RawPal), 0)); + Write(Handle, @LongVal, SizeOf(LongVal)); + if Info.IsIndexed then + begin + for I := 0 to Info.PaletteEntries - 1 do + begin + RawPal[I] := Palette[I].R; + RawPal[I + 256] := Palette[I].G; + RawPal[I + 512] := Palette[I].B; + end; + Write(Handle, @RawPal, SizeOf(RawPal)); + end; + + // Write empty resource and layer block sizes + LongVal := 0; + Write(Handle, @LongVal, SizeOf(LongVal)); + LayerBlockOffset := Tell(Handle); + Write(Handle, @LongVal, SizeOf(LongVal)); + + if FSaveAsLayer and (ChannelPixelSize < 4) then // No Layers for FP32 images + begin + LayerCount := SwapEndianWord(Iff(Info.HasAlphaChannel, Word(-1), 1)); // Must be -1 to get transparency in Photoshop + R.Top := 0; + R.Left := 0; + R.Bottom := SwapEndianLongWord(Height); + R.Right := SwapEndianLongWord(Width); + WordVal := SwapEndianWord(Info.ChannelCount); + Write(Handle, @LongVal, SizeOf(LongVal)); // Layer section size, empty now + Write(Handle, @LayerCount, SizeOf(LayerCount)); // Layer count + Write(Handle, @R, SizeOf(R)); // Bounds rect + Write(Handle, @WordVal, SizeOf(WordVal)); // Channel count + + ChannelInfoOffset := Tell(Handle); + SetLength(ChannelDataSizes, Info.ChannelCount); // Empty channel infos + FillChar(ChannelInfo, SizeOf(ChannelInfo), 0); + for I := 0 to Info.ChannelCount - 1 do + Write(Handle, @ChannelInfo, SizeOf(ChannelInfo)); + + Write(Handle, @BlendMode, SizeOf(BlendMode)); // Blend mode = normal + Write(Handle, @LayerOptions, SizeOf(LayerOptions)); // Predefined options + LongVal := SwapEndianLongWord(16); // Extra data size (4 (mask size) + 4 (ranges size) + 8 (name)) + Write(Handle, @LongVal, SizeOf(LongVal)); + LongVal := 0; + Write(Handle, @LongVal, SizeOf(LongVal)); // Mask size = 0 + LongVal := 0; + Write(Handle, @LongVal, SizeOf(LongVal)); // Blend ranges size + Write(Handle, @LayerName, SizeOf(LayerName)); // Layer name + + WriteChannelData(True); // Write Layer image data + + Write(Handle, @LongVal, SizeOf(LongVal)); // Global mask info size = 0 + + SaveOffset := Tell(Handle); + Seek(Handle, LayerBlockOffset, smFromBeginning); + + // Update layer and mask section sizes + LongVal := SwapEndianLongWord(SaveOffset - LayerBlockOffset - 4); + Write(Handle, @LongVal, SizeOf(LongVal)); + LongVal := SwapEndianLongWord(SaveOffset - LayerBlockOffset - 8); + Write(Handle, @LongVal, SizeOf(LongVal)); + + // Update layer channel info + Seek(Handle, ChannelInfoOffset, smFromBeginning); + for I := 0 to Info.ChannelCount - 1 do + begin + ChannelInfo.ChannelID := SwapEndianWord(I); + if (I = Info.ChannelCount - 1) and Info.HasAlphaChannel then + ChannelInfo.ChannelID := Swap(Word(-1)); + ChannelInfo.Size := SwapEndianLongWord(ChannelDataSizes[I] + 2); // datasize (incl RLE table) + comp. flag + Write(Handle, @ChannelInfo, SizeOf(ChannelInfo)); + end; + + Seek(Handle, SaveOffset, smFromBeginning); + end; + + // Write background merged image + WriteChannelData(False); + + Result := True; + finally + if MustBeFreed then + FreeImage(ImageToSave); + end; +end; + +procedure TPSDFileFormat.ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); +var + ConvFormat: TImageFormat; +begin + if Info.IsFloatingPoint then + begin + if Info.ChannelCount = 1 then + ConvFormat := ifR32F + else if Info.HasAlphaChannel then + ConvFormat := ifA32R32G32B32F + else + ConvFormat := ifR32G32B32F; + end + else if Info.HasGrayChannel then + ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16Gray16, ifGray16) + else if Info.RBSwapFormat in GetSupportedFormats then + ConvFormat := Info.RBSwapFormat + else + ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifR8G8B8); + + ConvertImage(Image, ConvFormat); +end; + +function TPSDFileFormat.TestFormat(Handle: TImagingHandle): Boolean; +var + Header: TPSDHeader; + ReadCount: LongInt; +begin + Result := False; + if Handle <> nil then + begin + ReadCount := GetIO.Read(Handle, @Header, SizeOf(Header)); + SwapHeader(Header); + GetIO.Seek(Handle, -ReadCount, smFromCurrent); + Result := (ReadCount >= SizeOf(Header)) and + (Header.Signature = SPSDMagic) and + (Header.Version = 1); + end; +end; + +initialization + RegisterImageFileFormat(TPSDFileFormat); + +{ + File Notes: + + -- 0.77.1 --------------------------------------------------- + - 3 channel RGB float images are loaded and saved directly + as ifR32G32B32F. + + -- 0.26.1 Changes/Bug Fixes --------------------------------- + - PSDs are now saved with RLE compression. + - Mask layer saving added to SaveData for images with alpha + (shows proper transparency when opened in Photoshop). Can be + enabled/disabled using option + - Fixed memory leak in SaveData. + + -- 0.23 Changes/Bug Fixes ----------------------------------- + - Saving implemented. + - Loading implemented. + - Unit created with initial stuff! +} + +end. + diff --git a/resources/libraries/deskew/Imaging/ImagingQuartz.pas b/resources/libraries/deskew/Imaging/ImagingQuartz.pas new file mode 100755 index 0000000..ba93d94 --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingQuartz.pas @@ -0,0 +1,206 @@ +unit ImagingQuartz; + +{$I ImagingOptions.inc} + +{$IFNDEF MACOSX} + {$FATAL 'Mac OSX only'} +{$ENDIF} + +interface + +uses + Types, SysUtils, Classes, ImagingTypes, Imaging, ImagingUtility, +{$IFDEF DCC} + Macapi.CocoaTypes, Macapi.CoreFoundation, Macapi.CoreGraphics, Macapi.QuartzCore, Macapi.ImageIO +{$ELSE} + CGBase, CGShading, CGColor, CGColorSpace, CGContext, CGBitmapContext, CGImageSource, + CGImageDestination, CGDataProvider, CGDataConsumer, CFDictionary, CGAffineTransforms, CGPath +{$ENDIF}; + +type + TQuartzImageHandler = class + private + FDefaultColorSpace: CGColorSpaceRef; + function FindImageFormat(ImgRef: CGImageRef): TImageFormat; + public + constructor Create; + destructor Destroy; override; + function LoadImage(Stream: TCustomMemoryStream; var Images: TDynImageDataArray; OnlyFirstImage: Boolean): Boolean; + end; + +implementation + +function CGRectFromRect(const R: TRect): CGRect; +begin + Result.origin.x := R.Left; + Result.origin.Y := R.Top; + Result.size.Width := R.Right - R.Left; + Result.size.Height := R.Bottom - R.Top; +end; + +{ TMacImageHandler } + +constructor TQuartzImageHandler.Create; +begin + FDefaultColorSpace := CGColorSpaceCreateDeviceRGB; +end; + +destructor TQuartzImageHandler.Destroy; +begin + CGColorSpaceRelease(FDefaultColorSpace); + inherited; +end; + +function TQuartzImageHandler.FindImageFormat(ImgRef: CGImageRef): TImageFormat; +var + ColorSpaceRef: CGColorSpaceRef; + ColorModel: CGColorSpaceModel; + AlphaInfo: CGImageAlphaInfo; + BitmapInfo: CGBitmapInfo; + BitsPerPixel, Components, BitsPerComponent: Integer; + + isMask: integer; + intent: CGColorRenderingIntent; +begin + Result := ifUnknown; + AlphaInfo := CGImageGetAlphaInfo(ImgRef); + BitmapInfo := CGImageGetBitmapInfo(ImgRef); + ColorSpaceRef := CGImageGetColorSpace(ImgRef); + + intent := CGImageGetRenderingIntent(ImgRef); + isMask := CGImageIsMask(ImgRef); + +{ + Also check BitmapInfo + kCGBitmapByteOrderDefault = NO + kCGBitmapByteOrder16Little = NO + kCGBitmapByteOrder32Little = NO + kCGBitmapByteOrder16Big = NO + kCGBitmapByteOrder32Big = NO + + float formats + } + + if ColorSpaceRef <> nil then + begin + try + BitsPerPixel := CGImageGetBitsPerPixel(ImgRef); + BitsPerComponent := CGImageGetBitsPerComponent(ImgRef); + ColorModel := CGColorSpaceGetModel(ColorSpaceRef); + Components := CGColorSpaceGetNumberOfComponents(ColorSpaceRef); + + if (ColorModel = kCGColorSpaceModelMonochrome) and (Components = 1) then + begin + // Grayscale formats + if AlphaInfo = kCGImageAlphaFirst then + begin + if (BitsPerComponent = 8) and (BitsPerPixel = 16) then + Result := ifA8Gray8 + else if (BitsPerComponent = 16) and (BitsPerPixel = 32) then + Result := ifA16Gray16; + end + else if AlphaInfo = kCGImageAlphaNone then + begin + if BitsPerPixel = 8 then + Result := ifGray8 + else if BitsPerPixel = 16 then + Result := ifGray16; + end; + end + else if ColorModel = kCGColorSpaceModelRGB then + begin + // RGB + if (BitsPerPixel = 16) and (AlphaInfo = kCGImageAlphaNoneSkipFirst) then + begin + Result := ifX1R5G5B5; + end + else if AlphaInfo = kCGImageAlphaFirst then + begin + if (BitsPerComponent = 8) and (BitsPerPixel = 32) then + Result := ifA8R8G8B8 + else if (BitsPerComponent = 16) and (BitsPerPixel = 64) then + Result := ifA16R16G16B16; + end + else if AlphaInfo = kCGImageAlphaNone then + begin + if (BitsPerComponent = 8) and (BitsPerPixel = 24) then + Result := ifR8G8B8 + else if (BitsPerComponent = 16) and (BitsPerPixel = 48) then + Result := ifR16G16B16; + end; + end; + finally + CGColorSpaceRelease(ColorSpaceRef); + end; + end; +end; + +function TQuartzImageHandler.LoadImage(Stream: TCustomMemoryStream; var Images: TDynImageDataArray; OnlyFirstImage: Boolean): Boolean; +var + Provider: CGDataProviderRef; + PixelsData: CFDataRef; + PixelsPtr, DestPtr: PByteArray; + ImgSourceRef: CGImageSourceRef; + ImgRef: CGImageRef; + CtxRef: CGContextRef; + I, Count, Y: Integer; + Width, Height, BytesPerRow, WidthBytes: Integer; + ImgFormat: TImageFormat; +begin + Result := False; + Provider := CGDataProviderCreateWithData(nil, Stream.Memory, Stream.Size, nil); + if Provider <> nil then + begin + ImgSourceRef := CGImageSourceCreateWithDataProvider(Provider, nil); + if ImgSourceRef <> nil then + begin + Count := CGImageSourceGetCount(ImgSourceRef); + if (Count > 1) and OnlyFirstImage then + Count := 1; + SetLength(Images, Count); + + for I := 0 to Count - 1 do + begin + ImgRef := CGImageSourceCreateImageAtIndex(ImgSourceRef, I, nil); + if ImgRef <> nil then + begin + Width := CGImageGetWidth(ImgRef); + Height := CGImageGetHeight(ImgRef); + BytesPerRow := CGImageGetBytesPerRow(ImgRef); + ImgFormat := FindImageFormat(ImgRef); + + if ImgFormat = ifUnknown then + begin + NewImage(Width, Height, ifA8R8G8B8, Images[I]); + CtxRef := CGBitmapContextCreate(Images[I].Bits, Width, Height, 8, + Width * 4, FDefaultColorSpace, kCGImageAlphaPremultipliedFirst); + CGContextDrawImage(CtxRef, CGRectFromRect(Rect(0, 0, Width, Height)), ImgRef); + CGContextRelease(CtxRef); + end + else + begin + NewImage(Width, Height, ImgFormat, Images[I]); + DestPtr := PByteArray(Images[I].Bits); + WidthBytes := Images[I].Size div Height; + + PixelsData := CGDataProviderCopyData(CGImageGetDataProvider(ImgRef)); + PixelsPtr := PByteArray(CFDataGetBytePtr(PixelsData)); + + for Y := 0 to Height - 1 do + begin + // + Move(PixelsPtr[Y * BytesPerRow], DestPtr[Y * WidthBytes], WidthBytes); + end; + CFRelease(PixelsData); + end; + + CGImageRelease(ImgRef); + end; + end; + CFRelease(ImgSourceRef); + end; + end; +end; + +end. + diff --git a/resources/libraries/deskew/Imaging/ImagingRadiance.pas b/resources/libraries/deskew/Imaging/ImagingRadiance.pas new file mode 100755 index 0000000..3ed18d2 --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingRadiance.pas @@ -0,0 +1,495 @@ +{ + Vampyre Imaging Library + by Marek Mauder + http://imaginglib.sourceforge.net + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +{ This unit contains image format loader/saver for Radiance HDR/RGBE images.} +unit ImagingRadiance; + +{$I ImagingOptions.inc} + +interface + +uses + SysUtils, Classes, Imaging, ImagingTypes, ImagingUtility; + +type + { Radiance is a suite of tools for performing lighting simulation. It's + development started in 1985 and it pioneered the concept of + high dynamic range imaging. Radiance defined an image format for storing + HDR images, now described as RGBE image format. Since it was the first + HDR image format, this format is supported by many other software packages. + + Radiance image file consists of three sections: a header, resolution string, + followed by the pixel data. Each pixel is stored as 4 bytes, one byte + mantissa for each r, g, b and a shared one byte exponent. + The pixel data may be stored uncompressed or using run length encoding. + + Imaging translates RGBE pixels to original float values and stores them + in ifR32G32B32F data format. It can read both compressed and uncompressed + files, and saves files as compressed.} + THdrFileFormat = class(TImageFileFormat) + protected + procedure Define; override; + function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; + OnlyFirstLevel: Boolean): Boolean; override; + function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; + Index: LongInt): Boolean; override; + procedure ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); override; + public + function TestFormat(Handle: TImagingHandle): Boolean; override; + end; + +implementation + +uses + Math, ImagingIO; + +const + SHdrFormatName = 'Radiance HDR/RGBE'; + SHdrMasks = '*.hdr'; + HdrSupportedFormats: TImageFormats = [ifR32G32B32F]; + +type + TSignature = array[0..9] of AnsiChar; + THdrFormat = (hfRgb, hfXyz); + + THdrHeader = record + Format: THdrFormat; + Width: Integer; + Height: Integer; + end; + + TRgbe = packed record + R, G, B, E: Byte; + end; + TDynRgbeArray = array of TRgbe; + +const + RadianceSignature: TSignature = '#?RADIANCE'; + RgbeSignature: TSignature = '#?RGBE'; + SFmtRgbeRle = '32-bit_rle_rgbe'; + SFmtXyzeRle = '32-bit_rle_xyze'; + +resourcestring + SErrorBadHeader = 'Bad HDR/RGBE header format.'; + SWrongScanLineWidth = 'Wrong scanline width.'; + SXyzNotSupported = 'XYZ color space not supported.'; + +{ THdrFileFormat } + +procedure THdrFileFormat.Define; +begin + inherited; + FName := SHdrFormatName; + FFeatures := [ffLoad, ffSave]; + FSupportedFormats := HdrSupportedFormats; + + AddMasks(SHdrMasks); +end; + +function THdrFileFormat.LoadData(Handle: TImagingHandle; + var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; +var + Header: THdrHeader; + IO: TIOFunctions; + + function ReadHeader: Boolean; + const + CommentIds: TAnsiCharSet = ['#', '!']; + var + Line: AnsiString; + HasResolution: Boolean; + Count, Idx: Integer; + ValStr, NativeLine: string; + ValFloat: Double; + begin + Result := False; + HasResolution := False; + Count := 0; + + repeat + if not ReadLine(IO, Handle, Line) then + Exit; + + Inc(Count); + if Count > 16 then // Too long header for HDR + Exit; + + if Length(Line) = 0 then + Continue; + if Line[1] in CommentIds then + Continue; + + NativeLine := string(Line); + + if StrMaskMatch(NativeLine, 'Format=*') then + begin + // Data format parsing + ValStr := Copy(NativeLine, 8, MaxInt); + if ValStr = SFmtRgbeRle then + Header.Format := hfRgb + else if ValStr = SFmtXyzeRle then + Header.Format := hfXyz + else + Exit; + end; + + if StrMaskMatch(NativeLine, 'Gamma=*') then + begin + ValStr := Copy(NativeLine, 7, MaxInt); + if TryStrToFloat(ValStr, ValFloat, GetFormatSettingsForFloats) then + FMetadata.SetMetaItem(SMetaGamma, ValFloat); + end; + + if StrMaskMatch(NativeLine, 'Exposure=*') then + begin + ValStr := Copy(NativeLine, 10, MaxInt); + if TryStrToFloat(ValStr, ValFloat, GetFormatSettingsForFloats) then + FMetadata.SetMetaItem(SMetaExposure, ValFloat); + end; + + if StrMaskMatch(NativeLine, '?Y * ?X *') then + begin + Idx := Pos('X', NativeLine); + ValStr := SubString(NativeLine, 4, Idx - 2); + if not TryStrToInt(ValStr, Header.Height) then + Exit; + ValStr := Copy(NativeLine, Idx + 2, MaxInt); + if not TryStrToInt(ValStr, Header.Width) then + Exit; + + if (NativeLine[1] = '-') then + Header.Height := -Header.Height; + if (NativeLine[Idx - 1] = '-') then + Header.Width := -Header.Width; + + HasResolution := True; + end; + + until HasResolution; + Result := True; + end; + + procedure DecodeRgbe(const Src: TRgbe; Dest: PColor96FPRec); {$IFDEF USE_INLINE}inline;{$ENDIF} + var + Mult: Single; + begin + if Src.E > 0 then + begin + Mult := Math.Ldexp(1, Src.E - 128); + Dest.R := Src.R / 255 * Mult; + Dest.G := Src.G / 255 * Mult; + Dest.B := Src.B / 255 * Mult; + end + else + begin + Dest.R := 0; + Dest.G := 0; + Dest.B := 0; + end; + end; + + procedure ReadCompressedLine(Width, Y: Integer; var DestBuffer: TDynRgbeArray); + var + Pos: Integer; + I, X, Count: Integer; + Code, Value: Byte; + LineBuff: TDynByteArray; + Rgbe: TRgbe; + Ptr: PByte; + begin + SetLength(LineBuff, Width); + IO.Read(Handle, @Rgbe, SizeOf(Rgbe)); + + if ((Rgbe.B shl 8) or Rgbe.E) <> Width then + RaiseImaging(SWrongScanLineWidth); + + for I := 0 to 3 do + begin + Pos := 0; + while Pos < Width do + begin + IO.Read(Handle, @Code, SizeOf(Byte)); + if Code > 128 then + begin + Count := Code - 128; + IO.Read(Handle, @Value, SizeOf(Byte)); + FillMemoryByte(@LineBuff[Pos], Count, Value); + end + else + begin + Count := Code; + IO.Read(Handle, @LineBuff[Pos], Count * SizeOf(Byte)); + end; + Inc(Pos, Count); + end; + + Ptr := @PByteArray(@DestBuffer[0])[I]; + for X := 0 to Width - 1 do + begin + Ptr^ := LineBuff[X]; + Inc(Ptr, 4); + end; + end; + end; + + procedure ReadPixels(var Image: TImageData); + var + Y, X, SrcLineLen: Integer; + Dest: PColor96FPRec; + Compressed: Boolean; + Rgbe: TRgbe; + Buffer: TDynRgbeArray; + begin + Dest := Image.Bits; + Compressed := not ((Image.Width < 8) or (Image.Width > $7FFFF)); + SrcLineLen := Image.Width * SizeOf(TRgbe); + + IO.Read(Handle, @Rgbe, SizeOf(Rgbe)); + IO.Seek(Handle, -SizeOf(Rgbe), smFromCurrent); + + if (Rgbe.R <> 2) or (Rgbe.G <> 2) or ((Rgbe.B and 128) > 0) then + Compressed := False; + + SetLength(Buffer, Image.Width); + + for Y := 0 to Image.Height - 1 do + begin + if Compressed then + ReadCompressedLine(Image.Width, Y, Buffer) + else + IO.Read(Handle, @Buffer[0], SrcLineLen); + + for X := 0 to Image.Width - 1 do + begin + DecodeRgbe(Buffer[X], Dest); + Inc(Dest); + end; + end; + end; + +begin + IO := GetIO; + SetLength(Images, 1); + + // Read header, allocate new image and, then read and convert the pixels + if not ReadHeader then + RaiseImaging(SErrorBadHeader); + if (Header.Format = hfXyz) then + RaiseImaging(SXyzNotSupported); + + NewImage(Abs(Header.Width), Abs(Header.Height), ifR32G32B32F, Images[0]); + ReadPixels(Images[0]); + + // Flip/mirror the image as needed (height < 0 is default top-down) + if Header.Width < 0 then + MirrorImage(Images[0]); + if Header.Height > 0 then + FlipImage(Images[0]); + + Result := True; +end; + +function THdrFileFormat.SaveData(Handle: TImagingHandle; + const Images: TDynImageDataArray; Index: Integer): Boolean; +const + LineEnd = #$0A; + SPrgComment = '#Made with Vampyre Imaging Library'; + SSizeFmt = '-Y %d +X %d'; +var + ImageToSave: TImageData; + MustBeFreed: Boolean; + IO: TIOFunctions; + + procedure SaveHeader; + begin + WriteLine(IO, Handle, RadianceSignature, LineEnd); + WriteLine(IO, Handle, SPrgComment, LineEnd); + WriteLine(IO, Handle, 'FORMAT=' + SFmtRgbeRle, LineEnd + LineEnd); + WriteLine(IO, Handle, AnsiString(Format(SSizeFmt, [ImageToSave.Height, ImageToSave.Width])), LineEnd); + end; + + procedure EncodeRgbe(const Src: TColor96FPRec; var DestR, DestG, DestB, DestE: Byte); {$IFDEF USE_INLINE}inline;{$ENDIF} + var + V, M: {$IFDEF FPC}Float{$ELSE}Extended{$ENDIF}; + E: Integer; + begin + V := Src.R; + if (Src.G > V) then + V := Src.G; + if (Src.B > V) then + V := Src.B; + + if V < 1e-32 then + begin + DestR := 0; + DestG := 0; + DestB := 0; + DestE := 0; + end + else + begin + Frexp(V, M, E); + V := M * 256.0 / V; + DestR := ClampToByte(Round(Src.R * V)); + DestG := ClampToByte(Round(Src.G * V)); + DestB := ClampToByte(Round(Src.B * V)); + DestE := ClampToByte(E + 128); + end; + end; + + procedure WriteRleLine(const Line: array of Byte; Width: Integer); + const + MinRunLength = 4; + var + Cur, BeginRun, RunCount, OldRunCount, NonRunCount: Integer; + Buf: array[0..1] of Byte; + begin + Cur := 0; + while Cur < Width do + begin + BeginRun := Cur; + RunCount := 0; + OldRunCount := 0; + while (RunCount < MinRunLength) and (BeginRun < Width) do + begin + Inc(BeginRun, RunCount); + OldRunCount := RunCount; + RunCount := 1; + while (BeginRun + RunCount < Width) and (RunCount < 127) and (Line[BeginRun] = Line[BeginRun + RunCount]) do + Inc(RunCount); + end; + if (OldRunCount > 1) and (OldRunCount = BeginRun - Cur) then + begin + Buf[0] := 128 + OldRunCount; + Buf[1] := Line[Cur]; + IO.Write(Handle, @Buf, 2); + Cur := BeginRun; + end; + while Cur < BeginRun do + begin + NonRunCount := Min(128, BeginRun - Cur); + Buf[0] := NonRunCount; + IO.Write(Handle, @Buf, 1); + IO.Write(Handle, @Line[Cur], NonRunCount); + Inc(Cur, NonRunCount); + end; + if RunCount >= MinRunLength then + begin + Buf[0] := 128 + RunCount; + Buf[1] := Line[BeginRun]; + IO.Write(Handle, @Buf, 2); + Inc(Cur, RunCount); + end; + end; + end; + + procedure SavePixels; + var + Y, X, I, Width: Integer; + SrcPtr: PColor96FPRecArray; + Components: array of array of Byte; + StartLine: array[0..3] of Byte; + begin + Width := ImageToSave.Width; + // Save using RLE, each component is compressed separately + SetLength(Components, 4, Width); + + for Y := 0 to ImageToSave.Height - 1 do + begin + SrcPtr := @PColor96FPRecArray(ImageToSave.Bits)[ImageToSave.Width * Y]; + + // Identify line as using "new" RLE scheme (separate components) + StartLine[0] := 2; + StartLine[1] := 2; + StartLine[2] := Width shr 8; + StartLine[3] := Width and $FF; + IO.Write(Handle, @StartLine, SizeOf(StartLine)); + + for X := 0 to Width - 1 do + begin + EncodeRgbe(SrcPtr[X], Components[0, X], Components[1, X], + Components[2, X], Components[3, X]); + end; + + for I := 0 to 3 do + WriteRleLine(Components[I], Width); + end; + end; + +begin + Result := False; + IO := GetIO; + // Makes image to save compatible with Jpeg saving capabilities + if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then + with ImageToSave do + try + // Save header + SaveHeader; + // Save uncompressed pixels + SavePixels; + finally + if MustBeFreed then + FreeImage(ImageToSave); + end; +end; + +procedure THdrFileFormat.ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); +begin + ConvertImage(Image, ifR32G32B32F); +end; + +function THdrFileFormat.TestFormat(Handle: TImagingHandle): Boolean; +var + FileSig: TSignature; + ReadCount: Integer; +begin + Result := False; + if Handle <> nil then + begin + ReadCount := GetIO.Read(Handle, @FileSig, SizeOf(FileSig)); + GetIO.Seek(Handle, -ReadCount, smFromCurrent); + Result := (ReadCount = SizeOf(FileSig)) and + ((FileSig = RadianceSignature) or CompareMem(@FileSig, @RgbeSignature, 6)); + end; +end; + +initialization + RegisterImageFileFormat(THdrFileFormat); + +{ + File Notes: + + -- 0.77.1 --------------------------------------------------- + - Added RLE compression to saving. + - Added image saving. + - Unit created with initial stuff (loading only). + +} + +end. diff --git a/resources/libraries/deskew/Imaging/ImagingTarga.pas b/resources/libraries/deskew/Imaging/ImagingTarga.pas new file mode 100755 index 0000000..a1d4c75 --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingTarga.pas @@ -0,0 +1,620 @@ +{ + Vampyre Imaging Library + by Marek Mauder + http://imaginglib.sourceforge.net + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +{ This unit contains image format loader/saver for Targa images.} +unit ImagingTarga; + +{$I ImagingOptions.inc} + +interface + +uses + ImagingTypes, Imaging, ImagingFormats, ImagingUtility; + +type + { Class for loading and saving Truevision Targa images. + It can load/save 8bit indexed or grayscale, 16 bit RGB or grayscale, + 24 bit RGB and 32 bit ARGB images with or without RLE compression.} + TTargaFileFormat = class(TImageFileFormat) + protected + FUseRLE: LongBool; + procedure Define; override; + function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; + OnlyFirstLevel: Boolean): Boolean; override; + function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; + Index: LongInt): Boolean; override; + procedure ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); override; + public + function TestFormat(Handle: TImagingHandle): Boolean; override; + published + { Controls that RLE compression is used during saving. Accessible trough + ImagingTargaRLE option.} + property UseRLE: LongBool read FUseRLE write FUseRLE; + end; + +implementation + +const + STargaFormatName = 'Truevision Targa Image'; + STargaMasks = '*.tga'; + TargaSupportedFormats: TImageFormats = [ifIndex8, ifGray8, ifA1R5G5B5, + ifR8G8B8, ifA8R8G8B8]; + TargaDefaultRLE = False; + +const + STargaSignature = 'TRUEVISION-XFILE'; + +type + { Targa file header.} + TTargaHeader = packed record + IDLength: Byte; + ColorMapType: Byte; + ImageType: Byte; + ColorMapOff: Word; + ColorMapLength: Word; + ColorEntrySize: Byte; + XOrg: SmallInt; + YOrg: SmallInt; + Width: SmallInt; + Height: SmallInt; + PixelSize: Byte; + Desc: Byte; + end; + + { Footer at the end of TGA file.} + TTargaFooter = packed record + ExtOff: LongWord; // Extension Area Offset + DevDirOff: LongWord; // Developer Directory Offset + Signature: TChar16; // TRUEVISION-XFILE + Reserved: Byte; // ASCII period '.' + NullChar: Byte; // 0 + end; + + +{ TTargaFileFormat class implementation } + +procedure TTargaFileFormat.Define; +begin + inherited; + FName := STargaFormatName; + FFeatures := [ffLoad, ffSave]; + FSupportedFormats := TargaSupportedFormats; + + FUseRLE := TargaDefaultRLE; + + AddMasks(STargaMasks); + RegisterOption(ImagingTargaRLE, @FUseRLE); +end; + +function TTargaFileFormat.LoadData(Handle: TImagingHandle; + var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; +var + Hdr: TTargaHeader; + Foo: TTargaFooter; + FooterFound, ExtFound: Boolean; + I, PSize, PalSize: LongWord; + Pal: Pointer; + FmtInfo: TImageFormatInfo; + WordValue: Word; + + procedure LoadRLE; + var + I, CPixel, Cnt: LongInt; + Bpp, Rle: Byte; + Buffer, Dest, Src: PByte; + BufSize: LongInt; + begin + with GetIO, Images[0] do + begin + // Alocates buffer large enough to hold the worst case + // RLE compressed data and reads then from input + BufSize := Width * Height * FmtInfo.BytesPerPixel; + BufSize := BufSize + BufSize div 2 + 1; + GetMem(Buffer, BufSize); + Src := Buffer; + Dest := Bits; + BufSize := Read(Handle, Buffer, BufSize); + + Cnt := Width * Height; + Bpp := FmtInfo.BytesPerPixel; + CPixel := 0; + while CPixel < Cnt do + begin + Rle := Src^; + Inc(Src); + if Rle < 128 then + begin + // Process uncompressed pixel + Rle := Rle + 1; + CPixel := CPixel + Rle; + for I := 0 to Rle - 1 do + begin + // Copy pixel from src to dest + case Bpp of + 1: Dest^ := Src^; + 2: PWord(Dest)^ := PWord(Src)^; + 3: PColor24Rec(Dest)^ := PColor24Rec(Src)^; + 4: PLongWord(Dest)^ := PLongWord(Src)^; + end; + Inc(Src, Bpp); + Inc(Dest, Bpp); + end; + end + else + begin + // Process compressed pixels + Rle := Rle - 127; + CPixel := CPixel + Rle; + // Copy one pixel from src to dest (many times there) + for I := 0 to Rle - 1 do + begin + case Bpp of + 1: Dest^ := Src^; + 2: PWord(Dest)^ := PWord(Src)^; + 3: PColor24Rec(Dest)^ := PColor24Rec(Src)^; + 4: PLongWord(Dest)^ := PLongWord(Src)^; + end; + Inc(Dest, Bpp); + end; + Inc(Src, Bpp); + end; + end; + // set position in source to real end of compressed data + Seek(Handle, -(BufSize - LongInt(LongWord(Src) - LongWord(Buffer))), + smFromCurrent); + FreeMem(Buffer); + end; + end; + +begin + SetLength(Images, 1); + with GetIO, Images[0] do + begin + // Read targa header + Read(Handle, @Hdr, SizeOf(Hdr)); + // Skip image ID info + Seek(Handle, Hdr.IDLength, smFromCurrent); + // Determine image format + Format := ifUnknown; + case Hdr.ImageType of + 1, 9: Format := ifIndex8; + 2, 10: case Hdr.PixelSize of + 15: Format := ifX1R5G5B5; + 16: Format := ifA1R5G5B5; + 24: Format := ifR8G8B8; + 32: Format := ifA8R8G8B8; + end; + 3, 11: Format := ifGray8; + end; + // Format was not assigned by previous testing (it should be in + // well formed targas), so formats which reflects bit dept are selected + if Format = ifUnknown then + case Hdr.PixelSize of + 8: Format := ifGray8; + 15: Format := ifX1R5G5B5; + 16: Format := ifA1R5G5B5; + 24: Format := ifR8G8B8; + 32: Format := ifA8R8G8B8; + end; + NewImage(Hdr.Width, Hdr.Height, Format, Images[0]); + FmtInfo := GetFormatInfo(Format); + + if (Hdr.ColorMapType = 1) and (Hdr.ImageType in [1, 9]) then + begin + // Read palette + PSize := Hdr.ColorMapLength * (Hdr.ColorEntrySize shr 3); + GetMem(Pal, PSize); + try + Read(Handle, Pal, PSize); + // Process palette + PalSize := Iff(Hdr.ColorMapLength > FmtInfo.PaletteEntries, + FmtInfo.PaletteEntries, Hdr.ColorMapLength); + for I := 0 to PalSize - 1 do + case Hdr.ColorEntrySize of + 24: + with Palette[I] do + begin + A := $FF; + R := PPalette24(Pal)[I].R; + G := PPalette24(Pal)[I].G; + B := PPalette24(Pal)[I].B; + end; + // I've never seen tga with these palettes so they are untested + 16: + with Palette[I] do + begin + A := (PWordArray(Pal)[I] and $8000) shr 12; + R := (PWordArray(Pal)[I] and $FC00) shr 7; + G := (PWordArray(Pal)[I] and $03E0) shr 2; + B := (PWordArray(Pal)[I] and $001F) shl 3; + end; + 32: + with Palette[I] do + begin + A := PPalette32(Pal)[I].A; + R := PPalette32(Pal)[I].R; + G := PPalette32(Pal)[I].G; + B := PPalette32(Pal)[I].B; + end; + end; + finally + FreeMemNil(Pal); + end; + end; + + case Hdr.ImageType of + 0, 1, 2, 3: + // Load uncompressed mode images + Read(Handle, Bits, Size); + 9, 10, 11: + // Load RLE compressed mode images + LoadRLE; + end; + + // Check if there is alpha channel present in A1R5GB5 images, if it is not + // change format to X1R5G5B5 + if Format = ifA1R5G5B5 then + begin + if not Has16BitImageAlpha(Width * Height, Bits) then + Format := ifX1R5G5B5; + end; + + // We must find true end of file and set input' position to it + // paint programs appends extra info at the end of Targas + // some of them multiple times (PSP Pro 8) + repeat + ExtFound := False; + FooterFound := False; + + if Read(Handle, @WordValue, 2) = 2 then + begin + // 495 = size of Extension Area + if WordValue = 495 then + begin + Seek(Handle, 493, smFromCurrent); + ExtFound := True; + end + else + Seek(Handle, -2, smFromCurrent); + end; + + if Read(Handle, @Foo, SizeOf(Foo)) = SizeOf(Foo) then + begin + if Foo.Signature = STargaSignature then + FooterFound := True + else + Seek(Handle, -SizeOf(Foo), smFromCurrent); + end; + until (not ExtFound) and (not FooterFound); + + // Some editors save targas flipped + if Hdr.Desc < 31 then + FlipImage(Images[0]); + + Result := True; + end; +end; + +function TTargaFileFormat.SaveData(Handle: TImagingHandle; + const Images: TDynImageDataArray; Index: LongInt): Boolean; +var + I: LongInt; + Hdr: TTargaHeader; + FmtInfo: TImageFormatInfo; + Pal: PPalette24; + ImageToSave: TImageData; + MustBeFreed: Boolean; + + procedure SaveRLE; + var + Dest: PByte; + WidthBytes, Written, I, Total, DestSize: LongInt; + + function CountDiff(Data: PByte; Bpp, PixelCount: Longint): LongInt; + var + Pixel: LongWord; + NextPixel: LongWord; + N: LongInt; + begin + N := 0; + Pixel := 0; + NextPixel := 0; + if PixelCount = 1 then + begin + Result := PixelCount; + Exit; + end; + case Bpp of + 1: Pixel := Data^; + 2: Pixel := PWord(Data)^; + 3: PColor24Rec(@Pixel)^ := PColor24Rec(Data)^; + 4: Pixel := PLongWord(Data)^; + end; + while PixelCount > 1 do + begin + Inc(Data, Bpp); + case Bpp of + 1: NextPixel := Data^; + 2: NextPixel := PWord(Data)^; + 3: PColor24Rec(@NextPixel)^ := PColor24Rec(Data)^; + 4: NextPixel := PLongWord(Data)^; + end; + if NextPixel = Pixel then + Break; + Pixel := NextPixel; + N := N + 1; + PixelCount := PixelCount - 1; + end; + if NextPixel = Pixel then + Result := N + else + Result := N + 1; + end; + + function CountSame(Data: PByte; Bpp, PixelCount: LongInt): LongInt; + var + Pixel: LongWord; + NextPixel: LongWord; + N: LongInt; + begin + N := 1; + Pixel := 0; + NextPixel := 0; + case Bpp of + 1: Pixel := Data^; + 2: Pixel := PWord(Data)^; + 3: PColor24Rec(@Pixel)^ := PColor24Rec(Data)^; + 4: Pixel := PLongWord(Data)^; + end; + PixelCount := PixelCount - 1; + while PixelCount > 0 do + begin + Inc(Data, Bpp); + case Bpp of + 1: NextPixel := Data^; + 2: NextPixel := PWord(Data)^; + 3: PColor24Rec(@NextPixel)^ := PColor24Rec(Data)^; + 4: NextPixel := PLongWord(Data)^; + end; + if NextPixel <> Pixel then + Break; + N := N + 1; + PixelCount := PixelCount - 1; + end; + Result := N; + end; + + procedure RleCompressLine(Data: PByte; PixelCount, Bpp: LongInt; Dest: + PByte; var Written: LongInt); + const + MaxRun = 128; + var + DiffCount: LongInt; + SameCount: LongInt; + RleBufSize: LongInt; + begin + RleBufSize := 0; + while PixelCount > 0 do + begin + DiffCount := CountDiff(Data, Bpp, PixelCount); + SameCount := CountSame(Data, Bpp, PixelCount); + if (DiffCount > MaxRun) then + DiffCount := MaxRun; + if (SameCount > MaxRun) then + SameCount := MaxRun; + if (DiffCount > 0) then + begin + Dest^ := Byte(DiffCount - 1); + Inc(Dest); + PixelCount := PixelCount - DiffCount; + RleBufSize := RleBufSize + (DiffCount * Bpp) + 1; + Move(Data^, Dest^, DiffCount * Bpp); + Inc(Data, DiffCount * Bpp); + Inc(Dest, DiffCount * Bpp); + end; + if SameCount > 1 then + begin + Dest^ := Byte((SameCount - 1) or $80); + Inc(Dest); + PixelCount := PixelCount - SameCount; + RleBufSize := RleBufSize + Bpp + 1; + Inc(Data, (SameCount - 1) * Bpp); + case Bpp of + 1: Dest^ := Data^; + 2: PWord(Dest)^ := PWord(Data)^; + 3: PColor24Rec(Dest)^ := PColor24Rec(Data)^; + 4: PLongWord(Dest)^ := PLongWord(Data)^; + end; + Inc(Data, Bpp); + Inc(Dest, Bpp); + end; + end; + Written := RleBufSize; + end; + + begin + with ImageToSave do + begin + // Allocate enough space to hold the worst case compression + // result and then compress source's scanlines + WidthBytes := Width * FmtInfo.BytesPerPixel; + DestSize := WidthBytes * Height; + DestSize := DestSize + DestSize div 2 + 1; + GetMem(Dest, DestSize); + Total := 0; + try + for I := 0 to Height - 1 do + begin + RleCompressLine(@PByteArray(Bits)[I * WidthBytes], Width, + FmtInfo.BytesPerPixel, @PByteArray(Dest)[Total], Written); + Total := Total + Written; + end; + GetIO.Write(Handle, Dest, Total); + finally + FreeMem(Dest); + end; + end; + end; + +begin + Result := False; + if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then + with GetIO, ImageToSave do + try + FmtInfo := GetFormatInfo(Format); + // Fill targa header + FillChar(Hdr, SizeOf(Hdr), 0); + Hdr.IDLength := 0; + Hdr.ColorMapType := Iff(FmtInfo.PaletteEntries > 0, 1, 0); + Hdr.Width := Width; + Hdr.Height := Height; + Hdr.PixelSize := FmtInfo.BytesPerPixel * 8; + Hdr.ColorMapLength := FmtInfo.PaletteEntries; + Hdr.ColorEntrySize := Iff(FmtInfo.PaletteEntries > 0, 24, 0); + Hdr.ColorMapOff := 0; + // This indicates that targa is stored in top-left format + // as our images -> no flipping is needed. + Hdr.Desc := 32; + // Set alpha channel size in descriptor (mostly ignored by other software though) + if Format = ifA8R8G8B8 then + Hdr.Desc := Hdr.Desc or 8 + else if Format = ifA1R5G5B5 then + Hdr.Desc := Hdr.Desc or 1; + + // Choose image type + if FmtInfo.IsIndexed then + Hdr.ImageType := Iff(FUseRLE, 9, 1) + else + if FmtInfo.HasGrayChannel then + Hdr.ImageType := Iff(FUseRLE, 11, 3) + else + Hdr.ImageType := Iff(FUseRLE, 10, 2); + + Write(Handle, @Hdr, SizeOf(Hdr)); + + // Write palette + if FmtInfo.PaletteEntries > 0 then + begin + GetMem(Pal, FmtInfo.PaletteEntries * SizeOf(TColor24Rec)); + try + for I := 0 to FmtInfo.PaletteEntries - 1 do + with Pal[I] do + begin + R := Palette[I].R; + G := Palette[I].G; + B := Palette[I].B; + end; + Write(Handle, Pal, FmtInfo.PaletteEntries * SizeOf(TColor24Rec)); + finally + FreeMemNil(Pal); + end; + end; + + if FUseRLE then + // Save rle compressed mode images + SaveRLE + else + // Save uncompressed mode images + Write(Handle, Bits, Size); + + Result := True; + finally + if MustBeFreed then + FreeImage(ImageToSave); + end; +end; + +procedure TTargaFileFormat.ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); +var + ConvFormat: TImageFormat; +begin + if Info.HasGrayChannel then + // Convert all grayscale images to Gray8 (preserve alpha of AxGrayx formats) + ConvFormat := IffFormat(not Info.HasAlphaChannel, ifGray8, ifA8R8G8B8) + else if Info.IsIndexed then + // Convert all indexed images to Index8 + ConvFormat := ifIndex8 + else if Info.HasAlphaChannel then + // Convert images with alpha channel to A8R8G8B8 + ConvFormat := ifA8R8G8B8 + else if Info.UsePixelFormat then + // Convert 16bit images (without alpha channel) to A1R5G5B5 + ConvFormat := ifA1R5G5B5 + else + // Convert all other formats to R8G8B8 + ConvFormat := ifR8G8B8; + + ConvertImage(Image, ConvFormat); +end; + +function TTargaFileFormat.TestFormat(Handle: TImagingHandle): Boolean; +var + Hdr: TTargaHeader; + ReadCount: LongInt; +begin + Result := False; + if Handle <> nil then + begin + ReadCount := GetIO.Read(Handle, @Hdr, SizeOf(Hdr)); + GetIO.Seek(Handle, -ReadCount, smFromCurrent); + Result := (ReadCount >= SizeOf(Hdr)) and + (Hdr.ImageType in [0, 1, 2, 3, 9, 10, 11]) and + (Hdr.PixelSize in [1, 8, 15, 16, 24, 32]) and + (Hdr.ColorEntrySize in [0, 16, 24, 32]); + end; +end; + +initialization + RegisterImageFileFormat(TTargaFileFormat); + +{ + File Notes: + + -- TODOS ---------------------------------------------------- + - nothing now + + -- 0.21 Changes/Bug Fixes ----------------------------------- + - MakeCompatible method moved to base class, put ConvertToSupported here. + GetSupportedFormats removed, it is now set in constructor. + - Made public properties for options registered to SetOption/GetOption + functions. + - Changed extensions to filename masks. + - Changed SaveData, LoadData, and MakeCompatible methods according + to changes in base class in Imaging unit. + + -- 0.17 Changes/Bug Fixes ----------------------------------- + - 16 bit images are usually without alpha but some has alpha + channel and there is no indication of it - so I have added + a check: if all pixels of image are with alpha = 0 image is treated + as X1R5G5B5 otherwise as A1R5G5B5 + - fixed problems with some nonstandard 15 bit images +} + +end. + diff --git a/resources/libraries/deskew/Imaging/ImagingTiff.pas b/resources/libraries/deskew/Imaging/ImagingTiff.pas new file mode 100755 index 0000000..35d16fc --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingTiff.pas @@ -0,0 +1,104 @@ +unit ImagingTiff; + +{$I ImagingOptions.inc} + +interface + +uses + SysUtils, Imaging, ImagingTypes, ImagingUtility, ImagingIO, ImagingExtras; + +type + { TIFF (Tag Image File Format) loader/saver base class.} + TBaseTiffFileFormat = class(TImageFileFormat) + protected + FCompression: Integer; + FJpegQuality: Integer; + procedure Define; override; + public + function TestFormat(Handle: TImagingHandle): Boolean; override; + { Specifies compression scheme used when saving TIFF images. Supported values + are 0 (Uncompressed), 1 (LZW), 2 (PackBits RLE), 3 (Deflate - ZLib), 4 (JPEG), + 5 (CCITT Group 4 fax encoding - for binary images only). + Default is 1 (LZW). Note that not all images can be stored with + JPEG compression - these images will be saved with default compression if + JPEG is set.} + property Compression: Integer read FCompression write FCompression; + { Controls compression quality when selected TIFF compression is Jpeg. + It is number in range 1..100. 1 means small/ugly file, + 100 means large/nice file. Accessible trough ImagingTiffJpegQuality option.} + property JpegQuality: Integer read FJpegQuality write FJpegQuality; + end; + +const + TiffCompressionOptionNone = 0; + TiffCompressionOptionLzw = 1; + TiffCompressionOptionPackbitsRle = 2; + TiffCompressionOptionDeflate = 3; + TiffCompressionOptionJpeg = 4; + TiffCompressionOptionGroup4 = 5; + + { Read only metadata info - name of compression scheme (LZW, none, JPEG, G4, ...) + used in last loaded TIFF. } + SMetaTiffCompressionName = 'TiffCompressionName'; + { Original resolution unit of loaded TIFF. Type is UInt. + RESUNIT_NONE = 1; // no meaningful units + RESUNIT_INCH = 2; // english + RESUNIT_CENTIMETER = 3; // metric } + SMetaTiffResolutionUnit = 'TiffResolutionUnit'; + +implementation + +{$IFNDEF DONT_LINK_FILE_FORMATS} +// So far we have only one TIFF support implementation - libtiff +// Note that libtiff for FPC ARM is disabled by default due to potential hardfp/softfp +// ABI problems (without linking to any lib FPC generated binary does not call "ld" +// and hardfp exe can run on softfp target). If you know what you're doing enable it. +{$IF (Defined(DELPHI) and not Defined(CPUX64)) or (Defined(FPC) and not Defined(CPUARM)))} +uses + ImagingTiffLib; +{$IFEND} +{$ENDIF} + +const + STiffFormatName = 'Tagged Image File Format'; + STiffMasks = '*.tif,*.tiff'; + TiffDefaultCompression = 1; + TiffDefaultJpegQuality = 90; + +const + TiffBEMagic: TChar4 = 'MM'#0#42; + TiffLEMagic: TChar4 = 'II'#42#0; + +{ + TBaseTiffFileFormat implementation +} + +procedure TBaseTiffFileFormat.Define; +begin + inherited; + FName := STiffFormatName; + FFeatures := [ffLoad, ffSave, ffMultiImage]; + FCompression := TiffDefaultCompression; + FJpegQuality := TiffDefaultJpegQuality; + + AddMasks(STiffMasks); + RegisterOption(ImagingTiffCompression, @FCompression); + RegisterOption(ImagingTiffJpegQuality, @FJpegQuality); +end; + +function TBaseTiffFileFormat.TestFormat(Handle: TImagingHandle): Boolean; +var + Magic: TChar4; + ReadCount: LongInt; +begin + Result := False; + if Handle <> nil then + begin + ReadCount := GetIO.Read(Handle, @Magic, SizeOf(Magic)); + GetIO.Seek(Handle, -ReadCount, smFromCurrent); + Result := (ReadCount >= SizeOf(Magic)) and + ((Magic = TiffBEMagic) or (Magic = TiffLEMagic)); + end; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/ImagingTiffMac.pas b/resources/libraries/deskew/Imaging/ImagingTiffMac.pas new file mode 100755 index 0000000..6f894eb --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingTiffMac.pas @@ -0,0 +1,74 @@ +unit ImagingTiffMac; + +{$I ImagingOptions.inc} + +{$IFNDEF MACOSX} + {$FATAL 'Mac OSX only'} +{$ENDIF} + +interface + +uses + Types, SysUtils, Classes, Imaging, ImagingTypes, ImagingTiff, ImagingUtility; + +type + + TTiffMacFileFormat = class(TTiffFileFormat) + protected + procedure Define; override; + function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; + OnlyFirstLevel: Boolean): Boolean; override; + function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; + Index: Integer): Boolean; override; + procedure ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); override; + end; + +implementation + +uses + ImagingQuartz, ImagingIO; + +{ TTiffMacFileFormat } + +procedure TTiffMacFileFormat.Define; +begin + inherited; +end; + +function TTiffMacFileFormat.LoadData(Handle: TImagingHandle; + var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; +var + Stream: TCustomMemoryStream; + Handler: TQuartzImageHandler; +begin + Stream := TReadMemoryStream.CreateFromIOHandle(GetIO, Handle); + Handler := TQuartzImageHandler.Create; + try + Handler.LoadImage(Stream, Images, OnlyFirstLevel); + finally + Handler.Free; + Stream.Free; + end; +end; + +function TTiffMacFileFormat.SaveData(Handle: TImagingHandle; + const Images: TDynImageDataArray; Index: Integer): Boolean; +begin + +end; + +procedure TTiffMacFileFormat.ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); +begin + inherited; + +end; + +initialization + RegisterImageFileFormat(TTiffMacFileFormat); + + + +end. + diff --git a/resources/libraries/deskew/Imaging/ImagingTypes.pas b/resources/libraries/deskew/Imaging/ImagingTypes.pas new file mode 100755 index 0000000..28c9b9f --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingTypes.pas @@ -0,0 +1,568 @@ +{ + Vampyre Imaging Library + by Marek Mauder + http://imaginglib.sourceforge.net + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +{ This unit contains basic types and constants used by Imaging library.} +unit ImagingTypes; + +{$I ImagingOptions.inc} + +interface + +const + { Current Major version of Imaging.} + ImagingVersionMajor = 0; + { Current Minor version of Imaging.} + ImagingVersionMinor = 80; + + { Imaging Option Ids whose values can be set/get by SetOption/ + GetOption functions.} + + { Defines Jpeg compression quality, ranges from 1 (ugly/small) to 100 (nice/large). + Default value is 90.} + ImagingJpegQuality = 10; + { Specifies whether Jpeg images are saved in progressive format, + can be 0 or 1. Default value is 0.} + ImagingJpegProgressive = 11; + + { Specifies whether Windows Bitmaps are saved using RLE compression + (only for 1/4/8 bit images), can be 0 or 1. Default value is 1.} + ImagingBitmapRLE = 12; + + { Specifies whether Targa images are saved using RLE compression, + can be 0 or 1. Default value is 0.} + ImagingTargaRLE = 13; + + { Value of this option is non-zero if last loaded DDS file was cube map.} + ImagingDDSLoadedCubeMap = 14; + { Value of this option is non-zero if last loaded DDS file was volume texture.} + ImagingDDSLoadedVolume = 15; + { Value of this option is number of mipmap levels of last loaded DDS image.} + ImagingDDSLoadedMipMapCount = 16; + { Value of this option is depth (slices of volume texture or faces of + cube map) of last loaded DDS image.} + ImagingDDSLoadedDepth = 17; + { If it is non-zero next saved DDS file should be stored as cube map.} + ImagingDDSSaveCubeMap = 18; + { If it is non-zero next saved DDS file should be stored as volume texture.} + ImagingDDSSaveVolume = 19; + { Sets the number of mipmaps which should be stored in the next saved DDS file. + Only applies to cube maps and volumes, ordinary 2D textures save all + levels present in input.} + ImagingDDSSaveMipMapCount = 20; + { Sets the depth (slices of volume texture or faces of cube map) + of the next saved DDS file.} + ImagingDDSSaveDepth = 21; + + { Sets precompression filter used when saving PNG images. Allowed values + are: 0 (none), 1 (sub), 2 (up), 3 (average), 4 (paeth), + 5 (use 0 for indexed/gray images and 4 for RGB/ARGB images), + 6 (adaptive filtering - use best filter for each scanline - very slow). + Note that filters 3 and 4 are much slower than filters 1 and 2. + Default value is 5.} + ImagingPNGPreFilter = 25; + { Sets ZLib compression level used when saving PNG images. + Allowed values are in range 0 (no compresstion) to 9 (best compression). + Default value is 5.} + ImagingPNGCompressLevel = 26; + { Boolean option that specifies whether PNG images with more frames (APNG format) + are animated by Imaging (according to frame disposal/blend methods) or just + raw frames are loaded and sent to user (if you want to animate APNG yourself). + Default value is 1.} + ImagingPNGLoadAnimated = 27; + { Sets ZLib compression strategy used when saving PNG files (see deflateInit2() + in ZLib for details). Allowed values are: 0 (default), 1 (filtered), + 2 (huffman only). Default value is 0.} + ImagingPNGZLibStrategy = 28; + + { Specifies whether MNG animation frames are saved with lossy or lossless + compression. Lossless frames are saved as PNG images and lossy frames are + saved as JNG images. Allowed values are 0 (False) and 1 (True). + Default value is 0.} + ImagingMNGLossyCompression = 32; + { Defines whether alpha channel of lossy compressed MNG frames + (when ImagingMNGLossyCompression is 1) is lossy compressed too. + Allowed values are 0 (False) and 1 (True). Default value is 0.} + ImagingMNGLossyAlpha = 33; + { Sets precompression filter used when saving MNG frames as PNG images. + For details look at ImagingPNGPreFilter.} + ImagingMNGPreFilter = 34; + { Sets ZLib compression level used when saving MNG frames as PNG images. + For details look at ImagingPNGCompressLevel.} + ImagingMNGCompressLevel = 35; + { Specifies compression quality used when saving MNG frames as JNG images. + For details look at ImagingJpegQuality.} + ImagingMNGQuality = 36; + { Specifies whether images are saved in progressive format when saving MNG + frames as JNG images. For details look at ImagingJpegProgressive.} + ImagingMNGProgressive = 37; + + { Specifies whether alpha channels of JNG images are lossy compressed. + Allowed values are 0 (False) and 1 (True). Default value is 0.} + ImagingJNGLossyAlpha = 40; + { Sets precompression filter used when saving lossless alpha channels. + For details look at ImagingPNGPreFilter.} + ImagingJNGAlphaPreFilter = 41; + { Sets ZLib compression level used when saving lossless alpha channels. + For details look at ImagingPNGCompressLevel.} + ImagingJNGAlphaCompressLevel = 42; + { Defines compression quality used when saving JNG images (and lossy alpha channels). + For details look at ImagingJpegQuality.} + ImagingJNGQuality = 43; + { Specifies whether JNG images are saved in progressive format. + For details look at ImagingJpegProgressive.} + ImagingJNGProgressive = 44; + + { Specifies whether PGM files are stored in text or in binary format. + Allowed values are 0 (store as text - very! large files) and 1 (save binary). + Default value is 1.} + ImagingPGMSaveBinary = 50; + + { Specifies whether PPM files are stored in text or in binary format. + Allowed values are 0 (store as text - very! large files) and 1 (save binary). + Default value is 1.} + ImagingPPMSaveBinary = 51; + + { Boolean option that specifies whether GIF images with more frames + are animated by Imaging (according to frame disposal methods) or just + raw frames are loaded and sent to user (if you want to animate GIF yourself). + Default value is 1. + Raw frames are 256 color indexed images (ifIndex8), whereas + animated frames are always in 32bit ifA8R8G8B8 format (simplifies animating).} + ImagingGIFLoadAnimated = 56; + + { This option is used when reducing number of colors used in + image (mainly when converting from ARGB image to indexed + format). Mask is 'anded' (bitwise AND) with every pixel's + channel value when creating color histogram. If $FF is used + all 8bits of color channels are used which can result in very + slow proccessing of large images with many colors so you can + use lower masks to speed it up (FC, F8 and F0 are good + choices). Allowed values are in range <0, $FF> and default is + $FE. } + ImagingColorReductionMask = 128; + { This option can be used to override image data format during image + loading. If set to format different from ifUnknown all loaded images + are automaticaly converted to this format. Useful when you have + many files in various formats but you want them all in one format for + further proccessing. Allowed values are in + range <Ord(Low(TImageFormat)), Ord(High(TImageFormat))> and + default value is ifUnknown.} + ImagingLoadOverrideFormat = 129; + { This option can be used to override image data format during image + saving. If set to format different from ifUnknown all images + to be saved are automaticaly internaly converted to this format. + Note that image file formats support only a subset of Imaging data formats + so final saved file may in different format than this override. + Allowed values are in range <Ord(Low(TImageFormat)), Ord(High(TImageFormat))> + and default value is ifUnknown.} + ImagingSaveOverrideFormat = 130; + { Specifies resampling filter used when generating mipmaps. It is used + in GenerateMipMaps low level function and Direct3D and OpenGL extensions. + Allowed values are in range + <Ord(Low(ImagingFormats.TSamplingFilter)), Ord(High(ImagingFormats.TSamplingFilter))> + and default value is 1 (linear filter).} + ImagingMipMapFilter = 131; + { Specifies treshold value used when automatically converting images to + ifBinary format. For adaptive tresholding see ImagingBinary.pas unit. + Default value is 128 and allowed range is 0..255.} + ImagingBinaryTreshold = 132; + + { Returned by GetOption if given Option Id is invalid.} + InvalidOption = -$7FFFFFFF; + + { Indices that can be used to access channel values in array parts + of structures like TColor32Rec. Note that this order can be + used only for ARGB images. For ABGR image you must swap Red and Blue.} + ChannelBlue = 0; + ChannelGreen = 1; + ChannelRed = 2; + ChannelAlpha = 3; + +type + { Enum defining image data format. In formats with more channels, + first channel after "if" is stored in the most significant bits and channel + before end is stored in the least significant.} + TImageFormat = ( + ifUnknown = 0, + ifDefault = 1, + { Indexed formats using palette } + ifIndex8 = 10, + { Grayscale/Luminance formats } + ifGray8 = 40, + ifA8Gray8 = 41, + ifGray16 = 42, + ifGray32 = 43, + ifGray64 = 44, + ifA16Gray16 = 45, + { ARGB formats } + ifX5R1G1B1 = 80, + ifR3G3B2 = 81, + ifR5G6B5 = 82, + ifA1R5G5B5 = 83, + ifA4R4G4B4 = 84, + ifX1R5G5B5 = 85, + ifX4R4G4B4 = 86, + ifR8G8B8 = 87, + ifA8R8G8B8 = 88, + ifX8R8G8B8 = 89, + ifR16G16B16 = 90, + ifA16R16G16B16 = 91, + ifB16G16R16 = 92, + ifA16B16G16R16 = 93, + { Floating point formats } + ifR32F = 160, + ifA32R32G32B32F = 161, + ifA32B32G32R32F = 162, + ifR16F = 163, + ifA16R16G16B16F = 164, + ifA16B16G16R16F = 165, + ifR32G32B32F = 166, + ifB32G32R32F = 167, + { Special formats } + ifDXT1 = 200, + ifDXT3 = 201, + ifDXT5 = 202, + ifBTC = 203, + ifATI1N = 204, + ifATI2N = 205, + ifBinary = 206, + { Passtrough formats } + {ifETC1 = 220, + ifETC2RGB = 221, + ifETC2RGBA = 222, + ifETC2PA = 223, + ifDXBC6 = 224, + ifDXBC7 = 225} + ifLast = 255 + ); + + { Color value for 32 bit images.} + TColor32 = LongWord; + PColor32 = ^TColor32; + + { Color value for 64 bit images.} + TColor64 = type Int64; + PColor64 = ^TColor64; + + { Color record for 24 bit images, which allows access to individual color + channels.} + TColor24Rec = packed record + case LongInt of + 0: (B, G, R: Byte); + 1: (Channels: array[0..2] of Byte); + end; + PColor24Rec = ^TColor24Rec; + TColor24RecArray = array[0..MaxInt div SizeOf(TColor24Rec) - 1] of TColor24Rec; + PColor24RecArray = ^TColor24RecArray; + + { Color record for 32 bit images, which allows access to individual color + channels.} + TColor32Rec = packed record + case LongInt of + 0: (Color: TColor32); + 1: (B, G, R, A: Byte); + 2: (Channels: array[0..3] of Byte); + 3: (Color24Rec: TColor24Rec); + end; + PColor32Rec = ^TColor32Rec; + TColor32RecArray = array[0..MaxInt div SizeOf(TColor32Rec) - 1] of TColor32Rec; + PColor32RecArray = ^TColor32RecArray; + + { Color record for 48 bit images, which allows access to individual color + channels.} + TColor48Rec = packed record + case LongInt of + 0: (B, G, R: Word); + 1: (Channels: array[0..2] of Word); + end; + PColor48Rec = ^TColor48Rec; + TColor48RecArray = array[0..MaxInt div SizeOf(TColor48Rec) - 1] of TColor48Rec; + PColor48RecArray = ^TColor48RecArray; + + { Color record for 64 bit images, which allows access to individual color + channels.} + TColor64Rec = packed record + case LongInt of + 0: (Color: TColor64); + 1: (B, G, R, A: Word); + 2: (Channels: array[0..3] of Word); + 3: (Color48Rec: TColor48Rec); + end; + PColor64Rec = ^TColor64Rec; + TColor64RecArray = array[0..MaxInt div SizeOf(TColor64Rec) - 1] of TColor64Rec; + PColor64RecArray = ^TColor64RecArray; + + { Color record for 96 bit floating point images, which allows access to + individual color channels.} + TColor96FPRec = packed record + case Integer of + 0: (B, G, R: Single); + 1: (Channels: array[0..2] of Single); + end; + PColor96FPRec = ^TColor96FPRec; + TColor96FPRecArray = array[0..MaxInt div SizeOf(TColor96FPRec) - 1] of TColor96FPRec; + PColor96FPRecArray = ^TColor96FPRecArray; + + { Color record for 128 bit floating point images, which allows access to + individual color channels.} + TColorFPRec = packed record + case LongInt of + 0: (B, G, R, A: Single); + 1: (Channels: array[0..3] of Single); + 2: (Color96Rec: TColor96FPRec); + end; + PColorFPRec = ^TColorFPRec; + TColorFPRecArray = array[0..MaxInt div SizeOf(TColorFPRec) - 1] of TColorFPRec; + PColorFPRecArray = ^TColorFPRecArray; + + { 16 bit floating-point value. It has 1 sign bit, 5 exponent bits, + and 10 mantissa bits.} + THalfFloat = type Word; + PHalfFloat = ^THalfFloat; + + { Color record for 64 bit floating point images, which allows access to + individual color channels.} + TColorHFRec = packed record + case LongInt of + 0: (B, G, R, A: THalfFloat); + 1: (Channels: array[0..3] of THalfFloat); + end; + PColorHFRec = ^TColorHFRec; + TColorHFRecArray = array[0..MaxInt div SizeOf(TColorHFRec) - 1] of TColorHFRec; + PColorHFRecArray = ^TColorHFRecArray; + + { Palette for indexed mode images with 32 bit colors.} + TPalette32 = TColor32RecArray; + TPalette32Size256 = array[0..255] of TColor32Rec; + PPalette32 = ^TPalette32; + + { Palette for indexd mode images with 24 bit colors.} + TPalette24 = TColor24RecArray; + TPalette24Size256 = array[0..255] of TColor24Rec; + PPalette24 = ^TPalette24; + + { Record that stores single image data and information describing it.} + TImageData = packed record + Width: LongInt; // Width of image in pixels + Height: LongInt; // Height of image in pixels + Format: TImageFormat; // Data format of image + Size: LongInt; // Size of image bits in Bytes + Bits: Pointer; // Pointer to memory containing image bits + Palette: PPalette32; // Image palette for indexed images + Tag: Pointer; // User data + end; + PImageData = ^TImageData; + + { Pixel format information used in conversions to/from 16 and 8 bit ARGB + image formats.} + TPixelFormatInfo = packed record + ABitCount, RBitCount, GBitCount, BBitCount: Byte; + ABitMask, RBitMask, GBitMask, BBitMask: LongWord; + AShift, RShift, GShift, BShift: Byte; + ARecDiv, RRecDiv, GRecDiv, BRecDiv: Byte; + end; + PPixelFormatInfo = ^TPixelFormatInfo; + + PImageFormatInfo = ^TImageFormatInfo; + + { Look at TImageFormatInfo.GetPixelsSize for details.} + TFormatGetPixelsSizeFunc = function(Format: TImageFormat; Width, + Height: LongInt): LongInt; + { Look at TImageFormatInfo.CheckDimensions for details.} + TFormatCheckDimensionsProc = procedure(Format: TImageFormat; var Width, + Height: LongInt); + { Function for getting pixel colors. Native pixel is read from Image and + then translated to 32 bit ARGB.} + TGetPixel32Func = function(Bits: Pointer; Info: PImageFormatInfo; + Palette: PPalette32): TColor32Rec; + { Function for getting pixel colors. Native pixel is read from Image and + then translated to FP ARGB.} + TGetPixelFPFunc = function(Bits: Pointer; Info: PImageFormatInfo; + Palette: PPalette32): TColorFPRec; + { Procedure for setting pixel colors. Input 32 bit ARGB color is translated to + native format and then written to Image.} + TSetPixel32Proc = procedure(Bits: Pointer; Info: PImageFormatInfo; + Palette: PPalette32;const Color: TColor32Rec); + { Procedure for setting pixel colors. Input FP ARGB color is translated to + native format and then written to Image.} + TSetPixelFPProc = procedure(Bits: Pointer; Info: PImageFormatInfo; + Palette: PPalette32; const Color: TColorFPRec); + + { Additional information for each TImageFormat value.} + TImageFormatInfo = packed record + Format: TImageFormat; // Format described by this record + Name: array[0..15] of Char; // Symbolic name of format + BytesPerPixel: LongInt; // Number of bytes per pixel (note: it is + // 0 for formats where BitsPerPixel < 8 (e.g. DXT). + // Use GetPixelsSize function to get size of + // image data. + ChannelCount: LongInt; // Number of image channels (R, G, B, A, Gray) + PaletteEntries: LongInt; // Number of palette entries + HasGrayChannel: Boolean; // True if image has grayscale channel + HasAlphaChannel: Boolean; // True if image has alpha channel + IsFloatingPoint: Boolean; // True if image has floating point pixels + UsePixelFormat: Boolean; // True if image uses pixel format + IsRBSwapped: Boolean; // True if Red and Blue channels are swapped + // e.g. A16B16G16R16 has IsRBSwapped True + RBSwapFormat: TImageFormat; // Indicates supported format with swapped + // Red and Blue channels, ifUnknown if such + // format does not exist + IsIndexed: Boolean; // True if image uses palette + IsSpecial: Boolean; // True if image is in special format + IsPasstrough: Boolean; // True if image is in passtrough program (Imaging + // iself doesn't know how to decode and encode it - + // complex texture compressions etc.) + PixelFormat: PPixelFormatInfo; // Pixel format structure + GetPixelsSize: TFormatGetPixelsSizeFunc; // Returns size in bytes of + // Width * Height pixels of image + CheckDimensions: TFormatCheckDimensionsProc; // some formats have limited + // values of Width and Height. This + // procedure checks and changes dimensions + // to be valid for given format. + GetPixel32: TGetPixel32Func; // 32bit ARGB pixel get function + GetPixelFP: TGetPixelFPFunc; // FP ARGB pixel get function + SetPixel32: TSetPixel32Proc; // 32bit ARGB pixel set procedure + SetPixelFP: TSetPixelFPProc; // FP ARGB pixel set procedure + SpecialNearestFormat: TImageFormat; // Regular image format used when + // compressing/decompressing special images + // as source/target + end; + + { Handle to list of image data records.} + TImageDataList = Pointer; + PImageDataList = ^TImageDataList; + + { Handle to input/output.} + TImagingHandle = Pointer; + + { Filters used in functions that resize images or their portions.} + TResizeFilter = ( + rfNearest = 0, + rfBilinear = 1, + rfBicubic = 2, + rfLanczos = 3); + + { Seek origin mode for IO function Seek.} + TSeekMode = ( + smFromBeginning = 0, + smFromCurrent = 1, + smFromEnd = 2); + + TOpenMode = ( + omReadOnly = 0, // Opens file for reading only + omCreate = 1, // Creates new file (overwriting any existing) and opens it for writing + omReadWrite = 2 // Opens for reading and writing. Non existing file is created. + ); + + { IO functions used for reading and writing images from/to input/output.} + TOpenProc = function(Source: PChar; Mode: TOpenMode): TImagingHandle; cdecl; + TCloseProc = procedure(Handle: TImagingHandle); cdecl; + TEofProc = function(Handle: TImagingHandle): Boolean; cdecl; + TSeekProc = function(Handle: TImagingHandle; Offset: Int64; Mode: TSeekMode): Int64; cdecl; + TTellProc = function(Handle: TImagingHandle): Int64; cdecl; + TReadProc = function(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt): LongInt; cdecl; + TWriteProc = function(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt): LongInt; cdecl; + +{$IFNDEF FPC} +type +{$IF CompilerVersion <= 18.5} + PtrUInt = LongWord; +{$ELSE} + PtrUInt = NativeUInt; +{$IFEND} +{$ENDIF} + +implementation + +{ + File Notes: + + -- TODOS ---------------------------------------------------- + - add lookup tables to pixel formats for fast conversions + + -- 0.80 ----------------------------------------------------- + - Dropped "patch version". + + -- 0.77.3 --------------------------------------------------- + - IO functions now have 64bit sizes and offsets. + + -- 0.77.1 --------------------------------------------------- + - Added Tag to TImageData for storing user data. + - Added ImagingPNGZLibStrategy option. + - Changed IO functions. Merged open functions to one + and added third open mode R/W (for TIFF append etc.). + - Added new image data formats and related structures: + ifR32G32B32F, ifB32G32G32F. + + -- 0.26.5 Changes/Bug Fixes --------------------------------- + - Added ifBinary image format and ImagingBinaryTreshold option. + - Lanczos filter added to TResizeFilter enum. + + -- 0.24.3 Changes/Bug Fixes --------------------------------- + - Added ifATI1N and ifATI2N image data formats. + + -- 0.23 Changes/Bug Fixes ----------------------------------- + - Added ifBTC image format and SpecialNearestFormat field + to TImageFormatInfo. + + -- 0.21 Changes/Bug Fixes ----------------------------------- + - Added option constants for PGM and PPM file formats. + - Added TPalette32Size256 and TPalette24Size256 types. + + -- 0.19 Changes/Bug Fixes ----------------------------------- + - added ImagingVersionPatch constant so bug fix only releases + can be distinguished from ordinary major/minor releases + - renamed TPixelFormat to TPixelFormatInfo to avoid name collisions + with Graphics.TPixelFormat + - added new image data formats: ifR16F, ifA16R16G16B16F, + ifA16B16G16R16F + - added pixel get/set function pointers to TImageFormatInfo + - added 16bit half float type and color record + - renamed TColorFRec to TColorFPRec (and related types too) + + -- 0.17 Changes/Bug Fixes ----------------------------------- + - added option ImagingMipMapFilter which now controls resampling filter + used when generating mipmaps + - added TResizeFilter type + - added ChannelCount to TImageFormatInfo + - added new option constants for MNG and JNG images + + -- 0.15 Changes/Bug Fixes ----------------------------------- + - added RBSwapFormat to TImageFormatInfo for faster conversions + between swapped formats (it just calls SwapChannels now if + RBSwapFormat is not ifUnknown) + - moved TImageFormatInfo and required types from Imaging unit + here, removed TImageFormatShortInfo + - added new options: ImagingLoadOverrideFormat, ImagingSaveOverrideFormat + + -- 0.13 Changes/Bug Fixes ----------------------------------- + - new ImagingColorReductionMask option added + - new image format added: ifA16Gray16 + +} + +end. diff --git a/resources/libraries/deskew/Imaging/ImagingUtility.pas b/resources/libraries/deskew/Imaging/ImagingUtility.pas new file mode 100755 index 0000000..162d399 --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingUtility.pas @@ -0,0 +1,1735 @@ +{ + Vampyre Imaging Library + by Marek Mauder + http://imaginglib.sourceforge.net + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +{ This unit contains utility functions and types for Imaging library.} +unit ImagingUtility; + +{$I ImagingOptions.inc} + +interface + +uses + SysUtils, Classes, Types; + +const + STrue = 'True'; + SFalse = 'False'; + +type + TByteArray = array[0..MaxInt - 1] of Byte; + PByteArray = ^TByteArray; + TWordArray = array[0..MaxInt div 2 - 1] of Word; + PWordArray = ^TWordArray; + TLongIntArray = array[0..MaxInt div 4 - 1] of LongInt; + PLongIntArray = ^TLongIntArray; + TLongWordArray = array[0..MaxInt div 4 - 1] of LongWord; + PLongWordArray = ^TLongWordArray; + TInt64Array = array[0..MaxInt div 8 - 1] of Int64; + PInt64Array = ^TInt64Array; + TSingleArray = array[0..MaxInt div 4 - 1] of Single; + PSingleArray = ^TSingleArray; + TBooleanArray = array[0..MaxInt - 1] of Boolean; + PBooleanArray = ^TBooleanArray; + + TDynByteArray = array of Byte; + TDynIntegerArray = array of Integer; + TDynBooleanArray = array of Boolean; + TDynStringArray = array of string; + + TWordRec = packed record + case Integer of + 0: (WordValue: Word); + 1: (Low, High: Byte); + end; + PWordRec = ^TWordRec; + TWordRecArray = array[0..MaxInt div 2 - 1] of TWordRec; + PWordRecArray = ^TWordRecArray; + + TLongWordRec = packed record + case Integer of + 0: (LongWordValue: LongWord); + 1: (Low, High: Word); + { Array variants - Index 0 means lowest significant byte (word, ...).} + 2: (Words: array[0..1] of Word); + 3: (Bytes: array[0..3] of Byte); + end; + PLongWordRec = ^TLongWordRec; + TLongWordRecArray = array[0..MaxInt div 4 - 1] of TLongWordRec; + PLongWordRecArray = ^TLongWordRecArray; + + TInt64Rec = packed record + case Integer of + 0: (Int64Value: Int64); + 1: (Low, High: LongWord); + { Array variants - Index 0 means lowest significant byte (word, ...).} + 2: (Words: array[0..3] of Word); + 3: (Bytes: array[0..7] of Byte); + end; + PInt64Rec = ^TInt64Rec; + TInt64RecArray = array[0..MaxInt div 8 - 1] of TInt64Rec; + PInt64RecArray = ^TInt64RecArray; + + TFloatHelper = record + Data: Int64; + case Integer of + 0: (Data64: Int64); + 1: (Data32: LongWord); + end; + PFloatHelper = ^TFloatHelper; + + TFloatPoint = record + X, Y: Single; + end; + + TFloatRect = record + Left, Top, Right, Bottom: Single; + end; + + TChar2 = array[0..1] of AnsiChar; + TChar3 = array[0..2] of AnsiChar; + TChar4 = array[0..3] of AnsiChar; + TChar8 = array[0..7] of AnsiChar; + TChar16 = array[0..15] of AnsiChar; + TAnsiCharSet = set of AnsiChar; + + ENotImplemented = class(Exception) + public + constructor Create; + end; + + { Options for BuildFileList function: + flFullNames - file names in result will have full path names + (ExtractFileDir(Path) + FileName) + flRelNames - file names in result will have names relative to + ExtractFileDir(Path) dir + flRecursive - adds files in subdirectories found in Path.} + TFileListOption = (flFullNames, flRelNames, flRecursive); + TFileListOptions = set of TFileListOption; + + +{ Frees class instance and sets its reference to nil.} +procedure FreeAndNil(var Obj); +{ Frees pointer and sets it to nil.} +procedure FreeMemNil(var P); {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Replacement of standard System.FreeMem procedure which checks if P is nil + (this is only needed for Free Pascal, Delphi makes checks in its FreeMem).} +procedure FreeMem(P: Pointer); {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Returns current exception object. Do not call outside exception handler.} +function GetExceptObject: Exception; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Returns time value with microsecond resolution.} +function GetTimeMicroseconds: Int64; +{ Returns time value with milisecond resolution.} +function GetTimeMilliseconds: Int64; + +{ Returns file extension (without "." dot)} +function GetFileExt(const FileName: string): string; +{ Returns file name of application's executable.} +function GetAppExe: string; +{ Returns directory where application's exceutable is located without + path delimiter at the end.} +function GetAppDir: string; +{ Works like SysUtils.ExtractFileName but supports '/' and '\' dir delimiters + at the same time (whereas ExtractFileName supports on default delimiter on current platform).} +function GetFileName(const FileName: string): string; +{ Works like SysUtils.ExtractFileDir but supports '/' and '\' dir delimiters + at the same time (whereas ExtractFileDir supports on default delimiter on current platform).} +function GetFileDir(const FileName: string): string; +{ Returns True if Subject matches given Mask with optional case sensitivity. + Mask can contain ? and * special characters: ? matches + one character, * matches zero or more characters.} +function StrMaskMatch(const Subject, Mask: string; CaseSensitive: Boolean = False): Boolean; +{ This function fills Files string list with names of files found + with FindFirst/FindNext functions (See details on Path/Atrr here). + - BuildFileList('c:\*.*', faAnyFile, List, [flRecursive]) returns + list of all files (only name.ext - no path) on C drive + - BuildFileList('d:\*.*', faDirectory, List, [flFullNames]) returns + list of all directories (d:\dirxxx) in root of D drive.} +function BuildFileList(Path: string; Attr: LongInt; Files: TStrings; + Options: TFileListOptions = []): Boolean; +{ Similar to RTL's Pos function but with optional Offset where search will start. + This function is in the RTL StrUtils unit but } +function PosEx(const SubStr, S: string; Offset: LongInt = 1): LongInt; +{ Same as PosEx but without case sensitivity.} +function PosNoCase(const SubStr, S: string; Offset: LongInt = 1): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Returns a sub-string from S which is followed by + Sep separator and deletes the sub-string from S including the separator.} +function StrToken(var S: string; Sep: Char): string; +{ Same as StrToken but searches from the end of S string.} +function StrTokenEnd(var S: string; Sep: Char): string; +{ Fills instance of TStrings with tokens from string S where tokens are separated by + one of Seps characters.} +procedure StrTokensToList(const S: string; Sep: Char; Tokens: TStrings); +{ Returns string representation of integer number (with digit grouping). + Uses current locale.} +function IntToStrFmt(const I: Int64): string; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Returns string representation of float number (with digit grouping). + Uses current locale.} +function FloatToStrFmt(const F: Double; Precision: Integer = 2): string; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Returns format settings for parsing floats (dot as decimal separator). + Useful when fomatting/parsing floats etc.} +function GetFormatSettingsForFloats: TFormatSettings; +{ Returns True if S contains at least one of the substrings in SubStrs array. Case sensitive.} +function ContainsAnySubStr(const S: string; const SubStrs: array of string): Boolean; +{ Extracts substring starting at IdxStart ending at IdxEnd. + S[IdxEnd] is not included in the result.} +function SubString(const S: string; IdxStart, IdxEnd: Integer): string; {$IFDEF USE_INLINE}inline;{$ENDIF} + +{ Clamps integer value to range <Min, Max>} +function ClampInt(Number: LongInt; Min, Max: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Clamps float value to range <Min, Max>} +function ClampFloat(Number: Single; Min, Max: Single): Single; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Clamps integer value to Byte boundaries.} +function ClampToByte(Value: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Clamps integer value to Word boundaries.} +function ClampToWord(Value: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Returns True if Num is power of 2.} +function IsPow2(Num: LongInt): Boolean; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Returns next power of 2 greater than or equal to Num + (if Num itself is power of 2 then it retuns Num).} +function NextPow2(Num: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Raises 2 to the given integer power (in range [0, 30]).} +function Pow2Int(Exponent: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Raises Base to any power.} +function Power(const Base, Exponent: Single): Single; +{ Returns log base 2 of integer X (max 2^30) or -1 if X is not power of 2.} +function Log2Int(X: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Returns log base 2 of X.} +function Log2(X: Single): Single; +{ Returns log base 10 of X.} +function Log10(X: Single): Single; +{ Returns largest integer <= Val (for 5.9 returns 5).} +function Floor(Value: Single): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Returns smallest integer >= Val (for 5.1 returns 6).} +function Ceil(Value: Single): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Returns lesser of two integer numbers.} +function Min(A, B: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Returns lesser of two float numbers.} +function MinFloat(A, B: Single): Single; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Returns greater of two integer numbers.} +function Max(A, B: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Returns greater of two float numbers.} +function MaxFloat(A, B: Single): Single; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Returns greater of two float numbers.} +function MaxFloat(const A, B: Double): Double; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Returns result from multiplying Number by Numerator and then dividing by Denominator. + Denominator must be greater than 0.} +function MulDiv(Number, Numerator, Denominator: Word): Word; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Returns true if give floats are the equal within given delta.} +function SameFloat(A, B: Single; Delta: Single = 0.001): Boolean; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Returns true if give floats are the equal within given delta.} +function SameFloat(const A, B: Double; const Delta: Double = 0.000001): Boolean; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} + +{ Switches Boolean value.} +procedure Switch(var Value: Boolean); {$IFDEF USE_INLINE}inline;{$ENDIF} +{ If Condition is True then TruePart is retured, otherwise + FalsePart is returned.} +function Iff(Condition: Boolean; TruePart, FalsePart: LongInt): LongInt; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ If Condition is True then TruePart is retured, otherwise + FalsePart is returned.} +function IffUnsigned(Condition: Boolean; TruePart, FalsePart: LongWord): LongWord; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ If Condition is True then TruePart is retured, otherwise + FalsePart is returned.} +function Iff(Condition, TruePart, FalsePart: Boolean): Boolean; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ If Condition is True then TruePart is retured, otherwise + FalsePart is returned.} +function Iff(Condition: Boolean; const TruePart, FalsePart: string): string; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ If Condition is True then TruePart is retured, otherwise + FalsePart is returned.} +function Iff(Condition: Boolean; TruePart, FalsePart: Char): Char; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ If Condition is True then TruePart is retured, otherwise + FalsePart is returned.} +function Iff(Condition: Boolean; TruePart, FalsePart: Pointer): Pointer; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ If Condition is True then TruePart is retured, otherwise + FalsePart is returned.} +function Iff(Condition: Boolean; const TruePart, FalsePart: Int64): Int64; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ If Condition is True then TruePart is retured, otherwise + FalsePart is returned.} +function IffFloat(Condition: Boolean; TruePart, FalsePart: Single): Single; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Swaps two Boolean values} +procedure SwapValues(var A, B: Boolean); overload; +{ Swaps two Byte values} +procedure SwapValues(var A, B: Byte); overload; +{ Swaps two Word values} +procedure SwapValues(var A, B: Word); overload; +{ Swaps two LongInt values} +procedure SwapValues(var A, B: LongInt); overload; +{ Swaps two Single values} +procedure SwapValues(var A, B: Single); overload; +{ Swaps two LongInt values if necessary to ensure that Min <= Max.} +procedure SwapMin(var Min, Max: LongInt); {$IFDEF USE_INLINE}inline;{$ENDIF} +{ This function returns True if running on little endian machine.} +function IsLittleEndian: Boolean; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Swaps byte order of Word value.} +function SwapEndianWord(Value: Word): Word; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Swaps byte order of multiple Word values.} +procedure SwapEndianWord(P: PWordArray; Count: LongInt); overload; +{ Swaps byte order of LongWord value.} +function SwapEndianLongWord(Value: LongWord): LongWord; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Swaps byte order of multiple LongWord values.} +procedure SwapEndianLongWord(P: PLongWord; Count: LongInt); overload; + +{ Calculates CRC32 for the given data.} +procedure CalcCrc32(var Crc: LongWord; Data: Pointer; Size: LongInt); +{ Fills given memory with given Byte value. Size is size of buffer in bytes.} +procedure FillMemoryByte(Data: Pointer; Size: LongInt; Value: Byte); +{ Fills given memory with given Word value. Size is size of buffer in bytes.} +procedure FillMemoryWord(Data: Pointer; Size: LongInt; Value: Word); +{ Fills given memory with given LongWord value. Size is size of buffer in bytes.} +procedure FillMemoryLongWord(Data: Pointer; Size: LongInt; Value: LongWord); +{ Fills given memory zeroes.} +{$EXTERNALSYM ZeroMemory} // Conflicts with WinAPI ZeroMemory in C++ Builder +procedure ZeroMemory(Data: Pointer; Size: Integer); {$IFDEF USE_INLINE}inline;{$ENDIF} + +{ Returns how many mipmap levels can be created for image of given size.} +function GetNumMipMapLevels(Width, Height: LongInt): LongInt; +{ Returns total number of levels of volume texture with given depth and + mipmap count (this is not depth * mipmaps!).} +function GetVolumeLevelCount(Depth, MipMaps: LongInt): LongInt; +{ Returns rectangle (X, Y, X + Width, Y + Height).} +function BoundsToRect(X, Y, Width, Height: LongInt): TRect; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Returns rectangle (R.Left, R.Top, R.Left + R.Right, R.Top + R.Bottom).} +function BoundsToRect(const R: TRect): TRect; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Returns rectangle (R.Left, R.Top, R.Right - R.Left, R.Bottom - R.Top).} +function RectToBounds(const R: TRect): TRect; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} +{ Clips given bounds to Clip rectangle.} +procedure ClipRectBounds(var X, Y, Width, Height: LongInt; const Clip: TRect); +{ Clips given source bounds and dest position. It is used by various CopyRect + functions that copy rect from one image to another. It handles clipping the same way + as Win32 BitBlt function. } +procedure ClipCopyBounds(var SrcX, SrcY, Width, Height, DstX, DstY: LongInt; + SrcImageWidth, SrcImageHeight: LongInt; const DstClip: TRect); +{ Clips given source bounds and dest bounds. It is used by various StretchRect + functions that stretch rectangle of pixels from one image to another. + It handles clipping the same way as Win32 StretchBlt function. } +procedure ClipStretchBounds(var SrcX, SrcY, SrcWidth, SrcHeight, DstX, DstY, + DstWidth, DstHeight: LongInt; SrcImageWidth, SrcImageHeight: LongInt; const DstClip: TRect); +{ Scales one rectangle to fit into another. Proportions are preserved so + it could be used for 'Stretch To Fit Window' image drawing for instance.} +function ScaleRectToRect(const SourceRect, TargetRect: TRect): TRect; +{ Scales given size to fit into max size while keeping the original ascpect ration. + Useful for calculating thumbnail dimensions etc.} +function ScaleSizeToFit(const CurrentSize, MaxSize: TSize): TSize; +{ Returns width of given rect. Part of RTL in newer Delphi.} +function RectWidth(const Rect: TRect): Integer; +{ Returns height of given rect. Part of RTL in newer Delphi.} +function RectHeight(const Rect: TRect): Integer; +{ Returns True if R1 fits into R2.} +function RectInRect(const R1, R2: TRect): Boolean; +{ Returns True if R1 and R2 intersects.} +function RectIntersects(const R1, R2: TRect): Boolean; + +{ Converts pixel size in micrometers to corrensponding DPI.} +function PixelSizeToDpi(SizeInMicroMeters: Single): Single; +{ Converts DPI to corrensponding pixel size in micrometers.} +function DpiToPixelSize(Dpi: Single): Single; + +function FloatPoint(AX, AY: Single): TFloatPoint; {$IFDEF USE_INLINE}inline;{$ENDIF} +function FloatRect(ALeft, ATop, ARight, ABottom: Single): TFloatRect; +function FloatRectWidth(const R: TFloatRect): Single; +function FloatRectHeight(const R: TFloatRect): Single; +function FloatRectFromRect(const R: TRect): TFloatRect; + +{ Formats given message for usage in Exception.Create(..). Use only + in except block - returned message contains message of last raised exception.} +function FormatExceptMsg(const Msg: string; const Args: array of const): string; +{ Outputs debug message - shows message dialog in Windows and writes to console + in Linux/Unix.} +procedure DebugMsg(const Msg: string; const Args: array of const); + +implementation + +uses +{$IF Defined(MSWINDOWS)} + Windows; +{$ELSEIF Defined(FPC)} + Dos, BaseUnix, Unix; +{$ELSEIF Defined(DELPHI)} + Posix.SysTime; +{$IFEND} + +var + FloatFormatSettings: TFormatSettings; + +constructor ENotImplemented.Create; +begin + inherited Create('Not implemented'); +end; + +procedure FreeAndNil(var Obj); +var + Temp: TObject; +begin + Temp := TObject(Obj); + Pointer(Obj) := nil; + Temp.Free; +end; + +procedure FreeMemNil(var P); +begin + FreeMem(Pointer(P)); + Pointer(P) := nil; +end; + +procedure FreeMem(P: Pointer); +begin + if P <> nil then + System.FreeMem(P); +end; + +function GetExceptObject: Exception; +begin + Result := Exception(ExceptObject); +end; + +{$IF Defined(MSWINDOWS)} +var + PerfFrequency: Int64; + InvPerfFrequency: Extended; + +function GetTimeMicroseconds: Int64; +var + Time: Int64; +begin + QueryPerformanceCounter(Time); + Result := Round(1000000 * InvPerfFrequency * Time); +end; +{$ELSEIF Defined(DELPHI)} +function GetTimeMicroseconds: Int64; +var + Time: TimeVal; +begin + Posix.SysTime.GetTimeOfDay(Time, nil); + Result := Int64(Time.tv_sec) * 1000000 + Time.tv_usec; +end; +{$ELSEIF Defined(FPC)} +function GetTimeMicroseconds: Int64; +var + TimeVal: TTimeVal; +begin + fpGetTimeOfDay(@TimeVal, nil); + Result := Int64(TimeVal.tv_sec) * 1000000 + TimeVal.tv_usec; +end; +{$IFEND} + +function GetTimeMilliseconds: Int64; +begin + Result := GetTimeMicroseconds div 1000; +end; + +function GetFileExt(const FileName: string): string; +begin + Result := ExtractFileExt(FileName); + if Length(Result) > 1 then + Delete(Result, 1, 1); +end; + +function GetAppExe: string; +{$IF Defined(MSWINDOWS)} +var + FileName: array[0..MAX_PATH] of Char; +begin + SetString(Result, FileName, + Windows.GetModuleFileName(MainInstance, FileName, SizeOf(FileName))); +{$ELSEIF Defined(DELPHI)} // Delphi non Win targets +var + FileName: array[0..1024] of Char; +begin + SetString(Result, FileName, + System.GetModuleFileName(MainInstance, FileName, SizeOf(FileName))); +{$ELSE} +begin + Result := ExpandFileName(ParamStr(0)); +{$IFEND} +end; + +function GetAppDir: string; +begin + Result := ExtractFileDir(GetAppExe); +end; + +function GetFileName(const FileName: string): string; +var + I: Integer; +begin + I := LastDelimiter('\/' + DriveDelim, FileName); + Result := Copy(FileName, I + 1, MaxInt); +end; + +function GetFileDir(const FileName: string): string; +const + Delims = '\/' + DriveDelim; +var + I: Integer; +begin + I := LastDelimiter(Delims, Filename); + if (I > 1) and + ((FileName[I] = Delims[1]) or (FileName[I] = Delims[2])) and + (not IsDelimiter(Delims, FileName, I - 1)) then Dec(I); + Result := Copy(FileName, 1, I); +end; + +function StrMaskMatch(const Subject, Mask: string; CaseSensitive: Boolean): Boolean; +var + MaskLen, KeyLen : LongInt; + + function CharMatch(A, B: Char): Boolean; + begin + if CaseSensitive then + Result := A = B + else + Result := AnsiUpperCase (A) = AnsiUpperCase (B); + end; + + function MatchAt(MaskPos, KeyPos: LongInt): Boolean; + begin + while (MaskPos <= MaskLen) and (KeyPos <= KeyLen) do + begin + case Mask[MaskPos] of + '?' : + begin + Inc(MaskPos); + Inc(KeyPos); + end; + '*' : + begin + while (MaskPos <= MaskLen) and (Mask[MaskPos] = '*') do + Inc(MaskPos); + if MaskPos > MaskLen then + begin + Result := True; + Exit; + end; + repeat + if MatchAt(MaskPos, KeyPos) then + begin + Result := True; + Exit; + end; + Inc(KeyPos); + until KeyPos > KeyLen; + Result := False; + Exit; + end; + else + if not CharMatch(Mask[MaskPos], Subject[KeyPos]) then + begin + Result := False; + Exit; + end + else + begin + Inc(MaskPos); + Inc(KeyPos); + end; + end; + end; + + while (MaskPos <= MaskLen) and (AnsiChar(Mask[MaskPos]) in ['?', '*']) do + Inc(MaskPos); + if (MaskPos <= MaskLen) or (KeyPos <= KeyLen) then + begin + Result := False; + Exit; + end; + + Result := True; + end; + +begin + MaskLen := Length(Mask); + KeyLen := Length(Subject); + if MaskLen = 0 then + begin + Result := True; + Exit; + end; + Result := MatchAt(1, 1); +end; + +function BuildFileList(Path: string; Attr: LongInt; + Files: TStrings; Options: TFileListOptions): Boolean; +var + FileMask: string; + RootDir: string; + Folders: TStringList; + CurrentItem: LongInt; + Counter: LongInt; + LocAttr: LongInt; + + procedure BuildFolderList; + var + FindInfo: TSearchRec; + Rslt: LongInt; + begin + Counter := Folders.Count - 1; + CurrentItem := 0; + while CurrentItem <= Counter do + begin + // Searching for subfolders + Rslt := SysUtils.FindFirst(Folders[CurrentItem] + '*', faDirectory, FindInfo); + try + while Rslt = 0 do + begin + if (FindInfo.Name <> '.') and (FindInfo.Name <> '..') and + (FindInfo.Attr and faDirectory = faDirectory) then + Folders.Add(Folders[CurrentItem] + FindInfo.Name + PathDelim); + Rslt := SysUtils.FindNext(FindInfo); + end; + finally + SysUtils.FindClose(FindInfo); + end; + Counter := Folders.Count - 1; + Inc(CurrentItem); + end; + end; + + procedure FillFileList(CurrentCounter: LongInt); + var + FindInfo: TSearchRec; + Res: LongInt; + CurrentFolder: string; + begin + CurrentFolder := Folders[CurrentCounter]; + Res := SysUtils.FindFirst(CurrentFolder + FileMask, LocAttr, FindInfo); + if flRelNames in Options then + CurrentFolder := ExtractRelativePath(RootDir, CurrentFolder); + try + while Res = 0 do + begin + if (FindInfo.Name <> '.') and (FindInfo.Name <> '..') then + begin + if (flFullNames in Options) or (flRelNames in Options) then + Files.Add(CurrentFolder + FindInfo.Name) + else + Files.Add(FindInfo.Name); + end; + Res := SysUtils.FindNext(FindInfo); + end; + finally + SysUtils.FindClose(FindInfo); + end; + end; + +begin + FileMask := ExtractFileName(Path); + RootDir := ExtractFilePath(Path); + Folders := TStringList.Create; + Folders.Add(RootDir); + Files.Clear; +{$IFDEF DCC} + {$WARN SYMBOL_PLATFORM OFF} +{$ENDIF} + if Attr = faAnyFile then + LocAttr := faSysFile or faHidden or faArchive or faReadOnly + else + LocAttr := Attr; +{$IFDEF DCC} + {$WARN SYMBOL_PLATFORM ON} +{$ENDIF} + // Here's the recursive search for nested folders + if flRecursive in Options then + BuildFolderList; + if Attr <> faDirectory then + for Counter := 0 to Folders.Count - 1 do + FillFileList(Counter) + else + Files.AddStrings(Folders); + Folders.Free; + Result := True; +end; + +function PosEx(const SubStr, S: string; Offset: LongInt = 1): LongInt; +var + I, X: LongInt; + Len, LenSubStr: LongInt; +begin + I := Offset; + LenSubStr := Length(SubStr); + Len := Length(S) - LenSubStr + 1; + while I <= Len do + begin + if S[I] = SubStr[1] then + begin + X := 1; + while (X < LenSubStr) and (S[I + X] = SubStr[X + 1]) do + Inc(X); + if (X = LenSubStr) then + begin + Result := I; + Exit; + end; + end; + Inc(I); + end; + Result := 0; +end; + +function PosNoCase(const SubStr, S: string; Offset: LongInt): LongInt; +begin + Result := PosEx(AnsiLowerCase(SubStr), AnsiLowerCase(S), Offset); +end; + +function StrToken(var S: string; Sep: Char): string; +var + I: LongInt; +begin + I := Pos(Sep, S); + if I <> 0 then + begin + Result := Copy(S, 1, I - 1); + Delete(S, 1, I); + end + else + begin + Result := S; + S := ''; + end; +end; + +function StrTokenEnd(var S: string; Sep: Char): string; +var + I, J: LongInt; +begin + J := 0; + I := Pos(Sep, S); + while I <> 0 do + begin + J := I; + I := PosEx(Sep, S, J + 1); + end; + if J <> 0 then + begin + Result := Copy(S, J + 1, MaxInt); + Delete(S, J, MaxInt); + end + else + begin + Result := S; + S := ''; + end; +end; + +procedure StrTokensToList(const S: string; Sep: Char; Tokens: TStrings); +var + Token, Str: string; +begin + Tokens.Clear; + Str := S; + while Str <> '' do + begin + Token := StrToken(Str, Sep); + Tokens.Add(Token); + end; +end; + +function IntToStrFmt(const I: Int64): string; +begin + Result := Format('%.0n', [I * 1.0]); +end; + +function FloatToStrFmt(const F: Double; Precision: Integer): string; +begin + Result := Format('%.' + IntToStr(Precision) + 'n', [F]); +end; + +function GetFormatSettingsForFloats: TFormatSettings; +begin + Result := FloatFormatSettings; +end; + +function ContainsAnySubStr(const S: string; const SubStrs: array of string): Boolean; +var + I: Integer; +begin + Result := False; + for I := 0 to High(SubStrs) do + begin + Result := Pos(SubStrs[I], S) > 0; + if Result then + Exit; + end; +end; + +function SubString(const S: string; IdxStart, IdxEnd: Integer): string; +begin + Result := Copy(S, IdxStart, IdxEnd - IdxStart); +end; + +function ClampInt(Number: LongInt; Min, Max: LongInt): LongInt; +begin + Result := Number; + if Result < Min then + Result := Min + else if Result > Max then + Result := Max; +end; + +function ClampFloat(Number: Single; Min, Max: Single): Single; +begin + Result := Number; + if Result < Min then + Result := Min + else if Result > Max then + Result := Max; +end; + +function ClampToByte(Value: LongInt): LongInt; +begin + Result := Value; + if Result > 255 then + Result := 255 + else if Result < 0 then + Result := 0; +end; + +function ClampToWord(Value: LongInt): LongInt; +begin + Result := Value; + if Result > 65535 then + Result := 65535 + else if Result < 0 then + Result := 0; +end; + +function IsPow2(Num: LongInt): Boolean; +begin + Result := (Num and -Num) = Num; +end; + +function NextPow2(Num: LongInt): LongInt; +begin + Result := Num and -Num; + while Result < Num do + Result := Result shl 1; +end; + +function Pow2Int(Exponent: LongInt): LongInt; +begin + Result := 1 shl Exponent; +end; + +function Power(const Base, Exponent: Single): Single; +begin + if Exponent = 0.0 then + Result := 1.0 + else if (Base = 0.0) and (Exponent > 0.0) then + Result := 0.0 + else + Result := Exp(Exponent * Ln(Base)); +end; + +function Log2Int(X: LongInt): LongInt; +begin + case X of + 1: Result := 0; + 2: Result := 1; + 4: Result := 2; + 8: Result := 3; + 16: Result := 4; + 32: Result := 5; + 64: Result := 6; + 128: Result := 7; + 256: Result := 8; + 512: Result := 9; + 1024: Result := 10; + 2048: Result := 11; + 4096: Result := 12; + 8192: Result := 13; + 16384: Result := 14; + 32768: Result := 15; + 65536: Result := 16; + 131072: Result := 17; + 262144: Result := 18; + 524288: Result := 19; + 1048576: Result := 20; + 2097152: Result := 21; + 4194304: Result := 22; + 8388608: Result := 23; + 16777216: Result := 24; + 33554432: Result := 25; + 67108864: Result := 26; + 134217728: Result := 27; + 268435456: Result := 28; + 536870912: Result := 29; + 1073741824: Result := 30; + else + Result := -1; + end; +end; + +function Log2(X: Single): Single; +{$IFDEF USE_ASM} +asm + FLD1 + FLD X + FYL2X + FWAIT +end; +{$ELSE} +const + Ln2: Single = 0.6931471; +begin + Result := Ln(X) / Ln2; +end; +{$ENDIF} + +function Log10(X: Single): Single; +{$IFDEF USE_ASM} +asm + FLDLG2 + FLD X + FYL2X + FWAIT +end; +{$ELSE} +const + Ln10: Single = 2.30258509299405; +begin + Result := Ln(X) / Ln10; +end; +{$ENDIF} + +function Floor(Value: Single): LongInt; +begin + Result := Trunc(Value); + if Value < Result then + Dec(Result); +end; + +function Ceil(Value: Single): LongInt; +begin + Result := Trunc(Value); + if Value > Result then + Inc(Result); +end; + +procedure Switch(var Value: Boolean); +begin + Value := not Value; +end; + +function Iff(Condition: Boolean; TruePart, FalsePart: LongInt): LongInt; +begin + if Condition then + Result := TruePart + else + Result := FalsePart; +end; + +function IffUnsigned(Condition: Boolean; TruePart, FalsePart: LongWord): LongWord; +begin + if Condition then + Result := TruePart + else + Result := FalsePart; +end; + +function Iff(Condition, TruePart, FalsePart: Boolean): Boolean; +begin + if Condition then + Result := TruePart + else + Result := FalsePart; +end; + +function Iff(Condition: Boolean; const TruePart, FalsePart: string): string; +begin + if Condition then + Result := TruePart + else + Result := FalsePart; +end; + +function Iff(Condition: Boolean; TruePart, FalsePart: Char): Char; +begin + if Condition then + Result := TruePart + else + Result := FalsePart; +end; + +function Iff(Condition: Boolean; TruePart, FalsePart: Pointer): Pointer; +begin + if Condition then + Result := TruePart + else + Result := FalsePart; +end; + +function Iff(Condition: Boolean; const TruePart, FalsePart: Int64): Int64; +begin + if Condition then + Result := TruePart + else + Result := FalsePart; +end; + +function IffFloat(Condition: Boolean; TruePart, FalsePart: Single): Single; +begin + if Condition then + Result := TruePart + else + Result := FalsePart; +end; + +procedure SwapValues(var A, B: Boolean); +var + Tmp: Boolean; +begin + Tmp := A; + A := B; + B := Tmp; +end; + +procedure SwapValues(var A, B: Byte); +var + Tmp: Byte; +begin + Tmp := A; + A := B; + B := Tmp; +end; + +procedure SwapValues(var A, B: Word); +var + Tmp: Word; +begin + Tmp := A; + A := B; + B := Tmp; +end; + +procedure SwapValues(var A, B: LongInt); +var + Tmp: LongInt; +begin + Tmp := A; + A := B; + B := Tmp; +end; + +procedure SwapValues(var A, B: Single); +var + Tmp: Single; +begin + Tmp := A; + A := B; + B := Tmp; +end; + +procedure SwapMin(var Min, Max: LongInt); +var + Tmp: LongInt; +begin + if Min > Max then + begin + Tmp := Min; + Min := Max; + Max := Tmp; + end; +end; + +function Min(A, B: LongInt): LongInt; +begin + if A < B then + Result := A + else + Result := B; +end; + +function MinFloat(A, B: Single): Single; +begin + if A < B then + Result := A + else + Result := B; +end; + +function Max(A, B: LongInt): LongInt; +begin + if A > B then + Result := A + else + Result := B; +end; + +function MaxFloat(A, B: Single): Single; +begin + if A > B then + Result := A + else + Result := B; +end; + +function MaxFloat(const A, B: Double): Double; +begin + if A > B then + Result := A + else + Result := B; +end; + +function MulDiv(Number, Numerator, Denominator: Word): Word; +{$IF Defined(USE_ASM) and (not Defined(USE_INLINE))} +asm + MUL DX + DIV CX +end; +{$ELSE} +begin + Result := Number * Numerator div Denominator; +end; +{$IFEND} + +function SameFloat(A, B: Single; Delta: Single): Boolean; +begin + Result := Abs(A - B) <= Delta; +end; + +function SameFloat(const A, B: Double; const Delta: Double): Boolean; +begin + Result := Abs(A - B) <= Delta; +end; + +function IsLittleEndian: Boolean; +var + W: Word; +begin + W := $00FF; + Result := PByte(@W)^ = $FF; +end; + +function SwapEndianWord(Value: Word): Word; +{$IF Defined(USE_ASM) and (not Defined(USE_INLINE))} +asm + XCHG AH, AL +end; +{$ELSE} +begin + TWordRec(Result).Low := TWordRec(Value).High; + TWordRec(Result).High := TWordRec(Value).Low; +end; +{$IFEND} + +procedure SwapEndianWord(P: PWordArray; Count: LongInt); +{$IFDEF USE_ASM} +asm +@Loop: + MOV CX, [EAX] + XCHG CH, CL + MOV [EAX], CX + ADD EAX, 2 + DEC EDX + JNZ @Loop +end; +{$ELSE} +var + I: LongInt; + Temp: Word; +begin + for I := 0 to Count - 1 do + begin + Temp := P[I]; + TWordRec(P[I]).Low := TWordRec(Temp).High; + TWordRec(P[I]).High := TWordRec(Temp).Low; + end; +end; +{$ENDIF} + +function SwapEndianLongWord(Value: LongWord): LongWord; +{$IF Defined(USE_ASM) and (not Defined(USE_INLINE))} +asm + BSWAP EAX +end; +{$ELSE} +begin + TLongWordRec(Result).Bytes[0] := TLongWordRec(Value).Bytes[3]; + TLongWordRec(Result).Bytes[1] := TLongWordRec(Value).Bytes[2]; + TLongWordRec(Result).Bytes[2] := TLongWordRec(Value).Bytes[1]; + TLongWordRec(Result).Bytes[3] := TLongWordRec(Value).Bytes[0]; +end; +{$IFEND} + +procedure SwapEndianLongWord(P: PLongWord; Count: LongInt); +{$IFDEF USE_ASM} +asm +@Loop: + MOV ECX, [EAX] + BSWAP ECX + MOV [EAX], ECX + ADD EAX, 4 + DEC EDX + JNZ @Loop +end; +{$ELSE} +var + I: LongInt; + Temp: LongWord; +begin + for I := 0 to Count - 1 do + begin + Temp := PLongWordArray(P)[I]; + TLongWordRec(PLongWordArray(P)[I]).Bytes[0] := TLongWordRec(Temp).Bytes[3]; + TLongWordRec(PLongWordArray(P)[I]).Bytes[1] := TLongWordRec(Temp).Bytes[2]; + TLongWordRec(PLongWordArray(P)[I]).Bytes[2] := TLongWordRec(Temp).Bytes[1]; + TLongWordRec(PLongWordArray(P)[I]).Bytes[3] := TLongWordRec(Temp).Bytes[0]; + end; +end; +{$ENDIF} + +type + TCrcTable = array[Byte] of LongWord; +var + CrcTable: TCrcTable; + +procedure InitCrcTable; +const + Polynom = $EDB88320; +var + I, J: LongInt; + C: LongWord; +begin + for I := 0 to 255 do + begin + C := I; + for J := 0 to 7 do + begin + if (C and $01) <> 0 then + C := Polynom xor (C shr 1) + else + C := C shr 1; + end; + CrcTable[I] := C; + end; +end; + +procedure CalcCrc32(var Crc: LongWord; Data: Pointer; Size: LongInt); +var + I: LongInt; + B: PByte; +begin + B := Data; + for I := 0 to Size - 1 do + begin + Crc := (Crc shr 8) xor CrcTable[B^ xor Byte(Crc)]; + Inc(B); + end +end; + +procedure FillMemoryByte(Data: Pointer; Size: LongInt; Value: Byte); +{$IFDEF USE_ASM} +asm + PUSH EDI + MOV EDI, EAX + MOV EAX, ECX + MOV AH, AL + MOV CX, AX + SHL EAX, 16 + MOV AX, CX + MOV ECX, EDX + SAR ECX, 2 + JS @Exit + REP STOSD + MOV ECX, EDX + AND ECX, 3 + REP STOSB + POP EDI +@Exit: +end; +{$ELSE} +begin + FillChar(Data^, Size, Value); +end; +{$ENDIF} + +procedure FillMemoryWord(Data: Pointer; Size: LongInt; Value: Word); +{$IFDEF USE_ASM} +asm + PUSH EDI + PUSH EBX + MOV EBX, EDX + MOV EDI, EAX + MOV EAX, ECX + MOV CX, AX + SHL EAX, 16 + MOV AX, CX + MOV ECX, EDX + SHR ECX, 2 + JZ @Word + REP STOSD +@Word: + MOV ECX, EBX + AND ECX, 2 + JZ @Byte + MOV [EDI], AX + ADD EDI, 2 +@Byte: + MOV ECX, EBX + AND ECX, 1 + JZ @Exit + MOV [EDI], AL +@Exit: + POP EBX + POP EDI +end; +{$ELSE} +var + I, V: LongWord; +begin + V := Value * $10000 + Value; + for I := 0 to Size div 4 - 1 do + PLongWordArray(Data)[I] := V; + case Size mod 4 of + 1: PByteArray(Data)[Size - 1] := Lo(Value); + 2: PWordArray(Data)[Size div 2] := Value; + 3: + begin + PWordArray(Data)[Size div 2 - 1] := Value; + PByteArray(Data)[Size - 1] := Lo(Value); + end; + end; +end; +{$ENDIF} + +procedure FillMemoryLongWord(Data: Pointer; Size: LongInt; Value: LongWord); +{$IFDEF USE_ASM} +asm + PUSH EDI + PUSH EBX + MOV EBX, EDX + MOV EDI, EAX + MOV EAX, ECX + MOV ECX, EDX + SHR ECX, 2 + JZ @Word + REP STOSD +@Word: + MOV ECX, EBX + AND ECX, 2 + JZ @Byte + MOV [EDI], AX + ADD EDI, 2 +@Byte: + MOV ECX, EBX + AND ECX, 1 + JZ @Exit + MOV [EDI], AL +@Exit: + POP EBX + POP EDI +end; +{$ELSE} +var + I: LongInt; +begin + for I := 0 to Size div 4 - 1 do + PLongWordArray(Data)[I] := Value; + case Size mod 4 of + 1: PByteArray(Data)[Size - 1] := TLongWordRec(Value).Bytes[0]; + 2: PWordArray(Data)[Size div 2] := TLongWordRec(Value).Words[0]; + 3: + begin + PWordArray(Data)[Size div 2 - 1] := TLongWordRec(Value).Words[0]; + PByteArray(Data)[Size - 1] := TLongWordRec(Value).Bytes[0]; + end; + end; +end; +{$ENDIF} + +procedure ZeroMemory(Data: Pointer; Size: Integer); +begin + FillMemoryByte(Data, Size, 0); +end; + +function GetNumMipMapLevels(Width, Height: LongInt): LongInt; +begin + Result := 0; + if (Width > 0) and (Height > 0) then + begin + Result := 1; + while (Width <> 1) or (Height <> 1) do + begin + Width := Width div 2; + Height := Height div 2; + if Width < 1 then Width := 1; + if Height < 1 then Height := 1; + Inc(Result); + end; + end; +end; + +function GetVolumeLevelCount(Depth, MipMaps: LongInt): LongInt; +var + I: LongInt; +begin + Result := Depth; + for I := 1 to MipMaps - 1 do + Inc(Result, ClampInt(Depth shr I, 1, Depth)); +end; + +function BoundsToRect(X, Y, Width, Height: LongInt): TRect; +begin + Result.Left := X; + Result.Top := Y; + Result.Right := X + Width; + Result.Bottom := Y + Height; +end; + +function BoundsToRect(const R: TRect): TRect; +begin + Result.Left := R.Left; + Result.Top := R.Top; + Result.Right := R.Left + R.Right; + Result.Bottom := R.Top + R.Bottom; +end; + +function RectToBounds(const R: TRect): TRect; +begin + Result.Left := R.Left; + Result.Top := R.Top; + Result.Right := R.Right - R.Left; + Result.Bottom := R.Bottom - R.Top; +end; + +procedure ClipRectBounds(var X, Y, Width, Height: LongInt; const Clip: TRect); + + procedure ClipDim(var AStart, ALength: LongInt; ClipMin, ClipMax: LongInt); + begin + if AStart < ClipMin then + begin + ALength := ALength - (ClipMin - AStart); + AStart := ClipMin; + end; + if AStart + ALength > ClipMax then ALength := Max(0, ClipMax - AStart); + end; + +begin + ClipDim(X, Width, Clip.Left, Clip.Right); + ClipDim(Y, Height, Clip.Top, Clip.Bottom); +end; + +procedure ClipCopyBounds(var SrcX, SrcY, Width, Height, DstX, DstY: LongInt; SrcImageWidth, SrcImageHeight: LongInt; const DstClip: TRect); + + procedure ClipDim(var SrcPos, DstPos, Size: LongInt; SrcClipMax, + DstClipMin, DstClipMax: LongInt); + var + OldDstPos: LongInt; + Diff: LongInt; + begin + OldDstPos := Iff(DstPos < 0, DstPos, 0); + if DstPos < DstClipMin then + begin + Diff := DstClipMin - DstPos; + Size := Size - Diff; + SrcPos := SrcPos + Diff; + DstPos := DstClipMin; + end; + if SrcPos < 0 then + begin + Size := Size + SrcPos - OldDstPos; + DstPos := DstPos - SrcPos + OldDstPos; + SrcPos := 0; + end; + if SrcPos + Size > SrcClipMax then Size := SrcClipMax - SrcPos; + if DstPos + Size > DstClipMax then Size := DstClipMax - DstPos; + end; + +begin + ClipDim(SrcX, DstX, Width, SrcImageWidth, DstClip.Left, DstClip.Right); + ClipDim(SrcY, DstY, Height, SrcImageHeight, DstClip.Top, DstClip.Bottom); +end; + +procedure ClipStretchBounds(var SrcX, SrcY, SrcWidth, SrcHeight, DstX, DstY, + DstWidth, DstHeight: LongInt; SrcImageWidth, SrcImageHeight: LongInt; const DstClip: TRect); + + procedure ClipDim(var SrcPos, DstPos, SrcSize, DstSize: LongInt; SrcClipMax, + DstClipMin, DstClipMax: LongInt); + var + OldSize: LongInt; + Diff: LongInt; + Scale: Single; + begin + Scale := DstSize / SrcSize; + if DstPos < DstClipMin then + begin + Diff := DstClipMin - DstPos; + DstSize := DstSize - Diff; + SrcPos := SrcPos + Round(Diff / Scale); + SrcSize := SrcSize - Round(Diff / Scale); + DstPos := DstClipMin; + end; + if SrcPos < 0 then + begin + SrcSize := SrcSize + SrcPos; + DstPos := DstPos - Round(SrcPos * Scale); + DstSize := DstSize + Round(SrcPos * Scale); + SrcPos := 0; + end; + if SrcPos + SrcSize > SrcClipMax then + begin + OldSize := SrcSize; + SrcSize := SrcClipMax - SrcPos; + DstSize := Round(DstSize * (SrcSize / OldSize)); + end; + if DstPos + DstSize > DstClipMax then + begin + OldSize := DstSize; + DstSize := DstClipMax - DstPos; + SrcSize := Round(SrcSize * (DstSize / OldSize)); + end; + end; + +begin + ClipDim(SrcX, DstX, SrcWidth, DstWidth, SrcImageWidth, DstClip.Left, DstClip.Right); + ClipDim(SrcY, DstY, SrcHeight, DstHeight, SrcImageHeight, DstClip.Top, DstClip.Bottom); +end; + +function ScaleRectToRect(const SourceRect, TargetRect: TRect): TRect; +var + SourceWidth: LongInt; + SourceHeight: LongInt; + TargetWidth: LongInt; + TargetHeight: LongInt; + ScaledWidth: LongInt; + ScaledHeight: LongInt; +begin + SourceWidth := SourceRect.Right - SourceRect.Left; + SourceHeight := SourceRect.Bottom - SourceRect.Top; + TargetWidth := TargetRect.Right - TargetRect.Left; + TargetHeight := TargetRect.Bottom - TargetRect.Top; + + if SourceWidth * TargetHeight < SourceHeight * TargetWidth then + begin + ScaledWidth := (SourceWidth * TargetHeight) div SourceHeight; + Result := BoundsToRect(TargetRect.Left + ((TargetWidth - ScaledWidth) div 2), + TargetRect.Top, ScaledWidth, TargetHeight); + end + else + begin + ScaledHeight := (SourceHeight * TargetWidth) div SourceWidth; + Result := BoundsToRect(TargetRect.Left, TargetRect.Top + ((TargetHeight - ScaledHeight) div 2), + TargetWidth, ScaledHeight); + end; +end; + +function ScaleSizeToFit(const CurrentSize, MaxSize: Types.TSize): Types.TSize; +var + SR, TR, ScaledRect: TRect; +begin + SR := Types.Rect(0, 0, CurrentSize.CX, CurrentSize.CY); + TR := Types.Rect(0, 0, MaxSize.CX, MaxSize.CY); + ScaledRect := ScaleRectToRect(SR, TR); + Result.CX := ScaledRect.Right - ScaledRect.Left; + Result.CY := ScaledRect.Bottom - ScaledRect.Top; +end; + +function RectWidth(const Rect: TRect): Integer; +begin + Result := Rect.Right - Rect.Left; +end; + +function RectHeight(const Rect: TRect): Integer; +begin + Result := Rect.Bottom - Rect.Top; +end; + +function RectInRect(const R1, R2: TRect): Boolean; +begin + Result:= + (R1.Left >= R2.Left) and + (R1.Top >= R2.Top) and + (R1.Right <= R2.Right) and + (R1.Bottom <= R2.Bottom); +end; + +function RectIntersects(const R1, R2: TRect): Boolean; +begin + Result := + not (R1.Left > R2.Right) and + not (R1.Top > R2.Bottom) and + not (R1.Right < R2.Left) and + not (R1.Bottom < R2.Top); +end; + +function PixelSizeToDpi(SizeInMicroMeters: Single): Single; +begin + Result := 25400 / SizeInMicroMeters; +end; + +function DpiToPixelSize(Dpi: Single): Single; +begin + Result := 1e03 / (Dpi / 25.4); +end; + +function FloatPoint(AX, AY: Single): TFloatPoint; +begin + Result.X := AX; + Result.Y := AY; +end; + +function FloatRect(ALeft, ATop, ARight, ABottom: Single): TFloatRect; +begin + with Result do + begin + Left := ALeft; + Top := ATop; + Right := ARight; + Bottom := ABottom; + end; +end; + +function FloatRectWidth(const R: TFloatRect): Single; +begin + Result := R.Right - R.Left; +end; + +function FloatRectHeight(const R: TFloatRect): Single; +begin + Result := R.Bottom - R.Top; +end; + +function FloatRectFromRect(const R: TRect): TFloatRect; +begin + Result := FloatRect(R.Left, R.Top, R.Right, R.Bottom); +end; + +function FormatExceptMsg(const Msg: string; const Args: array of const): string; +begin + Result := Format(Msg + SLineBreak + 'Message: ' + GetExceptObject.Message, Args); +end; + +procedure DebugMsg(const Msg: string; const Args: array of const); +var + FmtMsg: string; +begin + FmtMsg := Format(Msg, Args); +{$IFDEF MSWINDOWS} + if IsConsole then + WriteLn('DebugMsg: ' + FmtMsg) + else + MessageBox(GetActiveWindow, PChar(FmtMsg), 'DebugMsg', MB_OK); +{$ENDIF} +{$IFDEF UNIX} + WriteLn('DebugMsg: ' + FmtMsg); +{$ENDIF} +{$IFDEF MSDOS} + WriteLn('DebugMsg: ' + FmtMsg); +{$ENDIF} +end; + +initialization + InitCrcTable; +{$IFDEF MSWINDOWS} + QueryPerformanceFrequency(PerfFrequency); + InvPerfFrequency := 1.0 / PerfFrequency; +{$ENDIF} + +{$IF Defined(DELPHI)} + {$IF CompilerVersion >= 23} + FloatFormatSettings := TFormatSettings.Create('en-US'); + {$ELSE} + GetLocaleFormatSettings(1033, FloatFormatSettings); + {$IFEND} +{$ELSE FPC} + FloatFormatSettings := DefaultFormatSettings; + FloatFormatSettings.DecimalSeparator := '.'; + FloatFormatSettings.ThousandSeparator := ','; +{$IFEND} + +{ + File Notes: + + -- TODOS ---------------------------------------------------- + - nothing now + + -- 0.77.1 ---------------------------------------------------- + - Added GetFileName, GetFileDir, RectWidth, RectHeight function. + - Added ScaleSizeToFit function. + - Added ZeroMemory and SwapValues for Booleans. + - Added Substring function. + - Renamed MatchFileNameMask to StrMaskMatch (it's for general use not + just filenames). + - Delphi XE2 new targets (Win64, OSX32) compatibility changes. + - Added GetFormatSettingsForFloats function. + + -- 0.26.5 Changes/Bug Fixes ----------------------------------- + - Added Log10 function. + - Added TFloatRect type and helper functions FloatRect, FloatRectWidth, + FloatRectHeight. + - Added string function ContainsAnySubStr. + - Added functions PixelSizeToDpi, DpiToPixelSize. + + -- 0.26.1 Changes/Bug Fixes ----------------------------------- + - Some formatting changes. + - Changed some string functions to work with localized strings. + - ASM version of PosEx had bugs, removed it. + - Added StrTokensToList function. + + -- 0.25.0 Changes/Bug Fixes ----------------------------------- + - Fixed error in ClipCopyBounds which was causing ... bad clipping! + + -- 0.24.3 Changes/Bug Fixes ----------------------------------- + - Added GetTimeMilliseconds function. + - Added IntToStrFmt and FloatToStrFmt helper functions. + + -- 0.23 Changes/Bug Fixes ----------------------------------- + - Added RectInRect and RectIntersects functions + - Added some string utils: StrToken, StrTokenEnd, PosEx, PosNoCase. + - Moved BuildFileList here from DemoUtils. + + -- 0.21 Changes/Bug Fixes ----------------------------------- + - Moved GetVolumeLevelCount from ImagingDds here. + - Renamed FillMemory to FillMemoryByte to avoid name collision in C++ Builder. + - Added Iff function for Char, Pointer, and Int64 types. + - Added IsLittleEndian function. + - Added array types for TWordRec, TLongWordRec, and TInt64Rec. + - Added MatchFileNameMask function. + + -- 0.19 Changes/Bug Fixes ----------------------------------- + - added ScaleRectToRect (thanks to Paul Michell) + - added BoundsToRect, ClipBounds, ClipCopyBounds, ClipStretchBounds functions + - added MulDiv function + - FreeAndNil is not inline anymore - caused AV in one program + + -- 0.17 Changes/Bug Fixes ----------------------------------- + + - GetAppExe didn't return absolute path in FreeBSD, fixed + - added debug message output + - fixed Unix compatibility issues (thanks to Ales Katona). + Imaging now compiles in FreeBSD and maybe in other Unixes as well. + + -- 0.15 Changes/Bug Fixes ----------------------------------- + - added some new utility functions + + -- 0.13 Changes/Bug Fixes ----------------------------------- + - added many new utility functions + - minor change in SwapEndian to avoid range check error + +} +end. + + diff --git a/resources/libraries/deskew/Imaging/ImagingWic.pas b/resources/libraries/deskew/Imaging/ImagingWic.pas new file mode 100755 index 0000000..0ebd81d --- /dev/null +++ b/resources/libraries/deskew/Imaging/ImagingWic.pas @@ -0,0 +1,183 @@ +unit ImagingWic; + +interface + +implementation + +uses + Windows, ActiveX; + +const + SID_IWICPalette = '{00000040-a8f2-4877-ba0a-fd2b6645fb94}'; + SID_IWICBitmapSource = '{00000120-a8f2-4877-ba0a-fd2b6645fb94}'; + SID_IWICFormatConverter = '{00000301-a8f2-4877-ba0a-fd2b6645fb94}'; + SID_IWICBitmapScaler = '{00000302-a8f2-4877-ba0a-fd2b6645fb94}'; + SID_IWICBitmapClipper = '{E4FBCF03-223D-4e81-9333-D635556DD1B5}'; + SID_IWICBitmapFlipRotator = '{5009834F-2D6A-41ce-9E1B-17C5AFF7A782}'; + SID_IWICBitmapLock = '{00000123-a8f2-4877-ba0a-fd2b6645fb94}'; + SID_IWICBitmap = '{00000121-a8f2-4877-ba0a-fd2b6645fb94}'; + SID_IWICColorTransform = '{B66F034F-D0E2-40ab-B436-6DE39E321A94}'; + SID_IWICColorContext = '{3C613A02-34B2-44ea-9A7C-45AEA9C6FD6D}'; + SID_IWICFastMetadataEncoder = '{B84E2C09-78C9-4AC4-8BD3-524AE1663A2F}'; + SID_IWICStream = '{135FF860-22B7-4ddf-B0F6-218F4F299A43}'; + SID_IWICEnumMetadataItem = '{DC2BB46D-3F07-481E-8625-220C4AEDBB33}'; + SID_IWICMetadataQueryReader = '{30989668-E1C9-4597-B395-458EEDB808DF}'; + SID_IWICMetadataQueryWriter = '{A721791A-0DEF-4d06-BD91-2118BF1DB10B}'; + SID_IWICBitmapEncoder = '{00000103-a8f2-4877-ba0a-fd2b6645fb94}'; + SID_IWICBitmapFrameEncode = '{00000105-a8f2-4877-ba0a-fd2b6645fb94}'; + SID_IWICBitmapDecoder = '{9EDDE9E7-8DEE-47ea-99DF-E6FAF2ED44BF}'; + SID_IWICBitmapSourceTransform = '{3B16811B-6A43-4ec9-B713-3D5A0C13B940}'; + SID_IWICBitmapFrameDecode = '{3B16811B-6A43-4ec9-A813-3D930C13B940}'; + SID_IWICProgressiveLevelControl = '{DAAC296F-7AA5-4dbf-8D15-225C5976F891}'; + SID_IWICProgressCallback = '{4776F9CD-9517-45FA-BF24-E89C5EC5C60C}'; + SID_IWICBitmapCodecProgressNotification = '{64C1024E-C3CF-4462-8078-88C2B11C46D9}'; + SID_IWICComponentInfo = '{23BC3F0A-698B-4357-886B-F24D50671334}'; + SID_IWICFormatConverterInfo = '{9F34FB65-13F4-4f15-BC57-3726B5E53D9F}'; + SID_IWICBitmapCodecInfo = '{E87A44C4-B76E-4c47-8B09-298EB12A2714}'; + SID_IWICBitmapEncoderInfo = '{94C9B4EE-A09F-4f92-8A1E-4A9BCE7E76FB}'; + SID_IWICBitmapDecoderInfo = '{D8CD007F-D08F-4191-9BFC-236EA7F0E4B5}'; + SID_IWICPixelFormatInfo = '{E8EDA601-3D48-431a-AB44-69059BE88BBE}'; + SID_IWICPixelFormatInfo2 = '{A9DB33A2-AF5F-43C7-B679-74F5984B5AA4}'; + SID_IWICImagingFactory = '{ec5ec8a9-c395-4314-9c77-54d7a935ff70}'; + SID_IWICDevelopRawNotificationCallback = '{95c75a6e-3e8c-4ec2-85a8-aebcc551e59b}'; + SID_IWICDevelopRaw = '{fbec5e44-f7be-4b65-b7f8-c0c81fef026d}'; + + CLSID_WICImagingFactory: TGUID = '{CACAF262-9370-4615-A13B-9F5539DA4C0A}'; + GUID_VendorMicrosoft: TGUID = '{F0E749CA-EDEF-4589-A73A-EE0E626A2A2B}'; + GUID_VendorMicrosoftBuiltIn: TGUID = '{257A30FD-06B6-462B-AEA4-63F70B86E533}'; + CLSID_WICBmpDecoder: TGUID = '{6B462062-7CBF-400D-9FDB-813DD10F2778}'; + CLSID_WICPngDecoder: TGUID = '{389EA17B-5078-4CDE-B6EF-25C15175C751}'; + CLSID_WICIcoDecoder: TGUID = '{C61BFCDF-2E0F-4AAD-A8D7-E06BAFEBCDFE}'; + CLSID_WICJpegDecoder: TGUID = '{9456A480-E88B-43EA-9E73-0B2D9B71B1CA}'; + CLSID_WICGifDecoder: TGUID = '{381DDA3C-9CE9-4834-A23E-1F98F8FC52BE}'; + CLSID_WICTiffDecoder: TGUID = '{B54E85D9-FE23-499F-8B88-6ACEA713752B}'; + CLSID_WICWmpDecoder: TGUID = '{A26CEC36-234C-4950-AE16-E34AACE71D0D}'; + CLSID_WICBmpEncoder: TGUID = '{69BE8BB4-D66D-47C8-865A-ED1589433782}'; + CLSID_WICPngEncoder: TGUID = '{27949969-876A-41D7-9447-568F6A35A4DC}'; + CLSID_WICJpegEncoder: TGUID = '{1A34F5C1-4A5A-46DC-B644-1F4567E7A676}'; + CLSID_WICGifEncoder: TGUID = '{114F5598-0B22-40A0-86A1-C83EA495ADBD}'; + CLSID_WICTiffEncoder: TGUID = '{0131BE10-2001-4C5F-A9B0-CC88FAB64CE8}'; + CLSID_WICWmpEncoder: TGUID = '{AC4CE3CB-E1C1-44CD-8215-5A1665509EC2}'; + GUID_ContainerFormatBmp: TGUID = '{0AF1D87E-FCFE-4188-BDEB-A7906471CBE3}'; + GUID_ContainerFormatPng: TGUID = '{1B7CFAF4-713F-473C-BBCD-6137425FAEAF}'; + GUID_ContainerFormatIco: TGUID = '{A3A860C4-338F-4C17-919A-FBA4B5628F21}'; + GUID_ContainerFormatJpeg: TGUID = '{19E4A5AA-5662-4FC5-A0C0-1758028E1057}'; + GUID_ContainerFormatTiff: TGUID = '{163BCC30-E2E9-4F0B-961D-A3E9FDB788A3}'; + GUID_ContainerFormatGif: TGUID = '{1F8A5601-7D4D-4CBD-9C82-1BC8D4EEB9A5}'; + GUID_ContainerFormatWmp: TGUID = '{57A37CAA-367A-4540-916B-F183C5093A4B}'; + CLSID_WICImagingCategories: TGUID = '{FAE3D380-FEA4-4623-8C75-C6B61110B681}'; + CATID_WICBitmapDecoders: TGUID = '{7ED96837-96F0-4812-B211-F13C24117ED3}'; + CATID_WICBitmapEncoders: TGUID = '{AC757296-3522-4E11-9862-C17BE5A1767E}'; + CATID_WICPixelFormats: TGUID = '{2B46E70F-CDA7-473E-89F6-DC9630A2390B}'; + CATID_WICFormatConverters: TGUID = '{7835EAE8-BF14-49D1-93CE-533A407B2248}'; + CATID_WICMetadataReader: TGUID = '{05AF94D8-7174-4CD2-BE4A-4124B80EE4B8}'; + CATID_WICMetadataWriter: TGUID = '{ABE3B9A4-257D-4B97-BD1A-294AF496222E}'; + CLSID_WICDefaultFormatConverter: TGUID = '{1A3F11DC-B514-4B17-8C5F-2154513852F1}'; + CLSID_WICFormatConverterHighColor: TGUID = '{AC75D454-9F37-48F8-B972-4E19BC856011}'; + CLSID_WICFormatConverterNChannel: TGUID = '{C17CABB2-D4A3-47D7-A557-339B2EFBD4F1}'; + CLSID_WICFormatConverterWMPhoto: TGUID = '{9CB5172B-D600-46BA-AB77-77BB7E3A00D9}'; + + GUID_WICPixelFormatUndefined: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC900}'; + GUID_WICPixelFormatDontCare: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC900}'; + GUID_WICPixelFormat1bppIndexed: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC901}'; + GUID_WICPixelFormat2bppIndexed: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC902}'; + GUID_WICPixelFormat4bppIndexed: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC903}'; + GUID_WICPixelFormat8bppIndexed: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC904}'; + GUID_WICPixelFormatBlackWhite: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC905}'; + GUID_WICPixelFormat2bppGray: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC906}'; + GUID_WICPixelFormat4bppGray: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC907}'; + GUID_WICPixelFormat8bppGray: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC908}'; + GUID_WICPixelFormat8bppAlpha: TGUID = '{E6CD0116-EEBA-4161-AA85-27DD9FB3A895}'; + GUID_WICPixelFormat16bppBGR555: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC909}'; + GUID_WICPixelFormat16bppBGR565: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC90A}'; + GUID_WICPixelFormat16bppBGRA5551: TGUID = '{05EC7C2B-F1E6-4961-AD46-E1CC810A87D2}'; + GUID_WICPixelFormat16bppGray: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC90B}'; + GUID_WICPixelFormat24bppBGR: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC90C}'; + GUID_WICPixelFormat24bppRGB: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC90D}'; + GUID_WICPixelFormat32bppBGR: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC90E}'; + GUID_WICPixelFormat32bppBGRA: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC90F}'; + GUID_WICPixelFormat32bppPBGRA: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC910}'; + GUID_WICPixelFormat32bppGrayFloat: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC911}'; + GUID_WICPixelFormat32bppRGBA: TGUID = '{F5C7AD2D-6A8D-43DD-A7A8-A29935261AE9}'; + GUID_WICPixelFormat32bppPRGBA: TGUID = '{3CC4A650-A527-4D37-A916-3142C7EBEDBA}'; + GUID_WICPixelFormat48bppRGB: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC915}'; + GUID_WICPixelFormat48bppBGR: TGUID = '{E605A384-B468-46CE-BB2E-36F180E64313}'; + GUID_WICPixelFormat64bppRGBA: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC916}'; + GUID_WICPixelFormat64bppBGRA: TGUID = '{1562FF7C-D352-46F9-979E-42976B792246}'; + GUID_WICPixelFormat64bppPRGBA: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC917}'; + GUID_WICPixelFormat64bppPBGRA: TGUID = '{8C518E8E-A4EC-468B-AE70-C9A35A9C5530}'; + GUID_WICPixelFormat16bppGrayFixedPoint: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC913}'; + GUID_WICPixelFormat32bppBGR101010: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC914}'; + GUID_WICPixelFormat48bppRGBFixedPoint: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC912}'; + GUID_WICPixelFormat48bppBGRFixedPoint: TGUID = '{49CA140E-CAB6-493B-9DDF-60187C37532A}'; + GUID_WICPixelFormat96bppRGBFixedPoint: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC918}'; + GUID_WICPixelFormat128bppRGBAFloat: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC919}'; + GUID_WICPixelFormat128bppPRGBAFloat: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC91A}'; + GUID_WICPixelFormat128bppRGBFloat: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC91B}'; + GUID_WICPixelFormat32bppCMYK: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC91C}'; + GUID_WICPixelFormat64bppRGBAFixedPoint: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC91D}'; + GUID_WICPixelFormat64bppBGRAFixedPoint: TGUID = '{356de33c-54d2-4a23-bb04-9b7bf9b1d42d}'; + GUID_WICPixelFormat64bppRGBFixedPoint: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC940}'; + GUID_WICPixelFormat128bppRGBAFixedPoint: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC91E}'; + GUID_WICPixelFormat128bppRGBFixedPoint: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC941}'; + GUID_WICPixelFormat64bppRGBAHalf: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC93A}'; + GUID_WICPixelFormat64bppRGBHalf: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC942}'; + GUID_WICPixelFormat48bppRGBHalf: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC93B}'; + GUID_WICPixelFormat32bppRGBE: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC93D}'; + GUID_WICPixelFormat16bppGrayHalf: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC93E}'; + GUID_WICPixelFormat32bppGrayFixedPoint: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC93F}'; + GUID_WICPixelFormat32bppRGBA1010102: TGUID = '{25238D72-FCF9-4522-B514-5578E5AD55E0}'; + GUID_WICPixelFormat32bppRGBA1010102XR: TGUID = '{00DE6B9A-C101-434B-B502-D0165EE1122C}'; + GUID_WICPixelFormat64bppCMYK: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC91F}'; + GUID_WICPixelFormat24bpp3Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC920}'; + GUID_WICPixelFormat32bpp4Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC921}'; + GUID_WICPixelFormat40bpp5Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC922}'; + GUID_WICPixelFormat48bpp6Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC923}'; + GUID_WICPixelFormat56bpp7Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC924}'; + GUID_WICPixelFormat64bpp8Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC925}'; + GUID_WICPixelFormat48bpp3Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC926}'; + GUID_WICPixelFormat64bpp4Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC927}'; + GUID_WICPixelFormat80bpp5Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC928}'; + GUID_WICPixelFormat96bpp6Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC929}'; + GUID_WICPixelFormat112bpp7Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC92A}'; + GUID_WICPixelFormat128bpp8Channels: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC92B}'; + GUID_WICPixelFormat40bppCMYKAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC92C}'; + GUID_WICPixelFormat80bppCMYKAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC92D}'; + GUID_WICPixelFormat32bpp3ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC92E}'; + GUID_WICPixelFormat40bpp4ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC92F}'; + GUID_WICPixelFormat48bpp5ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC930}'; + GUID_WICPixelFormat56bpp6ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC931}'; + GUID_WICPixelFormat64bpp7ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC932}'; + GUID_WICPixelFormat72bpp8ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC933}'; + GUID_WICPixelFormat64bpp3ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC934}'; + GUID_WICPixelFormat80bpp4ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC935}'; + GUID_WICPixelFormat96bpp5ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC936}'; + GUID_WICPixelFormat112bpp6ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC937}'; + GUID_WICPixelFormat128bpp7ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC938}'; + GUID_WICPixelFormat144bpp8ChannelsAlpha: TGUID = '{6FDDC324-4E03-4BFE-B185-3D77768DC939}'; + +type + IWICStream = interface(IStream) + [SID_IWICStream] + function InitializeFromIStream(pIStream: IStream): HRESULT; stdcall; + function InitializeFromFilename(wzFileName: LPCWSTR; + dwDesiredAccess: DWORD): HRESULT; stdcall; + function InitializeFromMemory(pbBuffer: WICInProcPointer; + cbBufferSize: DWORD): HRESULT; stdcall; + function InitializeFromIStreamRegion(pIStream: IStream; + ulOffset: ULARGE_INTEGER; ulMaxSize: ULARGE_INTEGER): HRESULT; stdcall; + end; + + +class function TCanvasD2D.ImagingFactory: IWICImagingFactory; +begin + if not Assigned(FImagingFactory) then + begin + CoCreateInstance(CLSID_WICImagingFactory, nil, CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, + IUnknown, FImagingFactory); + end; + Result := FImagingFactory; +end; + + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjcapimin.pas b/resources/libraries/deskew/Imaging/JpegLib/imjcapimin.pas new file mode 100755 index 0000000..826d1b7 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjcapimin.pas @@ -0,0 +1,401 @@ +unit imjcapimin; + +{ 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. } + +{ jcapimin.c ; Copyright (C) 1994-1998, Thomas G. Lane. } + + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjdeferr, + imjerror, + imjpeglib, + imjcomapi, + imjmemmgr, + imjcmarker; + +{ Initialization of JPEG compression objects. + Nomssi: This is a macro in the original code. + + 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. } + +procedure jpeg_create_compress(cinfo : j_compress_ptr); + + +{ Initialization of a JPEG compression object. + The error manager must already be set up (in case memory manager fails). } + +{GLOBAL} +procedure jpeg_CreateCompress (cinfo : j_compress_ptr; + version : int; + structsize : size_t); + +{ Destruction of a JPEG compression object } + +{GLOBAL} +procedure jpeg_destroy_compress (cinfo : j_compress_ptr); + + +{ Abort processing of a JPEG compression operation, + but don't destroy the object itself. } + +{GLOBAL} +procedure jpeg_abort_compress (cinfo : j_compress_ptr); + + +{ 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} +procedure jpeg_suppress_tables (cinfo : j_compress_ptr; + suppress : boolean); + + +{ 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} +procedure jpeg_finish_compress (cinfo : j_compress_ptr); + +{ 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} +procedure jpeg_write_marker (cinfo : j_compress_ptr; + marker : int; + dataptr : JOCTETptr; + datalen : uInt); + +{GLOBAL} +procedure jpeg_write_m_header (cinfo : j_compress_ptr; + marker : int; + datalen : uint); +{GLOBAL} +procedure jpeg_write_m_byte (cinfo : j_compress_ptr; val : int); + +{ 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} +procedure jpeg_write_tables (cinfo : j_compress_ptr); + +implementation + +procedure jpeg_create_compress(cinfo : j_compress_ptr); +begin + jpeg_CreateCompress(cinfo, JPEG_LIB_VERSION, + size_t(sizeof(jpeg_compress_struct))); +end; + +{ Initialization of a JPEG compression object. + The error manager must already be set up (in case memory manager fails). } + +{GLOBAL} +procedure jpeg_CreateCompress (cinfo : j_compress_ptr; + version : int; + structsize : size_t); +var + i : int; +var + err : jpeg_error_mgr_ptr; + client_data : voidp; +begin + + { Guard against version mismatches between library and caller. } + cinfo^.mem := NIL; { so jpeg_destroy knows mem mgr not called } + if (version <> JPEG_LIB_VERSION) then + ERREXIT2(j_common_ptr(cinfo), JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version); + if (structsize <> SIZEOF(jpeg_compress_struct)) then + ERREXIT2(j_common_ptr(cinfo), JERR_BAD_STRUCT_SIZE, + int(SIZEOF(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. } + + err := cinfo^.err; + client_data := cinfo^.client_data; { ignore Purify complaint here } + MEMZERO(cinfo, SIZEOF(jpeg_compress_struct)); + cinfo^.err := err; + 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 := NIL; + cinfo^.dest := NIL; + + cinfo^.comp_info := NIL; + + for i := 0 to pred(NUM_QUANT_TBLS) do + cinfo^.quant_tbl_ptrs[i] := NIL; + + for i := 0 to pred(NUM_HUFF_TBLS) do + begin + cinfo^.dc_huff_tbl_ptrs[i] := NIL; + cinfo^.ac_huff_tbl_ptrs[i] := NIL; + end; + + cinfo^.script_space := NIL; + + cinfo^.input_gamma := 1.0; { in case application forgets } + + { OK, I'm ready } + cinfo^.global_state := CSTATE_START; +end; + + +{ Destruction of a JPEG compression object } + +{GLOBAL} +procedure jpeg_destroy_compress (cinfo : j_compress_ptr); +begin + jpeg_destroy(j_common_ptr(cinfo)); { use common routine } +end; + + +{ Abort processing of a JPEG compression operation, + but don't destroy the object itself. } + +{GLOBAL} +procedure jpeg_abort_compress (cinfo : j_compress_ptr); +begin + jpeg_abort(j_common_ptr(cinfo)); { use common routine } +end; + + +{ 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} +procedure jpeg_suppress_tables (cinfo : j_compress_ptr; + suppress : boolean); +var + i : int; + qtbl : JQUANT_TBL_PTR; + htbl : JHUFF_TBL_PTR; +begin + for i := 0 to pred(NUM_QUANT_TBLS) do + begin + qtbl := cinfo^.quant_tbl_ptrs[i]; + if (qtbl <> NIL) then + qtbl^.sent_table := suppress; + end; + + for i := 0 to pred(NUM_HUFF_TBLS) do + begin + htbl := cinfo^.dc_huff_tbl_ptrs[i]; + if (htbl <> NIL) then + htbl^.sent_table := suppress; + htbl := cinfo^.ac_huff_tbl_ptrs[i]; + if (htbl <> NIL) then + htbl^.sent_table := suppress; + end; +end; + + +{ 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} +procedure jpeg_finish_compress (cinfo : j_compress_ptr); +var + iMCU_row : JDIMENSION; +begin + if (cinfo^.global_state = CSTATE_SCANNING) or + (cinfo^.global_state = CSTATE_RAW_OK) then + begin + { Terminate first pass } + if (cinfo^.next_scanline < cinfo^.image_height) then + ERREXIT(j_common_ptr(cinfo), JERR_TOO_LITTLE_DATA); + cinfo^.master^.finish_pass (cinfo); + end + else + if (cinfo^.global_state <> CSTATE_WRCOEFS) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); + { Perform any remaining passes } + while (not cinfo^.master^.is_last_pass) do + begin + cinfo^.master^.prepare_for_pass (cinfo); + for iMCU_row := 0 to pred(cinfo^.total_iMCU_rows) do + begin + if (cinfo^.progress <> NIL) then + begin + cinfo^.progress^.pass_counter := long (iMCU_row); + cinfo^.progress^.pass_limit := long (cinfo^.total_iMCU_rows); + cinfo^.progress^.progress_monitor (j_common_ptr(cinfo)); + end; + { We bypass the main controller and invoke coef controller directly; + all work is being done from the coefficient buffer. } + + if (not cinfo^.coef^.compress_data (cinfo, JSAMPIMAGE(NIL))) then + ERREXIT(j_common_ptr(cinfo), JERR_CANT_SUSPEND); + end; + cinfo^.master^.finish_pass (cinfo); + end; + { 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)); +end; + + +{ 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} +procedure jpeg_write_marker (cinfo : j_compress_ptr; + marker : int; + dataptr : JOCTETptr; + datalen : uInt); +var + write_marker_byte : procedure(info : j_compress_ptr; val : int); +begin + if (cinfo^.next_scanline <> 0) or + ((cinfo^.global_state <> CSTATE_SCANNING) and + (cinfo^.global_state <> CSTATE_RAW_OK) and + (cinfo^.global_state <> CSTATE_WRCOEFS)) then + ERREXIT1(j_common_ptr(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 <> 0) do + begin + Dec(datalen); + write_marker_byte (cinfo, dataptr^); + Inc(dataptr); + end; +end; + +{ Same, but piecemeal. } + +{GLOBAL} +procedure jpeg_write_m_header (cinfo : j_compress_ptr; + marker : int; + datalen : uint); +begin + if (cinfo^.next_scanline <> 0) or + ((cinfo^.global_state <> CSTATE_SCANNING) and + (cinfo^.global_state <> CSTATE_RAW_OK) and + (cinfo^.global_state <> CSTATE_WRCOEFS)) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); + + cinfo^.marker^.write_marker_header (cinfo, marker, datalen); +end; + +{GLOBAL} +procedure jpeg_write_m_byte (cinfo : j_compress_ptr; val : int); +begin + cinfo^.marker^.write_marker_byte (cinfo, val); +end; + + +{ 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} +procedure jpeg_write_tables (cinfo : j_compress_ptr); +begin + if (cinfo^.global_state <> CSTATE_START) then + ERREXIT1(j_common_ptr(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(). } +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjcapistd.pas b/resources/libraries/deskew/Imaging/JpegLib/imjcapistd.pas new file mode 100755 index 0000000..f9ae613 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjcapistd.pas @@ -0,0 +1,222 @@ +unit imjcapistd; + +{ Original : 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. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjdeferr, + imjerror, + imjpeglib, + imjcapimin, imjcinit; + + + +{ 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} +procedure jpeg_start_compress (cinfo : j_compress_ptr; + write_all_tables : boolean); + + +{ 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} +function jpeg_write_scanlines (cinfo : j_compress_ptr; + scanlines : JSAMPARRAY; + num_lines : JDIMENSION) : JDIMENSION; + +{ Alternate entry point to write raw data. + Processes exactly one iMCU row per call, unless suspended. } + +{GLOBAL} +function jpeg_write_raw_data (cinfo : j_compress_ptr; + data : JSAMPIMAGE; + num_lines : JDIMENSION) : JDIMENSION; + +implementation + +{ 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} +procedure jpeg_start_compress (cinfo : j_compress_ptr; + write_all_tables : boolean); +begin + if (cinfo^.global_state <> CSTATE_START) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); + + if (write_all_tables) then + 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; + if cinfo^.raw_data_in then + cinfo^.global_state := CSTATE_RAW_OK + else + cinfo^.global_state := CSTATE_SCANNING; +end; + + +{ 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} +function jpeg_write_scanlines (cinfo : j_compress_ptr; + scanlines : JSAMPARRAY; + num_lines : JDIMENSION) : JDIMENSION; +var + row_ctr, rows_left : JDIMENSION; +begin + if (cinfo^.global_state <> CSTATE_SCANNING) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); + if (cinfo^.next_scanline >= cinfo^.image_height) then + WARNMS(j_common_ptr(cinfo), JWRN_TOO_MUCH_DATA); + + { Call progress monitor hook if present } + if (cinfo^.progress <> NIL) then + begin + cinfo^.progress^.pass_counter := long (cinfo^.next_scanline); + cinfo^.progress^.pass_limit := long (cinfo^.image_height); + cinfo^.progress^.progress_monitor (j_common_ptr(cinfo)); + end; + + { 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) then + 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) then + num_lines := rows_left; + + row_ctr := 0; + cinfo^.main^.process_data (cinfo, scanlines, {var}row_ctr, num_lines); + Inc(cinfo^.next_scanline, row_ctr); + jpeg_write_scanlines := row_ctr; +end; + + +{ Alternate entry point to write raw data. + Processes exactly one iMCU row per call, unless suspended. } + +{GLOBAL} +function jpeg_write_raw_data (cinfo : j_compress_ptr; + data : JSAMPIMAGE; + num_lines : JDIMENSION) : JDIMENSION; +var + lines_per_iMCU_row : JDIMENSION; +begin + if (cinfo^.global_state <> CSTATE_RAW_OK) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); + if (cinfo^.next_scanline >= cinfo^.image_height) then + begin + WARNMS(j_common_ptr(cinfo), JWRN_TOO_MUCH_DATA); + jpeg_write_raw_data := 0; + exit; + end; + + { Call progress monitor hook if present } + if (cinfo^.progress <> NIL) then + begin + cinfo^.progress^.pass_counter := long(cinfo^.next_scanline); + cinfo^.progress^.pass_limit := long(cinfo^.image_height); + cinfo^.progress^.progress_monitor (j_common_ptr(cinfo)); + end; + + { 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) then + 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) then + ERREXIT(j_common_ptr(cinfo), JERR_BUFFER_SIZE); + + { Directly compress the row. } + if (not cinfo^.coef^.compress_data (cinfo, data)) then + begin + { If compressor did not consume the whole row, suspend processing. } + jpeg_write_raw_data := 0; + exit; + end; + + { OK, we processed one iMCU row. } + Inc(cinfo^.next_scanline, lines_per_iMCU_row); + jpeg_write_raw_data := lines_per_iMCU_row; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjccoefct.pas b/resources/libraries/deskew/Imaging/JpegLib/imjccoefct.pas new file mode 100755 index 0000000..7dd97e5 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjccoefct.pas @@ -0,0 +1,521 @@ +unit imjccoefct; + +{ 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.} + +{ Original: jccoefct.c; Copyright (C) 1994-1997, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjerror, + imjdeferr, + imjutils, + imjpeglib; + +{ 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} + +{ Initialize coefficient buffer controller. } + +{GLOBAL} +procedure jinit_c_coef_controller (cinfo : j_compress_ptr; + need_full_buffer : boolean); + +implementation + +{ Private buffer controller object } + +type + my_coef_ptr = ^my_coef_controller; + my_coef_controller = record + pub : jpeg_c_coef_controller; { public fields } + + iMCU_row_num : JDIMENSION; { iMCU row # within image } + mcu_ctr : JDIMENSION; { counts MCUs processed in current row } + MCU_vert_offset : int; { counts MCU rows within iMCU row } + MCU_rows_per_iMCU_row : int; { 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. } + + MCU_buffer : array[0..C_MAX_BLOCKS_IN_MCU-1] of JBLOCKROW; + + { In multi-pass modes, we need a virtual block array for each component. } + whole_image : array[0..MAX_COMPONENTS-1] of jvirt_barray_ptr; + end; + + +{ Forward declarations } +{METHODDEF} +function compress_data(cinfo : j_compress_ptr; + input_buf : JSAMPIMAGE) : boolean; forward; +{$ifdef FULL_COEF_BUFFER_SUPPORTED} +{METHODDEF} +function compress_first_pass(cinfo : j_compress_ptr; + input_buf : JSAMPIMAGE) : boolean; forward; +{METHODDEF} +function compress_output(cinfo : j_compress_ptr; + input_buf : JSAMPIMAGE) : boolean; forward; +{$endif} + + +{LOCAL} +procedure start_iMCU_row (cinfo : j_compress_ptr); +{ Reset within-iMCU-row counters for a new row } +var + coef : my_coef_ptr; +begin + 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) then + begin + coef^.MCU_rows_per_iMCU_row := 1; + end + else + begin + if (coef^.iMCU_row_num < (cinfo^.total_iMCU_rows-1)) then + 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; + end; + + coef^.mcu_ctr := 0; + coef^.MCU_vert_offset := 0; +end; + + +{ Initialize for a processing pass. } + +{METHODDEF} +procedure start_pass_coef (cinfo : j_compress_ptr; + pass_mode : J_BUF_MODE); +var + coef : my_coef_ptr; +begin + coef := my_coef_ptr (cinfo^.coef); + + coef^.iMCU_row_num := 0; + start_iMCU_row(cinfo); + + case (pass_mode) of + JBUF_PASS_THRU: + begin + if (coef^.whole_image[0] <> NIL) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); + coef^.pub.compress_data := compress_data; + end; +{$ifdef FULL_COEF_BUFFER_SUPPORTED} + JBUF_SAVE_AND_PASS: + begin + if (coef^.whole_image[0] = NIL) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); + coef^.pub.compress_data := compress_first_pass; + end; + JBUF_CRANK_DEST: + begin + if (coef^.whole_image[0] = NIL) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); + coef^.pub.compress_data := compress_output; + end; +{$endif} + else + ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); + end; +end; + + +{ 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} +function compress_data (cinfo : j_compress_ptr; + input_buf : JSAMPIMAGE) : boolean; +var + coef : my_coef_ptr; + MCU_col_num : JDIMENSION; { index of current MCU within row } + last_MCU_col : JDIMENSION; + last_iMCU_row : JDIMENSION; + blkn, bi, ci, yindex, yoffset, blockcnt : int; + ypos, xpos : JDIMENSION; + compptr : jpeg_component_info_ptr; +begin + coef := my_coef_ptr (cinfo^.coef); + last_MCU_col := cinfo^.MCUs_per_row - 1; + last_iMCU_row := cinfo^.total_iMCU_rows - 1; + + { Loop to write as much as one whole iMCU row } + for yoffset := coef^.MCU_vert_offset to pred(coef^.MCU_rows_per_iMCU_row) do + begin + for MCU_col_num := coef^.mcu_ctr to last_MCU_col do + begin + { 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 to pred(cinfo^.comps_in_scan) do + begin + compptr := cinfo^.cur_comp_info[ci]; + if (MCU_col_num < last_MCU_col) then + blockcnt := compptr^.MCU_width + else + blockcnt := compptr^.last_col_width; + xpos := MCU_col_num * JDIMENSION(compptr^.MCU_sample_width); + ypos := yoffset * DCTSIZE; { ypos = (yoffset+yindex) * DCTSIZE } + for yindex := 0 to pred(compptr^.MCU_height) do + begin + if (coef^.iMCU_row_num < last_iMCU_row) or + (yoffset+yindex < compptr^.last_row_height) then + begin + cinfo^.fdct^.forward_DCT (cinfo, compptr, + input_buf^[compptr^.component_index], + coef^.MCU_buffer[blkn], + ypos, xpos, JDIMENSION (blockcnt)); + + if (blockcnt < compptr^.MCU_width) then + begin + { Create some dummy blocks at the right edge of the image. } + jzero_far({FAR}pointer(coef^.MCU_buffer[blkn + blockcnt]), + (compptr^.MCU_width - blockcnt) * SIZEOF(JBLOCK)); + for bi := blockcnt to pred(compptr^.MCU_width) do + begin + coef^.MCU_buffer[blkn+bi]^[0][0] := coef^.MCU_buffer[blkn+bi-1]^[0][0]; + end; + end; + end + else + begin + { Create a row of dummy blocks at the bottom of the image. } + jzero_far({FAR}pointer(coef^.MCU_buffer[blkn]), + compptr^.MCU_width * SIZEOF(JBLOCK)); + for bi := 0 to pred(compptr^.MCU_width) do + begin + coef^.MCU_buffer[blkn+bi]^[0][0] := coef^.MCU_buffer[blkn-1]^[0][0]; + end; + end; + Inc(blkn, compptr^.MCU_width); + Inc(ypos, DCTSIZE); + end; + end; + { 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 (not cinfo^.entropy^.encode_mcu (cinfo, JBLOCKARRAY(@coef^.MCU_buffer)^)) then + begin + { Suspension forced; update state counters and exit } + coef^.MCU_vert_offset := yoffset; + coef^.mcu_ctr := MCU_col_num; + compress_data := FALSE; + exit; + end; + end; + { Completed an MCU row, but perhaps not an iMCU row } + coef^.mcu_ctr := 0; + end; + { Completed the iMCU row, advance counters for next one } + Inc(coef^.iMCU_row_num); + start_iMCU_row(cinfo); + compress_data := TRUE; +end; + + +{$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} +function compress_first_pass (cinfo : j_compress_ptr; + input_buf : JSAMPIMAGE) : boolean; +var + coef : my_coef_ptr; + last_iMCU_row : JDIMENSION; + blocks_across, MCUs_across, MCUindex : JDIMENSION; + bi, ci, h_samp_factor, block_row, block_rows, ndummy : int; + lastDC : JCOEF; + compptr : jpeg_component_info_ptr; + buffer : JBLOCKARRAY; + thisblockrow, lastblockrow : JBLOCKROW; +begin + coef := my_coef_ptr (cinfo^.coef); + last_iMCU_row := cinfo^.total_iMCU_rows - 1; + + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + { 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 * JDIMENSION(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) then + block_rows := compptr^.v_samp_factor + else + begin + { NB: can't use last_row_height here, since may not be set! } + block_rows := int (compptr^.height_in_blocks) mod compptr^.v_samp_factor; + if (block_rows = 0) then + block_rows := compptr^.v_samp_factor; + end; + 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) mod h_samp_factor; + if (ndummy > 0) then + 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 to pred(block_rows) do + begin + 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) then + begin + { Create dummy blocks at the right edge of the image. } + Inc(JBLOCK_PTR(thisblockrow), blocks_across); { => first dummy block } + jzero_far({FAR}pointer(thisblockrow), ndummy * SIZEOF(JBLOCK)); + {lastDC := thisblockrow^[-1][0];} + { work around Range Checking } + Dec(JBLOCK_PTR(thisblockrow)); + lastDC := thisblockrow^[0][0]; + Inc(JBLOCK_PTR(thisblockrow)); + + for bi := 0 to pred(ndummy) do + begin + thisblockrow^[bi][0] := lastDC; + end; + end; + end; + { 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) then + begin + Inc(blocks_across, ndummy); { include lower right corner } + MCUs_across := blocks_across div JDIMENSION(h_samp_factor); + for block_row := block_rows to pred(compptr^.v_samp_factor) do + begin + thisblockrow := buffer^[block_row]; + lastblockrow := buffer^[block_row-1]; + jzero_far({FAR} pointer(thisblockrow), + size_t(blocks_across * SIZEOF(JBLOCK))); + for MCUindex := 0 to pred(MCUs_across) do + begin + lastDC := lastblockrow^[h_samp_factor-1][0]; + for bi := 0 to pred(h_samp_factor) do + begin + thisblockrow^[bi][0] := lastDC; + end; + Inc(JBLOCK_PTR(thisblockrow), h_samp_factor); { advance to next MCU in row } + Inc(JBLOCK_PTR(lastblockrow), h_samp_factor); + end; + end; + end; + Inc(compptr); + end; + { 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 } + compress_first_pass := compress_output(cinfo, input_buf); +end; + + +{ 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 NIL pointer. } + +{METHODDEF} +function compress_output (cinfo : j_compress_ptr; + input_buf : JSAMPIMAGE) : boolean; +var + coef : my_coef_ptr; + MCU_col_num : JDIMENSION; { index of current MCU within row } + blkn, ci, xindex, yindex, yoffset : int; + start_col : JDIMENSION; + buffer : array[0..MAX_COMPS_IN_SCAN-1] of JBLOCKARRAY; + buffer_ptr : JBLOCKROW; + compptr : jpeg_component_info_ptr; +begin + coef := my_coef_ptr (cinfo^.coef); + + { 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 to pred(cinfo^.comps_in_scan) do + begin + 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 * JDIMENSION(compptr^.v_samp_factor), + JDIMENSION (compptr^.v_samp_factor), FALSE); + end; + + { Loop to process one whole iMCU row } + for yoffset := coef^.MCU_vert_offset to pred(coef^.MCU_rows_per_iMCU_row) do + begin + for MCU_col_num := coef^.mcu_ctr to pred(cinfo^.MCUs_per_row) do + begin + { Construct list of pointers to DCT blocks belonging to this MCU } + blkn := 0; { index of current DCT block within MCU } + for ci := 0 to pred(cinfo^.comps_in_scan) do + begin + compptr := cinfo^.cur_comp_info[ci]; + start_col := MCU_col_num * JDIMENSION(compptr^.MCU_width); + for yindex := 0 to pred(compptr^.MCU_height) do + begin + buffer_ptr := JBLOCKROW(@ buffer[ci]^[yindex+yoffset]^[start_col]); + for xindex := 0 to pred(compptr^.MCU_width) do + begin + coef^.MCU_buffer[blkn] := buffer_ptr; + Inc(blkn); + Inc(JBLOCK_PTR(buffer_ptr)); + end; + end; + end; + { Try to write the MCU. } + if (not cinfo^.entropy^.encode_mcu (cinfo, coef^.MCU_buffer)) then + begin + { Suspension forced; update state counters and exit } + coef^.MCU_vert_offset := yoffset; + coef^.mcu_ctr := MCU_col_num; + compress_output := FALSE; + exit; + end; + end; + { Completed an MCU row, but perhaps not an iMCU row } + coef^.mcu_ctr := 0; + end; + { Completed the iMCU row, advance counters for next one } + Inc(coef^.iMCU_row_num); + start_iMCU_row(cinfo); + compress_output := TRUE; +end; + +{$endif} { FULL_COEF_BUFFER_SUPPORTED } + + +{ Initialize coefficient buffer controller. } + +{GLOBAL} +procedure jinit_c_coef_controller (cinfo : j_compress_ptr; + need_full_buffer : boolean); +var + coef : my_coef_ptr; +var + buffer : JBLOCKROW; + i : int; +var + ci : int; + compptr : jpeg_component_info_ptr; +begin + coef := my_coef_ptr ( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + SIZEOF(my_coef_controller)) ); + cinfo^.coef := jpeg_c_coef_controller_ptr(coef); + coef^.pub.start_pass := start_pass_coef; + + { Create the coefficient buffer. } + if (need_full_buffer) then + begin +{$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. } + + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + 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)); + Inc(compptr); + end; +{$else} + ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); +{$endif} + end + else + begin + { We only need a single-MCU buffer. } + buffer := JBLOCKROW ( + cinfo^.mem^.alloc_large (j_common_ptr(cinfo), JPOOL_IMAGE, + C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)) ); + for i := 0 to pred(C_MAX_BLOCKS_IN_MCU) do + begin + coef^.MCU_buffer[i] := JBLOCKROW(@ buffer^[i]); + end; + coef^.whole_image[0] := NIL; { flag for no virtual arrays } + end; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjccolor.pas b/resources/libraries/deskew/Imaging/JpegLib/imjccolor.pas new file mode 100755 index 0000000..0e8e16a --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjccolor.pas @@ -0,0 +1,530 @@ +unit imjccolor; + +{ This file contains input colorspace conversion routines. } + +{ Original : jccolor.c ; Copyright (C) 1991-1996, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjdeferr, + imjerror, + imjpeglib; + +{ Module initialization routine for input colorspace conversion. } + +{GLOBAL} +procedure jinit_color_converter (cinfo : j_compress_ptr); + +implementation + +{ Private subobject } +type + INT32_FIELD = array[0..MaxInt div SizeOf(INT32) - 1] of INT32; + INT32_FIELD_PTR = ^INT32_FIELD; + +type + my_cconvert_ptr = ^my_color_converter; + my_color_converter = record + pub : jpeg_color_converter; { public fields } + + { Private state for RGB -> YCC conversion } + rgb_ycc_tab : INT32_FIELD_PTR; { => table for RGB to YCbCr conversion } + end; {my_color_converter;} + + +{*************** 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. } +const + SCALEBITS = 16; { speediest right-shift on some machines } + CBCR_OFFSET = INT32(CENTERJSAMPLE shl SCALEBITS); + ONE_HALF = INT32(1) shl (SCALEBITS-1); + + +{ 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). } + + R_Y_OFF = 0; { offset to R => Y section } + G_Y_OFF = 1*(MAXJSAMPLE+1); { offset to G => Y section } + B_Y_OFF = 2*(MAXJSAMPLE+1); { etc. } + R_CB_OFF = 3*(MAXJSAMPLE+1); + G_CB_OFF = 4*(MAXJSAMPLE+1); + B_CB_OFF = 5*(MAXJSAMPLE+1); + R_CR_OFF = B_CB_OFF; { B=>Cb, R=>Cr are the same } + G_CR_OFF = 6*(MAXJSAMPLE+1); + B_CR_OFF = 7*(MAXJSAMPLE+1); + TABLE_SIZE = 8*(MAXJSAMPLE+1); + + +{ Initialize for RGB->YCC colorspace conversion. } + +{METHODDEF} +procedure rgb_ycc_start (cinfo : j_compress_ptr); +const + FIX_0_29900 = INT32(Round(0.29900 * (1 shl SCALEBITS))); + FIX_0_58700 = INT32(Round(0.58700 * (1 shl SCALEBITS))); + FIX_0_11400 = INT32(Round(0.11400 * (1 shl SCALEBITS))); + FIX_0_16874 = INT32(Round(0.16874 * (1 shl SCALEBITS))); + FIX_0_33126 = INT32(Round(0.33126 * (1 shl SCALEBITS))); + FIX_0_50000 = INT32(Round(0.50000 * (1 shl SCALEBITS))); + FIX_0_41869 = INT32(Round(0.41869 * (1 shl SCALEBITS))); + FIX_0_08131 = INT32(Round(0.08131 * (1 shl SCALEBITS))); +var + cconvert : my_cconvert_ptr; + rgb_ycc_tab : INT32_FIELD_PTR; + i : INT32; +begin + cconvert := my_cconvert_ptr (cinfo^.cconvert); + + { Allocate and fill in the conversion tables. } + rgb_ycc_tab := INT32_FIELD_PTR( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + (TABLE_SIZE * SIZEOF(INT32))) ); + cconvert^.rgb_ycc_tab := rgb_ycc_tab; + + for i := 0 to MAXJSAMPLE do + begin + 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; + end; +end; + + +{ 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} +procedure rgb_ycc_convert (cinfo : j_compress_ptr; + input_buf : JSAMPARRAY; + output_buf : JSAMPIMAGE; + output_row : JDIMENSION; + num_rows : int); +var + cconvert : my_cconvert_ptr; + {register} r, g, b : int; + {register} ctab : INT32_FIELD_PTR; + {register} inptr : JSAMPROW; + {register} outptr0, outptr1, outptr2 : JSAMPROW; + {register} col : JDIMENSION; + num_cols : JDIMENSION; +begin + cconvert := my_cconvert_ptr (cinfo^.cconvert); + ctab := cconvert^.rgb_ycc_tab; + num_cols := cinfo^.image_width; + + while (num_rows > 0) do + begin + Dec(num_rows); + inptr := input_buf^[0]; + Inc(JSAMPROW_PTR(input_buf)); + outptr0 := output_buf^[0]^[output_row]; + outptr1 := output_buf^[1]^[output_row]; + outptr2 := output_buf^[2]^[output_row]; + Inc(output_row); + for col := 0 to pred(num_cols) do + begin + r := GETJSAMPLE(inptr^[RGB_RED]); + g := GETJSAMPLE(inptr^[RGB_GREEN]); + b := GETJSAMPLE(inptr^[RGB_BLUE]); + Inc(JSAMPLE_PTR(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]) + shr SCALEBITS) ); + { Cb } + outptr1^[col] := JSAMPLE( + ((ctab^[r+R_CB_OFF] + ctab^[g+G_CB_OFF] + ctab^[b+B_CB_OFF]) + shr SCALEBITS) ); + { Cr } + outptr2^[col] := JSAMPLE( + ((ctab^[r+R_CR_OFF] + ctab^[g+G_CR_OFF] + ctab^[b+B_CR_OFF]) + shr SCALEBITS) ); + end; + end; +end; + + +{*************** 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} +procedure rgb_gray_convert (cinfo : j_compress_ptr; + input_buf : JSAMPARRAY; + output_buf : JSAMPIMAGE; + output_row : JDIMENSION; + num_rows : int); +var + cconvert : my_cconvert_ptr; + {register} r, g, b : int; + {register} ctab :INT32_FIELD_PTR; + {register} inptr : JSAMPROW; + {register} outptr : JSAMPROW; + {register} col : JDIMENSION; + num_cols : JDIMENSION; +begin + cconvert := my_cconvert_ptr (cinfo^.cconvert); + ctab := cconvert^.rgb_ycc_tab; + num_cols := cinfo^.image_width; + + while (num_rows > 0) do + begin + Dec(num_rows); + inptr := input_buf[0]; + Inc(JSAMPROW_PTR(input_buf)); + outptr := output_buf[0][output_row]; + Inc(output_row); + for col := 0 to num_cols - 1 do + begin + r := GETJSAMPLE(inptr[RGB_RED]); + g := GETJSAMPLE(inptr[RGB_GREEN]); + b := GETJSAMPLE(inptr[RGB_BLUE]); + Inc(JSAMPLE_PTR(inptr), RGB_PIXELSIZE); + (* Y *) + // kylix 3 compiler crashes on this + // it also crashes Delphi OSX compiler 9 years later :( + {$IF not (Defined(DCC) and not Defined(MSWINDOWS))} + outptr[col] := JSAMPLE(((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF]) shr SCALEBITS)); + {$IFEND} + end; + end; +end; + + +{ 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} +procedure cmyk_ycck_convert (cinfo : j_compress_ptr; + input_buf : JSAMPARRAY; + output_buf : JSAMPIMAGE; + output_row : JDIMENSION; + num_rows : int); +var + cconvert : my_cconvert_ptr; + {register} r, g, b : int; + {register} ctab : INT32_FIELD_PTR; + {register} inptr : JSAMPROW; + {register} outptr0, outptr1, outptr2, outptr3 : JSAMPROW; + {register} col : JDIMENSION; + num_cols : JDIMENSION; +begin + cconvert := my_cconvert_ptr (cinfo^.cconvert); + ctab := cconvert^.rgb_ycc_tab; + num_cols := cinfo^.image_width; + + while (num_rows > 0) do + begin + Dec(num_rows); + inptr := input_buf^[0]; + Inc(JSAMPROW_PTR(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]; + Inc(output_row); + for col := 0 to pred(num_cols) do + begin + 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 } + Inc(JSAMPLE_PTR(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]) + shr SCALEBITS) ); + { Cb } + outptr1^[col] := JSAMPLE( + ((ctab^[r+R_CB_OFF] + ctab^[g+G_CB_OFF] + ctab^[b+B_CB_OFF]) + shr SCALEBITS) ); + { Cr } + outptr2^[col] := JSAMPLE ( + ((ctab^[r+R_CR_OFF] + ctab^[g+G_CR_OFF] + ctab^[b+B_CR_OFF]) + shr SCALEBITS) ); + end; + end; +end; + + +{ 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} +procedure grayscale_convert (cinfo : j_compress_ptr; + input_buf : JSAMPARRAY; + output_buf : JSAMPIMAGE; + output_row : JDIMENSION; + num_rows: int); +var + {register} inptr : JSAMPROW; + {register} outptr : JSAMPROW; + {register} col : JDIMENSION; + num_cols :JDIMENSION; + instride : int; +begin + num_cols := cinfo^.image_width; + instride := cinfo^.input_components; + + while (num_rows > 0) do + begin + Dec(num_rows); + inptr := input_buf^[0]; + Inc(JSAMPROW_PTR(input_buf)); + outptr := output_buf^[0]^[output_row]; + Inc(output_row); + for col := 0 to pred(num_cols) do + begin + outptr^[col] := inptr^[0]; { don't need GETJSAMPLE() here } + Inc(JSAMPLE_PTR(inptr), instride); + end; + end; +end; + + +{ Convert some rows of samples to the JPEG colorspace. + This version handles multi-component colorspaces without conversion. + We assume input_components = num_components. } + +{METHODDEF} +procedure null_convert (cinfo : j_compress_ptr; + input_buf : JSAMPARRAY; + output_buf : JSAMPIMAGE; + output_row : JDIMENSION; + num_rows : int); +var + {register} inptr : JSAMPROW; + {register} outptr : JSAMPROW; + {register} col : JDIMENSION; + {register} ci : int; + nc : int; + num_cols : JDIMENSION; +begin + nc := cinfo^.num_components; + num_cols := cinfo^.image_width; + + while (num_rows > 0) do + begin + Dec(num_rows); + { It seems fastest to make a separate pass for each component. } + for ci := 0 to pred(nc) do + begin + inptr := input_buf^[0]; + outptr := output_buf^[ci]^[output_row]; + for col := 0 to pred(num_cols) do + begin + outptr^[col] := inptr^[ci]; { don't need GETJSAMPLE() here } + Inc(JSAMPLE_PTR(inptr), nc); + end; + end; + Inc(JSAMPROW_PTR(input_buf)); + Inc(output_row); + end; +end; + + +{ Empty method for start_pass. } + +{METHODDEF} +procedure null_method (cinfo : j_compress_ptr); +begin + { no work needed } +end; + + +{ Module initialization routine for input colorspace conversion. } + +{GLOBAL} +procedure jinit_color_converter (cinfo : j_compress_ptr); +var + cconvert : my_cconvert_ptr; +begin + cconvert := my_cconvert_ptr( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + SIZEOF(my_color_converter)) ); + cinfo^.cconvert := jpeg_color_converter_ptr(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 } + case (cinfo^.in_color_space) of + JCS_GRAYSCALE: + if (cinfo^.input_components <> 1) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_IN_COLORSPACE); + +{$ifdef RGB_PIXELSIZE <> 3} + JCS_RGB: + if (cinfo^.input_components <> RGB_PIXELSIZE) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_IN_COLORSPACE); +{$else} { share code with YCbCr } + JCS_RGB, +{$endif} + JCS_YCbCr: + if (cinfo^.input_components <> 3) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_IN_COLORSPACE); + + JCS_CMYK, + JCS_YCCK: + if (cinfo^.input_components <> 4) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_IN_COLORSPACE); + + else { JCS_UNKNOWN can be anything } + if (cinfo^.input_components < 1) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_IN_COLORSPACE); + end; + + { Check num_components, set conversion method based on requested space } + case (cinfo^.jpeg_color_space) of + JCS_GRAYSCALE: + begin + if (cinfo^.num_components <> 1) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_J_COLORSPACE); + if (cinfo^.in_color_space = JCS_GRAYSCALE) then + cconvert^.pub.color_convert := grayscale_convert + else + if (cinfo^.in_color_space = JCS_RGB) then + begin + cconvert^.pub.start_pass := rgb_ycc_start; + cconvert^.pub.color_convert := rgb_gray_convert; + end + else + if (cinfo^.in_color_space = JCS_YCbCr) then + cconvert^.pub.color_convert := grayscale_convert + else + ERREXIT(j_common_ptr(cinfo), JERR_CONVERSION_NOTIMPL); + end; + + JCS_RGB: + begin + if (cinfo^.num_components <> 3) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_J_COLORSPACE); + if (cinfo^.in_color_space = JCS_RGB) and (RGB_PIXELSIZE = 3) then + cconvert^.pub.color_convert := null_convert + else + ERREXIT(j_common_ptr(cinfo), JERR_CONVERSION_NOTIMPL); + end; + + JCS_YCbCr: + begin + if (cinfo^.num_components <> 3) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_J_COLORSPACE); + if (cinfo^.in_color_space = JCS_RGB) then + begin + cconvert^.pub.start_pass := rgb_ycc_start; + cconvert^.pub.color_convert := rgb_ycc_convert; + end + else + if (cinfo^.in_color_space = JCS_YCbCr) then + cconvert^.pub.color_convert := null_convert + else + ERREXIT(j_common_ptr(cinfo), JERR_CONVERSION_NOTIMPL); + end; + + JCS_CMYK: + begin + if (cinfo^.num_components <> 4) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_J_COLORSPACE); + if (cinfo^.in_color_space = JCS_CMYK) then + cconvert^.pub.color_convert := null_convert + else + ERREXIT(j_common_ptr(cinfo), JERR_CONVERSION_NOTIMPL); + end; + + JCS_YCCK: + begin + if (cinfo^.num_components <> 4) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_J_COLORSPACE); + if (cinfo^.in_color_space = JCS_CMYK) then + begin + cconvert^.pub.start_pass := rgb_ycc_start; + cconvert^.pub.color_convert := cmyk_ycck_convert; + end + else + if (cinfo^.in_color_space = JCS_YCCK) then + cconvert^.pub.color_convert := null_convert + else + ERREXIT(j_common_ptr(cinfo), JERR_CONVERSION_NOTIMPL); + end; + + else { allow null conversion of JCS_UNKNOWN } + begin + if (cinfo^.jpeg_color_space <> cinfo^.in_color_space) or + (cinfo^.num_components <> cinfo^.input_components) then + ERREXIT(j_common_ptr(cinfo), JERR_CONVERSION_NOTIMPL); + cconvert^.pub.color_convert := null_convert; + end; + end; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjcdctmgr.pas b/resources/libraries/deskew/Imaging/JpegLib/imjcdctmgr.pas new file mode 100755 index 0000000..3cc2992 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjcdctmgr.pas @@ -0,0 +1,513 @@ +unit imjcdctmgr; + +{ Original : 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. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjdeferr, + imjerror, + imjpeglib, + imjdct, { Private declarations for DCT subsystem } + imjfdctint, imjfdctfst, imjfdctflt; + +{ Initialize FDCT manager. } + +{GLOBAL} +procedure jinit_forward_dct (cinfo : j_compress_ptr); + +implementation + + +{ Private subobject for this module } + +type + my_fdct_ptr = ^my_fdct_controller; + my_fdct_controller = record + pub : jpeg_forward_dct; { public fields } + + { Pointer to the DCT routine actually in use } + do_dct : forward_DCT_method_ptr; + + { 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. } + + divisors : array[0..NUM_QUANT_TBLS-1] of DCTELEM_FIELD_PTR; + + {$ifdef DCT_FLOAT_SUPPORTED} + { Same as above for the floating-point case. } + do_float_dct : float_DCT_method_ptr; + float_divisors : array[0..NUM_QUANT_TBLS-1] of FAST_FLOAT_FIELD_PTR; + {$endif} + end; + + +{ 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} +procedure start_pass_fdctmgr (cinfo : j_compress_ptr); +var + fdct : my_fdct_ptr; + ci, qtblno, i : int; + compptr : jpeg_component_info_ptr; + qtbl : JQUANT_TBL_PTR; + dtbl : DCTELEM_FIELD_PTR; +{$ifdef DCT_IFAST_SUPPORTED} +const + CONST_BITS = 14; + aanscales : array[0..DCTSIZE2-1] of INT16 = + ({ 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} + + { 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. } + + function DESCALE(x : INT32; n : int) : INT32; + var + shift_temp : INT32; + begin + shift_temp := x + (INT32(1) shl (n-1)); + {$ifdef RIGHT_SHIFT_IS_UNSIGNED} + if shift_temp < 0 then + Descale := (shift_temp shr n) or ((not INT32(0)) shl (32-n)) + else + {$endif} + Descale := (shift_temp shr n); + end; + +{$endif} +{$ifdef DCT_FLOAT_SUPPORTED} +var + fdtbl : FAST_FLOAT_FIELD_PTR; + row, col : int; +const + aanscalefactor : array[0..DCTSIZE-1] of double = + (1.0, 1.387039845, 1.306562965, 1.175875602, + 1.0, 0.785694958, 0.541196100, 0.275899379); +{$endif} +begin + fdct := my_fdct_ptr (cinfo^.fdct); + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + qtblno := compptr^.quant_tbl_no; + { Make sure specified quantization table is present } + if (qtblno < 0) or (qtblno >= NUM_QUANT_TBLS) or + (cinfo^.quant_tbl_ptrs[qtblno] = NIL) then + ERREXIT1(j_common_ptr(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 } + case (cinfo^.dct_method) of +{$ifdef DCT_ISLOW_SUPPORTED} + JDCT_ISLOW: + begin + { For LL&M IDCT method, divisors are equal to raw quantization + coefficients multiplied by 8 (to counteract scaling). } + + if (fdct^.divisors[qtblno] = NIL) then + begin + fdct^.divisors[qtblno] := DCTELEM_FIELD_PTR( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + DCTSIZE2 * SIZEOF(DCTELEM)) ); + end; + dtbl := fdct^.divisors[qtblno]; + for i := 0 to pred(DCTSIZE2) do + begin + dtbl^[i] := (DCTELEM(qtbl^.quantval[i])) shl 3; + end; + end; +{$endif} +{$ifdef DCT_IFAST_SUPPORTED} + JDCT_IFAST: + begin + { 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. } + + + if (fdct^.divisors[qtblno] = NIL) then + begin + fdct^.divisors[qtblno] := DCTELEM_FIELD_PTR( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + DCTSIZE2 * SIZEOF(DCTELEM)) ); + end; + dtbl := fdct^.divisors[qtblno]; + for i := 0 to pred(DCTSIZE2) do + begin + dtbl^[i] := DCTELEM( + {MULTIPLY16V16} + DESCALE( INT32(qtbl^.quantval[i]) * INT32 (aanscales[i]), + CONST_BITS-3) ); + end; + end; +{$endif} +{$ifdef DCT_FLOAT_SUPPORTED} + + JDCT_FLOAT: + begin + { 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. } + + if (fdct^.float_divisors[qtblno] = NIL) then + begin + fdct^.float_divisors[qtblno] := FAST_FLOAT_FIELD_PTR( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + DCTSIZE2 * SIZEOF(FAST_FLOAT)) ); + end; + fdtbl := fdct^.float_divisors[qtblno]; + i := 0; + for row := 0 to pred(DCTSIZE) do + begin + for col := 0 to pred(DCTSIZE) do + begin + fdtbl^[i] := {FAST_FLOAT} + (1.0 / (( {double}(qtbl^.quantval[i]) * + aanscalefactor[row] * aanscalefactor[col] * 8.0))); + Inc(i); + end; + end; + end; +{$endif} + else + ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); + end; + Inc(compptr); + end; +end; + + +{ 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} +procedure forward_DCT (cinfo : j_compress_ptr; + compptr : jpeg_component_info_ptr; + sample_data : JSAMPARRAY; + coef_blocks : JBLOCKROW; + start_row : JDIMENSION; + start_col : JDIMENSION; + num_blocks : JDIMENSION); +{ This version is used for integer DCT implementations. } +var + { This routine is heavily used, so it's worth coding it tightly. } + fdct : my_fdct_ptr; + do_dct : forward_DCT_method_ptr; + divisors : DCTELEM_FIELD_PTR; + workspace : array[0..DCTSIZE2-1] of DCTELEM; { work area for FDCT subroutine } + bi : JDIMENSION; +var + {register} workspaceptr : DCTELEMPTR; + {register} elemptr : JSAMPLE_PTR; + {register} elemr : int; +{$ifndef DCTSIZE_IS_8} +var + {register} elemc : int; +{$endif} +var + {register} temp, qval : DCTELEM; + {register} i : int; + {register} output_ptr : JCOEFPTR; +begin + fdct := my_fdct_ptr (cinfo^.fdct); + do_dct := fdct^.do_dct; + divisors := fdct^.divisors[compptr^.quant_tbl_no]; + + Inc(JSAMPROW_PTR(sample_data), start_row); { fold in the vertical offset once } + + for bi := 0 to pred(num_blocks) do + begin + + { Load data into workspace, applying unsigned->signed conversion } + + workspaceptr := @workspace[0]; + for elemr := 0 to pred(DCTSIZE) do + begin + elemptr := @sample_data^[elemr]^[start_col]; +{$ifdef DCTSIZE_IS_8} { unroll the inner loop } + workspaceptr^ := GETJSAMPLE(elemptr^) - CENTERJSAMPLE; + Inc(workspaceptr); + Inc(elemptr); + workspaceptr^ := GETJSAMPLE(elemptr^) - CENTERJSAMPLE; + Inc(workspaceptr); + Inc(elemptr); + workspaceptr^ := GETJSAMPLE(elemptr^) - CENTERJSAMPLE; + Inc(workspaceptr); + Inc(elemptr); + workspaceptr^ := GETJSAMPLE(elemptr^) - CENTERJSAMPLE; + Inc(workspaceptr); + Inc(elemptr); + workspaceptr^ := GETJSAMPLE(elemptr^) - CENTERJSAMPLE; + Inc(workspaceptr); + Inc(elemptr); + workspaceptr^ := GETJSAMPLE(elemptr^) - CENTERJSAMPLE; + Inc(workspaceptr); + Inc(elemptr); + workspaceptr^ := GETJSAMPLE(elemptr^) - CENTERJSAMPLE; + Inc(workspaceptr); + Inc(elemptr); + workspaceptr^ := GETJSAMPLE(elemptr^) - CENTERJSAMPLE; + Inc(workspaceptr); + {Inc(elemptr); - Value never used } +{$else} + for elemc := pred(DCTSIZE) downto 0 do + begin + workspaceptr^ := GETJSAMPLE(elemptr^) - CENTERJSAMPLE; + Inc(workspaceptr); + Inc(elemptr); + end; +{$endif} + end; + + { Perform the DCT } + do_dct (workspace); + + { Quantize/descale the coefficients, and store into coef_blocks[] } + + output_ptr := JCOEFPTR(@coef_blocks^[bi]); + for i := 0 to pred(DCTSIZE2) do + begin + 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. } + + if (temp < 0) then + begin + temp := -temp; + Inc(temp, qval shr 1); { for rounding } + {DIVIDE_BY(temp, qval);} + {$ifdef FAST_DIVIDE} + temp := temp div qval; + {$else} + if (temp >= qval) then + temp := temp div qval + else + temp := 0; + {$endif} + temp := -temp; + end + else + begin + Inc(temp, qval shr 1); { for rounding } + {DIVIDE_BY(temp, qval);} + {$ifdef FAST_DIVIDE} + temp := temp div qval; + {$else} + if (temp >= qval) then + temp := temp div qval + else + temp := 0; + {$endif} + end; + output_ptr^[i] := JCOEF (temp); + end; + Inc(start_col, DCTSIZE); + end; +end; + + +{$ifdef DCT_FLOAT_SUPPORTED} + +{METHODDEF} +procedure forward_DCT_float (cinfo : j_compress_ptr; + compptr : jpeg_component_info_ptr; + sample_data : JSAMPARRAY; + coef_blocks : JBLOCKROW; + start_row : JDIMENSION; + start_col : JDIMENSION; + num_blocks : JDIMENSION); +{ This version is used for floating-point DCT implementations. } +var + { This routine is heavily used, so it's worth coding it tightly. } + fdct : my_fdct_ptr; + do_dct : float_DCT_method_ptr; + divisors : FAST_FLOAT_FIELD_PTR; + workspace : array[0..DCTSIZE2-1] of FAST_FLOAT; { work area for FDCT subroutine } + bi : JDIMENSION; +var + {register} workspaceptr : FAST_FLOAT_PTR; + {register} elemptr : JSAMPLE_PTR; + {register} elemr : int; +{$ifndef DCTSIZE_IS_8} +var + {register} elemc : int; +{$endif} +var + {register} temp : FAST_FLOAT; + {register} i : int; + {register} output_ptr : JCOEFPTR; +begin + fdct := my_fdct_ptr (cinfo^.fdct); + do_dct := fdct^.do_float_dct; + divisors := fdct^.float_divisors[compptr^.quant_tbl_no]; + + Inc(JSAMPROW_PTR(sample_data), start_row); { fold in the vertical offset once } + + for bi := 0 to pred(num_blocks) do + begin + { Load data into workspace, applying unsigned->signed conversion } + + workspaceptr := @workspace[0]; + for elemr := 0 to pred(DCTSIZE) do + begin + elemptr := @(sample_data^[elemr]^[start_col]); +{$ifdef DCTSIZE_IS_8} { unroll the inner loop } + workspaceptr^ := {FAST_FLOAT}(GETJSAMPLE(elemptr^) - CENTERJSAMPLE); + Inc(workspaceptr); + Inc(elemptr); + workspaceptr^ := {FAST_FLOAT}(GETJSAMPLE(elemptr^) - CENTERJSAMPLE); + Inc(workspaceptr); + Inc(elemptr); + workspaceptr^ := {FAST_FLOAT}(GETJSAMPLE(elemptr^) - CENTERJSAMPLE); + Inc(workspaceptr); + Inc(elemptr); + workspaceptr^ := {FAST_FLOAT}(GETJSAMPLE(elemptr^) - CENTERJSAMPLE); + Inc(workspaceptr); + Inc(elemptr); + workspaceptr^ := {FAST_FLOAT}(GETJSAMPLE(elemptr^) - CENTERJSAMPLE); + Inc(workspaceptr); + Inc(elemptr); + workspaceptr^ := {FAST_FLOAT}(GETJSAMPLE(elemptr^) - CENTERJSAMPLE); + Inc(workspaceptr); + Inc(elemptr); + workspaceptr^ := {FAST_FLOAT}(GETJSAMPLE(elemptr^) - CENTERJSAMPLE); + Inc(workspaceptr); + Inc(elemptr); + workspaceptr^ := {FAST_FLOAT}(GETJSAMPLE(elemptr^) - CENTERJSAMPLE); + Inc(workspaceptr); + {Inc(elemptr); - value never used } +{$else} + for elemc := pred(DCTSIZE) downto 0 do + begin + workspaceptr^ := {FAST_FLOAT}( + (GETJSAMPLE(elemptr^) - CENTERJSAMPLE) ); + Inc(workspaceptr); + Inc(elemptr); + end; +{$endif} + end; + + + { Perform the DCT } + do_dct (workspace); + + { Quantize/descale the coefficients, and store into coef_blocks[] } + + output_ptr := JCOEFPTR(@(coef_blocks^[bi])); + + for i := 0 to pred(DCTSIZE2) do + begin + { 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(Trunc (temp + {FAST_FLOAT}(16384.5))) - 16384); + end; + Inc(start_col, DCTSIZE); + end; +end; + +{$endif} { DCT_FLOAT_SUPPORTED } + + +{ Initialize FDCT manager. } + +{GLOBAL} +procedure jinit_forward_dct (cinfo : j_compress_ptr); +var + fdct : my_fdct_ptr; + i : int; +begin + fdct := my_fdct_ptr( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + SIZEOF(my_fdct_controller)) ); + cinfo^.fdct := jpeg_forward_dct_ptr (fdct); + fdct^.pub.start_pass := start_pass_fdctmgr; + + case (cinfo^.dct_method) of +{$ifdef DCT_ISLOW_SUPPORTED} + JDCT_ISLOW: + begin + fdct^.pub.forward_DCT := forward_DCT; + fdct^.do_dct := jpeg_fdct_islow; + end; +{$endif} +{$ifdef DCT_IFAST_SUPPORTED} + JDCT_IFAST: + begin + fdct^.pub.forward_DCT := forward_DCT; + fdct^.do_dct := jpeg_fdct_ifast; + end; +{$endif} +{$ifdef DCT_FLOAT_SUPPORTED} + JDCT_FLOAT: + begin + fdct^.pub.forward_DCT := forward_DCT_float; + fdct^.do_float_dct := jpeg_fdct_float; + end; +{$endif} + else + ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); + end; + + { Mark divisor tables unallocated } + for i := 0 to pred(NUM_QUANT_TBLS) do + begin + fdct^.divisors[i] := NIL; +{$ifdef DCT_FLOAT_SUPPORTED} + fdct^.float_divisors[i] := NIL; +{$endif} + end; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjchuff.pas b/resources/libraries/deskew/Imaging/JpegLib/imjchuff.pas new file mode 100755 index 0000000..ff004a4 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjchuff.pas @@ -0,0 +1,1116 @@ +unit imjchuff; + +{ 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. } + +{ Original: jchuff.c; Copyright (C) 1991-1997, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, { longptr definition missing } + imjpeglib, + imjdeferr, + imjerror, + imjutils, + imjinclude, + imjcomapi; + +{ 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. } + + +{$ifdef BITS_IN_JSAMPLE_IS_8} +const + MAX_COEF_BITS = 10; +{$else} +const + MAX_COEF_BITS = 14; +{$endif} + +{ Derived data constructed for each Huffman table } +{ Declarations shared with jcphuff.c } +type + c_derived_tbl_ptr = ^c_derived_tbl; + c_derived_tbl = record + ehufco : array[0..256-1] of uInt; { code for each symbol } + ehufsi : array[0..256-1] of byte; { length of code for each symbol } + { If no code has been allocated for a symbol S, ehufsi[S] contains 0 } + end; +{ for JCHUFF und JCPHUFF } +type + TLongTable = array[0..256] of long; + TLongTablePtr = ^TLongTable; + +{ Compute the derived values for a Huffman table. + Note this is also used by jcphuff.c. } + +{GLOBAL} +procedure jpeg_make_c_derived_tbl (cinfo : j_compress_ptr; + isDC : boolean; + tblno : int; + var pdtbl : c_derived_tbl_ptr); + +{ Generate the optimal coding for the given counts, fill htbl. + Note this is also used by jcphuff.c. } + +{GLOBAL} +procedure jpeg_gen_optimal_table (cinfo : j_compress_ptr; + htbl : JHUFF_TBL_PTR; + var freq : TLongTable); { Nomssi } + +{ Module initialization routine for Huffman entropy encoding. } + +{GLOBAL} +procedure jinit_huff_encoder (cinfo : j_compress_ptr); + +implementation + +{ 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. } + +type + savable_state = record + put_buffer : INT32; { current bit-accumulation buffer } + put_bits : int; { # of bits now in it } + last_dc_val : array[0..MAX_COMPS_IN_SCAN-1] of int; + { last DC coef for each component } + end; + + +type + huff_entropy_ptr = ^huff_entropy_encoder; + huff_entropy_encoder = record + pub : jpeg_entropy_encoder; { public fields } + + saved : savable_state; { Bit buffer & DC state at start of MCU } + + { These fields are NOT loaded into local working state. } + restarts_to_go : uInt; { MCUs left in this restart interval } + next_restart_num : int; { next restart number to write (0-7) } + + { Pointers to derived tables (these workspaces have image lifespan) } + dc_derived_tbls : array[0..NUM_HUFF_TBLS-1] of c_derived_tbl_ptr; + ac_derived_tbls : array[0..NUM_HUFF_TBLS-1] of c_derived_tbl_ptr; + + {$ifdef ENTROPY_OPT_SUPPORTED} { Statistics tables for optimization } + dc_count_ptrs : array[0..NUM_HUFF_TBLS-1] of TLongTablePtr; + ac_count_ptrs : array[0..NUM_HUFF_TBLS-1] of TLongTablePtr; + {$endif} + end; + + + +{ Working state while writing an MCU. + This struct contains all the fields that are needed by subroutines. } + +type + working_state = record + next_output_byte : JOCTETptr; { => next byte to write in buffer } + free_in_buffer : size_t; { # of byte spaces remaining in buffer } + cur : savable_state; { Current bit buffer & DC state } + cinfo : j_compress_ptr; { dump_buffer needs access to this } + end; + + +{ Forward declarations } +{METHODDEF} +function encode_mcu_huff (cinfo : j_compress_ptr; + const MCU_data : array of JBLOCKROW) : boolean; + forward; +{METHODDEF} +procedure finish_pass_huff (cinfo : j_compress_ptr); forward; +{$ifdef ENTROPY_OPT_SUPPORTED} +{METHODDEF} +function encode_mcu_gather (cinfo : j_compress_ptr; + const MCU_data: array of JBLOCKROW) : boolean; + forward; + +{METHODDEF} +procedure finish_pass_gather (cinfo : j_compress_ptr); forward; +{$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} +procedure start_pass_huff (cinfo : j_compress_ptr; + gather_statistics : boolean); +var + entropy : huff_entropy_ptr; + ci, dctbl, actbl : int; + compptr : jpeg_component_info_ptr; +begin + entropy := huff_entropy_ptr (cinfo^.entropy); + + if (gather_statistics) then + begin +{$ifdef ENTROPY_OPT_SUPPORTED} + entropy^.pub.encode_mcu := encode_mcu_gather; + entropy^.pub.finish_pass := finish_pass_gather; +{$else} + ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); +{$endif} + end + else + begin + entropy^.pub.encode_mcu := encode_mcu_huff; + entropy^.pub.finish_pass := finish_pass_huff; + end; + + for ci := 0 to pred(cinfo^.comps_in_scan) do + begin + compptr := cinfo^.cur_comp_info[ci]; + dctbl := compptr^.dc_tbl_no; + actbl := compptr^.ac_tbl_no; + if (gather_statistics) then + begin +{$ifdef ENTROPY_OPT_SUPPORTED} + { Check for invalid table indexes } + { (make_c_derived_tbl does this in the other path) } + if (dctbl < 0) or (dctbl >= NUM_HUFF_TBLS) then + ERREXIT1(j_common_ptr(cinfo), JERR_NO_HUFF_TABLE, dctbl); + if (actbl < 0) or (actbl >= NUM_HUFF_TBLS) then + ERREXIT1(j_common_ptr(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] = NIL) then + entropy^.dc_count_ptrs[dctbl] := TLongTablePtr( + 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] = NIL) then + entropy^.ac_count_ptrs[actbl] := TLongTablePtr( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + 257 * SIZEOF(long)) ); + MEMZERO(entropy^.ac_count_ptrs[actbl], 257 * SIZEOF(long)); +{$endif} + end + else + begin + { 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]); + end; + { Initialize DC predictions to 0 } + entropy^.saved.last_dc_val[ci] := 0; + end; + + { 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; +end; + + +{ 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} +procedure jpeg_make_c_derived_tbl (cinfo : j_compress_ptr; + isDC : boolean; + tblno : int; + var pdtbl : c_derived_tbl_ptr); +var + htbl : JHUFF_TBL_PTR; + dtbl : c_derived_tbl_ptr; + p, i, l, lastp, si, maxsymbol : int; + huffsize : array[0..257-1] of byte; + huffcode : array[0..257-1] of uInt; + code : uInt; +begin + { 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) or (tblno >= NUM_HUFF_TBLS) then + ERREXIT1(j_common_ptr(cinfo), JERR_NO_HUFF_TABLE, tblno); + if isDC then + htbl := cinfo^.dc_huff_tbl_ptrs[tblno] + else + htbl := cinfo^.ac_huff_tbl_ptrs[tblno]; + if (htbl = NIL) then + ERREXIT1(j_common_ptr(cinfo), JERR_NO_HUFF_TABLE, tblno); + + { Allocate a workspace if we haven't already done so. } + if (pdtbl = NIL) then + pdtbl := c_derived_tbl_ptr( + 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 to 16 do + begin + i := int(htbl^.bits[l]); + if (i < 0) and (p + i > 256) then { protect against table overrun } + ERREXIT(j_common_ptr(cinfo), JERR_BAD_HUFF_TABLE); + while (i > 0) do + begin + huffsize[p] := byte(l); + Inc(p); + Dec(i); + end; + end; + 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] <> 0) do + begin + while (( int(huffsize[p]) ) = si) do + begin + huffcode[p] := code; + Inc(p); + Inc(code); + end; + { 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) shl si)) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_HUFF_TABLE); + code := code shl 1; + Inc(si); + end; + + { 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.) } + + if isDC then + maxsymbol := 15 + else + maxsymbol := 255; + + for p := 0 to pred(lastp) do + begin + i := htbl^.huffval[p]; + if (i < 0) or (i > maxsymbol) or (dtbl^.ehufsi[i] <> 0) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_HUFF_TABLE); + dtbl^.ehufco[i] := huffcode[p]; + dtbl^.ehufsi[i] := huffsize[p]; + end; +end; + + +{ Outputting bytes to the file } + + +{LOCAL} +function dump_buffer (var state : working_state) : boolean; +{ Empty the output buffer; return TRUE if successful, FALSE if must suspend } +var + dest : jpeg_destination_mgr_ptr; +begin + dest := state.cinfo^.dest; + + if (not dest^.empty_output_buffer (state.cinfo)) then + begin + dump_buffer := FALSE; + exit; + end; + { 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; + dump_buffer := TRUE; +end; + + +{ 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. } + + +{LOCAL} +function emit_bits (var state : working_state; + code : uInt; + size : int) : boolean; {INLINE} +{ Emit some bits; return TRUE if successful, FALSE if must suspend } +var + { This routine is heavily used, so it's worth coding tightly. } + {register} put_buffer : INT32; + {register} put_bits : int; +var + c : int; +begin + put_buffer := INT32 (code); + put_bits := state.cur.put_bits; + + { if size is 0, caller used an invalid Huffman table entry } + if (size = 0) then + ERREXIT(j_common_ptr(state.cinfo), JERR_HUFF_MISSING_CODE); + + put_buffer := put_buffer and pred(INT32(1) shl size); + { mask off any extra bits in code } + + Inc(put_bits, size); { new number of bits in buffer } + + put_buffer := put_buffer shl (24 - put_bits); + { align incoming bits } + put_buffer := put_buffer or state.cur.put_buffer; + { and merge with old buffer contents } + while (put_bits >= 8) do + begin + c := int ((put_buffer shr 16) and $FF); + + {emit_byte(state, c, return FALSE);} + { Emit a byte, return FALSE if must suspend. } + state.next_output_byte^ := JOCTET (c); + Inc(state.next_output_byte); + Dec(state.free_in_buffer); + if (state.free_in_buffer = 0) then + if not dump_buffer(state) then + begin + emit_bits := FALSE; + exit; + end; + + if (c = $FF) then { need to stuff a zero byte? } + begin + {emit_byte(state, 0, return FALSE);} + state.next_output_byte^ := JOCTET (0); + Inc(state.next_output_byte); + Dec(state.free_in_buffer); + if (state.free_in_buffer = 0) then + if not dump_buffer(state) then + begin + emit_bits := FALSE; + exit; + end; + + end; + put_buffer := put_buffer shl 8; + Dec(put_bits, 8); + end; + + state.cur.put_buffer := put_buffer; { update state variables } + state.cur.put_bits := put_bits; + + emit_bits := TRUE; +end; + + +{LOCAL} +function flush_bits (var state : working_state) : boolean; +begin + if (not emit_bits(state, $7F, 7)) then { fill any partial byte with ones } + begin + flush_bits := FALSE; + exit; + end; + state.cur.put_buffer := 0; { and reset bit-buffer to empty } + state.cur.put_bits := 0; + flush_bits := TRUE; +end; + + +{ Encode a single block's worth of coefficients } + +{LOCAL} +function encode_one_block (var state : working_state; + const block : JBLOCK; + last_dc_val : int; + dctbl : c_derived_tbl_ptr; + actbl : c_derived_tbl_ptr) : boolean; +var + {register} temp, temp2 : int; + {register} nbits : int; + {register} k, r, i : int; +begin + { Encode the DC coefficient difference per section F.1.2.1 } + + temp2 := block[0] - last_dc_val; + temp := temp2; + + if (temp < 0) then + begin + 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 } + Dec(temp2); + end; + + { Find the number of bits needed for the magnitude of the coefficient } + nbits := 0; + while (temp <> 0) do + begin + Inc(nbits); + temp := temp shr 1; + end; + + { 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) then + ERREXIT(j_common_ptr(state.cinfo), JERR_BAD_DCT_COEF); + + { Emit the Huffman-coded symbol for the number of bits } + if not emit_bits(state, dctbl^.ehufco[nbits], dctbl^.ehufsi[nbits]) then + begin + encode_one_block := FALSE; + exit; + end; + + { Emit that number of bits of the value, if positive, } + { or the complement of its magnitude, if negative. } + if (nbits <> 0) then { emit_bits rejects calls with size 0 } + if not emit_bits(state, uInt(temp2), nbits) then + begin + encode_one_block := FALSE; + exit; + end; + + { Encode the AC coefficients per section F.1.2.2 } + + r := 0; { r := run length of zeros } + + for k := 1 to pred(DCTSIZE2) do + begin + temp := block[jpeg_natural_order[k]]; + if (temp = 0) then + begin + Inc(r); + end + else + begin + { if run length > 15, must emit special run-length-16 codes ($F0) } + while (r > 15) do + begin + if not emit_bits(state, actbl^.ehufco[$F0], actbl^.ehufsi[$F0]) then + begin + encode_one_block := FALSE; + exit; + end; + Dec(r, 16); + end; + + temp2 := temp; + if (temp < 0) then + begin + temp := -temp; { temp is abs value of input } + { This code assumes we are on a two's complement machine } + Dec(temp2); + end; + + { Find the number of bits needed for the magnitude of the coefficient } + nbits := 0; { there must be at least one 1 bit } + repeat + Inc(nbits); + temp := temp shr 1; + until (temp = 0); + + { Check for out-of-range coefficient values } + if (nbits > MAX_COEF_BITS) then + ERREXIT(j_common_ptr(state.cinfo), JERR_BAD_DCT_COEF); + + { Emit Huffman symbol for run length / number of bits } + i := (r shl 4) + nbits; + if not emit_bits(state, actbl^.ehufco[i], actbl^.ehufsi[i]) then + begin + encode_one_block := FALSE; + exit; + end; + + { Emit that number of bits of the value, if positive, } + { or the complement of its magnitude, if negative. } + if not emit_bits(state, uInt(temp2), nbits) then + begin + encode_one_block := FALSE; + exit; + end; + + r := 0; + end; + end; + + { If the last coef(s) were zero, emit an end-of-block code } + if (r > 0) then + if not emit_bits(state, actbl^.ehufco[0], actbl^.ehufsi[0]) then + begin + encode_one_block := FALSE; + exit; + end; + + encode_one_block := TRUE; +end; + + +{ Emit a restart marker & resynchronize predictions. } + +{LOCAL} +function emit_restart (var state : working_state; + restart_num : int) : boolean; +var + ci : int; +begin + if (not flush_bits(state)) then + begin + emit_restart := FALSE; + exit; + end; + + {emit_byte(state, $FF, return FALSE);} + { Emit a byte, return FALSE if must suspend. } + state.next_output_byte^ := JOCTET ($FF); + Inc(state.next_output_byte); + Dec(state.free_in_buffer); + if (state.free_in_buffer = 0) then + if not dump_buffer(state) then + begin + emit_restart := FALSE; + exit; + end; + + {emit_byte(state, JPEG_RST0 + restart_num, return FALSE);} + { Emit a byte, return FALSE if must suspend. } + state.next_output_byte^ := JOCTET (JPEG_RST0 + restart_num); + Inc(state.next_output_byte); + Dec(state.free_in_buffer); + if (state.free_in_buffer = 0) then + if not dump_buffer(state) then + begin + emit_restart := FALSE; + exit; + end; + + { Re-initialize DC predictions to 0 } + for ci := 0 to pred(state.cinfo^.comps_in_scan) do + state.cur.last_dc_val[ci] := 0; + + { The restart counter is not updated until we successfully write the MCU. } + + emit_restart := TRUE; +end; + + +{ Encode and output one MCU's worth of Huffman-compressed coefficients. } + +{METHODDEF} +function encode_mcu_huff (cinfo : j_compress_ptr; + const MCU_data: array of JBLOCKROW) : boolean; +var + entropy : huff_entropy_ptr; + state : working_state; + blkn, ci : int; + compptr : jpeg_component_info_ptr; +begin + entropy := huff_entropy_ptr (cinfo^.entropy); + { 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.cur := entropy^.saved; + state.cinfo := cinfo; + + { Emit restart marker if needed } + if (cinfo^.restart_interval <> 0) then + begin + if (entropy^.restarts_to_go = 0) then + if not emit_restart(state, entropy^.next_restart_num) then + begin + encode_mcu_huff := FALSE; + exit; + end; + end; + + { Encode the MCU data blocks } + for blkn := 0 to pred(cinfo^.blocks_in_MCU) do + begin + ci := cinfo^.MCU_membership[blkn]; + compptr := cinfo^.cur_comp_info[ci]; + if not 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]) then + begin + encode_mcu_huff := FALSE; + exit; + end; + { Update last_dc_val } + state.cur.last_dc_val[ci] := MCU_data[blkn]^[0][0]; + end; + + { 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);} + entropy^.saved := state.cur; + + { Update restart-interval state too } + if (cinfo^.restart_interval <> 0) then + begin + if (entropy^.restarts_to_go = 0) then + begin + entropy^.restarts_to_go := cinfo^.restart_interval; + Inc(entropy^.next_restart_num); + with entropy^ do + next_restart_num := next_restart_num and 7; + end; + Dec(entropy^.restarts_to_go); + end; + + encode_mcu_huff := TRUE; +end; + + +{ Finish up at the end of a Huffman-compressed scan. } + +{METHODDEF} +procedure finish_pass_huff (cinfo : j_compress_ptr); +var + entropy : huff_entropy_ptr; + state : working_state; +begin + entropy := huff_entropy_ptr (cinfo^.entropy); + + { 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.cur := entropy^.saved; + state.cinfo := cinfo; + + { Flush out the last data } + if not flush_bits(state) then + ERREXIT(j_common_ptr(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);} + entropy^.saved := state.cur; +end; + + +{ 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} +procedure htest_one_block (cinfo : j_compress_ptr; + const block : JBLOCK; + last_dc_val : int; + dc_counts : TLongTablePtr; + ac_counts : TLongTablePtr); + +var + {register} temp : int; + {register} nbits : int; + {register} k, r : int; +begin + { Encode the DC coefficient difference per section F.1.2.1 } + temp := block[0] - last_dc_val; + if (temp < 0) then + temp := -temp; + + { Find the number of bits needed for the magnitude of the coefficient } + nbits := 0; + while (temp <> 0) do + begin + Inc(nbits); + temp := temp shr 1; + end; + + { 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) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_DCT_COEF); + + { Count the Huffman symbol for the number of bits } + Inc(dc_counts^[nbits]); + + { Encode the AC coefficients per section F.1.2.2 } + + r := 0; { r := run length of zeros } + + for k := 1 to pred(DCTSIZE2) do + begin + temp := block[jpeg_natural_order[k]]; + if (temp = 0) then + begin + Inc(r); + end + else + begin + { if run length > 15, must emit special run-length-16 codes ($F0) } + while (r > 15) do + begin + Inc(ac_counts^[$F0]); + Dec(r, 16); + end; + + { Find the number of bits needed for the magnitude of the coefficient } + if (temp < 0) then + temp := -temp; + + { Find the number of bits needed for the magnitude of the coefficient } + nbits := 0; { there must be at least one 1 bit } + repeat + Inc(nbits); + temp := temp shr 1; + until (temp = 0); + + + { Count Huffman symbol for run length / number of bits } + Inc(ac_counts^[(r shl 4) + nbits]); + + r := 0; + end; + end; + + { If the last coef(s) were zero, emit an end-of-block code } + if (r > 0) then + Inc(ac_counts^[0]); +end; + + +{ Trial-encode one MCU's worth of Huffman-compressed coefficients. + No data is actually output, so no suspension return is possible. } + +{METHODDEF} +function encode_mcu_gather (cinfo : j_compress_ptr; + const MCU_data: array of JBLOCKROW) : boolean; +var + entropy : huff_entropy_ptr; + blkn, ci : int; + compptr : jpeg_component_info_ptr; +begin + entropy := huff_entropy_ptr (cinfo^.entropy); + { Take care of restart intervals if needed } + if (cinfo^.restart_interval <> 0) then + begin + if (entropy^.restarts_to_go = 0) then + begin + { Re-initialize DC predictions to 0 } + for ci := 0 to pred(cinfo^.comps_in_scan) do + entropy^.saved.last_dc_val[ci] := 0; + { Update restart state } + entropy^.restarts_to_go := cinfo^.restart_interval; + end; + Dec(entropy^.restarts_to_go); + end; + + for blkn := 0 to pred(cinfo^.blocks_in_MCU) do + begin + 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]; + end; + + encode_mcu_gather := TRUE; +end; + + +{ 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} +procedure jpeg_gen_optimal_table (cinfo : j_compress_ptr; + htbl : JHUFF_TBL_PTR; + var freq : TLongTable); +const + MAX_CLEN = 32; { assumed maximum initial code length } +var + bits : array[0..MAX_CLEN+1-1] of UINT8; { bits[k] := # of symbols with code length k } + codesize : array[0..257-1] of int; { codesize[k] := code length of symbol k } + others : array[0..257-1] of int; { next symbol in current branch of tree } + c1, c2 : int; + p, i, j : int; + v : long; +begin + { This algorithm is explained in section K.2 of the JPEG standard } + + MEMZERO(@bits, SIZEOF(bits)); + MEMZERO(@codesize, SIZEOF(codesize)); + for i := 0 to 256 do + 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 } + + while TRUE do + begin + { Find the smallest nonzero frequency, set c1 := its symbol } + { In case of ties, take the larger symbol number } + c1 := -1; + v := long(1000000000); + for i := 0 to 256 do + begin + if (freq[i] <> 0) and (freq[i] <= v) then + begin + v := freq[i]; + c1 := i; + end; + end; + + { Find the next smallest nonzero frequency, set c2 := its symbol } + { In case of ties, take the larger symbol number } + c2 := -1; + v := long(1000000000); + for i := 0 to 256 do + begin + if (freq[i] <> 0) and (freq[i] <= v) and (i <> c1) then + begin + v := freq[i]; + c2 := i; + end; + end; + + { Done if we've merged everything into one frequency } + if (c2 < 0) then + break; + + { Else merge the two counts/trees } + Inc(freq[c1], freq[c2]); + freq[c2] := 0; + + { Increment the codesize of everything in c1's tree branch } + Inc(codesize[c1]); + while (others[c1] >= 0) do + begin + c1 := others[c1]; + Inc(codesize[c1]); + end; + + others[c1] := c2; { chain c2 onto c1's tree branch } + + { Increment the codesize of everything in c2's tree branch } + Inc(codesize[c2]); + while (others[c2] >= 0) do + begin + c2 := others[c2]; + Inc(codesize[c2]); + end; + end; + + { Now count the number of symbols of each code length } + for i := 0 to 256 do + begin + if (codesize[i]<>0) then + begin + { The JPEG standard seems to think that this can't happen, } + { but I'm paranoid... } + if (codesize[i] > MAX_CLEN) then + ERREXIT(j_common_ptr(cinfo), JERR_HUFF_CLEN_OVERFLOW); + + Inc(bits[codesize[i]]); + end; + end; + + { 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 downto 17 do + begin + while (bits[i] > 0) do + begin + j := i - 2; { find length of new prefix to be used } + while (bits[j] = 0) do + Dec(j); + + Dec(bits[i], 2); { remove two symbols } + Inc(bits[i-1]); { one goes in this length } + Inc(bits[j+1], 2); { two new symbols in this length } + Dec(bits[j]); { symbol of this length is now a prefix } + end; + end; + + { Delphi 2: FOR-loop variable 'i' may be undefined after loop } + i := 16; { Nomssi: work around } + + { Remove the count for the pseudo-symbol 256 from the largest codelength } + while (bits[i] = 0) do { find largest codelength still in use } + Dec(i); + Dec(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 to MAX_CLEN do + begin + for j := 0 to 255 do + begin + if (codesize[j] = i) then + begin + htbl^.huffval[p] := UINT8 (j); + Inc(p); + end; + end; + end; + + { Set sent_table FALSE so updated table will be written to JPEG file. } + htbl^.sent_table := FALSE; +end; + + +{ Finish up a statistics-gathering pass and create the new Huffman tables. } + +{METHODDEF} +procedure finish_pass_gather (cinfo : j_compress_ptr); +var + entropy : huff_entropy_ptr; + ci, dctbl, actbl : int; + compptr : jpeg_component_info_ptr; + htblptr : ^JHUFF_TBL_PTR; + did_dc : array[0..NUM_HUFF_TBLS-1] of boolean; + did_ac : array[0..NUM_HUFF_TBLS-1] of boolean; +begin + entropy := huff_entropy_ptr (cinfo^.entropy); + + { 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 to pred(cinfo^.comps_in_scan) do + begin + compptr := cinfo^.cur_comp_info[ci]; + dctbl := compptr^.dc_tbl_no; + actbl := compptr^.ac_tbl_no; + if (not did_dc[dctbl]) then + begin + htblptr := @(cinfo^.dc_huff_tbl_ptrs[dctbl]); + if ( htblptr^ = NIL) then + htblptr^ := jpeg_alloc_huff_table(j_common_ptr(cinfo)); + jpeg_gen_optimal_table(cinfo, htblptr^, entropy^.dc_count_ptrs[dctbl]^); + did_dc[dctbl] := TRUE; + end; + if (not did_ac[actbl]) then + begin + htblptr := @(cinfo^.ac_huff_tbl_ptrs[actbl]); + if ( htblptr^ = NIL) then + htblptr^ := jpeg_alloc_huff_table(j_common_ptr(cinfo)); + jpeg_gen_optimal_table(cinfo, htblptr^, entropy^.ac_count_ptrs[actbl]^); + did_ac[actbl] := TRUE; + end; + end; +end; + +{$endif} { ENTROPY_OPT_SUPPORTED } + + +{ Module initialization routine for Huffman entropy encoding. } + +{GLOBAL} +procedure jinit_huff_encoder (cinfo : j_compress_ptr); +var + entropy : huff_entropy_ptr; + i : int; +begin + entropy := huff_entropy_ptr( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + SIZEOF(huff_entropy_encoder)) ); + cinfo^.entropy := jpeg_entropy_encoder_ptr (entropy); + entropy^.pub.start_pass := start_pass_huff; + + { Mark tables unallocated } + for i := 0 to pred(NUM_HUFF_TBLS) do + begin + entropy^.ac_derived_tbls[i] := NIL; + entropy^.dc_derived_tbls[i] := NIL; +{$ifdef ENTROPY_OPT_SUPPORTED} + entropy^.ac_count_ptrs[i] := NIL; + entropy^.dc_count_ptrs[i] := NIL; +{$endif} + end; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjcinit.pas b/resources/libraries/deskew/Imaging/JpegLib/imjcinit.pas new file mode 100755 index 0000000..2a844e6 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjcinit.pas @@ -0,0 +1,95 @@ +unit imjcinit; + +{ Original: jcinit.c ; Copyright (C) 1991-1997, Thomas G. Lane. } + +{ 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. } + +interface + +{$I imjconfig.inc} + +uses + imjinclude, + imjdeferr, + imjerror, + imjpeglib, +{$ifdef C_PROGRESSIVE_SUPPORTED} + imjcphuff, +{$endif} + imjchuff, imjcmaster, imjccolor, imjcsample, imjcprepct, + imjcdctmgr, imjccoefct, imjcmainct, imjcmarker; + +{ 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} +procedure jinit_compress_master (cinfo : j_compress_ptr); + +implementation + + + +{ 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} +procedure jinit_compress_master (cinfo : j_compress_ptr); +begin + { Initialize master control (includes parameter checking/processing) } + jinit_c_master_control(cinfo, FALSE { full compression }); + + { Preprocessing } + if (not cinfo^.raw_data_in) then + begin + jinit_color_converter(cinfo); + jinit_downsampler(cinfo); + jinit_c_prep_controller(cinfo, FALSE { never need full buffer here }); + end; + { Forward DCT } + jinit_forward_dct(cinfo); + { Entropy encoding: either Huffman or arithmetic coding. } + if (cinfo^.arith_code) then + begin + ERREXIT(j_common_ptr(cinfo), JERR_ARITH_NOTIMPL); + end + else + begin + if (cinfo^.progressive_mode) then + begin +{$ifdef C_PROGRESSIVE_SUPPORTED} + jinit_phuff_encoder(cinfo); +{$else} + ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); +{$endif} + end + else + jinit_huff_encoder(cinfo); + end; + + { Need a full-image coefficient buffer in any multi-pass mode. } + jinit_c_coef_controller(cinfo, + (cinfo^.num_scans > 1) or (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); +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjcmainct.pas b/resources/libraries/deskew/Imaging/JpegLib/imjcmainct.pas new file mode 100755 index 0000000..196fad4 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjcmainct.pas @@ -0,0 +1,343 @@ +unit imjcmainct; + +{ 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. } + +{ Original : jcmainct.c ; Copyright (C) 1994-1996, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +{ 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} + +uses + imjmorecfg, + imjinclude, + imjdeferr, + imjerror, +{$ifdef FULL_MAIN_BUFFER_SUPPORTED} + imjutils, +{$endif} + imjpeglib; + +{ Initialize main buffer controller. } + +{GLOBAL} +procedure jinit_c_main_controller (cinfo : j_compress_ptr; + need_full_buffer : boolean); + +implementation + + +{ Private buffer controller object } + +type + my_main_ptr = ^my_main_controller; + my_main_controller = record + pub : jpeg_c_main_controller; { public fields } + + cur_iMCU_row : JDIMENSION; { number of current iMCU row } + rowgroup_ctr : JDIMENSION; { counts row groups received in iMCU row } + suspended : boolean; { remember if we suspended output } + pass_mode : J_BUF_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. } + + buffer : array[0..MAX_COMPONENTS-1] of JSAMPARRAY; + + {$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. } + + whole_image : array[0..MAX_COMPONENTS-1] of jvirt_sarray_ptr; + {$endif} + end; {my_main_controller} + + +{ Forward declarations } +{METHODDEF} +procedure process_data_simple_main(cinfo : j_compress_ptr; + input_buf : JSAMPARRAY; + var in_row_ctr: JDIMENSION; + in_rows_avail : JDIMENSION); forward; + +{$ifdef FULL_MAIN_BUFFER_SUPPORTED} +{METHODDEF} +procedure process_data_buffer_main(cinfo : j_compress_ptr; + input_buf : JSAMPARRAY; + var in_row_ctr : JDIMENSION; + in_rows_avail : JDIMENSION); forward; +{$endif} + + +{ Initialize for a processing pass. } + +{METHODDEF} +procedure start_pass_main (cinfo : j_compress_ptr; + pass_mode : J_BUF_MODE); +var + main : my_main_ptr; +begin + main := my_main_ptr (cinfo^.main); + + { Do nothing in raw-data mode. } + if (cinfo^.raw_data_in) then + exit; + + 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 } + + case (pass_mode) of + JBUF_PASS_THRU: + begin +{$ifdef FULL_MAIN_BUFFER_SUPPORTED} + if (main^.whole_image[0] <> NIL) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); +{$endif} + main^.pub.process_data := process_data_simple_main; + end; +{$ifdef FULL_MAIN_BUFFER_SUPPORTED} + JBUF_SAVE_SOURCE, + JBUF_CRANK_DEST, + JBUF_SAVE_AND_PASS: + begin + if (main^.whole_image[0] = NIL) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); + main^.pub.process_data := process_data_buffer_main; + end; +{$endif} + else + ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); + end; +end; + + +{ Process some data. + This routine handles the simple pass-through mode, + where we have only a strip buffer. } + +{METHODDEF} +procedure process_data_simple_main (cinfo : j_compress_ptr; + input_buf : JSAMPARRAY; + var in_row_ctr : JDIMENSION; + in_rows_avail : JDIMENSION); +var + main : my_main_ptr; +begin + main := my_main_ptr (cinfo^.main); + + while (main^.cur_iMCU_row < cinfo^.total_iMCU_rows) do + begin + { Read input data if we haven't filled the main buffer yet } + if (main^.rowgroup_ctr < DCTSIZE) then + cinfo^.prep^.pre_process_data (cinfo, + input_buf, + in_row_ctr, + in_rows_avail, + JSAMPIMAGE(@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) then + exit; + + { Send the completed row to the compressor } + if (not cinfo^.coef^.compress_data (cinfo, JSAMPIMAGE(@main^.buffer))) then + begin + { 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 (not main^.suspended) then + begin + Dec(in_row_ctr); + main^.suspended := TRUE; + end; + exit; + end; + { We did finish the row. Undo our little suspension hack if a previous + call suspended; then mark the main buffer empty. } + + if (main^.suspended) then + begin + Inc(in_row_ctr); + main^.suspended := FALSE; + end; + main^.rowgroup_ctr := 0; + Inc(main^.cur_iMCU_row); + end; +end; + + +{$ifdef FULL_MAIN_BUFFER_SUPPORTED} + +{ Process some data. + This routine handles all of the modes that use a full-size buffer. } + +{METHODDEF} +procedure process_data_buffer_main (cinfo : j_compress_ptr; + input_buf : JSAMPARRAY; + var in_row_ctr : JDIMENSION; + in_rows_avail : JDIMENSION); +var + main : my_main_ptr; + ci : int; + compptr : jpeg_component_info_ptr; + writing : boolean; +begin + main := my_main_ptr (cinfo^.main); + writing := (main^.pass_mode <> JBUF_CRANK_DEST); + + while (main^.cur_iMCU_row < cinfo^.total_iMCU_rows) do + begin + { Realign the virtual buffers if at the start of an iMCU row. } + if (main^.rowgroup_ctr = 0) then + begin + compptr := cinfo^.comp_info; + for ci := 0 to pred(cinfo^.num_components) do + begin + 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); + Inc(compptr); + end; + { In a read pass, pretend we just read some source data. } + if (not writing) then + begin + Inc(in_row_ctr, cinfo^.max_v_samp_factor * DCTSIZE); + main^.rowgroup_ctr := DCTSIZE; + end; + end; + + { 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) then + begin + cinfo^.prep^.pre_process_data (cinfo, + input_buf, in_row_ctr, in_rows_avail, + JSAMPIMAGE(@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) then + exit; + end; + + { Emit data, unless this is a sink-only pass. } + if (main^.pass_mode <> JBUF_SAVE_SOURCE) then + begin + if (not cinfo^.coef^.compress_data (cinfo, + JSAMPIMAGE(@main^.buffer))) then + begin + { 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 (not main^.suspended) then + begin + Dec(in_row_ctr); + main^.suspended := TRUE; + end; + exit; + end; + { We did finish the row. Undo our little suspension hack if a previous + call suspended; then mark the main buffer empty. } + + if (main^.suspended) then + begin + Inc(in_row_ctr); + main^.suspended := FALSE; + end; + end; + + { If get here, we are done with this iMCU row. Mark buffer empty. } + main^.rowgroup_ctr := 0; + Inc(main^.cur_iMCU_row); + end; +end; + +{$endif} { FULL_MAIN_BUFFER_SUPPORTED } + + +{ Initialize main buffer controller. } + +{GLOBAL} +procedure jinit_c_main_controller (cinfo : j_compress_ptr; + need_full_buffer : boolean); +var + main : my_main_ptr; + ci : int; + compptr : jpeg_component_info_ptr; +begin + main := my_main_ptr( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + SIZEOF(my_main_controller)) ); + cinfo^.main := jpeg_c_main_controller_ptr(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) then + exit; + + { Create the buffer. It holds downsampled data, so each component + may be of a different size. } + + if (need_full_buffer) then + begin +{$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 } + compptr := cinfo^.comp_info; + for ci := 0 to pred(cinfo^.num_components) do + begin + 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)); + Inc(compptr); + end; +{$else} + ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); +{$endif} + end + else + begin +{$ifdef FULL_MAIN_BUFFER_SUPPORTED} + main^.whole_image[0] := NIL; { flag for no virtual arrays } +{$endif} + { Allocate a strip buffer for each component } + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + main^.buffer[ci] := cinfo^.mem^.alloc_sarray + (j_common_ptr(cinfo), JPOOL_IMAGE, + compptr^.width_in_blocks * DCTSIZE, + JDIMENSION (compptr^.v_samp_factor * DCTSIZE)); + Inc(compptr); + end; + end; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjcmarker.pas b/resources/libraries/deskew/Imaging/JpegLib/imjcmarker.pas new file mode 100755 index 0000000..d415607 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjcmarker.pas @@ -0,0 +1,724 @@ +unit imjcmarker; + +{ This file contains routines to write JPEG datastream markers. } + +{ Original: jcmarker.c; Copyright (C) 1991-1998, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjinclude, imjmorecfg, imjerror, + imjdeferr, imjpeglib, imjutils; + + +const + { JPEG marker codes } + M_SOF0 = $c0; + M_SOF1 = $c1; + M_SOF2 = $c2; + M_SOF3 = $c3; + + M_SOF5 = $c5; + M_SOF6 = $c6; + M_SOF7 = $c7; + + M_JPG = $c8; + M_SOF9 = $c9; + M_SOF10 = $ca; + M_SOF11 = $cb; + + M_SOF13 = $cd; + M_SOF14 = $ce; + M_SOF15 = $cf; + + M_DHT = $c4; + + M_DAC = $cc; + + M_RST0 = $d0; + M_RST1 = $d1; + M_RST2 = $d2; + M_RST3 = $d3; + M_RST4 = $d4; + M_RST5 = $d5; + M_RST6 = $d6; + M_RST7 = $d7; + + M_SOI = $d8; + M_EOI = $d9; + M_SOS = $da; + M_DQT = $db; + M_DNL = $dc; + M_DRI = $dd; + M_DHP = $de; + M_EXP = $df; + + M_APP0 = $e0; + M_APP1 = $e1; + M_APP2 = $e2; + M_APP3 = $e3; + M_APP4 = $e4; + M_APP5 = $e5; + M_APP6 = $e6; + M_APP7 = $e7; + M_APP8 = $e8; + M_APP9 = $e9; + M_APP10 = $ea; + M_APP11 = $eb; + M_APP12 = $ec; + M_APP13 = $ed; + M_APP14 = $ee; + M_APP15 = $ef; + + M_JPG0 = $f0; + M_JPG13 = $fd; + M_COM = $fe; + + M_TEM = $01; + + M_ERROR = $100; + +type + JPEG_MARKER = Word; + +{ Private state } + +type + my_marker_ptr = ^my_marker_writer; + my_marker_writer = record + pub : jpeg_marker_writer; { public fields } + + last_restart_interval : uint; { last DRI value emitted; 0 after SOI } + end; + + + + +{GLOBAL} +procedure jinit_marker_writer (cinfo : j_compress_ptr); + +implementation + +{ 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} +procedure emit_byte (cinfo : j_compress_ptr; val : int); +{ Emit a byte } +var + dest : jpeg_destination_mgr_ptr; +begin + dest := cinfo^.dest; + + dest^.next_output_byte^ := JOCTET(val); + Inc(dest^.next_output_byte); + + Dec(dest^.free_in_buffer); + if (dest^.free_in_buffer = 0) then + begin + if not dest^.empty_output_buffer(cinfo) then + ERREXIT(j_common_ptr(cinfo), JERR_CANT_SUSPEND); + end; +end; + + +{LOCAL} +procedure emit_marker(cinfo : j_compress_ptr; mark : JPEG_MARKER); +{ Emit a marker code } +begin + emit_byte(cinfo, $FF); + emit_byte(cinfo, int(mark)); +end; + + +{LOCAL} +procedure emit_2bytes (cinfo : j_compress_ptr; value : int); +{ Emit a 2-byte integer; these are always MSB first in JPEG files } +begin + emit_byte(cinfo, (value shr 8) and $FF); + emit_byte(cinfo, value and $FF); +end; + + +{ Routines to write specific marker types. } + +{LOCAL} +function emit_dqt (cinfo : j_compress_ptr; index : int) : int; +{ Emit a DQT marker } +{ Returns the precision used (0 = 8bits, 1 = 16bits) for baseline checking } +var + qtbl : JQUANT_TBL_PTR; + prec : int; + i : int; +var + qval : uint; +begin + qtbl := cinfo^.quant_tbl_ptrs[index]; + if (qtbl = NIL) then + ERREXIT1(j_common_ptr(cinfo), JERR_NO_QUANT_TABLE, index); + + prec := 0; + for i := 0 to Pred(DCTSIZE2) do + begin + if (qtbl^.quantval[i] > 255) then + prec := 1; + end; + + if not qtbl^.sent_table then + begin + emit_marker(cinfo, M_DQT); + + if (prec <> 0) then + emit_2bytes(cinfo, DCTSIZE2*2 + 1 + 2) + else + emit_2bytes(cinfo, DCTSIZE2 + 1 + 2); + + emit_byte(cinfo, index + (prec shl 4)); + + for i := 0 to Pred(DCTSIZE2) do + begin + { The table entries must be emitted in zigzag order. } + qval := qtbl^.quantval[jpeg_natural_order[i]]; + if (prec <> 0) then + emit_byte(cinfo, int(qval shr 8)); + emit_byte(cinfo, int(qval and $FF)); + end; + + qtbl^.sent_table := TRUE; + end; + + emit_dqt := prec; +end; + + +{LOCAL} +procedure emit_dht (cinfo : j_compress_ptr; index : int; is_ac : boolean); +{ Emit a DHT marker } +var + htbl : JHUFF_TBL_PTR; + length, i : int; +begin + if (is_ac) then + begin + htbl := cinfo^.ac_huff_tbl_ptrs[index]; + index := index + $10; { output index has AC bit set } + end + else + begin + htbl := cinfo^.dc_huff_tbl_ptrs[index]; + end; + + if (htbl = NIL) then + ERREXIT1(j_common_ptr(cinfo), JERR_NO_HUFF_TABLE, index); + + if not htbl^.sent_table then + begin + emit_marker(cinfo, M_DHT); + + length := 0; + for i := 1 to 16 do + length := length + htbl^.bits[i]; + + emit_2bytes(cinfo, length + 2 + 1 + 16); + emit_byte(cinfo, index); + + for i := 1 to 16 do + emit_byte(cinfo, htbl^.bits[i]); + + for i := 0 to Pred(length) do + emit_byte(cinfo, htbl^.huffval[i]); + + htbl^.sent_table := TRUE; + end; +end; + + +{LOCAL} +procedure emit_dac (cinfo : j_compress_ptr); +{ 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} +var + dc_in_use : array[0..NUM_ARITH_TBLS] of byte; + ac_in_use : array[0..NUM_ARITH_TBLS] of byte; + length, i : int; + compptr : jpeg_component_info_ptr; +begin + for i := 0 to pred(NUM_ARITH_TBLS) do + begin + dc_in_use[i] := 0; + ac_in_use[i] := 0; + end; + + for i := 0 to pred(cinfo^.comps_in_scan) do + begin + compptr := cinfo^.cur_comp_info[i]; + dc_in_use[compptr^.dc_tbl_no] := 1; + ac_in_use[compptr^.ac_tbl_no] := 1; + end; + + length := 0; + for i := 0 to pred(NUM_ARITH_TBLS) do + Inc(length, dc_in_use[i] + ac_in_use[i]); + + emit_marker(cinfo, M_DAC); + + emit_2bytes(cinfo, length*2 + 2); + + for i := 0 to pred(NUM_ARITH_TBLS) do + begin + if (dc_in_use[i] <> 0) then + begin + emit_byte(cinfo, i); + emit_byte(cinfo, cinfo^.arith_dc_L[i] + (cinfo^.arith_dc_U[i] shl 4)); + end; + if (ac_in_use[i] <> 0) then + begin + emit_byte(cinfo, i + $10); + emit_byte(cinfo, cinfo^.arith_ac_K[i]); + end; + end; +end; +{$else} +begin +end; +{$endif} {C_ARITH_CODING_SUPPORTED} + + +{LOCAL} +procedure emit_dri (cinfo : j_compress_ptr); +{ Emit a DRI marker } +begin + emit_marker(cinfo, M_DRI); + + emit_2bytes(cinfo, 4); { fixed length } + + emit_2bytes(cinfo, int(cinfo^.restart_interval)); +end; + + +{LOCAL} +procedure emit_sof (cinfo : j_compress_ptr; code : JPEG_MARKER); +{ Emit a SOF marker } +var + ci : int; + compptr : jpeg_component_info_ptr; +begin + 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) > long(65535)) or + (long(cinfo^.image_width) > long(65535)) then + ERREXIT1(j_common_ptr(cinfo), JERR_IMAGE_TOO_BIG, uInt(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); + + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to Pred(cinfo^.num_components) do + begin + emit_byte(cinfo, compptr^.component_id); + emit_byte(cinfo, (compptr^.h_samp_factor shl 4) + compptr^.v_samp_factor); + emit_byte(cinfo, compptr^.quant_tbl_no); + Inc(compptr); + end; +end; + + +{LOCAL} +procedure emit_sos (cinfo : j_compress_ptr); +{ Emit a SOS marker } +var + i, td, ta : int; + compptr : jpeg_component_info_ptr; +begin + 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 to Pred(cinfo^.comps_in_scan) do + begin + 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) then + begin + { 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) then + begin + ta := 0; { DC scan } + if (cinfo^.Ah <> 0) and not cinfo^.arith_code then + td := 0; { no DC table either } + end + else + begin + td := 0; { AC scan } + end; + end; + emit_byte(cinfo, (td shl 4) + ta); + end; + + emit_byte(cinfo, cinfo^.Ss); + emit_byte(cinfo, cinfo^.Se); + emit_byte(cinfo, (cinfo^.Ah shl 4) + cinfo^.Al); +end; + + +{LOCAL} +procedure emit_jfif_app0 (cinfo : j_compress_ptr); +{ 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 - $00 = none, $01 = inch, $02 = 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) +} +begin + emit_marker(cinfo, M_APP0); + + emit_2bytes(cinfo, 2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1); { length } + + emit_byte(cinfo, $4A); { Identifier: ASCII "JFIF" } + emit_byte(cinfo, $46); + emit_byte(cinfo, $49); + emit_byte(cinfo, $46); + 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); +end; + + +{LOCAL} +procedure emit_adobe_app14 (cinfo : j_compress_ptr); +{ 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. +} +begin + emit_marker(cinfo, M_APP14); + + emit_2bytes(cinfo, 2 + 5 + 2 + 2 + 2 + 1); { length } + + emit_byte(cinfo, $41); { Identifier: ASCII "Adobe" } + emit_byte(cinfo, $64); + emit_byte(cinfo, $6F); + emit_byte(cinfo, $62); + emit_byte(cinfo, $65); + emit_2bytes(cinfo, 100); { Version } + emit_2bytes(cinfo, 0); { Flags0 } + emit_2bytes(cinfo, 0); { Flags1 } + case (cinfo^.jpeg_color_space) of + JCS_YCbCr: + emit_byte(cinfo, 1); { Color transform = 1 } + JCS_YCCK: + emit_byte(cinfo, 2); { Color transform = 2 } + else + emit_byte(cinfo, 0); { Color transform = 0 } + end; +end; + + +{ 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} +procedure write_marker_header (cinfo : j_compress_ptr; + marker : int; + datalen : uint); +{ Emit an arbitrary marker header } +begin + if (datalen > uint(65533)) then { safety check } + ERREXIT(j_common_ptr(cinfo), JERR_BAD_LENGTH); + + emit_marker(cinfo, JPEG_MARKER(marker)); + + emit_2bytes(cinfo, int(datalen + 2)); { total length } +end; + +{METHODDEF} +procedure write_marker_byte (cinfo : j_compress_ptr; val : int); +{ Emit one byte of marker parameters following write_marker_header } +begin + emit_byte(cinfo, val); +end; + +{ 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} +procedure write_file_header (cinfo : j_compress_ptr); +var + marker : my_marker_ptr; +begin + 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) then { next an optional JFIF APP0 } + emit_jfif_app0(cinfo); + if (cinfo^.write_Adobe_marker) then { next an optional Adobe APP14 } + emit_adobe_app14(cinfo); +end; + + +{ 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} +procedure write_frame_header (cinfo : j_compress_ptr); +var + ci, prec : int; + is_baseline : boolean; + compptr : jpeg_component_info_ptr; +begin + { Emit DQT for each quantization table. + Note that emit_dqt() suppresses any duplicate tables. } + + prec := 0; + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to Pred(cinfo^.num_components) do + begin + prec := prec + emit_dqt(cinfo, compptr^.quant_tbl_no); + Inc(compptr); + end; + { 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) or (cinfo^.progressive_mode) + or (cinfo^.data_precision <> 8) then + begin + is_baseline := FALSE; + end + else + begin + is_baseline := TRUE; + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to Pred(cinfo^.num_components) do + begin + if (compptr^.dc_tbl_no > 1) or (compptr^.ac_tbl_no > 1) then + is_baseline := FALSE; + Inc(compptr); + end; + if (prec <> 0) and (is_baseline) then + begin + is_baseline := FALSE; + { If it's baseline except for quantizer size, warn the user } + {$IFDEF DEBUG} + TRACEMS(j_common_ptr(cinfo), 0, JTRC_16BIT_TABLES); + {$ENDIF} + end; + end; + + { Emit the proper SOF marker } + if (cinfo^.arith_code) then + begin + emit_sof(cinfo, M_SOF9); { SOF code for arithmetic coding } + end + else + begin + if (cinfo^.progressive_mode) then + emit_sof(cinfo, M_SOF2) { SOF code for progressive Huffman } + else if (is_baseline) then + emit_sof(cinfo, M_SOF0) { SOF code for baseline implementation } + else + emit_sof(cinfo, M_SOF1); { SOF code for non-baseline Huffman file } + end; +end; + + +{ Write scan header. + This consists of DHT or DAC markers, optional DRI, and SOS. + Compressed data will be written following the SOS. } + +{METHODDEF} +procedure write_scan_header (cinfo : j_compress_ptr); +var + marker : my_marker_ptr; + i : int; + compptr : jpeg_component_info_ptr; +begin + marker := my_marker_ptr(cinfo^.marker); + if (cinfo^.arith_code) then + begin + { 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); + end + else + begin + { Emit Huffman tables. + Note that emit_dht() suppresses any duplicate tables. } + for i := 0 to Pred(cinfo^.comps_in_scan) do + begin + compptr := cinfo^.cur_comp_info[i]; + if (cinfo^.progressive_mode) then + begin + { Progressive mode: only DC or only AC tables are used in one scan } + if (cinfo^.Ss = 0) then + begin + if (cinfo^.Ah = 0) then { DC needs no table for refinement scan } + emit_dht(cinfo, compptr^.dc_tbl_no, FALSE); + end + else + begin + emit_dht(cinfo, compptr^.ac_tbl_no, TRUE); + end; + end + else + begin + { Sequential mode: need both DC and AC tables } + emit_dht(cinfo, compptr^.dc_tbl_no, FALSE); + emit_dht(cinfo, compptr^.ac_tbl_no, TRUE); + end; + end; + end; + + { 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) then + begin + emit_dri(cinfo); + marker^.last_restart_interval := cinfo^.restart_interval; + end; + + emit_sos(cinfo); +end; + + + +{ Write datastream trailer. } + + +{METHODDEF} +procedure write_file_trailer (cinfo : j_compress_ptr); +begin + emit_marker(cinfo, M_EOI); +end; + + +{ 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} +procedure write_tables_only (cinfo : j_compress_ptr); +var + i : int; +begin + emit_marker(cinfo, M_SOI); + + for i := 0 to Pred(NUM_QUANT_TBLS) do + begin + if (cinfo^.quant_tbl_ptrs[i] <> NIL) then + emit_dqt(cinfo, i); { dummy := ... } + end; + + if (not cinfo^.arith_code) then + begin + for i := 0 to Pred(NUM_HUFF_TBLS) do + begin + if (cinfo^.dc_huff_tbl_ptrs[i] <> NIL) then + emit_dht(cinfo, i, FALSE); + if (cinfo^.ac_huff_tbl_ptrs[i] <> NIL) then + emit_dht(cinfo, i, TRUE); + end; + end; + + emit_marker(cinfo, M_EOI); +end; + + +{ Initialize the marker writer module. } + +{GLOBAL} +procedure jinit_marker_writer (cinfo : j_compress_ptr); +var + marker : my_marker_ptr; +begin + { Create the subobject } + marker := my_marker_ptr( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + SIZEOF(my_marker_writer)) ); + cinfo^.marker := jpeg_marker_writer_ptr(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; +end; + + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjcmaster.pas b/resources/libraries/deskew/Imaging/JpegLib/imjcmaster.pas new file mode 100755 index 0000000..90faeb5 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjcmaster.pas @@ -0,0 +1,701 @@ +unit imjcmaster; + +{ 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). } + +{ Original: jcmaster.c ; Copyright (C) 1991-1997, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjdeferr, + imjerror, + imjutils, + imjpeglib; + +{ Initialize master compression control. } + +{GLOBAL} +procedure jinit_c_master_control (cinfo : j_compress_ptr; + transcode_only : boolean); + +implementation + +{ Private state } + +type + c_pass_type = ( + main_pass, { input data, also do first output step } + huff_opt_pass, { Huffman code optimization pass } + output_pass { data output pass } + ); + +type + my_master_ptr = ^my_comp_master; + my_comp_master = record + pub : jpeg_comp_master; { public fields } + + pass_type : c_pass_type; { the type of the current pass } + + pass_number : int; { # of passes completed } + total_passes : int; { total # of passes needed } + + scan_number : int; { current index in scan_info[] } + end; + + +{ Support routines that do various essential calculations. } + +{LOCAL} +procedure initial_setup (cinfo : j_compress_ptr); +{ Do computations that are needed before master selection phase } +var + ci : int; + compptr : jpeg_component_info_ptr; + samplesperrow : long; + jd_samplesperrow : JDIMENSION; +begin + + { Sanity check on image dimensions } + if (cinfo^.image_height <= 0) or (cinfo^.image_width <= 0) or + (cinfo^.num_components <= 0) or (cinfo^.input_components <= 0) then + ERREXIT(j_common_ptr(cinfo), JERR_EMPTY_IMAGE); + + { Make sure image isn't bigger than I can handle } + if ( long(cinfo^.image_height) > long(JPEG_MAX_DIMENSION)) or + ( long(cinfo^.image_width) > long(JPEG_MAX_DIMENSION)) then + ERREXIT1(j_common_ptr(cinfo), JERR_IMAGE_TOO_BIG, + uInt(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) then + ERREXIT(j_common_ptr(cinfo), JERR_WIDTH_OVERFLOW); + + { For now, precision must match compiled-in value... } + if (cinfo^.data_precision <> BITS_IN_JSAMPLE) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PRECISION, cinfo^.data_precision); + + { Check that number of components won't exceed internal array sizes } + if (cinfo^.num_components > MAX_COMPONENTS) then + ERREXIT2(j_common_ptr(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; + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + if (compptr^.h_samp_factor<=0) or (compptr^.h_samp_factor>MAX_SAMP_FACTOR) + or (compptr^.v_samp_factor<=0) or (compptr^.v_samp_factor>MAX_SAMP_FACTOR) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_SAMPLING); + { MAX } + if cinfo^.max_h_samp_factor > compptr^.h_samp_factor then + cinfo^.max_h_samp_factor := cinfo^.max_h_samp_factor + else + cinfo^.max_h_samp_factor := compptr^.h_samp_factor; + { MAX } + if cinfo^.max_v_samp_factor > compptr^.v_samp_factor then + cinfo^.max_v_samp_factor := cinfo^.max_v_samp_factor + else + cinfo^.max_v_samp_factor := compptr^.v_samp_factor; + Inc(compptr); + end; + + { Compute dimensions of components } + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + { 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; + Inc(compptr); + end; + + { 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)) ); +end; + + +{$ifdef C_MULTISCAN_FILES_SUPPORTED} + +{LOCAL} +procedure validate_script (cinfo : j_compress_ptr); +{ Verify that the scan script in cinfo^.scan_info[] is valid; also + determine whether it uses progressive JPEG, and set cinfo^.progressive_mode. } +type + IntRow = array[0..DCTSIZE2-1] of int; + introw_ptr = ^IntRow; +var + {const}scanptr : jpeg_scan_info_ptr; + scanno, ncomps, ci, coefi, thisi : int; + Ss, Se, Ah, Al : int; + component_sent : array[0..MAX_COMPONENTS-1] of boolean; +{$ifdef C_PROGRESSIVE_SUPPORTED} + last_bitpos_int_ptr : int_ptr; + last_bitpos_ptr : introw_ptr; + last_bitpos : array[0..MAX_COMPONENTS-1] of IntRow; + { -1 until that coefficient has been seen; then last Al for it } + { 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. } +{$ifdef BITS_IN_JSAMPLE_IS_8} +const + MAX_AH_AL = 10; +{$else} +const + MAX_AH_AL = 13; +{$endif} +{$endif} +begin + + if (cinfo^.num_scans <= 0) then + ERREXIT1(j_common_ptr(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) or (scanptr^.Se <> DCTSIZE2-1) then + begin +{$ifdef C_PROGRESSIVE_SUPPORTED} + cinfo^.progressive_mode := TRUE; + last_bitpos_int_ptr := @(last_bitpos[0][0]); + for ci := 0 to pred(cinfo^.num_components) do + for coefi := 0 to pred(DCTSIZE2) do + begin + last_bitpos_int_ptr^ := -1; + Inc(last_bitpos_int_ptr); + end; +{$else} + ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); +{$endif} + end + else + begin + cinfo^.progressive_mode := FALSE; + for ci := 0 to pred(cinfo^.num_components) do + component_sent[ci] := FALSE; + end; + + for scanno := 1 to cinfo^.num_scans do + begin + { Validate component indexes } + ncomps := scanptr^.comps_in_scan; + if (ncomps <= 0) or (ncomps > MAX_COMPS_IN_SCAN) then + ERREXIT2(j_common_ptr(cinfo), JERR_COMPONENT_COUNT, ncomps, MAX_COMPS_IN_SCAN); + for ci := 0 to pred(ncomps) do + begin + thisi := scanptr^.component_index[ci]; + if (thisi < 0) or (thisi >= cinfo^.num_components) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_SCAN_SCRIPT, scanno); + { Components must appear in SOF order within each scan } + if (ci > 0) and (thisi <= scanptr^.component_index[ci-1]) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_SCAN_SCRIPT, scanno); + end; + { Validate progression parameters } + Ss := scanptr^.Ss; + Se := scanptr^.Se; + Ah := scanptr^.Ah; + Al := scanptr^.Al; + if (cinfo^.progressive_mode) then + begin +{$ifdef C_PROGRESSIVE_SUPPORTED} + if (Ss < 0) or (Ss >= DCTSIZE2) or (Se < Ss) or (Se >= DCTSIZE2) or + (Ah < 0) or (Ah > MAX_AH_AL) or (Al < 0) or (Al > MAX_AH_AL) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); + + if (Ss < 0) or (Ss >= DCTSIZE2) or (Se < Ss) or (Se >= DCTSIZE2) + or (Ah < 0) or (Ah > MAX_AH_AL) or (Al < 0) or (Al > MAX_AH_AL) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); + if (Ss = 0) then + begin + if (Se <> 0) then { DC and AC together not OK } + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); + end + else + begin + if (ncomps <> 1) then { AC scans must be for only one component } + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); + end; + for ci := 0 to pred(ncomps) do + begin + last_bitpos_ptr := @( last_bitpos[scanptr^.component_index[ci]]); + if (Ss <> 0) and (last_bitpos_ptr^[0] < 0) then { AC without prior DC scan } + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); + for coefi := Ss to Se do + begin + if (last_bitpos_ptr^[coefi] < 0) then + begin + { first scan of this coefficient } + if (Ah <> 0) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); + end + else + begin + { not first scan } + if (Ah <> last_bitpos_ptr^[coefi]) or (Al <> Ah-1) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); + end; + last_bitpos_ptr^[coefi] := Al; + end; + end; +{$endif} + end + else + begin + { For sequential JPEG, all progression parameters must be these: } + if (Ss <> 0) or (Se <> DCTSIZE2-1) or (Ah <> 0) or (Al <> 0) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PROG_SCRIPT, scanno); + { Make sure components are not sent twice } + for ci := 0 to pred(ncomps) do + begin + thisi := scanptr^.component_index[ci]; + if (component_sent[thisi]) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_SCAN_SCRIPT, scanno); + component_sent[thisi] := TRUE; + end; + end; + Inc(scanptr); + end; + + { Now verify that everything got sent. } + if (cinfo^.progressive_mode) then + begin +{$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 to pred(cinfo^.num_components) do + begin + if (last_bitpos[ci][0] < 0) then + ERREXIT(j_common_ptr(cinfo), JERR_MISSING_DATA); + end; +{$endif} + end + else + begin + for ci := 0 to pred(cinfo^.num_components) do + begin + if (not component_sent[ci]) then + ERREXIT(j_common_ptr(cinfo), JERR_MISSING_DATA); + end; + end; +end; + +{$endif} { C_MULTISCAN_FILES_SUPPORTED } + + +{LOCAL} +procedure select_scan_parameters (cinfo : j_compress_ptr); +{ Set up the scan parameters for the current scan } +var + master : my_master_ptr; + {const} scanptr : jpeg_scan_info_ptr; + ci : int; +var + comp_infos : jpeg_component_info_list_ptr; +begin +{$ifdef C_MULTISCAN_FILES_SUPPORTED} + if (cinfo^.scan_info <> NIL) then + begin + { Prepare for current scan --- the script is already validated } + master := my_master_ptr (cinfo^.master); + scanptr := cinfo^.scan_info; + Inc(scanptr, master^.scan_number); + + cinfo^.comps_in_scan := scanptr^.comps_in_scan; + comp_infos := cinfo^.comp_info; + for ci := 0 to pred(scanptr^.comps_in_scan) do + begin + cinfo^.cur_comp_info[ci] := + @(comp_infos^[scanptr^.component_index[ci]]); + end; + cinfo^.Ss := scanptr^.Ss; + cinfo^.Se := scanptr^.Se; + cinfo^.Ah := scanptr^.Ah; + cinfo^.Al := scanptr^.Al; + end + else +{$endif} + begin + { Prepare for single sequential-JPEG scan containing all components } + if (cinfo^.num_components > MAX_COMPS_IN_SCAN) then + ERREXIT2(j_common_ptr(cinfo), JERR_COMPONENT_COUNT, cinfo^.num_components, + MAX_COMPS_IN_SCAN); + cinfo^.comps_in_scan := cinfo^.num_components; + comp_infos := cinfo^.comp_info; + for ci := 0 to pred(cinfo^.num_components) do + begin + cinfo^.cur_comp_info[ci] := @(comp_infos^[ci]); + end; + cinfo^.Ss := 0; + cinfo^.Se := DCTSIZE2-1; + cinfo^.Ah := 0; + cinfo^.Al := 0; + end; +end; + + +{LOCAL} +procedure per_scan_setup (cinfo : j_compress_ptr); +{ Do computations that are needed before processing a JPEG scan } +{ cinfo^.comps_in_scan and cinfo^.cur_comp_info[] are already set } +var + ci, mcublks, tmp : int; + compptr : jpeg_component_info_ptr; + nominal : long; +begin + if (cinfo^.comps_in_scan = 1) then + begin + + { 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) mod compptr^.v_samp_factor; + if (tmp = 0) then + 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; + + end + else + begin + + { Interleaved (multi-component) scan } + if (cinfo^.comps_in_scan <= 0) or + (cinfo^.comps_in_scan > MAX_COMPS_IN_SCAN) then + ERREXIT2(j_common_ptr(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 to pred(cinfo^.comps_in_scan) do + begin + 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) mod compptr^.MCU_width; + if (tmp = 0) then + tmp := compptr^.MCU_width; + compptr^.last_col_width := tmp; + tmp := int (compptr^.height_in_blocks) mod compptr^.MCU_height; + if (tmp = 0) then + 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) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_MCU_SIZE); + while (mcublks > 0) do + begin + Dec(mcublks); + cinfo^.MCU_membership[cinfo^.blocks_in_MCU] := ci; + Inc(cinfo^.blocks_in_MCU); + end; + end; + + end; + + { 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) then + begin + nominal := long(cinfo^.restart_in_rows) * long(cinfo^.MCUs_per_row); + if nominal < long(65535) then + cinfo^.restart_interval := uInt (nominal) + else + cinfo^.restart_interval := long(65535); + end; +end; + + +{ 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} +procedure prepare_for_pass (cinfo : j_compress_ptr); +var + master : my_master_ptr; +var + fallthrough : boolean; +begin + master := my_master_ptr (cinfo^.master); + fallthrough := true; + + case (master^.pass_type) of + main_pass: + begin + { 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 (not cinfo^.raw_data_in) then + begin + cinfo^.cconvert^.start_pass (cinfo); + cinfo^.downsample^.start_pass (cinfo); + cinfo^.prep^.start_pass (cinfo, JBUF_PASS_THRU); + end; + cinfo^.fdct^.start_pass (cinfo); + cinfo^.entropy^.start_pass (cinfo, cinfo^.optimize_coding); + if master^.total_passes > 1 then + cinfo^.coef^.start_pass (cinfo, JBUF_SAVE_AND_PASS) + else + cinfo^.coef^.start_pass (cinfo, JBUF_PASS_THRU); + cinfo^.main^.start_pass (cinfo, JBUF_PASS_THRU); + if (cinfo^.optimize_coding) then + begin + { No immediate data output; postpone writing frame/scan headers } + master^.pub.call_pass_startup := FALSE; + end + else + begin + { Will write frame/scan headers at first jpeg_write_scanlines call } + master^.pub.call_pass_startup := TRUE; + end; + end; +{$ifdef ENTROPY_OPT_SUPPORTED} + huff_opt_pass, + output_pass: + begin + if (master^.pass_type = huff_opt_pass) then + begin + { Do Huffman optimization for a scan after the first one. } + select_scan_parameters(cinfo); + per_scan_setup(cinfo); + if (cinfo^.Ss <> 0) or (cinfo^.Ah = 0) or (cinfo^.arith_code) then + begin + cinfo^.entropy^.start_pass (cinfo, TRUE); + cinfo^.coef^.start_pass (cinfo, JBUF_CRANK_DEST); + master^.pub.call_pass_startup := FALSE; + fallthrough := false; + end; + { Special case: Huffman DC refinement scans need no Huffman table + and therefore we can skip the optimization pass for them. } + if fallthrough then + begin + master^.pass_type := output_pass; + Inc(master^.pass_number); + {FALLTHROUGH} + end; + end; +{$else} + output_pass: + begin +{$endif} + if fallthrough then + begin + { Do a data-output pass. } + { We need not repeat per-scan setup if prior optimization pass did it. } + if (not cinfo^.optimize_coding) then + begin + select_scan_parameters(cinfo); + per_scan_setup(cinfo); + end; + 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) then + cinfo^.marker^.write_frame_header (cinfo); + cinfo^.marker^.write_scan_header (cinfo); + master^.pub.call_pass_startup := FALSE; + end; + end; + else + ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); + end; + + master^.pub.is_last_pass := (master^.pass_number = master^.total_passes-1); + + { Set up progress monitor's pass info if present } + if (cinfo^.progress <> NIL) then + begin + cinfo^.progress^.completed_passes := master^.pass_number; + cinfo^.progress^.total_passes := master^.total_passes; + end; +end; + + +{ 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} +procedure pass_startup (cinfo : j_compress_ptr); +begin + cinfo^.master^.call_pass_startup := FALSE; { reset flag so call only once } + + cinfo^.marker^.write_frame_header (cinfo); + cinfo^.marker^.write_scan_header (cinfo); +end; + + +{ Finish up at end of pass. } + +{METHODDEF} +procedure finish_pass_master (cinfo : j_compress_ptr); +var + master : my_master_ptr; +begin + 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 } + case (master^.pass_type) of + main_pass: + begin + { next pass is either output of scan 0 (after optimization) + or output of scan 1 (if no optimization). } + + master^.pass_type := output_pass; + if (not cinfo^.optimize_coding) then + Inc(master^.scan_number); + end; + huff_opt_pass: + { next pass is always output of current scan } + master^.pass_type := output_pass; + output_pass: + begin + { next pass is either optimization or output of next scan } + if (cinfo^.optimize_coding) then + master^.pass_type := huff_opt_pass; + Inc(master^.scan_number); + end; + end; + + Inc(master^.pass_number); +end; + + +{ Initialize master compression control. } + +{GLOBAL} +procedure jinit_c_master_control (cinfo : j_compress_ptr; + transcode_only : boolean); +var + master : my_master_ptr; +begin + master := my_master_ptr( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + SIZEOF(my_comp_master)) ); + cinfo^.master := jpeg_comp_master_ptr(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 <> NIL) then + begin +{$ifdef C_MULTISCAN_FILES_SUPPORTED} + validate_script(cinfo); +{$else} + ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); +{$endif} + end + else + begin + cinfo^.progressive_mode := FALSE; + cinfo^.num_scans := 1; + end; + + if (cinfo^.progressive_mode) then { TEMPORARY HACK ??? } + cinfo^.optimize_coding := TRUE; { assume default tables no good for progressive mode } + + { Initialize my private state } + if (transcode_only) then + begin + { no main pass in transcoding } + if (cinfo^.optimize_coding) then + master^.pass_type := huff_opt_pass + else + master^.pass_type := output_pass; + end + else + begin + { for normal compression, first pass is always this type: } + master^.pass_type := main_pass; + end; + master^.scan_number := 0; + master^.pass_number := 0; + if (cinfo^.optimize_coding) then + master^.total_passes := cinfo^.num_scans * 2 + else + master^.total_passes := cinfo^.num_scans; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjcomapi.pas b/resources/libraries/deskew/Imaging/JpegLib/imjcomapi.pas new file mode 100755 index 0000000..c58a7ae --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjcomapi.pas @@ -0,0 +1,130 @@ +unit imjcomapi; + +{ This file contains application interface routines that are used for both + compression and decompression. } + +{ Original: jcomapi.c; Copyright (C) 1994-1997, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjpeglib; + +{ Abort processing of a JPEG compression or decompression operation, + but don't destroy the object itself. } + +{GLOBAL} +procedure jpeg_abort (cinfo : j_common_ptr); + + +{ Destruction of a JPEG object. } + +{GLOBAL} +procedure jpeg_destroy (cinfo : j_common_ptr); + +{GLOBAL} +function jpeg_alloc_quant_table (cinfo : j_common_ptr) : JQUANT_TBL_PTR; + +{GLOBAL} +function jpeg_alloc_huff_table (cinfo : j_common_ptr) : JHUFF_TBL_PTR; + +implementation + +{ 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} +procedure jpeg_abort (cinfo : j_common_ptr); +var + pool : int; +begin + { Do nothing if called on a not-initialized or destroyed JPEG object. } + if (cinfo^.mem = NIL) then + exit; + + { Releasing pools in reverse order might help avoid fragmentation + with some (brain-damaged) malloc libraries. } + + for pool := JPOOL_NUMPOOLS-1 downto JPOOL_PERMANENT+1 do + begin + cinfo^.mem^.free_pool (cinfo, pool); + end; + + { Reset overall state for possible reuse of object } + if (cinfo^.is_decompressor) then + begin + 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 := NIL; + end + else + begin + cinfo^.global_state := CSTATE_START; + end; +end; + + +{ 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} +procedure jpeg_destroy (cinfo : j_common_ptr); +begin + { We need only tell the memory manager to release everything. } + { NB: mem pointer is NIL if memory mgr failed to initialize. } + if (cinfo^.mem <> NIL) then + cinfo^.mem^.self_destruct (cinfo); + cinfo^.mem := NIL; { be safe if jpeg_destroy is called twice } + cinfo^.global_state := 0; { mark it destroyed } +end; + + +{ Convenience routines for allocating quantization and Huffman tables. + (Would jutils.c be a more reasonable place to put these?) } + + +{GLOBAL} +function jpeg_alloc_quant_table (cinfo : j_common_ptr) : JQUANT_TBL_PTR; +var + tbl : JQUANT_TBL_PTR; +begin + tbl := JQUANT_TBL_PTR( + cinfo^.mem^.alloc_small (cinfo, JPOOL_PERMANENT, SIZEOF(JQUANT_TBL)) + ); + tbl^.sent_table := FALSE; { make sure this is false in any new table } + jpeg_alloc_quant_table := tbl; +end; + + +{GLOBAL} +function jpeg_alloc_huff_table (cinfo : j_common_ptr) : JHUFF_TBL_PTR; +var + tbl : JHUFF_TBL_PTR; +begin + tbl := JHUFF_TBL_PTR( + cinfo^.mem^.alloc_small (cinfo, JPOOL_PERMANENT, SIZEOF(JHUFF_TBL)) + ); + tbl^.sent_table := FALSE; { make sure this is false in any new table } + jpeg_alloc_huff_table := tbl; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjconfig.inc b/resources/libraries/deskew/Imaging/JpegLib/imjconfig.inc new file mode 100755 index 0000000..4daf195 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjconfig.inc @@ -0,0 +1,126 @@ +{ ----------------------- 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? } + +{ If you happen not to want the image transform support, disable it here } +{$define TRANSFORMS_SUPPORTED} + +{ more capability options later, no doubt } + +{$ifopt I+} {$define IOcheck} {$endif} + +{ ------------------------------------------------------------------------ } + +{$define USE_FMEM} { Borland has _fmemcpy() and _fmemset() } + +{$define FMEMCOPY} +{$define FMEMZERO} + +{$define DCTSIZE_IS_8} { e.g. unroll the inner loop } +{$define RIGHT_SHIFT_IS_UNSIGNED} +{$undef AVOID_TABLES} +{$undef FAST_DIVIDE} + +{$define BITS_IN_JSAMPLE_IS_8} + +{----------------------------------------------------------------} +{ for test of 12 bit JPEG code only. !! } +{-- $undef BITS_IN_JSAMPLE_IS_8} +{----------------------------------------------------------------} + +//{$define RGB_RED_IS_0} +{ !CHANGE: This must be defined for Delphi/Kylix/FPC } +{$define RGB_RED_IS_2} { RGB byte order } + + +{$define RGB_PIXELSIZE_IS_3} +{$define SLOW_SHIFT_32} +{$undef NO_ZERO_ROW_TEST} + +{$define USE_MSDOS_MEMMGR} { Define this if you use jmemdos.c } +{$define XMS_SUPPORTED} +{$define EMS_SUPPORTED} + +{$undef MEM_STATS} { Write out memory usage } +{$define AM_MEMORY_MANAGER} { we define jvirt_Xarray_control structs } + +{$undef FULL_MAIN_BUFFER_SUPPORTED} + +{$define PROGRESS_REPORT} +{$define TWO_FILE_COMMANDLINE} +{$undef BMP_SUPPORTED} +{$undef PPM_SUPPORTED} +{$undef GIF_SUPPORTED} +{$undef RLE_SUPPORTED} +{$undef TARGA_SUPPORTED} +{$define EXT_SWITCH} + +{$ifndef BITS_IN_JSAMPLE_IS_8} { for 12 bit samples } +{$undef BMP_SUPPORTED} +{$undef RLE_SUPPORTED} +{$undef TARGA_SUPPORTED} +{$endif} + + +{!CHANGE: Allowed only for Delphi} +{$undef BASM16} { for TP7 - use BASM for fast multiply } +{$ifdef Win32} + {$ifndef FPC} + {$define BASM} { jidctint with BASM for Delphi 2/3 } + {$undef RGB_RED_IS_0} { BGR byte order in JQUANT2 } + {$endif} +{$endif} + +{$ifdef FPC} + {$MODE DELPHI} +{$endif} + +{!CHANGE: Added this} +{$define Delphi_Stream} +{$Q-} +{$MINENUMSIZE 4} +{$ALIGN 8} + diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjcparam.pas b/resources/libraries/deskew/Imaging/JpegLib/imjcparam.pas new file mode 100755 index 0000000..345fc32 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjcparam.pas @@ -0,0 +1,701 @@ +unit imjcparam; + +{ 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. } + +{ Original: jcparam.c ; Copyright (C) 1991-1998, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjdeferr, + imjerror, + imjcomapi, + imjpeglib; + +{ Quantization table setup routines } + +{GLOBAL} +procedure jpeg_add_quant_table (cinfo : j_compress_ptr; + which_tbl : int; + const basic_table : array of uInt; + scale_factor : int; + force_baseline : boolean); + +{GLOBAL} +procedure jpeg_set_linear_quality (cinfo : j_compress_ptr; + scale_factor : int; + force_baseline : boolean); +{ 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. } + +{GLOBAL} +function jpeg_quality_scaling (quality : int) : int; +{ 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). } + +{GLOBAL} +procedure jpeg_set_quality (cinfo : j_compress_ptr; + quality : int; + force_baseline : boolean); +{ 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. } + +{GLOBAL} +procedure jpeg_set_defaults (cinfo : j_compress_ptr); + +{ Create a recommended progressive-JPEG script. + cinfo^.num_components and cinfo^.jpeg_color_space must be correct. } + +{ Set the JPEG colorspace, and choose colorspace-dependent default values. } + +{GLOBAL} +procedure jpeg_set_colorspace (cinfo : j_compress_ptr; + colorspace : J_COLOR_SPACE); + +{ Select an appropriate JPEG colorspace for in_color_space. } + +{GLOBAL} +procedure jpeg_default_colorspace (cinfo : j_compress_ptr); + +{GLOBAL} +procedure jpeg_simple_progression (cinfo : j_compress_ptr); + + +implementation + +{ Quantization table setup routines } + +{GLOBAL} +procedure jpeg_add_quant_table (cinfo : j_compress_ptr; + which_tbl : int; + const basic_table : array of uInt; + scale_factor : int; + force_baseline : boolean); +{ 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. } +var + qtblptr :^JQUANT_TBL_PTR; + i : int; + temp : long; +begin + { Safety check to ensure start_compress not called yet. } + if (cinfo^.global_state <> CSTATE_START) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); + + if (which_tbl < 0) or (which_tbl >= NUM_QUANT_TBLS) then + ERREXIT1(j_common_ptr(cinfo), JERR_DQT_INDEX, which_tbl); + + qtblptr := @(cinfo^.quant_tbl_ptrs[which_tbl]); + + if (qtblptr^ = NIL) then + qtblptr^ := jpeg_alloc_quant_table(j_common_ptr(cinfo)); + + for i := 0 to pred(DCTSIZE2) do + begin + temp := (long(basic_table[i]) * scale_factor + long(50)) div long(100); + { limit the values to the valid range } + if (temp <= long(0)) then + temp := long(1); + if (temp > long(32767)) then + temp := long(32767); { max quantizer needed for 12 bits } + if (force_baseline) and (temp > long(255)) then + temp := long(255); { limit to baseline range if requested } + (qtblptr^)^.quantval[i] := UINT16 (temp); + end; + + { Initialize sent_table FALSE so table will be written to JPEG file. } + (qtblptr^)^.sent_table := FALSE; +end; + + +{GLOBAL} +procedure jpeg_set_linear_quality (cinfo : j_compress_ptr; + scale_factor : int; + force_baseline : boolean); +{ 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. } + +const + std_luminance_quant_tbl : array[0..DCTSIZE2-1] of uInt = + (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); + +const + std_chrominance_quant_tbl : array[0..DCTSIZE2-1] of uInt = + (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); +begin + { 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); +end; + + +{GLOBAL} +function jpeg_quality_scaling (quality : int) : int; +{ 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). } +begin + { Safety limit on quality factor. Convert 0 to 1 to avoid zero divide. } + if (quality <= 0) then + quality := 1; + if (quality > 100) then + 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) then + quality := 5000 div quality + else + quality := 200 - quality*2; + + jpeg_quality_scaling := quality; +end; + + +{GLOBAL} +procedure jpeg_set_quality (cinfo : j_compress_ptr; + quality : int; + force_baseline : boolean); +{ 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. } +begin + { 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); +end; + + +{ Huffman table setup routines } + +{LOCAL} +procedure add_huff_table (cinfo : j_compress_ptr; + var htblptr : JHUFF_TBL_PTR; + var bits : array of UINT8; + var val : array of UINT8); +{ Define a Huffman table } +var + nsymbols, len : int; +begin + if (htblptr = NIL) then + 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 to 16 do + Inc(nsymbols, bits[len]); + if (nsymbols < 1) or (nsymbols > 256) then + ERREXIT(j_common_ptr(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; +end; + + +{$J+} +{LOCAL} +procedure std_huff_tables (cinfo : j_compress_ptr); +{ Set up the standard Huffman tables (cf. JPEG standard section K.3) } +{ IMPORTANT: these are only valid for 8-bit data precision! } + const bits_dc_luminance : array[0..17-1] of UINT8 = + ({ 0-base } 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0); + const val_dc_luminance : array[0..11] of UINT8 = + (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11); + + const bits_dc_chrominance : array[0..17-1] of UINT8 = + ( { 0-base } 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 ); + const val_dc_chrominance : array[0..11] of UINT8 = + ( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ); + + const bits_ac_luminance : array[0..17-1] of UINT8 = + ( { 0-base } 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, $7d ); + const val_ac_luminance : array[0..161] of UINT8 = + ( $01, $02, $03, $00, $04, $11, $05, $12, + $21, $31, $41, $06, $13, $51, $61, $07, + $22, $71, $14, $32, $81, $91, $a1, $08, + $23, $42, $b1, $c1, $15, $52, $d1, $f0, + $24, $33, $62, $72, $82, $09, $0a, $16, + $17, $18, $19, $1a, $25, $26, $27, $28, + $29, $2a, $34, $35, $36, $37, $38, $39, + $3a, $43, $44, $45, $46, $47, $48, $49, + $4a, $53, $54, $55, $56, $57, $58, $59, + $5a, $63, $64, $65, $66, $67, $68, $69, + $6a, $73, $74, $75, $76, $77, $78, $79, + $7a, $83, $84, $85, $86, $87, $88, $89, + $8a, $92, $93, $94, $95, $96, $97, $98, + $99, $9a, $a2, $a3, $a4, $a5, $a6, $a7, + $a8, $a9, $aa, $b2, $b3, $b4, $b5, $b6, + $b7, $b8, $b9, $ba, $c2, $c3, $c4, $c5, + $c6, $c7, $c8, $c9, $ca, $d2, $d3, $d4, + $d5, $d6, $d7, $d8, $d9, $da, $e1, $e2, + $e3, $e4, $e5, $e6, $e7, $e8, $e9, $ea, + $f1, $f2, $f3, $f4, $f5, $f6, $f7, $f8, + $f9, $fa ); + + const bits_ac_chrominance : array[0..17-1] of UINT8 = + ( { 0-base } 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, $77 ); + const val_ac_chrominance : array[0..161] of UINT8 = + ( $00, $01, $02, $03, $11, $04, $05, $21, + $31, $06, $12, $41, $51, $07, $61, $71, + $13, $22, $32, $81, $08, $14, $42, $91, + $a1, $b1, $c1, $09, $23, $33, $52, $f0, + $15, $62, $72, $d1, $0a, $16, $24, $34, + $e1, $25, $f1, $17, $18, $19, $1a, $26, + $27, $28, $29, $2a, $35, $36, $37, $38, + $39, $3a, $43, $44, $45, $46, $47, $48, + $49, $4a, $53, $54, $55, $56, $57, $58, + $59, $5a, $63, $64, $65, $66, $67, $68, + $69, $6a, $73, $74, $75, $76, $77, $78, + $79, $7a, $82, $83, $84, $85, $86, $87, + $88, $89, $8a, $92, $93, $94, $95, $96, + $97, $98, $99, $9a, $a2, $a3, $a4, $a5, + $a6, $a7, $a8, $a9, $aa, $b2, $b3, $b4, + $b5, $b6, $b7, $b8, $b9, $ba, $c2, $c3, + $c4, $c5, $c6, $c7, $c8, $c9, $ca, $d2, + $d3, $d4, $d5, $d6, $d7, $d8, $d9, $da, + $e2, $e3, $e4, $e5, $e6, $e7, $e8, $e9, + $ea, $f2, $f3, $f4, $f5, $f6, $f7, $f8, + $f9, $fa ); +begin + 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); +end; + + +{ 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} +procedure jpeg_set_defaults (cinfo : j_compress_ptr); +var + i : int; +begin + { Safety check to ensure start_compress not called yet. } + if (cinfo^.global_state <> CSTATE_START) then + ERREXIT1(J_common_ptr(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 = NIL) then + cinfo^.comp_info := jpeg_component_info_list_ptr( + 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 to pred(NUM_ARITH_TBLS) do + begin + cinfo^.arith_dc_L[i] := 0; + cinfo^.arith_dc_U[i] := 1; + cinfo^.arith_ac_K[i] := 5; + end; + + { Default is no multiple-scan output } + cinfo^.scan_info := NIL; + 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) then + 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); +end; + + +{ Select an appropriate JPEG colorspace for in_color_space. } + +{GLOBAL} +procedure jpeg_default_colorspace (cinfo : j_compress_ptr); +begin + case (cinfo^.in_color_space) of + JCS_GRAYSCALE: + jpeg_set_colorspace(cinfo, JCS_GRAYSCALE); + JCS_RGB: + jpeg_set_colorspace(cinfo, JCS_YCbCr); + JCS_YCbCr: + jpeg_set_colorspace(cinfo, JCS_YCbCr); + JCS_CMYK: + jpeg_set_colorspace(cinfo, JCS_CMYK); { By default, no translation } + JCS_YCCK: + jpeg_set_colorspace(cinfo, JCS_YCCK); + JCS_UNKNOWN: + jpeg_set_colorspace(cinfo, JCS_UNKNOWN); + else + ERREXIT(j_common_ptr(cinfo), JERR_BAD_IN_COLORSPACE); + end; +end; + + +{ Set the JPEG colorspace, and choose colorspace-dependent default values. } + +{GLOBAL} +procedure jpeg_set_colorspace (cinfo : j_compress_ptr; + colorspace : J_COLOR_SPACE); + { macro } + procedure SET_COMP(index,id,hsamp,vsamp,quant,dctbl,actbl : int); + begin + with cinfo^.comp_info^[index] do + begin + component_id := (id); + h_samp_factor := (hsamp); + v_samp_factor := (vsamp); + quant_tbl_no := (quant); + dc_tbl_no := (dctbl); + ac_tbl_no := (actbl); + end; + end; + +var + ci : int; +begin + { Safety check to ensure start_compress not called yet. } + if (cinfo^.global_state <> CSTATE_START) then + ERREXIT1(j_common_ptr(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 } + + case (colorspace) of + JCS_GRAYSCALE: + begin + 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); + end; + JCS_RGB: + begin + cinfo^.write_Adobe_marker := TRUE; { write Adobe marker to flag RGB } + cinfo^.num_components := 3; + SET_COMP(0, $52 { 'R' }, 1,1, 0, 0,0); + SET_COMP(1, $47 { 'G' }, 1,1, 0, 0,0); + SET_COMP(2, $42 { 'B' }, 1,1, 0, 0,0); + end; + JCS_YCbCr: + begin + 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); + end; + JCS_CMYK: + begin + cinfo^.write_Adobe_marker := TRUE; { write Adobe marker to flag CMYK } + cinfo^.num_components := 4; + SET_COMP(0, $43 { 'C' }, 1,1, 0, 0,0); + SET_COMP(1, $4D { 'M' }, 1,1, 0, 0,0); + SET_COMP(2, $59 { 'Y' }, 1,1, 0, 0,0); + SET_COMP(3, $4B { 'K' }, 1,1, 0, 0,0); + end; + JCS_YCCK: + begin + 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); + end; + JCS_UNKNOWN: + begin + cinfo^.num_components := cinfo^.input_components; + if (cinfo^.num_components < 1) + or (cinfo^.num_components > MAX_COMPONENTS) then + ERREXIT2(j_common_ptr(cinfo), JERR_COMPONENT_COUNT, + cinfo^.num_components, MAX_COMPONENTS); + for ci := 0 to pred(cinfo^.num_components) do + begin + SET_COMP(ci, ci, 1,1, 0, 0,0); + end; + end; + else + ERREXIT(j_common_ptr(cinfo), JERR_BAD_J_COLORSPACE); + end; +end; + + +{$ifdef C_PROGRESSIVE_SUPPORTED} + +{LOCAL} +function fill_a_scan (scanptr : jpeg_scan_info_ptr; + ci : int; Ss : int; + Se : int; Ah : int; + Al : int) : jpeg_scan_info_ptr; +{ Support routine: generate one scan for specified component } +begin + scanptr^.comps_in_scan := 1; + scanptr^.component_index[0] := ci; + scanptr^.Ss := Ss; + scanptr^.Se := Se; + scanptr^.Ah := Ah; + scanptr^.Al := Al; + Inc(scanptr); + fill_a_scan := scanptr; +end; + +{LOCAL} +function fill_scans (scanptr : jpeg_scan_info_ptr; + ncomps : int; + Ss : int; Se : int; + Ah : int; Al : int) : jpeg_scan_info_ptr; +{ Support routine: generate one scan for each component } +var + ci : int; +begin + + for ci := 0 to pred(ncomps) do + begin + scanptr^.comps_in_scan := 1; + scanptr^.component_index[0] := ci; + scanptr^.Ss := Ss; + scanptr^.Se := Se; + scanptr^.Ah := Ah; + scanptr^.Al := Al; + Inc(scanptr); + end; + fill_scans := scanptr; +end; + +{LOCAL} +function fill_dc_scans (scanptr : jpeg_scan_info_ptr; + ncomps : int; + Ah : int; Al : int) : jpeg_scan_info_ptr; +{ Support routine: generate interleaved DC scan if possible, else N scans } +var + ci : int; +begin + + if (ncomps <= MAX_COMPS_IN_SCAN) then + begin + { Single interleaved DC scan } + scanptr^.comps_in_scan := ncomps; + for ci := 0 to pred(ncomps) do + scanptr^.component_index[ci] := ci; + scanptr^.Ss := 0; + scanptr^.Se := 0; + scanptr^.Ah := Ah; + scanptr^.Al := Al; + Inc(scanptr); + end + else + begin + { Noninterleaved DC scan for each component } + scanptr := fill_scans(scanptr, ncomps, 0, 0, Ah, Al); + end; + fill_dc_scans := scanptr; +end; + + +{ Create a recommended progressive-JPEG script. + cinfo^.num_components and cinfo^.jpeg_color_space must be correct. } + +{GLOBAL} +procedure jpeg_simple_progression (cinfo : j_compress_ptr); +var + ncomps : int; + nscans : int; + scanptr : jpeg_scan_info_ptr; +begin + ncomps := cinfo^.num_components; + + { Safety check to ensure start_compress not called yet. } + if (cinfo^.global_state <> CSTATE_START) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); + + { Figure space needed for script. Calculation must match code below! } + if (ncomps = 3) and (cinfo^.jpeg_color_space = JCS_YCbCr) then + begin + { Custom script for YCbCr color images. } + nscans := 10; + end + else + begin + { All-purpose script for other color spaces. } + if (ncomps > MAX_COMPS_IN_SCAN) then + nscans := 6 * ncomps { 2 DC + 4 AC scans per component } + else + nscans := 2 + 4 * ncomps; { 2 DC scans; 4 AC scans per component } + end; + + { 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 = NIL) or (cinfo^.script_space_size < nscans) then + begin + if nscans > 10 then + cinfo^.script_space_size := nscans + else + cinfo^.script_space_size := 10; + + cinfo^.script_space := jpeg_scan_info_ptr( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_PERMANENT, + cinfo^.script_space_size * SIZEOF(jpeg_scan_info)) ); + end; + scanptr := cinfo^.script_space; + + cinfo^.scan_info := scanptr; + cinfo^.num_scans := nscans; + + if (ncomps = 3) and (cinfo^.jpeg_color_space = JCS_YCbCr) then + begin + { 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); + end + else + begin + { 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); + end; +end; + +{$endif} +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjcphuff.pas b/resources/libraries/deskew/Imaging/JpegLib/imjcphuff.pas new file mode 100755 index 0000000..2b779ef --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjcphuff.pas @@ -0,0 +1,962 @@ +unit imjcphuff; + +{ 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. } + +{ Original: jcphuff.c; Copyright (C) 1995-1997, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjpeglib, + imjdeferr, + imjerror, + imjutils, + imjcomapi, + imjchuff; { Declarations shared with jchuff.c } + +{ Module initialization routine for progressive Huffman entropy encoding. } + +{GLOBAL} +procedure jinit_phuff_encoder (cinfo : j_compress_ptr); + +implementation + +{ Expanded entropy encoder object for progressive Huffman encoding. } +type + phuff_entropy_ptr = ^phuff_entropy_encoder; + phuff_entropy_encoder = record + pub : jpeg_entropy_encoder; { public fields } + + { Mode flag: TRUE for optimization, FALSE for actual data output } + gather_statistics : boolean; + + { Bit-level coding status. + next_output_byte/free_in_buffer are local copies of cinfo^.dest fields.} + + next_output_byte : JOCTETptr; { => next byte to write in buffer } + free_in_buffer : size_t; { # of byte spaces remaining in buffer } + put_buffer : INT32; { current bit-accumulation buffer } + put_bits : int; { # of bits now in it } + cinfo : j_compress_ptr; { link to cinfo (needed for dump_buffer) } + + { Coding status for DC components } + last_dc_val : array[0..MAX_COMPS_IN_SCAN-1] of int; + { last DC coef for each component } + + { Coding status for AC components } + ac_tbl_no : int; { the table number of the single component } + EOBRUN : uInt; { run length of EOBs } + BE : uInt; { # of buffered correction bits before MCU } + bit_buffer : JBytePtr; { buffer for correction bits (1 per char) } + { packing correction bits tightly would save some space but cost time... } + + restarts_to_go : uInt; { MCUs left in this restart interval } + next_restart_num : int; { 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. } + + derived_tbls : array[0..NUM_HUFF_TBLS-1] of c_derived_tbl_ptr; + + { Statistics tables for optimization; again, one set is enough } + count_ptrs : array[0..NUM_HUFF_TBLS-1] of TLongTablePtr; + end; + + +{ 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. } + +const + MAX_CORR_BITS = 1000; { Max # of correction bits I can buffer } + + +{ Forward declarations } +{METHODDEF} +function encode_mcu_DC_first (cinfo : j_compress_ptr; + const MCU_data: array of JBLOCKROW) : boolean; + forward; +{METHODDEF} +function encode_mcu_AC_first (cinfo : j_compress_ptr; + const MCU_data: array of JBLOCKROW) : boolean; + forward; +{METHODDEF} +function encode_mcu_DC_refine (cinfo : j_compress_ptr; + const MCU_data: array of JBLOCKROW) : boolean; + forward; +{METHODDEF} +function encode_mcu_AC_refine (cinfo : j_compress_ptr; + const MCU_data: array of JBLOCKROW) : boolean; + forward; + +{METHODDEF} +procedure finish_pass_phuff (cinfo : j_compress_ptr); forward; + +{METHODDEF} +procedure finish_pass_gather_phuff (cinfo : j_compress_ptr); forward; + + +{ Initialize for a Huffman-compressed scan using progressive JPEG. } + +{METHODDEF} +procedure start_pass_phuff (cinfo : j_compress_ptr; + gather_statistics : boolean); +var + entropy : phuff_entropy_ptr; + is_DC_band : boolean; + ci, tbl : int; + compptr : jpeg_component_info_ptr; +begin + tbl := 0; + entropy := phuff_entropy_ptr (cinfo^.entropy); + + 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) then + begin + if (is_DC_band) then + entropy^.pub.encode_mcu := encode_mcu_DC_first + else + entropy^.pub.encode_mcu := encode_mcu_AC_first; + end + else + begin + if (is_DC_band) then + entropy^.pub.encode_mcu := encode_mcu_DC_refine + else + begin + entropy^.pub.encode_mcu := encode_mcu_AC_refine; + { AC refinement needs a correction bit buffer } + if (entropy^.bit_buffer = NIL) then + entropy^.bit_buffer := JBytePtr( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + MAX_CORR_BITS * SIZEOF(byte)) ); + end; + end; + if (gather_statistics) then + 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 to pred(cinfo^.comps_in_scan) do + begin + compptr := cinfo^.cur_comp_info[ci]; + { Initialize DC predictions to 0 } + entropy^.last_dc_val[ci] := 0; + { Get table index } + if (is_DC_band) then + begin + if (cinfo^.Ah <> 0) then { DC refinement needs no table } + continue; + tbl := compptr^.dc_tbl_no; + end + else + begin + tbl := compptr^.ac_tbl_no; + entropy^.ac_tbl_no := tbl; + end; + if (gather_statistics) then + begin + { Check for invalid table index } + { (make_c_derived_tbl does this in the other path) } + if (tbl < 0) or (tbl >= NUM_HUFF_TBLS) then + ERREXIT1(j_common_ptr(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] = NIL) then + entropy^.count_ptrs[tbl] := TLongTablePtr( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + 257 * SIZEOF(long)) ); + MEMZERO(entropy^.count_ptrs[tbl], 257 * SIZEOF(long)); + end else + begin + { 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]); + end; + end; + + { 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; +end; + + + + +{LOCAL} +procedure dump_buffer (entropy : phuff_entropy_ptr); +{ Empty the output buffer; we do not support suspension in this module. } +var + dest : jpeg_destination_mgr_ptr; +begin + dest := entropy^.cinfo^.dest; + + if (not dest^.empty_output_buffer (entropy^.cinfo)) then + ERREXIT(j_common_ptr(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; +end; + + +{ 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. } + + +{LOCAL} +procedure emit_bits (entropy : phuff_entropy_ptr; + code : uInt; + size : int); {INLINE} +{ Emit some bits, unless we are in gather mode } +var + {register} put_buffer : INT32; + {register} put_bits : int; +var + c : int; +begin + { This routine is heavily used, so it's worth coding tightly. } + put_buffer := INT32 (code); + put_bits := entropy^.put_bits; + + { if size is 0, caller used an invalid Huffman table entry } + if (size = 0) then + ERREXIT(j_common_ptr(entropy^.cinfo), JERR_HUFF_MISSING_CODE); + + if (entropy^.gather_statistics) then + exit; { do nothing if we're only getting stats } + + put_buffer := put_buffer and ((INT32(1) shl size) - 1); + { mask off any extra bits in code } + + Inc(put_bits, size); { new number of bits in buffer } + + put_buffer := put_buffer shl (24 - put_bits); { align incoming bits } + + put_buffer := put_buffer or entropy^.put_buffer; + { and merge with old buffer contents } + + while (put_bits >= 8) do + begin + c := int ((put_buffer shr 16) and $FF); + + {emit_byte(entropy, c);} + { Outputting bytes to the file. + NB: these must be called only when actually outputting, + that is, entropy^.gather_statistics = FALSE. } + { Emit a byte } + entropy^.next_output_byte^ := JOCTET(c); + Inc(entropy^.next_output_byte); + Dec(entropy^.free_in_buffer); + if (entropy^.free_in_buffer = 0) then + dump_buffer(entropy); + + if (c = $FF) then + begin { need to stuff a zero byte? } + {emit_byte(entropy, 0);} + entropy^.next_output_byte^ := JOCTET(0); + Inc(entropy^.next_output_byte); + Dec(entropy^.free_in_buffer); + if (entropy^.free_in_buffer = 0) then + dump_buffer(entropy); + end; + put_buffer := put_buffer shl 8; + Dec(put_bits, 8); + end; + + entropy^.put_buffer := put_buffer; { update variables } + entropy^.put_bits := put_bits; +end; + + +{LOCAL} +procedure flush_bits (entropy : phuff_entropy_ptr); +begin + emit_bits(entropy, $7F, 7); { fill any partial byte with ones } + entropy^.put_buffer := 0; { and reset bit-buffer to empty } + entropy^.put_bits := 0; +end; + +{ Emit (or just count) a Huffman symbol. } + + +{LOCAL} +procedure emit_symbol (entropy : phuff_entropy_ptr; + tbl_no : int; + symbol : int); {INLINE} +var + tbl : c_derived_tbl_ptr; +begin + if (entropy^.gather_statistics) then + Inc(entropy^.count_ptrs[tbl_no]^[symbol]) + else + begin + tbl := entropy^.derived_tbls[tbl_no]; + emit_bits(entropy, tbl^.ehufco[symbol], tbl^.ehufsi[symbol]); + end; +end; + + +{ Emit bits from a correction bit buffer. } + +{LOCAL} +procedure emit_buffered_bits (entropy : phuff_entropy_ptr; + bufstart : JBytePtr; + nbits : uInt); +var + bufptr : byteptr; +begin + if (entropy^.gather_statistics) then + exit; { no real work } + + bufptr := byteptr(bufstart); + while (nbits > 0) do + begin + emit_bits(entropy, uInt(bufptr^), 1); + Inc(bufptr); + Dec(nbits); + end; +end; + + +{ Emit any pending EOBRUN symbol. } + +{LOCAL} +procedure emit_eobrun (entropy : phuff_entropy_ptr); +var + {register} temp, nbits : int; +begin + if (entropy^.EOBRUN > 0) then + begin { if there is any pending EOBRUN } + temp := entropy^.EOBRUN; + nbits := 0; + temp := temp shr 1; + while (temp <> 0) do + begin + Inc(nbits); + temp := temp shr 1; + end; + + { safety check: shouldn't happen given limited correction-bit buffer } + if (nbits > 14) then + ERREXIT(j_common_ptr(entropy^.cinfo), JERR_HUFF_MISSING_CODE); + + emit_symbol(entropy, entropy^.ac_tbl_no, nbits shl 4); + if (nbits <> 0) then + 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; + end; +end; + + +{ Emit a restart marker & resynchronize predictions. } + +{LOCAL} +procedure emit_restart (entropy : phuff_entropy_ptr; + restart_num : int); +var + ci : int; +begin + emit_eobrun(entropy); + + if (not entropy^.gather_statistics) then + begin + flush_bits(entropy); + {emit_byte(entropy, $FF);} + { Outputting bytes to the file. + NB: these must be called only when actually outputting, + that is, entropy^.gather_statistics = FALSE. } + + entropy^.next_output_byte^ := JOCTET($FF); + Inc(entropy^.next_output_byte); + Dec(entropy^.free_in_buffer); + if (entropy^.free_in_buffer = 0) then + dump_buffer(entropy); + + {emit_byte(entropy, JPEG_RST0 + restart_num);} + entropy^.next_output_byte^ := JOCTET(JPEG_RST0 + restart_num); + Inc(entropy^.next_output_byte); + Dec(entropy^.free_in_buffer); + if (entropy^.free_in_buffer = 0) then + dump_buffer(entropy); + end; + + if (entropy^.cinfo^.Ss = 0) then + begin + { Re-initialize DC predictions to 0 } + for ci := 0 to pred(entropy^.cinfo^.comps_in_scan) do + entropy^.last_dc_val[ci] := 0; + end + else + begin + { Re-initialize all AC-related fields to 0 } + entropy^.EOBRUN := 0; + entropy^.BE := 0; + end; +end; + + +{ MCU encoding for DC initial scan (either spectral selection, + or first pass of successive approximation). } + +{METHODDEF} +function encode_mcu_DC_first (cinfo : j_compress_ptr; + const MCU_data: array of JBLOCKROW) : boolean; +var + entropy : phuff_entropy_ptr; + {register} temp, temp2 : int; + {register} nbits : int; + blkn, ci : int; + Al : int; + block : JBLOCK_PTR; + compptr : jpeg_component_info_ptr; + ishift_temp : int; +begin + entropy := phuff_entropy_ptr (cinfo^.entropy); + Al := cinfo^.Al; + + 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 <> 0) then + if (entropy^.restarts_to_go = 0) then + emit_restart(entropy, entropy^.next_restart_num); + + { Encode the MCU data blocks } + for blkn := 0 to pred(cinfo^.blocks_in_MCU) do + begin + block := JBLOCK_PTR(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);} + {IRIGHT_SHIFT_IS_UNSIGNED} + ishift_temp := int(block^[0]); + if ishift_temp < 0 then + temp2 := (ishift_temp shr Al) or ((not 0) shl (16-Al)) + else + temp2 := ishift_temp shr 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) then + begin + 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 } + Dec(temp2); + end; + + { Find the number of bits needed for the magnitude of the coefficient } + nbits := 0; + while (temp <> 0) do + begin + Inc(nbits); + temp := temp shr 1; + end; + + { 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) then + ERREXIT(j_common_ptr(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 <> 0) then { emit_bits rejects calls with size 0 } + emit_bits(entropy, uInt(temp2), nbits); + end; + + 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 <> 0) then + begin + if (entropy^.restarts_to_go = 0) then + begin + entropy^.restarts_to_go := cinfo^.restart_interval; + Inc(entropy^.next_restart_num); + with entropy^ do + next_restart_num := next_restart_num and 7; + end; + Dec(entropy^.restarts_to_go); + end; + + encode_mcu_DC_first := TRUE; +end; + + +{ MCU encoding for AC initial scan (either spectral selection, + or first pass of successive approximation). } + +{METHODDEF} +function encode_mcu_AC_first (cinfo : j_compress_ptr; + const MCU_data: array of JBLOCKROW) : boolean; +var + entropy : phuff_entropy_ptr; + {register} temp, temp2 : int; + {register} nbits : int; + {register} r, k : int; + Se : int; + Al : int; + block : JBLOCK_PTR; +begin + entropy := phuff_entropy_ptr (cinfo^.entropy); + Se := cinfo^.Se; + Al := cinfo^.Al; + + 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 <> 0) then + if (entropy^.restarts_to_go = 0) then + emit_restart(entropy, entropy^.next_restart_num); + + { Encode the MCU data block } + block := JBLOCK_PTR(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 to Se do + begin + temp := (block^[jpeg_natural_order[k]]); + if (temp = 0) then + begin + Inc(r); + continue; + end; + { 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) then + begin + temp := -temp; { temp is abs value of input } + temp := temp shr Al; { apply the point transform } + { For a negative coef, want temp2 := bitwise complement of abs(coef) } + temp2 := not temp; + end + else + begin + temp := temp shr Al; { apply the point transform } + temp2 := temp; + end; + { Watch out for case that nonzero coef is zero after point transform } + if (temp = 0) then + begin + Inc(r); + continue; + end; + + { Emit any pending EOBRUN } + if (entropy^.EOBRUN > 0) then + emit_eobrun(entropy); + { if run length > 15, must emit special run-length-16 codes ($F0) } + while (r > 15) do + begin + emit_symbol(entropy, entropy^.ac_tbl_no, $F0); + Dec(r, 16); + end; + + { Find the number of bits needed for the magnitude of the coefficient } + nbits := 0; { there must be at least one 1 bit } + repeat + Inc(nbits); + temp := temp shr 1; + until (temp = 0); + + { Check for out-of-range coefficient values } + if (nbits > MAX_COEF_BITS) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_DCT_COEF); + + { Count/emit Huffman symbol for run length / number of bits } + emit_symbol(entropy, entropy^.ac_tbl_no, (r shl 4) + nbits); + + { Emit that number of bits of the value, if positive, } + { or the complement of its magnitude, if negative. } + emit_bits(entropy, uInt(temp2), nbits); + + r := 0; { reset zero run length } + end; + + if (r > 0) then + begin { If there are trailing zeroes, } + Inc(entropy^.EOBRUN); { count an EOB } + if (entropy^.EOBRUN = $7FFF) then + emit_eobrun(entropy); { force it out to avoid overflow } + end; + + 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 <> 0) then + begin + if (entropy^.restarts_to_go = 0) then + begin + entropy^.restarts_to_go := cinfo^.restart_interval; + Inc(entropy^.next_restart_num); + with entropy^ do + next_restart_num := next_restart_num and 7; + end; + Dec(entropy^.restarts_to_go); + end; + + encode_mcu_AC_first := TRUE; +end; + + +{ 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} +function encode_mcu_DC_refine (cinfo : j_compress_ptr; + const MCU_data: array of JBLOCKROW) : boolean; +var + entropy : phuff_entropy_ptr; + {register} temp : int; + blkn : int; + Al : int; + block : JBLOCK_PTR; +begin + entropy := phuff_entropy_ptr (cinfo^.entropy); + Al := cinfo^.Al; + + 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 <> 0) then + if (entropy^.restarts_to_go = 0) then + emit_restart(entropy, entropy^.next_restart_num); + + { Encode the MCU data blocks } + for blkn := 0 to pred(cinfo^.blocks_in_MCU) do + begin + block := JBLOCK_PTR(MCU_data[blkn]); + + { We simply emit the Al'th bit of the DC coefficient value. } + temp := block^[0]; + emit_bits(entropy, uInt(temp shr Al), 1); + end; + + 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 <> 0) then + begin + if (entropy^.restarts_to_go = 0) then + begin + entropy^.restarts_to_go := cinfo^.restart_interval; + Inc(entropy^.next_restart_num); + with entropy^ do + next_restart_num := next_restart_num and 7; + end; + Dec(entropy^.restarts_to_go); + end; + + encode_mcu_DC_refine := TRUE; +end; + + +{ MCU encoding for AC successive approximation refinement scan. } + +{METHODDEF} +function encode_mcu_AC_refine (cinfo : j_compress_ptr; + const MCU_data: array of JBLOCKROW) : boolean; + +var + entropy : phuff_entropy_ptr; + {register} temp : int; + {register} r, k : int; + EOB : int; + BR_buffer : JBytePtr; + BR : uInt; + Se : int; + Al : int; + block : JBLOCK_PTR; + absvalues : array[0..DCTSIZE2-1] of int; +begin + entropy := phuff_entropy_ptr(cinfo^.entropy); + Se := cinfo^.Se; + Al := cinfo^.Al; + + 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 <> 0) then + if (entropy^.restarts_to_go = 0) then + emit_restart(entropy, entropy^.next_restart_num); + + { Encode the MCU data block } + block := JBLOCK_PTR(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 to Se do + begin + 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) then + temp := -temp; { temp is abs value of input } + temp := temp shr Al; { apply the point transform } + absvalues[k] := temp; { save abs value for main pass } + if (temp = 1) then + EOB := k; { EOB := index of last newly-nonzero coef } + end; + + { 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 := JBytePtr(@(entropy^.bit_buffer^[entropy^.BE])); + { Append bits to buffer } + + for k := cinfo^.Ss to Se do + begin + temp := absvalues[k]; + if (temp = 0) then + begin + Inc(r); + continue; + end; + + { Emit any required ZRLs, but not if they can be folded into EOB } + while (r > 15) and (k <= EOB) do + begin + { emit any pending EOBRUN and the BE correction bits } + emit_eobrun(entropy); + { Emit ZRL } + emit_symbol(entropy, entropy^.ac_tbl_no, $F0); + Dec(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; + end; + + { 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) then + begin + { The correction bit is the next bit of the absolute value. } + BR_buffer^[BR] := byte (temp and 1); + Inc(BR); + continue; + end; + + { 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 shl 4) + 1); + + { Emit output bit for newly-nonzero coef } + if (block^[jpeg_natural_order[k]] < 0) then + temp := 0 + else + temp := 1; + emit_bits(entropy, uInt(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 } + end; + + if (r > 0) or (BR > 0) then + begin { If there are trailing zeroes, } + Inc(entropy^.EOBRUN); { count an EOB } + Inc(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 = $7FFF) or + (entropy^.BE > (MAX_CORR_BITS-DCTSIZE2+1)) then + emit_eobrun(entropy); + end; + + 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 <> 0) then + begin + if (entropy^.restarts_to_go = 0) then + begin + entropy^.restarts_to_go := cinfo^.restart_interval; + Inc(entropy^.next_restart_num); + with entropy^ do + next_restart_num := next_restart_num and 7; + end; + Dec(entropy^.restarts_to_go); + end; + + encode_mcu_AC_refine := TRUE; +end; + + +{ Finish up at the end of a Huffman-compressed progressive scan. } + +{METHODDEF} +procedure finish_pass_phuff (cinfo : j_compress_ptr); +var + entropy : phuff_entropy_ptr; +begin + 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; +end; + + +{ Finish up a statistics-gathering pass and create the new Huffman tables. } + +{METHODDEF} +procedure finish_pass_gather_phuff (cinfo : j_compress_ptr); +var + entropy : phuff_entropy_ptr; + is_DC_band : boolean; + ci, tbl : int; + compptr : jpeg_component_info_ptr; + htblptr : ^JHUFF_TBL_PTR; + did : array[0..NUM_HUFF_TBLS-1] of boolean; +begin + tbl := 0; + entropy := phuff_entropy_ptr (cinfo^.entropy); + + { 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 to pred(cinfo^.comps_in_scan) do + begin + compptr := cinfo^.cur_comp_info[ci]; + if (is_DC_band) then + begin + if (cinfo^.Ah <> 0) then { DC refinement needs no table } + continue; + tbl := compptr^.dc_tbl_no; + end + else + begin + tbl := compptr^.ac_tbl_no; + end; + if (not did[tbl]) then + begin + if (is_DC_band) then + htblptr := @(cinfo^.dc_huff_tbl_ptrs[tbl]) + else + htblptr := @(cinfo^.ac_huff_tbl_ptrs[tbl]); + if (htblptr^ = NIL) then + htblptr^ := jpeg_alloc_huff_table(j_common_ptr(cinfo)); + jpeg_gen_optimal_table(cinfo, htblptr^, entropy^.count_ptrs[tbl]^); + did[tbl] := TRUE; + end; + end; +end; + + +{ Module initialization routine for progressive Huffman entropy encoding. } + +{GLOBAL} +procedure jinit_phuff_encoder (cinfo : j_compress_ptr); +var + entropy : phuff_entropy_ptr; + i : int; +begin + entropy := phuff_entropy_ptr( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + SIZEOF(phuff_entropy_encoder)) ); + cinfo^.entropy := jpeg_entropy_encoder_ptr(entropy); + entropy^.pub.start_pass := start_pass_phuff; + + { Mark tables unallocated } + for i := 0 to pred(NUM_HUFF_TBLS) do + begin + entropy^.derived_tbls[i] := NIL; + entropy^.count_ptrs[i] := NIL; + end; + entropy^.bit_buffer := NIL; { needed only in AC refinement scan } +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjcprepct.pas b/resources/libraries/deskew/Imaging/JpegLib/imjcprepct.pas new file mode 100755 index 0000000..9750652 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjcprepct.pas @@ -0,0 +1,406 @@ +unit imjcprepct; + +{ Original : jcprepct.c ; Copyright (C) 1994-1996, Thomas G. Lane. } + +{ 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. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjpeglib, + imjdeferr, + imjerror, + imjinclude, + imjutils; + +{GLOBAL} +procedure jinit_c_prep_controller (cinfo : j_compress_ptr; + need_full_buffer : boolean); + +implementation + + +{ 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 } + +type + my_prep_ptr = ^my_prep_controller; + my_prep_controller = record + pub : jpeg_c_prep_controller; { public fields } + + { Downsampling input buffer. This buffer holds color-converted data + until we have enough to do a downsample step. } + + color_buf : array[0..MAX_COMPONENTS-1] of JSAMPARRAY; + + rows_to_go : JDIMENSION; { counts rows remaining in source image } + next_buf_row : int; { index of next row to store in color_buf } + + {$ifdef CONTEXT_ROWS_SUPPORTED} { only needed for context case } + this_row_group : int; { starting row index of group to process } + next_buf_stop : int; { downsample when we reach this index } + {$endif} + end; {my_prep_controller;} + + +{ Initialize for a processing pass. } + +{METHODDEF} +procedure start_pass_prep (cinfo : j_compress_ptr; + pass_mode : J_BUF_MODE ); +var + prep : my_prep_ptr; +begin + prep := my_prep_ptr (cinfo^.prep); + + if (pass_mode <> JBUF_PASS_THRU) then + ERREXIT(j_common_ptr(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} +end; + + +{ Expand an image vertically from height input_rows to height output_rows, + by duplicating the bottom row. } + +{LOCAL} +procedure expand_bottom_edge (image_data : JSAMPARRAY; + num_cols : JDIMENSION; + input_rows : int; + output_rows : int); +var + {register} row : int; +begin + for row := input_rows to pred(output_rows) do + begin + jcopy_sample_rows(image_data, input_rows-1, image_data, row, + 1, num_cols); + end; +end; + + +{ 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} +procedure pre_process_data (cinfo : j_compress_ptr; + input_buf : JSAMPARRAY; + var in_row_ctr : JDIMENSION; + in_rows_avail : JDIMENSION; + output_buf : JSAMPIMAGE; + var out_row_group_ctr : JDIMENSION; + out_row_groups_avail : JDIMENSION); +var + prep : my_prep_ptr; + numrows, ci : int; + inrows : JDIMENSION; + compptr : jpeg_component_info_ptr; +var + local_input_buf : JSAMPARRAY; +begin + prep := my_prep_ptr (cinfo^.prep); + + while (in_row_ctr < in_rows_avail) and + (out_row_group_ctr < out_row_groups_avail) do + begin + { 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) );} + if inrows < JDIMENSION(numrows) then + numrows := int(inrows); + local_input_buf := JSAMPARRAY(@(input_buf^[in_row_ctr])); + cinfo^.cconvert^.color_convert (cinfo, local_input_buf, + JSAMPIMAGE(@prep^.color_buf), + JDIMENSION(prep^.next_buf_row), + numrows); + Inc(in_row_ctr, numrows); + Inc(prep^.next_buf_row, numrows); + Dec(prep^.rows_to_go, numrows); + { If at bottom of image, pad to fill the conversion buffer. } + if (prep^.rows_to_go = 0) and + (prep^.next_buf_row < cinfo^.max_v_samp_factor) then + begin + for ci := 0 to pred(cinfo^.num_components) do + begin + expand_bottom_edge(prep^.color_buf[ci], cinfo^.image_width, + prep^.next_buf_row, cinfo^.max_v_samp_factor); + end; + prep^.next_buf_row := cinfo^.max_v_samp_factor; + end; + { If we've filled the conversion buffer, empty it. } + if (prep^.next_buf_row = cinfo^.max_v_samp_factor) then + begin + cinfo^.downsample^.downsample (cinfo, + JSAMPIMAGE(@prep^.color_buf), + JDIMENSION (0), + output_buf, + out_row_group_ctr); + prep^.next_buf_row := 0; + Inc(out_row_group_ctr);; + end; + { 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) and + (out_row_group_ctr < out_row_groups_avail) then + begin + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + 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); + Inc(compptr); + end; + out_row_group_ctr := out_row_groups_avail; + break; { can exit outer loop without test } + end; + end; +end; + + +{$ifdef CONTEXT_ROWS_SUPPORTED} + +{ Process some data in the context case. } + +{METHODDEF} +procedure pre_process_context (cinfo : j_compress_ptr; + input_buf : JSAMPARRAY; + var in_row_ctr : JDIMENSION; + in_rows_avail : JDIMENSION; + output_buf : JSAMPIMAGE; + var out_row_group_ctr : JDIMENSION; + out_row_groups_avail : JDIMENSION); +var + prep : my_prep_ptr; + numrows, ci : int; + buf_height : int; + inrows : JDIMENSION; +var + row : int; + +begin + prep := my_prep_ptr (cinfo^.prep); + buf_height := cinfo^.max_v_samp_factor * 3; + + while (out_row_group_ctr < out_row_groups_avail) do + begin + if (in_row_ctr < in_rows_avail) then + begin + { 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) );} + if inrows < JDIMENSION(numrows) then + numrows := int(inrows); + cinfo^.cconvert^.color_convert (cinfo, + JSAMPARRAY(@input_buf^[in_row_ctr]), + JSAMPIMAGE(@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) then + begin + for ci := 0 to pred(cinfo^.num_components) do + begin + for row := 1 to cinfo^.max_v_samp_factor do + begin + jcopy_sample_rows(prep^.color_buf[ci], 0, + prep^.color_buf[ci], -row, + 1, cinfo^.image_width); + end; + end; + end; + Inc(in_row_ctr, numrows); + Inc(prep^.next_buf_row, numrows); + Dec(prep^.rows_to_go, numrows); + end + else + begin + { Return for more data, unless we are at the bottom of the image. } + if (prep^.rows_to_go <> 0) then + break; + { When at bottom of image, pad to fill the conversion buffer. } + if (prep^.next_buf_row < prep^.next_buf_stop) then + begin + for ci := 0 to pred(cinfo^.num_components) do + begin + expand_bottom_edge(prep^.color_buf[ci], cinfo^.image_width, + prep^.next_buf_row, prep^.next_buf_stop); + end; + prep^.next_buf_row := prep^.next_buf_stop; + end; + end; + { If we've gotten enough data, downsample a row group. } + if (prep^.next_buf_row = prep^.next_buf_stop) then + begin + cinfo^.downsample^.downsample (cinfo, + JSAMPIMAGE(@prep^.color_buf), + JDIMENSION(prep^.this_row_group), + output_buf, + out_row_group_ctr); + Inc(out_row_group_ctr); + { Advance pointers with wraparound as necessary. } + Inc(prep^.this_row_group, cinfo^.max_v_samp_factor); + if (prep^.this_row_group >= buf_height) then + prep^.this_row_group := 0; + if (prep^.next_buf_row >= buf_height) then + prep^.next_buf_row := 0; + prep^.next_buf_stop := prep^.next_buf_row + cinfo^.max_v_samp_factor; + end; + end; +end; + + +{ Create the wrapped-around downsampling input buffer needed for context mode. } + +{LOCAL} +procedure create_context_buffer (cinfo : j_compress_ptr); +var + prep : my_prep_ptr; + rgroup_height : int; + ci, i : int; + compptr : jpeg_component_info_ptr; + true_buffer, fake_buffer : JSAMPARRAY; +begin + prep := my_prep_ptr (cinfo^.prep); + rgroup_height := cinfo^.max_v_samp_factor; + { 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)) ); + + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + { 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) div compptr^.h_samp_factor), + JDIMENSION (3 * rgroup_height)); + { Copy true buffer row pointers into the middle of the fake row array } + MEMCOPY(JSAMPARRAY(@ fake_buffer^[rgroup_height]), true_buffer, + 3 * rgroup_height * SIZEOF(JSAMPROW)); + { Fill in the above and below wraparound pointers } + for i := 0 to pred(rgroup_height) do + begin + fake_buffer^[i] := true_buffer^[2 * rgroup_height + i]; + fake_buffer^[4 * rgroup_height + i] := true_buffer^[i]; + end; + prep^.color_buf[ci] := JSAMPARRAY(@ fake_buffer^[rgroup_height]); + Inc(JSAMPROW_PTR(fake_buffer), 5 * rgroup_height); { point to space for next component } + Inc(compptr); + end; +end; + +{$endif} { CONTEXT_ROWS_SUPPORTED } + + +{ Initialize preprocessing controller. } + +{GLOBAL} +procedure jinit_c_prep_controller (cinfo : j_compress_ptr; + need_full_buffer : boolean); +var + prep : my_prep_ptr; + ci : int; + compptr : jpeg_component_info_ptr; +begin + + if (need_full_buffer) then { safety check } + ERREXIT(j_common_ptr(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 := jpeg_c_prep_controller_ptr(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) then + begin + { Set up to provide context rows } +{$ifdef CONTEXT_ROWS_SUPPORTED} + prep^.pub.pre_process_data := pre_process_context; + create_context_buffer(cinfo); +{$else} + ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); +{$endif} + end + else + begin + { No context, just make it tall enough for one row group } + prep^.pub.pre_process_data := pre_process_data; + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + 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) div compptr^.h_samp_factor), + JDIMENSION(cinfo^.max_v_samp_factor) ); + Inc(compptr); + end; + end; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjcsample.pas b/resources/libraries/deskew/Imaging/JpegLib/imjcsample.pas new file mode 100755 index 0000000..5be2e80 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjcsample.pas @@ -0,0 +1,631 @@ +unit imjcsample; + +{ 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. } + +{ Original: jcsample.c ; Copyright (C) 1991-1996, Thomas G. Lane. } + + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjutils, + imjdeferr, + imjerror, + imjpeglib; + + +{ Module initialization routine for downsampling. + Note that we must select a routine for each component. } + +{GLOBAL} +procedure jinit_downsampler (cinfo : j_compress_ptr); + +implementation + +{ Pointer to routine to downsample a single component } +type + downsample1_ptr = procedure(cinfo : j_compress_ptr; + compptr : jpeg_component_info_ptr; + input_data : JSAMPARRAY; + output_data : JSAMPARRAY); + +{ Private subobject } + +type + my_downsample_ptr = ^my_downsampler; + my_downsampler = record + pub : jpeg_downsampler; { public fields } + + { Downsampling method pointers, one per component } + methods : array[0..MAX_COMPONENTS-1] of downsample1_ptr; + end; + +{ Initialize for a downsampling pass. } + +{METHODDEF} +procedure start_pass_downsample (cinfo : j_compress_ptr); +begin + { no work for now } +end; + + +{ Expand a component horizontally from width input_cols to width output_cols, + by duplicating the rightmost samples. } + +{LOCAL} +procedure expand_right_edge (image_data : JSAMPARRAY; + num_rows : int; + input_cols : JDIMENSION; + output_cols : JDIMENSION); +var + {register} ptr : JSAMPLE_PTR; + {register} pixval : JSAMPLE; + {register} count : int; + row : int; + numcols : int; +begin + numcols := int (output_cols - input_cols); + + if (numcols > 0) then + begin + for row := 0 to pred(num_rows) do + begin + ptr := JSAMPLE_PTR(@(image_data^[row]^[input_cols-1])); + pixval := ptr^; { don't need GETJSAMPLE() here } + for count := pred(numcols) downto 0 do + begin + Inc(ptr); + ptr^ := pixval; + end; + end; + end; +end; + + +{ Do downsampling for a whole row group (all components). + + In this version we simply downsample each component independently. } + +{METHODDEF} +procedure sep_downsample (cinfo : j_compress_ptr; + input_buf : JSAMPIMAGE; + in_row_index : JDIMENSION; + output_buf : JSAMPIMAGE; + out_row_group_index : JDIMENSION); +var + downsample : my_downsample_ptr; + ci : int; + compptr : jpeg_component_info_ptr; + in_ptr, out_ptr : JSAMPARRAY; +begin + downsample := my_downsample_ptr (cinfo^.downsample); + + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + in_ptr := JSAMPARRAY(@ input_buf^[ci]^[in_row_index]); + out_ptr := JSAMPARRAY(@ output_buf^[ci]^ + [out_row_group_index * JDIMENSION(compptr^.v_samp_factor)]); + downsample^.methods[ci] (cinfo, compptr, in_ptr, out_ptr); + Inc(compptr); + end; +end; + + +{ 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} +procedure int_downsample (cinfo : j_compress_ptr; + compptr : jpeg_component_info_ptr; + input_data : JSAMPARRAY; + output_data : JSAMPARRAY); +var + inrow, outrow, h_expand, v_expand, numpix, numpix2, h, v : int; + outcol, outcol_h : JDIMENSION; { outcol_h = outcol*h_expand } + output_cols : JDIMENSION; + inptr, + outptr : JSAMPLE_PTR; + outvalue : INT32; +begin + output_cols := compptr^.width_in_blocks * DCTSIZE; + + h_expand := cinfo^.max_h_samp_factor div compptr^.h_samp_factor; + v_expand := cinfo^.max_v_samp_factor div compptr^.v_samp_factor; + numpix := h_expand * v_expand; + numpix2 := numpix div 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 * JDIMENSION(h_expand)); + + inrow := 0; + for outrow := 0 to pred(compptr^.v_samp_factor) do + begin + outptr := JSAMPLE_PTR(output_data^[outrow]); + outcol_h := 0; + for outcol := 0 to pred(output_cols) do + begin + outvalue := 0; + for v := 0 to pred(v_expand) do + begin + inptr := @(input_data^[inrow+v]^[outcol_h]); + for h := 0 to pred(h_expand) do + begin + Inc(outvalue, INT32 (GETJSAMPLE(inptr^)) ); + Inc(inptr); + end; + end; + outptr^ := JSAMPLE ((outvalue + numpix2) div numpix); + Inc(outptr); + Inc(outcol_h, h_expand); + end; + Inc(inrow, v_expand); + end; +end; + + +{ Downsample pixel values of a single component. + This version handles the special case of a full-size component, + without smoothing. } + +{METHODDEF} +procedure fullsize_downsample (cinfo : j_compress_ptr; + compptr : jpeg_component_info_ptr; + input_data : JSAMPARRAY; + output_data : JSAMPARRAY); +begin + { 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); +end; + + +{ 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} +procedure h2v1_downsample (cinfo : j_compress_ptr; + compptr : jpeg_component_info_ptr; + input_data : JSAMPARRAY; + output_data : JSAMPARRAY); +var + outrow : int; + outcol : JDIMENSION; + output_cols : JDIMENSION; + {register} inptr, outptr : JSAMPLE_PTR; + {register} bias : int; +begin + output_cols := compptr^.width_in_blocks * DCTSIZE; + + { 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 to pred(compptr^.v_samp_factor) do + begin + outptr := JSAMPLE_PTR(output_data^[outrow]); + inptr := JSAMPLE_PTR(input_data^[outrow]); + bias := 0; { bias := 0,1,0,1,... for successive samples } + for outcol := 0 to pred(output_cols) do + begin + outptr^ := JSAMPLE ((GETJSAMPLE(inptr^) + + GETJSAMPLE(JSAMPROW(inptr)^[1]) + bias) shr 1); + Inc(outptr); + bias := bias xor 1; { 0=>1, 1=>0 } + Inc(inptr, 2); + end; + end; +end; + + +{ Downsample pixel values of a single component. + This version handles the standard case of 2:1 horizontal and 2:1 vertical, + without smoothing. } + +{METHODDEF} +procedure h2v2_downsample (cinfo : j_compress_ptr; + compptr : jpeg_component_info_ptr; + input_data : JSAMPARRAY; + output_data : JSAMPARRAY); +var + inrow, outrow : int; + outcol : JDIMENSION; + output_cols : JDIMENSION; + {register} inptr0, inptr1, outptr : JSAMPLE_PTR; + {register} bias : int; +begin + output_cols := compptr^.width_in_blocks * DCTSIZE; + + { 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 to pred(compptr^.v_samp_factor) do + begin + outptr := JSAMPLE_PTR(output_data^[outrow]); + inptr0 := JSAMPLE_PTR(input_data^[inrow]); + inptr1 := JSAMPLE_PTR(input_data^[inrow+1]); + bias := 1; { bias := 1,2,1,2,... for successive samples } + for outcol := 0 to pred(output_cols) do + begin + outptr^ := JSAMPLE ((GETJSAMPLE(inptr0^) + + GETJSAMPLE(JSAMPROW(inptr0)^[1]) + + GETJSAMPLE(inptr1^) + + GETJSAMPLE(JSAMPROW(inptr1)^[1]) + bias) shr 2); + Inc(outptr); + bias := bias xor 3; { 1=>2, 2=>1 } + Inc(inptr0, 2); + Inc(inptr1, 2); + end; + Inc(inrow, 2); + end; +end; + + +{$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} +procedure h2v2_smooth_downsample (cinfo : j_compress_ptr; + compptr : jpeg_component_info_ptr; + input_data : JSAMPARRAY; + output_data : JSAMPARRAY); +var + inrow, outrow : int; + colctr : JDIMENSION; + output_cols : JDIMENSION; + {register} inptr0, inptr1, above_ptr, below_ptr, outptr : JSAMPLE_PTR; + membersum, neighsum, memberscale, neighscale : INT32; +var + prev_input_data : JSAMPARRAY; + prev_inptr0, prev_inptr1, prev_above_ptr, prev_below_ptr : JSAMPLE_PTR; +begin + output_cols := compptr^.width_in_blocks * DCTSIZE; + + { Expand input data enough to let all the output samples be generated + by the standard loop. Special-casing padded output would be more + efficient. } + + prev_input_data := input_data; + Dec(JSAMPROW_PTR(prev_input_data)); + expand_right_edge(prev_input_data, 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 to pred(compptr^.v_samp_factor) do + begin + outptr := JSAMPLE_PTR(output_data^[outrow]); + inptr0 := JSAMPLE_PTR(input_data^[inrow]); + inptr1 := JSAMPLE_PTR(input_data^[inrow+1]); + above_ptr := JSAMPLE_PTR(input_data^[inrow-1]); + below_ptr := JSAMPLE_PTR(input_data^[inrow+2]); + + { Special case for first column: pretend column -1 is same as column 0 } + membersum := GETJSAMPLE(inptr0^) + GETJSAMPLE(JSAMPROW(inptr0)^[1]) + + GETJSAMPLE(inptr1^) + GETJSAMPLE(JSAMPROW(inptr1)^[1]); + neighsum := GETJSAMPLE(above_ptr^) + GETJSAMPLE(JSAMPROW(above_ptr)^[1]) + + GETJSAMPLE(below_ptr^) + GETJSAMPLE(JSAMPROW(below_ptr)^[1]) + + GETJSAMPLE(inptr0^) + GETJSAMPLE(JSAMPROW(inptr0)^[2]) + + GETJSAMPLE(inptr1^) + GETJSAMPLE(JSAMPROW(inptr1)^[2]); + Inc(neighsum, neighsum); + Inc(neighsum, GETJSAMPLE(above_ptr^) + + GETJSAMPLE(JSAMPROW(above_ptr)^[2]) + + GETJSAMPLE(below_ptr^) + + GETJSAMPLE(JSAMPROW(below_ptr)^[2]) ); + membersum := membersum * memberscale + neighsum * neighscale; + outptr^ := JSAMPLE ((membersum + 32768) shr 16); + Inc(outptr); + prev_inptr0 := inptr0; + prev_inptr1 := inptr1; + Inc(prev_inptr0); + Inc(prev_inptr1); + Inc(inptr0, 2); + Inc(inptr1, 2); + prev_above_ptr := above_ptr; + prev_below_ptr := below_ptr; + Inc(above_ptr, 2); + Inc(below_ptr, 2); + Inc(prev_above_ptr, 1); + Inc(prev_below_ptr, 1); + + for colctr := pred(output_cols - 2) downto 0 do + begin + { sum of pixels directly mapped to this output element } + membersum := GETJSAMPLE(inptr0^) + GETJSAMPLE(JSAMPROW(inptr0)^[1]) + + GETJSAMPLE(inptr1^) + GETJSAMPLE(JSAMPROW(inptr1)^[1]); + { sum of edge-neighbor pixels } + neighsum := GETJSAMPLE(above_ptr^) + GETJSAMPLE(JSAMPROW(above_ptr)^[1]) + + GETJSAMPLE(below_ptr^) + GETJSAMPLE(JSAMPROW(below_ptr)^[1]) + + GETJSAMPLE(prev_inptr0^) + GETJSAMPLE(JSAMPROW(inptr0)^[2]) + + GETJSAMPLE(prev_inptr1^) + GETJSAMPLE(JSAMPROW(inptr1)^[2]); + { The edge-neighbors count twice as much as corner-neighbors } + Inc(neighsum, neighsum); + { Add in the corner-neighbors } + Inc(neighsum, GETJSAMPLE(prev_above_ptr^) + + GETJSAMPLE(JSAMPROW(above_ptr)^[2]) + + GETJSAMPLE(prev_below_ptr^) + + GETJSAMPLE(JSAMPROW(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) shr 16); + Inc(outptr); + Inc(inptr0, 2); + Inc(inptr1, 2); + Inc(prev_inptr0, 2); + Inc(prev_inptr1, 2); + Inc(above_ptr, 2); + Inc(below_ptr, 2); + Inc(prev_above_ptr, 2); + Inc(prev_below_ptr, 2); + end; + + { Special case for last column } + membersum := GETJSAMPLE(inptr0^) + GETJSAMPLE(JSAMPROW(inptr0)^[1]) + + GETJSAMPLE(inptr1^) + GETJSAMPLE(JSAMPROW(inptr1)^[1]); + neighsum := GETJSAMPLE(above_ptr^) + GETJSAMPLE(JSAMPROW(above_ptr)^[1]) + + GETJSAMPLE(below_ptr^) + GETJSAMPLE(JSAMPROW(below_ptr)^[1]) + + GETJSAMPLE(prev_inptr0^) + GETJSAMPLE(JSAMPROW(inptr0)^[1]) + + GETJSAMPLE(prev_inptr1^) + GETJSAMPLE(JSAMPROW(inptr1)^[1]); + Inc(neighsum, neighsum); + Inc(neighsum, GETJSAMPLE(prev_above_ptr^) + + GETJSAMPLE(JSAMPROW(above_ptr)^[1]) + + GETJSAMPLE(prev_below_ptr^) + + GETJSAMPLE(JSAMPROW(below_ptr)^[1]) ); + membersum := membersum * memberscale + neighsum * neighscale; + outptr^ := JSAMPLE ((membersum + 32768) shr 16); + + Inc(inrow, 2); + end; +end; + + +{ 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} +procedure fullsize_smooth_downsample (cinfo : j_compress_ptr; + compptr : jpeg_component_info_ptr; + input_data : JSAMPARRAY; + output_data : JSAMPARRAY); +var + outrow : int; + colctr : JDIMENSION; + output_cols : JDIMENSION; + {register} inptr, above_ptr, below_ptr, outptr : JSAMPLE_PTR; + membersum, neighsum, memberscale, neighscale : INT32; + colsum, lastcolsum, nextcolsum : int; +var + prev_input_data : JSAMPARRAY; +begin + output_cols := compptr^.width_in_blocks * DCTSIZE; + + { Expand input data enough to let all the output samples be generated + by the standard loop. Special-casing padded output would be more + efficient. } + + prev_input_data := input_data; + Dec(JSAMPROW_PTR(prev_input_data)); + expand_right_edge(prev_input_data, 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 := long(65536) - cinfo^.smoothing_factor * long(512); { scaled 1-8*SF } + neighscale := cinfo^.smoothing_factor * 64; { scaled SF } + + for outrow := 0 to pred(compptr^.v_samp_factor) do + begin + outptr := JSAMPLE_PTR(output_data^[outrow]); + inptr := JSAMPLE_PTR(input_data^[outrow]); + above_ptr := JSAMPLE_PTR(input_data^[outrow-1]); + below_ptr := JSAMPLE_PTR(input_data^[outrow+1]); + + { Special case for first column } + colsum := GETJSAMPLE(above_ptr^) + GETJSAMPLE(below_ptr^) + + GETJSAMPLE(inptr^); + Inc(above_ptr); + Inc(below_ptr); + membersum := GETJSAMPLE(inptr^); + Inc(inptr); + nextcolsum := GETJSAMPLE(above_ptr^) + GETJSAMPLE(below_ptr^) + + GETJSAMPLE(inptr^); + neighsum := colsum + (colsum - membersum) + nextcolsum; + membersum := membersum * memberscale + neighsum * neighscale; + outptr^ := JSAMPLE ((membersum + 32768) shr 16); + Inc(outptr); + lastcolsum := colsum; colsum := nextcolsum; + + for colctr := pred(output_cols - 2) downto 0 do + begin + membersum := GETJSAMPLE(inptr^); + Inc(inptr); + Inc(above_ptr); + Inc(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) shr 16); + Inc(outptr); + lastcolsum := colsum; colsum := nextcolsum; + end; + + { Special case for last column } + membersum := GETJSAMPLE(inptr^); + neighsum := lastcolsum + (colsum - membersum) + colsum; + membersum := membersum * memberscale + neighsum * neighscale; + outptr^ := JSAMPLE ((membersum + 32768) shr 16); + end; +end; + +{$endif} { INPUT_SMOOTHING_SUPPORTED } + + +{ Module initialization routine for downsampling. + Note that we must select a routine for each component. } + +{GLOBAL} +procedure jinit_downsampler (cinfo : j_compress_ptr); +var + downsample : my_downsample_ptr; + ci : int; + compptr : jpeg_component_info_ptr; + smoothok : boolean; +begin + smoothok := TRUE; + + downsample := my_downsample_ptr( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + SIZEOF(my_downsampler)) ); + cinfo^.downsample := jpeg_downsampler_ptr (downsample); + downsample^.pub.start_pass := start_pass_downsample; + downsample^.pub.downsample := sep_downsample; + downsample^.pub.need_context_rows := FALSE; + + if (cinfo^.CCIR601_sampling) then + ERREXIT(j_common_ptr(cinfo), JERR_CCIR601_NOTIMPL); + + { Verify we can handle the sampling factors, and set up method pointers } + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + if (compptr^.h_samp_factor = cinfo^.max_h_samp_factor) and + (compptr^.v_samp_factor = cinfo^.max_v_samp_factor) then + begin +{$ifdef INPUT_SMOOTHING_SUPPORTED} + if (cinfo^.smoothing_factor <> 0) then + begin + downsample^.methods[ci] := fullsize_smooth_downsample; + downsample^.pub.need_context_rows := TRUE; + end + else +{$endif} + downsample^.methods[ci] := fullsize_downsample; + end + else + if (compptr^.h_samp_factor * 2 = cinfo^.max_h_samp_factor) and + (compptr^.v_samp_factor = cinfo^.max_v_samp_factor) then + begin + smoothok := FALSE; + downsample^.methods[ci] := h2v1_downsample; + end + else + if (compptr^.h_samp_factor * 2 = cinfo^.max_h_samp_factor) and + (compptr^.v_samp_factor * 2 = cinfo^.max_v_samp_factor) then + begin + {$ifdef INPUT_SMOOTHING_SUPPORTED} + if (cinfo^.smoothing_factor <> 0) then + begin + downsample^.methods[ci] := h2v2_smooth_downsample; + downsample^.pub.need_context_rows := TRUE; + end + else + {$endif} + downsample^.methods[ci] := h2v2_downsample; + end + else + if ((cinfo^.max_h_samp_factor mod compptr^.h_samp_factor) = 0) and + ((cinfo^.max_v_samp_factor mod compptr^.v_samp_factor) = 0) then + begin + smoothok := FALSE; + downsample^.methods[ci] := int_downsample; + end + else + ERREXIT(j_common_ptr(cinfo), JERR_FRACT_SAMPLE_NOTIMPL); + Inc(compptr); + end; + +{$ifdef INPUT_SMOOTHING_SUPPORTED} + if (cinfo^.smoothing_factor <> 0) and (not smoothok) then + TRACEMS(j_common_ptr(cinfo), 0, JTRC_SMOOTH_NOTIMPL); +{$endif} +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjdapimin.pas b/resources/libraries/deskew/Imaging/JpegLib/imjdapimin.pas new file mode 100755 index 0000000..6b8f403 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjdapimin.pas @@ -0,0 +1,503 @@ +unit imjdapimin; + +{ 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. } + +{ Original : jdapimin.c ; Copyright (C) 1994-1998, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjdeferr, + imjerror, + imjpeglib, + imjmemmgr, imjdmarker, imjdinput, imjcomapi; + +{ Nomssi } +procedure jpeg_create_decompress(cinfo : j_decompress_ptr); + +{ Initialization of a JPEG decompression object. + The error manager must already be set up (in case memory manager fails). } + +{GLOBAL} +procedure jpeg_CreateDecompress (cinfo : j_decompress_ptr; + version : int; + structsize : size_t); + +{ Destruction of a JPEG decompression object } + +{GLOBAL} +procedure jpeg_destroy_decompress (cinfo : j_decompress_ptr); + + +{ 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} +function jpeg_read_header (cinfo : j_decompress_ptr; + require_image : boolean) : int; + +{ 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} +function jpeg_consume_input (cinfo : j_decompress_ptr) : int; + +{ Have we finished reading the input file? } + +{GLOBAL} +function jpeg_input_complete (cinfo : j_decompress_ptr) : boolean; + +{ Is there more than one scan? } + +{GLOBAL} +function jpeg_has_multiple_scans (cinfo : j_decompress_ptr) : boolean; + + +{ 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} +function jpeg_finish_decompress (cinfo : j_decompress_ptr) : boolean; + +implementation + +procedure jpeg_create_decompress(cinfo : j_decompress_ptr); +begin + jpeg_CreateDecompress(cinfo, JPEG_LIB_VERSION, + size_t(sizeof(jpeg_decompress_struct))); +end; + +{ Initialization of a JPEG decompression object. + The error manager must already be set up (in case memory manager fails). } + +{GLOBAL} +procedure jpeg_CreateDecompress (cinfo : j_decompress_ptr; + version : int; + structsize : size_t); +var + i : int; +var + err : jpeg_error_mgr_ptr; + client_data : voidp; +begin + { Guard against version mismatches between library and caller. } + cinfo^.mem := NIL; { so jpeg_destroy knows mem mgr not called } + if (version <> JPEG_LIB_VERSION) then + ERREXIT2(j_common_ptr(cinfo), JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version); + if (structsize <> SIZEOF(jpeg_decompress_struct)) then + ERREXIT2(j_common_ptr(cinfo), JERR_BAD_STRUCT_SIZE, + int(SIZEOF(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. } + begin + err := cinfo^.err; + client_data := cinfo^.client_data; { ignore Purify complaint here } + MEMZERO(j_common_ptr(cinfo), SIZEOF(jpeg_decompress_struct)); + cinfo^.err := err; + cinfo^.client_data := client_data; + end; + 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 := NIL; + cinfo^.src := NIL; + + for i := 0 to pred(NUM_QUANT_TBLS) do + cinfo^.quant_tbl_ptrs[i] := NIL; + + for i := 0 to pred(NUM_HUFF_TBLS) do + begin + cinfo^.dc_huff_tbl_ptrs[i] := NIL; + cinfo^.ac_huff_tbl_ptrs[i] := NIL; + end; + + { Initialize marker processor so application can override methods + for COM, APPn markers before calling jpeg_read_header. } + cinfo^.marker_list := NIL; + jinit_marker_reader(cinfo); + + { And initialize the overall input controller. } + jinit_input_controller(cinfo); + + { OK, I'm ready } + cinfo^.global_state := DSTATE_START; +end; + + +{ Destruction of a JPEG decompression object } + +{GLOBAL} +procedure jpeg_destroy_decompress (cinfo : j_decompress_ptr); +begin + jpeg_destroy(j_common_ptr(cinfo)); { use common routine } +end; + + +{ Abort processing of a JPEG decompression operation, + but don't destroy the object itself. } + +{GLOBAL} +procedure jpeg_abort_decompress (cinfo : j_decompress_ptr); +begin + jpeg_abort(j_common_ptr(cinfo)); { use common routine } +end; + + +{ Set default decompression parameters. } + +{LOCAL} +procedure default_decompress_parms (cinfo : j_decompress_ptr); +var + cid0 : int; + cid1 : int; + cid2 : int; +begin + { 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. } + case (cinfo^.num_components) of + 1: begin + cinfo^.jpeg_color_space := JCS_GRAYSCALE; + cinfo^.out_color_space := JCS_GRAYSCALE; + end; + + 3: begin + if (cinfo^.saw_JFIF_marker) then + begin + cinfo^.jpeg_color_space := JCS_YCbCr; { JFIF implies YCbCr } + end + else + if (cinfo^.saw_Adobe_marker) then + begin + case (cinfo^.Adobe_transform) of + 0: cinfo^.jpeg_color_space := JCS_RGB; + 1: cinfo^.jpeg_color_space := JCS_YCbCr; + else + begin + WARNMS1(j_common_ptr(cinfo), JWRN_ADOBE_XFORM, cinfo^.Adobe_transform); + cinfo^.jpeg_color_space := JCS_YCbCr; { assume it's YCbCr } + end; + end; + end + else + begin + { Saw no special markers, try to guess from the component IDs } + cid0 := cinfo^.comp_info^[0].component_id; + cid1 := cinfo^.comp_info^[1].component_id; + cid2 := cinfo^.comp_info^[2].component_id; + + if (cid0 = 1) and (cid1 = 2) and (cid2 = 3) then + cinfo^.jpeg_color_space := JCS_YCbCr { assume JFIF w/out marker } + else + if (cid0 = 82) and (cid1 = 71) and (cid2 = 66) then + cinfo^.jpeg_color_space := JCS_RGB { ASCII 'R', 'G', 'B' } + else + begin + {$IFDEF DEBUG} + TRACEMS3(j_common_ptr(cinfo), 1, JTRC_UNKNOWN_IDS, cid0, cid1, cid2); + {$ENDIF} + cinfo^.jpeg_color_space := JCS_YCbCr; { assume it's YCbCr } + end; + end; + { Always guess RGB is proper output colorspace. } + cinfo^.out_color_space := JCS_RGB; + end; + + 4: begin + if (cinfo^.saw_Adobe_marker) then + begin + case (cinfo^.Adobe_transform) of + 0: cinfo^.jpeg_color_space := JCS_CMYK; + 2: cinfo^.jpeg_color_space := JCS_YCCK; + else + begin + WARNMS1(j_common_ptr(cinfo), JWRN_ADOBE_XFORM, cinfo^.Adobe_transform); + cinfo^.jpeg_color_space := JCS_YCCK; { assume it's YCCK } + end; + end; + end + else + begin + { No special markers, assume straight CMYK. } + cinfo^.jpeg_color_space := JCS_CMYK; + end; + cinfo^.out_color_space := JCS_CMYK; + end; + + else + begin + cinfo^.jpeg_color_space := JCS_UNKNOWN; + cinfo^.out_color_space := JCS_UNKNOWN; + end; + end; + + { 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 := NIL; + { Initialize for no mode change in buffered-image mode. } + cinfo^.enable_1pass_quant := FALSE; + cinfo^.enable_external_quant := FALSE; + cinfo^.enable_2pass_quant := FALSE; +end; + + +{ 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} +function jpeg_read_header (cinfo : j_decompress_ptr; + require_image : boolean) : int; +var + retcode : int; +begin + if (cinfo^.global_state <> DSTATE_START) and + (cinfo^.global_state <> DSTATE_INHEADER) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); + + retcode := jpeg_consume_input(cinfo); + + case (retcode) of + JPEG_REACHED_SOS: + retcode := JPEG_HEADER_OK; + JPEG_REACHED_EOI: + begin + if (require_image) then { Complain if application wanted an image } + ERREXIT(j_common_ptr(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; + end; + JPEG_SUSPENDED: ; { no work } + end; + + jpeg_read_header := retcode; +end; + + +{ 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} +function jpeg_consume_input (cinfo : j_decompress_ptr) : int; +var + retcode : int; +begin + retcode := JPEG_SUSPENDED; + + { NB: every possible DSTATE value should be listed in this switch } + + if (cinfo^.global_state) = DSTATE_START then + begin {work around the FALLTHROUGH} + { 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; + end; + + case (cinfo^.global_state) of + DSTATE_START, + DSTATE_INHEADER: + begin + retcode := cinfo^.inputctl^.consume_input (cinfo); + if (retcode = JPEG_REACHED_SOS) then + begin { 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; + end; + end; + DSTATE_READY: + { Can't advance past first SOS until start_decompress is called } + retcode := JPEG_REACHED_SOS; + + DSTATE_PRELOAD, + DSTATE_PRESCAN, + DSTATE_SCANNING, + DSTATE_RAW_OK, + DSTATE_BUFIMAGE, + DSTATE_BUFPOST, + DSTATE_STOPPING: + retcode := cinfo^.inputctl^.consume_input (cinfo); + else + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); + end; + jpeg_consume_input := retcode; +end; + + +{ Have we finished reading the input file? } + +{GLOBAL} +function jpeg_input_complete (cinfo : j_decompress_ptr) : boolean; +begin + { Check for valid jpeg object } + if (cinfo^.global_state < DSTATE_START) or + (cinfo^.global_state > DSTATE_STOPPING) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); + jpeg_input_complete := cinfo^.inputctl^.eoi_reached; +end; + + +{ Is there more than one scan? } + +{GLOBAL} +function jpeg_has_multiple_scans (cinfo : j_decompress_ptr) : boolean; +begin + { Only valid after jpeg_read_header completes } + if (cinfo^.global_state < DSTATE_READY) or + (cinfo^.global_state > DSTATE_STOPPING) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); + jpeg_has_multiple_scans := cinfo^.inputctl^.has_multiple_scans; +end; + + +{ 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} +function jpeg_finish_decompress (cinfo : j_decompress_ptr) : boolean; +begin + if ((cinfo^.global_state = DSTATE_SCANNING) or + (cinfo^.global_state = DSTATE_RAW_OK) and (not cinfo^.buffered_image)) then + begin + { Terminate final pass of non-buffered mode } + if (cinfo^.output_scanline < cinfo^.output_height) then + ERREXIT(j_common_ptr(cinfo), JERR_TOO_LITTLE_DATA); + cinfo^.master^.finish_output_pass (cinfo); + cinfo^.global_state := DSTATE_STOPPING; + end + else + if (cinfo^.global_state = DSTATE_BUFIMAGE) then + begin + { Finishing after a buffered-image operation } + cinfo^.global_state := DSTATE_STOPPING; + end + else + if (cinfo^.global_state <> DSTATE_STOPPING) then + begin + { STOPPING := repeat call after a suspension, anything else is error } + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); + end; + { Read until EOI } + while (not cinfo^.inputctl^.eoi_reached) do + begin + if (cinfo^.inputctl^.consume_input (cinfo) = JPEG_SUSPENDED) then + begin + jpeg_finish_decompress := FALSE; { Suspend, come back later } + exit; + end; + end; + { 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)); + jpeg_finish_decompress := TRUE; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjdapistd.pas b/resources/libraries/deskew/Imaging/JpegLib/imjdapistd.pas new file mode 100755 index 0000000..cef249b --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjdapistd.pas @@ -0,0 +1,377 @@ +unit imjdapistd; + +{ Original : 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. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjdeferr, + imjerror, + imjpeglib, + imjdmaster; + +{ 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} +function jpeg_read_scanlines (cinfo : j_decompress_ptr; + scanlines : JSAMPARRAY; + max_lines : JDIMENSION) : JDIMENSION; + + +{ Alternate entry point to read raw data. + Processes exactly one iMCU row per call, unless suspended. } + +{GLOBAL} +function jpeg_read_raw_data (cinfo : j_decompress_ptr; + data : JSAMPIMAGE; + max_lines : JDIMENSION) : JDIMENSION; + +{$ifdef D_MULTISCAN_FILES_SUPPORTED} + +{ Initialize for an output pass in buffered-image mode. } + +{GLOBAL} +function jpeg_start_output (cinfo : j_decompress_ptr; + scan_number : int) : boolean; + +{ 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} +function jpeg_finish_output (cinfo : j_decompress_ptr) : boolean; + +{$endif} { D_MULTISCAN_FILES_SUPPORTED } + +{ 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} +function jpeg_start_decompress (cinfo : j_decompress_ptr) : boolean; + + +implementation + +{ Forward declarations } +{LOCAL} +function output_pass_setup (cinfo : j_decompress_ptr) : boolean; forward; + +{ 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} +function jpeg_start_decompress (cinfo : j_decompress_ptr) : boolean; +var + retcode : int; +begin + if (cinfo^.global_state = DSTATE_READY) then + begin + { First call: initialize master control, select active modules } + jinit_master_decompress(cinfo); + if (cinfo^.buffered_image) then + begin + { No more work here; expecting jpeg_start_output next } + cinfo^.global_state := DSTATE_BUFIMAGE; + jpeg_start_decompress := TRUE; + exit; + end; + cinfo^.global_state := DSTATE_PRELOAD; + end; + if (cinfo^.global_state = DSTATE_PRELOAD) then + begin + { If file has multiple scans, absorb them all into the coef buffer } + if (cinfo^.inputctl^.has_multiple_scans) then + begin +{$ifdef D_MULTISCAN_FILES_SUPPORTED} + while TRUE do + begin + + { Call progress monitor hook if present } + if (cinfo^.progress <> NIL) then + cinfo^.progress^.progress_monitor (j_common_ptr(cinfo)); + { Absorb some more input } + retcode := cinfo^.inputctl^.consume_input (cinfo); + if (retcode = JPEG_SUSPENDED) then + begin + jpeg_start_decompress := FALSE; + exit; + end; + if (retcode = JPEG_REACHED_EOI) then + break; + { Advance progress counter if appropriate } + if (cinfo^.progress <> NIL) and + ((retcode = JPEG_ROW_COMPLETED) or (retcode = JPEG_REACHED_SOS)) then + begin + Inc(cinfo^.progress^.pass_counter); + if (cinfo^.progress^.pass_counter >= cinfo^.progress^.pass_limit) then + begin + { jdmaster underestimated number of scans; ratchet up one scan } + Inc(cinfo^.progress^.pass_limit, long(cinfo^.total_iMCU_rows)); + end; + end; + end; +{$else} + ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); +{$endif} { D_MULTISCAN_FILES_SUPPORTED } + end; + cinfo^.output_scan_number := cinfo^.input_scan_number; + end + else + if (cinfo^.global_state <> DSTATE_PRESCAN) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); + { Perform any dummy output passes, and set up for the final pass } + jpeg_start_decompress := output_pass_setup(cinfo); +end; + + +{ 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} +function output_pass_setup (cinfo : j_decompress_ptr) : boolean; +var + last_scanline : JDIMENSION; +begin + if (cinfo^.global_state <> DSTATE_PRESCAN) then + begin + { First call: do pass setup } + cinfo^.master^.prepare_for_output_pass (cinfo); + cinfo^.output_scanline := 0; + cinfo^.global_state := DSTATE_PRESCAN; + end; + { Loop over any required dummy passes } + while (cinfo^.master^.is_dummy_pass) do + begin +{$ifdef QUANT_2PASS_SUPPORTED} + { Crank through the dummy pass } + while (cinfo^.output_scanline < cinfo^.output_height) do + begin + { Call progress monitor hook if present } + if (cinfo^.progress <> NIL) then + begin + cinfo^.progress^.pass_counter := long (cinfo^.output_scanline); + cinfo^.progress^.pass_limit := long (cinfo^.output_height); + cinfo^.progress^.progress_monitor (j_common_ptr(cinfo)); + end; + { Process some data } + last_scanline := cinfo^.output_scanline; + cinfo^.main^.process_data (cinfo, JSAMPARRAY(NIL), + cinfo^.output_scanline, {var} + JDIMENSION(0)); + if (cinfo^.output_scanline = last_scanline) then + begin + output_pass_setup := FALSE; { No progress made, must suspend } + exit; + end; + end; + { 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(j_common_ptr(cinfo), JERR_NOT_COMPILED); +{$endif} { QUANT_2PASS_SUPPORTED } + end; + { Ready for application to drive output pass through + jpeg_read_scanlines or jpeg_read_raw_data. } + if cinfo^.raw_data_out then + cinfo^.global_state := DSTATE_RAW_OK + else + cinfo^.global_state := DSTATE_SCANNING; + output_pass_setup := TRUE; +end; + + +{ 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} +function jpeg_read_scanlines (cinfo : j_decompress_ptr; + scanlines : JSAMPARRAY; + max_lines : JDIMENSION) : JDIMENSION; +var + row_ctr : JDIMENSION; +begin + if (cinfo^.global_state <> DSTATE_SCANNING) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); + if (cinfo^.output_scanline >= cinfo^.output_height) then + begin + WARNMS(j_common_ptr(cinfo), JWRN_TOO_MUCH_DATA); + jpeg_read_scanlines := 0; + exit; + end; + + { Call progress monitor hook if present } + if (cinfo^.progress <> NIL) then + begin + cinfo^.progress^.pass_counter := long (cinfo^.output_scanline); + cinfo^.progress^.pass_limit := long (cinfo^.output_height); + cinfo^.progress^.progress_monitor (j_common_ptr(cinfo)); + end; + + { Process some data } + row_ctr := 0; + cinfo^.main^.process_data (cinfo, scanlines, {var}row_ctr, max_lines); + Inc(cinfo^.output_scanline, row_ctr); + jpeg_read_scanlines := row_ctr; +end; + + +{ Alternate entry point to read raw data. + Processes exactly one iMCU row per call, unless suspended. } + +{GLOBAL} +function jpeg_read_raw_data (cinfo : j_decompress_ptr; + data : JSAMPIMAGE; + max_lines : JDIMENSION) : JDIMENSION; +var + lines_per_iMCU_row : JDIMENSION; +begin + if (cinfo^.global_state <> DSTATE_RAW_OK) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); + if (cinfo^.output_scanline >= cinfo^.output_height) then + begin + WARNMS(j_common_ptr(cinfo), JWRN_TOO_MUCH_DATA); + jpeg_read_raw_data := 0; + exit; + end; + + { Call progress monitor hook if present } + if (cinfo^.progress <> NIL) then + begin + cinfo^.progress^.pass_counter := long (cinfo^.output_scanline); + cinfo^.progress^.pass_limit := long (cinfo^.output_height); + cinfo^.progress^.progress_monitor (j_common_ptr(cinfo)); + end; + + { 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) then + ERREXIT(j_common_ptr(cinfo), JERR_BUFFER_SIZE); + + { Decompress directly into user's buffer. } + if (cinfo^.coef^.decompress_data (cinfo, data) = 0) then + begin + jpeg_read_raw_data := 0; { suspension forced, can do nothing more } + exit; + end; + + { OK, we processed one iMCU row. } + Inc(cinfo^.output_scanline, lines_per_iMCU_row); + jpeg_read_raw_data := lines_per_iMCU_row; +end; + + +{ Additional entry points for buffered-image mode. } + +{$ifdef D_MULTISCAN_FILES_SUPPORTED} + +{ Initialize for an output pass in buffered-image mode. } + +{GLOBAL} +function jpeg_start_output (cinfo : j_decompress_ptr; + scan_number : int) : boolean; +begin + if (cinfo^.global_state <> DSTATE_BUFIMAGE) and + (cinfo^.global_state <> DSTATE_PRESCAN) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); + { Limit scan number to valid range } + if (scan_number <= 0) then + scan_number := 1; + if (cinfo^.inputctl^.eoi_reached) and + (scan_number > cinfo^.input_scan_number) then + scan_number := cinfo^.input_scan_number; + cinfo^.output_scan_number := scan_number; + { Perform any dummy output passes, and set up for the real pass } + jpeg_start_output := output_pass_setup(cinfo); +end; + + +{ 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} +function jpeg_finish_output (cinfo : j_decompress_ptr) : boolean; +begin + if ((cinfo^.global_state = DSTATE_SCANNING) or + (cinfo^.global_state = DSTATE_RAW_OK) and cinfo^.buffered_image) then + begin + { 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; + end + else + if (cinfo^.global_state <> DSTATE_BUFPOST) then + begin + { BUFPOST := repeat call after a suspension, anything else is error } + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); + end; + { Read markers looking for SOS or EOI } + while (cinfo^.input_scan_number <= cinfo^.output_scan_number) and + (not cinfo^.inputctl^.eoi_reached) do + begin + if (cinfo^.inputctl^.consume_input (cinfo) = JPEG_SUSPENDED) then + begin + jpeg_finish_output := FALSE; { Suspend, come back later } + exit; + end; + end; + cinfo^.global_state := DSTATE_BUFIMAGE; + jpeg_finish_output := TRUE; +end; + +{$endif} { D_MULTISCAN_FILES_SUPPORTED } + +end. + diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjdcoefct.pas b/resources/libraries/deskew/Imaging/JpegLib/imjdcoefct.pas new file mode 100755 index 0000000..caa69a2 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjdcoefct.pas @@ -0,0 +1,895 @@ +unit imjdcoefct; + +{ 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. } + +{ Original: jdcoefct.c ; Copyright (C) 1994-1997, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjdeferr, + imjerror, + imjutils, + imjpeglib; + +{GLOBAL} +procedure jinit_d_coef_controller (cinfo : j_decompress_ptr; + need_full_buffer : boolean); + + +implementation + + +{ Block smoothing is only applicable for progressive JPEG, so: } +{$ifndef D_PROGRESSIVE_SUPPORTED} +{$undef BLOCK_SMOOTHING_SUPPORTED} +{$endif} + +{ Private buffer controller object } + +{$ifdef BLOCK_SMOOTHING_SUPPORTED} +const + SAVED_COEFS = 6; { we save coef_bits[0..5] } +type + Latch = array[0..SAVED_COEFS-1] of int; + Latch_ptr = ^Latch; +{$endif} + +type + my_coef_ptr = ^my_coef_controller; + my_coef_controller = record + pub : jpeg_d_coef_controller; { public fields } + + { These variables keep track of the current location of the input side. } + { cinfo^.input_iMCU_row is also used for this. } + MCU_ctr : JDIMENSION; { counts MCUs processed in current row } + MCU_vert_offset : int; { counts MCU rows within iMCU row } + MCU_rows_per_iMCU_row : int; { 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. } + + MCU_buffer : array[0..D_MAX_BLOCKS_IN_MCU-1] of JBLOCKROW; + + {$ifdef D_MULTISCAN_FILES_SUPPORTED} + { In multi-pass modes, we need a virtual block array for each component. } + whole_image : jvirt_barray_tbl; + {$endif} + + {$ifdef BLOCK_SMOOTHING_SUPPORTED} + { When doing block smoothing, we latch coefficient Al values here } + coef_bits_latch : Latch_Ptr; + {$endif} + end; + +{ Forward declarations } +{METHODDEF} +function decompress_onepass (cinfo : j_decompress_ptr; + output_buf : JSAMPIMAGE) : int; forward; +{$ifdef D_MULTISCAN_FILES_SUPPORTED} +{METHODDEF} +function decompress_data (cinfo : j_decompress_ptr; + output_buf : JSAMPIMAGE) : int; forward; +{$endif} +{$ifdef BLOCK_SMOOTHING_SUPPORTED} +{LOCAL} +function smoothing_ok (cinfo : j_decompress_ptr) : boolean; forward; + +{METHODDEF} +function decompress_smooth_data (cinfo : j_decompress_ptr; + output_buf : JSAMPIMAGE) : int; forward; +{$endif} + + +{LOCAL} +procedure start_iMCU_row (cinfo : j_decompress_ptr); +{ Reset within-iMCU-row counters for a new row (input side) } +var + coef : my_coef_ptr; +begin + 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) then + begin + coef^.MCU_rows_per_iMCU_row := 1; + end + else + begin + if (cinfo^.input_iMCU_row < (cinfo^.total_iMCU_rows-1)) then + 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; + end; + + coef^.MCU_ctr := 0; + coef^.MCU_vert_offset := 0; +end; + + +{ Initialize for an input processing pass. } + +{METHODDEF} +procedure start_input_pass (cinfo : j_decompress_ptr); +begin + cinfo^.input_iMCU_row := 0; + start_iMCU_row(cinfo); +end; + + +{ Initialize for an output processing pass. } + +{METHODDEF} +procedure start_output_pass (cinfo : j_decompress_ptr); +var + coef : my_coef_ptr; +begin +{$ifdef BLOCK_SMOOTHING_SUPPORTED} + coef := my_coef_ptr (cinfo^.coef); + + { If multipass, check to see whether to use block smoothing on this pass } + if (coef^.pub.coef_arrays <> NIL) then + begin + if (cinfo^.do_block_smoothing) and smoothing_ok(cinfo) then + coef^.pub.decompress_data := decompress_smooth_data + else + coef^.pub.decompress_data := decompress_data; + end; +{$endif} + cinfo^.output_iMCU_row := 0; +end; + + +{ 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} +function decompress_onepass (cinfo : j_decompress_ptr; + output_buf : JSAMPIMAGE) : int; +var + coef : my_coef_ptr; + MCU_col_num : JDIMENSION; { index of current MCU within row } + last_MCU_col : JDIMENSION; + last_iMCU_row : JDIMENSION; + blkn, ci, xindex, yindex, yoffset, useful_width : int; + output_ptr : JSAMPARRAY; + start_col, output_col : JDIMENSION; + compptr : jpeg_component_info_ptr; + inverse_DCT : inverse_DCT_method_ptr; +begin + coef := my_coef_ptr (cinfo^.coef); + last_MCU_col := cinfo^.MCUs_per_row - 1; + last_iMCU_row := cinfo^.total_iMCU_rows - 1; + + { Loop to process as much as one whole iMCU row } + for yoffset := coef^.MCU_vert_offset to pred(coef^.MCU_rows_per_iMCU_row) do + begin + for MCU_col_num := coef^.MCU_ctr to last_MCU_col do + begin + { Try to fetch an MCU. Entropy decoder expects buffer to be zeroed. } + jzero_far( coef^.MCU_buffer[0], + size_t (cinfo^.blocks_in_MCU * SIZEOF(JBLOCK))); + if (not cinfo^.entropy^.decode_mcu (cinfo, coef^.MCU_buffer)) then + begin + { Suspension forced; update state counters and exit } + coef^.MCU_vert_offset := yoffset; + coef^.MCU_ctr := MCU_col_num; + decompress_onepass := JPEG_SUSPENDED; + exit; + end; + { 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 to pred(cinfo^.comps_in_scan) do + begin + compptr := cinfo^.cur_comp_info[ci]; + { Don't bother to IDCT an uninteresting component. } + if (not compptr^.component_needed) then + begin + Inc(blkn, compptr^.MCU_blocks); + continue; + end; + inverse_DCT := cinfo^.idct^.inverse_DCT[compptr^.component_index]; + if (MCU_col_num < last_MCU_col) then + useful_width := compptr^.MCU_width + else + useful_width := compptr^.last_col_width; + + output_ptr := JSAMPARRAY(@ output_buf^[compptr^.component_index]^ + [yoffset * compptr^.DCT_scaled_size]); + start_col := LongInt(MCU_col_num) * compptr^.MCU_sample_width; + for yindex := 0 to pred(compptr^.MCU_height) do + begin + if (cinfo^.input_iMCU_row < last_iMCU_row) or + (yoffset+yindex < compptr^.last_row_height) then + begin + output_col := start_col; + for xindex := 0 to pred(useful_width) do + begin + inverse_DCT (cinfo, compptr, + JCOEFPTR(coef^.MCU_buffer[blkn+xindex]), + output_ptr, output_col); + Inc(output_col, compptr^.DCT_scaled_size); + end; + end; + Inc(blkn, compptr^.MCU_width); + Inc(JSAMPROW_PTR(output_ptr), compptr^.DCT_scaled_size); + end; + end; + end; + { Completed an MCU row, but perhaps not an iMCU row } + coef^.MCU_ctr := 0; + end; + { Completed the iMCU row, advance counters for next one } + Inc(cinfo^.output_iMCU_row); + + Inc(cinfo^.input_iMCU_row); + if (cinfo^.input_iMCU_row < cinfo^.total_iMCU_rows) then + begin + start_iMCU_row(cinfo); + decompress_onepass := JPEG_ROW_COMPLETED; + exit; + end; + { Completed the scan } + cinfo^.inputctl^.finish_input_pass (cinfo); + decompress_onepass := JPEG_SCAN_COMPLETED; +end; + +{ Dummy consume-input routine for single-pass operation. } + +{METHODDEF} +function dummy_consume_data (cinfo : j_decompress_ptr) : int; +begin + dummy_consume_data := JPEG_SUSPENDED; { Always indicate nothing was done } +end; + + +{$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} +function consume_data (cinfo : j_decompress_ptr) : int; +var + coef : my_coef_ptr; + MCU_col_num : JDIMENSION; { index of current MCU within row } + blkn, ci, xindex, yindex, yoffset : int; + start_col : JDIMENSION; + buffer : array[0..MAX_COMPS_IN_SCAN-1] of JBLOCKARRAY; + buffer_ptr : JBLOCKROW; + compptr : jpeg_component_info_ptr; +begin + coef := my_coef_ptr (cinfo^.coef); + + { Align the virtual buffers for the components used in this scan. } + for ci := 0 to pred(cinfo^.comps_in_scan) do + begin + compptr := cinfo^.cur_comp_info[ci]; + buffer[ci] := cinfo^.mem^.access_virt_barray + (j_common_ptr (cinfo), coef^.whole_image[compptr^.component_index], + LongInt(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. } + + end; + + { Loop to process one whole iMCU row } + for yoffset := coef^.MCU_vert_offset to pred(coef^.MCU_rows_per_iMCU_row) do + begin + for MCU_col_num := coef^.MCU_ctr to pred(cinfo^.MCUs_per_row) do + begin + { Construct list of pointers to DCT blocks belonging to this MCU } + blkn := 0; { index of current DCT block within MCU } + for ci := 0 to pred(cinfo^.comps_in_scan) do + begin + compptr := cinfo^.cur_comp_info[ci]; + start_col := LongInt(MCU_col_num) * compptr^.MCU_width; + for yindex := 0 to pred(compptr^.MCU_height) do + begin + buffer_ptr := JBLOCKROW(@ buffer[ci]^[yindex+yoffset]^[start_col]); + for xindex := 0 to pred(compptr^.MCU_width) do + begin + coef^.MCU_buffer[blkn] := buffer_ptr; + Inc(blkn); + Inc(JBLOCK_PTR(buffer_ptr)); + end; + end; + end; + { Try to fetch the MCU. } + if (not cinfo^.entropy^.decode_mcu (cinfo, coef^.MCU_buffer)) then + begin + { Suspension forced; update state counters and exit } + coef^.MCU_vert_offset := yoffset; + coef^.MCU_ctr := MCU_col_num; + consume_data := JPEG_SUSPENDED; + exit; + end; + end; + { Completed an MCU row, but perhaps not an iMCU row } + coef^.MCU_ctr := 0; + end; + { Completed the iMCU row, advance counters for next one } + Inc(cinfo^.input_iMCU_row); + if (cinfo^.input_iMCU_row < cinfo^.total_iMCU_rows) then + begin + start_iMCU_row(cinfo); + consume_data := JPEG_ROW_COMPLETED; + exit; + end; + { Completed the scan } + cinfo^.inputctl^.finish_input_pass (cinfo); + consume_data := JPEG_SCAN_COMPLETED; +end; + + +{ 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} +function decompress_data (cinfo : j_decompress_ptr; + output_buf : JSAMPIMAGE) : int; +var + coef : my_coef_ptr; + last_iMCU_row : JDIMENSION; + block_num : JDIMENSION; + ci, block_row, block_rows : int; + buffer : JBLOCKARRAY; + buffer_ptr : JBLOCKROW; + output_ptr : JSAMPARRAY; + output_col : JDIMENSION; + compptr : jpeg_component_info_ptr; + inverse_DCT : inverse_DCT_method_ptr; +begin + coef := my_coef_ptr (cinfo^.coef); + last_iMCU_row := cinfo^.total_iMCU_rows - 1; + + { Force some input to be done if we are getting ahead of the input. } + while (cinfo^.input_scan_number < cinfo^.output_scan_number) or + ((cinfo^.input_scan_number = cinfo^.output_scan_number) and + (LongInt(cinfo^.input_iMCU_row) <= cinfo^.output_iMCU_row)) do + begin + if (cinfo^.inputctl^.consume_input(cinfo) = JPEG_SUSPENDED) then + begin + decompress_data := JPEG_SUSPENDED; + exit; + end; + end; + + { OK, output from the virtual arrays. } + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + { Don't bother to IDCT an uninteresting component. } + if (not compptr^.component_needed) then + 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 < LongInt(last_iMCU_row)) then + block_rows := compptr^.v_samp_factor + else + begin + { NB: can't use last_row_height here; it is input-side-dependent! } + block_rows := int(LongInt(compptr^.height_in_blocks) mod compptr^.v_samp_factor); + if (block_rows = 0) then + block_rows := compptr^.v_samp_factor; + end; + inverse_DCT := cinfo^.idct^.inverse_DCT[ci]; + output_ptr := output_buf^[ci]; + { Loop over all DCT blocks to be processed. } + for block_row := 0 to pred(block_rows) do + begin + buffer_ptr := buffer^[block_row]; + output_col := 0; + for block_num := 0 to pred(compptr^.width_in_blocks) do + begin + inverse_DCT (cinfo, compptr, JCOEFPTR (buffer_ptr), + output_ptr, output_col); + Inc(JBLOCK_PTR(buffer_ptr)); + Inc(output_col, compptr^.DCT_scaled_size); + end; + Inc(JSAMPROW_PTR(output_ptr), compptr^.DCT_scaled_size); + end; + Inc(compptr); + end; + + Inc(cinfo^.output_iMCU_row); + if (cinfo^.output_iMCU_row < LongInt(cinfo^.total_iMCU_rows)) then + begin + decompress_data := JPEG_ROW_COMPLETED; + exit; + end; + decompress_data := JPEG_SCAN_COMPLETED; +end; + +{$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 } +const + Q01_POS = 1; + Q10_POS = 8; + Q20_POS = 16; + Q11_POS = 9; + 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} +function smoothing_ok (cinfo : j_decompress_ptr) : boolean; +var + coef : my_coef_ptr; + smoothing_useful : boolean; + ci, coefi : int; + compptr : jpeg_component_info_ptr; + qtable : JQUANT_TBL_PTR; + coef_bits : coef_bits_ptr; + coef_bits_latch : Latch_Ptr; +begin + coef := my_coef_ptr (cinfo^.coef); + smoothing_useful := FALSE; + + if (not cinfo^.progressive_mode) or (cinfo^.coef_bits = NIL) then + begin + smoothing_ok := FALSE; + exit; + end; + + { Allocate latch area if not already done } + if (coef^.coef_bits_latch = NIL) then + coef^.coef_bits_latch := Latch_Ptr( + cinfo^.mem^.alloc_small (j_common_ptr (cinfo), JPOOL_IMAGE, + cinfo^.num_components * + (SAVED_COEFS * SIZEOF(int))) ); + coef_bits_latch := (coef^.coef_bits_latch); + + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + { All components' quantization values must already be latched. } + qtable := compptr^.quant_table; + if (qtable = NIL) then + begin + smoothing_ok := FALSE; + exit; + end; + { Verify DC & first 5 AC quantizers are nonzero to avoid zero-divide. } + if (qtable^.quantval[0] = 0) or + (qtable^.quantval[Q01_POS] = 0) or + (qtable^.quantval[Q10_POS] = 0) or + (qtable^.quantval[Q20_POS] = 0) or + (qtable^.quantval[Q11_POS] = 0) or + (qtable^.quantval[Q02_POS] = 0) then + begin + smoothing_ok := FALSE; + exit; + end; + { DC values must be at least partly known for all components. } + coef_bits := @cinfo^.coef_bits^[ci]; { Nomssi } + if (coef_bits^[0] < 0) then + begin + smoothing_ok := FALSE; + exit; + end; + { Block smoothing is helpful if some AC coefficients remain inaccurate. } + for coefi := 1 to 5 do + begin + coef_bits_latch^[coefi] := coef_bits^[coefi]; + if (coef_bits^[coefi] <> 0) then + smoothing_useful := TRUE; + end; + Inc(coef_bits_latch {SAVED_COEFS}); + Inc(compptr); + end; + + smoothing_ok := smoothing_useful; +end; + + +{ Variant of decompress_data for use when doing block smoothing. } + +{METHODDEF} +function decompress_smooth_data (cinfo : j_decompress_ptr; + output_buf : JSAMPIMAGE) : int; +var + coef : my_coef_ptr; + last_iMCU_row : JDIMENSION; + block_num, last_block_column : JDIMENSION; + ci, block_row, block_rows, access_rows : int; + buffer : JBLOCKARRAY; + buffer_ptr, prev_block_row, next_block_row : JBLOCKROW; + output_ptr : JSAMPARRAY; + output_col : JDIMENSION; + compptr : jpeg_component_info_ptr; + inverse_DCT : inverse_DCT_method_ptr; + first_row, last_row : boolean; + workspace : JBLOCK; + coef_bits : Latch_Ptr; { coef_bits_ptr; } + quanttbl : JQUANT_TBL_PTR; + Q00,Q01,Q02,Q10,Q11,Q20, num : INT32; + DC1,DC2,DC3,DC4,DC5,DC6,DC7,DC8,DC9 : int; + Al, pred : int; +var + delta : JDIMENSION; +begin + coef := my_coef_ptr (cinfo^.coef); + last_iMCU_row := cinfo^.total_iMCU_rows - 1; + + { Force some input to be done if we are getting ahead of the input. } + while (cinfo^.input_scan_number <= cinfo^.output_scan_number) and + (not cinfo^.inputctl^.eoi_reached) do + begin + if (cinfo^.input_scan_number = cinfo^.output_scan_number) then + begin + { 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. } + + if (cinfo^.Ss = 0) then + delta := 1 + else + delta := 0; + if (LongInt(cinfo^.input_iMCU_row) > cinfo^.output_iMCU_row+LongInt(delta)) then + break; + end; + if (cinfo^.inputctl^.consume_input(cinfo) = JPEG_SUSPENDED) then + begin + decompress_smooth_data := JPEG_SUSPENDED; + exit; + end; + end; + + { OK, output from the virtual arrays. } + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to (cinfo^.num_components-1) do + begin + { Don't bother to IDCT an uninteresting component. } + if (not compptr^.component_needed) then + continue; + { Count non-dummy DCT block rows in this iMCU row. } + if (cinfo^.output_iMCU_row < LongInt(last_iMCU_row)) then + begin + block_rows := compptr^.v_samp_factor; + access_rows := block_rows * 2; { this and next iMCU row } + last_row := FALSE; + end + else + begin + { NB: can't use last_row_height here; it is input-side-dependent! } + block_rows := int (compptr^.height_in_blocks) mod compptr^.v_samp_factor; + if (block_rows = 0) then + block_rows := compptr^.v_samp_factor; + access_rows := block_rows; { this iMCU row only } + last_row := TRUE; + end; + { Align the virtual buffer for this component. } + if (cinfo^.output_iMCU_row > 0) then + begin + Inc(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); + Inc(JBLOCKROW_PTR(buffer), compptr^.v_samp_factor); { point to current iMCU row } + first_row := FALSE; + end + else + begin + buffer := cinfo^.mem^.access_virt_barray + (j_common_ptr (cinfo), coef^.whole_image[ci], + JDIMENSION (0), JDIMENSION (access_rows), FALSE); + first_row := TRUE; + end; + { Fetch component-dependent info } + coef_bits := coef^.coef_bits_latch; + Inc(coef_bits, ci); { 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 to (block_rows-1) do + begin + buffer_ptr := buffer^[block_row]; + if (first_row) and (block_row = 0) then + prev_block_row := buffer_ptr + else + prev_block_row := buffer^[block_row-1]; + if (last_row) and (block_row = block_rows-1) then + 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.} + + DC3 := int(prev_block_row^[0][0]); + DC2 := DC3; + DC1 := DC2; + DC6 := int(buffer_ptr^[0][0]); + DC5 := DC6; + DC4 := DC5; + DC9 := int(next_block_row^[0][0]); + DC8 := DC9; + DC7 := DC8 ; + output_col := 0; + last_block_column := compptr^.width_in_blocks - 1; + for block_num := 0 to last_block_column do + begin + { 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) then + begin + DC3 := int (prev_block_row^[1][0]); + DC6 := int (buffer_ptr^[1][0]); + DC9 := int (next_block_row^[1][0]); + end; + { 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 } + Al := coef_bits^[1]; + if (Al <> 0) and (workspace[1] = 0) then + begin + num := 36 * Q00 * (DC4 - DC6); + if (num >= 0) then + begin + pred := int (((Q01 shl 7) + num) div (Q01 shl 8)); + if (Al > 0) and (pred >= (1 shl Al)) then + pred := (1 shl Al)-1; + end + else + begin + pred := int (((Q01 shl 7) - num) div (Q01 shl 8)); + if (Al > 0) and (pred >= (1 shl Al)) then + pred := (1 shl Al)-1; + pred := -pred; + end; + workspace[1] := JCOEF (pred); + end; + { AC10 } + Al := coef_bits^[2]; + if (Al <> 0) and (workspace[8] = 0) then + begin + num := 36 * Q00 * (DC2 - DC8); + if (num >= 0) then + begin + pred := int (((Q10 shl 7) + num) div (Q10 shl 8)); + if (Al > 0) and (pred >= (1 shl Al)) then + pred := (1 shl Al)-1; + end + else + begin + pred := int (((Q10 shl 7) - num) div (Q10 shl 8)); + if (Al > 0) and (pred >= (1 shl Al)) then + pred := (1 shl Al)-1; + pred := -pred; + end; + workspace[8] := JCOEF (pred); + end; + { AC20 } + Al := coef_bits^[3]; + if (Al <> 0) and (workspace[16] = 0) then + begin + num := 9 * Q00 * (DC2 + DC8 - 2*DC5); + if (num >= 0) then + begin + pred := int (((Q20 shl 7) + num) div (Q20 shl 8)); + if (Al > 0) and (pred >= (1 shl Al)) then + pred := (1 shl Al)-1; + end + else + begin + pred := int (((Q20 shl 7) - num) div (Q20 shl 8)); + if (Al > 0) and (pred >= (1 shl Al)) then + pred := (1 shl Al)-1; + pred := -pred; + end; + workspace[16] := JCOEF (pred); + end; + { AC11 } + Al := coef_bits^[4]; + if (Al <> 0) and (workspace[9] = 0) then + begin + num := 5 * Q00 * (DC1 - DC3 - DC7 + DC9); + if (num >= 0) then + begin + pred := int (((Q11 shl 7) + num) div (Q11 shl 8)); + if (Al > 0) and (pred >= (1 shl Al)) then + pred := (1 shl Al)-1; + end + else + begin + pred := int (((Q11 shl 7) - num) div (Q11 shl 8)); + if (Al > 0) and (pred >= (1 shl Al)) then + pred := (1 shl Al)-1; + pred := -pred; + end; + workspace[9] := JCOEF (pred); + end; + { AC02 } + Al := coef_bits^[5]; + if (Al <> 0) and (workspace[2] = 0) then + begin + num := 9 * Q00 * (DC4 + DC6 - 2*DC5); + if (num >= 0) then + begin + pred := int (((Q02 shl 7) + num) div (Q02 shl 8)); + if (Al > 0) and (pred >= (1 shl Al)) then + pred := (1 shl Al)-1; + end + else + begin + pred := int (((Q02 shl 7) - num) div (Q02 shl 8)); + if (Al > 0) and (pred >= (1 shl Al)) then + pred := (1 shl Al)-1; + pred := -pred; + end; + workspace[2] := JCOEF (pred); + end; + { 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; + Inc(JBLOCK_PTR(buffer_ptr)); + Inc(JBLOCK_PTR(prev_block_row)); + Inc(JBLOCK_PTR(next_block_row)); + Inc(output_col, compptr^.DCT_scaled_size); + end; + Inc(JSAMPROW_PTR(output_ptr), compptr^.DCT_scaled_size); + end; + Inc(compptr); + end; + + Inc(cinfo^.output_iMCU_row); + if (cinfo^.output_iMCU_row < LongInt(cinfo^.total_iMCU_rows)) then + begin + decompress_smooth_data := JPEG_ROW_COMPLETED; + exit; + end; + decompress_smooth_data := JPEG_SCAN_COMPLETED; +end; + +{$endif} { BLOCK_SMOOTHING_SUPPORTED } + + +{ Initialize coefficient buffer controller. } + +{GLOBAL} +procedure jinit_d_coef_controller (cinfo : j_decompress_ptr; + need_full_buffer : boolean); +var + coef : my_coef_ptr; +{$ifdef D_MULTISCAN_FILES_SUPPORTED} +var + ci, access_rows : int; + compptr : jpeg_component_info_ptr; +{$endif} +var + buffer : JBLOCK_PTR; + i : int; +begin + coef := my_coef_ptr( + cinfo^.mem^.alloc_small (j_common_ptr (cinfo), JPOOL_IMAGE, + SIZEOF(my_coef_controller)) ); + cinfo^.coef := jpeg_d_coef_controller_ptr(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 := NIL; +{$endif} + + { Create the coefficient buffer. } + if (need_full_buffer) then + begin +{$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. } + + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + access_rows := compptr^.v_samp_factor; +{$ifdef BLOCK_SMOOTHING_SUPPORTED} + { If block smoothing could be used, need a bigger window } + if (cinfo^.progressive_mode) then + access_rows := 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)); + Inc(compptr); + end; + 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(j_common_ptr(cinfo), JERR_NOT_COMPILED); +{$endif} + end + else + begin + { We only need a single-MCU buffer. } + buffer := JBLOCK_PTR ( + cinfo^.mem^.alloc_large (j_common_ptr (cinfo), JPOOL_IMAGE, + D_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)) ); + for i := 0 to pred(D_MAX_BLOCKS_IN_MCU) do + begin + coef^.MCU_buffer[i] := JBLOCKROW(buffer); + Inc(buffer); + end; + coef^.pub.consume_data := dummy_consume_data; + coef^.pub.decompress_data := decompress_onepass; + coef^.pub.coef_arrays := NIL; { flag for no virtual arrays } + end; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjdcolor.pas b/resources/libraries/deskew/Imaging/JpegLib/imjdcolor.pas new file mode 100755 index 0000000..64c5f41 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjdcolor.pas @@ -0,0 +1,501 @@ +unit imjdcolor; + +{ This file contains output colorspace conversion routines. } + +{ Original: jdcolor.c ; Copyright (C) 1991-1997, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjutils, + imjdeferr, + imjerror, + imjpeglib; + +{ Module initialization routine for output colorspace conversion. } + +{GLOBAL} +procedure jinit_color_deconverter (cinfo : j_decompress_ptr); + +implementation + +{ Private subobject } +type + int_Color_Table = array[0..MAXJSAMPLE+1-1] of int; + int_table_ptr = ^int_Color_Table; + INT32_Color_Table = array[0..MAXJSAMPLE+1-1] of INT32; + INT32_table_ptr = ^INT32_Color_Table; +type + my_cconvert_ptr = ^my_color_deconverter; + my_color_deconverter = record + pub : jpeg_color_deconverter; { public fields } + + { Private state for YCC^.RGB conversion } + Cr_r_tab : int_table_ptr; { => table for Cr to R conversion } + Cb_b_tab : int_table_ptr; { => table for Cb to B conversion } + Cr_g_tab : INT32_table_ptr; { => table for Cr to G conversion } + Cb_g_tab : INT32_table_ptr; { => table for Cb to G conversion } + end; + + + + +{*************** 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. } + +const + SCALEBITS = 16; { speediest right-shift on some machines } + ONE_HALF = (INT32(1) shl (SCALEBITS-1)); + + +{ Initialize tables for YCC->RGB colorspace conversion. } + +{LOCAL} +procedure build_ycc_rgb_table (cinfo : j_decompress_ptr); +const + FIX_1_40200 = INT32(Round( 1.40200 * (1 shl SCALEBITS))); + FIX_1_77200 = INT32(Round( 1.77200 * (1 shl SCALEBITS))); + FIX_0_71414 = INT32(Round( 0.71414 * (1 shl SCALEBITS))); + FIX_0_34414 = INT32(Round( 0.34414 * (1 shl SCALEBITS))); + +var + cconvert : my_cconvert_ptr; + i : int; + x : INT32; +var + shift_temp : INT32; +begin + cconvert := my_cconvert_ptr (cinfo^.cconvert); + + + cconvert^.Cr_r_tab := int_table_ptr( + cinfo^.mem^.alloc_small ( j_common_ptr(cinfo), JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)) ); + cconvert^.Cb_b_tab := int_table_ptr ( + cinfo^.mem^.alloc_small ( j_common_ptr(cinfo), JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)) ); + cconvert^.Cr_g_tab := INT32_table_ptr ( + cinfo^.mem^.alloc_small ( j_common_ptr(cinfo), JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)) ); + cconvert^.Cb_g_tab := INT32_table_ptr ( + cinfo^.mem^.alloc_small ( j_common_ptr(cinfo), JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)) ); + + + x := -CENTERJSAMPLE; + for i := 0 to MAXJSAMPLE do + begin + { 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 } + + shift_temp := FIX_1_40200 * x + ONE_HALF; + if shift_temp < 0 then { SHIFT arithmetic RIGHT } + cconvert^.Cr_r_tab^[i] := int((shift_temp shr SCALEBITS) + or ( (not INT32(0)) shl (32-SCALEBITS))) + else + cconvert^.Cr_r_tab^[i] := int(shift_temp shr SCALEBITS); + + { Cb=>B value is nearest int to 1.77200 * x } + shift_temp := FIX_1_77200 * x + ONE_HALF; + if shift_temp < 0 then { SHIFT arithmetic RIGHT } + cconvert^.Cb_b_tab^[i] := int((shift_temp shr SCALEBITS) + or ( (not INT32(0)) shl (32-SCALEBITS))) + else + cconvert^.Cb_b_tab^[i] := int(shift_temp shr 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; + Inc(x); + end; +end; + + +{ 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} +procedure ycc_rgb_convert (cinfo : j_decompress_ptr; + input_buf : JSAMPIMAGE; + input_row : JDIMENSION; + output_buf : JSAMPARRAY; + num_rows : int); +var + cconvert : my_cconvert_ptr; + {register} y, cb, cr : int; + {register} outptr : JSAMPROW; + {register} inptr0, inptr1, inptr2 : JSAMPROW; + {register} col : JDIMENSION; + num_cols : JDIMENSION; + { copy these pointers into registers if possible } + {register} range_limit : range_limit_table_ptr; + {register} Crrtab : int_table_ptr; + {register} Cbbtab : int_table_ptr; + {register} Crgtab : INT32_table_ptr; + {register} Cbgtab : INT32_table_ptr; +var + shift_temp : INT32; +begin + cconvert := my_cconvert_ptr (cinfo^.cconvert); + num_cols := cinfo^.output_width; + range_limit := cinfo^.sample_range_limit; + Crrtab := cconvert^.Cr_r_tab; + Cbbtab := cconvert^.Cb_b_tab; + Crgtab := cconvert^.Cr_g_tab; + Cbgtab := cconvert^.Cb_g_tab; + + while (num_rows > 0) do + begin + Dec(num_rows); + inptr0 := input_buf^[0]^[input_row]; + inptr1 := input_buf^[1]^[input_row]; + inptr2 := input_buf^[2]^[input_row]; + Inc(input_row); + outptr := output_buf^[0]; + Inc(JSAMPROW_PTR(output_buf)); + for col := 0 to pred(num_cols) do + begin + 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]]; + shift_temp := Cbgtab^[cb] + Crgtab^[cr]; + if shift_temp < 0 then { SHIFT arithmetic RIGHT } + outptr^[RGB_GREEN] := range_limit^[y + int((shift_temp shr SCALEBITS) + or ( (not INT32(0)) shl (32-SCALEBITS)))] + else + outptr^[RGB_GREEN] := range_limit^[y + int(shift_temp shr SCALEBITS)]; + + outptr^[RGB_BLUE] := range_limit^[y + Cbbtab^[cb]]; + Inc(JSAMPLE_PTR(outptr), RGB_PIXELSIZE); + end; + end; +end; + + +{*************** Cases other than YCbCr -> RGB *************} + + +{ Color conversion for no colorspace change: just copy the data, + converting from separate-planes to interleaved representation. } + +{METHODDEF} +procedure null_convert (cinfo : j_decompress_ptr; + input_buf : JSAMPIMAGE; + input_row : JDIMENSION; + output_buf : JSAMPARRAY; + num_rows : int); +var + {register} inptr, + outptr : JSAMPLE_PTR; + {register} count : JDIMENSION; + {register} num_components : int; + num_cols : JDIMENSION; + ci : int; +begin + num_components := cinfo^.num_components; + num_cols := cinfo^.output_width; + + while (num_rows > 0) do + begin + Dec(num_rows); + for ci := 0 to pred(num_components) do + begin + inptr := JSAMPLE_PTR(input_buf^[ci]^[input_row]); + outptr := JSAMPLE_PTR(@(output_buf^[0]^[ci])); + + for count := pred(num_cols) downto 0 do + begin + outptr^ := inptr^; { needn't bother with GETJSAMPLE() here } + Inc(inptr); + Inc(outptr, num_components); + end; + end; + Inc(input_row); + Inc(JSAMPROW_PTR(output_buf)); + end; +end; + + +{ 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} +procedure grayscale_convert (cinfo : j_decompress_ptr; + input_buf : JSAMPIMAGE; + input_row : JDIMENSION; + output_buf : JSAMPARRAY; + num_rows : int); +begin + jcopy_sample_rows(input_buf^[0], int(input_row), output_buf, 0, + num_rows, cinfo^.output_width); +end; + +{ 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} +procedure gray_rgb_convert (cinfo : j_decompress_ptr; + input_buf : JSAMPIMAGE; + input_row : JDIMENSION; + output_buf : JSAMPARRAY; + num_rows : int); +var + {register} inptr, outptr : JSAMPLE_PTR; + {register} col : JDIMENSION; + num_cols : JDIMENSION; +begin + num_cols := cinfo^.output_width; + while (num_rows > 0) do + begin + inptr := JSAMPLE_PTR(input_buf^[0]^[input_row]); + Inc(input_row); + outptr := JSAMPLE_PTR(@output_buf^[0]); + Inc(JSAMPROW_PTR(output_buf)); + for col := 0 to pred(num_cols) do + begin + { We can dispense with GETJSAMPLE() here } + JSAMPROW(outptr)^[RGB_RED] := inptr^; + JSAMPROW(outptr)^[RGB_GREEN] := inptr^; + JSAMPROW(outptr)^[RGB_BLUE] := inptr^; + Inc(inptr); + Inc(outptr, RGB_PIXELSIZE); + end; + Dec(num_rows); + end; +end; + + +{ 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} +procedure ycck_cmyk_convert (cinfo : j_decompress_ptr; + input_buf : JSAMPIMAGE; + input_row : JDIMENSION; + output_buf : JSAMPARRAY; + num_rows : int); +var + cconvert : my_cconvert_ptr; + {register} y, cb, cr : int; + {register} outptr : JSAMPROW; + {register} inptr0, inptr1, inptr2, inptr3 : JSAMPROW; + {register} col : JDIMENSION; + num_cols : JDIMENSION; + { copy these pointers into registers if possible } + {register} range_limit : range_limit_table_ptr; + {register} Crrtab : int_table_ptr; + {register} Cbbtab : int_table_ptr; + {register} Crgtab : INT32_table_ptr; + {register} Cbgtab : INT32_table_ptr; +var + shift_temp : INT32; +begin + cconvert := my_cconvert_ptr (cinfo^.cconvert); + num_cols := cinfo^.output_width; + { copy these pointers into registers if possible } + range_limit := cinfo^.sample_range_limit; + Crrtab := cconvert^.Cr_r_tab; + Cbbtab := cconvert^.Cb_b_tab; + Crgtab := cconvert^.Cr_g_tab; + Cbgtab := cconvert^.Cb_g_tab; + + while (num_rows > 0) do + begin + Dec(num_rows); + inptr0 := input_buf^[0]^[input_row]; + inptr1 := input_buf^[1]^[input_row]; + inptr2 := input_buf^[2]^[input_row]; + inptr3 := input_buf^[3]^[input_row]; + Inc(input_row); + outptr := output_buf^[0]; + Inc(JSAMPROW_PTR(output_buf)); + for col := 0 to pred(num_cols) do + begin + 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 } + shift_temp := Cbgtab^[cb] + Crgtab^[cr]; + if shift_temp < 0 then + outptr^[1] := range_limit^[MAXJSAMPLE - (y + int( + (shift_temp shr SCALEBITS) or ((not INT32(0)) shl (32-SCALEBITS)) + ) )] + else + outptr^[1] := range_limit^[MAXJSAMPLE - { green } + (y + int(shift_temp shr SCALEBITS) )]; + outptr^[2] := range_limit^[MAXJSAMPLE - (y + Cbbtab^[cb])]; { blue } + { K passes through unchanged } + outptr^[3] := inptr3^[col]; { don't need GETJSAMPLE here } + Inc(JSAMPLE_PTR(outptr), 4); + end; + end; +end; + + +{ Empty method for start_pass. } + +{METHODDEF} +procedure start_pass_dcolor (cinfo : j_decompress_ptr); +begin + { no work needed } +end; + + +{ Module initialization routine for output colorspace conversion. } + +{GLOBAL} +procedure jinit_color_deconverter (cinfo : j_decompress_ptr); +var + cconvert : my_cconvert_ptr; + ci : int; +begin + cconvert := my_cconvert_ptr ( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + SIZEOF(my_color_deconverter)) ); + cinfo^.cconvert := jpeg_color_deconverter_ptr (cconvert); + cconvert^.pub.start_pass := start_pass_dcolor; + + { Make sure num_components agrees with jpeg_color_space } + case (cinfo^.jpeg_color_space) of + JCS_GRAYSCALE: + if (cinfo^.num_components <> 1) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_J_COLORSPACE); + + JCS_RGB, + JCS_YCbCr: + if (cinfo^.num_components <> 3) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_J_COLORSPACE); + + JCS_CMYK, + JCS_YCCK: + if (cinfo^.num_components <> 4) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_J_COLORSPACE); + + else { JCS_UNKNOWN can be anything } + if (cinfo^.num_components < 1) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_J_COLORSPACE); + end; + + { 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. } + + case (cinfo^.out_color_space) of + JCS_GRAYSCALE: + begin + cinfo^.out_color_components := 1; + if (cinfo^.jpeg_color_space = JCS_GRAYSCALE) + or (cinfo^.jpeg_color_space = JCS_YCbCr) then + begin + cconvert^.pub.color_convert := grayscale_convert; + { For color -> grayscale conversion, only the + Y (0) component is needed } + for ci := 1 to pred(cinfo^.num_components) do + cinfo^.comp_info^[ci].component_needed := FALSE; + end + else + ERREXIT(j_common_ptr(cinfo), JERR_CONVERSION_NOTIMPL); + end; + + JCS_RGB: + begin + cinfo^.out_color_components := RGB_PIXELSIZE; + if (cinfo^.jpeg_color_space = JCS_YCbCr) then + begin + cconvert^.pub.color_convert := ycc_rgb_convert; + build_ycc_rgb_table(cinfo); + end + else + if (cinfo^.jpeg_color_space = JCS_GRAYSCALE) then + begin + cconvert^.pub.color_convert := gray_rgb_convert; + end + else + if (cinfo^.jpeg_color_space = JCS_RGB) and (RGB_PIXELSIZE = 3) then + begin + cconvert^.pub.color_convert := null_convert; + end + else + ERREXIT(j_common_ptr(cinfo), JERR_CONVERSION_NOTIMPL); + end; + + JCS_CMYK: + begin + cinfo^.out_color_components := 4; + if (cinfo^.jpeg_color_space = JCS_YCCK) then + begin + cconvert^.pub.color_convert := ycck_cmyk_convert; + build_ycc_rgb_table(cinfo); + end + else + if (cinfo^.jpeg_color_space = JCS_CMYK) then + begin + cconvert^.pub.color_convert := null_convert; + end + else + ERREXIT(j_common_ptr(cinfo), JERR_CONVERSION_NOTIMPL); + end; + + else + begin { Permit null conversion to same output space } + if (cinfo^.out_color_space = cinfo^.jpeg_color_space) then + begin + cinfo^.out_color_components := cinfo^.num_components; + cconvert^.pub.color_convert := null_convert; + end + else { unsupported non-null conversion } + ERREXIT(j_common_ptr(cinfo), JERR_CONVERSION_NOTIMPL); + end; + end; + + if (cinfo^.quantize_colors) then + cinfo^.output_components := 1 { single colormapped output component } + else + cinfo^.output_components := cinfo^.out_color_components; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjdct.pas b/resources/libraries/deskew/Imaging/JpegLib/imjdct.pas new file mode 100755 index 0000000..30d3356 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjdct.pas @@ -0,0 +1,109 @@ +unit imjdct; + +{ Orignal: jdct.h; Copyright (C) 1994-1996, Thomas G. Lane. } + +{ 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). } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg; + + +{ 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. } + + +{$ifdef BITS_IN_JSAMPLE_IS_8} +type + DCTELEM = int; { 16 or 32 bits is fine } +{$else} +type { must have 32 bits } + DCTELEM = INT32; +{$endif} +type + jTDctElem = 0..(MaxInt div SizeOf(DCTELEM))-1; + DCTELEM_FIELD = array[jTDctElem] of DCTELEM; + DCTELEM_FIELD_PTR = ^DCTELEM_FIELD; + DCTELEMPTR = ^DCTELEM; + +type + forward_DCT_method_ptr = procedure(var data : array of DCTELEM); + float_DCT_method_ptr = procedure(var data : array of FAST_FLOAT); + + +{ 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. } + + +type + ISLOW_MULT_TYPE = MULTIPLIER; { short or int, whichever is faster } + +{$ifdef BITS_IN_JSAMPLE_IS_8} +type + IFAST_MULT_TYPE = MULTIPLIER; { 16 bits is OK, use short if faster } +const + IFAST_SCALE_BITS = 2; { fractional bits in scale factors } +{$else} +type + IFAST_MULT_TYPE = INT32; { need 32 bits for scaled quantizers } +const + IFAST_SCALE_BITS = 13; { fractional bits in scale factors } +{$endif} +type + FLOAT_MULT_TYPE = FAST_FLOAT; { preferred floating type } + +const + RANGE_MASK = (MAXJSAMPLE * 4 + 3); { 2 bits wider than legal samples } + +type + jTMultType = 0..(MaxInt div SizeOf(ISLOW_MULT_TYPE))-1; + ISLOW_MULT_TYPE_FIELD = array[jTMultType] of ISLOW_MULT_TYPE; + ISLOW_MULT_TYPE_FIELD_PTR = ^ISLOW_MULT_TYPE_FIELD; + ISLOW_MULT_TYPE_PTR = ^ISLOW_MULT_TYPE; + + jTFloatType = 0..(MaxInt div SizeOf(FLOAT_MULT_TYPE))-1; + FLOAT_MULT_TYPE_FIELD = array[jTFloatType] of FLOAT_MULT_TYPE; + FLOAT_MULT_TYPE_FIELD_PTR = ^FLOAT_MULT_TYPE_FIELD; + FLOAT_MULT_TYPE_PTR = ^FLOAT_MULT_TYPE; + + jTFastType = 0..(MaxInt div SizeOf(IFAST_MULT_TYPE))-1; + IFAST_MULT_TYPE_FIELD = array[jTFastType] of IFAST_MULT_TYPE; + IFAST_MULT_TYPE_FIELD_PTR = ^IFAST_MULT_TYPE_FIELD; + IFAST_MULT_TYPE_PTR = ^IFAST_MULT_TYPE; + +type + jTFastFloat = 0..(MaxInt div SizeOf(FAST_FLOAT))-1; + FAST_FLOAT_FIELD = array[jTFastFloat] of FAST_FLOAT; + FAST_FLOAT_FIELD_PTR = ^FAST_FLOAT_FIELD; + FAST_FLOAT_PTR = ^FAST_FLOAT; + +implementation + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjddctmgr.pas b/resources/libraries/deskew/Imaging/JpegLib/imjddctmgr.pas new file mode 100755 index 0000000..28b68be --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjddctmgr.pas @@ -0,0 +1,328 @@ +unit imjddctmgr; + +{ Original : jddctmgr.c ; Copyright (C) 1994-1996, Thomas G. Lane. } + +{ 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. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjdeferr, + imjerror, + imjpeglib, + imjdct, { Private declarations for DCT subsystem } + imjidctfst, + {$IFDEF BASM} + imjidctasm, + {$ELSE} + imjidctint, + {$ENDIF} + imjidctflt, + imjidctred; + + + +{ Initialize IDCT manager. } + +{GLOBAL} +procedure jinit_inverse_dct (cinfo : j_decompress_ptr); + + +implementation + +{ 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 } + +type + my_idct_ptr = ^my_idct_controller; + my_idct_controller = record + pub : jpeg_inverse_dct; { 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. } + + cur_method : array[0..MAX_COMPONENTS-1] of int; + end; {my_idct_controller;} + + +{ Allocated multiplier tables: big enough for any supported variant } + +type + multiplier_table = record + case byte of + 0:(islow_array : array[0..DCTSIZE2-1] of ISLOW_MULT_TYPE); + {$ifdef DCT_IFAST_SUPPORTED} + 1:(ifast_array : array[0..DCTSIZE2-1] of IFAST_MULT_TYPE); + {$endif} + {$ifdef DCT_FLOAT_SUPPORTED} + 2:(float_array : array[0..DCTSIZE2-1] of FLOAT_MULT_TYPE); + {$endif} + end; + + +{ 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} +procedure start_pass (cinfo : j_decompress_ptr); +var + idct : my_idct_ptr; + ci, i : int; + compptr : jpeg_component_info_ptr; + method : J_DCT_METHOD; + method_ptr : inverse_DCT_method_ptr; + qtbl : JQUANT_TBL_PTR; +{$ifdef PROVIDE_ISLOW_TABLES} +var + ismtbl : ISLOW_MULT_TYPE_FIELD_PTR; +{$endif} +{$ifdef DCT_IFAST_SUPPORTED} +const + CONST_BITS = 14; +const + aanscales : array[0..DCTSIZE2-1] of INT16 = + ({ 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); +var + ifmtbl : IFAST_MULT_TYPE_FIELD_PTR; + {SHIFT_TEMPS} + + { 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. } + + function DESCALE(x : INT32; n : int) : INT32; + var + shift_temp : INT32; + begin + {$ifdef RIGHT_SHIFT_IS_UNSIGNED} + shift_temp := x + (INT32(1) shl (n-1)); + if shift_temp < 0 then + Descale := (shift_temp shr n) or ((not INT32(0)) shl (32-n)) + else + Descale := (shift_temp shr n); + {$else} + Descale := (x + (INT32(1) shl (n-1)) shr n; + {$endif} + end; + +{$endif} +{$ifdef DCT_FLOAT_SUPPORTED} +const + aanscalefactor : array[0..DCTSIZE-1] of double = + (1.0, 1.387039845, 1.306562965, 1.175875602, + 1.0, 0.785694958, 0.541196100, 0.275899379); +var + fmtbl : FLOAT_MULT_TYPE_FIELD_PTR; + row, col : int; +{$endif} +begin + idct := my_idct_ptr (cinfo^.idct); + method := J_DCT_METHOD(0); + method_ptr := NIL; + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + + for ci := 0 to pred(cinfo^.num_components) do + begin + { Select the proper IDCT routine for this component's scaling } + case (compptr^.DCT_scaled_size) of +{$ifdef IDCT_SCALING_SUPPORTED} + 1:begin + method_ptr := jpeg_idct_1x1; + method := JDCT_ISLOW; { jidctred uses islow-style table } + end; + 2:begin + method_ptr := jpeg_idct_2x2; + method := JDCT_ISLOW; { jidctred uses islow-style table } + end; + 4:begin + method_ptr := jpeg_idct_4x4; + method := JDCT_ISLOW; { jidctred uses islow-style table } + end; +{$endif} + DCTSIZE: + case (cinfo^.dct_method) of +{$ifdef DCT_ISLOW_SUPPORTED} + JDCT_ISLOW: + begin + method_ptr := @jpeg_idct_islow; + method := JDCT_ISLOW; + end; +{$endif} +{$ifdef DCT_IFAST_SUPPORTED} + JDCT_IFAST: + begin + method_ptr := @jpeg_idct_ifast; + method := JDCT_IFAST; + end; +{$endif} +{$ifdef DCT_FLOAT_SUPPORTED} + JDCT_FLOAT: + begin + method_ptr := @jpeg_idct_float; + method := JDCT_FLOAT; + end; +{$endif} + else + ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); + end; + else + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_DCTSIZE, compptr^.DCT_scaled_size); + end; + 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 (not compptr^.component_needed) or (idct^.cur_method[ci] = int(method)) then + continue; + qtbl := compptr^.quant_table; + if (qtbl = NIL) then { happens if no data yet for component } + continue; + idct^.cur_method[ci] := int(method); + case (method) of +{$ifdef PROVIDE_ISLOW_TABLES} + JDCT_ISLOW: + begin + { For LL&M IDCT method, multipliers are equal to raw quantization + coefficients, but are stored as ints to ensure access efficiency. } + + ismtbl := ISLOW_MULT_TYPE_FIELD_PTR (compptr^.dct_table); + for i := 0 to pred(DCTSIZE2) do + begin + ismtbl^[i] := ISLOW_MULT_TYPE (qtbl^.quantval[i]); + end; + end; +{$endif} +{$ifdef DCT_IFAST_SUPPORTED} + JDCT_IFAST: + begin + { 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. } + + ifmtbl := IFAST_MULT_TYPE_FIELD_PTR (compptr^.dct_table); + + for i := 0 to pred(DCTSIZE2) do + begin + ifmtbl^[i] := IFAST_MULT_TYPE( + DESCALE( INT32 (qtbl^.quantval[i]) * INT32 (aanscales[i]), + CONST_BITS-IFAST_SCALE_BITS) ); + end; + end; +{$endif} +{$ifdef DCT_FLOAT_SUPPORTED} + JDCT_FLOAT: + begin + { 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 } + + fmtbl := FLOAT_MULT_TYPE_FIELD_PTR(compptr^.dct_table); + + i := 0; + for row := 0 to pred(DCTSIZE) do + begin + for col := 0 to pred(DCTSIZE) do + begin + fmtbl^[i] := {FLOAT_MULT_TYPE} ( + {double} qtbl^.quantval[i] * + aanscalefactor[row] * aanscalefactor[col] ); + Inc(i); + end; + end; + end; +{$endif} + else + ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); + break; + end; + Inc(compptr); + end; +end; + + +{ Initialize IDCT manager. } + +{GLOBAL} +procedure jinit_inverse_dct (cinfo : j_decompress_ptr); +var + idct : my_idct_ptr; + ci : int; + compptr : jpeg_component_info_ptr; +begin + idct := my_idct_ptr( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + SIZEOF(my_idct_controller)) ); + cinfo^.idct := jpeg_inverse_dct_ptr (idct); + idct^.pub.start_pass := start_pass; + + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + { 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; + Inc(compptr); + end; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjdeferr.pas b/resources/libraries/deskew/Imaging/JpegLib/imjdeferr.pas new file mode 100755 index 0000000..eeaf6f6 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjdeferr.pas @@ -0,0 +1,497 @@ +unit imjdeferr; + +{ This file defines the error and message codes for the cjpeg/djpeg + applications. These strings are not needed as part of the JPEG library + proper. + Edit this file to add new codes, or to translate the message strings to + some other language. } + +{ Original cderror.h ; Copyright (C) 1994, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +{ 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). } + + +{ Original: jversion.h ; Copyright (C) 1991-1996, Thomas G. Lane. } +{ This file contains software version identification. } + +const + JVERSION = '6a 7-Feb-96'; + + JCOPYRIGHT = 'Copyright (C) 1996, Thomas G. Lane'; + + JNOTICE = 'Pascal Translation, Copyright (C) 1996, Jacques Nomssi Nzali'; + +{ 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. } + +type + J_MESSAGE_CODE =( + JMSG_NOMESSAGE, + JERR_ARITH_NOTIMPL, + JERR_BAD_ALIGN_TYPE, + JERR_BAD_ALLOC_CHUNK, + JERR_BAD_BUFFER_MODE, + JERR_BAD_COMPONENT_ID, + JERR_BAD_DCT_COEF, + JERR_BAD_DCTSIZE, + JERR_BAD_HUFF_TABLE, + JERR_BAD_IN_COLORSPACE, + JERR_BAD_J_COLORSPACE, + JERR_BAD_LENGTH, + JERR_BAD_LIB_VERSION, + JERR_BAD_MCU_SIZE, + JERR_BAD_POOL_ID, + JERR_BAD_PRECISION, + JERR_BAD_PROGRESSION, + JERR_BAD_PROG_SCRIPT, + JERR_BAD_SAMPLING, + JERR_BAD_SCAN_SCRIPT, + JERR_BAD_STATE, + JERR_BAD_STRUCT_SIZE, + JERR_BAD_VIRTUAL_ACCESS, + JERR_BUFFER_SIZE, + JERR_CANT_SUSPEND, + JERR_CCIR601_NOTIMPL, + JERR_COMPONENT_COUNT, + JERR_CONVERSION_NOTIMPL, + JERR_DAC_INDEX, + JERR_DAC_VALUE, + JERR_DHT_COUNTS, + JERR_DHT_INDEX, + JERR_DQT_INDEX, + JERR_EMPTY_IMAGE, + JERR_EMS_READ, + JERR_EMS_WRITE, + JERR_EOI_EXPECTED, + JERR_FILE_READ, + JERR_FILE_WRITE, + JERR_FRACT_SAMPLE_NOTIMPL, + JERR_HUFF_CLEN_OVERFLOW, + JERR_HUFF_MISSING_CODE, + JERR_IMAGE_TOO_BIG, + JERR_INPUT_EMPTY, + JERR_INPUT_EOF, + JERR_MISMATCHED_QUANT_TABLE, + JERR_MISSING_DATA, + JERR_MODE_CHANGE, + JERR_NOTIMPL, + JERR_NOT_COMPILED, + JERR_NO_BACKING_STORE, + JERR_NO_HUFF_TABLE, + JERR_NO_IMAGE, + JERR_NO_QUANT_TABLE, + JERR_NO_SOI, + JERR_OUT_OF_MEMORY, + JERR_QUANT_COMPONENTS, + JERR_QUANT_FEW_COLORS, + JERR_QUANT_MANY_COLORS, + JERR_SOF_DUPLICATE, + JERR_SOF_NO_SOS, + JERR_SOF_UNSUPPORTED, + JERR_SOI_DUPLICATE, + JERR_SOS_NO_SOF, + JERR_TFILE_CREATE, + JERR_TFILE_READ, + JERR_TFILE_SEEK, + JERR_TFILE_WRITE, + JERR_TOO_LITTLE_DATA, + JERR_UNKNOWN_MARKER, + JERR_VIRTUAL_BUG, + JERR_WIDTH_OVERFLOW, + JERR_XMS_READ, + JERR_XMS_WRITE, + JMSG_COPYRIGHT, + JMSG_VERSION, + JTRC_16BIT_TABLES, + JTRC_ADOBE, + JTRC_APP0, + JTRC_APP14, + JTRC_DAC, + JTRC_DHT, + JTRC_DQT, + JTRC_DRI, + JTRC_EMS_CLOSE, + JTRC_EMS_OPEN, + JTRC_EOI, + JTRC_HUFFBITS, + JTRC_JFIF, + JTRC_JFIF_BADTHUMBNAILSIZE, + JTRC_JFIF_EXTENSION, + JTRC_JFIF_THUMBNAIL, + JTRC_MISC_MARKER, + JTRC_PARMLESS_MARKER, + JTRC_QUANTVALS, + JTRC_QUANT_3_NCOLORS, + JTRC_QUANT_NCOLORS, + JTRC_QUANT_SELECTED, + JTRC_RECOVERY_ACTION, + JTRC_RST, + JTRC_SMOOTH_NOTIMPL, + JTRC_SOF, + JTRC_SOF_COMPONENT, + JTRC_SOI, + JTRC_SOS, + JTRC_SOS_COMPONENT, + JTRC_SOS_PARAMS, + JTRC_TFILE_CLOSE, + JTRC_TFILE_OPEN, + JTRC_THUMB_JPEG, + JTRC_THUMB_PALETTE, + JTRC_THUMB_RGB, + JTRC_UNKNOWN_IDS, + JTRC_XMS_CLOSE, + JTRC_XMS_OPEN, + JWRN_ADOBE_XFORM, + JWRN_BOGUS_PROGRESSION, + JWRN_EXTRANEOUS_DATA, + JWRN_HIT_MARKER, + JWRN_HUFF_BAD_CODE, + JWRN_JFIF_MAJOR, + JWRN_JPEG_EOF, + JWRN_MUST_RESYNC, + JWRN_NOT_SEQUENTIAL, + JWRN_TOO_MUCH_DATA, + + + JMSG_FIRSTADDONCODE, { Must be first entry! } + + {$ifdef BMP_SUPPORTED} + JERR_BMP_BADCMAP, { Unsupported BMP colormap format } + JERR_BMP_BADDEPTH, { Only 8- and 24-bit BMP files are supported } + JERR_BMP_BADHEADER, { Invalid BMP file: bad header length } + JERR_BMP_BADPLANES, { Invalid BMP file: biPlanes not equal to 1 } + JERR_BMP_COLORSPACE, { BMP output must be grayscale or RGB } + JERR_BMP_COMPRESSED, { Sorry, compressed BMPs not yet supported } + JERR_BMP_NOT, { Not a BMP file - does not start with BM } + JTRC_BMP, { %dx%d 24-bit BMP image } + JTRC_BMP_MAPPED, { %dx%d 8-bit colormapped BMP image } + JTRC_BMP_OS2, { %dx%d 24-bit OS2 BMP image } + JTRC_BMP_OS2_MAPPED, { %dx%d 8-bit colormapped OS2 BMP image } + {$endif} { BMP_SUPPORTED } + + {$ifdef GIF_SUPPORTED} + JERR_GIF_BUG, { GIF output got confused } + JERR_GIF_CODESIZE, { Bogus GIF codesize %d } + JERR_GIF_COLORSPACE, { GIF output must be grayscale or RGB } + JERR_GIF_IMAGENOTFOUND, { Too few images in GIF file } + JERR_GIF_NOT, { Not a GIF file } + JTRC_GIF, { %dx%dx%d GIF image } + JTRC_GIF_BADVERSION, + { Warning: unexpected GIF version number '%c%c%c' } + JTRC_GIF_EXTENSION, { Ignoring GIF extension block of type 0x%02x } + JTRC_GIF_NONSQUARE, { Caution: nonsquare pixels in input } + JWRN_GIF_BADDATA, { Corrupt data in GIF file } + JWRN_GIF_CHAR, { Bogus char 0x%02x in GIF file, ignoring } + JWRN_GIF_ENDCODE, { Premature end of GIF image } + JWRN_GIF_NOMOREDATA, { Ran out of GIF bits } + {$endif} { GIF_SUPPORTED } + + {$ifdef PPM_SUPPORTED} + JERR_PPM_COLORSPACE, { PPM output must be grayscale or RGB } + JERR_PPM_NONNUMERIC, { Nonnumeric data in PPM file } + JERR_PPM_NOT, { Not a PPM file } + JTRC_PGM, { %dx%d PGM image } + JTRC_PGM_TEXT, { %dx%d text PGM image } + JTRC_PPM, { %dx%d PPM image } + JTRC_PPM_TEXT, { %dx%d text PPM image } + {$endif} { PPM_SUPPORTED } + + {$ifdef RLE_SUPPORTED} + JERR_RLE_BADERROR, { Bogus error code from RLE library } + JERR_RLE_COLORSPACE, { RLE output must be grayscale or RGB } + JERR_RLE_DIMENSIONS, { Image dimensions (%dx%d) too large for RLE } + JERR_RLE_EMPTY, { Empty RLE file } + JERR_RLE_EOF, { Premature EOF in RLE header } + JERR_RLE_MEM, { Insufficient memory for RLE header } + JERR_RLE_NOT, { Not an RLE file } + JERR_RLE_TOOMANYCHANNELS, { Cannot handle %d output channels for RLE } + JERR_RLE_UNSUPPORTED, { Cannot handle this RLE setup } + JTRC_RLE, { %dx%d full-color RLE file } + JTRC_RLE_FULLMAP, { %dx%d full-color RLE file with map of length %d } + JTRC_RLE_GRAY, { %dx%d grayscale RLE file } + JTRC_RLE_MAPGRAY, { %dx%d grayscale RLE file with map of length %d } + JTRC_RLE_MAPPED, { %dx%d colormapped RLE file with map of length %d } + {$endif} { RLE_SUPPORTED } + + {$ifdef TARGA_SUPPORTED} + JERR_TGA_BADCMAP, { Unsupported Targa colormap format } + JERR_TGA_BADPARMS, { Invalid or unsupported Targa file } + JERR_TGA_COLORSPACE, { Targa output must be grayscale or RGB } + JTRC_TGA, { %dx%d RGB Targa image } + JTRC_TGA_GRAY, { %dx%d grayscale Targa image } + JTRC_TGA_MAPPED, { %dx%d colormapped Targa image } + {$else} + JERR_TGA_NOTCOMP, { Targa support was not compiled } + {$endif} { TARGA_SUPPORTED } + + JERR_BAD_CMAP_FILE, + { Color map file is invalid or of unsupported format } + JERR_TOO_MANY_COLORS, + { Output file format cannot handle %d colormap entries } + JERR_UNGETC_FAILED, { ungetc failed } + {$ifdef TARGA_SUPPORTED} + JERR_UNKNOWN_FORMAT, + { Unrecognized input file format --- perhaps you need -targa } + {$else} + JERR_UNKNOWN_FORMAT, { Unrecognized input file format } + {$endif} + JERR_UNSUPPORTED_FORMAT, { Unsupported output file format } + + JMSG_LASTADDONCODE + ); + + +const + JMSG_LASTMSGCODE : J_MESSAGE_CODE = JMSG_LASTADDONCODE; + +type + msg_table = Array[J_MESSAGE_CODE] of string[80]; +const + jpeg_std_message_table : msg_table = ( + + { JMSG_NOMESSAGE } 'Bogus message code %d', { Must be first entry! } + +{ For maintenance convenience, list is alphabetical by message code name } + { JERR_ARITH_NOTIMPL } + 'Sorry, there are legal restrictions on arithmetic coding', + { JERR_BAD_ALIGN_TYPE } 'ALIGN_TYPE is wrong, please fix', + { JERR_BAD_ALLOC_CHUNK } 'MAX_ALLOC_CHUNK is wrong, please fix', + { JERR_BAD_BUFFER_MODE } 'Bogus buffer control mode', + { JERR_BAD_COMPONENT_ID } 'Invalid component ID %d in SOS', + { JERR_BAD_DCT_COEF } 'DCT coefficient out of range', + { JERR_BAD_DCTSIZE } 'IDCT output block size %d not supported', + { JERR_BAD_HUFF_TABLE } 'Bogus Huffman table definition', + { JERR_BAD_IN_COLORSPACE } 'Bogus input colorspace', + { JERR_BAD_J_COLORSPACE } 'Bogus JPEG colorspace', + { JERR_BAD_LENGTH } 'Bogus marker length', + { JERR_BAD_LIB_VERSION } + 'Wrong JPEG library version: library is %d, caller expects %d', + { JERR_BAD_MCU_SIZE } 'Sampling factors too large for interleaved scan', + { JERR_BAD_POOL_ID } 'Invalid memory pool code %d', + { JERR_BAD_PRECISION } 'Unsupported JPEG data precision %d', + { JERR_BAD_PROGRESSION } + 'Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d', + { JERR_BAD_PROG_SCRIPT } + 'Invalid progressive parameters at scan script entry %d', + { JERR_BAD_SAMPLING } 'Bogus sampling factors', + { JERR_BAD_SCAN_SCRIPT } 'Invalid scan script at entry %d', + { JERR_BAD_STATE } 'Improper call to JPEG library in state %d', + { JERR_BAD_STRUCT_SIZE } + 'JPEG parameter struct mismatch: library thinks size is %d, caller expects %d', + { JERR_BAD_VIRTUAL_ACCESS } 'Bogus virtual array access', + { JERR_BUFFER_SIZE } 'Buffer passed to JPEG library is too small', + { JERR_CANT_SUSPEND } 'Suspension not allowed here', + { JERR_CCIR601_NOTIMPL } 'CCIR601 sampling not implemented yet', + { JERR_COMPONENT_COUNT } 'Too many color components: %d, max %d', + { JERR_CONVERSION_NOTIMPL } 'Unsupported color conversion request', + { JERR_DAC_INDEX } 'Bogus DAC index %d', + { JERR_DAC_VALUE } 'Bogus DAC value $%x', + { JERR_DHT_COUNTS } 'Bogus DHT counts', + { JERR_DHT_INDEX } 'Bogus DHT index %d', + { JERR_DQT_INDEX } 'Bogus DQT index %d', + { JERR_EMPTY_IMAGE } 'Empty JPEG image (DNL not supported)', + { JERR_EMS_READ } 'Read from EMS failed', + { JERR_EMS_WRITE } 'Write to EMS failed', + { JERR_EOI_EXPECTED } 'Didn''t expect more than one scan', + { JERR_FILE_READ } 'Input file read error', + { JERR_FILE_WRITE } 'Output file write error --- out of disk space?', + { JERR_FRACT_SAMPLE_NOTIMPL } 'Fractional sampling not implemented yet', + { JERR_HUFF_CLEN_OVERFLOW } 'Huffman code size table overflow', + { JERR_HUFF_MISSING_CODE } 'Missing Huffman code table entry', + { JERR_IMAGE_TOO_BIG } 'Maximum supported image dimension is %d pixels', + { JERR_INPUT_EMPTY } 'Empty input file', + { JERR_INPUT_EOF } 'Premature end of input file', + { JERR_MISMATCHED_QUANT_TABLE } + 'Cannot transcode due to multiple use of quantization table %d', + { JERR_MISSING_DATA } 'Scan script does not transmit all data', + { JERR_MODE_CHANGE } 'Invalid color quantization mode change', + { JERR_NOTIMPL } 'Not implemented yet', + { JERR_NOT_COMPILED } 'Requested feature was omitted at compile time', + { JERR_NO_BACKING_STORE } 'Backing store not supported', + { JERR_NO_HUFF_TABLE } 'Huffman table $%02x was not defined', + { JERR_NO_IMAGE } 'JPEG datastream contains no image', + { JERR_NO_QUANT_TABLE } 'Quantization table $%02x was not defined', + { JERR_NO_SOI } 'Not a JPEG file: starts with $%02x $%02x', + { JERR_OUT_OF_MEMORY } 'Insufficient memory (case %d)', + { JERR_QUANT_COMPONENTS } + 'Cannot quantize more than %d color components', + { JERR_QUANT_FEW_COLORS } 'Cannot quantize to fewer than %d colors', + { JERR_QUANT_MANY_COLORS } 'Cannot quantize to more than %d colors', + { JERR_SOF_DUPLICATE } 'Invalid JPEG file structure: two SOF markers', + { JERR_SOF_NO_SOS } 'Invalid JPEG file structure: missing SOS marker', + { JERR_SOF_UNSUPPORTED } 'Unsupported JPEG process: SOF type $%02x', + { JERR_SOI_DUPLICATE } 'Invalid JPEG file structure: two SOI markers', + { JERR_SOS_NO_SOF } 'Invalid JPEG file structure: SOS before SOF', + { JERR_TFILE_CREATE } 'Failed to create temporary file %s', + { JERR_TFILE_READ } 'Read failed on temporary file', + { JERR_TFILE_SEEK } 'Seek failed on temporary file', + { JERR_TFILE_WRITE } + 'Write failed on temporary file --- out of disk space?', + { JERR_TOO_LITTLE_DATA } 'Application transferred too few scanlines', + { JERR_UNKNOWN_MARKER } 'Unsupported marker type $%02x', + { JERR_VIRTUAL_BUG } 'Virtual array controller messed up', + { JERR_WIDTH_OVERFLOW } 'Image too wide for this implementation', + { JERR_XMS_READ } 'Read from XMS failed', + { JERR_XMS_WRITE } 'Write to XMS failed', + { JMSG_COPYRIGHT } JCOPYRIGHT, + { JMSG_VERSION } JVERSION, + { JTRC_16BIT_TABLES } + 'Caution: quantization tables are too coarse for baseline JPEG', + { JTRC_ADOBE } + 'Adobe APP14 marker: version %d, flags $%04x $%04x, transform %d', + { JTRC_APP0 } 'Unknown APP0 marker (not JFIF), length %d', + { JTRC_APP14 } 'Unknown APP14 marker (not Adobe), length %d', + { JTRC_DAC } 'Define Arithmetic Table $%02x: $%02x', + { JTRC_DHT } 'Define Huffman Table $%02x', + { JTRC_DQT } 'Define Quantization Table %d precision %d', + { JTRC_DRI } 'Define Restart Interval %d', + { JTRC_EMS_CLOSE } 'Freed EMS handle %d', + { JTRC_EMS_OPEN } 'Obtained EMS handle %d', + { JTRC_EOI } 'End Of Image', + { JTRC_HUFFBITS } ' %3d %3d %3d %3d %3d %3d %3d %3d', + { JTRC_JFIF } 'JFIF APP0 marker, density %dx%d %d', + { JTRC_JFIF_BADTHUMBNAILSIZE } + 'Warning: thumbnail image size does not match data length %d', + { JTRC_JFIF_EXTENSION } 'JFIF extension marker: type 0x%02x, length %u', + { JTRC_JFIF_THUMBNAIL } ' with %d x %d thumbnail image', + { JTRC_MISC_MARKER } 'Skipping marker $%02x, length %d', + { JTRC_PARMLESS_MARKER } 'Unexpected marker $%02x', + { JTRC_QUANTVALS } ' %4d %4d %4d %4d %4d %4d %4d %4d', + { JTRC_QUANT_3_NCOLORS } 'Quantizing to %d = %d*%d*%d colors', + { JTRC_QUANT_NCOLORS } 'Quantizing to %d colors', + { JTRC_QUANT_SELECTED } 'Selected %d colors for quantization', + { JTRC_RECOVERY_ACTION } 'At marker $%02x, recovery action %d', + { JTRC_RST } 'RST%d', + { JTRC_SMOOTH_NOTIMPL } + 'Smoothing not supported with nonstandard sampling ratios', + { JTRC_SOF } 'Start Of Frame $%02x: width=%d, height=%d, components=%d', + { JTRC_SOF_COMPONENT } ' Component %d: %dhx%dv q=%d', + { JTRC_SOI } 'Start of Image', + { JTRC_SOS } 'Start Of Scan: %d components', + { JTRC_SOS_COMPONENT } ' Component %d: dc=%d ac=%d', + { JTRC_SOS_PARAMS } ' Ss=%d, Se=%d, Ah=%d, Al=%d', + { JTRC_TFILE_CLOSE } 'Closed temporary file %s', + { JTRC_TFILE_OPEN } 'Opened temporary file %s', + { 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', + { JTRC_UNKNOWN_IDS } + 'Unrecognized component IDs %d %d %d, assuming YCbCr', + { JTRC_XMS_CLOSE } 'Freed XMS handle %d', + { JTRC_XMS_OPEN } 'Obtained XMS handle %d', + { JWRN_ADOBE_XFORM } 'Unknown Adobe color transform code %d', + { JWRN_BOGUS_PROGRESSION } + 'Inconsistent progression sequence for component %d coefficient %d', + { JWRN_EXTRANEOUS_DATA } + 'Corrupt JPEG data: %d extraneous bytes before marker $%02x', + { JWRN_HIT_MARKER } 'Corrupt JPEG data: premature end of data segment', + { JWRN_HUFF_BAD_CODE } 'Corrupt JPEG data: bad Huffman code', + { JWRN_JFIF_MAJOR } 'Warning: unknown JFIF revision number %d.%02d', + { JWRN_JPEG_EOF } 'Premature end of JPEG file', + { JWRN_MUST_RESYNC } + 'Corrupt JPEG data: found marker $%02x instead of RST%d', + { JWRN_NOT_SEQUENTIAL } 'Invalid SOS parameters for sequential JPEG', + { JWRN_TOO_MUCH_DATA } 'Application transferred too many scanlines', + + { JMSG_FIRSTADDONCODE } '', { Must be first entry! } + +{$ifdef BMP_SUPPORTED} + { JERR_BMP_BADCMAP } 'Unsupported BMP colormap format', + { JERR_BMP_BADDEPTH } 'Only 8- and 24-bit BMP files are supported', + { JERR_BMP_BADHEADER } 'Invalid BMP file: bad header length', + { JERR_BMP_BADPLANES } 'Invalid BMP file: biPlanes not equal to 1', + { JERR_BMP_COLORSPACE } 'BMP output must be grayscale or RGB', + { JERR_BMP_COMPRESSED } 'Sorry, compressed BMPs not yet supported', + { JERR_BMP_NOT } 'Not a BMP file - does not start with BM', + { JTRC_BMP } '%dx%d 24-bit BMP image', + { JTRC_BMP_MAPPED } '%dx%d 8-bit colormapped BMP image', + { JTRC_BMP_OS2 } '%dx%d 24-bit OS2 BMP image', + { JTRC_BMP_OS2_MAPPED } '%dx%d 8-bit colormapped OS2 BMP image', +{$endif} { BMP_SUPPORTED } + +{$ifdef GIF_SUPPORTED} + { JERR_GIF_BUG } 'GIF output got confused', + { JERR_GIF_CODESIZE } 'Bogus GIF codesize %d', + { JERR_GIF_COLORSPACE } 'GIF output must be grayscale or RGB', + { JERR_GIF_IMAGENOTFOUND } 'Too few images in GIF file', + { JERR_GIF_NOT } 'Not a GIF file', + { JTRC_GIF } '%dx%dx%d GIF image', + { JTRC_GIF_BADVERSION } + 'Warning: unexpected GIF version number "%c%c%c"', + { JTRC_GIF_EXTENSION } 'Ignoring GIF extension block of type 0x%02x', + { JTRC_GIF_NONSQUARE } 'Caution: nonsquare pixels in input', + { JWRN_GIF_BADDATA } 'Corrupt data in GIF file', + { JWRN_GIF_CHAR } 'Bogus char 0x%02x in GIF file, ignoring', + { JWRN_GIF_ENDCODE } 'Premature end of GIF image', + { JWRN_GIF_NOMOREDATA } 'Ran out of GIF bits', +{$endif} { GIF_SUPPORTED } + +{$ifdef PPM_SUPPORTED} + { JERR_PPM_COLORSPACE } 'PPM output must be grayscale or RGB', + { JERR_PPM_NONNUMERIC } 'Nonnumeric data in PPM file', + { JERR_PPM_NOT } 'Not a PPM file', + { JTRC_PGM } '%dx%d PGM image', + { JTRC_PGM_TEXT } '%dx%d text PGM image', + { JTRC_PPM } '%dx%d PPM image', + { JTRC_PPM_TEXT } '%dx%d text PPM image', +{$endif} { PPM_SUPPORTED } + +{$ifdef RLE_SUPPORTED} + { JERR_RLE_BADERROR } 'Bogus error code from RLE library', + { JERR_RLE_COLORSPACE } 'RLE output must be grayscale or RGB', + { JERR_RLE_DIMENSIONS } 'Image dimensions (%dx%d) too large for RLE', + { JERR_RLE_EMPTY } 'Empty RLE file', + { JERR_RLE_EOF } 'Premature EOF in RLE header', + { JERR_RLE_MEM } 'Insufficient memory for RLE header', + { JERR_RLE_NOT } 'Not an RLE file', + { JERR_RLE_TOOMANYCHANNELS } 'Cannot handle %d output channels for RLE', + { JERR_RLE_UNSUPPORTED } 'Cannot handle this RLE setup', + { JTRC_RLE } '%dx%d full-color RLE file', + { JTRC_RLE_FULLMAP } '%dx%d full-color RLE file with map of length %d', + { JTRC_RLE_GRAY } '%dx%d grayscale RLE file', + { JTRC_RLE_MAPGRAY } '%dx%d grayscale RLE file with map of length %d', + { JTRC_RLE_MAPPED } '%dx%d colormapped RLE file with map of length %d', +{$endif} { RLE_SUPPORTED } + +{$ifdef TARGA_SUPPORTED} + { JERR_TGA_BADCMAP } 'Unsupported Targa colormap format', + { JERR_TGA_BADPARMS } 'Invalid or unsupported Targa file', + { JERR_TGA_COLORSPACE } 'Targa output must be grayscale or RGB', + { JTRC_TGA } '%dx%d RGB Targa image', + { JTRC_TGA_GRAY } '%dx%d grayscale Targa image', + { JTRC_TGA_MAPPED } '%dx%d colormapped Targa image', +{$else} + { JERR_TGA_NOTCOMP } 'Targa support was not compiled', +{$endif} { TARGA_SUPPORTED } + + { JERR_BAD_CMAP_FILE } + 'Color map file is invalid or of unsupported format', + { JERR_TOO_MANY_COLORS } + 'Output file format cannot handle %d colormap entries', + { JERR_UNGETC_FAILED } 'ungetc failed', +{$ifdef TARGA_SUPPORTED} + { JERR_UNKNOWN_FORMAT } + 'Unrecognized input file format --- perhaps you need -targa', +{$else} + { JERR_UNKNOWN_FORMAT } 'Unrecognized input file format', +{$endif} + { JERR_UNSUPPORTED_FORMAT } 'Unsupported output file format', + + + { JMSG_LASTADDONCODE } ''); + +implementation + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjdhuff.pas b/resources/libraries/deskew/Imaging/JpegLib/imjdhuff.pas new file mode 100755 index 0000000..f56cbe9 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjdhuff.pas @@ -0,0 +1,1205 @@ +unit imjdhuff; + +{ 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. } + +{ 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. } + +{ Original: jdhuff.h+jdhuff.c; Copyright (C) 1991-1997, Thomas G. Lane. } + + + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjdeferr, + imjerror, + imjutils, + imjpeglib; + + +{ Declarations shared with jdphuff.c } + + + +{ Derived data constructed for each Huffman table } + +const + HUFF_LOOKAHEAD = 8; { # of bits of lookahead } + +type + d_derived_tbl_ptr = ^d_derived_tbl; + d_derived_tbl = record + { Basic tables: (element [0] of each array is unused) } + maxcode : array[0..18-1] of INT32; { largest code of length k (-1 if none) } + { (maxcode[17] is a sentinel to ensure jpeg_huff_decode terminates) } + valoffset : array[0..17-1] of INT32; { 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) } + pub : JHUFF_TBL_PTR; + + { 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. } + + look_nbits : array[0..(1 shl HUFF_LOOKAHEAD)-1] of int; + { # bits, or 0 if too long } + look_sym : array[0..(1 shl HUFF_LOOKAHEAD)-1] of UINT8; + { symbol, or unused } + end; + +{ 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. } + + +type + bit_buf_type = INT32 ; { type of bit-extraction buffer } +const + 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. } + +type + bitread_perm_state = record { Bitreading state saved across MCUs } + get_buffer : bit_buf_type; { current bit-extraction buffer } + bits_left : int; { # of unused bits in it } + end; + +type + bitread_working_state = record + { Bitreading working state within an MCU } + { current data source location } + { We need a copy, rather than munging the original, in case of suspension } + next_input_byte : JOCTETptr; { => next byte to read from source } + bytes_in_buffer : size_t; { # 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. } + + get_buffer : bit_buf_type; { current bit-extraction buffer } + bits_left : int; { # of unused bits in it } + { Pointer needed by jpeg_fill_bit_buffer } + cinfo : j_decompress_ptr; { back link to decompress master record } + end; + +{ Module initialization routine for Huffman entropy decoding. } + +{GLOBAL} +procedure jinit_huff_decoder (cinfo : j_decompress_ptr); + +{GLOBAL} +function jpeg_huff_decode(var state : bitread_working_state; + get_buffer : bit_buf_type; {register} + bits_left : int; {register} + htbl : d_derived_tbl_ptr; + min_bits : int) : int; + +{ Compute the derived values for a Huffman table. + Note this is also used by jdphuff.c. } + +{GLOBAL} +procedure jpeg_make_d_derived_tbl (cinfo : j_decompress_ptr; + isDC : boolean; + tblno : int; + var pdtbl : d_derived_tbl_ptr); + +{ Load up the bit buffer to a depth of at least nbits } + +function jpeg_fill_bit_buffer (var state : bitread_working_state; + get_buffer : bit_buf_type; {register} + bits_left : int; {register} + nbits : int) : boolean; + +implementation + +{$IFDEF MACRO} + +{ Macros to declare and load/save bitread local variables. } +{$define BITREAD_STATE_VARS} + get_buffer : bit_buf_type ; {register} + bits_left : int; {register} + br_state : bitread_working_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)) then + begin + if (not jpeg_fill_bit_buffer(&(state),get_buffer,bits_left,nbits)) then + begin + action; + exit; + end; + get_buffer := state.get_buffer; + bits_left := state.bits_left; + end; + + +{$define GET_BITS(nbits)} + Dec(bits_left, (nbits)); + ( (int(get_buffer shr bits_left)) and ( pred(1 shl (nbits)) ) ) + +{$define PEEK_BITS(nbits)} + int(get_buffer shr (bits_left - (nbits))) and pred(1 shl (nbits)) + +{$define DROP_BITS(nbits)} + Dec(bits_left, 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. } + + + + +macro HUFF_DECODE(s,br_state,htbl,return FALSE,slowlabel); +label showlabel; +var + nb, look : int; {register} +begin + if (bits_left < HUFF_LOOKAHEAD) then + begin + if (not jpeg_fill_bit_buffer(br_state,get_buffer,bits_left, 0)) then + begin + decode_mcu := FALSE; + exit; + end; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + if (bits_left < HUFF_LOOKAHEAD) then + begin + nb := 1; + goto slowlabel; + end; + end; + {look := PEEK_BITS(HUFF_LOOKAHEAD);} + look := int(get_buffer shr (bits_left - HUFF_LOOKAHEAD)) and + pred(1 shl HUFF_LOOKAHEAD); + + nb := htbl^.look_nbits[look]; + if (nb <> 0) then + begin + {DROP_BITS(nb);} + Dec(bits_left, nb); + + s := htbl^.look_sym[look]; + end + else + begin + nb := HUFF_LOOKAHEAD+1; +slowlabel: + s := jpeg_huff_decode(br_state,get_buffer,bits_left,htbl,nb)); + if (s < 0) then + begin + result := FALSE; + exit; + end; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + end; +end; + + +{$ENDIF} {MACRO} + +{ 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. } + +type + savable_state = record + last_dc_val : array[0..MAX_COMPS_IN_SCAN-1] of int; { last DC coef for each component } + end; + + +type + huff_entropy_ptr = ^huff_entropy_decoder; + huff_entropy_decoder = record + pub : jpeg_entropy_decoder; { public fields } + + { These fields are loaded into local variables at start of each MCU. + In case of suspension, we exit WITHOUT updating them. } + + bitstate : bitread_perm_state; { Bit buffer at start of MCU } + saved : savable_state; { Other state at start of MCU } + + { These fields are NOT loaded into local working state. } + restarts_to_go : uInt; { MCUs left in this restart interval } + + { Pointers to derived tables (these workspaces have image lifespan) } + dc_derived_tbls : array[0..NUM_HUFF_TBLS] of d_derived_tbl_ptr; + ac_derived_tbls : array[0..NUM_HUFF_TBLS] of d_derived_tbl_ptr; + + { 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 } + dc_cur_tbls : array[0..D_MAX_BLOCKS_IN_MCU-1] of d_derived_tbl_ptr; + ac_cur_tbls : array[0..D_MAX_BLOCKS_IN_MCU-1] of d_derived_tbl_ptr; + { Whether we care about the DC and AC coefficient values for each block } + dc_needed : array[0..D_MAX_BLOCKS_IN_MCU-1] of boolean; + ac_needed : array[0..D_MAX_BLOCKS_IN_MCU-1] of boolean; + end; + + + +{ Initialize for a Huffman-compressed scan. } + +{METHODDEF} +procedure start_pass_huff_decoder (cinfo : j_decompress_ptr); +var + entropy : huff_entropy_ptr; + ci, blkn, dctbl, actbl : int; + compptr : jpeg_component_info_ptr; +begin + entropy := huff_entropy_ptr (cinfo^.entropy); + + { 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) or (cinfo^.Se <> DCTSIZE2-1) or + (cinfo^.Ah <> 0) or (cinfo^.Al <> 0) then + WARNMS(j_common_ptr(cinfo), JWRN_NOT_SEQUENTIAL); + + for ci := 0 to pred(cinfo^.comps_in_scan) do + begin + 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; + end; + + { Precalculate decoding info for each block in an MCU of this scan } + for blkn := 0 to pred(cinfo^.blocks_in_MCU) do + begin + 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) then + begin + 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); + end + else + begin + entropy^.ac_needed[blkn] := FALSE; + entropy^.dc_needed[blkn] := FALSE; + end; + end; + + { 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; +end; + + +{ 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} +procedure jpeg_make_d_derived_tbl (cinfo : j_decompress_ptr; + isDC : boolean; + tblno : int; + var pdtbl : d_derived_tbl_ptr); +var + htbl : JHUFF_TBL_PTR; + dtbl : d_derived_tbl_ptr; + p, i, l, si, numsymbols : int; + lookbits, ctr : int; + huffsize : array[0..257-1] of byte; + huffcode : array[0..257-1] of uInt; + code : uInt; +var + sym : int; +begin + { 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) or (tblno >= NUM_HUFF_TBLS) then + ERREXIT1(j_common_ptr(cinfo), JERR_NO_HUFF_TABLE, tblno); + if isDC then + htbl := cinfo^.dc_huff_tbl_ptrs[tblno] + else + htbl := cinfo^.ac_huff_tbl_ptrs[tblno]; + if (htbl = NIL) then + ERREXIT1(j_common_ptr(cinfo), JERR_NO_HUFF_TABLE, tblno); + + { Allocate a workspace if we haven't already done so. } + if (pdtbl = NIL) then + pdtbl := d_derived_tbl_ptr( + 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 to 16 do + begin + i := int(htbl^.bits[l]); + if (i < 0) or (p + i > 256) then { protect against table overrun } + ERREXIT(j_common_ptr(cinfo), JERR_BAD_HUFF_TABLE); + while (i > 0) do + begin + huffsize[p] := byte(l); + Inc(p); + Dec(i); + end; + end; + 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] <> 0) do + begin + while (( int (huffsize[p]) ) = si) do + begin + huffcode[p] := code; + Inc(p); + Inc(code); + end; + { 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) shl si)) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_HUFF_TABLE); + + code := code shl 1; + Inc(si); + end; + + { Figure F.15: generate decoding tables for bit-sequential decoding } + + p := 0; + for l := 1 to 16 do + begin + if (htbl^.bits[l] <> 0) then + begin + { 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]); + Inc(p, htbl^.bits[l]); + dtbl^.maxcode[l] := huffcode[p-1]; { maximum code of length l } + end + else + begin + dtbl^.maxcode[l] := -1; { -1 if no codes of this length } + end; + end; + dtbl^.maxcode[17] := long($FFFFF); { 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 to HUFF_LOOKAHEAD do + begin + for i := 1 to int (htbl^.bits[l]) do + begin + { l := current code's length, p := its index in huffcode[] & huffval[]. } + { Generate left-justified code followed by all possible bit sequences } + lookbits := huffcode[p] shl (HUFF_LOOKAHEAD-l); + for ctr := pred(1 shl (HUFF_LOOKAHEAD-l)) downto 0 do + begin + dtbl^.look_nbits[lookbits] := l; + dtbl^.look_sym[lookbits] := htbl^.huffval[p]; + Inc(lookbits); + end; + Inc(p); + end; + end; + + { 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) then + begin + for i := 0 to pred(numsymbols) do + begin + sym := htbl^.huffval[i]; + if (sym < 0) or (sym > 15) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_HUFF_TABLE); + end; + end; +end; + + +{ 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} +const + MIN_GET_BITS = 15; { minimum allowable value } +{$else} +const + MIN_GET_BITS = (BIT_BUF_SIZE-7); +{$endif} + + +{GLOBAL} +function jpeg_fill_bit_buffer (var state : bitread_working_state; + {register} get_buffer : bit_buf_type; + {register} bits_left : int; + nbits : int) : boolean; +label + no_more_bytes; +{ Load up the bit buffer to a depth of at least nbits } +var + { Copy heavily used state fields into locals (hopefully registers) } + {register} next_input_byte : {const} JOCTETptr; + {register} bytes_in_buffer : size_t; +var + {register} c : int; +var + cinfo : j_decompress_ptr; +begin + next_input_byte := state.next_input_byte; + bytes_in_buffer := state.bytes_in_buffer; + 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) then { cannot advance past a marker } + begin + while (bits_left < MIN_GET_BITS) do + begin + { Attempt to read a byte } + if (bytes_in_buffer = 0) then + begin + if not cinfo^.src^.fill_input_buffer(cinfo) then + begin + jpeg_fill_bit_buffer := FALSE; + exit; + end; + next_input_byte := cinfo^.src^.next_input_byte; + bytes_in_buffer := cinfo^.src^.bytes_in_buffer; + end; + Dec(bytes_in_buffer); + c := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + + + { If it's $FF, check and discard stuffed zero byte } + if (c = $FF) then + begin + { 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. } + + repeat + if (bytes_in_buffer = 0) then + begin + if (not state.cinfo^.src^.fill_input_buffer (state.cinfo)) then + begin + jpeg_fill_bit_buffer := FALSE; + exit; + end; + next_input_byte := state.cinfo^.src^.next_input_byte; + bytes_in_buffer := state.cinfo^.src^.bytes_in_buffer; + end; + Dec(bytes_in_buffer); + c := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + Until (c <> $FF); + + if (c = 0) then + begin + { Found FF/00, which represents an FF data byte } + c := $FF; + end + else + begin + { 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; + end; + end; + + { OK, load c into get_buffer } + get_buffer := (get_buffer shl 8) or c; + Inc(bits_left, 8); + end { end while } + end + else + begin + 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) then + begin + { 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 not cinfo^.entropy^.insufficient_data then + begin + WARNMS(j_common_ptr(cinfo), JWRN_HIT_MARKER); + cinfo^.entropy^.insufficient_data := TRUE; + end; + { Fill the buffer with zero bits } + get_buffer := get_buffer shl (MIN_GET_BITS - bits_left); + bits_left := MIN_GET_BITS; + end; + end; + + { 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; + + jpeg_fill_bit_buffer := TRUE; +end; + + +{ Out-of-line code for Huffman code decoding. + See jdhuff.h for info about usage. } + +{GLOBAL} +function jpeg_huff_decode (var state : bitread_working_state; + {register} get_buffer : bit_buf_type; + {register} bits_left : int; + htbl : d_derived_tbl_ptr; + min_bits : int) : int; +var + {register} l : int; + {register} code : INT32; +begin + l := min_bits; + + { 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);} + if (bits_left < l) then + begin + if (not jpeg_fill_bit_buffer(state, get_buffer, bits_left, l)) then + begin + jpeg_huff_decode := -1; + exit; + end; + get_buffer := state.get_buffer; + bits_left := state.bits_left; + end; + + {code := GET_BITS(l);} + Dec(bits_left, l); + code := (int(get_buffer shr bits_left)) and ( pred(1 shl 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]) do + begin + code := code shl 1; + {CHECK_BIT_BUFFER(state, 1, return -1);} + if (bits_left < 1) then + begin + if (not jpeg_fill_bit_buffer(state, get_buffer, bits_left, 1)) then + begin + jpeg_huff_decode := -1; + exit; + end; + get_buffer := state.get_buffer; + bits_left := state.bits_left; + end; + + {code := code or GET_BITS(1);} + Dec(bits_left); + code := code or ( (int(get_buffer shr bits_left)) and pred(1 shl 1) ); + + Inc(l); + end; + + { 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) then + begin + WARNMS(j_common_ptr(state.cinfo), JWRN_HUFF_BAD_CODE); + jpeg_huff_decode := 0; { fake a zero as the safest result } + exit; + end; + + jpeg_huff_decode := htbl^.pub^.huffval[ int (code + htbl^.valoffset[l]) ]; +end; + + +{ 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) + if (x < extend_test[s]) then + := x + extend_offset[s] + else + x;} + +const + extend_test : array[0..16-1] of int = { entry n is 2**(n-1) } + ($0000, $0001, $0002, $0004, $0008, $0010, $0020, $0040, + $0080, $0100, $0200, $0400, $0800, $1000, $2000, $4000); + +const + extend_offset : array[0..16-1] of int = { entry n is (-1 << n) + 1 } +(0, ((-1) shl 1) + 1, ((-1) shl 2) + 1, ((-1) shl 3) + 1, ((-1) shl 4) + 1, + ((-1) shl 5) + 1, ((-1) shl 6) + 1, ((-1) shl 7) + 1, ((-1) shl 8) + 1, + ((-1) shl 9) + 1, ((-1) shl 10) + 1, ((-1) shl 11) + 1,((-1) shl 12) + 1, + ((-1) shl 13) + 1, ((-1) shl 14) + 1, ((-1) shl 15) + 1); + +{$endif} { AVOID_TABLES } + + +{ Check for a restart marker & resynchronize decoder. + Returns FALSE if must suspend. } + +{LOCAL} +function process_restart (cinfo : j_decompress_ptr) : boolean; +var + entropy : huff_entropy_ptr; + ci : int; +begin + entropy := huff_entropy_ptr (cinfo^.entropy); + + { Throw away any unused bits remaining in bit buffer; } + { include any full bytes in next_marker's count of discarded bytes } + Inc(cinfo^.marker^.discarded_bytes, entropy^.bitstate.bits_left div 8); + entropy^.bitstate.bits_left := 0; + + { Advance past the RSTn marker } + if (not cinfo^.marker^.read_restart_marker (cinfo)) then + begin + process_restart := FALSE; + exit; + end; + + { Re-initialize DC predictions to 0 } + for ci := 0 to pred(cinfo^.comps_in_scan) do + 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) then + entropy^.pub.insufficient_data := FALSE; + + process_restart := TRUE; +end; + + +{ 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} +function decode_mcu (cinfo : j_decompress_ptr; + var MCU_data : array of JBLOCKROW) : boolean; +label + label1, label2, label3; +var + entropy : huff_entropy_ptr; + {register} s, k, r : int; + blkn, ci : int; + block : JBLOCK_PTR; + {BITREAD_STATE_VARS} + get_buffer : bit_buf_type ; {register} + bits_left : int; {register} + br_state : bitread_working_state; + + state : savable_state; + dctbl : d_derived_tbl_ptr; + actbl : d_derived_tbl_ptr; +var + nb, look : int; {register} +begin + entropy := huff_entropy_ptr (cinfo^.entropy); + + { Process restart marker if needed; may have to suspend } + if (cinfo^.restart_interval <> 0) then + begin + if (entropy^.restarts_to_go = 0) then + if (not process_restart(cinfo)) then + begin + decode_mcu := FALSE; + exit; + end; + end; + + { 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 not entropy^.pub.insufficient_data then + begin + + { Load up working state } + {BITREAD_LOAD_STATE(cinfo,entropy^.bitstate);} + br_state.cinfo := cinfo; + br_state.next_input_byte := cinfo^.src^.next_input_byte; + br_state.bytes_in_buffer := cinfo^.src^.bytes_in_buffer; + get_buffer := entropy^.bitstate.get_buffer; + bits_left := entropy^.bitstate.bits_left; + + {ASSIGN_STATE(state, entropy^.saved);} + state := entropy^.saved; + + { Outer loop handles each block in the MCU } + + for blkn := 0 to pred(cinfo^.blocks_in_MCU) do + begin + block := JBLOCK_PTR(MCU_data[blkn]); + dctbl := entropy^.dc_cur_tbls[blkn]; + actbl := entropy^.ac_cur_tbls[blkn]; + + { 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 (bits_left < HUFF_LOOKAHEAD) then + begin + if (not jpeg_fill_bit_buffer(br_state,get_buffer,bits_left, 0)) then + begin + decode_mcu := False; + exit; + end; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + if (bits_left < HUFF_LOOKAHEAD) then + begin + nb := 1; + goto label1; + end; + end; + {look := PEEK_BITS(HUFF_LOOKAHEAD);} + look := int(get_buffer shr (bits_left - HUFF_LOOKAHEAD)) and + pred(1 shl HUFF_LOOKAHEAD); + + nb := dctbl^.look_nbits[look]; + if (nb <> 0) then + begin + {DROP_BITS(nb);} + Dec(bits_left, nb); + + s := dctbl^.look_sym[look]; + end + else + begin + nb := HUFF_LOOKAHEAD+1; + label1: + s := jpeg_huff_decode(br_state,get_buffer,bits_left,dctbl,nb); + if (s < 0) then + begin + decode_mcu := FALSE; + exit; + end; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + end; + + if (s <> 0) then + begin + {CHECK_BIT_BUFFER(br_state, s, return FALSE);} + if (bits_left < s) then + begin + if (not jpeg_fill_bit_buffer(br_state,get_buffer,bits_left,s)) then + begin + decode_mcu := FALSE; + exit; + end; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + end; + + {r := GET_BITS(s);} + Dec(bits_left, s); + r := ( int(get_buffer shr bits_left)) and ( pred(1 shl s) ); + + {s := HUFF_EXTEND(r, s);} + if (r < extend_test[s]) then + s := r + extend_offset[s] + else + s := r; + end; + + if (entropy^.dc_needed[blkn]) then + begin + { Convert DC difference to actual value, update last_dc_val } + ci := cinfo^.MCU_membership[blkn]; + Inc(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); + end; + + if (entropy^.ac_needed[blkn]) then + begin + + { Section F.2.2.2: decode the AC coefficients } + { Since zeroes are skipped, output area must be cleared beforehand } + k := 1; + while (k < DCTSIZE2) do { Nomssi: k is incr. in the loop } + begin + {HUFF_DECODE(s, br_state, actbl, return FALSE, label2);} + if (bits_left < HUFF_LOOKAHEAD) then + begin + if (not jpeg_fill_bit_buffer(br_state,get_buffer,bits_left, 0)) then + begin + decode_mcu := False; + exit; + end; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + if (bits_left < HUFF_LOOKAHEAD) then + begin + nb := 1; + goto label2; + end; + end; + {look := PEEK_BITS(HUFF_LOOKAHEAD);} + look := int(get_buffer shr (bits_left - HUFF_LOOKAHEAD)) and + pred(1 shl HUFF_LOOKAHEAD); + + nb := actbl^.look_nbits[look]; + if (nb <> 0) then + begin + {DROP_BITS(nb);} + Dec(bits_left, nb); + + s := actbl^.look_sym[look]; + end + else + begin + nb := HUFF_LOOKAHEAD+1; + label2: + s := jpeg_huff_decode(br_state,get_buffer,bits_left,actbl,nb); + if (s < 0) then + begin + decode_mcu := FALSE; + exit; + end; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + end; + + r := s shr 4; + s := s and 15; + + if (s <> 0) then + begin + Inc(k, r); + {CHECK_BIT_BUFFER(br_state, s, return FALSE);} + if (bits_left < s) then + begin + if (not jpeg_fill_bit_buffer(br_state,get_buffer,bits_left,s)) then + begin + decode_mcu := FALSE; + exit; + end; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + end; + + {r := GET_BITS(s);} + Dec(bits_left, s); + r := (int(get_buffer shr bits_left)) and ( pred(1 shl s) ); + + {s := HUFF_EXTEND(r, s);} + if (r < extend_test[s]) then + s := r + extend_offset[s] + else + s := r; + { 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); + end + else + begin + if (r <> 15) then + break; + Inc(k, 15); + end; + Inc(k); + end; + end + else + begin + + { Section F.2.2.2: decode the AC coefficients } + { In this path we just discard the values } + k := 1; + while (k < DCTSIZE2) do + begin + {HUFF_DECODE(s, br_state, actbl, return FALSE, label3);} + if (bits_left < HUFF_LOOKAHEAD) then + begin + if (not jpeg_fill_bit_buffer(br_state,get_buffer,bits_left, 0)) then + begin + decode_mcu := False; + exit; + end; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + if (bits_left < HUFF_LOOKAHEAD) then + begin + nb := 1; + goto label3; + end; + end; + {look := PEEK_BITS(HUFF_LOOKAHEAD);} + look := int(get_buffer shr (bits_left - HUFF_LOOKAHEAD)) and + pred(1 shl HUFF_LOOKAHEAD); + + nb := actbl^.look_nbits[look]; + if (nb <> 0) then + begin + {DROP_BITS(nb);} + Dec(bits_left, nb); + + s := actbl^.look_sym[look]; + end + else + begin + nb := HUFF_LOOKAHEAD+1; + label3: + s := jpeg_huff_decode(br_state,get_buffer,bits_left,actbl,nb); + if (s < 0) then + begin + decode_mcu := FALSE; + exit; + end; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + end; + + r := s shr 4; + s := s and 15; + + if (s <> 0) then + begin + Inc(k, r); + {CHECK_BIT_BUFFER(br_state, s, return FALSE);} + if (bits_left < s) then + begin + if (not jpeg_fill_bit_buffer(br_state,get_buffer,bits_left,s)) then + begin + decode_mcu := FALSE; + exit; + end; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + end; + + {DROP_BITS(s);} + Dec(bits_left, s); + end + else + begin + if (r <> 15) then + break; + Inc(k, 15); + end; + Inc(k); + end; + + end; + end; + + { Completed MCU, so update state } + {BITREAD_SAVE_STATE(cinfo,entropy^.bitstate);} + cinfo^.src^.next_input_byte := br_state.next_input_byte; + cinfo^.src^.bytes_in_buffer := br_state.bytes_in_buffer; + entropy^.bitstate.get_buffer := get_buffer; + entropy^.bitstate.bits_left := bits_left; + + {ASSIGN_STATE(entropy^.saved, state);} + entropy^.saved := state; + + end; + + { Account for restart interval (no-op if not using restarts) } + if entropy^.restarts_to_go > 0 then + Dec(entropy^.restarts_to_go); + + decode_mcu := TRUE; +end; + + +{ Module initialization routine for Huffman entropy decoding. } + +{GLOBAL} +procedure jinit_huff_decoder (cinfo : j_decompress_ptr); +var + entropy : huff_entropy_ptr; + i : int; +begin + entropy := huff_entropy_ptr( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + SIZEOF(huff_entropy_decoder)) ); + cinfo^.entropy := jpeg_entropy_decoder_ptr (entropy); + entropy^.pub.start_pass := start_pass_huff_decoder; + entropy^.pub.decode_mcu := decode_mcu; + + { Mark tables unallocated } + for i := 0 to pred(NUM_HUFF_TBLS) do + begin + entropy^.dc_derived_tbls[i] := NIL; + entropy^.ac_derived_tbls[i] := NIL; + end; +end; + +end. \ No newline at end of file diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjdinput.pas b/resources/libraries/deskew/Imaging/JpegLib/imjdinput.pas new file mode 100755 index 0000000..16a13e2 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjdinput.pas @@ -0,0 +1,416 @@ +unit imjdinput; + +{ Original: 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. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjpeglib, + imjdeferr, + imjerror, + imjinclude, imjutils; + +{ Initialize the input controller module. + This is called only once, when the decompression object is created. } + +{GLOBAL} +procedure jinit_input_controller (cinfo : j_decompress_ptr); + +implementation + +{ Private state } + +type + my_inputctl_ptr = ^my_input_controller; + my_input_controller = record + pub : jpeg_input_controller; { public fields } + + inheaders : boolean; { TRUE until first SOS is reached } + end; {my_input_controller;} + + + +{ Forward declarations } +{METHODDEF} +function consume_markers (cinfo : j_decompress_ptr) : int; forward; + + +{ Routines to calculate various quantities related to the size of the image. } + +{LOCAL} +procedure initial_setup (cinfo : j_decompress_ptr); +{ Called once, when first SOS marker is reached } +var + ci : int; + compptr : jpeg_component_info_ptr; +begin + { Make sure image isn't bigger than I can handle } + if (long(cinfo^.image_height) > long (JPEG_MAX_DIMENSION)) or + (long(cinfo^.image_width) > long(JPEG_MAX_DIMENSION)) then + ERREXIT1(j_common_ptr(cinfo), JERR_IMAGE_TOO_BIG, uInt(JPEG_MAX_DIMENSION)); + + { For now, precision must match compiled-in value... } + if (cinfo^.data_precision <> BITS_IN_JSAMPLE) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_PRECISION, cinfo^.data_precision); + + { Check that number of components won't exceed internal array sizes } + if (cinfo^.num_components > MAX_COMPONENTS) then + ERREXIT2(j_common_ptr(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; + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + if (compptr^.h_samp_factor<=0) or (compptr^.h_samp_factor>MAX_SAMP_FACTOR) or + (compptr^.v_samp_factor<=0) or (compptr^.v_samp_factor>MAX_SAMP_FACTOR) then + ERREXIT(j_common_ptr(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);} + if cinfo^.max_h_samp_factor < compptr^.h_samp_factor then + cinfo^.max_h_samp_factor := compptr^.h_samp_factor; + if cinfo^.max_v_samp_factor < compptr^.v_samp_factor then + cinfo^.max_v_samp_factor := compptr^.v_samp_factor; + Inc(compptr); + end; + + { 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 } + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + 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 := NIL; + Inc(compptr); + end; + + { 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) or + (cinfo^.progressive_mode) then + cinfo^.inputctl^.has_multiple_scans := TRUE + else + cinfo^.inputctl^.has_multiple_scans := FALSE; +end; + + +{LOCAL} +procedure per_scan_setup (cinfo : j_decompress_ptr); +{ Do computations that are needed before processing a JPEG scan } +{ cinfo^.comps_in_scan and cinfo^.cur_comp_info[] were set from SOS marker } +var + ci, mcublks, tmp : int; + compptr : jpeg_component_info_ptr; +begin + if (cinfo^.comps_in_scan = 1) then + begin + { 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 (LongInt(compptr^.height_in_blocks) mod compptr^.v_samp_factor); + if (tmp = 0) then + 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; + + end + else + begin + + { Interleaved (multi-component) scan } + if (cinfo^.comps_in_scan <= 0) or (cinfo^.comps_in_scan > MAX_COMPS_IN_SCAN) then + ERREXIT2(j_common_ptr(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 to pred(cinfo^.comps_in_scan) do + begin + 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 (LongInt(compptr^.width_in_blocks) mod compptr^.MCU_width); + if (tmp = 0) then + tmp := compptr^.MCU_width; + compptr^.last_col_width := tmp; + tmp := int (LongInt(compptr^.height_in_blocks) mod compptr^.MCU_height); + if (tmp = 0) then + tmp := compptr^.MCU_height; + compptr^.last_row_height := tmp; + { Prepare array describing MCU composition } + mcublks := compptr^.MCU_blocks; + if (LongInt(cinfo^.blocks_in_MCU) + mcublks > D_MAX_BLOCKS_IN_MCU) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_MCU_SIZE); + while (mcublks > 0) do + begin + Dec(mcublks); + cinfo^.MCU_membership[cinfo^.blocks_in_MCU] := ci; + Inc(cinfo^.blocks_in_MCU); + end; + end; + + end; +end; + + +{ 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} +procedure latch_quant_tables (cinfo : j_decompress_ptr); +var + ci, qtblno : int; + compptr : jpeg_component_info_ptr; + qtbl : JQUANT_TBL_PTR; +begin + for ci := 0 to pred(cinfo^.comps_in_scan) do + begin + compptr := cinfo^.cur_comp_info[ci]; + { No work if we already saved Q-table for this component } + if (compptr^.quant_table <> NIL) then + continue; + { Make sure specified quantization table is present } + qtblno := compptr^.quant_tbl_no; + if (qtblno < 0) or (qtblno >= NUM_QUANT_TBLS) or + (cinfo^.quant_tbl_ptrs[qtblno] = NIL) then + ERREXIT1(j_common_ptr(cinfo), JERR_NO_QUANT_TABLE, qtblno); + { OK, save away the quantization table } + qtbl := JQUANT_TBL_PTR( + 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; + end; +end; + + +{ 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} +procedure start_input_pass (cinfo : j_decompress_ptr); +begin + 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; +end; + + +{ 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} +procedure finish_input_pass (cinfo : j_decompress_ptr); +begin + cinfo^.inputctl^.consume_input := consume_markers; +end; + + +{ 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} +function consume_markers (cinfo : j_decompress_ptr) : int; +var + val : int; + inputctl : my_inputctl_ptr; +begin + inputctl := my_inputctl_ptr (cinfo^.inputctl); + + if (inputctl^.pub.eoi_reached) then { After hitting EOI, read no further } + begin + consume_markers := JPEG_REACHED_EOI; + exit; + end; + + val := cinfo^.marker^.read_markers (cinfo); + + case (val) of + JPEG_REACHED_SOS: { Found SOS } + begin + if (inputctl^.inheaders) then + begin { 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. } + end + else + begin { 2nd or later SOS marker } + if (not inputctl^.pub.has_multiple_scans) then + ERREXIT(j_common_ptr(cinfo), JERR_EOI_EXPECTED); { Oops, I wasn't expecting this! } + start_input_pass(cinfo); + end; + end; + JPEG_REACHED_EOI: { Found EOI } + begin + inputctl^.pub.eoi_reached := TRUE; + if (inputctl^.inheaders) then + begin { Tables-only datastream, apparently } + if (cinfo^.marker^.saw_SOF) then + ERREXIT(j_common_ptr(cinfo), JERR_SOF_NO_SOS); + end + else + begin + { 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) then + cinfo^.output_scan_number := cinfo^.input_scan_number; + end; + end; + JPEG_SUSPENDED:; + end; + + consume_markers := val; +end; + + +{ Reset state to begin a fresh datastream. } + +{METHODDEF} +procedure reset_input_controller (cinfo : j_decompress_ptr); +var + inputctl : my_inputctl_ptr; +begin + 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 := NIL; +end; + + +{ Initialize the input controller module. + This is called only once, when the decompression object is created. } + +{GLOBAL} +procedure jinit_input_controller (cinfo : j_decompress_ptr); +var + inputctl : my_inputctl_ptr; +begin + { 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 := jpeg_input_controller_ptr(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; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjdmainct.pas b/resources/libraries/deskew/Imaging/JpegLib/imjdmainct.pas new file mode 100755 index 0000000..8c04d7e --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjdmainct.pas @@ -0,0 +1,610 @@ +unit imjdmainct; + + +{ 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. } + +{ Original: jdmainct.c ; Copyright (C) 1994-1996, Thomas G. Lane. } + + +{ 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. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, +{$ifdef QUANT_2PASS_SUPPORTED} + imjquant2, +{$endif} + imjdeferr, + imjerror, + imjpeglib; + + +{GLOBAL} +procedure jinit_d_main_controller (cinfo : j_decompress_ptr; + need_full_buffer : boolean); + + +implementation + +{ Private buffer controller object } + +type + my_main_ptr = ^my_main_controller; + my_main_controller = record + pub : jpeg_d_main_controller; { public fields } + + { Pointer to allocated workspace (M or M+2 row groups). } + buffer : array[0..MAX_COMPONENTS-1] of JSAMPARRAY; + + buffer_full : boolean; { Have we gotten an iMCU row from decoder? } + rowgroup_ctr : JDIMENSION ; { 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. } + xbuffer : array[0..2-1] of JSAMPIMAGE; { pointers to weird pointer lists } + + whichptr : int; { indicates which pointer set is now in use } + context_state : int; { process_data state machine status } + rowgroups_avail : JDIMENSION; { row groups available to postprocessor } + iMCU_row_ctr : JDIMENSION; { counts iMCU rows to detect image top/bot } + end; { my_main_controller; } + + +{ context_state values: } +const + CTX_PREPARE_FOR_IMCU = 0; { need to prepare for MCU row } + CTX_PROCESS_IMCU = 1; { feeding iMCU to postprocessor } + CTX_POSTPONED_ROW = 2; { feeding postponed row group } + + +{ Forward declarations } +{METHODDEF} +procedure process_data_simple_main(cinfo : j_decompress_ptr; + output_buf : JSAMPARRAY; + var out_row_ctr : JDIMENSION; + out_rows_avail : JDIMENSION); forward; +{METHODDEF} +procedure process_data_context_main (cinfo : j_decompress_ptr; + output_buf : JSAMPARRAY; + var out_row_ctr : JDIMENSION; + out_rows_avail : JDIMENSION); forward; + +{$ifdef QUANT_2PASS_SUPPORTED} +{METHODDEF} +procedure process_data_crank_post (cinfo : j_decompress_ptr; + output_buf : JSAMPARRAY; + var out_row_ctr : JDIMENSION; + out_rows_avail : JDIMENSION); forward; +{$endif} + + +{LOCAL} +procedure alloc_funny_pointers (cinfo : j_decompress_ptr); +{ Allocate space for the funny pointer lists. + This is done only once, not once per pass. } +var + main : my_main_ptr; + ci, rgroup : int; + M : int; + compptr : jpeg_component_info_ptr; + xbuf : JSAMPARRAY; +begin + main := my_main_ptr (cinfo^.main); + M := cinfo^.min_DCT_scaled_size; + + { 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] := JSAMPIMAGE(@( main^.xbuffer[0]^[cinfo^.num_components] )); + + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + rgroup := (compptr^.v_samp_factor * compptr^.DCT_scaled_size) div + 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)) ); + Inc(JSAMPROW_PTR(xbuf), rgroup); { want one row group at negative offsets } + main^.xbuffer[0]^[ci] := xbuf; + Inc(JSAMPROW_PTR(xbuf), rgroup * (M + 4)); + main^.xbuffer[1]^[ci] := xbuf; + Inc(compptr); + end; +end; + +{LOCAL} +procedure make_funny_pointers (cinfo : j_decompress_ptr); +{ 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. } +var + main : my_main_ptr; + ci, i, rgroup : int; + M : int; + compptr : jpeg_component_info_ptr; + buf, xbuf0, xbuf1 : JSAMPARRAY; +var + help_xbuf0 : JSAMPARRAY; { work around negative offsets } +begin + main := my_main_ptr (cinfo^.main); + M := cinfo^.min_DCT_scaled_size; + + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + rgroup := (compptr^.v_samp_factor * compptr^.DCT_scaled_size) div + 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 to pred(rgroup * (M + 2)) do + begin + xbuf0^[i] := buf^[i]; + xbuf1^[i] := buf^[i]; + end; + { In the second list, put the last four row groups in swapped order } + for i := 0 to pred(rgroup * 2) do + begin + xbuf1^[rgroup*(M-2) + i] := buf^[rgroup*M + i]; + xbuf1^[rgroup*M + i] := buf^[rgroup*(M-2) + i]; + end; + { 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]. } + + help_xbuf0 := xbuf0; + Dec(JSAMPROW_PTR(help_xbuf0), rgroup); + + for i := 0 to pred(rgroup) do + begin + {xbuf0^[i - rgroup] := xbuf0^[0];} + help_xbuf0^[i] := xbuf0^[0]; + end; + Inc(compptr); + end; +end; + + +{LOCAL} +procedure set_wraparound_pointers (cinfo : j_decompress_ptr); +{ 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. } +var + main : my_main_ptr; + ci, i, rgroup : int; + M : int; + compptr : jpeg_component_info_ptr; + xbuf0, xbuf1 : JSAMPARRAY; +var + help_xbuf0, + help_xbuf1 : JSAMPARRAY; { work around negative offsets } +begin + main := my_main_ptr (cinfo^.main); + M := cinfo^.min_DCT_scaled_size; + + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + rgroup := (compptr^.v_samp_factor * compptr^.DCT_scaled_size) div + cinfo^.min_DCT_scaled_size; { height of a row group of component } + xbuf0 := main^.xbuffer[0]^[ci]; + xbuf1 := main^.xbuffer[1]^[ci]; + + help_xbuf0 := xbuf0; + Dec(JSAMPROW_PTR(help_xbuf0), rgroup); + help_xbuf1 := xbuf1; + Dec(JSAMPROW_PTR(help_xbuf1), rgroup); + + for i := 0 to pred(rgroup) do + begin + {xbuf0^[i - rgroup] := xbuf0^[rgroup*(M+1) + i]; + xbuf1^[i - rgroup] := xbuf1^[rgroup*(M+1) + i];} + + help_xbuf0^[i] := xbuf0^[rgroup*(M+1) + i]; + help_xbuf1^[i] := xbuf1^[rgroup*(M+1) + i]; + + xbuf0^[rgroup*(M+2) + i] := xbuf0^[i]; + xbuf1^[rgroup*(M+2) + i] := xbuf1^[i]; + end; + Inc(compptr); + end; +end; + + +{LOCAL} +procedure set_bottom_pointers (cinfo : j_decompress_ptr); +{ 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. } +var + main : my_main_ptr; + ci, i, rgroup, iMCUheight, rows_left : int; + compptr : jpeg_component_info_ptr; + xbuf : JSAMPARRAY; +begin + main := my_main_ptr (cinfo^.main); + + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + { Count sample rows in one iMCU row and in one row group } + iMCUheight := compptr^.v_samp_factor * compptr^.DCT_scaled_size; + rgroup := iMCUheight div cinfo^.min_DCT_scaled_size; + { Count nondummy sample rows remaining for this component } + rows_left := int (compptr^.downsampled_height mod JDIMENSION (iMCUheight)); + if (rows_left = 0) then + rows_left := iMCUheight; + { Count nondummy row groups. Should get same answer for each component, + so we need only do it once. } + if (ci = 0) then + begin + main^.rowgroups_avail := JDIMENSION ((rows_left-1) div rgroup + 1); + end; + { 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 to pred(rgroup * 2) do + begin + xbuf^[rows_left + i] := xbuf^[rows_left-1]; + end; + Inc(compptr); + end; +end; + + +{ Initialize for a processing pass. } + +{METHODDEF} +procedure start_pass_main (cinfo : j_decompress_ptr; + pass_mode : J_BUF_MODE); +var + main : my_main_ptr; +begin + main := my_main_ptr (cinfo^.main); + + case (pass_mode) of + JBUF_PASS_THRU: + begin + if (cinfo^.upsample^.need_context_rows) then + begin + 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; + end + else + begin + { Simple case with no context needed } + main^.pub.process_data := process_data_simple_main; + end; + main^.buffer_full := FALSE; { Mark buffer empty } + main^.rowgroup_ctr := 0; + end; +{$ifdef QUANT_2PASS_SUPPORTED} + JBUF_CRANK_DEST: + { For last pass of 2-pass quantization, just crank the postprocessor } + main^.pub.process_data := process_data_crank_post; +{$endif} + else + ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); + end; +end; + + +{ Process some data. + This handles the simple case where no context is required. } + +{METHODDEF} +procedure process_data_simple_main (cinfo : j_decompress_ptr; + output_buf : JSAMPARRAY; + var out_row_ctr : JDIMENSION; + out_rows_avail : JDIMENSION); +var + main : my_main_ptr; + rowgroups_avail : JDIMENSION; +var + main_buffer_ptr : JSAMPIMAGE; +begin + main := my_main_ptr (cinfo^.main); + main_buffer_ptr := JSAMPIMAGE(@(main^.buffer)); + + { Read input data if we haven't filled the main buffer yet } + if (not main^.buffer_full) then + begin + if (cinfo^.coef^.decompress_data (cinfo, main_buffer_ptr)=0) then + exit; { suspension forced, can do nothing more } + main^.buffer_full := TRUE; { OK, we have an iMCU row to work with } + end; + + { 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_ptr, + 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) then + begin + main^.buffer_full := FALSE; + main^.rowgroup_ctr := 0; + end; +end; + + +{ Process some data. + This handles the case where context rows must be provided. } + +{METHODDEF} +procedure process_data_context_main (cinfo : j_decompress_ptr; + output_buf : JSAMPARRAY; + var out_row_ctr : JDIMENSION; + out_rows_avail : JDIMENSION); +var + main : my_main_ptr; +begin + main := my_main_ptr (cinfo^.main); + + { Read input data if we haven't filled the main buffer yet } + if (not main^.buffer_full) then + begin + if (cinfo^.coef^.decompress_data (cinfo, + main^.xbuffer[main^.whichptr])=0) then + exit; { suspension forced, can do nothing more } + main^.buffer_full := TRUE; { OK, we have an iMCU row to work with } + Inc(main^.iMCU_row_ctr); { count rows received } + end; + + { 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. } + + case (main^.context_state) of + CTX_POSTPONED_ROW: + begin + { 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) then + exit; { Need to suspend } + main^.context_state := CTX_PREPARE_FOR_IMCU; + if (out_row_ctr >= out_rows_avail) then + exit; { Postprocessor exactly filled output buf } + end; + end; + case (main^.context_state) of + CTX_POSTPONED_ROW, + CTX_PREPARE_FOR_IMCU: {FALLTHROUGH} + begin + { 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) then + set_bottom_pointers(cinfo); + main^.context_state := CTX_PROCESS_IMCU; + + end; + end; + case (main^.context_state) of + CTX_POSTPONED_ROW, + CTX_PREPARE_FOR_IMCU, {FALLTHROUGH} + CTX_PROCESS_IMCU: + begin + { 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) then + exit; { Need to suspend } + { After the first iMCU, change wraparound pointers to normal state } + if (main^.iMCU_row_ctr = 1) then + set_wraparound_pointers(cinfo); + { Prepare to load new iMCU row using other xbuffer list } + main^.whichptr := main^.whichptr xor 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; + end; + end; +end; + + +{ 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} +procedure process_data_crank_post (cinfo : j_decompress_ptr; + output_buf : JSAMPARRAY; + var out_row_ctr : JDIMENSION; + out_rows_avail : JDIMENSION); +var + in_row_group_ctr : JDIMENSION; +begin + in_row_group_ctr := 0; + cinfo^.post^.post_process_data (cinfo, JSAMPIMAGE (NIL), + in_row_group_ctr, + JDIMENSION(0), + output_buf, + out_row_ctr, + out_rows_avail); +end; + +{$endif} { QUANT_2PASS_SUPPORTED } + + +{ Initialize main buffer controller. } + +{GLOBAL} +procedure jinit_d_main_controller (cinfo : j_decompress_ptr; + need_full_buffer : boolean); +var + main : my_main_ptr; + ci, rgroup, ngroups : int; + compptr : jpeg_component_info_ptr; +begin + main := my_main_ptr( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + SIZEOF(my_main_controller)) ); + cinfo^.main := jpeg_d_main_controller_ptr(main); + main^.pub.start_pass := start_pass_main; + + if (need_full_buffer) then { shouldn't happen } + ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); + + { Allocate the workspace. + ngroups is the number of row groups we need.} + + if (cinfo^.upsample^.need_context_rows) then + begin + if (cinfo^.min_DCT_scaled_size < 2) then { unsupported, see comments above } + ERREXIT(j_common_ptr(cinfo), JERR_NOTIMPL); + alloc_funny_pointers(cinfo); { Alloc space for xbuffer[] lists } + ngroups := cinfo^.min_DCT_scaled_size + 2; + end + else + begin + ngroups := cinfo^.min_DCT_scaled_size; + end; + + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + rgroup := (compptr^.v_samp_factor * compptr^.DCT_scaled_size) div + 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 * LongWord(compptr^.DCT_scaled_size), + JDIMENSION (rgroup * ngroups)); + Inc(compptr); + end; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjdmarker.pas b/resources/libraries/deskew/Imaging/JpegLib/imjdmarker.pas new file mode 100755 index 0000000..ad175b5 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjdmarker.pas @@ -0,0 +1,2648 @@ +unit imjdmarker; + +{ 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. } + +{ Original: jdmarker.c; Copyright (C) 1991-1998; Thomas G. Lane. } +{ History + 9.7.96 Conversion to pascal started jnn + 22.3.98 updated to 6b jnn } + + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjdeferr, + imjerror, + imjcomapi, + imjpeglib; + +const { JPEG marker codes } + M_SOF0 = $c0; + M_SOF1 = $c1; + M_SOF2 = $c2; + M_SOF3 = $c3; + + M_SOF5 = $c5; + M_SOF6 = $c6; + M_SOF7 = $c7; + + M_JPG = $c8; + M_SOF9 = $c9; + M_SOF10 = $ca; + M_SOF11 = $cb; + + M_SOF13 = $cd; + M_SOF14 = $ce; + M_SOF15 = $cf; + + M_DHT = $c4; + + M_DAC = $cc; + + M_RST0 = $d0; + M_RST1 = $d1; + M_RST2 = $d2; + M_RST3 = $d3; + M_RST4 = $d4; + M_RST5 = $d5; + M_RST6 = $d6; + M_RST7 = $d7; + + M_SOI = $d8; + M_EOI = $d9; + M_SOS = $da; + M_DQT = $db; + M_DNL = $dc; + M_DRI = $dd; + M_DHP = $de; + M_EXP = $df; + + M_APP0 = $e0; + M_APP1 = $e1; + M_APP2 = $e2; + M_APP3 = $e3; + M_APP4 = $e4; + M_APP5 = $e5; + M_APP6 = $e6; + M_APP7 = $e7; + M_APP8 = $e8; + M_APP9 = $e9; + M_APP10 = $ea; + M_APP11 = $eb; + M_APP12 = $ec; + M_APP13 = $ed; + M_APP14 = $ee; + M_APP15 = $ef; + + M_JPG0 = $f0; + M_JPG13 = $fd; + M_COM = $fe; + + M_TEM = $01; + + M_ERROR = $100; + +type + JPEG_MARKER = uint; { JPEG marker codes } + +{ Private state } + +type + my_marker_ptr = ^my_marker_reader; + my_marker_reader = record + pub : jpeg_marker_reader; { public fields } + + { Application-overridable marker processing methods } + process_COM : jpeg_marker_parser_method; + process_APPn : array[0..16-1] of jpeg_marker_parser_method; + + { Limit on marker data length to save for each marker type } + length_limit_COM : uint; + length_limit_APPn : array[0..16-1] of uint; + + { Status of COM/APPn marker saving } + cur_marker : jpeg_saved_marker_ptr; { NIL if not processing a marker } + bytes_read : uint; { data bytes read so far in marker } + { Note: cur_marker is not linked into marker_list until it's all read. } + end; + +{GLOBAL} +function jpeg_resync_to_restart(cinfo : j_decompress_ptr; + desired : int) : boolean; +{GLOBAL} +procedure jinit_marker_reader (cinfo : j_decompress_ptr); + +{$ifdef SAVE_MARKERS_SUPPORTED} + +{GLOBAL} +procedure jpeg_save_markers (cinfo : j_decompress_ptr; + marker_code : int; + length_limit : uint); +{$ENDIF} + +{GLOBAL} +procedure jpeg_set_marker_processor (cinfo : j_decompress_ptr; + marker_code : int; + routine : jpeg_marker_parser_method); + +implementation + +uses + imjutils; + +{ At all times, cinfo1.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. } + + +{ 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} +function get_soi (cinfo : j_decompress_ptr) : boolean; +{ Process an SOI marker } +var + i : int; +begin + {$IFDEF DEBUG} + TRACEMS(j_common_ptr(cinfo), 1, JTRC_SOI); + {$ENDIF} + + if (cinfo^.marker^.saw_SOI) then + ERREXIT(j_common_ptr(cinfo), JERR_SOI_DUPLICATE); + + { Reset all parameters that are defined to be reset by SOI } + + for i := 0 to Pred(NUM_ARITH_TBLS) do + with cinfo^ do + begin + arith_dc_L[i] := 0; + arith_dc_U[i] := 1; + arith_ac_K[i] := 5; + end; + cinfo^.restart_interval := 0; + + { Set initial assumptions for colorspace etc } + + with cinfo^ do + begin + jpeg_color_space := JCS_UNKNOWN; + CCIR601_sampling := FALSE; { Assume non-CCIR sampling??? } + + saw_JFIF_marker := FALSE; + JFIF_major_version := 1; { set default JFIF APP0 values } + JFIF_minor_version := 1; + density_unit := 0; + X_density := 1; + Y_density := 1; + saw_Adobe_marker := FALSE; + Adobe_transform := 0; + + marker^.saw_SOI := TRUE; + end; + get_soi := TRUE; +end; { get_soi } + + +{LOCAL} +function get_sof(cinfo : j_decompress_ptr; + is_prog : boolean; + is_arith : boolean) : boolean; +{ Process a SOFn marker } +var + length : INT32; + c, ci : int; + compptr : jpeg_component_info_ptr; +{ Declare and initialize local copies of input pointer/count } +var + datasrc : jpeg_source_mgr_ptr; + next_input_byte : JOCTETptr; + bytes_in_buffer : size_t; +begin + datasrc := cinfo^.src; + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; +{} + cinfo^.progressive_mode := is_prog; + cinfo^.arith_code := is_arith; + +{ Read two bytes interpreted as an unsigned 16-bit integer. + length should be declared unsigned int or perhaps INT32. } + +{ 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_sof := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + length := (uint( GETJOCTET(next_input_byte^)) shl 8); + Inc( next_input_byte ); + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_sof := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + Inc( length, GETJOCTET( next_input_byte^)); + Inc( next_input_byte ); + + + { Read a byte into variable cinfo^.data_precision. + If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_sof := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + cinfo^.data_precision := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + +{ Read two bytes interpreted as an unsigned 16-bit integer. + cinfo^.image_height should be declared unsigned int or perhaps INT32. } + +{ 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_sof := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + cinfo^.image_height := (uint( GETJOCTET(next_input_byte^)) shl 8); + Inc( next_input_byte ); + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_sof := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + Inc( cinfo^.image_height, GETJOCTET( next_input_byte^)); + Inc( next_input_byte ); + +{ Read two bytes interpreted as an unsigned 16-bit integer. + cinfo^.image_width should be declared unsigned int or perhaps INT32. } + +{ 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_sof := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + cinfo^.image_width := (uint( GETJOCTET(next_input_byte^)) shl 8); + Inc( next_input_byte ); + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_sof := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + Inc( cinfo^.image_width, GETJOCTET( next_input_byte^)); + Inc( next_input_byte ); + + { Read a byte into variable cinfo^.num_components. + If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_sof := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + cinfo^.num_components := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + + Dec(length, 8); + + {$IFDEF DEBUG} + TRACEMS4(j_common_ptr(cinfo), 1, JTRC_SOF, cinfo^.unread_marker, + int(cinfo^.image_width), int(cinfo^.image_height), + cinfo^.num_components); + {$ENDIF} + + if (cinfo^.marker^.saw_SOF) then + ERREXIT(j_common_ptr(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) or (cinfo^.image_width <= 0) + or (cinfo^.num_components <= 0) then + ERREXIT(j_common_ptr(cinfo), JERR_EMPTY_IMAGE); + + if (length <> (cinfo^.num_components * 3)) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_LENGTH); + + if (cinfo^.comp_info = NIL) then { do only once, even if suspend } + cinfo^.comp_info := jpeg_component_info_list_ptr( + cinfo^.mem^.alloc_small(j_common_ptr(cinfo), JPOOL_IMAGE, + cinfo^.num_components * SIZEOF(jpeg_component_info))); + + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + compptr^.component_index := ci; + + { Read a byte into variable compptr^.component_id. + If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_sof := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + compptr^.component_id := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + + { Read a byte into variable c. If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_sof := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + c := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + + compptr^.h_samp_factor := (c shr 4) and 15; + compptr^.v_samp_factor := (c ) and 15; + + { Read a byte into variable compptr^.quant_tbl_no. + If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_sof := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + compptr^.quant_tbl_no := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + + {$IFDEF DEBUG} + TRACEMS4(j_common_ptr(cinfo), 1, JTRC_SOF_COMPONENT, + compptr^.component_id, compptr^.h_samp_factor, + compptr^.v_samp_factor, compptr^.quant_tbl_no); + {$ENDIF} + + Inc(compptr); + end; + + cinfo^.marker^.saw_SOF := TRUE; + + { Unload the local copies --- do this only at a restart boundary } + datasrc^.next_input_byte := next_input_byte; + datasrc^.bytes_in_buffer := bytes_in_buffer; + + get_sof := TRUE; +end; { get_sof } + + +{LOCAL} +function get_sos (cinfo : j_decompress_ptr) : boolean; +{ Process a SOS marker } +label + id_found; +var + length : INT32; + i, ci, n, c, cc : int; + compptr : jpeg_component_info_ptr; +{ Declare and initialize local copies of input pointer/count } +var + datasrc : jpeg_source_mgr_ptr; + next_input_byte : JOCTETptr; { Array[] of JOCTET; } + bytes_in_buffer : size_t; +begin + datasrc := cinfo^.src; + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + +{} + + if not cinfo^.marker^.saw_SOF then + ERREXIT(j_common_ptr(cinfo), JERR_SOS_NO_SOF); + +{ Read two bytes interpreted as an unsigned 16-bit integer. + length should be declared unsigned int or perhaps INT32. } + +{ 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_sos := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + length := (uint( GETJOCTET(next_input_byte^)) shl 8); + Inc( next_input_byte ); + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_sos := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + Inc( length, GETJOCTET( next_input_byte^)); + Inc( next_input_byte ); + + + { Read a byte into variable n (Number of components). + If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_sos := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + n := GETJOCTET(next_input_byte^); { Number of components } + Inc(next_input_byte); + + {$IFDEF DEBUG} + TRACEMS1(j_common_ptr(cinfo), 1, JTRC_SOS, n); + {$ENDIF} + + if ((length <> (n * 2 + 6)) or (n < 1) or (n > MAX_COMPS_IN_SCAN)) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_LENGTH); + + cinfo^.comps_in_scan := n; + + { Collect the component-spec parameters } + + for i := 0 to Pred(n) do + begin + { Read a byte into variable cc. If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_sos := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + cc := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + + { Read a byte into variable c. If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_sos := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + c := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to Pred(cinfo^.num_components) do + begin + if (cc = compptr^.component_id) then + goto id_found; + Inc(compptr); + end; + + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_COMPONENT_ID, cc); + + id_found: + + cinfo^.cur_comp_info[i] := compptr; + compptr^.dc_tbl_no := (c shr 4) and 15; + compptr^.ac_tbl_no := (c ) and 15; + + {$IFDEF DEBUG} + TRACEMS3(j_common_ptr(cinfo), 1, JTRC_SOS_COMPONENT, cc, + compptr^.dc_tbl_no, compptr^.ac_tbl_no); + {$ENDIF} + end; + + { Collect the additional scan parameters Ss, Se, Ah/Al. } + { Read a byte into variable c. If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_sos := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + c := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + + cinfo^.Ss := c; + + { Read a byte into variable c. If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_sos := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + c := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + + cinfo^.Se := c; + + { Read a byte into variable c. If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_sos := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + c := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + + cinfo^.Ah := (c shr 4) and 15; + cinfo^.Al := (c ) and 15; + + {$IFDEF DEBUG} + TRACEMS4(j_common_ptr(cinfo), 1, JTRC_SOS_PARAMS, cinfo^.Ss, cinfo^.Se, + cinfo^.Ah, cinfo^.Al); + {$ENDIF} + + { Prepare to scan data & restart markers } + cinfo^.marker^.next_restart_num := 0; + + { Count another SOS marker } + Inc( cinfo^.input_scan_number ); + + { Unload the local copies --- do this only at a restart boundary } + datasrc^.next_input_byte := next_input_byte; + datasrc^.bytes_in_buffer := bytes_in_buffer; + + get_sos := TRUE; +end; { get_sos } + + +{METHODDEF} +function skip_variable (cinfo : j_decompress_ptr) : boolean; +{ Skip over an unknown or uninteresting variable-length marker } +var + length : INT32; +var + datasrc : jpeg_source_mgr_ptr; + next_input_byte : JOCTETptr; { Array[] of JOCTET; } + bytes_in_buffer : size_t; +begin + datasrc := cinfo^.src; + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + +{ Read two bytes interpreted as an unsigned 16-bit integer. + length should be declared unsigned int or perhaps INT32. } + +{ 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + skip_variable := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + length := uint(GETJOCTET(next_input_byte^)) shl 8; + Inc( next_input_byte ); + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + skip_variable := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + Inc( length, GETJOCTET(next_input_byte^)); + Inc( next_input_byte ); + + Dec(length, 2); + + {$IFDEF DEBUG} + TRACEMS2(j_common_ptr(cinfo), 1, JTRC_MISC_MARKER, + cinfo^.unread_marker, int(length)); + {$ENDIF} + + { Unload the local copies --- do this only at a restart boundary } + { do before skip_input_data } + datasrc^.next_input_byte := next_input_byte; + datasrc^.bytes_in_buffer := bytes_in_buffer; + + if (length > 0) then + cinfo^.src^.skip_input_data(cinfo, long(length)); + + skip_variable := TRUE; +end; { skip_variable } + + +{$IFDEF D_ARITH_CODING_SUPPORTED} + +{LOCAL} +function get_dac (cinfo : j_decompress_ptr) : boolean; +{ Process a DAC marker } +var + length : INT32; + index, val : int; +var + datasrc : jpeg_source_mgr_ptr; + next_input_byte : JOCTETptr; + bytes_in_buffer : size_t; +begin + datasrc := cinfo^.src; + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + +{ Read two bytes interpreted as an unsigned 16-bit integer. + length should be declared unsigned int or perhaps INT32. } + +{ 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_dac := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + length := (uint( GETJOCTET(next_input_byte^)) shl 8); + Inc( next_input_byte ); + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_dac := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + Inc( length, GETJOCTET( next_input_byte^)); + Inc( next_input_byte ); + + Dec(length, 2); + + while (length > 0) do + begin + { Read a byte into variable index. If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_dac := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + index := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + + { Read a byte into variable val. If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_dac := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + val := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + + Dec( length, 2); + + {$IFDEF DEBUG} + TRACEMS2(j_common_ptr(cinfo), 1, JTRC_DAC, index, val); + {$ENDIF} + + if (index < 0) or (index >= (2*NUM_ARITH_TBLS)) then + ERREXIT1(j_common_ptr(cinfo) , JERR_DAC_INDEX, index); + + if (index >= NUM_ARITH_TBLS) then + begin { define AC table } + cinfo^.arith_ac_K[index-NUM_ARITH_TBLS] := UINT8(val); + end + else + begin { define DC table } + cinfo^.arith_dc_L[index] := UINT8(val and $0F); + cinfo^.arith_dc_U[index] := UINT8(val shr 4); + if (cinfo^.arith_dc_L[index] > cinfo^.arith_dc_U[index]) then + ERREXIT1(j_common_ptr(cinfo) , JERR_DAC_VALUE, val); + end; + end; + + if (length <> 0) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_LENGTH); + + { Unload the local copies --- do this only at a restart boundary } + datasrc^.next_input_byte := next_input_byte; + datasrc^.bytes_in_buffer := bytes_in_buffer; + + get_dac := TRUE; +end; { get_dac } + +{$ELSE} + +{LOCAL} +function get_dac (cinfo : j_decompress_ptr) : boolean; +begin + get_dac := skip_variable(cinfo); +end; + +{$ENDIF} + +{LOCAL} +function get_dht (cinfo : j_decompress_ptr) : boolean; +{ Process a DHT marker } +var + length : INT32; + bits : Array[0..17-1] of UINT8; + huffval : Array[0..256-1] of UINT8; + i, index, count : int; + htblptr : ^JHUFF_TBL_PTR; +var + datasrc : jpeg_source_mgr_ptr; + next_input_byte : JOCTETptr; + bytes_in_buffer : size_t; +begin + datasrc := cinfo^.src; + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + +{ Read two bytes interpreted as an unsigned 16-bit integer. + length should be declared unsigned int or perhaps INT32. } + +{ 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_dht := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + length := (uint( GETJOCTET(next_input_byte^)) shl 8); + Inc( next_input_byte ); + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_dht := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + Inc( length, GETJOCTET( next_input_byte^)); + Inc( next_input_byte ); + + Dec(length, 2); + + while (length > 16) do + begin + { Read a byte into variable index. If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_dht := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + index := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + + {$IFDEF DEBUG} + TRACEMS1(j_common_ptr(cinfo), 1, JTRC_DHT, index); + {$ENDIF} + + bits[0] := 0; + count := 0; + for i := 1 to 16 do + begin + { Read a byte into variable bits[i]. If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_dht := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + bits[i] := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + + Inc( count, bits[i] ); + end; + + Dec( length, (1 + 16) ); + + {$IFDEF DEBUG} + TRACEMS8(j_common_ptr(cinfo), 2, JTRC_HUFFBITS, + bits[1], bits[2], bits[3], bits[4], + bits[5], bits[6], bits[7], bits[8]); + TRACEMS8(j_common_ptr(cinfo), 2, JTRC_HUFFBITS, + bits[9], bits[10], bits[11], bits[12], + bits[13], bits[14], bits[15], bits[16]); + {$ENDIF} + + { 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) or (INT32(count) > length) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_HUFF_TABLE); + + for i := 0 to Pred(count) do + begin + { Read a byte into variable huffval[i]. If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_dht := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + huffval[i] := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + end; + + Dec( length, count ); + + if (index and $10)<>0 then + begin { AC table definition } + Dec( index, $10 ); + htblptr := @cinfo^.ac_huff_tbl_ptrs[index]; + end + else + begin { DC table definition } + htblptr := @cinfo^.dc_huff_tbl_ptrs[index]; + end; + + if (index < 0) or (index >= NUM_HUFF_TBLS) then + ERREXIT1(j_common_ptr(cinfo), JERR_DHT_INDEX, index); + + if (htblptr^ = NIL) then + htblptr^ := jpeg_alloc_huff_table(j_common_ptr(cinfo)); + + MEMCOPY(@(htblptr^)^.bits, @bits, SIZEOF((htblptr^)^.bits)); + MEMCOPY(@(htblptr^)^.huffval, @huffval, SIZEOF((htblptr^)^.huffval)); + end; + + if (length <> 0) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_LENGTH); + + { Unload the local copies --- do this only at a restart boundary } + datasrc^.next_input_byte := next_input_byte; + datasrc^.bytes_in_buffer := bytes_in_buffer; + + get_dht := TRUE; +end; { get_dht } + + +{LOCAL} +function get_dqt (cinfo : j_decompress_ptr) : boolean; +{ Process a DQT marker } +var + length : INT32; + n, i, prec : int; + tmp : uint; + quant_ptr : JQUANT_TBL_PTR; +var + datasrc : jpeg_source_mgr_ptr; + next_input_byte : JOCTETptr; + bytes_in_buffer : size_t; +begin + datasrc := cinfo^.src; + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + +{ Read two bytes interpreted as an unsigned 16-bit integer. + length should be declared unsigned int or perhaps INT32. } + +{ 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_dqt := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + length := (uint( GETJOCTET(next_input_byte^)) shl 8); + Inc( next_input_byte ); + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_dqt := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + Inc( length, GETJOCTET( next_input_byte^)); + Inc( next_input_byte ); + + Dec( length, 2 ); + + while (length > 0) do + begin + { Read a byte into variable n. If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_dqt := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + n := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + + prec := n shr 4; + n := n and $0F; + + {$IFDEF DEBUG} + TRACEMS2(j_common_ptr(cinfo), 1, JTRC_DQT, n, prec); + {$ENDIF} + + if (n >= NUM_QUANT_TBLS) then + ERREXIT1(j_common_ptr(cinfo) , JERR_DQT_INDEX, n); + + if (cinfo^.quant_tbl_ptrs[n] = NIL) then + cinfo^.quant_tbl_ptrs[n] := jpeg_alloc_quant_table(j_common_ptr(cinfo)); + quant_ptr := cinfo^.quant_tbl_ptrs[n]; + + for i := 0 to Pred(DCTSIZE2) do + begin + if (prec <> 0) then + begin + { Read two bytes interpreted as an unsigned 16-bit integer. + tmp should be declared unsigned int or perhaps INT32. } + + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_dqt := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + tmp := (uint( GETJOCTET(next_input_byte^)) shl 8); + Inc( next_input_byte ); + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_dqt := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + Inc( tmp, GETJOCTET( next_input_byte^)); + Inc( next_input_byte ); + + end + else + begin + { Read a byte into variable tmp. If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_dqt := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + tmp := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + end; + + { We convert the zigzag-order table to natural array order. } + quant_ptr^.quantval[jpeg_natural_order[i]] := UINT16(tmp); + end; + + if (cinfo^.err^.trace_level >= 2) then + begin + i := 0; + while i < Pred(DCTSIZE2) do + begin + {$IFDEF DEBUG} + TRACEMS8(j_common_ptr(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]); + {$ENDIF} + Inc(i, 8); + end; + end; + + Dec( length, DCTSIZE2+1 ); + if (prec <> 0) then + Dec( length, DCTSIZE2 ); + end; + + if (length <> 0) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_LENGTH); + + { Unload the local copies --- do this only at a restart boundary } + datasrc^.next_input_byte := next_input_byte; + datasrc^.bytes_in_buffer := bytes_in_buffer; + + get_dqt := TRUE; +end; { get_dqt } + + +{LOCAL} +function get_dri (cinfo : j_decompress_ptr) : boolean; +{ Process a DRI marker } +var + length : INT32; + tmp : uint; +var + datasrc : jpeg_source_mgr_ptr; + next_input_byte : JOCTETptr; + bytes_in_buffer : size_t; +begin + datasrc := cinfo^.src; + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + +{ Read two bytes interpreted as an unsigned 16-bit integer. + length should be declared unsigned int or perhaps INT32. } + +{ 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_dri := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + length := (uint( GETJOCTET(next_input_byte^)) shl 8); + Inc( next_input_byte ); + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_dri := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + Inc( length, GETJOCTET( next_input_byte^)); + Inc( next_input_byte ); + + if (length <> 4) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_LENGTH); + +{ Read two bytes interpreted as an unsigned 16-bit integer. + tmp should be declared unsigned int or perhaps INT32. } + +{ 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_dri := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + tmp := (uint( GETJOCTET(next_input_byte^)) shl 8); + Inc( next_input_byte ); + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_dri := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + Inc( tmp, GETJOCTET( next_input_byte^)); + Inc( next_input_byte ); + + {$IFDEF DEBUG} + TRACEMS1(j_common_ptr(cinfo), 1, JTRC_DRI, tmp); + {$ENDIF} + + cinfo^.restart_interval := tmp; + + { Unload the local copies --- do this only at a restart boundary } + datasrc^.next_input_byte := next_input_byte; + datasrc^.bytes_in_buffer := bytes_in_buffer; + + get_dri := TRUE; +end; { get_dri } + + +{ 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. } + +const + APP0_DATA_LEN = 14; { Length of interesting data in APP0 } + APP14_DATA_LEN = 12; { Length of interesting data in APP14 } + APPN_DATA_LEN = 14; { Must be the largest of the above!! } + + +{LOCAL} +procedure examine_app0 (cinfo : j_decompress_ptr; + var data : array of JOCTET; + datalen : uint; + remaining : INT32); + +{ 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. +} +{$IFDEF DEBUG} +var + totallen : INT32; +{$ENDIF} +begin + {$IFDEF DEBUG} + totallen := INT32(datalen) + remaining; + {$ENDIF} + if (datalen >= APP0_DATA_LEN) and + (GETJOCTET(data[0]) = $4A) and + (GETJOCTET(data[1]) = $46) and + (GETJOCTET(data[2]) = $49) and + (GETJOCTET(data[3]) = $46) and + (GETJOCTET(data[4]) = 0) then + begin + { 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]) shl 8) + GETJOCTET(data[9]); + cinfo^.Y_density := (GETJOCTET(data[10]) shl 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) then + WARNMS2(j_common_ptr(cinfo), JWRN_JFIF_MAJOR, + cinfo^.JFIF_major_version, cinfo^.JFIF_minor_version); + { Generate trace messages } + {$IFDEF DEBUG} + TRACEMS5(j_common_ptr(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]) or GETJOCTET(data[13])) <> 0 then + TRACEMS2(j_common_ptr(cinfo), 1, JTRC_JFIF_THUMBNAIL, + GETJOCTET(data[12]), GETJOCTET(data[13])); + Dec(totallen, APP0_DATA_LEN); + if (totallen <> + ( INT32(GETJOCTET(data[12])) * INT32(GETJOCTET(data[13])) * INT32(3) )) then + TRACEMS1(j_common_ptr(cinfo), 1, JTRC_JFIF_BADTHUMBNAILSIZE, int(totallen)); + {$ENDIF} + end + else + if (datalen >= 6) and + (GETJOCTET(data[0]) = $4A) and + (GETJOCTET(data[1]) = $46) and + (GETJOCTET(data[2]) = $58) and + (GETJOCTET(data[3]) = $58) and + (GETJOCTET(data[4]) = 0) then + begin + { Found JFIF "JFXX" extension APP0 marker } + { The library doesn't actually do anything with these, + but we try to produce a helpful trace message. } + {$IFDEF DEBUG} + case (GETJOCTET(data[5])) of + $10: + TRACEMS1(j_common_ptr(cinfo), 1, JTRC_THUMB_JPEG, int(totallen)); + $11: + TRACEMS1(j_common_ptr(cinfo), 1, JTRC_THUMB_PALETTE, int(totallen)); + $13: + TRACEMS1(j_common_ptr(cinfo), 1, JTRC_THUMB_RGB, int(totallen)); + else + TRACEMS2(j_common_ptr(cinfo), 1, JTRC_JFIF_EXTENSION, + GETJOCTET(data[5]), int(totallen)); + end; + {$ENDIF} + end + else + begin + { Start of APP0 does not match "JFIF" or "JFXX", or too short } + {$IFDEF DEBUG} + TRACEMS1(j_common_ptr(cinfo), 1, JTRC_APP0, int(totallen)); + {$ENDIF} + end; +end; + + +{LOCAL} +procedure examine_app14 (cinfo : j_decompress_ptr; + var data : array of JOCTET; + datalen : uint; + remaining : INT32); +{ 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. + } +var + {$IFDEF DEBUG} + version, flags0, flags1, + {$ENDIF} + transform : uint; +begin + if (datalen >= APP14_DATA_LEN) and + (GETJOCTET(data[0]) = $41) and + (GETJOCTET(data[1]) = $64) and + (GETJOCTET(data[2]) = $6F) and + (GETJOCTET(data[3]) = $62) and + (GETJOCTET(data[4]) = $65) then + begin + { Found Adobe APP14 marker } + {$IFDEF DEBUG} + version := (GETJOCTET(data[5]) shl 8) + GETJOCTET(data[6]); + flags0 := (GETJOCTET(data[7]) shl 8) + GETJOCTET(data[8]); + flags1 := (GETJOCTET(data[9]) shl 8) + GETJOCTET(data[10]); + {$ENDIF} + transform := GETJOCTET(data[11]); + {$IFDEF DEBUG} + TRACEMS4(j_common_ptr(cinfo), 1, JTRC_ADOBE, version, flags0, flags1, transform); + {$ENDIF} + cinfo^.saw_Adobe_marker := TRUE; + cinfo^.Adobe_transform := UINT8 (transform); + end + else + begin + { Start of APP14 does not match "Adobe", or too short } + {$IFDEF DEBUG} + TRACEMS1(j_common_ptr(cinfo), 1, JTRC_APP14, int (datalen + remaining)); + {$ENDIF} + end; +end; + + +{METHODDEF} +function get_interesting_appn (cinfo : j_decompress_ptr) : boolean; +{ Process an APP0 or APP14 marker without saving it } +var + length : INT32; + b : array[0..APPN_DATA_LEN-1] of JOCTET; + i, numtoread: uint; +var + datasrc : jpeg_source_mgr_ptr; + next_input_byte : JOCTETptr; + bytes_in_buffer : size_t; +begin + datasrc := cinfo^.src; + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + +{ Read two bytes interpreted as an unsigned 16-bit integer. + length should be declared unsigned int or perhaps INT32. } + + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_interesting_appn := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + length := (uint( GETJOCTET(next_input_byte^)) shl 8); + Inc( next_input_byte ); + + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_interesting_appn := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + Inc( length, GETJOCTET(next_input_byte^)); + Inc( next_input_byte ); + + Dec(length, 2); + + { get the interesting part of the marker data } + if (length >= APPN_DATA_LEN) then + numtoread := APPN_DATA_LEN + else + if (length > 0) then + numtoread := uint(length) + else + numtoread := 0; + + if numtoread > 0 then + begin + for i := 0 to numtoread-1 do + begin + { Read a byte into b[i]. If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + get_interesting_appn := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + b[i] := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + end; + end; + + Dec(length, numtoread); + + { process it } + case (cinfo^.unread_marker) of + M_APP0: + examine_app0(cinfo, b, numtoread, length); + M_APP14: + examine_app14(cinfo, b, numtoread, length); + else + { can't get here unless jpeg_save_markers chooses wrong processor } + ERREXIT1(j_common_ptr(cinfo), JERR_UNKNOWN_MARKER, cinfo^.unread_marker); + end; + + { skip any remaining data -- could be lots } + + { Unload the local copies --- do this only at a restart boundary } + datasrc^.next_input_byte := next_input_byte; + datasrc^.bytes_in_buffer := bytes_in_buffer; + + if (length > 0) then + cinfo^.src^.skip_input_data(cinfo, long(length)); + + get_interesting_appn := TRUE; +end; + +{$ifdef SAVE_MARKERS_SUPPORTED} + +{METHODDEF} +function save_marker (cinfo : j_decompress_ptr) : boolean; +{ Save an APPn or COM marker into the marker list } +var + marker : my_marker_ptr; + cur_marker : jpeg_saved_marker_ptr; + bytes_read, data_length : uint; + data : JOCTET_FIELD_PTR; + length : INT32; +var + datasrc : jpeg_source_mgr_ptr; + next_input_byte : JOCTETptr; + bytes_in_buffer : size_t; +var + limit : uint; +var + prev : jpeg_saved_marker_ptr; +begin + { local copies of input pointer/count } + datasrc := cinfo^.src; + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + + marker := my_marker_ptr(cinfo^.marker); + cur_marker := marker^.cur_marker; + length := 0; + + if (cur_marker = NIL) then + begin + { begin reading a marker } + { Read two bytes interpreted as an unsigned 16-bit integer. } + + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + save_marker := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + length := (uint( GETJOCTET(next_input_byte^)) shl 8); + Inc( next_input_byte ); + + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + save_marker := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + Inc( length, GETJOCTET(next_input_byte^)); + Inc( next_input_byte ); + + Dec(length, 2); + if (length >= 0) then + begin { watch out for bogus length word } + { figure out how much we want to save } + + if (cinfo^.unread_marker = int(M_COM)) then + limit := marker^.length_limit_COM + else + limit := marker^.length_limit_APPn[cinfo^.unread_marker - int(M_APP0)]; + if (uint(length) < limit) then + limit := uint(length); + { allocate and initialize the marker item } + cur_marker := jpeg_saved_marker_ptr( + cinfo^.mem^.alloc_large (j_common_ptr(cinfo), JPOOL_IMAGE, + SIZEOF(jpeg_marker_struct) + limit) ); + cur_marker^.next := NIL; + cur_marker^.marker := UINT8 (cinfo^.unread_marker); + cur_marker^.original_length := uint(length); + cur_marker^.data_length := limit; + { data area is just beyond the jpeg_marker_struct } + cur_marker^.data := JOCTET_FIELD_PTR(cur_marker); + Inc(jpeg_saved_marker_ptr(cur_marker^.data)); + data := cur_marker^.data; + + marker^.cur_marker := cur_marker; + marker^.bytes_read := 0; + bytes_read := 0; + data_length := limit; + end + else + begin + { deal with bogus length word } + data_length := 0; + bytes_read := 0; + data := NIL; + end + end + else + begin + { resume reading a marker } + bytes_read := marker^.bytes_read; + data_length := cur_marker^.data_length; + data := cur_marker^.data; + Inc(data, bytes_read); + end; + + while (bytes_read < data_length) do + begin + { move the restart point to here } + datasrc^.next_input_byte := next_input_byte; + datasrc^.bytes_in_buffer := bytes_in_buffer; + + marker^.bytes_read := bytes_read; + { If there's not at least one byte in buffer, suspend } + if (bytes_in_buffer = 0) then + begin + if not datasrc^.fill_input_buffer (cinfo) then + begin + save_marker := FALSE; + exit; + end; + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + + { Copy bytes with reasonable rapidity } + while (bytes_read < data_length) and (bytes_in_buffer > 0) do + begin + JOCTETPTR(data)^ := next_input_byte^; + Inc(JOCTETPTR(data)); + Inc(next_input_byte); + Dec(bytes_in_buffer); + Inc(bytes_read); + end; + end; + + { Done reading what we want to read } + if (cur_marker <> NIL) then + begin { will be NIL if bogus length word } + { Add new marker to end of list } + if (cinfo^.marker_list = NIL) then + begin + cinfo^.marker_list := cur_marker + end + else + begin + prev := cinfo^.marker_list; + while (prev^.next <> NIL) do + prev := prev^.next; + prev^.next := cur_marker; + end; + { Reset pointer & calc remaining data length } + data := cur_marker^.data; + length := cur_marker^.original_length - data_length; + end; + { Reset to initial state for next marker } + marker^.cur_marker := NIL; + + { Process the marker if interesting; else just make a generic trace msg } + case (cinfo^.unread_marker) of + M_APP0: + examine_app0(cinfo, data^, data_length, length); + M_APP14: + examine_app14(cinfo, data^, data_length, length); + else + {$IFDEF DEBUG} + TRACEMS2(j_common_ptr(cinfo), 1, JTRC_MISC_MARKER, cinfo^.unread_marker, + int(data_length + length)); + {$ENDIF} + end; + + { skip any remaining data -- could be lots } + { do before skip_input_data } + datasrc^.next_input_byte := next_input_byte; + datasrc^.bytes_in_buffer := bytes_in_buffer; + + if (length > 0) then + cinfo^.src^.skip_input_data (cinfo, long(length) ); + + save_marker := TRUE; +end; + +{$endif} { SAVE_MARKERS_SUPPORTED } + + +{ 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} +function next_marker (cinfo : j_decompress_ptr) : boolean; +var + c : int; +var + datasrc : jpeg_source_mgr_ptr; + next_input_byte : JOCTETptr; + bytes_in_buffer : size_t; +begin + datasrc := cinfo^.src; + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + + {while TRUE do} + repeat + { Read a byte into variable c. If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + next_marker := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + c := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + + { 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 <> $FF) do + begin + Inc(cinfo^.marker^.discarded_bytes); + { Unload the local copies --- do this only at a restart boundary } + datasrc^.next_input_byte := next_input_byte; + datasrc^.bytes_in_buffer := bytes_in_buffer; + + { Read a byte into variable c. If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + next_marker := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + c := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + + end; + { 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. } + + repeat + { Read a byte into variable c. If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + next_marker := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + c := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + Until (c <> $FF); + if (c <> 0) then + 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. } + + Inc(cinfo^.marker^.discarded_bytes, 2); + { Unload the local copies --- do this only at a restart boundary } + datasrc^.next_input_byte := next_input_byte; + datasrc^.bytes_in_buffer := bytes_in_buffer; + Until False; + + if (cinfo^.marker^.discarded_bytes <> 0) then + begin + WARNMS2(j_common_ptr(cinfo), JWRN_EXTRANEOUS_DATA, + cinfo^.marker^.discarded_bytes, c); + cinfo^.marker^.discarded_bytes := 0; + end; + + cinfo^.unread_marker := c; + + { Unload the local copies --- do this only at a restart boundary } + datasrc^.next_input_byte := next_input_byte; + datasrc^.bytes_in_buffer := bytes_in_buffer; + + next_marker := TRUE; +end; { next_marker } + + +{LOCAL} +function first_marker (cinfo : j_decompress_ptr) : boolean; +{ 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. } +var + c, c2 : int; +var + datasrc : jpeg_source_mgr_ptr; + next_input_byte : JOCTETptr; + bytes_in_buffer : size_t; +begin + datasrc := cinfo^.src; + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + + { Read a byte into variable c. If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + first_marker := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + c := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + + { Read a byte into variable c2. If must suspend, return FALSE. } + { 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. } + if (bytes_in_buffer = 0) then + begin + if (not datasrc^.fill_input_buffer(cinfo)) then + begin + first_marker := FALSE; + exit; + end; + { Reload the local copies } + next_input_byte := datasrc^.next_input_byte; + bytes_in_buffer := datasrc^.bytes_in_buffer; + end; + Dec( bytes_in_buffer ); + + c2 := GETJOCTET(next_input_byte^); + Inc(next_input_byte); + + if (c <> $FF) or (c2 <> int(M_SOI)) then + ERREXIT2(j_common_ptr(cinfo), JERR_NO_SOI, c, c2); + + cinfo^.unread_marker := c2; + + { Unload the local copies --- do this only at a restart boundary } + datasrc^.next_input_byte := next_input_byte; + datasrc^.bytes_in_buffer := bytes_in_buffer; + + first_marker := TRUE; +end; { first_marker } + + +{ 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} +function read_markers (cinfo : j_decompress_ptr) : int; +begin + { Outer loop repeats once for each marker. } + repeat + { Collect the marker proper, unless we already did. } + { NB: first_marker() enforces the requirement that SOI appear first. } + if (cinfo^.unread_marker = 0) then + begin + if not cinfo^.marker^.saw_SOI then + begin + if not first_marker(cinfo) then + begin + read_markers := JPEG_SUSPENDED; + exit; + end; + end + else + begin + if not next_marker(cinfo) then + begin + read_markers := JPEG_SUSPENDED; + exit; + end; + end; + end; + { 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. } + + case (cinfo^.unread_marker) of + M_SOI: + if not get_soi(cinfo) then + begin + read_markers := JPEG_SUSPENDED; + exit; + end; + + M_SOF0, { Baseline } + M_SOF1: { Extended sequential, Huffman } + if not get_sof(cinfo, FALSE, FALSE) then + begin + read_markers := JPEG_SUSPENDED; + exit; + end; + M_SOF2: { Progressive, Huffman } + if not get_sof(cinfo, TRUE, FALSE) then + begin + read_markers := JPEG_SUSPENDED; + exit; + end; + + M_SOF9: { Extended sequential, arithmetic } + if not get_sof(cinfo, FALSE, TRUE) then + begin + read_markers := JPEG_SUSPENDED; + exit; + end; + + M_SOF10: { Progressive, arithmetic } + if not get_sof(cinfo, TRUE, TRUE) then + begin + read_markers := JPEG_SUSPENDED; + exit; + end; + + { Currently unsupported SOFn types } + M_SOF3, { Lossless, Huffman } + M_SOF5, { Differential sequential, Huffman } + M_SOF6, { Differential progressive, Huffman } + M_SOF7, { Differential lossless, Huffman } + M_JPG, { Reserved for JPEG extensions } + M_SOF11, { Lossless, arithmetic } + M_SOF13, { Differential sequential, arithmetic } + M_SOF14, { Differential progressive, arithmetic } + M_SOF15: { Differential lossless, arithmetic } + ERREXIT1(j_common_ptr(cinfo), JERR_SOF_UNSUPPORTED, cinfo^.unread_marker); + + M_SOS: + begin + if not get_sos(cinfo) then + begin + read_markers := JPEG_SUSPENDED; + exit; + end; + cinfo^.unread_marker := 0; { processed the marker } + read_markers := JPEG_REACHED_SOS; + exit; + end; + + M_EOI: + begin + {$IFDEF DEBUG} + TRACEMS(j_common_ptr(cinfo), 1, JTRC_EOI); + {$ENDIF} + cinfo^.unread_marker := 0; { processed the marker } + read_markers := JPEG_REACHED_EOI; + exit; + end; + + M_DAC: + if not get_dac(cinfo) then + begin + read_markers := JPEG_SUSPENDED; + exit; + end; + + M_DHT: + if not get_dht(cinfo) then + begin + read_markers := JPEG_SUSPENDED; + exit; + end; + + M_DQT: + if not get_dqt(cinfo) then + begin + read_markers := JPEG_SUSPENDED; + exit; + end; + + M_DRI: + if not get_dri(cinfo) then + begin + read_markers := JPEG_SUSPENDED; + exit; + end; + + M_APP0, + M_APP1, + M_APP2, + M_APP3, + M_APP4, + M_APP5, + M_APP6, + M_APP7, + M_APP8, + M_APP9, + M_APP10, + M_APP11, + M_APP12, + M_APP13, + M_APP14, + M_APP15: + if not my_marker_ptr(cinfo^.marker)^. + process_APPn[cinfo^.unread_marker - int(M_APP0)](cinfo) then + begin + read_markers := JPEG_SUSPENDED; + exit; + end; + + M_COM: + if not my_marker_ptr(cinfo^.marker)^.process_COM (cinfo) then + begin + read_markers := JPEG_SUSPENDED; + exit; + end; + + M_RST0, { these are all parameterless } + M_RST1, + M_RST2, + M_RST3, + M_RST4, + M_RST5, + M_RST6, + M_RST7, + M_TEM: + {$IFDEF DEBUG} + TRACEMS1(j_common_ptr(cinfo), 1, JTRC_PARMLESS_MARKER, + cinfo^.unread_marker) + {$ENDIF} + ; + + M_DNL: { Ignore DNL ... perhaps the wrong thing } + if not skip_variable(cinfo) then + begin + read_markers := JPEG_SUSPENDED; + exit; + end; + + else { 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(j_common_ptr(cinfo) , JERR_UNKNOWN_MARKER, + cinfo^.unread_marker); + end; { end of case } + { Successfully processed marker, so reset state variable } + cinfo^.unread_marker := 0; + Until false; +end; { read_markers } + + +{ 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} +function read_restart_marker (cinfo : j_decompress_ptr) :boolean; +begin + { Obtain a marker unless we already did. } + { Note that next_marker will complain if it skips any data. } + if (cinfo^.unread_marker = 0) then + begin + if not next_marker(cinfo) then + begin + read_restart_marker := FALSE; + exit; + end; + end; + + if (cinfo^.unread_marker = (int(M_RST0) + cinfo^.marker^.next_restart_num)) then + begin + { Normal case --- swallow the marker and let entropy decoder continue } + {$IFDEF DEBUG} + TRACEMS1(j_common_ptr(cinfo), 3, JTRC_RST, + cinfo^.marker^.next_restart_num); + {$ENDIF} + cinfo^.unread_marker := 0; + end + else + begin + { Uh-oh, the restart markers have been messed up. } + { Let the data source manager determine how to resync. } + if not cinfo^.src^.resync_to_restart(cinfo, + cinfo^.marker^.next_restart_num) then + begin + read_restart_marker := FALSE; + exit; + end; + end; + + { Update next-restart state } + with cinfo^.marker^ do + next_restart_num := (next_restart_num + 1) and 7; + + read_restart_marker := TRUE; +end; { read_restart_marker } + + +{ 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} +function jpeg_resync_to_restart(cinfo : j_decompress_ptr; + desired : int) : boolean; +var + marker : int; + action : int; +begin + marker := cinfo^.unread_marker; + //action := 1; { never used } + { Always put up a warning. } + WARNMS2(j_common_ptr(cinfo), JWRN_MUST_RESYNC, marker, desired); + + { Outer loop handles repeated decision after scanning forward. } + repeat + if (marker < int(M_SOF0)) then + action := 2 { invalid marker } + else + if (marker < int(M_RST0)) or (marker > int(M_RST7)) then + action := 3 { valid non-restart marker } + else + begin + if (marker = (int(M_RST0) + ((desired+1) and 7))) or + (marker = (int(M_RST0) + ((desired+2) and 7))) then + action := 3 { one of the next two expected restarts } + else + if (marker = (int(M_RST0) + ((desired-1) and 7))) or + (marker = (int(M_RST0) + ((desired-2) and 7))) then + action := 2 { a prior restart, so advance } + else + action := 1; { desired restart or too far away } + end; + + {$IFDEF DEBUG} + TRACEMS2(j_common_ptr(cinfo), 4, JTRC_RECOVERY_ACTION, marker, action); + {$ENDIF} + case action of + 1: + { Discard marker and let entropy decoder resume processing. } + begin + cinfo^.unread_marker := 0; + jpeg_resync_to_restart := TRUE; + exit; + end; + 2: + { Scan to the next marker, and repeat the decision loop. } + begin + if not next_marker(cinfo) then + begin + jpeg_resync_to_restart := FALSE; + exit; + end; + marker := cinfo^.unread_marker; + end; + 3: + { Return without advancing past this marker. } + { Entropy decoder will be forced to process an empty segment. } + begin + jpeg_resync_to_restart := TRUE; + exit; + end; + end; { case } + Until false; { end loop } +end; { jpeg_resync_to_restart } + + +{ Reset marker processing state to begin a fresh datastream. } + +{METHODDEF} +procedure reset_marker_reader (cinfo : j_decompress_ptr); +var + marker : my_marker_ptr; +begin + marker := my_marker_ptr (cinfo^.marker); + with cinfo^ do + begin + comp_info := NIL; { until allocated by get_sof } + input_scan_number := 0; { no SOS seen yet } + unread_marker := 0; { no pending marker } + end; + marker^.pub.saw_SOI := FALSE; { set internal state too } + marker^.pub.saw_SOF := FALSE; + marker^.pub.discarded_bytes := 0; + marker^.cur_marker := NIL; +end; { reset_marker_reader } + + +{ Initialize the marker reader module. + This is called only once, when the decompression object is created. } + +{GLOBAL} +procedure jinit_marker_reader (cinfo : j_decompress_ptr); +var + marker : my_marker_ptr; + i : int; +begin + { 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 := jpeg_marker_reader_ptr(marker); + { Initialize 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 to 16-1 do + begin + marker^.process_APPn[i] := skip_variable; + marker^.length_limit_APPn[i] := 0; + end; + marker^.process_APPn[0] := get_interesting_appn; + marker^.process_APPn[14] := get_interesting_appn; + { Reset marker processing state } + reset_marker_reader(cinfo); +end; { jinit_marker_reader } + + +{ Control saving of COM and APPn markers into marker_list. } + + +{$ifdef SAVE_MARKERS_SUPPORTED} + +{GLOBAL} +procedure jpeg_save_markers (cinfo : j_decompress_ptr; + marker_code : int; + length_limit : uint); +var + marker : my_marker_ptr; + maxlength : long; + processor : jpeg_marker_parser_method; +begin + marker := my_marker_ptr (cinfo^.marker); + + { 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(jpeg_marker_struct); + if (long(length_limit) > maxlength) then + length_limit := uint(maxlength); + + { Choose processor routine to use. + APP0/APP14 have special requirements. } + + if (length_limit <> 0) then + begin + processor := save_marker; + { If saving APP0/APP14, save at least enough for our internal use. } + if (marker_code = int(M_APP0)) and (length_limit < APP0_DATA_LEN) then + length_limit := APP0_DATA_LEN + else + if (marker_code = int(M_APP14)) and (length_limit < APP14_DATA_LEN) then + length_limit := APP14_DATA_LEN; + end + else + begin + processor := skip_variable; + { If discarding APP0/APP14, use our regular on-the-fly processor. } + if (marker_code = int(M_APP0)) or (marker_code = int(M_APP14)) then + processor := get_interesting_appn; + end; + + if (marker_code = int(M_COM)) then + begin + marker^.process_COM := processor; + marker^.length_limit_COM := length_limit; + end + else + if (marker_code >= int(M_APP0)) and (marker_code <= int(M_APP15)) then + begin + marker^.process_APPn[marker_code - int(M_APP0)] := processor; + marker^.length_limit_APPn[marker_code - int(M_APP0)] := length_limit; + end + else + ERREXIT1(j_common_ptr(cinfo), JERR_UNKNOWN_MARKER, marker_code); +end; + +{$endif} { SAVE_MARKERS_SUPPORTED } + +{ Install a special processing method for COM or APPn markers. } + +{GLOBAL} + +procedure jpeg_set_marker_processor (cinfo : j_decompress_ptr; + marker_code : int; + routine : jpeg_marker_parser_method); +var + marker : my_marker_ptr; +begin + marker := my_marker_ptr (cinfo^.marker); + if (marker_code = int(M_COM)) then + marker^.process_COM := routine + else + if (marker_code >= int(M_APP0)) and (marker_code <= int(M_APP15)) then + marker^.process_APPn[marker_code - int(M_APP0)] := routine + else + ERREXIT1(j_common_ptr(cinfo), JERR_UNKNOWN_MARKER, marker_code); +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjdmaster.pas b/resources/libraries/deskew/Imaging/JpegLib/imjdmaster.pas new file mode 100755 index 0000000..076eeec --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjdmaster.pas @@ -0,0 +1,679 @@ +unit imjdmaster; + +{ 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. } + +{ Original: jdmaster.c ; Copyright (C) 1991-1998, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjutils, + imjerror, + imjdeferr, + imjdcolor, imjdsample, imjdpostct, imjddctmgr, imjdphuff, + imjdhuff, imjdcoefct, imjdmainct, +{$ifdef QUANT_1PASS_SUPPORTED} + imjquant1, +{$endif} +{$ifdef QUANT_2PASS_SUPPORTED} + imjquant2, +{$endif} +{$ifdef UPSAMPLE_MERGING_SUPPORTED} + imjdmerge, +{$endif} + imjpeglib; + + +{ 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} +procedure jpeg_calc_output_dimensions (cinfo : j_decompress_ptr); +{ Do computations that are needed before master selection phase } + + +{$ifdef D_MULTISCAN_FILES_SUPPORTED} + +{GLOBAL} +procedure jpeg_new_colormap (cinfo : j_decompress_ptr); + +{$endif} + +{ Initialize master decompression control and select active modules. + This is performed at the start of jpeg_start_decompress. } + +{GLOBAL} +procedure jinit_master_decompress (cinfo : j_decompress_ptr); + +implementation + +{ Private state } + +type + my_master_ptr = ^my_decomp_master; + my_decomp_master = record + pub : jpeg_decomp_master; { public fields } + + pass_number : int; { # of passes completed } + + using_merged_upsample : boolean; { TRUE if using merged upsample/cconvert } + + { Saved references to initialized quantizer modules, + in case we need to switch modes. } + + quantizer_1pass : jpeg_color_quantizer_ptr; + quantizer_2pass : jpeg_color_quantizer_ptr; + end; + +{ Determine whether merged upsample/color conversion should be used. + CRUCIAL: this must match the actual capabilities of jdmerge.c! } + +{LOCAL} +function use_merged_upsample (cinfo : j_decompress_ptr) : boolean; +var + compptr : jpeg_component_info_list_ptr; +begin + compptr := cinfo^.comp_info; + +{$ifdef UPSAMPLE_MERGING_SUPPORTED} + { Merging is the equivalent of plain box-filter upsampling } + if (cinfo^.do_fancy_upsampling) or (cinfo^.CCIR601_sampling) then + begin + use_merged_upsample := FALSE; + exit; + end; + { jdmerge.c only supports YCC=>RGB color conversion } + if (cinfo^.jpeg_color_space <> JCS_YCbCr) or (cinfo^.num_components <> 3) + or (cinfo^.out_color_space <> JCS_RGB) + or (cinfo^.out_color_components <> RGB_PIXELSIZE) then + begin + use_merged_upsample := FALSE; + exit; + end; + + { and it only handles 2h1v or 2h2v sampling ratios } + if (compptr^[0].h_samp_factor <> 2) or + (compptr^[1].h_samp_factor <> 1) or + (compptr^[2].h_samp_factor <> 1) or + (compptr^[0].v_samp_factor > 2) or + (compptr^[1].v_samp_factor <> 1) or + (compptr^[2].v_samp_factor <> 1) then + begin + use_merged_upsample := FALSE; + exit; + end; + { furthermore, it doesn't work if we've scaled the IDCTs differently } + if (compptr^[0].DCT_scaled_size <> cinfo^.min_DCT_scaled_size) or + (compptr^[1].DCT_scaled_size <> cinfo^.min_DCT_scaled_size) or + (compptr^[2].DCT_scaled_size <> cinfo^.min_DCT_scaled_size) then + begin + use_merged_upsample := FALSE; + exit; + end; + { ??? also need to test for upsample-time rescaling, when & if supported } + use_merged_upsample := TRUE; { by golly, it'll work... } +{$else} + use_merged_upsample := FALSE; +{$endif} +end; + + +{ 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} +procedure jpeg_calc_output_dimensions (cinfo : j_decompress_ptr); +{ Do computations that are needed before master selection phase } +{$ifdef IDCT_SCALING_SUPPORTED} +var + ci : int; + compptr : jpeg_component_info_ptr; +{$endif} +var + ssize : int; +begin + { Prevent application from calling me at wrong times } + if (cinfo^.global_state <> DSTATE_READY) then + ERREXIT1(j_common_ptr(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) then + begin + { Provide 1/8 scaling } + cinfo^.output_width := JDIMENSION ( + jdiv_round_up( long(cinfo^.image_width), long(8)) ); + cinfo^.output_height := JDIMENSION ( + jdiv_round_up( long(cinfo^.image_height), long(8)) ); + cinfo^.min_DCT_scaled_size := 1; + end + else + if (cinfo^.scale_num * 4 <= cinfo^.scale_denom) then + begin + { Provide 1/4 scaling } + cinfo^.output_width := JDIMENSION ( + jdiv_round_up( long (cinfo^.image_width), long(4)) ); + cinfo^.output_height := JDIMENSION ( + jdiv_round_up( long (cinfo^.image_height), long(4)) ); + cinfo^.min_DCT_scaled_size := 2; + end + else + if (cinfo^.scale_num * 2 <= cinfo^.scale_denom) then + begin + { Provide 1/2 scaling } + cinfo^.output_width := JDIMENSION ( + jdiv_round_up( long(cinfo^.image_width), long(2)) ); + cinfo^.output_height := JDIMENSION ( + jdiv_round_up( long(cinfo^.image_height), long(2)) ); + cinfo^.min_DCT_scaled_size := 4; + end + else + begin + { Provide 1/1 scaling } + cinfo^.output_width := cinfo^.image_width; + cinfo^.output_height := cinfo^.image_height; + cinfo^.min_DCT_scaled_size := DCTSIZE; + end; + { 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. } + + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + ssize := cinfo^.min_DCT_scaled_size; + while (ssize < DCTSIZE) and + ((compptr^.h_samp_factor * ssize * 2 <= + cinfo^.max_h_samp_factor * cinfo^.min_DCT_scaled_size) and + (compptr^.v_samp_factor * ssize * 2 <= + cinfo^.max_v_samp_factor * cinfo^.min_DCT_scaled_size)) do + begin + ssize := ssize * 2; + end; + compptr^.DCT_scaled_size := ssize; + Inc(compptr); + end; + + { Recompute downsampled dimensions of components; + application needs to know these if using raw downsampled data. } + + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + { 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)) ); + Inc(compptr); + end; + +{$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... } + case (cinfo^.out_color_space) of + JCS_GRAYSCALE: + cinfo^.out_color_components := 1; +{$ifndef RGB_PIXELSIZE_IS_3} + JCS_RGB: + cinfo^.out_color_components := RGB_PIXELSIZE; +{$else} + JCS_RGB, +{$endif} { else share code with YCbCr } + JCS_YCbCr: + cinfo^.out_color_components := 3; + JCS_CMYK, + JCS_YCCK: + cinfo^.out_color_components := 4; + else { else must be same colorspace as in file } + cinfo^.out_color_components := cinfo^.num_components; + end; + if (cinfo^.quantize_colors) then + cinfo^.output_components := 1 + else + cinfo^.output_components := cinfo^.out_color_components; + + { See if upsampler will want to emit more than one row at a time } + if (use_merged_upsample(cinfo)) then + cinfo^.rec_outbuf_height := cinfo^.max_v_samp_factor + else + cinfo^.rec_outbuf_height := 1; +end; + + +{ 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} +procedure prepare_range_limit_table (cinfo : j_decompress_ptr); +{ Allocate and fill in the sample_range_limit table } +var + table : range_limit_table_ptr; + idct_table : JSAMPROW; + i : int; +begin + table := range_limit_table_ptr ( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + (5 * (MAXJSAMPLE+1) + CENTERJSAMPLE) * SIZEOF(JSAMPLE)) ); + + { First segment of "simple" table: limit[x] := 0 for x < 0 } + MEMZERO(table, (MAXJSAMPLE+1) * SIZEOF(JSAMPLE)); + + cinfo^.sample_range_limit := (table); + { allow negative subscripts of simple table } + { is noop, handled via type definition (Nomssi) } + { Main part of "simple" table: limit[x] := x } + for i := 0 to MAXJSAMPLE do + table^[i] := JSAMPLE (i); + idct_table := JSAMPROW(@ table^[CENTERJSAMPLE]); + { Point to where post-IDCT table starts } + { End of simple table, rest of first half of post-IDCT table } + for i := CENTERJSAMPLE to pred(2*(MAXJSAMPLE+1)) do + idct_table^[i] := MAXJSAMPLE; + { Second half of post-IDCT table } + MEMZERO(@(idct_table^[2 * (MAXJSAMPLE+1)]), + (2 * (MAXJSAMPLE+1) - CENTERJSAMPLE) * SIZEOF(JSAMPLE)); + MEMCOPY(@(idct_table^[(4 * (MAXJSAMPLE+1) - CENTERJSAMPLE)]), + @cinfo^.sample_range_limit^[0], CENTERJSAMPLE * SIZEOF(JSAMPLE)); + +end; + + +{ 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} +procedure master_selection (cinfo : j_decompress_ptr); +var + master : my_master_ptr; + use_c_buffer : boolean; + samplesperrow : long; + jd_samplesperrow : JDIMENSION; +var + nscans : int; +begin + master := my_master_ptr (cinfo^.master); + + { 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) then + ERREXIT(j_common_ptr(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 := NIL; + master^.quantizer_2pass := NIL; + { No mode changes if not using buffered-image mode. } + if (not cinfo^.quantize_colors) or (not cinfo^.buffered_image) then + begin + cinfo^.enable_1pass_quant := FALSE; + cinfo^.enable_external_quant := FALSE; + cinfo^.enable_2pass_quant := FALSE; + end; + if (cinfo^.quantize_colors) then + begin + if (cinfo^.raw_data_out) then + ERREXIT(j_common_ptr(cinfo), JERR_NOTIMPL); + { 2-pass quantizer only works in 3-component color space. } + if (cinfo^.out_color_components <> 3) then + begin + cinfo^.enable_1pass_quant := TRUE; + cinfo^.enable_external_quant := FALSE; + cinfo^.enable_2pass_quant := FALSE; + cinfo^.colormap := NIL; + end + else + if (cinfo^.colormap <> NIL) then + begin + cinfo^.enable_external_quant := TRUE; + end + else + if (cinfo^.two_pass_quantize) then + begin + cinfo^.enable_2pass_quant := TRUE; + end + else + begin + cinfo^.enable_1pass_quant := TRUE; + end; + + if (cinfo^.enable_1pass_quant) then + begin +{$ifdef QUANT_1PASS_SUPPORTED} + jinit_1pass_quantizer(cinfo); + master^.quantizer_1pass := cinfo^.cquantize; +{$else} + ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); +{$endif} + end; + + { We use the 2-pass code to map to external colormaps. } + if (cinfo^.enable_2pass_quant) or (cinfo^.enable_external_quant) then + begin +{$ifdef QUANT_2PASS_SUPPORTED} + jinit_2pass_quantizer(cinfo); + master^.quantizer_2pass := cinfo^.cquantize; +{$else} + ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); +{$endif} + end; + { If both quantizers are initialized, the 2-pass one is left active; + this is necessary for starting with quantization to an external map. } + end; + + { Post-processing: in particular, color conversion first } + if (not cinfo^.raw_data_out) then + begin + if (master^.using_merged_upsample) then + begin +{$ifdef UPSAMPLE_MERGING_SUPPORTED} + jinit_merged_upsampler(cinfo); { does color conversion too } +{$else} + ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); +{$endif} + end + else + begin + jinit_color_deconverter(cinfo); + jinit_upsampler(cinfo); + end; + jinit_d_post_controller(cinfo, cinfo^.enable_2pass_quant); + end; + { Inverse DCT } + jinit_inverse_dct(cinfo); + { Entropy decoding: either Huffman or arithmetic coding. } + if (cinfo^.arith_code) then + begin + ERREXIT(j_common_ptr(cinfo), JERR_ARITH_NOTIMPL); + end + else + begin + if (cinfo^.progressive_mode) then + begin +{$ifdef D_PROGRESSIVE_SUPPORTED} + jinit_phuff_decoder(cinfo); +{$else} + ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); +{$endif} + end + else + jinit_huff_decoder(cinfo); + end; + + { Initialize principal buffer controllers. } + use_c_buffer := cinfo^.inputctl^.has_multiple_scans or cinfo^.buffered_image; + jinit_d_coef_controller(cinfo, use_c_buffer); + + if (not cinfo^.raw_data_out) then + 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 <> NIL) and (not cinfo^.buffered_image) and + (cinfo^.inputctl^.has_multiple_scans) then + begin + + { Estimate number of scans to set pass_limit. } + if (cinfo^.progressive_mode) then + begin + { Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. } + nscans := 2 + 3 * cinfo^.num_components; + end + else + begin + { For a nonprogressive multiscan file, estimate 1 scan per component. } + nscans := cinfo^.num_components; + end; + cinfo^.progress^.pass_counter := Long(0); + cinfo^.progress^.pass_limit := long (cinfo^.total_iMCU_rows) * nscans; + cinfo^.progress^.completed_passes := 0; + if cinfo^.enable_2pass_quant then + cinfo^.progress^.total_passes := 3 + else + cinfo^.progress^.total_passes := 2; + { Count the input pass as done } + Inc(master^.pass_number); + end; +{$endif} { D_MULTISCAN_FILES_SUPPORTED } +end; + + +{ 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} +procedure prepare_for_output_pass (cinfo : j_decompress_ptr); +var + master : my_master_ptr; +begin + master := my_master_ptr (cinfo^.master); + + if (master^.pub.is_dummy_pass) then + begin +{$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(j_common_ptr(cinfo), JERR_NOT_COMPILED); +{$endif} { QUANT_2PASS_SUPPORTED } + end + else + begin + if (cinfo^.quantize_colors) and (cinfo^.colormap = NIL) then + begin + { Select new quantization method } + if (cinfo^.two_pass_quantize) and (cinfo^.enable_2pass_quant) then + begin + cinfo^.cquantize := master^.quantizer_2pass; + master^.pub.is_dummy_pass := TRUE; + end + else + if (cinfo^.enable_1pass_quant) then + begin + cinfo^.cquantize := master^.quantizer_1pass; + end + else + begin + ERREXIT(j_common_ptr(cinfo), JERR_MODE_CHANGE); + end; + end; + cinfo^.idct^.start_pass (cinfo); + cinfo^.coef^.start_output_pass (cinfo); + if (not cinfo^.raw_data_out) then + begin + if (not master^.using_merged_upsample) then + cinfo^.cconvert^.start_pass (cinfo); + cinfo^.upsample^.start_pass (cinfo); + if (cinfo^.quantize_colors) then + cinfo^.cquantize^.start_pass (cinfo, master^.pub.is_dummy_pass); + if master^.pub.is_dummy_pass then + cinfo^.post^.start_pass (cinfo, JBUF_SAVE_AND_PASS) + else + cinfo^.post^.start_pass (cinfo, JBUF_PASS_THRU); + cinfo^.main^.start_pass (cinfo, JBUF_PASS_THRU); + end; + end; + + { Set up progress monitor's pass info if present } + if (cinfo^.progress <> NIL) then + begin + cinfo^.progress^.completed_passes := master^.pass_number; + if master^.pub.is_dummy_pass then + cinfo^.progress^.total_passes := master^.pass_number + 2 + else + cinfo^.progress^.total_passes := master^.pass_number + 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) and (not cinfo^.inputctl^.eoi_reached) then + begin + if cinfo^.enable_2pass_quant then + Inc(cinfo^.progress^.total_passes, 2) + else + Inc(cinfo^.progress^.total_passes, 1); + end; + end; +end; + + +{ Finish up at end of an output pass. } + +{METHODDEF} +procedure finish_output_pass (cinfo : j_decompress_ptr); +var + master : my_master_ptr; +begin + master := my_master_ptr (cinfo^.master); + + if (cinfo^.quantize_colors) then + cinfo^.cquantize^.finish_pass (cinfo); + Inc(master^.pass_number); +end; + + +{$ifdef D_MULTISCAN_FILES_SUPPORTED} + +{ Switch to a new external colormap between output passes. } + +{GLOBAL} +procedure jpeg_new_colormap (cinfo : j_decompress_ptr); +var + master : my_master_ptr; +begin + master := my_master_ptr (cinfo^.master); + + { Prevent application from calling me at wrong times } + if (cinfo^.global_state <> DSTATE_BUFIMAGE) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_STATE, cinfo^.global_state); + + if (cinfo^.quantize_colors) and (cinfo^.enable_external_quant) and + (cinfo^.colormap <> NIL) then + begin + { 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 } + end + else + ERREXIT(j_common_ptr(cinfo), JERR_MODE_CHANGE); +end; + +{$endif} { D_MULTISCAN_FILES_SUPPORTED } + + +{ Initialize master decompression control and select active modules. + This is performed at the start of jpeg_start_decompress. } + +{GLOBAL} +procedure jinit_master_decompress (cinfo : j_decompress_ptr); +var + master : my_master_ptr; +begin + master := my_master_ptr ( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + SIZEOF(my_decomp_master)) ); + cinfo^.master := jpeg_decomp_master_ptr(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); +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjdmerge.pas b/resources/libraries/deskew/Imaging/JpegLib/imjdmerge.pas new file mode 100755 index 0000000..3e9c7fb --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjdmerge.pas @@ -0,0 +1,514 @@ +unit imjdmerge; + +{ 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.) } + +{ Original: jdmerge.c ; Copyright (C) 1994-1996, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjpeglib, + imjutils; + +{ 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} +procedure jinit_merged_upsampler (cinfo : j_decompress_ptr); + +implementation + + +{ Private subobject } + +type { the same definition as in JdColor } + int_Color_Table = array[0..MAXJSAMPLE+1-1] of int; + int_CConvertPtr = ^int_Color_Table; + INT32_Color_Table = array[0..MAXJSAMPLE+1-1] of INT32; + INT32_CConvertPtr = ^INT32_Color_Table; + +type + my_upsample_ptr = ^my_upsampler; + my_upsampler = record + pub : jpeg_upsampler; { public fields } + + { Pointer to routine to do actual upsampling/conversion of one row group } + upmethod : procedure (cinfo : j_decompress_ptr; + input_buf : JSAMPIMAGE; + in_row_group_ctr : JDIMENSION; + output_buf : JSAMPARRAY); + + { Private state for YCC->RGB conversion } + Cr_r_tab : int_CConvertPtr; { => table for Cr to R conversion } + Cb_b_tab : int_CConvertPtr; { => table for Cb to B conversion } + Cr_g_tab : INT32_CConvertPtr; { => table for Cr to G conversion } + Cb_g_tab : INT32_CConvertPtr; { => 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. } + + spare_row : JSAMPROW; + spare_full : boolean; { TRUE if spare buffer is occupied } + + out_row_width : JDIMENSION; { samples per output row } + rows_to_go : JDIMENSION; { counts rows remaining in image } + end; {my_upsampler;} + + +const + SCALEBITS = 16; { speediest right-shift on some machines } + ONE_HALF = (INT32(1) shl (SCALEBITS-1)); + + +{ Initialize tables for YCC->RGB colorspace conversion. + This is taken directly from jdcolor.c; see that file for more info. } + +{LOCAL} +procedure build_ycc_rgb_table (cinfo : j_decompress_ptr); +const + FIX_1_40200 = INT32( Round(1.40200 * (INT32(1) shl SCALEBITS)) ); + FIX_1_77200 = INT32( Round(1.77200 * (INT32(1) shl SCALEBITS)) ); + FIX_0_71414 = INT32( Round(0.71414 * (INT32(1) shl SCALEBITS)) ); + FIX_0_34414 = INT32( Round(0.34414 * (INT32(1) shl SCALEBITS)) ); +var + upsample : my_upsample_ptr; + i : int; + x : INT32; +var + shift_temp : INT32; +begin + upsample := my_upsample_ptr (cinfo^.upsample); + + upsample^.Cr_r_tab := int_CConvertPtr ( + cinfo^.mem^.alloc_small (j_common_ptr (cinfo), JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)) ); + upsample^.Cb_b_tab := int_CConvertPtr ( + cinfo^.mem^.alloc_small (j_common_ptr (cinfo), JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)) ); + upsample^.Cr_g_tab := INT32_CConvertPtr ( + cinfo^.mem^.alloc_small (j_common_ptr (cinfo), JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)) ); + upsample^.Cb_g_tab := INT32_CConvertPtr ( + cinfo^.mem^.alloc_small (j_common_ptr (cinfo), JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)) ); + + x := -CENTERJSAMPLE; + for i := 0 to pred(MAXJSAMPLE) do + begin + { 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) );} + shift_temp := FIX_1_40200 * x + ONE_HALF; + if shift_temp < 0 then { SHIFT arithmetic RIGHT } + upsample^.Cr_r_tab^[i] := int((shift_temp shr SCALEBITS) + or ( (not INT32(0)) shl (32-SCALEBITS))) + else + upsample^.Cr_r_tab^[i] := int(shift_temp shr 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) );} + shift_temp := FIX_1_77200 * x + ONE_HALF; + if shift_temp < 0 then { SHIFT arithmetic RIGHT } + upsample^.Cb_b_tab^[i] := int((shift_temp shr SCALEBITS) + or ( (not INT32(0)) shl (32-SCALEBITS))) + else + upsample^.Cb_b_tab^[i] := int(shift_temp shr 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; + Inc(x); + end; +end; + + +{ Initialize for an upsampling pass. } + +{METHODDEF} +procedure start_pass_merged_upsample (cinfo : j_decompress_ptr); +var + upsample : my_upsample_ptr; +begin + 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; +end; + + +{ Control routine to do upsampling (and color conversion). + + The control routine just handles the row buffering considerations. } + +{METHODDEF} +procedure merged_2v_upsample (cinfo : j_decompress_ptr; + input_buf : JSAMPIMAGE; + var in_row_group_ctr : JDIMENSION; + in_row_groups_avail : JDIMENSION; + output_buf : JSAMPARRAY; + var out_row_ctr : JDIMENSION; + out_rows_avail : JDIMENSION); +{ 2:1 vertical sampling case: may need a spare row. } +var + upsample : my_upsample_ptr; + work_ptrs : array[0..2-1] of JSAMPROW; + num_rows : JDIMENSION; { number of rows returned to caller } +begin + upsample := my_upsample_ptr (cinfo^.upsample); + + if (upsample^.spare_full) then + begin + { If we have a spare row saved from a previous cycle, just return it. } + jcopy_sample_rows(JSAMPARRAY(@upsample^.spare_row), + 0, + JSAMPARRAY(@ output_buf^[out_row_ctr]), + 0, 1, upsample^.out_row_width); + num_rows := 1; + upsample^.spare_full := FALSE; + end + else + begin + { 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) then + num_rows := upsample^.rows_to_go; + { And not more than what the client can accept: } + Dec(out_rows_avail, {var} out_row_ctr); + if (num_rows > out_rows_avail) then + num_rows := out_rows_avail; + { Create output pointer array for upsampler. } + work_ptrs[0] := output_buf^[out_row_ctr]; + if (num_rows > 1) then + begin + work_ptrs[1] := output_buf^[out_row_ctr + 1]; + end + else + begin + work_ptrs[1] := upsample^.spare_row; + upsample^.spare_full := TRUE; + end; + { Now do the upsampling. } + upsample^.upmethod (cinfo, input_buf, {var}in_row_group_ctr, + JSAMPARRAY(@work_ptrs)); + end; + + { Adjust counts } + Inc(out_row_ctr, num_rows); + Dec(upsample^.rows_to_go, num_rows); + { When the buffer is emptied, declare this input row group consumed } + if (not upsample^.spare_full) then + Inc(in_row_group_ctr); +end; + + +{METHODDEF} +procedure merged_1v_upsample (cinfo : j_decompress_ptr; + input_buf : JSAMPIMAGE; + var in_row_group_ctr : JDIMENSION; + in_row_groups_avail : JDIMENSION; + output_buf : JSAMPARRAY; + var out_row_ctr : JDIMENSION; + out_rows_avail : JDIMENSION); +{ 1:1 vertical sampling case: much easier, never need a spare row. } +var + upsample : my_upsample_ptr; +begin + upsample := my_upsample_ptr (cinfo^.upsample); + + { Just do the upsampling. } + upsample^.upmethod (cinfo, input_buf, in_row_group_ctr, + JSAMPARRAY(@ output_buf^[out_row_ctr])); + { Adjust counts } + Inc(out_row_ctr); + Inc(in_row_group_ctr); +end; + + +{ 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} +procedure h2v1_merged_upsample (cinfo : j_decompress_ptr; + input_buf : JSAMPIMAGE; + in_row_group_ctr : JDIMENSION; + output_buf : JSAMPARRAY); +var + upsample : my_upsample_ptr; + {register} y, cred, cgreen, cblue : int; + cb, cr : int; + {register} outptr : JSAMPROW; + inptr0, inptr1, inptr2 : JSAMPLE_PTR; + col : JDIMENSION; + { copy these pointers into registers if possible } + {register} range_limit : range_limit_table_ptr; + Crrtab : int_CConvertPtr; + Cbbtab : int_CConvertPtr; + Crgtab : INT32_CConvertPtr; + Cbgtab : INT32_CConvertPtr; +var + shift_temp : INT32; +begin + upsample := my_upsample_ptr (cinfo^.upsample); + range_limit := cinfo^.sample_range_limit; + Crrtab := upsample^.Cr_r_tab; + Cbbtab := upsample^.Cb_b_tab; + Crgtab := upsample^.Cr_g_tab; + Cbgtab := upsample^.Cb_g_tab; + + inptr0 := JSAMPLE_PTR(input_buf^[0]^[in_row_group_ctr]); + inptr1 := JSAMPLE_PTR(input_buf^[1]^[in_row_group_ctr]); + inptr2 := JSAMPLE_PTR(input_buf^[2]^[in_row_group_ctr]); + outptr := output_buf^[0]; + { Loop for each pair of output pixels } + for col := pred(cinfo^.output_width shr 1) downto 0 do + begin + { Do the chroma part of the calculation } + cb := GETJSAMPLE(inptr1^); + Inc(inptr1); + cr := GETJSAMPLE(inptr2^); + Inc(inptr2); + cred := Crrtab^[cr]; + {cgreen := int( RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS) );} + shift_temp := Cbgtab^[cb] + Crgtab^[cr]; + if shift_temp < 0 then { SHIFT arithmetic RIGHT } + cgreen := int((shift_temp shr SCALEBITS) + or ( (not INT32(0)) shl (32-SCALEBITS))) + else + cgreen := int(shift_temp shr SCALEBITS); + + cblue := Cbbtab^[cb]; + { Fetch 2 Y values and emit 2 pixels } + y := GETJSAMPLE(inptr0^); + Inc(inptr0); + outptr^[RGB_RED] := range_limit^[y + cred]; + outptr^[RGB_GREEN] := range_limit^[y + cgreen]; + outptr^[RGB_BLUE] := range_limit^[y + cblue]; + Inc(JSAMPLE_PTR(outptr), RGB_PIXELSIZE); + y := GETJSAMPLE(inptr0^); + Inc(inptr0); + outptr^[RGB_RED] := range_limit^[y + cred]; + outptr^[RGB_GREEN] := range_limit^[y + cgreen]; + outptr^[RGB_BLUE] := range_limit^[y + cblue]; + Inc(JSAMPLE_PTR(outptr), RGB_PIXELSIZE); + end; + { If image width is odd, do the last output column separately } + if Odd(cinfo^.output_width) then + begin + cb := GETJSAMPLE(inptr1^); + cr := GETJSAMPLE(inptr2^); + cred := Crrtab^[cr]; + {cgreen := int ( RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS) );} + shift_temp := Cbgtab^[cb] + Crgtab^[cr]; + if shift_temp < 0 then { SHIFT arithmetic RIGHT } + cgreen := int((shift_temp shr SCALEBITS) + or ( (not INT32(0)) shl (32-SCALEBITS))) + else + cgreen := int(shift_temp shr 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]; + end; +end; + + +{ Upsample and color convert for the case of 2:1 horizontal and 2:1 vertical. } + +{METHODDEF} +procedure h2v2_merged_upsample (cinfo : j_decompress_ptr; + input_buf : JSAMPIMAGE; + in_row_group_ctr : JDIMENSION; + output_buf : JSAMPARRAY); +var + upsample : my_upsample_ptr; + {register} y, cred, cgreen, cblue : int; + cb, cr : int; + {register} outptr0, outptr1 : JSAMPROW; + inptr00, inptr01, inptr1, inptr2 : JSAMPLE_PTR; + col : JDIMENSION; + { copy these pointers into registers if possible } + {register} range_limit : range_limit_table_ptr; + Crrtab : int_CConvertPtr; + Cbbtab : int_CConvertPtr; + Crgtab : INT32_CConvertPtr; + Cbgtab : INT32_CConvertPtr; +var + shift_temp : INT32; +begin + upsample := my_upsample_ptr (cinfo^.upsample); + range_limit := cinfo^.sample_range_limit; + Crrtab := upsample^.Cr_r_tab; + Cbbtab := upsample^.Cb_b_tab; + Crgtab := upsample^.Cr_g_tab; + Cbgtab := upsample^.Cb_g_tab; + + inptr00 := JSAMPLE_PTR(input_buf^[0]^[in_row_group_ctr*2]); + inptr01 := JSAMPLE_PTR(input_buf^[0]^[in_row_group_ctr*2 + 1]); + inptr1 := JSAMPLE_PTR(input_buf^[1]^[in_row_group_ctr]); + inptr2 := JSAMPLE_PTR(input_buf^[2]^[in_row_group_ctr]); + outptr0 := output_buf^[0]; + outptr1 := output_buf^[1]; + { Loop for each group of output pixels } + for col := pred(cinfo^.output_width shr 1) downto 0 do + begin + { Do the chroma part of the calculation } + cb := GETJSAMPLE(inptr1^); + Inc(inptr1); + cr := GETJSAMPLE(inptr2^); + Inc(inptr2); + cred := Crrtab^[cr]; + {cgreen := int( RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS) );} + shift_temp := Cbgtab^[cb] + Crgtab^[cr]; + if shift_temp < 0 then { SHIFT arithmetic RIGHT } + cgreen := int((shift_temp shr SCALEBITS) + or ( (not INT32(0)) shl (32-SCALEBITS))) + else + cgreen := int(shift_temp shr SCALEBITS); + + cblue := Cbbtab^[cb]; + { Fetch 4 Y values and emit 4 pixels } + y := GETJSAMPLE(inptr00^); + Inc(inptr00); + outptr0^[RGB_RED] := range_limit^[y + cred]; + outptr0^[RGB_GREEN] := range_limit^[y + cgreen]; + outptr0^[RGB_BLUE] := range_limit^[y + cblue]; + Inc(JSAMPLE_PTR(outptr0), RGB_PIXELSIZE); + y := GETJSAMPLE(inptr00^); + Inc(inptr00); + outptr0^[RGB_RED] := range_limit^[y + cred]; + outptr0^[RGB_GREEN] := range_limit^[y + cgreen]; + outptr0^[RGB_BLUE] := range_limit^[y + cblue]; + Inc(JSAMPLE_PTR(outptr0), RGB_PIXELSIZE); + y := GETJSAMPLE(inptr01^); + Inc(inptr01); + outptr1^[RGB_RED] := range_limit^[y + cred]; + outptr1^[RGB_GREEN] := range_limit^[y + cgreen]; + outptr1^[RGB_BLUE] := range_limit^[y + cblue]; + Inc(JSAMPLE_PTR(outptr1), RGB_PIXELSIZE); + y := GETJSAMPLE(inptr01^); + Inc(inptr01); + outptr1^[RGB_RED] := range_limit^[y + cred]; + outptr1^[RGB_GREEN] := range_limit^[y + cgreen]; + outptr1^[RGB_BLUE] := range_limit^[y + cblue]; + Inc(JSAMPLE_PTR(outptr1), RGB_PIXELSIZE); + end; + { If image width is odd, do the last output column separately } + if Odd(cinfo^.output_width) then + begin + cb := GETJSAMPLE(inptr1^); + cr := GETJSAMPLE(inptr2^); + cred := Crrtab^[cr]; + {cgreen := int (RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS));} + shift_temp := Cbgtab^[cb] + Crgtab^[cr]; + if shift_temp < 0 then { SHIFT arithmetic RIGHT } + cgreen := int((shift_temp shr SCALEBITS) + or ( (not INT32(0)) shl (32-SCALEBITS))) + else + cgreen := int(shift_temp shr 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]; + end; +end; + + +{ 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} +procedure jinit_merged_upsampler (cinfo : j_decompress_ptr); +var + upsample : my_upsample_ptr; +begin + upsample := my_upsample_ptr ( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + SIZEOF(my_upsampler)) ); + cinfo^.upsample := jpeg_upsampler_ptr (upsample); + upsample^.pub.start_pass := start_pass_merged_upsample; + upsample^.pub.need_context_rows := FALSE; + + upsample^.out_row_width := cinfo^.output_width * JDIMENSION(cinfo^.out_color_components); + + if (cinfo^.max_v_samp_factor = 2) then + begin + 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))) ); + end + else + begin + upsample^.pub.upsample := merged_1v_upsample; + upsample^.upmethod := h2v1_merged_upsample; + { No spare row needed } + upsample^.spare_row := NIL; + end; + + build_ycc_rgb_table(cinfo); +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjdphuff.pas b/resources/libraries/deskew/Imaging/JpegLib/imjdphuff.pas new file mode 100755 index 0000000..a49bed1 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjdphuff.pas @@ -0,0 +1,1061 @@ +unit imjdphuff; + +{ 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. } + +{ Original: jdphuff.c ; Copyright (C) 1995-1997, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjpeglib, + imjdeferr, + imjerror, + imjutils, + imjdhuff; { Declarations shared with jdhuff.c } + + +{GLOBAL} +procedure jinit_phuff_decoder (cinfo : j_decompress_ptr); + +implementation + +{ 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. } + +type + savable_state = record + EOBRUN : uInt; { remaining EOBs in EOBRUN } + last_dc_val : array[00..MAX_COMPS_IN_SCAN-1] of int; + { last DC coef for each component } + end; + + +type + phuff_entropy_ptr = ^phuff_entropy_decoder; + phuff_entropy_decoder = record + pub : jpeg_entropy_decoder; { public fields } + + { These fields are loaded into local variables at start of each MCU. + In case of suspension, we exit WITHOUT updating them. } + + bitstate : bitread_perm_state; { Bit buffer at start of MCU } + saved : savable_state; { Other state at start of MCU } + + { These fields are NOT loaded into local working state. } + restarts_to_go : uInt; { MCUs left in this restart interval } + + { Pointers to derived tables (these workspaces have image lifespan) } + derived_tbls : array[0..NUM_HUFF_TBLS-1] of d_derived_tbl_ptr; + + ac_derived_tbl : d_derived_tbl_ptr; { active table during an AC scan } + end; + + + +{ Forward declarations } +{METHODDEF} +function decode_mcu_DC_first (cinfo : j_decompress_ptr; + var MCU_data : array of JBLOCKROW) : boolean; + forward; +{METHODDEF} +function decode_mcu_AC_first (cinfo : j_decompress_ptr; + var MCU_data : array of JBLOCKROW) : boolean; + forward; +{METHODDEF} +function decode_mcu_DC_refine (cinfo : j_decompress_ptr; + var MCU_data : array of JBLOCKROW) : boolean; + forward; +{METHODDEF} +function decode_mcu_AC_refine (cinfo : j_decompress_ptr; + var MCU_data : array of JBLOCKROW) : boolean; + forward; + +{ Initialize for a Huffman-compressed scan. } + +{METHODDEF} +procedure start_pass_phuff_decoder (cinfo : j_decompress_ptr); +var + entropy : phuff_entropy_ptr; + is_DC_band, bad : boolean; + ci, coefi, tbl : int; + coef_bit_ptr : coef_bits_ptr; + compptr : jpeg_component_info_ptr; +var + cindex : int; + expected : int; +begin + entropy := phuff_entropy_ptr (cinfo^.entropy); + + is_DC_band := (cinfo^.Ss = 0); + + { Validate scan parameters } + bad := FALSE; + if (is_DC_band) then + begin + if (cinfo^.Se <> 0) then + bad := TRUE; + end + else + begin + { need not check Ss/Se < 0 since they came from unsigned bytes } + if (cinfo^.Ss > cinfo^.Se) or (cinfo^.Se >= DCTSIZE2) then + bad := TRUE; + { AC scans may have only one component } + if (cinfo^.comps_in_scan <> 1) then + bad := TRUE; + end; + if (cinfo^.Ah <> 0) then + begin + { Successive approximation refinement scan: must have Al = Ah-1. } + if (cinfo^.Al <> cinfo^.Ah-1) then + bad := TRUE; + end; + if (cinfo^.Al > 13) then { 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) then + ERREXIT4(j_common_ptr(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 to pred(cinfo^.comps_in_scan) do + begin + cindex := cinfo^.cur_comp_info[ci]^.component_index; + coef_bit_ptr := coef_bits_ptr(@(cinfo^.coef_bits^[cindex])); {^[0] ??? + Nomssi } + if (not is_DC_band) and (coef_bit_ptr^[0] < 0) then + { AC without prior DC scan } + WARNMS2(j_common_ptr(cinfo), JWRN_BOGUS_PROGRESSION, cindex, 0); + for coefi := cinfo^.Ss to cinfo^.Se do + begin + if (coef_bit_ptr^[coefi] < 0) then + expected := 0 + else + expected := coef_bit_ptr^[coefi]; + if (cinfo^.Ah <> expected) then + WARNMS2(j_common_ptr(cinfo), JWRN_BOGUS_PROGRESSION, cindex, coefi); + coef_bit_ptr^[coefi] := cinfo^.Al; + end; + end; + + { Select MCU decoding routine } + if (cinfo^.Ah = 0) then + begin + if (is_DC_band) then + entropy^.pub.decode_mcu := decode_mcu_DC_first + else + entropy^.pub.decode_mcu := decode_mcu_AC_first; + end + else + begin + if (is_DC_band) then + entropy^.pub.decode_mcu := decode_mcu_DC_refine + else + entropy^.pub.decode_mcu := decode_mcu_AC_refine; + end; + + for ci := 0 to pred(cinfo^.comps_in_scan) do + begin + 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) then + begin + if (cinfo^.Ah = 0) then + begin { DC refinement needs no table } + tbl := compptr^.dc_tbl_no; + jpeg_make_d_derived_tbl(cinfo, TRUE, tbl, + entropy^.derived_tbls[tbl]); + end; + end + else + begin + 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]; + end; + { Initialize DC predictions to 0 } + entropy^.saved.last_dc_val[ci] := 0; + end; + + { 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; +end; + + +{ 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) < (1shl((s)-1)) ? (x) + (((-1)shl(s)) + 1) : (x)) + +{$else} + +{ #define HUFF_EXTEND(x,s) + if (x) < extend_test[s] then + (x) + extend_offset[s] + else + (x)} + +const + extend_test : Array[0..16-1] of int = { entry n is 2**(n-1) } + ($0000, $0001, $0002, $0004, $0008, $0010, $0020, $0040, + $0080, $0100, $0200, $0400, $0800, $1000, $2000, $4000); + +const + extend_offset : array[0..16-1] of int = { entry n is (-1 shl n) + 1 } + ( 0, ((-1) shl 1) + 1, ((-1) shl 2) + 1, ((-1) shl 3) + 1, ((-1) shl 4) + 1, + ((-1) shl 5) + 1, ((-1) shl 6) + 1, ((-1) shl 7) + 1, ((-1) shl 8) + 1, + ((-1) shl 9) + 1, ((-1) shl 10) + 1, ((-1) shl 11) + 1, ((-1) shl 12) + 1, + ((-1) shl 13) + 1, ((-1) shl 14) + 1, ((-1) shl 15) + 1 ); + +{$endif} { AVOID_TABLES } + + +{ Check for a restart marker & resynchronize decoder. + return:=s FALSE if must suspend. } + +{LOCAL} +function process_restart (cinfo : j_decompress_ptr) : boolean; +var + entropy : phuff_entropy_ptr; + ci : int; +begin + entropy := phuff_entropy_ptr (cinfo^.entropy); + + { Throw away any unused bits remaining in bit buffer; } + { include any full bytes in next_marker's count of discarded bytes } + Inc(cinfo^.marker^.discarded_bytes, entropy^.bitstate.bits_left div 8); + entropy^.bitstate.bits_left := 0; + + { Advance past the RSTn marker } + if (not cinfo^.marker^.read_restart_marker (cinfo)) then + begin + process_restart := FALSE; + exit; + end; + + { Re-initialize DC predictions to 0 } + for ci := 0 to pred(cinfo^.comps_in_scan) do + 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) then + entropy^.pub.insufficient_data := FALSE; + + process_restart := TRUE; +end; + + +{ 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} +function decode_mcu_DC_first (cinfo : j_decompress_ptr; + var MCU_data : array of JBLOCKROW) : boolean; +label + label1; +var + entropy : phuff_entropy_ptr; + Al : int; + {register} s, r : int; + blkn, ci : int; + block : JBLOCK_PTR; + {BITREAD_STATE_VARS;} + get_buffer : bit_buf_type ; {register} + bits_left : int; {register} + br_state : bitread_working_state; + + state : savable_state; + tbl : d_derived_tbl_ptr; + compptr : jpeg_component_info_ptr; +var + nb, look : int; {register} +begin + entropy := phuff_entropy_ptr (cinfo^.entropy); + Al := cinfo^.Al; + + { Process restart marker if needed; may have to suspend } + if (cinfo^.restart_interval <> 0) then + begin + if (entropy^.restarts_to_go = 0) then + if (not process_restart(cinfo)) then + begin + decode_mcu_DC_first := FALSE; + exit; + end; + end; + + { 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 not entropy^.pub.insufficient_data then + begin + + { Load up working state } + {BITREAD_LOAD_STATE(cinfo,entropy^.bitstate);} + br_state.cinfo := cinfo; + br_state.next_input_byte := cinfo^.src^.next_input_byte; + br_state.bytes_in_buffer := cinfo^.src^.bytes_in_buffer; + get_buffer := entropy^.bitstate.get_buffer; + bits_left := entropy^.bitstate.bits_left; + + {ASSIGN_STATE(state, entropy^.saved);} + state := entropy^.saved; + + { Outer loop handles each block in the MCU } + + for blkn := 0 to pred(cinfo^.blocks_in_MCU) do + begin + block := JBLOCK_PTR(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 (bits_left < HUFF_LOOKAHEAD) then + begin + if (not jpeg_fill_bit_buffer(br_state,get_buffer,bits_left, 0)) then + begin + decode_mcu_DC_first := FALSE; + exit; + end; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + if (bits_left < HUFF_LOOKAHEAD) then + begin + nb := 1; + goto label1; + end; + end; + {look := PEEK_BITS(HUFF_LOOKAHEAD);} + look := int(get_buffer shr (bits_left - HUFF_LOOKAHEAD)) and + pred(1 shl HUFF_LOOKAHEAD); + + nb := tbl^.look_nbits[look]; + if (nb <> 0) then + begin + {DROP_BITS(nb);} + Dec(bits_left, nb); + + s := tbl^.look_sym[look]; + end + else + begin + nb := HUFF_LOOKAHEAD+1; + label1: + s := jpeg_huff_decode(br_state,get_buffer,bits_left,tbl,nb); + if (s < 0) then + begin + decode_mcu_DC_first := FALSE; + exit; + end; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + end; + + if (s <> 0) then + begin + {CHECK_BIT_BUFFER(br_state, s, return FALSE);} + if (bits_left < s) then + begin + if (not jpeg_fill_bit_buffer(br_state,get_buffer,bits_left,s)) then + begin + decode_mcu_DC_first := FALSE; + exit; + end; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + end; + + {r := GET_BITS(s);} + Dec(bits_left, s); + r := (int(get_buffer shr bits_left)) and ( pred(1 shl s) ); + + {s := HUFF_EXTEND(r, s);} + if (r < extend_test[s]) then + s := r + extend_offset[s] + else + s := r; + end; + + { Convert DC difference to actual value, update last_dc_val } + Inc(s, state.last_dc_val[ci]); + state.last_dc_val[ci] := s; + { Scale and output the DC coefficient (assumes jpeg_natural_order[0]=0) } + block^[0] := JCOEF (s shl Al); + end; + + { Completed MCU, so update state } + {BITREAD_SAVE_STATE(cinfo,entropy^.bitstate);} + cinfo^.src^.next_input_byte := br_state.next_input_byte; + cinfo^.src^.bytes_in_buffer := br_state.bytes_in_buffer; + entropy^.bitstate.get_buffer := get_buffer; + entropy^.bitstate.bits_left := bits_left; + + {ASSIGN_STATE(entropy^.saved, state);} + entropy^.saved := state; + end; + + { Account for restart interval (no-op if not using restarts) } + Dec(entropy^.restarts_to_go); + + decode_mcu_DC_first := TRUE; +end; + + +{ MCU decoding for AC initial scan (either spectral selection, + or first pass of successive approximation). } + +{METHODDEF} +function decode_mcu_AC_first (cinfo : j_decompress_ptr; + var MCU_data : array of JBLOCKROW) : boolean; +label + label2; +var + entropy : phuff_entropy_ptr; + Se : int; + Al : int; + {register} s, k, r : int; + EOBRUN : uInt; + block : JBLOCK_PTR; + {BITREAD_STATE_VARS;} + get_buffer : bit_buf_type ; {register} + bits_left : int; {register} + br_state : bitread_working_state; + + tbl : d_derived_tbl_ptr; +var + nb, look : int; {register} +begin + entropy := phuff_entropy_ptr (cinfo^.entropy); + Se := cinfo^.Se; + Al := cinfo^.Al; + + { Process restart marker if needed; may have to suspend } + if (cinfo^.restart_interval <> 0) then + begin + if (entropy^.restarts_to_go = 0) then + if (not process_restart(cinfo)) then + begin + decode_mcu_AC_first := FALSE; + exit; + end; + end; + + { 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 not entropy^.pub.insufficient_data then + begin + + { 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 care about } + + { There is always only one block per MCU } + + if (EOBRUN > 0) then { if it's a band of zeroes... } + Dec(EOBRUN) { ...process it now (we do nothing) } + else + begin + {BITREAD_LOAD_STATE(cinfo,entropy^.bitstate);} + br_state.cinfo := cinfo; + br_state.next_input_byte := cinfo^.src^.next_input_byte; + br_state.bytes_in_buffer := cinfo^.src^.bytes_in_buffer; + get_buffer := entropy^.bitstate.get_buffer; + bits_left := entropy^.bitstate.bits_left; + + block := JBLOCK_PTR(MCU_data[0]); + tbl := entropy^.ac_derived_tbl; + + k := cinfo^.Ss; + while (k <= Se) do + begin + {HUFF_DECODE(s, br_state, tbl, return FALSE, label2);} + if (bits_left < HUFF_LOOKAHEAD) then + begin + if (not jpeg_fill_bit_buffer(br_state,get_buffer,bits_left, 0)) then + begin + decode_mcu_AC_first := FALSE; + exit; + end; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + if (bits_left < HUFF_LOOKAHEAD) then + begin + nb := 1; + goto label2; + end; + end; + {look := PEEK_BITS(HUFF_LOOKAHEAD);} + look := int(get_buffer shr (bits_left - HUFF_LOOKAHEAD)) and + pred(1 shl HUFF_LOOKAHEAD); + + nb := tbl^.look_nbits[look]; + if (nb <> 0) then + begin + {DROP_BITS(nb);} + Dec(bits_left, nb); + + s := tbl^.look_sym[look]; + end + else + begin + nb := HUFF_LOOKAHEAD+1; + label2: + s := jpeg_huff_decode(br_state,get_buffer,bits_left,tbl,nb); + if (s < 0) then + begin + decode_mcu_AC_first := FALSE; + exit; + end; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + end; + + r := s shr 4; + s := s and 15; + if (s <> 0) then + begin + Inc(k, r); + {CHECK_BIT_BUFFER(br_state, s, return FALSE);} + if (bits_left < s) then + begin + if (not jpeg_fill_bit_buffer(br_state,get_buffer,bits_left,s)) then + begin + decode_mcu_AC_first := FALSE; + exit; + end; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + end; + + {r := GET_BITS(s);} + Dec(bits_left, s); + r := (int(get_buffer shr bits_left)) and ( pred(1 shl s) ); + + {s := HUFF_EXTEND(r, s);} + if (r < extend_test[s]) then + s := r + extend_offset[s] + else + s := r; + + { Scale and output coefficient in natural (dezigzagged) order } + block^[jpeg_natural_order[k]] := JCOEF (s shl Al); + end + else + begin + if (r = 15) then + begin { ZRL } + Inc(k, 15); { skip 15 zeroes in band } + end + else + begin { EOBr, run length is 2^r + appended bits } + EOBRUN := 1 shl r; + if (r <> 0) then + begin { EOBr, r > 0 } + {CHECK_BIT_BUFFER(br_state, r, return FALSE);} + if (bits_left < r) then + begin + if (not jpeg_fill_bit_buffer(br_state,get_buffer,bits_left,r)) then + begin + decode_mcu_AC_first := FALSE; + exit; + end; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + end; + + {r := GET_BITS(r);} + Dec(bits_left, r); + r := (int(get_buffer shr bits_left)) and ( pred(1 shl r) ); + + Inc(EOBRUN, r); + end; + Dec(EOBRUN); { this band is processed at this moment } + break; { force end-of-band } + end; + end; + Inc(k); + end; + + {BITREAD_SAVE_STATE(cinfo,entropy^.bitstate);} + cinfo^.src^.next_input_byte := br_state.next_input_byte; + cinfo^.src^.bytes_in_buffer := br_state.bytes_in_buffer; + entropy^.bitstate.get_buffer := get_buffer; + entropy^.bitstate.bits_left := bits_left; + end; + + { Completed MCU, so update state } + entropy^.saved.EOBRUN := EOBRUN; { only part of saved state we care about } + end; + + { Account for restart interval (no-op if not using restarts) } + Dec(entropy^.restarts_to_go); + + decode_mcu_AC_first := TRUE; +end; + + +{ 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} +function decode_mcu_DC_refine (cinfo : j_decompress_ptr; + var MCU_data : array of JBLOCKROW) : boolean; + +var + entropy : phuff_entropy_ptr; + p1 : int; { 1 in the bit position being coded } + blkn : int; + block : JBLOCK_PTR; + {BITREAD_STATE_VARS;} + get_buffer : bit_buf_type ; {register} + bits_left : int; {register} + br_state : bitread_working_state; +begin + entropy := phuff_entropy_ptr (cinfo^.entropy); + p1 := 1 shl cinfo^.Al; + + { Process restart marker if needed; may have to suspend } + if (cinfo^.restart_interval <> 0) then + begin + if (entropy^.restarts_to_go = 0) then + if (not process_restart(cinfo)) then + begin + decode_mcu_DC_refine := FALSE; + exit; + end; + end; + + { 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);} + br_state.cinfo := cinfo; + br_state.next_input_byte := cinfo^.src^.next_input_byte; + br_state.bytes_in_buffer := cinfo^.src^.bytes_in_buffer; + get_buffer := entropy^.bitstate.get_buffer; + bits_left := entropy^.bitstate.bits_left; + + { Outer loop handles each block in the MCU } + + for blkn := 0 to pred(cinfo^.blocks_in_MCU) do + begin + block := JBLOCK_PTR(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 (bits_left < 1) then + begin + if (not jpeg_fill_bit_buffer(br_state,get_buffer,bits_left,1)) then + begin + decode_mcu_DC_refine := FALSE; + exit; + end; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + end; + + {if (GET_BITS(1)) then} + Dec(bits_left); + if (int(get_buffer shr bits_left)) and ( pred(1 shl 1) ) <> 0 then + block^[0] := block^[0] or p1; + { Note: since we use OR, repeating the assignment later is safe } + end; + + { Completed MCU, so update state } + {BITREAD_SAVE_STATE(cinfo,entropy^.bitstate);} + cinfo^.src^.next_input_byte := br_state.next_input_byte; + cinfo^.src^.bytes_in_buffer := br_state.bytes_in_buffer; + entropy^.bitstate.get_buffer := get_buffer; + entropy^.bitstate.bits_left := bits_left; + + { Account for restart interval (no-op if not using restarts) } + Dec(entropy^.restarts_to_go); + + decode_mcu_DC_refine := TRUE; +end; + + +{ MCU decoding for AC successive approximation refinement scan. } + +{METHODDEF} +function decode_mcu_AC_refine (cinfo : j_decompress_ptr; + var MCU_data : array of JBLOCKROW) : boolean; +label + undoit, label3; +var + entropy : phuff_entropy_ptr; + Se : int; + p1 : int; { 1 in the bit position being coded } + m1 : int; { -1 in the bit position being coded } + {register} s, k, r : int; + EOBRUN : uInt; + block : JBLOCK_PTR; + thiscoef : JCOEF_PTR; + {BITREAD_STATE_VARS;} + get_buffer : bit_buf_type ; {register} + bits_left : int; {register} + br_state : bitread_working_state; + + tbl : d_derived_tbl_ptr; + num_newnz : int; + newnz_pos : array[0..DCTSIZE2-1] of int; +var + pos : int; +var + nb, look : int; {register} +begin + num_newnz := 0; + block := nil; + + entropy := phuff_entropy_ptr (cinfo^.entropy); + Se := cinfo^.Se; + p1 := 1 shl cinfo^.Al; { 1 in the bit position being coded } + m1 := (-1) shl cinfo^.Al; { -1 in the bit position being coded } + + { Process restart marker if needed; may have to suspend } + if (cinfo^.restart_interval <> 0) then + begin + if (entropy^.restarts_to_go = 0) then + if (not process_restart(cinfo)) then + begin + decode_mcu_AC_refine := FALSE; + exit; + end; + end; + + { If we've run out of data, don't modify the MCU. } + if not entropy^.pub.insufficient_data then + begin + + { Load up working state } + {BITREAD_LOAD_STATE(cinfo,entropy^.bitstate);} + br_state.cinfo := cinfo; + br_state.next_input_byte := cinfo^.src^.next_input_byte; + br_state.bytes_in_buffer := cinfo^.src^.bytes_in_buffer; + get_buffer := entropy^.bitstate.get_buffer; + bits_left := entropy^.bitstate.bits_left; + + EOBRUN := entropy^.saved.EOBRUN; { only part of saved state we care about } + + { There is always only one block per MCU } + block := JBLOCK_PTR(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 position to see if we already did it.} + + num_newnz := 0; + + { initialize coefficient loop counter to start of band } + k := cinfo^.Ss; + + if (EOBRUN = 0) then + begin + while (k <= Se) do + begin + {HUFF_DECODE(s, br_state, tbl, goto undoit, label3);} + if (bits_left < HUFF_LOOKAHEAD) then + begin + if (not jpeg_fill_bit_buffer(br_state,get_buffer,bits_left, 0)) then + goto undoit; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + if (bits_left < HUFF_LOOKAHEAD) then + begin + nb := 1; + goto label3; + end; + end; + {look := PEEK_BITS(HUFF_LOOKAHEAD);} + look := int(get_buffer shr (bits_left - HUFF_LOOKAHEAD)) and + pred(1 shl HUFF_LOOKAHEAD); + + nb := tbl^.look_nbits[look]; + if (nb <> 0) then + begin + {DROP_BITS(nb);} + Dec(bits_left, nb); + + s := tbl^.look_sym[look]; + end + else + begin + nb := HUFF_LOOKAHEAD+1; + label3: + s := jpeg_huff_decode(br_state,get_buffer,bits_left,tbl,nb); + if (s < 0) then + goto undoit; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + end; + + r := s shr 4; + s := s and 15; + if (s <> 0) then + begin + if (s <> 1) then { size of new coef should always be 1 } + WARNMS(j_common_ptr(cinfo), JWRN_HUFF_BAD_CODE); + {CHECK_BIT_BUFFER(br_state, 1, goto undoit);} + if (bits_left < 1) then + begin + if (not jpeg_fill_bit_buffer(br_state,get_buffer,bits_left,1)) then + goto undoit; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + end; + + {if (GET_BITS(1)) then} + Dec(bits_left); + if (int(get_buffer shr bits_left)) and ( pred(1 shl 1) )<>0 then + s := p1 { newly nonzero coef is positive } + else + s := m1; { newly nonzero coef is negative } + end + else + begin + if (r <> 15) then + begin + EOBRUN := 1 shl r; { EOBr, run length is 2^r + appended bits } + if (r <> 0) then + begin + {CHECK_BIT_BUFFER(br_state, r, goto undoit);} + if (bits_left < r) then + begin + if (not jpeg_fill_bit_buffer(br_state,get_buffer,bits_left,r)) then + goto undoit; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + end; + + {r := GET_BITS(r);} + Dec(bits_left, r); + r := (int(get_buffer shr bits_left)) and ( pred(1 shl r) ); + + Inc(EOBRUN, r); + end; + break; { rest of block is handled by EOB logic } + end; + { note s := 0 for processing ZRL } + end; + { 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. } + + repeat + thiscoef :=@(block^[jpeg_natural_order[k]]); + if (thiscoef^ <> 0) then + begin + {CHECK_BIT_BUFFER(br_state, 1, goto undoit);} + if (bits_left < 1) then + begin + if (not jpeg_fill_bit_buffer(br_state,get_buffer,bits_left,1)) then + goto undoit; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + end; + + {if (GET_BITS(1)) then} + Dec(bits_left); + if (int(get_buffer shr bits_left)) and ( pred(1 shl 1) )<>0 then + begin + if ((thiscoef^ and p1) = 0) then + begin { do nothing if already set it } + if (thiscoef^ >= 0) then + Inc(thiscoef^, p1) + else + Inc(thiscoef^, m1); + end; + end; + end + else + begin + Dec(r); + if (r < 0) then + break; { reached target zero coefficient } + end; + Inc(k); + until (k > Se); + if (s <> 0) then + begin + 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; + Inc(num_newnz); + end; + Inc(k); + end; + end; + + if (EOBRUN > 0) then + begin + { 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. } + + while (k <= Se) do + begin + thiscoef := @(block^[jpeg_natural_order[k]]); + if (thiscoef^ <> 0) then + begin + {CHECK_BIT_BUFFER(br_state, 1, goto undoit);} + if (bits_left < 1) then + begin + if (not jpeg_fill_bit_buffer(br_state,get_buffer,bits_left,1)) then + goto undoit; + get_buffer := br_state.get_buffer; + bits_left := br_state.bits_left; + end; + + {if (GET_BITS(1)) then} + Dec(bits_left); + if (int(get_buffer shr bits_left)) and ( pred(1 shl 1) )<>0 then + begin + if ((thiscoef^ and p1) = 0) then + begin { do nothing if already changed it } + if (thiscoef^ >= 0) then + Inc(thiscoef^, p1) + else + Inc(thiscoef^, m1); + end; + end; + end; + Inc(k); + end; + { Count one block completed in EOB run } + Dec(EOBRUN); + end; + + { Completed MCU, so update state } + {BITREAD_SAVE_STATE(cinfo,entropy^.bitstate);} + cinfo^.src^.next_input_byte := br_state.next_input_byte; + cinfo^.src^.bytes_in_buffer := br_state.bytes_in_buffer; + entropy^.bitstate.get_buffer := get_buffer; + entropy^.bitstate.bits_left := bits_left; + + entropy^.saved.EOBRUN := EOBRUN; { only part of saved state we care about } + end; + + { Account for restart interval (no-op if not using restarts) } + Dec(entropy^.restarts_to_go); + + decode_mcu_AC_refine := TRUE; + exit; + +undoit: + { Re-zero any output coefficients that we made newly nonzero } + while (num_newnz > 0) do + begin + Dec(num_newnz); + block^[newnz_pos[num_newnz]] := 0; + end; + + decode_mcu_AC_refine := FALSE; +end; + + +{ Module initialization routine for progressive Huffman entropy decoding. } + +{GLOBAL} +procedure jinit_phuff_decoder (cinfo : j_decompress_ptr); +var + entropy : phuff_entropy_ptr; + coef_bit_ptr : int_ptr; + ci, i : int; +begin + entropy := phuff_entropy_ptr( + cinfo^.mem^.alloc_small (j_common_ptr (cinfo), JPOOL_IMAGE, + SIZEOF(phuff_entropy_decoder)) ); + cinfo^.entropy := jpeg_entropy_decoder_ptr (entropy); + entropy^.pub.start_pass := start_pass_phuff_decoder; + + { Mark derived tables unallocated } + for i := 0 to pred(NUM_HUFF_TBLS) do + begin + entropy^.derived_tbls[i] := NIL; + end; + + { Create progression status table } + cinfo^.coef_bits := coef_bits_ptrrow ( + 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 to pred(cinfo^.num_components) do + for i := 0 to pred(DCTSIZE2) do + begin + coef_bit_ptr^ := -1; + Inc(coef_bit_ptr); + end; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjdpostct.pas b/resources/libraries/deskew/Imaging/JpegLib/imjdpostct.pas new file mode 100755 index 0000000..f3078c3 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjdpostct.pas @@ -0,0 +1,341 @@ +unit imjdpostct; + +{ Original: jdpostct.c ; Copyright (C) 1994-1996, Thomas G. Lane. } + +{ 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. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjdeferr, + imjerror, + imjutils, + imjpeglib; + +{ Initialize postprocessing controller. } + +{GLOBAL} +procedure jinit_d_post_controller (cinfo : j_decompress_ptr; + need_full_buffer : boolean); +implementation + + +{ Private buffer controller object } + +type + my_post_ptr = ^my_post_controller; + my_post_controller = record + pub : jpeg_d_post_controller; { 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. } + + whole_image : jvirt_sarray_ptr; { virtual array, or NIL if one-pass } + buffer : JSAMPARRAY; { strip buffer, or current strip of virtual } + strip_height : JDIMENSION; { buffer size in rows } + { for two-pass mode only: } + starting_row : JDIMENSION; { row # of first row in current strip } + next_row : JDIMENSION; { index of next row to fill/empty in strip } + end; + +{ Forward declarations } +{METHODDEF} +procedure post_process_1pass(cinfo : j_decompress_ptr; + input_buf : JSAMPIMAGE; + var in_row_group_ctr : JDIMENSION; + in_row_groups_avail : JDIMENSION; + output_buf : JSAMPARRAY; + var out_row_ctr : JDIMENSION; + out_rows_avail : JDIMENSION); forward; +{$ifdef QUANT_2PASS_SUPPORTED} +{METHODDEF} +procedure post_process_prepass(cinfo : j_decompress_ptr; + input_buf : JSAMPIMAGE; + var in_row_group_ctr : JDIMENSION; + in_row_groups_avail : JDIMENSION; + output_buf : JSAMPARRAY; + var out_row_ctr : JDIMENSION; + out_rows_avail : JDIMENSION); forward; +{METHODDEF} +procedure post_process_2pass(cinfo : j_decompress_ptr; + input_buf : JSAMPIMAGE; + var in_row_group_ctr : JDIMENSION; + in_row_groups_avail : JDIMENSION; + output_buf : JSAMPARRAY; + var out_row_ctr : JDIMENSION; + out_rows_avail : JDIMENSION); forward; +{$endif} + + +{ Initialize for a processing pass. } + +{METHODDEF} +procedure start_pass_dpost (cinfo : j_decompress_ptr; + pass_mode : J_BUF_MODE); +var + post : my_post_ptr; +begin + post := my_post_ptr(cinfo^.post); + + case (pass_mode) of + JBUF_PASS_THRU: + if (cinfo^.quantize_colors) then + begin + { 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 = NIL) then + begin + post^.buffer := cinfo^.mem^.access_virt_sarray + (j_common_ptr(cinfo), post^.whole_image, + JDIMENSION(0), post^.strip_height, TRUE); + end; + end + else + begin + { 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; + end; + +{$ifdef QUANT_2PASS_SUPPORTED} + JBUF_SAVE_AND_PASS: + begin + { First pass of 2-pass quantization } + if (post^.whole_image = NIL) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); + post^.pub.post_process_data := post_process_prepass; + end; + JBUF_CRANK_DEST: + begin + { Second pass of 2-pass quantization } + if (post^.whole_image = NIL) then + ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); + post^.pub.post_process_data := post_process_2pass; + end; +{$endif} { QUANT_2PASS_SUPPORTED } + else + ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); + end; + post^.next_row := 0; + post^.starting_row := 0; +end; + + +{ Process some data in the one-pass (strip buffer) case. + This is used for color precision reduction as well as one-pass quantization. } + +{METHODDEF} +procedure post_process_1pass (cinfo : j_decompress_ptr; + input_buf : JSAMPIMAGE; + var in_row_group_ctr : JDIMENSION; + in_row_groups_avail : JDIMENSION; + output_buf : JSAMPARRAY; + var out_row_ctr : JDIMENSION; + out_rows_avail : JDIMENSION); +var + post : my_post_ptr; + num_rows, max_rows : JDIMENSION; +begin + post := my_post_ptr (cinfo^.post); + + { 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) then + 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, { var } + max_rows); + { Quantize and emit data. } + + cinfo^.cquantize^.color_quantize (cinfo, + post^.buffer, + JSAMPARRAY(@ output_buf^[out_row_ctr]), + int(num_rows)); + + Inc(out_row_ctr, num_rows); +end; + + +{$ifdef QUANT_2PASS_SUPPORTED} + +{ Process some data in the first pass of 2-pass quantization. } + +{METHODDEF} +procedure post_process_prepass (cinfo : j_decompress_ptr; + input_buf : JSAMPIMAGE; + var in_row_group_ctr : JDIMENSION; + in_row_groups_avail : JDIMENSION; + output_buf : JSAMPARRAY; + var out_row_ctr : JDIMENSION; + out_rows_avail:JDIMENSION); +var + post : my_post_ptr; + old_next_row, num_rows : JDIMENSION; +begin + post := my_post_ptr(cinfo^.post); + + { Reposition virtual buffer if at start of strip. } + if (post^.next_row = 0) then + begin + post^.buffer := cinfo^.mem^.access_virt_sarray + (j_common_ptr(cinfo), post^.whole_image, + post^.starting_row, post^.strip_height, TRUE); + end; + + { 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) then + begin + num_rows := post^.next_row - old_next_row; + + + cinfo^.cquantize^.color_quantize (cinfo, + JSAMPARRAY(@ post^.buffer^[old_next_row]), + JSAMPARRAY(NIL), + int(num_rows)); + Inc(out_row_ctr, num_rows); + end; + + { Advance if we filled the strip. } + if (post^.next_row >= post^.strip_height) then + begin + Inc(post^.starting_row, post^.strip_height); + post^.next_row := 0; + end; +end; + + +{ Process some data in the second pass of 2-pass quantization. } + +{METHODDEF} +procedure post_process_2pass (cinfo : j_decompress_ptr; + input_buf : JSAMPIMAGE; + var in_row_group_ctr : JDIMENSION; + in_row_groups_avail : JDIMENSION; + output_buf : JSAMPARRAY; + var out_row_ctr : JDIMENSION; + out_rows_avail : JDIMENSION); +var + post : my_post_ptr; + num_rows, max_rows : JDIMENSION; +begin + post := my_post_ptr(cinfo^.post); + + { Reposition virtual buffer if at start of strip. } + if (post^.next_row = 0) then + begin + post^.buffer := cinfo^.mem^.access_virt_sarray + (j_common_ptr(cinfo), post^.whole_image, + post^.starting_row, post^.strip_height, FALSE); + end; + + { 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) then + 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) then + num_rows := max_rows; + + { Quantize and emit data. } + cinfo^.cquantize^.color_quantize (cinfo, + JSAMPARRAY(@ post^.buffer^[post^.next_row]), + JSAMPARRAY(@ output_buf^[out_row_ctr]), + int(num_rows)); + Inc(out_row_ctr, num_rows); + + { Advance if we filled the strip. } + Inc(post^.next_row, num_rows); + if (post^.next_row >= post^.strip_height) then + begin + Inc(post^.starting_row, post^.strip_height); + post^.next_row := 0; + end; +end; + +{$endif} { QUANT_2PASS_SUPPORTED } + + +{ Initialize postprocessing controller. } + +{GLOBAL} +procedure jinit_d_post_controller (cinfo : j_decompress_ptr; + need_full_buffer : boolean); +var + post : my_post_ptr; +begin + post := my_post_ptr( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + SIZEOF(my_post_controller)) ); + cinfo^.post := jpeg_d_post_controller_ptr (post); + post^.pub.start_pass := start_pass_dpost; + post^.whole_image := NIL; { flag for no virtual arrays } + post^.buffer := NIL; { flag for no strip buffer } + + { Create the quantization buffer, if needed } + if (cinfo^.quantize_colors) then + begin + { 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) then + begin + { 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, + LongInt(cinfo^.output_width) * cinfo^.out_color_components, + JDIMENSION (jround_up( long(cinfo^.output_height), + long(post^.strip_height)) ), + post^.strip_height); +{$else} + ERREXIT(j_common_ptr(cinfo), JERR_BAD_BUFFER_MODE); +{$endif} { QUANT_2PASS_SUPPORTED } + end + else + begin + { One-pass color quantization: just make a strip buffer. } + post^.buffer := cinfo^.mem^.alloc_sarray + (j_common_ptr (cinfo), JPOOL_IMAGE, + LongInt(cinfo^.output_width) * cinfo^.out_color_components, + post^.strip_height); + end; + end; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjdsample.pas b/resources/libraries/deskew/Imaging/JpegLib/imjdsample.pas new file mode 100755 index 0000000..ed2488c --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjdsample.pas @@ -0,0 +1,592 @@ +unit imjdsample; + +{ Original: jdsample.c; Copyright (C) 1991-1996, Thomas G. Lane. } + +{ 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.} + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjutils, + imjpeglib, + imjdeferr, + imjerror; + + +{ Pointer to routine to upsample a single component } +type + upsample1_ptr = procedure (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + input_data : JSAMPARRAY; + var output_data_ptr : JSAMPARRAY); + +{ Module initialization routine for upsampling. } + +{GLOBAL} +procedure jinit_upsampler (cinfo : j_decompress_ptr); + +implementation + +{ Private subobject } + +type + my_upsample_ptr = ^my_upsampler; + my_upsampler = record + pub : jpeg_upsampler; { 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.} + + color_buf : array[0..MAX_COMPONENTS-1] of JSAMPARRAY; + + { Per-component upsampling method pointers } + methods : array[0..MAX_COMPONENTS-1] of upsample1_ptr; + + next_row_out : int; { counts rows emitted from color_buf } + rows_to_go : JDIMENSION; { counts rows remaining in image } + + { Height of an input row group for each component. } + rowgroup_height : array[0..MAX_COMPONENTS-1] of int; + + { These arrays save pixel expansion factors so that int_expand need not + recompute them each time. They are unused for other upsampling methods.} + h_expand : array[0..MAX_COMPONENTS-1] of UINT8 ; + v_expand : array[0..MAX_COMPONENTS-1] of UINT8 ; + end; + + +{ Initialize for an upsampling pass. } + +{METHODDEF} +procedure start_pass_upsample (cinfo : j_decompress_ptr); +var + upsample : my_upsample_ptr; +begin + 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; +end; + + +{ 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} +procedure sep_upsample (cinfo : j_decompress_ptr; + input_buf : JSAMPIMAGE; + var in_row_group_ctr : JDIMENSION; + in_row_groups_avail : JDIMENSION; + output_buf : JSAMPARRAY; + var out_row_ctr : JDIMENSION; + out_rows_avail : JDIMENSION); +var + upsample : my_upsample_ptr; + ci : int; + compptr : jpeg_component_info_ptr; + num_rows : JDIMENSION; +begin + upsample := my_upsample_ptr (cinfo^.upsample); + + { Fill the conversion buffer, if it's empty } + if (upsample^.next_row_out >= cinfo^.max_v_samp_factor) then + begin + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + { 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, + JSAMPARRAY(@ input_buf^[ci]^ + [LongInt(in_row_group_ctr) * upsample^.rowgroup_height[ci]]), + upsample^.color_buf[ci]); + + Inc(compptr); + end; + upsample^.next_row_out := 0; + end; + + { 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) then + num_rows := upsample^.rows_to_go; + { And not more than what the client can accept: } + Dec(out_rows_avail, out_row_ctr); + if (num_rows > out_rows_avail) then + num_rows := out_rows_avail; + + cinfo^.cconvert^.color_convert (cinfo, + JSAMPIMAGE(@(upsample^.color_buf)), + JDIMENSION (upsample^.next_row_out), + JSAMPARRAY(@(output_buf^[out_row_ctr])), + int (num_rows)); + + { Adjust counts } + Inc(out_row_ctr, num_rows); + Dec(upsample^.rows_to_go, num_rows); + Inc(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) then + Inc(in_row_group_ctr); +end; + + +{ 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} +procedure fullsize_upsample (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + input_data : JSAMPARRAY; + var output_data_ptr : JSAMPARRAY); +begin + output_data_ptr := input_data; +end; + + +{ This is a no-op version used for "uninteresting" components. + These components will not be referenced by color conversion. } + +{METHODDEF} +procedure noop_upsample (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + input_data : JSAMPARRAY; + var output_data_ptr : JSAMPARRAY); +begin + output_data_ptr := NIL; { safety check } +end; + + +{ 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} +procedure int_upsample (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + input_data : JSAMPARRAY; + var output_data_ptr : JSAMPARRAY); +var + upsample : my_upsample_ptr; + output_data : JSAMPARRAY; + {register} inptr, outptr : JSAMPLE_PTR; + {register} invalue : JSAMPLE; + {register} h : int; + {outend} + h_expand, v_expand : int; + inrow, outrow : int; +var + outcount : int; { Nomssi: avoid pointer arithmetic } +begin + upsample := my_upsample_ptr (cinfo^.upsample); + output_data := output_data_ptr; + + h_expand := upsample^.h_expand[compptr^.component_index]; + v_expand := upsample^.v_expand[compptr^.component_index]; + + inrow := 0; + outrow := 0; + while (outrow < cinfo^.max_v_samp_factor) do + begin + { Generate one output row with proper horizontal expansion } + inptr := JSAMPLE_PTR(input_data^[inrow]); + outptr := JSAMPLE_PTR(output_data^[outrow]); + outcount := cinfo^.output_width; + while (outcount > 0) do { Nomssi } + begin + invalue := inptr^; { don't need GETJSAMPLE() here } + Inc(inptr); + for h := pred(h_expand) downto 0 do + begin + outptr^ := invalue; + inc(outptr); { <-- fix: this was left out in PasJpeg 1.0 } + Dec(outcount); { thanks to Jannie Gerber for the report } + end; + end; + + { Generate any additional output rows by duplicating the first one } + if (v_expand > 1) then + begin + jcopy_sample_rows(output_data, outrow, output_data, outrow+1, + v_expand-1, cinfo^.output_width); + end; + Inc(inrow); + Inc(outrow, v_expand); + end; +end; + + +{ Fast processing for the common case of 2:1 horizontal and 1:1 vertical. + It's still a box filter. } + +{METHODDEF} +procedure h2v1_upsample (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + input_data : JSAMPARRAY; + var output_data_ptr : JSAMPARRAY); +var + output_data : JSAMPARRAY; + {register} inptr, outptr : JSAMPLE_PTR; + {register} invalue : JSAMPLE; + {outend : JSAMPROW;} + outcount : int; + inrow : int; +begin + output_data := output_data_ptr; + + for inrow := 0 to pred(cinfo^.max_v_samp_factor) do + begin + inptr := JSAMPLE_PTR(input_data^[inrow]); + outptr := JSAMPLE_PTR(output_data^[inrow]); + {outend := outptr + cinfo^.output_width;} + outcount := cinfo^.output_width; + while (outcount > 0) do + begin + invalue := inptr^; { don't need GETJSAMPLE() here } + Inc(inptr); + outptr^ := invalue; + Inc(outptr); + outptr^ := invalue; + Inc(outptr); + Dec(outcount, 2); { Nomssi: to avoid pointer arithmetic } + end; + end; +end; + + +{ Fast processing for the common case of 2:1 horizontal and 2:1 vertical. + It's still a box filter. } + +{METHODDEF} +procedure h2v2_upsample (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + input_data : JSAMPARRAY; + var output_data_ptr : JSAMPARRAY); +var + output_data : JSAMPARRAY; + {register} inptr, outptr : JSAMPLE_PTR; + {register} invalue : JSAMPLE; + {outend : JSAMPROW;} + outcount : int; + inrow, outrow : int; +begin + output_data := output_data_ptr; + + inrow := 0; + outrow := 0; + while (outrow < cinfo^.max_v_samp_factor) do + begin + inptr := JSAMPLE_PTR(input_data^[inrow]); + outptr := JSAMPLE_PTR(output_data^[outrow]); + {outend := outptr + cinfo^.output_width;} + outcount := cinfo^.output_width; + while (outcount > 0) do + begin + invalue := inptr^; { don't need GETJSAMPLE() here } + Inc(inptr); + outptr^ := invalue; + Inc(outptr); + outptr^ := invalue; + Inc(outptr); + Dec(outcount, 2); + end; + jcopy_sample_rows(output_data, outrow, output_data, outrow+1, + 1, cinfo^.output_width); + Inc(inrow); + Inc(outrow, 2); + end; +end; + + +{ 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} +procedure h2v1_fancy_upsample (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + input_data : JSAMPARRAY; + var output_data_ptr : JSAMPARRAY); +var + output_data : JSAMPARRAY; + {register} pre_inptr, inptr, outptr : JSAMPLE_PTR; + {register} invalue : int; + {register} colctr : JDIMENSION; + inrow : int; +begin + output_data := output_data_ptr; + + for inrow := 0 to pred(cinfo^.max_v_samp_factor) do + begin + inptr := JSAMPLE_PTR(input_data^[inrow]); + outptr := JSAMPLE_PTR(output_data^[inrow]); + { Special case for first column } + pre_inptr := inptr; + invalue := GETJSAMPLE(inptr^); + Inc(inptr); + outptr^ := JSAMPLE (invalue); + Inc(outptr); + outptr^ := JSAMPLE ((invalue * 3 + GETJSAMPLE(inptr^) + 2) shr 2); + Inc(outptr); + + for colctr := pred(compptr^.downsampled_width - 2) downto 0 do + begin + { General case: 3/4 * nearer pixel + 1/4 * further pixel } + invalue := GETJSAMPLE(inptr^) * 3; + Inc(inptr); + outptr^ := JSAMPLE ((invalue + GETJSAMPLE(pre_inptr^) + 1) shr 2); + Inc(pre_inptr); + Inc(outptr); + outptr^ := JSAMPLE ((invalue + GETJSAMPLE(inptr^) + 2) shr 2); + Inc(outptr); + end; + + { Special case for last column } + invalue := GETJSAMPLE(inptr^); + outptr^ := JSAMPLE ((invalue * 3 + GETJSAMPLE(pre_inptr^) + 1) shr 2); + Inc(outptr); + outptr^ := JSAMPLE (invalue); + {Inc(outptr); - value never used } + end; +end; + + +{ 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} +procedure h2v2_fancy_upsample (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + input_data : JSAMPARRAY; + var output_data_ptr : JSAMPARRAY); +var + output_data : JSAMPARRAY; + {register} inptr0, inptr1, outptr : JSAMPLE_PTR; +{$ifdef BITS_IN_JSAMPLE_IS_8} + {register} thiscolsum, lastcolsum, nextcolsum : int; +{$else} + {register} thiscolsum, lastcolsum, nextcolsum : INT32; +{$endif} + {register} colctr : JDIMENSION; + inrow, outrow, v : int; +var + prev_input_data : JSAMPARRAY; { Nomssi work around } +begin + output_data := output_data_ptr; + + outrow := 0; + inrow := 0; + while (outrow < cinfo^.max_v_samp_factor) do + begin + for v := 0 to pred(2) do + begin + { inptr0 points to nearest input row, inptr1 points to next nearest } + inptr0 := JSAMPLE_PTR(input_data^[inrow]); + if (v = 0) then { next nearest is row above } + begin + {inptr1 := JSAMPLE_PTR(input_data^[inrow-1]);} + prev_input_data := input_data; { work around } + Dec(JSAMPROW_PTR(prev_input_data)); { negative offsets } + inptr1 := JSAMPLE_PTR(prev_input_data^[inrow]); + end + else { next nearest is row below } + inptr1 := JSAMPLE_PTR(input_data^[inrow+1]); + outptr := JSAMPLE_PTR(output_data^[outrow]); + Inc(outrow); + + { Special case for first column } + thiscolsum := GETJSAMPLE(inptr0^) * 3 + GETJSAMPLE(inptr1^); + Inc(inptr0); + Inc(inptr1); + nextcolsum := GETJSAMPLE(inptr0^) * 3 + GETJSAMPLE(inptr1^); + Inc(inptr0); + Inc(inptr1); + + outptr^ := JSAMPLE ((thiscolsum * 4 + 8) shr 4); + Inc(outptr); + outptr^ := JSAMPLE ((thiscolsum * 3 + nextcolsum + 7) shr 4); + Inc(outptr); + lastcolsum := thiscolsum; thiscolsum := nextcolsum; + + for colctr := pred(compptr^.downsampled_width - 2) downto 0 do + begin + { 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^); + Inc(inptr0); + Inc(inptr1); + outptr^ := JSAMPLE ((thiscolsum * 3 + lastcolsum + 8) shr 4); + Inc(outptr); + outptr^ := JSAMPLE ((thiscolsum * 3 + nextcolsum + 7) shr 4); + Inc(outptr); + lastcolsum := thiscolsum; + thiscolsum := nextcolsum; + end; + + { Special case for last column } + outptr^ := JSAMPLE ((thiscolsum * 3 + lastcolsum + 8) shr 4); + Inc(outptr); + outptr^ := JSAMPLE ((thiscolsum * 4 + 7) shr 4); + {Inc(outptr); - value never used } + end; + Inc(inrow); + end; +end; + + +{ Module initialization routine for upsampling. } + +{GLOBAL} +procedure jinit_upsampler (cinfo : j_decompress_ptr); +var + upsample : my_upsample_ptr; + ci : int; + compptr : jpeg_component_info_ptr; + need_buffer, do_fancy : boolean; + h_in_group, v_in_group, h_out_group, v_out_group : int; +begin + upsample := my_upsample_ptr ( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + SIZEOF(my_upsampler)) ); + cinfo^.upsample := jpeg_upsampler_ptr (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) then { this isn't supported } + ERREXIT(j_common_ptr(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 and (cinfo^.min_DCT_scaled_size > 1); + + { Verify we can handle the sampling factors, select per-component methods, + and create storage as needed. } + + compptr := jpeg_component_info_ptr(cinfo^.comp_info); + for ci := 0 to pred(cinfo^.num_components) do + begin + { 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) div + cinfo^.min_DCT_scaled_size; + v_in_group := (compptr^.v_samp_factor * compptr^.DCT_scaled_size) div + 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 (not compptr^.component_needed) then + begin + { Don't bother to upsample an uninteresting component. } + upsample^.methods[ci] := noop_upsample; + need_buffer := FALSE; + end + else + if (h_in_group = h_out_group) and (v_in_group = v_out_group) then + begin + { Fullsize components can be processed without any work. } + upsample^.methods[ci] := fullsize_upsample; + need_buffer := FALSE; + end + else + if (h_in_group * 2 = h_out_group) and + (v_in_group = v_out_group) then + begin + { Special cases for 2h1v upsampling } + if (do_fancy) and (compptr^.downsampled_width > 2) then + upsample^.methods[ci] := h2v1_fancy_upsample + else + upsample^.methods[ci] := h2v1_upsample; + end + else + if (h_in_group * 2 = h_out_group) and + (v_in_group * 2 = v_out_group) then + begin + { Special cases for 2h2v upsampling } + if (do_fancy) and (compptr^.downsampled_width > 2) then + begin + upsample^.methods[ci] := h2v2_fancy_upsample; + upsample^.pub.need_context_rows := TRUE; + end + else + upsample^.methods[ci] := h2v2_upsample; + end + else + if ((h_out_group mod h_in_group) = 0) and + ((v_out_group mod v_in_group) = 0) then + begin + { Generic integral-factors upsampling method } + upsample^.methods[ci] := int_upsample; + upsample^.h_expand[ci] := UINT8 (h_out_group div h_in_group); + upsample^.v_expand[ci] := UINT8 (v_out_group div v_in_group); + end + else + ERREXIT(j_common_ptr(cinfo), JERR_FRACT_SAMPLE_NOTIMPL); + if (need_buffer) then + begin + 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)); + end; + Inc(compptr); + end; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjerror.pas b/resources/libraries/deskew/Imaging/JpegLib/imjerror.pas new file mode 100755 index 0000000..d1e4ebc --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjerror.pas @@ -0,0 +1,462 @@ +unit imjerror; + +{ 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. + + These routines are used by both the compression and decompression code. } + +{ Source: jerror.c; Copyright (C) 1991-1996, Thomas G. Lane. } +{ note: format_message still contains a hack } +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjdeferr, + imjpeglib; +{ + jversion; +} + +const + EXIT_FAILURE = 1; { define halt() codes if not provided } + +{GLOBAL} +function jpeg_std_error (var err : jpeg_error_mgr) : jpeg_error_mgr_ptr; + + + +procedure ERREXIT(cinfo : j_common_ptr; code : J_MESSAGE_CODE); + +procedure ERREXIT1(cinfo : j_common_ptr; code : J_MESSAGE_CODE; p1 : uInt); + +procedure ERREXIT2(cinfo : j_common_ptr; code : J_MESSAGE_CODE; p1 : int; p2 : int); + +procedure ERREXIT3(cinfo : j_common_ptr; code : J_MESSAGE_CODE; + p1 : int; p2 : int; p3 : int); + +procedure ERREXIT4(cinfo : j_common_ptr; code : J_MESSAGE_CODE; + p1 : int; p2 : int; p3 : int; p4 : int); + +procedure ERREXITS(cinfo : j_common_ptr;code : J_MESSAGE_CODE; + str : AnsiString); +{ Nonfatal errors (we can keep going, but the data is probably corrupt) } + +procedure WARNMS(cinfo : j_common_ptr; code : J_MESSAGE_CODE); + +procedure WARNMS1(cinfo : j_common_ptr;code : J_MESSAGE_CODE; p1 : int); + +procedure WARNMS2(cinfo : j_common_ptr; code : J_MESSAGE_CODE; + p1 : int; p2 : int); + +{ Informational/debugging messages } +procedure TRACEMS(cinfo : j_common_ptr; lvl : int; code : J_MESSAGE_CODE); + +procedure TRACEMS1(cinfo : j_common_ptr; lvl : int; + code : J_MESSAGE_CODE; p1 : long); + +procedure TRACEMS2(cinfo : j_common_ptr; lvl : int; code : J_MESSAGE_CODE; + p1 : int; + p2 : int); + +procedure TRACEMS3(cinfo : j_common_ptr; + lvl : int; + code : J_MESSAGE_CODE; + p1 : int; p2 : int; p3 : int); + +procedure TRACEMS4(cinfo : j_common_ptr; lvl : int; code : J_MESSAGE_CODE; + p1 : int; p2 : int; p3 : int; p4 : int); + +procedure TRACEMS5(cinfo : j_common_ptr; lvl : int; code : J_MESSAGE_CODE; + p1 : int; p2 : int; p3 : int; p4 : int; p5 : int); + +procedure TRACEMS8(cinfo : j_common_ptr; lvl : int; code : J_MESSAGE_CODE; + p1 : int; p2 : int; p3 : int; p4 : int; + p5 : int; p6 : int; p7 : int; p8 : int); + +procedure TRACEMSS(cinfo : j_common_ptr; lvl : int; + code : J_MESSAGE_CODE; str : AnsiString); + +implementation + + +{ How to format a message string, in format_message() ? } + +{$IFDEF OS2} + {$DEFINE NO_FORMAT} +{$ENDIF} +{$IFDEF FPC} + {$DEFINE NO_FORMAT} +{$ENDIF} + +uses +{$IFNDEF NO_FORMAT} + {$IFDEF VER70} + drivers, { Turbo Vision unit with FormatStr } + {$ELSE} + sysutils, { Delphi Unit with Format() } + {$ENDIF} +{$ENDIF} + imjcomapi; + +{ 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} +procedure error_exit (cinfo : j_common_ptr); +begin + { Always display the message } + cinfo^.err^.output_message(cinfo); + + { Let the memory manager delete any temp files before we die } + jpeg_destroy(cinfo); + + halt(EXIT_FAILURE); +end; + + +{ Actual output of an error or trace message. + Applications may override this method to send JPEG messages somewhere + other than stderr. } + +{ 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) } +procedure ERREXIT(cinfo : j_common_ptr; code : J_MESSAGE_CODE); +begin + cinfo^.err^.msg_code := ord(code); + cinfo^.err^.error_exit(cinfo); +end; + +procedure ERREXIT1(cinfo : j_common_ptr; code : J_MESSAGE_CODE; p1 : uInt); +begin + cinfo^.err^.msg_code := ord(code); + cinfo^.err^.msg_parm.i[0] := p1; + cinfo^.err^.error_exit (cinfo); +end; + +procedure ERREXIT2(cinfo : j_common_ptr; code : J_MESSAGE_CODE; + p1 : int; p2 : int); +begin + cinfo^.err^.msg_code := ord(code); + cinfo^.err^.msg_parm.i[0] := p1; + cinfo^.err^.msg_parm.i[1] := p2; + cinfo^.err^.error_exit (cinfo); +end; + +procedure ERREXIT3(cinfo : j_common_ptr; code : J_MESSAGE_CODE; + p1 : int; p2 : int; p3 : int); +begin + cinfo^.err^.msg_code := ord(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 (cinfo); +end; + +procedure ERREXIT4(cinfo : j_common_ptr; code : J_MESSAGE_CODE; + p1 : int; p2 : int; p3 : int; p4 : int); +begin + cinfo^.err^.msg_code := ord(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 (cinfo); +end; + +procedure ERREXITS(cinfo : j_common_ptr;code : J_MESSAGE_CODE; + str : AnsiString); +begin + cinfo^.err^.msg_code := ord(code); + cinfo^.err^.msg_parm.s := str; { string[JMSG_STR_PARM_MAX] } + cinfo^.err^.error_exit (cinfo); +end; + +{ Nonfatal errors (we can keep going, but the data is probably corrupt) } + +procedure WARNMS(cinfo : j_common_ptr; code : J_MESSAGE_CODE); +begin + cinfo^.err^.msg_code := ord(code); + cinfo^.err^.emit_message(cinfo, -1); +end; + +procedure WARNMS1(cinfo : j_common_ptr;code : J_MESSAGE_CODE; p1 : int); +begin + cinfo^.err^.msg_code := ord(code); + cinfo^.err^.msg_parm.i[0] := p1; + cinfo^.err^.emit_message (cinfo, -1); +end; + +procedure WARNMS2(cinfo : j_common_ptr; code : J_MESSAGE_CODE; + p1 : int; p2 : int); +begin + cinfo^.err^.msg_code := ord(code); + cinfo^.err^.msg_parm.i[0] := p1; + cinfo^.err^.msg_parm.i[1] := p2; + cinfo^.err^.emit_message (cinfo, -1); +end; + +{ Informational/debugging messages } +procedure TRACEMS(cinfo : j_common_ptr; lvl : int; code : J_MESSAGE_CODE); +begin + cinfo^.err^.msg_code := ord(code); + cinfo^.err^.emit_message(cinfo, lvl); +end; + +procedure TRACEMS1(cinfo : j_common_ptr; lvl : int; + code : J_MESSAGE_CODE; p1 : long); +begin + cinfo^.err^.msg_code := ord(code); + cinfo^.err^.msg_parm.i[0] := p1; + cinfo^.err^.emit_message (cinfo, lvl); +end; + +procedure TRACEMS2(cinfo : j_common_ptr; lvl : int; code : J_MESSAGE_CODE; + p1 : int; + p2 : int); +begin + cinfo^.err^.msg_code := ord(code); + cinfo^.err^.msg_parm.i[0] := p1; + cinfo^.err^.msg_parm.i[1] := p2; + cinfo^.err^.emit_message (cinfo, lvl); +end; + +procedure TRACEMS3(cinfo : j_common_ptr; + lvl : int; + code : J_MESSAGE_CODE; + p1 : int; p2 : int; p3 : int); +var + _mp : int8array; +begin + _mp[0] := p1; _mp[1] := p2; _mp[2] := p3; + cinfo^.err^.msg_parm.i := _mp; + cinfo^.err^.msg_code := ord(code); + cinfo^.err^.emit_message (cinfo, lvl); +end; + + +procedure TRACEMS4(cinfo : j_common_ptr; lvl : int; code : J_MESSAGE_CODE; + p1 : int; p2 : int; p3 : int; p4 : int); +var + _mp : int8array; +begin + _mp[0] := p1; _mp[1] := p2; _mp[2] := p3; _mp[3] := p4; + cinfo^.err^.msg_parm.i := _mp; + cinfo^.err^.msg_code := ord(code); + cinfo^.err^.emit_message (cinfo, lvl); +end; + +procedure TRACEMS5(cinfo : j_common_ptr; lvl : int; code : J_MESSAGE_CODE; + p1 : int; p2 : int; p3 : int; p4 : int; p5 : int); +var + _mp : ^int8array; +begin + _mp := @cinfo^.err^.msg_parm.i; + _mp^[0] := p1; _mp^[1] := p2; _mp^[2] := p3; + _mp^[3] := p4; _mp^[5] := p5; + cinfo^.err^.msg_code := ord(code); + cinfo^.err^.emit_message (cinfo, lvl); +end; + +procedure TRACEMS8(cinfo : j_common_ptr; lvl : int; code : J_MESSAGE_CODE; + p1 : int; p2 : int; p3 : int; p4 : int; + p5 : int; p6 : int; p7 : int; p8 : int); +var + _mp : int8array; +begin + _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_parm.i := _mp; + cinfo^.err^.msg_code := ord(code); + cinfo^.err^.emit_message (cinfo, lvl); +end; + +procedure TRACEMSS(cinfo : j_common_ptr; lvl : int; + code : J_MESSAGE_CODE; str : AnsiString); +begin + cinfo^.err^.msg_code := ord(code); + cinfo^.err^.msg_parm.s := str; { string JMSG_STR_PARM_MAX } + cinfo^.err^.emit_message (cinfo, lvl); +end; + +{METHODDEF} +procedure output_message (cinfo : j_common_ptr); +var + buffer : AnsiString; {[JMSG_LENGTH_MAX];} +begin + { Create the message } + cinfo^.err^.format_message (cinfo, buffer); + + { Send it to stderr, adding a newline } + WriteLn(output, buffer); +end; + + + +{ 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} +procedure emit_message (cinfo : j_common_ptr; msg_level : int); +var + err : jpeg_error_mgr_ptr; +begin + err := cinfo^.err; + if (msg_level < 0) then + begin + { 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) or (err^.trace_level >= 3) then + err^.output_message(cinfo); + { Always count warnings in num_warnings. } + Inc( err^.num_warnings ); + end + else + begin + { It's a trace message. Show it if trace_level >= msg_level. } + if (err^.trace_level >= msg_level) then + err^.output_message (cinfo); + end; +end; + + +{ 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} +procedure format_message (cinfo : j_common_ptr; var buffer : AnsiString); +var + err : jpeg_error_mgr_ptr; + msg_code : J_MESSAGE_CODE; + msgtext : AnsiString; + isstring : boolean; +begin + err := cinfo^.err; + msg_code := J_MESSAGE_CODE(err^.msg_code); + msgtext := ''; + + { Look up message string in proper table } + if (msg_code > JMSG_NOMESSAGE) + and (msg_code <= J_MESSAGE_CODE(err^.last_jpeg_message)) then + begin + msgtext := err^.jpeg_message_table^[msg_code]; + end + else + if (err^.addon_message_table <> NIL) and + (msg_code >= err^.first_addon_message) and + (msg_code <= err^.last_addon_message) then + begin + msgtext := err^.addon_message_table^[J_MESSAGE_CODE + (ord(msg_code) - ord(err^.first_addon_message))]; + end; + + { Defend against bogus message number } + if (msgtext = '') then + begin + err^.msg_parm.i[0] := int(msg_code); + msgtext := err^.jpeg_message_table^[JMSG_NOMESSAGE]; + end; + + { Check for string parameter, as indicated by %s in the message text } + isstring := Pos('%s', msgtext) > 0; + + { Format the message into the passed buffer } + if (isstring) then + buffer := Concat(msgtext, err^.msg_parm.s) + else + begin + {$IFDEF VER70} + FormatStr(buffer, msgtext, err^.msg_parm.i); + {$ELSE} + {$IFDEF NO_FORMAT} + buffer := msgtext; + {$ELSE} + buffer := Format(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] ]); + {$ENDIF} + {$ENDIF} + end; +end; + + + +{ 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} +procedure reset_error_mgr (cinfo : j_common_ptr); +begin + 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" } +end; + + +{ Fill in the standard error-handling methods in a jpeg_error_mgr object. + Typical call is: + cinfo : jpeg_compress_struct; + err : jpeg_error_mgr; + + cinfo.err := jpeg_std_error(@err); + after which the application may override some of the methods. } + + +{GLOBAL} +function jpeg_std_error (var err : jpeg_error_mgr) : jpeg_error_mgr_ptr; +begin + 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 := pred(JMSG_LASTMSGCODE); + + err.addon_message_table := NIL; + err.first_addon_message := JMSG_NOMESSAGE; { for safety } + err.last_addon_message := JMSG_NOMESSAGE; + + jpeg_std_error := @err; +end; + + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjfdctflt.pas b/resources/libraries/deskew/Imaging/JpegLib/imjfdctflt.pas new file mode 100755 index 0000000..4d8596e --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjfdctflt.pas @@ -0,0 +1,175 @@ +unit imjfdctflt; + +{ 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. } + +{ Original : jfdctflt.c ; Copyright (C) 1994-1996, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjpeglib, + imjdct; { Private declarations for DCT subsystem } + + +{ Perform the forward DCT on one block of samples.} + +{GLOBAL} +procedure jpeg_fdct_float (var data : array of FAST_FLOAT); + +implementation + +{ This module is specialized to the case DCTSIZE = 8. } + +{$ifndef DCTSIZE_IS_8} + Sorry, this code only copes with 8x8 DCTs. { deliberate syntax err } +{$endif} + + +{ Perform the forward DCT on one block of samples.} + +{GLOBAL} +procedure jpeg_fdct_float (var data : array of FAST_FLOAT); +type + PWorkspace = ^TWorkspace; + TWorkspace = array [0..DCTSIZE2-1] of FAST_FLOAT; +var + 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 : PWorkspace; + ctr : int; +begin + { Pass 1: process rows. } + + dataptr := PWorkspace(@data); + for ctr := DCTSIZE-1 downto 0 do + begin + 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; + + Inc(FAST_FLOAT_PTR(dataptr), DCTSIZE); { advance pointer to next row } + end; + + { Pass 2: process columns. } + + dataptr := PWorkspace(@data); + for ctr := DCTSIZE-1 downto 0 do + begin + 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; + + Inc(FAST_FLOAT_PTR(dataptr)); { advance pointer to next column } + end; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjfdctfst.pas b/resources/libraries/deskew/Imaging/JpegLib/imjfdctfst.pas new file mode 100755 index 0000000..51e4bc6 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjfdctfst.pas @@ -0,0 +1,237 @@ +unit imjfdctfst; + +{ 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. } + +{ Original: jfdctfst.c ; Copyright (C) 1994-1996, Thomas G. Lane. } + + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjpeglib, + imjdct; { Private declarations for DCT subsystem } + + +{ Perform the forward DCT on one block of samples. } + +{GLOBAL} +procedure jpeg_fdct_ifast (var data : array of DCTELEM); + +implementation + +{ This module is specialized to the case DCTSIZE = 8. } + +{$ifndef DCTSIZE_IS_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). } + +const + CONST_BITS = 8; +const + CONST_SCALE = (INT32(1) shl CONST_BITS); + + +const + FIX_0_382683433 = INT32(Round(CONST_SCALE * 0.382683433)); {98} + FIX_0_541196100 = INT32(Round(CONST_SCALE * 0.541196100)); {139} + FIX_0_707106781 = INT32(Round(CONST_SCALE * 0.707106781)); {181} + FIX_1_306562965 = INT32(Round(CONST_SCALE * 1.306562965)); {334} + +{ 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. } + +function DESCALE(x : INT32; n : int) : INT32; +var + shift_temp : INT32; +begin +{ 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} + shift_temp := x; +{$else} + shift_temp := x + (INT32(1) shl (n-1)); +{$endif} + +{$ifdef RIGHT_SHIFT_IS_UNSIGNED} + if shift_temp < 0 then + Descale := (shift_temp shr n) or ((not INT32(0)) shl (32-n)) + else +{$endif} + Descale := (shift_temp shr n); +end; + +{ Multiply a DCTELEM variable by an INT32 constant, and immediately + descale to yield a DCTELEM result. } + + + function MULTIPLY(X : DCTELEM; Y: INT32): DCTELEM; + begin + Multiply := DeScale((X) * (Y), CONST_BITS); + end; + + +{ Perform the forward DCT on one block of samples. } + +{GLOBAL} +procedure jpeg_fdct_ifast (var data : array of DCTELEM); +type + PWorkspace = ^TWorkspace; + TWorkspace = array [0..DCTSIZE2-1] of DCTELEM; +var + tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7 : DCTELEM; + tmp10, tmp11, tmp12, tmp13 : DCTELEM; + z1, z2, z3, z4, z5, z11, z13 : DCTELEM; + dataptr : PWorkspace; + ctr : int; + {SHIFT_TEMPS} +begin + { Pass 1: process rows. } + + dataptr := PWorkspace(@data); + for ctr := DCTSIZE-1 downto 0 do + begin + 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; + + Inc(DCTELEMPTR(dataptr), DCTSIZE); { advance pointer to next row } + end; + + { Pass 2: process columns. } + + dataptr := PWorkspace(@data); + for ctr := DCTSIZE-1 downto 0 do + begin + 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; + + Inc(DCTELEMPTR(dataptr)); { advance pointer to next column } + end; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjfdctint.pas b/resources/libraries/deskew/Imaging/JpegLib/imjfdctint.pas new file mode 100755 index 0000000..bc94638 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjfdctint.pas @@ -0,0 +1,297 @@ +unit imjfdctint; + + +{ 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. } + +{ Original : jfdctint.c ; Copyright (C) 1991-1996, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjutils, + imjpeglib, + imjdct; { Private declarations for DCT subsystem } + + +{ Perform the forward DCT on one block of samples. } + +{GLOBAL} +procedure jpeg_fdct_islow (var data : array of DCTELEM); + +implementation + +{ This module is specialized to the case DCTSIZE = 8. } + +{$ifndef DCTSIZE_IS_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. } + +{$ifdef BITS_IN_JSAMPLE_IS_8} +const + CONST_BITS = 13; + PASS1_BITS = 2; +{$else} +const + CONST_BITS = 13; + PASS1_BITS = 1; { lose a little precision to avoid overflow } +{$endif} + +const + CONST_SCALE = (INT32(1) shl CONST_BITS); + +const + FIX_0_298631336 = INT32(Round(CONST_SCALE * 0.298631336)); {2446} + FIX_0_390180644 = INT32(Round(CONST_SCALE * 0.390180644)); {3196} + FIX_0_541196100 = INT32(Round(CONST_SCALE * 0.541196100)); {4433} + FIX_0_765366865 = INT32(Round(CONST_SCALE * 0.765366865)); {6270} + FIX_0_899976223 = INT32(Round(CONST_SCALE * 0.899976223)); {7373} + FIX_1_175875602 = INT32(Round(CONST_SCALE * 1.175875602)); {9633} + FIX_1_501321110 = INT32(Round(CONST_SCALE * 1.501321110)); {12299} + FIX_1_847759065 = INT32(Round(CONST_SCALE * 1.847759065)); {15137} + FIX_1_961570560 = INT32(Round(CONST_SCALE * 1.961570560)); {16069} + FIX_2_053119869 = INT32(Round(CONST_SCALE * 2.053119869)); {16819} + FIX_2_562915447 = INT32(Round(CONST_SCALE * 2.562915447)); {20995} + FIX_3_072711026 = INT32(Round(CONST_SCALE * 3.072711026)); {25172} + + +{ 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. } + +{$ifdef BITS_IN_JSAMPLE_IS_8} + + {MULTIPLY16C16(var,const)} + function Multiply(X, Y: int): INT32; + begin + Multiply := int(X) * INT32(Y); + end; + +{$else} + function Multiply(X, Y: INT32): INT32; + begin + Multiply := X * Y; + end; +{$endif} + +{ 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. } + +function DESCALE(x : INT32; n : int) : INT32; +var + shift_temp : INT32; +begin +{$ifdef RIGHT_SHIFT_IS_UNSIGNED} + shift_temp := x + (INT32(1) shl (n-1)); + if shift_temp < 0 then + Descale := (shift_temp shr n) or ((not INT32(0)) shl (32-n)) + else + Descale := (shift_temp shr n); +{$else} + Descale := (x + (INT32(1) shl (n-1)) shr n; +{$endif} +end; + + +{ Perform the forward DCT on one block of samples. } + +{GLOBAL} +procedure jpeg_fdct_islow (var data : array of DCTELEM); +type + PWorkspace = ^TWorkspace; + TWorkspace = array [0..DCTSIZE2-1] of DCTELEM; +var + tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7 : INT32; + tmp10, tmp11, tmp12, tmp13 : INT32; + z1, z2, z3, z4, z5 : INT32; + dataptr : PWorkspace; + ctr : int; + {SHIFT_TEMPS} +begin + + { 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 := PWorkspace(@data); + for ctr := DCTSIZE-1 downto 0 do + begin + 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) shl PASS1_BITS); + dataptr^[4] := DCTELEM ((tmp10 - tmp11) shl 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) } + + Inc(z3, z5); + Inc(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)); + + Inc(DCTELEMPTR(dataptr), DCTSIZE); { advance pointer to next row } + end; + + { Pass 2: process columns. + We remove the PASS1_BITS scaling, but leave the results scaled up + by an overall factor of 8. } + + dataptr := PWorkspace(@data); + for ctr := DCTSIZE-1 downto 0 do + begin + 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) } + + Inc(z3, z5); + Inc(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)); + + Inc(DCTELEMPTR(dataptr)); { advance pointer to next column } + end; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjidctasm.pas b/resources/libraries/deskew/Imaging/JpegLib/imjidctasm.pas new file mode 100755 index 0000000..3cf4e70 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjidctasm.pas @@ -0,0 +1,793 @@ +unit imjidctasm; + +{ 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. } + +{ Original : jidctint.c ; Copyright (C) 1991-1996, Thomas G. Lane. } +{ ;------------------------------------------------------------------------- + ; JIDCTINT.ASM + ; 80386 protected mode assembly translation of JIDCTINT.C + ; **** Optimized to all hell by Jason M. Felice (jasonf@apk.net) **** + ; **** E-mail welcome **** + ; + ; ** This code does not make O/S calls -- use it for OS/2, Win95, WinNT, + ; ** DOS prot. mode., Linux, whatever... have fun. + ; + ; ** Note, this code is dependant on the structure member order in the .h + ; ** files for the following structures: + ; -- amazingly NOT j_decompress_struct... cool. + ; -- jpeg_component_info (dependant on position of dct_table element) + ; + ; Originally created with the /Fa option of MSVC 4.0 (why work when you + ; don't have to?) + ; + ; (this code, when compiled is 1K bytes smaller than the optimized MSVC + ; release build, not to mention 120-130 ms faster in my profile test with 1 + ; small color and and 1 medium black-and-white jpeg: stats using TASM 4.0 + ; and MSVC 4.0 to create a non-console app; jpeg_idct_islow accumulated + ; 5,760 hits on all trials) + ; + ; TASM -t -ml -os jidctint.asm, jidctint.obj + ;------------------------------------------------------------------------- + Converted to Delphi 2.0 BASM for PasJPEG + by Jacques NOMSSI NZALI <nomssi@physik.tu-chemnitz.de> + October 13th 1996 + * assumes Delphi "register" calling convention + first 3 parameter are in EAX,EDX,ECX + * register allocation revised +} + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjpeglib, + imjdct; { Private declarations for DCT subsystem } + +{ Perform dequantization and inverse DCT on one block of coefficients. } + +{GLOBAL} +procedure jpeg_idct_islow (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + coef_block : JCOEFPTR; + output_buf : JSAMPARRAY; + output_col : JDIMENSION); + +implementation + +{ This module is specialized to the case DCTSIZE = 8. } + +{$ifndef DCTSIZE_IS_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. } + +const + CONST_BITS = 13; + +{$ifdef BITS_IN_JSAMPLE_IS_8} +const + PASS1_BITS = 2; +{$else} +const + PASS1_BITS = 1; { lose a little precision to avoid overflow } +{$endif} + +const + CONST_SCALE = (INT32(1) shl CONST_BITS); + +const + FIX_0_298631336 = INT32(Round(CONST_SCALE * 0.298631336)); {2446} + FIX_0_390180644 = INT32(Round(CONST_SCALE * 0.390180644)); {3196} + FIX_0_541196100 = INT32(Round(CONST_SCALE * 0.541196100)); {4433} + FIX_0_765366865 = INT32(Round(CONST_SCALE * 0.765366865)); {6270} + FIX_0_899976223 = INT32(Round(CONST_SCALE * 0.899976223)); {7373} + FIX_1_175875602 = INT32(Round(CONST_SCALE * 1.175875602)); {9633} + FIX_1_501321110 = INT32(Round(CONST_SCALE * 1.501321110)); {12299} + FIX_1_847759065 = INT32(Round(CONST_SCALE * 1.847759065)); {15137} + FIX_1_961570560 = INT32(Round(CONST_SCALE * 1.961570560)); {16069} + FIX_2_053119869 = INT32(Round(CONST_SCALE * 2.053119869)); {16819} + FIX_2_562915447 = INT32(Round(CONST_SCALE * 2.562915447)); {20995} + FIX_3_072711026 = INT32(Round(CONST_SCALE * 3.072711026)); {25172} + + +{ for DESCALE } +const + ROUND_CONST = (INT32(1) shl (CONST_BITS-PASS1_BITS-1)); +const + ROUND_CONST_2 = (INT32(1) shl (CONST_BITS+PASS1_BITS+3-1)); + +{ Perform dequantization and inverse DCT on one block of coefficients. } + +{GLOBAL} +procedure jpeg_idct_islow (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + coef_block : JCOEFPTR; + output_buf : JSAMPARRAY; + output_col : JDIMENSION); +type + PWorkspace = ^TWorkspace; + TWorkspace = coef_bits_field; { buffers data between passes } +const + coefDCTSIZE = DCTSIZE*SizeOf(JCOEF); + wrkDCTSIZE = DCTSIZE*SizeOf(int); +var + tmp0, tmp1, tmp2, tmp3 : INT32; + tmp10, tmp11, tmp12, tmp13 : INT32; + z1, z2, z3, z4, z5 : INT32; +var + inptr : JCOEFPTR; + quantptr : ISLOW_MULT_TYPE_FIELD_PTR; + wsptr : PWorkspace; + outptr : JSAMPROW; +var + range_limit : JSAMPROW; + ctr : int; + workspace : TWorkspace; +var + dcval : int; +var + dcval_ : JSAMPLE; +asm + push edi + push esi + push ebx + + cld { The only direction we use, might as well set it now, as opposed } + { to inside 2 loops. } + +{ 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. } + + {range_limit := JSAMPROW(@(cinfo^.sample_range_limit^[CENTERJSAMPLE]));} + mov eax, [eax].jpeg_decompress_struct.sample_range_limit {eax=cinfo} + add eax, (MAXJSAMPLE+1 + CENTERJSAMPLE)*(Type JSAMPLE) + mov range_limit, eax + + { 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;} + mov esi, ecx { ecx=coef_block } + {quantptr := ISLOW_MULT_TYPE_FIELD_PTR (compptr^.dct_table);} + mov edi, [edx].jpeg_component_info.dct_table { edx=compptr } + + {wsptr := PWorkspace(@workspace);} + lea ecx, workspace + + {for ctr := pred(DCTSIZE) downto 0 do + begin} + mov ctr, DCTSIZE +@loop518: + { 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]) or (inptr^[DCTSIZE*2]) or (inptr^[DCTSIZE*3]) or + (inptr^[DCTSIZE*4]) or (inptr^[DCTSIZE*5]) or (inptr^[DCTSIZE*6]) or + (inptr^[DCTSIZE*7]) = 0) then + begin} + mov eax, DWORD PTR [esi+coefDCTSIZE*1] + or eax, DWORD PTR [esi+coefDCTSIZE*2] + or eax, DWORD PTR [esi+coefDCTSIZE*3] + mov edx, DWORD PTR [esi+coefDCTSIZE*4] + or eax, edx + or eax, DWORD PTR [esi+coefDCTSIZE*5] + or eax, DWORD PTR [esi+coefDCTSIZE*6] + or eax, DWORD PTR [esi+coefDCTSIZE*7] + jne @loop520 + + { AC terms all zero } + {dcval := ISLOW_MULT_TYPE(inptr^[DCTSIZE*0]) * + (quantptr^[DCTSIZE*0]) shl PASS1_BITS;} + mov eax, DWORD PTR [esi+coefDCTSIZE*0] + imul eax, DWORD PTR [edi+wrkDCTSIZE*0] + shl eax, 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;} + + mov DWORD PTR [ecx+ wrkDCTSIZE*0], eax + mov DWORD PTR [ecx+ wrkDCTSIZE*1], eax + mov DWORD PTR [ecx+ wrkDCTSIZE*2], eax + mov DWORD PTR [ecx+ wrkDCTSIZE*3], eax + mov DWORD PTR [ecx+ wrkDCTSIZE*4], eax + mov DWORD PTR [ecx+ wrkDCTSIZE*5], eax + mov DWORD PTR [ecx+ wrkDCTSIZE*6], eax + mov DWORD PTR [ecx+ wrkDCTSIZE*7], eax + + {Inc(JCOEF_PTR(inptr)); { advance pointers to next column } + {Inc(ISLOW_MULT_TYPE_PTR(quantptr)); + Inc(int_ptr(wsptr)); + continue;} + dec ctr + je @loop519 + + add esi, Type JCOEF + add edi, Type ISLOW_MULT_TYPE + add ecx, Type int { int_ptr } + jmp @loop518 + +@loop520: + + {end;} + + { Even part: reverse the even part of the forward DCT. } + { The rotator is sqrt(2)*c(-6). } + + {z2 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*2]) * quantptr^[DCTSIZE*2]; + z3 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*6]) * quantptr^[DCTSIZE*6]; + + z1 := (z2 + z3) * INT32(FIX_0_541196100); + tmp2 := z1 + INT32(z3) * INT32(- FIX_1_847759065); + tmp3 := z1 + INT32(z2) * INT32(FIX_0_765366865);} + + mov edx, DWORD PTR [esi+coefDCTSIZE*2] + imul edx, DWORD PTR [edi+wrkDCTSIZE*2] {z2} + + mov eax, DWORD PTR [esi+coefDCTSIZE*6] + imul eax, DWORD PTR [edi+wrkDCTSIZE*6] {z3} + + lea ebx, [eax+edx] + imul ebx, FIX_0_541196100 {z1} + + imul eax, (-FIX_1_847759065) + add eax, ebx + mov tmp2, eax + + imul edx, FIX_0_765366865 + add edx, ebx + mov tmp3, edx + + {z2 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*0]) * quantptr^[DCTSIZE*0]; + z3 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*4]) * quantptr^[DCTSIZE*4];} + + mov edx, DWORD PTR [esi+coefDCTSIZE*4] + imul edx, DWORD PTR [edi+wrkDCTSIZE*4] { z3 = edx } + + mov eax, DWORD PTR [esi+coefDCTSIZE*0] + imul eax, DWORD PTR [edi+wrkDCTSIZE*0] { z2 = eax } + + {tmp0 := (z2 + z3) shl CONST_BITS; + tmp1 := (z2 - z3) shl CONST_BITS;} + lea ebx,[eax+edx] + sub eax, edx + shl ebx, CONST_BITS { tmp0 = ebx } + shl eax, CONST_BITS { tmp1 = eax } + + {tmp10 := tmp0 + tmp3; + tmp13 := tmp0 - tmp3;} + mov edx, tmp3 + sub ebx, edx + mov tmp13, ebx + add edx, edx + add ebx, edx + mov tmp10, ebx + + {tmp11 := tmp1 + tmp2; + tmp12 := tmp1 - tmp2;} + mov ebx, tmp2 + sub eax, ebx + mov tmp12, eax + add ebx, ebx + add eax, ebx + mov tmp11, eax + + { 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 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*7]) * quantptr^[DCTSIZE*7];} + mov eax, DWORD PTR [esi+coefDCTSIZE*7] + imul eax, DWORD PTR [edi+wrkDCTSIZE*7] + mov edx, eax { edx = tmp0 } + {tmp0 := (tmp0) * INT32(FIX_0_298631336); { sqrt(2) * (-c1+c3+c5-c7) } + imul eax, FIX_0_298631336 + mov tmp0, eax + + {tmp3 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*1]) * quantptr^[DCTSIZE*1];} + mov eax, DWORD PTR [esi+coefDCTSIZE*1] + imul eax, DWORD PTR [edi+wrkDCTSIZE*1] + mov tmp3, eax + + {z1 := tmp0 + tmp3;} + {z1 := (z1) * INT32(- FIX_0_899976223); { sqrt(2) * (c7-c3) } + add eax, edx + imul eax, (-FIX_0_899976223) + mov z1, eax + + {tmp1 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*5]) * quantptr^[DCTSIZE*5];} + mov eax, DWORD PTR [esi+coefDCTSIZE*5] + imul eax, DWORD PTR [edi+wrkDCTSIZE*5] + mov ebx, eax { ebx = tmp1 } + {tmp1 := (tmp1) * INT32(FIX_2_053119869); { sqrt(2) * ( c1+c3-c5+c7) } + imul eax, FIX_2_053119869 + mov tmp1, eax + + {tmp2 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*3]) * quantptr^[DCTSIZE*3];} + mov eax, DWORD PTR [esi+coefDCTSIZE*3] + imul eax, DWORD PTR [edi+wrkDCTSIZE*3] + mov tmp2, eax + + {z3 := tmp0 + tmp2;} + add edx, eax { edx = z3 } + + {z2 := tmp1 + tmp2;} + {z2 := (z2) * INT32(- FIX_2_562915447); { sqrt(2) * (-c1-c3) } + add eax, ebx + imul eax, (-FIX_2_562915447) + mov z2, eax + + {z4 := tmp1 + tmp3;} + add ebx, tmp3 { ebx = z4 } + + {z5 := INT32(z3 + z4) * INT32(FIX_1_175875602); { sqrt(2) * c3 } + lea eax, [edx+ebx] + imul eax, FIX_1_175875602 { eax = z5 } + + {z4 := (z4) * INT32(- FIX_0_390180644); { sqrt(2) * (c5-c3) } + {Inc(z4, z5);} + imul ebx, (-FIX_0_390180644) + add ebx, eax + mov z4, ebx + + {z3 := (z3) * INT32(- FIX_1_961570560); { sqrt(2) * (-c3-c5) } + {Inc(z3, z5);} + imul edx, (-FIX_1_961570560) + add eax, edx { z3 = eax } + + {Inc(tmp0, z1 + z3);} + mov ebx, z1 + add ebx, eax + add tmp0, ebx + + {tmp2 := (tmp2) * INT32(FIX_3_072711026); { sqrt(2) * ( c1+c3+c5-c7) } + {Inc(tmp2, z2 + z3);} + mov ebx, tmp2 + imul ebx, FIX_3_072711026 + mov edx, z2 { z2 = edx } + add ebx, edx + add eax, ebx + mov tmp2, eax + + {Inc(tmp1, z2 + z4);} + mov eax, z4 { z4 = eax } + add edx, eax + add tmp1, edx + + {tmp3 := (tmp3) * INT32(FIX_1_501321110); { sqrt(2) * ( c1+c3-c5-c7) } + {Inc(tmp3, z1 + z4);} + mov edx, tmp3 + imul edx, FIX_1_501321110 + + add edx, eax + add edx, z1 { tmp3 = edx } + + { 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));} + mov eax, tmp10 + add eax, ROUND_CONST + lea ebx, [eax+edx] + sar ebx, CONST_BITS-PASS1_BITS + mov DWORD PTR [ecx+wrkDCTSIZE*0], ebx + + sub eax, edx + sar eax, CONST_BITS-PASS1_BITS + mov DWORD PTR [ecx+wrkDCTSIZE*7], eax + + {wsptr^[DCTSIZE*1] := int (DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS));} + {wsptr^[DCTSIZE*6] := int (DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS));} + mov eax, tmp11 + add eax, ROUND_CONST + mov edx, tmp2 + lea ebx, [eax+edx] + sar ebx, CONST_BITS-PASS1_BITS + mov DWORD PTR [ecx+wrkDCTSIZE*1], ebx + + sub eax, edx + sar eax, CONST_BITS-PASS1_BITS + mov DWORD PTR [ecx+wrkDCTSIZE*6], eax + + {wsptr^[DCTSIZE*2] := int (DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS));} + {wsptr^[DCTSIZE*5] := int (DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS));} + mov eax, tmp12 + add eax, ROUND_CONST + mov edx, tmp1 + lea ebx, [eax+edx] + sar ebx, CONST_BITS-PASS1_BITS + mov DWORD PTR [ecx+wrkDCTSIZE*2], ebx + + sub eax, edx + sar eax, CONST_BITS-PASS1_BITS + mov DWORD PTR [ecx+wrkDCTSIZE*5], eax + + {wsptr^[DCTSIZE*3] := int (DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS));} + {wsptr^[DCTSIZE*4] := int (DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS));} + mov eax, tmp13 + add eax, ROUND_CONST + mov edx, tmp0 + lea ebx, [eax+edx] + sar ebx, CONST_BITS-PASS1_BITS + mov DWORD PTR [ecx+wrkDCTSIZE*3], ebx + + sub eax, edx + sar eax, CONST_BITS-PASS1_BITS + mov DWORD PTR [ecx+wrkDCTSIZE*4], eax + + {Inc(JCOEF_PTR(inptr)); { advance pointers to next column } + {Inc(ISLOW_MULT_TYPE_PTR(quantptr)); + Inc(int_ptr(wsptr));} + dec ctr + je @loop519 + + add esi, Type JCOEF + add edi, Type ISLOW_MULT_TYPE + add ecx, Type int { int_ptr } + {end;} + jmp @loop518 +@loop519: + { Save to memory what we've registerized for the preceding loop. } + + { 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;} + lea esi, workspace + + {for ctr := 0 to pred(DCTSIZE) do + begin} + mov ctr, 0 +@loop523: + + {outptr := output_buf^[ctr];} + mov eax, ctr + mov ebx, output_buf + mov edi, DWORD PTR [ebx+eax*4] { 4 = SizeOf(pointer) } + + {Inc(JSAMPLE_PTR(outptr), output_col);} + add edi, LongWord(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]) or (wsptr^[2]) or (wsptr^[3]) or (wsptr^[4]) or + (wsptr^[5]) or (wsptr^[6]) or (wsptr^[7]) = 0) then + begin} + mov eax, DWORD PTR [esi+4*1] + or eax, DWORD PTR [esi+4*2] + or eax, DWORD PTR [esi+4*3] + jne @loop525 { Nomssi: early exit path may help } + or eax, DWORD PTR [esi+4*4] + or eax, DWORD PTR [esi+4*5] + or eax, DWORD PTR [esi+4*6] + or eax, DWORD PTR [esi+4*7] + jne @loop525 + + { AC terms all zero } + {JSAMPLE(dcval_) := range_limit^[int(DESCALE(INT32(wsptr^[0]), + PASS1_BITS+3)) and RANGE_MASK];} + mov eax, DWORD PTR [esi+4*0] + add eax, (INT32(1) shl (PASS1_BITS+3-1)) + sar eax, PASS1_BITS+3 + and eax, RANGE_MASK + mov ebx, range_limit + mov al, BYTE PTR [ebx+eax] + mov ah, al + + {outptr^[0] := dcval_; + outptr^[1] := dcval_; + outptr^[2] := dcval_; + outptr^[3] := dcval_; + outptr^[4] := dcval_; + outptr^[5] := dcval_; + outptr^[6] := dcval_; + outptr^[7] := dcval_;} + + stosw + stosw + stosw + stosw + + {Inc(int_ptr(wsptr), DCTSIZE); { advance pointer to next row } + {continue;} + add esi, wrkDCTSIZE + inc ctr + cmp ctr, DCTSIZE + jl @loop523 + jmp @loop524 + {end;} +@loop525: +{$endif} + + + { Even part: reverse the even part of the forward DCT. } + { The rotator is sqrt(2)*c(-6). } + + {z2 := INT32 (wsptr^[2]);} + mov edx, DWORD PTR [esi+4*2] { z2 = edx } + + {z3 := INT32 (wsptr^[6]);} + mov ecx, DWORD PTR [esi+4*6] { z3 = ecx } + + {z1 := (z2 + z3) * INT32(FIX_0_541196100);} + lea eax, [edx+ecx] + imul eax, FIX_0_541196100 + mov ebx, eax { z1 = ebx } + + {tmp2 := z1 + (z3) * INT32(- FIX_1_847759065);} + imul ecx, (-FIX_1_847759065) + add ecx, ebx { tmp2 = ecx } + + {tmp3 := z1 + (z2) * INT32(FIX_0_765366865);} + imul edx, FIX_0_765366865 + add ebx, edx { tmp3 = ebx } + + {tmp0 := (INT32(wsptr^[0]) + INT32(wsptr^[4])) shl CONST_BITS;} + {tmp1 := (INT32(wsptr^[0]) - INT32(wsptr^[4])) shl CONST_BITS;} + mov edx, DWORD PTR [esi+4*4] + mov eax, DWORD PTR [esi+4*0] + sub eax, edx + add edx, edx + add edx, eax + shl edx, CONST_BITS { tmp0 = edx } + shl eax, CONST_BITS { tmp1 = eax } + + {tmp10 := tmp0 + tmp3;} + {tmp13 := tmp0 - tmp3;} + sub edx, ebx + mov tmp13, edx + add ebx, ebx + add edx, ebx + mov tmp10, edx + + {tmp11 := tmp1 + tmp2;} + {tmp12 := tmp1 - tmp2;} + lea ebx, [ecx+eax] + mov tmp11, ebx + sub eax, ecx + mov tmp12, eax + + { Odd part per figure 8; the matrix is unitary and hence its + transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. } + +{ The following lines no longer produce code, since wsptr has been + optimized to esi, it is more efficient to access these values + directly. + tmp0 := INT32(wsptr^[7]); + tmp1 := INT32(wsptr^[5]); + tmp2 := INT32(wsptr^[3]); + tmp3 := INT32(wsptr^[1]); } + + {z2 := tmp1 + tmp2;} + {z2 := (z2) * INT32(- FIX_2_562915447); { sqrt(2) * (-c1-c3) } + mov ebx, DWORD PTR [esi+4*3] { tmp2 } + mov ecx, DWORD PTR [esi+4*5] { tmp1 } + lea eax, [ebx+ecx] + imul eax, (-FIX_2_562915447) + mov z2, eax + + {z3 := tmp0 + tmp2;} + mov edx, DWORD PTR [esi+4*7] { tmp0 } + add ebx, edx { old z3 = ebx } + mov eax, ebx + {z3 := (z3) * INT32(- FIX_1_961570560); { sqrt(2) * (-c3-c5) } + imul eax, (-FIX_1_961570560) + mov z3, eax + + {z1 := tmp0 + tmp3;} + {z1 := (z1) * INT32(- FIX_0_899976223); { sqrt(2) * (c7-c3) } + mov eax, DWORD PTR [esi+4*1] { tmp3 } + add edx, eax + imul edx, (-FIX_0_899976223) { z1 = edx } + + {z4 := tmp1 + tmp3;} + add eax, ecx { +tmp1 } + add ebx, eax { z3 + z4 = ebx } + {z4 := (z4) * INT32(- FIX_0_390180644); { sqrt(2) * (c5-c3) } + imul eax, (-FIX_0_390180644) { z4 = eax } + + {z5 := (z3 + z4) * INT32(FIX_1_175875602); { sqrt(2) * c3 } + {Inc(z3, z5);} + imul ebx, FIX_1_175875602 + mov ecx, z3 + add ecx, ebx { ecx = z3 } + + {Inc(z4, z5);} + add ebx, eax { z4 = ebx } + + {tmp0 := (tmp0) * INT32(FIX_0_298631336); { sqrt(2) * (-c1+c3+c5-c7) } + {Inc(tmp0, z1 + z3);} + mov eax, DWORD PTR [esi+4*7] + imul eax, FIX_0_298631336 + add eax, edx + add eax, ecx + mov tmp0, eax + + {tmp1 := (tmp1) * INT32(FIX_2_053119869); { sqrt(2) * ( c1+c3-c5+c7) } + {Inc(tmp1, z2 + z4);} + mov eax, DWORD PTR [esi+4*5] + imul eax, FIX_2_053119869 + add eax, z2 + add eax, ebx + mov tmp1, eax + + {tmp2 := (tmp2) * INT32(FIX_3_072711026); { sqrt(2) * ( c1+c3+c5-c7) } + {Inc(tmp2, z2 + z3);} + mov eax, DWORD PTR [esi+4*3] + imul eax, FIX_3_072711026 + add eax, z2 + add ecx, eax { ecx = tmp2 } + + {tmp3 := (tmp3) * INT32(FIX_1_501321110); { sqrt(2) * ( c1+c3-c5-c7) } + {Inc(tmp3, z1 + z4);} + mov eax, DWORD PTR [esi+4*1] + imul eax, FIX_1_501321110 + add eax, edx + add ebx, eax { ebx = tmp3 } + + { Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 } + + {outptr^[0] := range_limit^[ int(DESCALE(tmp10 + tmp3, + CONST_BITS+PASS1_BITS+3)) and RANGE_MASK]; } + {outptr^[7] := range_limit^[ int(DESCALE(tmp10 - tmp3, + CONST_BITS+PASS1_BITS+3)) and RANGE_MASK];} + + mov edx, tmp10 + add edx, ROUND_CONST_2 + lea eax, [ebx+edx] + sub edx, ebx + + shr eax, CONST_BITS+PASS1_BITS+3 + and eax, RANGE_MASK + mov ebx, range_limit { once for all } + mov al, BYTE PTR [ebx+eax] + mov [edi+0], al + + shr edx, CONST_BITS+PASS1_BITS+3 + and edx, RANGE_MASK + mov al, BYTE PTR [ebx+edx] + mov [edi+7], al + + {outptr^[1] := range_limit^[ int(DESCALE(tmp11 + tmp2, + CONST_BITS+PASS1_BITS+3)) and RANGE_MASK];} + mov eax, tmp11 + add eax, ROUND_CONST_2 + lea edx, [eax+ecx] + shr edx, CONST_BITS+PASS1_BITS+3 + and edx, RANGE_MASK + mov dl, BYTE PTR [ebx+edx] + mov [edi+1], dl + + {outptr^[6] := range_limit^[ int(DESCALE(tmp11 - tmp2, + CONST_BITS+PASS1_BITS+3)) and RANGE_MASK];} + sub eax, ecx + shr eax, CONST_BITS+PASS1_BITS+3 + and eax, RANGE_MASK + mov al, BYTE PTR [ebx+eax] + mov [edi+6], al + + {outptr^[2] := range_limit^[ int(DESCALE(tmp12 + tmp1, + CONST_BITS+PASS1_BITS+3)) and RANGE_MASK];} + mov eax, tmp12 + add eax, ROUND_CONST_2 + mov ecx, tmp1 + lea edx, [eax+ecx] + shr edx, CONST_BITS+PASS1_BITS+3 + and edx, RANGE_MASK + mov dl, BYTE PTR [ebx+edx] + mov [edi+2], dl + + {outptr^[5] := range_limit^[ int(DESCALE(tmp12 - tmp1, + CONST_BITS+PASS1_BITS+3)) and RANGE_MASK];} + sub eax, ecx + shr eax, CONST_BITS+PASS1_BITS+3 + and eax, RANGE_MASK + mov al, BYTE PTR [ebx+eax] + mov [edi+5], al + + {outptr^[3] := range_limit^[ int(DESCALE(tmp13 + tmp0, + CONST_BITS+PASS1_BITS+3)) and RANGE_MASK];} + mov eax, tmp13 + add eax, ROUND_CONST_2 + mov ecx, tmp0 + lea edx, [eax+ecx] + shr edx, CONST_BITS+PASS1_BITS+3 + and edx, RANGE_MASK + mov dl, BYTE PTR [ebx+edx] + mov [edi+3], dl + + {outptr^[4] := range_limit^[ int(DESCALE(tmp13 - tmp0, + CONST_BITS+PASS1_BITS+3)) and RANGE_MASK];} + sub eax, ecx + shr eax, CONST_BITS+PASS1_BITS+3 + and eax, RANGE_MASK + mov al, BYTE PTR [ebx+eax] + mov [edi+4], al + + {Inc(int_ptr(wsptr), DCTSIZE); { advance pointer to next row } + add esi, wrkDCTSIZE + add edi, DCTSIZE + + {end;} + inc ctr + cmp ctr, DCTSIZE + jl @loop523 + +@loop524: +@loop496: + pop ebx + pop esi + pop edi +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjidctflt.pas b/resources/libraries/deskew/Imaging/JpegLib/imjidctflt.pas new file mode 100755 index 0000000..9ca5209 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjidctflt.pas @@ -0,0 +1,285 @@ +unit imjidctflt; + +{ 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. } + +{ Original: jidctflt.c ; Copyright (C) 1994-1996, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjpeglib, + imjdct; { Private declarations for DCT subsystem } + +{ Perform dequantization and inverse DCT on one block of coefficients. } + +{GLOBAL} +procedure jpeg_idct_float (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + coef_block : JCOEFPTR; + output_buf : JSAMPARRAY; + output_col : JDIMENSION); + +implementation + +{ This module is specialized to the case DCTSIZE = 8. } + +{$ifndef DCTSIZE_IS_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. } + +function DEQUANTIZE(coef : int; quantval : FAST_FLOAT) : FAST_FLOAT; +begin + Dequantize := ( (coef) * quantval); +end; + +{ 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. } + +function DESCALE(x : INT32; n : int) : INT32; +var + shift_temp : INT32; +begin +{$ifdef RIGHT_SHIFT_IS_UNSIGNED} + shift_temp := x + (INT32(1) shl (n-1)); + if shift_temp < 0 then + Descale := (shift_temp shr n) or ((not INT32(0)) shl (32-n)) + else + Descale := (shift_temp shr n); +{$else} + Descale := (x + (INT32(1) shl (n-1)) shr n; +{$endif} +end; + + +{ Perform dequantization and inverse DCT on one block of coefficients. } + +{GLOBAL} +procedure jpeg_idct_float (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + coef_block : JCOEFPTR; + output_buf : JSAMPARRAY; + output_col : JDIMENSION); +type + PWorkspace = ^TWorkspace; + TWorkspace = array[0..DCTSIZE2-1] of FAST_FLOAT; +var + tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7 : FAST_FLOAT; + tmp10, tmp11, tmp12, tmp13 : FAST_FLOAT; + z5, z10, z11, z12, z13 : FAST_FLOAT; + inptr : JCOEFPTR; + quantptr : FLOAT_MULT_TYPE_FIELD_PTR; + wsptr : PWorkSpace; + outptr : JSAMPROW; + range_limit : JSAMPROW; + ctr : int; + workspace : TWorkspace; { buffers data between passes } + {SHIFT_TEMPS} +var + dcval : FAST_FLOAT; +begin +{ 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. } + + range_limit := JSAMPROW(@(cinfo^.sample_range_limit^[CENTERJSAMPLE])); + + { Pass 1: process columns from input, store into work array. } + + inptr := coef_block; + quantptr := FLOAT_MULT_TYPE_FIELD_PTR (compptr^.dct_table); + wsptr := @workspace; + for ctr := pred(DCTSIZE) downto 0 do + begin + { 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) and (inptr^[DCTSIZE*2]=0) and + (inptr^[DCTSIZE*3]=0) and (inptr^[DCTSIZE*4]=0) and + (inptr^[DCTSIZE*5]=0) and (inptr^[DCTSIZE*6]=0) and + (inptr^[DCTSIZE*7]=0) then + begin + { 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; + + Inc(JCOEF_PTR(inptr)); { advance pointers to next column } + Inc(FLOAT_MULT_TYPE_PTR(quantptr)); + Inc(FAST_FLOAT_PTR(wsptr)); + continue; + end; + + { 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; + + Inc(JCOEF_PTR(inptr)); { advance pointers to next column } + Inc(FLOAT_MULT_TYPE_PTR(quantptr)); + Inc(FAST_FLOAT_PTR(wsptr)); + end; + + { 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 to pred(DCTSIZE) do + begin + outptr := JSAMPROW(@(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(Round((tmp0 + tmp7))), 3)) + and RANGE_MASK]; + outptr^[7] := range_limit^[ int(DESCALE( INT32(Round((tmp0 - tmp7))), 3)) + and RANGE_MASK]; + outptr^[1] := range_limit^[ int(DESCALE( INT32(Round((tmp1 + tmp6))), 3)) + and RANGE_MASK]; + outptr^[6] := range_limit^[ int(DESCALE( INT32(Round((tmp1 - tmp6))), 3)) + and RANGE_MASK]; + outptr^[2] := range_limit^[ int(DESCALE( INT32(Round((tmp2 + tmp5))), 3)) + and RANGE_MASK]; + outptr^[5] := range_limit^[ int(DESCALE( INT32(Round((tmp2 - tmp5))), 3)) + and RANGE_MASK]; + outptr^[4] := range_limit^[ int(DESCALE( INT32(Round((tmp3 + tmp4))), 3)) + and RANGE_MASK]; + outptr^[3] := range_limit^[ int(DESCALE( INT32(Round((tmp3 - tmp4))), 3)) + and RANGE_MASK]; + + Inc(FAST_FLOAT_PTR(wsptr), DCTSIZE); { advance pointer to next row } + end; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjidctfst.pas b/resources/libraries/deskew/Imaging/JpegLib/imjidctfst.pas new file mode 100755 index 0000000..6b1dd9b --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjidctfst.pas @@ -0,0 +1,410 @@ +unit imjidctfst; + +{ 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. } + +{ Original : jidctfst.c ; Copyright (C) 1994-1996, Thomas G. Lane. } + + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjpeglib, + imjdct; { Private declarations for DCT subsystem } + + +{ Perform dequantization and inverse DCT on one block of coefficients. } + +{GLOBAL} +procedure jpeg_idct_ifast (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + coef_block : JCOEFPTR; + output_buf : JSAMPARRAY; + output_col : JDIMENSION); + +implementation + +{ This module is specialized to the case DCTSIZE = 8. } + +{$ifndef DCTSIZE_IS_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). } + +{$ifdef BITS_IN_JSAMPLE_IS_8} +const + CONST_BITS = 8; + PASS1_BITS = 2; +{$else} +const + CONST_BITS = 8; + PASS1_BITS = 1; { lose a little precision to avoid overflow } +{$endif} + + +const + FIX_1_082392200 = INT32(Round((INT32(1) shl CONST_BITS)*1.082392200)); {277} + FIX_1_414213562 = INT32(Round((INT32(1) shl CONST_BITS)*1.414213562)); {362} + FIX_1_847759065 = INT32(Round((INT32(1) shl CONST_BITS)*1.847759065)); {473} + FIX_2_613125930 = INT32(Round((INT32(1) shl CONST_BITS)*2.613125930)); {669} + + +{ 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. } + +function DESCALE(x : INT32; n : int) : INT32; +var + shift_temp : INT32; +begin +{$ifdef USE_ACCURATE_ROUNDING} + shift_temp := x + (INT32(1) shl (n-1)); +{$else} +{ 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... } + shift_temp := x; +{$endif} + +{$ifdef RIGHT_SHIFT_IS_UNSIGNED} + if shift_temp < 0 then + Descale := (shift_temp shr n) or ((not INT32(0)) shl (32-n)) + else +{$endif} + Descale := (shift_temp shr n); +end; + + +{ Multiply a DCTELEM variable by an INT32 constant, and immediately + descale to yield a DCTELEM result. } + + {(DCTELEM( DESCALE((var) * (const), CONST_BITS))} + function Multiply(Avar, Aconst: Integer): DCTELEM; + begin + Multiply := DCTELEM( Avar*INT32(Aconst) div (INT32(1) shl CONST_BITS)); + end; + + +{ 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. } + +{$ifdef BITS_IN_JSAMPLE_IS_8} + function DEQUANTIZE(coef,quantval : int) : int; + begin + Dequantize := ( IFAST_MULT_TYPE(coef) * quantval); + end; +{$else} + function DEQUANTIZE(coef,quantval : INT32) : int; + begin + Dequantize := DESCALE((coef)*(quantval), IFAST_SCALE_BITS-PASS1_BITS); + end; +{$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. } + +function IDESCALE(x : DCTELEM; n : int) : int; +{$ifdef BITS_IN_JSAMPLE_IS_8} +const + DCTELEMBITS = 16; { DCTELEM may be 16 or 32 bits } +{$else} +const + DCTELEMBITS = 32; { DCTELEM must be 32 bits } +{$endif} +var + ishift_temp : DCTELEM; +begin +{$ifndef USE_ACCURATE_ROUNDING} + ishift_temp := x + (INT32(1) shl (n-1)); +{$else} +{ 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... } + ishift_temp := x; +{$endif} + +{$ifdef RIGHT_SHIFT_IS_UNSIGNED} + if ishift_temp < 0 then + IDescale := (ishift_temp shr n) + or ((not DCTELEM(0)) shl (DCTELEMBITS-n)) + else +{$endif} + IDescale := (ishift_temp shr n); +end; + + + +{ Perform dequantization and inverse DCT on one block of coefficients. } + +{GLOBAL} +procedure jpeg_idct_ifast (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + coef_block : JCOEFPTR; + output_buf : JSAMPARRAY; + output_col : JDIMENSION); +type + PWorkspace = ^TWorkspace; + TWorkspace = coef_bits_field; { buffers data between passes } +var + tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7 : DCTELEM; + tmp10, tmp11, tmp12, tmp13 : DCTELEM; + z5, z10, z11, z12, z13 : DCTELEM; + inptr : JCOEFPTR; + quantptr : IFAST_MULT_TYPE_FIELD_PTR; + wsptr : PWorkspace; + outptr : JSAMPROW; + range_limit : JSAMPROW; + ctr : int; + workspace : TWorkspace; { buffers data between passes } + {SHIFT_TEMPS} { for DESCALE } + {ISHIFT_TEMPS} { for IDESCALE } +var + dcval : int; +var + dcval_ : JSAMPLE; +begin +{ 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. } + + range_limit := JSAMPROW(@(cinfo^.sample_range_limit^[CENTERJSAMPLE])); + { Pass 1: process columns from input, store into work array. } + + inptr := coef_block; + quantptr := IFAST_MULT_TYPE_FIELD_PTR(compptr^.dct_table); + wsptr := @workspace; + for ctr := pred(DCTSIZE) downto 0 do + begin + { 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) and (inptr^[DCTSIZE*2]=0) and (inptr^[DCTSIZE*3]=0) and + (inptr^[DCTSIZE*4]=0) and (inptr^[DCTSIZE*5]=0) and (inptr^[DCTSIZE*6]=0) and + (inptr^[DCTSIZE*7]=0) then + begin + { AC terms all zero } + 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; + + Inc(JCOEF_PTR(inptr)); { advance pointers to next column } + Inc(IFAST_MULT_TYPE_PTR(quantptr)); + Inc(int_ptr(wsptr)); + continue; + end; + + { 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); + + Inc(JCOEF_PTR(inptr)); { advance pointers to next column } + Inc(IFAST_MULT_TYPE_PTR(quantptr)); + Inc(int_ptr(wsptr)); + end; + + { 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 to pred(DCTSIZE) do + begin + outptr := JSAMPROW(@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) and (wsptr^[2]=0) and (wsptr^[3]=0) and (wsptr^[4]=0) and + (wsptr^[5]=0) and (wsptr^[6]=0) and (wsptr^[7]=0) then + begin + { AC terms all zero } + dcval_ := range_limit^[IDESCALE(wsptr^[0], PASS1_BITS+3) + and 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_; + + Inc(int_ptr(wsptr), DCTSIZE); { advance pointer to next row } + continue; + end; +{$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) + and RANGE_MASK]; + outptr^[7] := range_limit^[IDESCALE(tmp0 - tmp7, PASS1_BITS+3) + and RANGE_MASK]; + outptr^[1] := range_limit^[IDESCALE(tmp1 + tmp6, PASS1_BITS+3) + and RANGE_MASK]; + outptr^[6] := range_limit^[IDESCALE(tmp1 - tmp6, PASS1_BITS+3) + and RANGE_MASK]; + outptr^[2] := range_limit^[IDESCALE(tmp2 + tmp5, PASS1_BITS+3) + and RANGE_MASK]; + outptr^[5] := range_limit^[IDESCALE(tmp2 - tmp5, PASS1_BITS+3) + and RANGE_MASK]; + outptr^[4] := range_limit^[IDESCALE(tmp3 + tmp4, PASS1_BITS+3) + and RANGE_MASK]; + outptr^[3] := range_limit^[IDESCALE(tmp3 - tmp4, PASS1_BITS+3) + and RANGE_MASK]; + + Inc(int_ptr(wsptr), DCTSIZE); { advance pointer to next row } + end; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjidctint.pas b/resources/libraries/deskew/Imaging/JpegLib/imjidctint.pas new file mode 100755 index 0000000..f46cc8f --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjidctint.pas @@ -0,0 +1,440 @@ +unit imjidctint; +{$Q+} + +{ 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. } + +{ Original : jidctint.c ; Copyright (C) 1991-1998, Thomas G. Lane. } + + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjpeglib, + imjdct; { Private declarations for DCT subsystem } + +{ Perform dequantization and inverse DCT on one block of coefficients. } + +{GLOBAL} +procedure jpeg_idct_islow (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + coef_block : JCOEFPTR; + output_buf : JSAMPARRAY; + output_col : JDIMENSION); + +implementation + +{ This module is specialized to the case DCTSIZE = 8. } + +{$ifndef DCTSIZE_IS_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. } + +{$ifdef BITS_IN_JSAMPLE_IS_8} +const + CONST_BITS = 13; + PASS1_BITS = 2; +{$else} +const + CONST_BITS = 13; + PASS1_BITS = 1; { lose a little precision to avoid overflow } +{$endif} + +const + CONST_SCALE = (INT32(1) shl CONST_BITS); + +const + FIX_0_298631336 = INT32(Round(CONST_SCALE * 0.298631336)); {2446} + FIX_0_390180644 = INT32(Round(CONST_SCALE * 0.390180644)); {3196} + FIX_0_541196100 = INT32(Round(CONST_SCALE * 0.541196100)); {4433} + FIX_0_765366865 = INT32(Round(CONST_SCALE * 0.765366865)); {6270} + FIX_0_899976223 = INT32(Round(CONST_SCALE * 0.899976223)); {7373} + FIX_1_175875602 = INT32(Round(CONST_SCALE * 1.175875602)); {9633} + FIX_1_501321110 = INT32(Round(CONST_SCALE * 1.501321110)); {12299} + FIX_1_847759065 = INT32(Round(CONST_SCALE * 1.847759065)); {15137} + FIX_1_961570560 = INT32(Round(CONST_SCALE * 1.961570560)); {16069} + FIX_2_053119869 = INT32(Round(CONST_SCALE * 2.053119869)); {16819} + FIX_2_562915447 = INT32(Round(CONST_SCALE * 2.562915447)); {20995} + FIX_3_072711026 = INT32(Round(CONST_SCALE * 3.072711026)); {25172} + + + +{ 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. } + +{$ifdef BITS_IN_JSAMPLE_IS_8} + + {$IFDEF BASM16} + {$IFNDEF WIN32} + {MULTIPLY16C16(var,const)} + function Multiply(X, Y: Integer): integer; assembler; + asm + mov ax, X + imul Y + mov al, ah + mov ah, dl + end; + {$ENDIF} + {$ENDIF} + + function Multiply(X, Y: INT32): INT32; + begin + Multiply := INT32(X) * INT32(Y); + end; + + +{$else} + {#define MULTIPLY(var,const) ((var) * (const))} + function Multiply(X, Y: INT32): INT32; + begin + Multiply := INT32(X) * INT32(Y); + end; +{$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. } + +function DEQUANTIZE(coef,quantval : int) : int; +begin + Dequantize := ( ISLOW_MULT_TYPE(coef) * quantval); +end; + +{ 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. } + +function DESCALE(x : INT32; n : int) : INT32; +var + shift_temp : INT32; +begin +{$ifdef RIGHT_SHIFT_IS_UNSIGNED} + shift_temp := x + (INT32(1) shl (n-1)); + if shift_temp < 0 then + Descale := (shift_temp shr n) or ((not INT32(0)) shl (32-n)) + else + Descale := (shift_temp shr n); +{$else} + Descale := (x + (INT32(1) shl (n-1)) shr n; +{$endif} +end; + +{ Perform dequantization and inverse DCT on one block of coefficients. } + +{GLOBAL} +procedure jpeg_idct_islow (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + coef_block : JCOEFPTR; + output_buf : JSAMPARRAY; + output_col : JDIMENSION); +type + PWorkspace = ^TWorkspace; + TWorkspace = coef_bits_field; { buffers data between passes } +var + tmp0, tmp1, tmp2, tmp3 : INT32; + tmp10, tmp11, tmp12, tmp13 : INT32; + z1, z2, z3, z4, z5 : INT32; + inptr : JCOEFPTR; + quantptr : ISLOW_MULT_TYPE_FIELD_PTR; + wsptr : PWorkspace; + outptr : JSAMPROW; + range_limit : JSAMPROW; + ctr : int; + workspace : TWorkspace; + {SHIFT_TEMPS} +var + dcval : int; +var + dcval_ : JSAMPLE; +begin +{ 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. } + + range_limit := JSAMPROW(@(cinfo^.sample_range_limit^[CENTERJSAMPLE])); + + + { 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_FIELD_PTR (compptr^.dct_table); + wsptr := PWorkspace(@workspace); + for ctr := pred(DCTSIZE) downto 0 do + begin + { 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) and (inptr^[DCTSIZE*2]=0) and + (inptr^[DCTSIZE*3]=0) and (inptr^[DCTSIZE*4]=0) and + (inptr^[DCTSIZE*5]=0) and (inptr^[DCTSIZE*6]=0) and + (inptr^[DCTSIZE*7]=0)) then + begin + { AC terms all zero } + dcval := DEQUANTIZE(inptr^[DCTSIZE*0], quantptr^[DCTSIZE*0]) shl 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; + + Inc(JCOEF_PTR(inptr)); { advance pointers to next column } + Inc(ISLOW_MULT_TYPE_PTR(quantptr)); + Inc(int_ptr(wsptr)); + continue; + end; + + { 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) shl CONST_BITS; + tmp1 := (z2 - z3) shl 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) } + + Inc(z3, z5); + Inc(z4, z5); + + Inc(tmp0, z1 + z3); + Inc(tmp1, z2 + z4); + Inc(tmp2, z2 + z3); + Inc(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)); + + Inc(JCOEF_PTR(inptr)); { advance pointers to next column } + Inc(ISLOW_MULT_TYPE_PTR(quantptr)); + Inc(int_ptr(wsptr)); + end; + + { 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 to pred(DCTSIZE) do + begin + outptr := output_buf^[ctr]; + Inc(JSAMPLE_PTR(outptr), 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) and (wsptr^[2]=0) and (wsptr^[3]=0) and (wsptr^[4]=0) + and (wsptr^[5]=0) and (wsptr^[6]=0) and (wsptr^[7]=0)) then + begin + { AC terms all zero } + JSAMPLE(dcval_) := range_limit^[int(DESCALE(INT32(wsptr^[0]), + PASS1_BITS+3)) and 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_; + + Inc(int_ptr(wsptr), DCTSIZE); { advance pointer to next row } + continue; + end; +{$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])) shl CONST_BITS; + tmp1 := (INT32(wsptr^[0]) - INT32(wsptr^[4])) shl 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) } + + Inc(z3, z5); + Inc(z4, z5); + + Inc(tmp0, z1 + z3); + Inc(tmp1, z2 + z4); + Inc(tmp2, z2 + z3); + Inc(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)) + and RANGE_MASK]; + outptr^[7] := range_limit^[ int(DESCALE(tmp10 - tmp3, + CONST_BITS+PASS1_BITS+3)) + and RANGE_MASK]; + outptr^[1] := range_limit^[ int(DESCALE(tmp11 + tmp2, + CONST_BITS+PASS1_BITS+3)) + and RANGE_MASK]; + outptr^[6] := range_limit^[ int(DESCALE(tmp11 - tmp2, + CONST_BITS+PASS1_BITS+3)) + and RANGE_MASK]; + outptr^[2] := range_limit^[ int(DESCALE(tmp12 + tmp1, + CONST_BITS+PASS1_BITS+3)) + and RANGE_MASK]; + outptr^[5] := range_limit^[ int(DESCALE(tmp12 - tmp1, + CONST_BITS+PASS1_BITS+3)) + and RANGE_MASK]; + outptr^[3] := range_limit^[ int(DESCALE(tmp13 + tmp0, + CONST_BITS+PASS1_BITS+3)) + and RANGE_MASK]; + outptr^[4] := range_limit^[ int(DESCALE(tmp13 - tmp0, + CONST_BITS+PASS1_BITS+3)) + and RANGE_MASK]; + + Inc(int_ptr(wsptr), DCTSIZE); { advance pointer to next row } + end; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjidctred.pas b/resources/libraries/deskew/Imaging/JpegLib/imjidctred.pas new file mode 100755 index 0000000..dae7bfc --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjidctred.pas @@ -0,0 +1,525 @@ +unit imjidctred; + + +{ 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. } + + +{ Original : jidctred.c ; Copyright (C) 1994-1998, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjpeglib, + imjdct; { Private declarations for DCT subsystem } + +{ Perform dequantization and inverse DCT on one block of coefficients, + producing a reduced-size 1x1 output block. } + +{GLOBAL} +procedure jpeg_idct_1x1 (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + coef_block : JCOEFPTR; + output_buf : JSAMPARRAY; + output_col : JDIMENSION); + +{ Perform dequantization and inverse DCT on one block of coefficients, + producing a reduced-size 2x2 output block. } + +{GLOBAL} +procedure jpeg_idct_2x2 (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + coef_block : JCOEFPTR; + output_buf : JSAMPARRAY; + output_col : JDIMENSION); + +{ Perform dequantization and inverse DCT on one block of coefficients, + producing a reduced-size 4x4 output block. } + +{GLOBAL} +procedure jpeg_idct_4x4 (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + coef_block : JCOEFPTR; + output_buf : JSAMPARRAY; + output_col : JDIMENSION); + +implementation + +{ This module is specialized to the case DCTSIZE = 8. } + +{$ifndef DCTSIZE_IS_8} + Sorry, this code only copes with 8x8 DCTs. { deliberate syntax err } +{$endif} + + +{ Scaling is the same as in jidctint.c. } + +{$ifdef BITS_IN_JSAMPLE_IS_8} +const + CONST_BITS = 13; + PASS1_BITS = 2; +{$else} +const + CONST_BITS = 13; + PASS1_BITS = 1; { lose a little precision to avoid overflow } +{$endif} + +const + FIX_0_211164243 = INT32(Round((INT32(1) shl CONST_BITS) * 0.211164243)); {1730} + FIX_0_509795579 = INT32(Round((INT32(1) shl CONST_BITS) * 0.509795579)); {4176} + FIX_0_601344887 = INT32(Round((INT32(1) shl CONST_BITS) * 0.601344887)); {4926} + FIX_0_720959822 = INT32(Round((INT32(1) shl CONST_BITS) * 0.720959822)); {5906} + FIX_0_765366865 = INT32(Round((INT32(1) shl CONST_BITS) * 0.765366865)); {6270} + FIX_0_850430095 = INT32(Round((INT32(1) shl CONST_BITS) * 0.850430095)); {6967} + FIX_0_899976223 = INT32(Round((INT32(1) shl CONST_BITS) * 0.899976223)); {7373} + FIX_1_061594337 = INT32(Round((INT32(1) shl CONST_BITS) * 1.061594337)); {8697} + FIX_1_272758580 = INT32(Round((INT32(1) shl CONST_BITS) * 1.272758580)); {10426} + FIX_1_451774981 = INT32(Round((INT32(1) shl CONST_BITS) * 1.451774981)); {11893} + FIX_1_847759065 = INT32(Round((INT32(1) shl CONST_BITS) * 1.847759065)); {15137} + FIX_2_172734803 = INT32(Round((INT32(1) shl CONST_BITS) * 2.172734803)); {17799} + FIX_2_562915447 = INT32(Round((INT32(1) shl CONST_BITS) * 2.562915447)); {20995} + FIX_3_624509785 = INT32(Round((INT32(1) shl CONST_BITS) * 3.624509785)); {29692} + + +{ 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. } + +{$ifdef BITS_IN_JSAMPLE_IS_8} + + {function Multiply(X, Y: Integer): integer; assembler; + asm + mov ax, X + imul Y + mov al, ah + mov ah, dl + end;} + + {MULTIPLY16C16(var,const)} + function Multiply(X, Y: Integer): INT32; + begin + Multiply := X*INT32(Y); + end; + + +{$else} + function Multiply(X, Y: INT32): INT32; + begin + Multiply := X*Y; + end; +{$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. } + +function DEQUANTIZE(coef,quantval : int) : int; +begin + Dequantize := ( ISLOW_MULT_TYPE(coef) * quantval); +end; + + +{ 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. } + +function DESCALE(x : INT32; n : int) : INT32; +var + shift_temp : INT32; +begin +{$ifdef RIGHT_SHIFT_IS_UNSIGNED} + shift_temp := x + (INT32(1) shl (n-1)); + if shift_temp < 0 then + Descale := (shift_temp shr n) or ((not INT32(0)) shl (32-n)) + else + Descale := (shift_temp shr n); +{$else} + Descale := (x + (INT32(1) shl (n-1)) shr n; +{$endif} +end; + +{ Perform dequantization and inverse DCT on one block of coefficients, + producing a reduced-size 4x4 output block. } + +{GLOBAL} +procedure jpeg_idct_4x4 (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + coef_block : JCOEFPTR; + output_buf : JSAMPARRAY; + output_col : JDIMENSION); +type + PWorkspace = ^TWorkspace; + TWorkspace = array[0..(DCTSIZE*4)-1] of int; { buffers data between passes } +var + tmp0, tmp2, tmp10, tmp12 : INT32; + z1, z2, z3, z4 : INT32; + inptr : JCOEFPTR; + quantptr : ISLOW_MULT_TYPE_FIELD_PTR; + wsptr : PWorkspace; + outptr : JSAMPROW; + range_limit : JSAMPROW; + ctr : int; + workspace : TWorkspace; { buffers data between passes } + {SHIFT_TEMPS} +var + dcval : int; +var + dcval_ : JSAMPLE; +begin +{ 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. } + + range_limit := JSAMPROW(@(cinfo^.sample_range_limit^[CENTERJSAMPLE])); + + { Pass 1: process columns from input, store into work array. } + + inptr := coef_block; + quantptr := ISLOW_MULT_TYPE_FIELD_PTR (compptr^.dct_table); + wsptr := @workspace; + for ctr := DCTSIZE downto 1 do + begin + { Don't bother to process column 4, because second pass won't use it } + if (ctr = DCTSIZE-4) then + begin + Inc(JCOEF_PTR(inptr)); + Inc(ISLOW_MULT_TYPE_PTR(quantptr)); + Inc(int_ptr(wsptr)); + + continue; + end; + if (inptr^[DCTSIZE*1]=0) and (inptr^[DCTSIZE*2]=0) and (inptr^[DCTSIZE*3]=0) and + (inptr^[DCTSIZE*5]=0) and (inptr^[DCTSIZE*6]=0) and (inptr^[DCTSIZE*7]=0) then + begin + { AC terms all zero; we need not examine term 4 for 4x4 output } + dcval := (ISLOW_MULT_TYPE(inptr^[DCTSIZE*0]) * + quantptr^[DCTSIZE*0]) shl PASS1_BITS; + + wsptr^[DCTSIZE*0] := dcval; + wsptr^[DCTSIZE*1] := dcval; + wsptr^[DCTSIZE*2] := dcval; + wsptr^[DCTSIZE*3] := dcval; + + Inc(JCOEF_PTR(inptr)); + Inc(ISLOW_MULT_TYPE_PTR(quantptr)); + Inc(int_ptr(wsptr)); + + continue; + end; + + { Even part } + + tmp0 := (ISLOW_MULT_TYPE(inptr^[DCTSIZE*0]) * quantptr^[DCTSIZE*0]); + + tmp0 := tmp0 shl (CONST_BITS+1); + + z2 := (ISLOW_MULT_TYPE(inptr^[DCTSIZE*2]) * quantptr^[DCTSIZE*2]); + z3 := (ISLOW_MULT_TYPE(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 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*7]) * quantptr^[DCTSIZE*7]; + z2 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*5]) * quantptr^[DCTSIZE*5]; + z3 := ISLOW_MULT_TYPE(inptr^[DCTSIZE*3]) * quantptr^[DCTSIZE*3]; + z4 := ISLOW_MULT_TYPE(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)); + + Inc(JCOEF_PTR(inptr)); + Inc(ISLOW_MULT_TYPE_PTR(quantptr)); + Inc(int_ptr(wsptr)); + end; + + { Pass 2: process 4 rows from work array, store into output array. } + + wsptr := @workspace; + for ctr := 0 to pred(4) do + begin + outptr := JSAMPROW(@ 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) and (wsptr^[2]=0) and (wsptr^[3]=0) and + (wsptr^[5]=0) and (wsptr^[6]=0) and (wsptr^[7]=0) then + begin + { AC terms all zero } + dcval_ := range_limit^[int(DESCALE(INT32(wsptr^[0]), PASS1_BITS+3)) + and RANGE_MASK]; + + outptr^[0] := dcval_; + outptr^[1] := dcval_; + outptr^[2] := dcval_; + outptr^[3] := dcval_; + + Inc(int_ptr(wsptr), DCTSIZE); { advance pointer to next row } + continue; + end; +{$endif} + + { Even part } + + tmp0 := (INT32(wsptr^[0])) shl (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)) + and RANGE_MASK]; + outptr^[3] := range_limit^[ int(DESCALE(tmp10 - tmp2, + CONST_BITS+PASS1_BITS+3+1)) + and RANGE_MASK]; + outptr^[1] := range_limit^[ int(DESCALE(tmp12 + tmp0, + CONST_BITS+PASS1_BITS+3+1)) + and RANGE_MASK]; + outptr^[2] := range_limit^[ int(DESCALE(tmp12 - tmp0, + CONST_BITS+PASS1_BITS+3+1)) + and RANGE_MASK]; + + Inc(int_ptr(wsptr), DCTSIZE); { advance pointer to next row } + end; +end; + + +{ Perform dequantization and inverse DCT on one block of coefficients, + producing a reduced-size 2x2 output block. } + +{GLOBAL} +procedure jpeg_idct_2x2 (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + coef_block : JCOEFPTR; + output_buf : JSAMPARRAY; + output_col : JDIMENSION); +type + PWorkspace = ^TWorkspace; + TWorkspace = array[0..(DCTSIZE*2)-1] of int; { buffers data between passes } +var + tmp0, tmp10, z1 : INT32; + inptr : JCOEFPTR; + quantptr : ISLOW_MULT_TYPE_FIELD_PTR; + wsptr : PWorkspace; + outptr : JSAMPROW; + range_limit : JSAMPROW; + ctr : int; + workspace : TWorkspace; { buffers data between passes } + {SHIFT_TEMPS} +var + dcval : int; +var + dcval_ : JSAMPLE; +begin +{ 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. } + + range_limit := JSAMPROW(@(cinfo^.sample_range_limit^[CENTERJSAMPLE])); + { Pass 1: process columns from input, store into work array. } + + inptr := coef_block; + quantptr := ISLOW_MULT_TYPE_FIELD_PTR (compptr^.dct_table); + wsptr := @workspace; + for ctr := DCTSIZE downto 1 do + begin + { Don't bother to process columns 2,4,6 } + if (ctr = DCTSIZE-2) or (ctr = DCTSIZE-4) or (ctr = DCTSIZE-6) then + begin + Inc(JCOEF_PTR(inptr)); + Inc(ISLOW_MULT_TYPE_PTR(quantptr)); + Inc(int_ptr(wsptr)); + + continue; + end; + if (inptr^[DCTSIZE*1]=0) and (inptr^[DCTSIZE*3]=0) and + (inptr^[DCTSIZE*5]=0) and (inptr^[DCTSIZE*7]=0) then + begin + { AC terms all zero; we need not examine terms 2,4,6 for 2x2 output } + dcval := (ISLOW_MULT_TYPE(inptr^[DCTSIZE*0]) * + quantptr^[DCTSIZE*0]) shl PASS1_BITS; + + wsptr^[DCTSIZE*0] := dcval; + wsptr^[DCTSIZE*1] := dcval; + + Inc(JCOEF_PTR(inptr)); + Inc(ISLOW_MULT_TYPE_PTR(quantptr)); + Inc(int_ptr(wsptr)); + + continue; + end; + + { Even part } + + z1 := (ISLOW_MULT_TYPE(inptr^[DCTSIZE*0]) * quantptr^[DCTSIZE*0]); + + tmp10 := z1 shl (CONST_BITS+2); + + { Odd part } + + z1 := (ISLOW_MULT_TYPE(inptr^[DCTSIZE*7]) * quantptr^[DCTSIZE*7]); + tmp0 := MULTIPLY(z1, - FIX_0_720959822); { sqrt(2) * (c7-c5+c3-c1) } + z1 := (ISLOW_MULT_TYPE(inptr^[DCTSIZE*5]) * quantptr^[DCTSIZE*5]); + Inc(tmp0, MULTIPLY(z1, FIX_0_850430095)); { sqrt(2) * (-c1+c3+c5+c7) } + z1 := (ISLOW_MULT_TYPE(inptr^[DCTSIZE*3]) * quantptr^[DCTSIZE*3]); + Inc(tmp0, MULTIPLY(z1, - FIX_1_272758580)); { sqrt(2) * (-c1+c3-c5-c7) } + z1 := (ISLOW_MULT_TYPE(inptr^[DCTSIZE*1]) * quantptr^[DCTSIZE*1]); + Inc(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)); + + Inc(JCOEF_PTR(inptr)); + Inc(ISLOW_MULT_TYPE_PTR(quantptr)); + Inc(int_ptr(wsptr)); + end; + + { Pass 2: process 2 rows from work array, store into output array. } + + wsptr := @workspace; + for ctr := 0 to pred(2) do + begin + outptr := JSAMPROW(@ 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) and (wsptr^[3]=0) and (wsptr^[5]=0) and (wsptr^[7]= 0) then + begin + { AC terms all zero } + dcval_ := range_limit^[ int(DESCALE(INT32(wsptr^[0]), PASS1_BITS+3)) + and RANGE_MASK]; + + outptr^[0] := dcval_; + outptr^[1] := dcval_; + + Inc(int_ptr(wsptr), DCTSIZE); { advance pointer to next row } + continue; + end; +{$endif} + + { Even part } + + tmp10 := (INT32 (wsptr^[0])) shl (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)) + and RANGE_MASK]; + outptr^[1] := range_limit^[ int(DESCALE(tmp10 - tmp0, + CONST_BITS+PASS1_BITS+3+2)) + and RANGE_MASK]; + + Inc(int_ptr(wsptr), DCTSIZE); { advance pointer to next row } + end; +end; + + +{ Perform dequantization and inverse DCT on one block of coefficients, + producing a reduced-size 1x1 output block. } + +{GLOBAL} +procedure jpeg_idct_1x1 (cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + coef_block : JCOEFPTR; + output_buf : JSAMPARRAY; + output_col : JDIMENSION); +var + dcval : int; + quantptr : ISLOW_MULT_TYPE_FIELD_PTR; + range_limit : JSAMPROW; + {SHIFT_TEMPS} +begin +{ 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. } + + range_limit := JSAMPROW(@(cinfo^.sample_range_limit^[CENTERJSAMPLE])); + { Pass 1: process columns from input, store into work array. } + + { 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_FIELD_PTR (compptr^.dct_table); + dcval := (ISLOW_MULT_TYPE(coef_block^[0]) * quantptr^[0]); + dcval := int (DESCALE( INT32(dcval), 3)); + + output_buf^[0]^[output_col] := range_limit^[dcval and RANGE_MASK]; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjinclude.pas b/resources/libraries/deskew/Imaging/JpegLib/imjinclude.pas new file mode 100755 index 0000000..dcaa684 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjinclude.pas @@ -0,0 +1,126 @@ +unit imjinclude; + +{ 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. } + +{ Original: jinclude.h Copyright (C) 1991-1994, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +{ Include auto-config file to find out which system include files we need. } + +uses +{$ifdef Delphi_Stream} + classes, +{$endif} + imjmorecfg; + +{ Nomssi: + To write a dest/source manager that handle streams rather than files, + you can edit the FILEptr definition and the JFREAD() and JFWRITE() + functions in this unit, you don't need to change the default managers + JDATASRC and JDATADST. } + +{$ifdef Delphi_Stream} +type + FILEptr = ^TStream; +{$else} + {$ifdef Delphi_Jpeg} + type + FILEptr = TCustomMemoryStream; + {$else} + type + FILEptr = ^File; + {$endif} +{$endif} + +{ 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>.} + + + +{ 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! } + +procedure MEMZERO(target : pointer; size : size_t); + +procedure MEMCOPY(dest, src : pointer; size : size_t); + +{function SIZEOF(object) : size_t;} + +function JFREAD(fp : FILEptr; buf : pointer; sizeofbuf : size_t) : size_t; + +function JFWRITE(fp : FILEptr; buf : pointer; sizeofbuf : size_t) : size_t; + +implementation + +procedure MEMZERO(target : pointer; size : size_t); +begin + FillChar(target^, size, 0); +end; + +procedure MEMCOPY(dest, src : pointer; size : size_t); +begin + Move(src^, dest^, size); +end; + +{ 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! } + + +function JFREAD(fp : FILEptr; buf : pointer; sizeofbuf : size_t) : size_t; +var + count : uint; +begin + {$ifdef Delphi_Stream} + count := fp^.Read(buf^, sizeofbuf); + {$else} + blockread(fp^, buf^, sizeofbuf, count); + {$endif} + JFREAD := size_t(count); +end; + +function JFWRITE(fp : FILEptr; buf : pointer; sizeofbuf : size_t) : size_t; +var + count : uint; +begin + {$ifdef Delphi_Stream} + count := fp^.Write(buf^, sizeofbuf); + {$else} + blockwrite(fp^, buf^, sizeofbuf, count); + {$endif} + JFWRITE := size_t(count); +end; + + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjmemmgr.pas b/resources/libraries/deskew/Imaging/JpegLib/imjmemmgr.pas new file mode 100755 index 0000000..b3122f6 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjmemmgr.pas @@ -0,0 +1,1283 @@ +unit imjmemmgr; + +{ 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...) } + +{ Original : jmemmgr.c ; Copyright (C) 1991-1997, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjdeferr, + imjerror, + imjpeglib, + imjutils, +{$IFDEF VER70} +{$ifndef NO_GETENV} + Dos, { DOS unit should declare getenv() } + { function GetEnv(name : string) : string; } +{$endif} + imjmemdos; { import the system-dependent declarations } +{$ELSE} + imjmemnobs; + {$DEFINE NO_GETENV} +{$ENDIF} + +{ Memory manager initialization. + When this is called, only the error manager pointer is valid in cinfo! } + +{GLOBAL} +procedure jinit_memory_mgr (cinfo : j_common_ptr); + +implementation + + +{ Some important notes: + The allocation routines provided here must never return NIL. + 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 } +type + 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. } + +type + small_pool_ptr = ^small_pool_hdr; + small_pool_hdr = record + case byte of + 0:(hdr : record + next : small_pool_ptr; { next in list of pools } + bytes_used : size_t; { how many bytes already used within pool } + bytes_left : size_t; { bytes still available in this pool } + end); + 1:(dummy : ALIGN_TYPE); { included in union to ensure alignment } + end; {small_pool_hdr;} + +type + large_pool_ptr = ^large_pool_hdr; {FAR} + large_pool_hdr = record + case byte of + 0:(hdr : record + next : large_pool_ptr; { next in list of pools } + bytes_used : size_t; { how many bytes already used within pool } + bytes_left : size_t; { bytes still available in this pool } + end); + 1:(dummy : ALIGN_TYPE); { included in union to ensure alignment } + end; {large_pool_hdr;} + + +{ Here is the full definition of a memory manager object. } + +type + my_mem_ptr = ^my_memory_mgr; + my_memory_mgr = record + pub : jpeg_memory_mgr; { public fields } + + { Each pool identifier (lifetime class) names a linked list of pools. } + small_list : array[0..JPOOL_NUMPOOLS-1] of small_pool_ptr ; + large_list : array[0..JPOOL_NUMPOOLS-1] of large_pool_ptr ; + + { 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. } + + virt_sarray_list : jvirt_sarray_ptr; + virt_barray_list : jvirt_barray_ptr; + + { This counts total space obtained from jpeg_get_small/large } + total_space_allocated : long; + + { alloc_sarray and alloc_barray set this value for use by virtual + array routines. } + + last_rowsperchunk : JDIMENSION; { from most recent alloc_sarray/barray } + end; {my_memory_mgr;} + + {$ifndef AM_MEMORY_MANAGER} { only jmemmgr.c defines these } + +{ 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. } +type + jvirt_sarray_control = record + mem_buffer : JSAMPARRAY; { => the in-memory buffer } + rows_in_array : JDIMENSION; { total virtual array height } + samplesperrow : JDIMENSION; { width of array (and of memory buffer) } + maxaccess : JDIMENSION; { max rows accessed by access_virt_sarray } + rows_in_mem : JDIMENSION; { height of memory buffer } + rowsperchunk : JDIMENSION; { allocation chunk size in mem_buffer } + cur_start_row : JDIMENSION; { first logical row # in the buffer } + first_undef_row : JDIMENSION; { row # of first uninitialized row } + pre_zero : boolean; { pre-zero mode requested? } + dirty : boolean; { do current buffer contents need written? } + b_s_open : boolean; { is backing-store data valid? } + next : jvirt_sarray_ptr; { link to next virtual sarray control block } + b_s_info : backing_store_info; { System-dependent control info } + end; + + jvirt_barray_control = record + mem_buffer : JBLOCKARRAY; { => the in-memory buffer } + rows_in_array : JDIMENSION; { total virtual array height } + blocksperrow : JDIMENSION; { width of array (and of memory buffer) } + maxaccess : JDIMENSION; { max rows accessed by access_virt_barray } + rows_in_mem : JDIMENSION; { height of memory buffer } + rowsperchunk : JDIMENSION; { allocation chunk size in mem_buffer } + cur_start_row : JDIMENSION; { first logical row # in the buffer } + first_undef_row : JDIMENSION; { row # of first uninitialized row } + pre_zero : boolean; { pre-zero mode requested? } + dirty : boolean; { do current buffer contents need written? } + b_s_open : boolean; { is backing-store data valid? } + next : jvirt_barray_ptr; { link to next virtual barray control block } + b_s_info : backing_store_info; { System-dependent control info } + end; + {$endif} { AM_MEMORY_MANAGER} + +{$ifdef MEM_STATS} { optional extra stuff for statistics } + +{LOCAL} +procedure print_mem_stats (cinfo : j_common_ptr; pool_id : int); +var + mem : my_mem_ptr; + shdr_ptr : small_pool_ptr; + lhdr_ptr : large_pool_ptr; +begin + mem := my_mem_ptr (cinfo^.mem); + + { 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. } + + WriteLn(output, 'Freeing pool ', pool_id,', total space := ', + mem^.total_space_allocated); + + lhdr_ptr := mem^.large_list[pool_id]; + while (lhdr_ptr <> NIL) do + begin + WriteLn(output, ' Large chunk used ', + long (lhdr_ptr^.hdr.bytes_used)); + lhdr_ptr := lhdr_ptr^.hdr.next; + end; + + shdr_ptr := mem^.small_list[pool_id]; + + while (shdr_ptr <> NIL) do + begin + WriteLn(output, ' Small chunk used ', + long (shdr_ptr^.hdr.bytes_used), ' free ', + long (shdr_ptr^.hdr.bytes_left) ); + shdr_ptr := shdr_ptr^.hdr.next; + end; +end; + +{$endif} { MEM_STATS } + + +{LOCAL} +procedure out_of_memory (cinfo : j_common_ptr; which : int); +{ Report an out-of-memory error and stop execution } +{ If we compiled MEM_STATS support, report alloc requests before dying } +begin +{$ifdef MEM_STATS} + cinfo^.err^.trace_level := 2; { force self_destruct to report stats } +{$endif} + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, which); +end; + + +{ 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. } + +const + first_pool_slop : array[0..JPOOL_NUMPOOLS-1] of size_t = + (1600, { first PERMANENT pool } + 16000); { first IMAGE pool } + +const + extra_pool_slop : array[0..JPOOL_NUMPOOLS-1] of size_t = + (0, { additional PERMANENT pools } + 5000); { additional IMAGE pools } + +const + MIN_SLOP = 50; { greater than 0 to avoid futile looping } + + +{METHODDEF} +function alloc_small (cinfo : j_common_ptr; + pool_id : int; + sizeofobject : size_t) : pointer; +type + byteptr = ^byte; +{ Allocate a "small" object } +var + mem : my_mem_ptr; + hdr_ptr, prev_hdr_ptr : small_pool_ptr; + data_ptr : byteptr; + odd_bytes, min_request, slop : size_t; +begin + mem := my_mem_ptr (cinfo^.mem); + + { Check for unsatisfiable request (do now to ensure no overflow below) } + if (sizeofobject > size_t(MAX_ALLOC_CHUNK-SIZEOF(small_pool_hdr))) then + 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 mod SIZEOF(ALIGN_TYPE); + if (odd_bytes > 0) then + Inc(sizeofobject, SIZEOF(ALIGN_TYPE) - odd_bytes); + + { See if space is available in any existing pool } + if (pool_id < 0) or (pool_id >= JPOOL_NUMPOOLS) then + ERREXIT1(j_common_ptr(cinfo), JERR_BAD_POOL_ID, pool_id); { safety check } + prev_hdr_ptr := NIL; + hdr_ptr := mem^.small_list[pool_id]; + while (hdr_ptr <> NIL) do + begin + if (hdr_ptr^.hdr.bytes_left >= sizeofobject) then + break; { found pool with enough space } + prev_hdr_ptr := hdr_ptr; + hdr_ptr := hdr_ptr^.hdr.next; + end; + + { Time to make a new pool? } + if (hdr_ptr = NIL) then + begin + { min_request is what we need now, slop is what will be leftover } + min_request := sizeofobject + SIZEOF(small_pool_hdr); + if (prev_hdr_ptr = NIL) then { 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)) then + slop := size_t (MAX_ALLOC_CHUNK-min_request); + { Try to get space, if fail reduce slop and try again } + while TRUE do + begin + hdr_ptr := small_pool_ptr(jpeg_get_small(cinfo, min_request + slop)); + if (hdr_ptr <> NIL) then + break; + slop := slop div 2; + if (slop < MIN_SLOP) then { give up when it gets real small } + out_of_memory(cinfo, 2); { jpeg_get_small failed } + end; + Inc(mem^.total_space_allocated, min_request + slop); + { Success, initialize the new pool header and add to end of list } + hdr_ptr^.hdr.next := NIL; + hdr_ptr^.hdr.bytes_used := 0; + hdr_ptr^.hdr.bytes_left := sizeofobject + slop; + if (prev_hdr_ptr = NIL) then { first pool in class? } + mem^.small_list[pool_id] := hdr_ptr + else + prev_hdr_ptr^.hdr.next := hdr_ptr; + end; + + { OK, allocate the object from the current pool } + data_ptr := byteptr (hdr_ptr); + Inc(small_pool_ptr(data_ptr)); { point to first data byte in pool } + Inc(data_ptr, hdr_ptr^.hdr.bytes_used); { point to place for object } + Inc(hdr_ptr^.hdr.bytes_used, sizeofobject); + Dec(hdr_ptr^.hdr.bytes_left, sizeofobject); + + alloc_small := pointer(data_ptr); +end; + + +{ 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} +function alloc_large (cinfo : j_common_ptr; + pool_id : int; + sizeofobject : size_t) : pointer; +{ Allocate a "large" object } +var + mem : my_mem_ptr; + hdr_ptr : large_pool_ptr; + odd_bytes : size_t; +var + dest_ptr : large_pool_ptr; +begin + mem := my_mem_ptr (cinfo^.mem); + + { Check for unsatisfiable request (do now to ensure no overflow below) } + if (sizeofobject > size_t (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr))) then + 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 mod SIZEOF(ALIGN_TYPE); + if (odd_bytes > 0) then + Inc(sizeofobject, SIZEOF(ALIGN_TYPE) - odd_bytes); + + { Always make a new pool } + if (pool_id < 0) or (pool_id >= JPOOL_NUMPOOLS) then + 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 = NIL) then + out_of_memory(cinfo, 4); { jpeg_get_large failed } + Inc(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; + + {alloc_large := pointerFAR (hdr_ptr + 1); - point to first data byte in pool } + dest_ptr := hdr_ptr; + Inc(large_pool_ptr(dest_ptr)); + alloc_large := dest_ptr; +end; + + +{ 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} +function alloc_sarray (cinfo : j_common_ptr; + pool_id : int; + samplesperrow : JDIMENSION; + numrows : JDIMENSION) : JSAMPARRAY; +{ Allocate a 2-D sample array } +var + mem : my_mem_ptr; + the_result : JSAMPARRAY; + workspace : JSAMPROW; + rowsperchunk, currow, i : JDIMENSION; + ltemp : long; +begin + mem := my_mem_ptr(cinfo^.mem); + + { Calculate max # of rows allowed in one allocation chunk } + ltemp := (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) div + (long(samplesperrow) * SIZEOF(JSAMPLE)); + if (ltemp <= 0) then + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + if (ltemp < long(numrows)) then + rowsperchunk := JDIMENSION (ltemp) + else + rowsperchunk := numrows; + mem^.last_rowsperchunk := rowsperchunk; + + { Get space for row pointers (small object) } + the_result := JSAMPARRAY (alloc_small(cinfo, pool_id, + size_t (numrows * SIZEOF(JSAMPROW)))); + + { Get the rows themselves (large objects) } + currow := 0; + while (currow < numrows) do + begin + {rowsperchunk := MIN(rowsperchunk, numrows - currow);} + if rowsperchunk > numrows - currow then + rowsperchunk := numrows - currow; + + workspace := JSAMPROW (alloc_large(cinfo, pool_id, + size_t (size_t(rowsperchunk) * size_t(samplesperrow) + * SIZEOF(JSAMPLE))) ); + for i := pred(rowsperchunk) downto 0 do + begin + the_result^[currow] := workspace; + Inc(currow); + Inc(JSAMPLE_PTR(workspace), samplesperrow); + end; + end; + + alloc_sarray := the_result; +end; + + +{ Creation of 2-D coefficient-block arrays. + This is essentially the same as the code for sample arrays, above. } + +{METHODDEF} +function alloc_barray (cinfo : j_common_ptr; + pool_id : int; + blocksperrow : JDIMENSION; + numrows : JDIMENSION) : JBLOCKARRAY; +{ Allocate a 2-D coefficient-block array } +var + mem : my_mem_ptr; + the_result : JBLOCKARRAY; + workspace : JBLOCKROW; + rowsperchunk, currow, i : JDIMENSION; + ltemp : long; +begin + mem := my_mem_ptr(cinfo^.mem); + + { Calculate max # of rows allowed in one allocation chunk } + ltemp := (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) div + (long(blocksperrow) * SIZEOF(JBLOCK)); + + if (ltemp <= 0) then + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + if (ltemp < long(numrows)) then + rowsperchunk := JDIMENSION (ltemp) + else + rowsperchunk := numrows; + mem^.last_rowsperchunk := rowsperchunk; + + { Get space for row pointers (small object) } + the_result := JBLOCKARRAY (alloc_small(cinfo, pool_id, + size_t (numrows * SIZEOF(JBLOCKROW))) ); + + { Get the rows themselves (large objects) } + currow := 0; + while (currow < numrows) do + begin + {rowsperchunk := MIN(rowsperchunk, numrows - currow);} + if rowsperchunk > numrows - currow then + rowsperchunk := numrows - currow; + + workspace := JBLOCKROW (alloc_large(cinfo, pool_id, + size_t (size_t(rowsperchunk) * size_t(blocksperrow) + * SIZEOF(JBLOCK))) ); + for i := rowsperchunk downto 1 do + begin + the_result^[currow] := workspace; + Inc(currow); + Inc(JBLOCK_PTR(workspace), blocksperrow); + end; + end; + + alloc_barray := the_result; +end; + + +{ 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} +function request_virt_sarray (cinfo : j_common_ptr; + pool_id : int; + pre_zero : boolean; + samplesperrow : JDIMENSION; + numrows : JDIMENSION; + maxaccess : JDIMENSION) : jvirt_sarray_ptr; +{ Request a virtual 2-D sample array } +var + mem : my_mem_ptr; + the_result : jvirt_sarray_ptr; +begin + mem := my_mem_ptr (cinfo^.mem); + + { Only IMAGE-lifetime virtual arrays are currently supported } + if (pool_id <> JPOOL_IMAGE) then + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); { safety check } + + { get control block } + the_result := jvirt_sarray_ptr (alloc_small(cinfo, pool_id, + SIZEOF(jvirt_sarray_control)) ); + + the_result^.mem_buffer := NIL; { marks array not yet realized } + the_result^.rows_in_array := numrows; + the_result^.samplesperrow := samplesperrow; + the_result^.maxaccess := maxaccess; + the_result^.pre_zero := pre_zero; + the_result^.b_s_open := FALSE; { no associated backing-store object } + the_result^.next := mem^.virt_sarray_list; { add to list of virtual arrays } + mem^.virt_sarray_list := the_result; + + request_virt_sarray := the_result; +end; + + +{METHODDEF} +function request_virt_barray (cinfo : j_common_ptr; + pool_id : int; + pre_zero : boolean; + blocksperrow : JDIMENSION; + numrows : JDIMENSION; + maxaccess : JDIMENSION) : jvirt_barray_ptr; +{ Request a virtual 2-D coefficient-block array } +var + mem : my_mem_ptr; + the_result : jvirt_barray_ptr; +begin + mem := my_mem_ptr(cinfo^.mem); + + { Only IMAGE-lifetime virtual arrays are currently supported } + if (pool_id <> JPOOL_IMAGE) then + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); { safety check } + + { get control block } + the_result := jvirt_barray_ptr(alloc_small(cinfo, pool_id, + SIZEOF(jvirt_barray_control)) ); + + the_result^.mem_buffer := NIL; { marks array not yet realized } + the_result^.rows_in_array := numrows; + the_result^.blocksperrow := blocksperrow; + the_result^.maxaccess := maxaccess; + the_result^.pre_zero := pre_zero; + the_result^.b_s_open := FALSE; { no associated backing-store object } + the_result^.next := mem^.virt_barray_list; { add to list of virtual arrays } + mem^.virt_barray_list := the_result; + + request_virt_barray := the_result; +end; + + +{METHODDEF} +procedure realize_virt_arrays (cinfo : j_common_ptr); +{ Allocate the in-memory buffers for any unrealized virtual arrays } +var + mem : my_mem_ptr; + space_per_minheight, maximum_space, avail_mem : long; + minheights, max_minheights : long; + sptr : jvirt_sarray_ptr; + bptr : jvirt_barray_ptr; +begin + mem := my_mem_ptr (cinfo^.mem); + { 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; + sptr := mem^.virt_sarray_list; + while (sptr <> NIL) do + begin + if (sptr^.mem_buffer = NIL) then + begin { if not realized yet } + Inc(space_per_minheight, long(sptr^.maxaccess) * + long(sptr^.samplesperrow) * SIZEOF(JSAMPLE)); + Inc(maximum_space, long(sptr^.rows_in_array) * + long(sptr^.samplesperrow) * SIZEOF(JSAMPLE)); + end; + sptr := sptr^.next; + end; + bptr := mem^.virt_barray_list; + while (bptr <> NIL) do + begin + if (bptr^.mem_buffer = NIL) then + begin { if not realized yet } + Inc(space_per_minheight, long(bptr^.maxaccess) * + long(bptr^.blocksperrow) * SIZEOF(JBLOCK)); + Inc(maximum_space, long(bptr^.rows_in_array) * + long(bptr^.blocksperrow) * SIZEOF(JBLOCK)); + end; + bptr := bptr^.next; + end; + + if (space_per_minheight <= 0) then + exit; { 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) then + max_minheights := long(1000000000) + else + begin + max_minheights := avail_mem div 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) then + max_minheights := 1; + end; + + { Allocate the in-memory buffers and initialize backing store as needed. } + + sptr := mem^.virt_sarray_list; + while (sptr <> NIL) do + begin + if (sptr^.mem_buffer = NIL) then + begin { if not realized yet } + minheights := (long(sptr^.rows_in_array) - long(1)) div LongInt(sptr^.maxaccess) + long(1); + if (minheights <= max_minheights) then + begin + { This buffer fits in memory } + sptr^.rows_in_mem := sptr^.rows_in_array; + end + else + begin + { 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; + end; + 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; + end; + sptr := sptr^.next; + end; + + bptr := mem^.virt_barray_list; + while (bptr <> NIL) do + begin + if (bptr^.mem_buffer = NIL) then + begin { if not realized yet } + minheights := (long(bptr^.rows_in_array) - long(1)) div LongInt(bptr^.maxaccess) + long(1); + if (minheights <= max_minheights) then + begin + { This buffer fits in memory } + bptr^.rows_in_mem := bptr^.rows_in_array; + end + else + begin + { 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; + end; + 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; + end; + bptr := bptr^.next; + end; +end; + + +{LOCAL} +procedure do_sarray_io (cinfo : j_common_ptr; + ptr : jvirt_sarray_ptr; + writing : boolean); +{ Do backing store read or write of a virtual sample array } +var + bytesperrow, file_offset, byte_count, rows, thisrow, i : long; +begin + + bytesperrow := long(ptr^.samplesperrow * SIZEOF(JSAMPLE)); + file_offset := LongInt(ptr^.cur_start_row) * bytesperrow; + { Loop to read or write each allocation chunk in mem_buffer } + i := 0; + while i < long(ptr^.rows_in_mem) do + begin + + { One chunk, but check for short chunk at end of buffer } + {rows := MIN(long(ptr^.rowsperchunk), long(ptr^.rows_in_mem - i));} + rows := long(ptr^.rowsperchunk); + if rows > long(ptr^.rows_in_mem) - i then + rows := 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);} + if (rows > long(ptr^.first_undef_row) - thisrow) then + 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 > long(ptr^.rows_in_array) - thisrow) then + rows := long(ptr^.rows_in_array) - thisrow; + + if (rows <= 0) then { this chunk might be past end of file! } + break; + byte_count := rows * bytesperrow; + if (writing) then + ptr^.b_s_info.write_backing_store (cinfo, + @ptr^.b_s_info, + pointer {FAR} (ptr^.mem_buffer^[i]), + file_offset, byte_count) + else + ptr^.b_s_info.read_backing_store (cinfo, + @ptr^.b_s_info, + pointer {FAR} (ptr^.mem_buffer^[i]), + file_offset, byte_count); + Inc(file_offset, byte_count); + Inc(i, ptr^.rowsperchunk); + end; +end; + + +{LOCAL} +procedure do_barray_io (cinfo : j_common_ptr; + ptr : jvirt_barray_ptr; + writing : boolean); +{ Do backing store read or write of a virtual coefficient-block array } +var + bytesperrow, file_offset, byte_count, rows, thisrow, i : long; +begin + bytesperrow := long (ptr^.blocksperrow) * SIZEOF(JBLOCK); + file_offset := LongInt(ptr^.cur_start_row) * bytesperrow; + { Loop to read or write each allocation chunk in mem_buffer } + i := 0; + while (i < long(ptr^.rows_in_mem)) do + begin + { One chunk, but check for short chunk at end of buffer } + {rows := MIN(long(ptr^.rowsperchunk), long(ptr^.rows_in_mem - i));} + rows := long(ptr^.rowsperchunk); + if rows > long(ptr^.rows_in_mem) - i then + rows := 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));} + if rows > long(ptr^.first_undef_row) - thisrow then + 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 > long (ptr^.rows_in_array) - thisrow) then + rows := long (ptr^.rows_in_array) - thisrow; + + if (rows <= 0) then { this chunk might be past end of file! } + break; + byte_count := rows * bytesperrow; + if (writing) then + ptr^.b_s_info.write_backing_store (cinfo, + @ptr^.b_s_info, + {FAR} pointer(ptr^.mem_buffer^[i]), + file_offset, byte_count) + else + ptr^.b_s_info.read_backing_store (cinfo, + @ptr^.b_s_info, + {FAR} pointer(ptr^.mem_buffer^[i]), + file_offset, byte_count); + Inc(file_offset, byte_count); + Inc(i, ptr^.rowsperchunk); + end; +end; + + +{METHODDEF} +function access_virt_sarray (cinfo : j_common_ptr; + ptr : jvirt_sarray_ptr; + start_row : JDIMENSION; + num_rows : JDIMENSION; + writable : boolean ) : JSAMPARRAY; +{ 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. } +var + end_row : JDIMENSION; + undef_row : JDIMENSION; +var + bytesperrow : size_t; +var + ltemp : long; +begin + end_row := start_row + num_rows; + { debugging check } + if (end_row > ptr^.rows_in_array) or (num_rows > ptr^.maxaccess) or + (ptr^.mem_buffer = NIL) then + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + { Make the desired part of the virtual array accessible } + if (start_row < ptr^.cur_start_row) or + (end_row > ptr^.cur_start_row+ptr^.rows_in_mem) then + begin + if (not ptr^.b_s_open) then + ERREXIT(cinfo, JERR_VIRTUAL_BUG); + { Flush old buffer contents if necessary } + if (ptr^.dirty) then + begin + do_sarray_io(cinfo, ptr, TRUE); + ptr^.dirty := FALSE; + end; + { 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) then + begin + ptr^.cur_start_row := start_row; + end + else + begin + { use long arithmetic here to avoid overflow & unsigned problems } + + + ltemp := long(end_row) - long(ptr^.rows_in_mem); + if (ltemp < 0) then + ltemp := 0; { don't fall off front end of file } + ptr^.cur_start_row := JDIMENSION(ltemp); + end; + { 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); + end; + { 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) then + begin + if (ptr^.first_undef_row < start_row) then + begin + if (writable) then { writer skipped over a section of array } + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + undef_row := start_row; { but reader is allowed to read ahead } + end + else + begin + undef_row := ptr^.first_undef_row; + end; + if (writable) then + ptr^.first_undef_row := end_row; + if (ptr^.pre_zero) then + begin + bytesperrow := size_t(ptr^.samplesperrow) * SIZEOF(JSAMPLE); + Dec(undef_row, ptr^.cur_start_row); { make indexes relative to buffer } + Dec(end_row, ptr^.cur_start_row); + while (undef_row < end_row) do + begin + jzero_far({FAR} pointer(ptr^.mem_buffer^[undef_row]), bytesperrow); + Inc(undef_row); + end; + end + else + begin + if (not writable) then { reader looking at undefined data } + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + end; + end; + { Flag the buffer dirty if caller will write in it } + if (writable) then + ptr^.dirty := TRUE; + { Return address of proper part of the buffer } + access_virt_sarray := JSAMPARRAY(@ ptr^.mem_buffer^[start_row - ptr^.cur_start_row]); +end; + + +{METHODDEF} +function access_virt_barray (cinfo : j_common_ptr; + ptr : jvirt_barray_ptr; + start_row : JDIMENSION; + num_rows : JDIMENSION; + writable : boolean) : JBLOCKARRAY; +{ 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. } +var + end_row : JDIMENSION; + undef_row : JDIMENSION; + ltemp : long; +var + bytesperrow : size_t; +begin + end_row := start_row + num_rows; + + { debugging check } + if (end_row > ptr^.rows_in_array) or (num_rows > ptr^.maxaccess) or + (ptr^.mem_buffer = NIL) then + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + { Make the desired part of the virtual array accessible } + if (start_row < ptr^.cur_start_row) or + (end_row > ptr^.cur_start_row+ptr^.rows_in_mem) then + begin + if (not ptr^.b_s_open) then + ERREXIT(cinfo, JERR_VIRTUAL_BUG); + { Flush old buffer contents if necessary } + if (ptr^.dirty) then + begin + do_barray_io(cinfo, ptr, TRUE); + ptr^.dirty := FALSE; + end; + { 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) then + begin + ptr^.cur_start_row := start_row; + end + else + begin + { use long arithmetic here to avoid overflow & unsigned problems } + + ltemp := long(end_row) - long(ptr^.rows_in_mem); + if (ltemp < 0) then + ltemp := 0; { don't fall off front end of file } + ptr^.cur_start_row := JDIMENSION (ltemp); + end; + { 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); + end; + { 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) then + begin + if (ptr^.first_undef_row < start_row) then + begin + if (writable) then { writer skipped over a section of array } + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + undef_row := start_row; { but reader is allowed to read ahead } + end + else + begin + undef_row := ptr^.first_undef_row; + end; + if (writable) then + ptr^.first_undef_row := end_row; + if (ptr^.pre_zero) then + begin + bytesperrow := size_t (ptr^.blocksperrow) * SIZEOF(JBLOCK); + Dec(undef_row, ptr^.cur_start_row); { make indexes relative to buffer } + Dec(end_row, ptr^.cur_start_row); + while (undef_row < end_row) do + begin + jzero_far({FAR}pointer(ptr^.mem_buffer^[undef_row]), bytesperrow); + Inc(undef_row); + end; + end + else + begin + if (not writable) then { reader looking at undefined data } + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + end; + end; + { Flag the buffer dirty if caller will write in it } + if (writable) then + ptr^.dirty := TRUE; + { Return address of proper part of the buffer } + access_virt_barray := JBLOCKARRAY(@ ptr^.mem_buffer^[start_row - ptr^.cur_start_row]); +end; + + +{ Release all objects belonging to a specified pool. } + +{METHODDEF} +procedure free_pool (cinfo : j_common_ptr; pool_id : int); +var + mem : my_mem_ptr; + shdr_ptr : small_pool_ptr; + lhdr_ptr : large_pool_ptr; + space_freed : size_t; +var + sptr : jvirt_sarray_ptr; + bptr : jvirt_barray_ptr; +var + next_lhdr_ptr : large_pool_ptr; + next_shdr_ptr : small_pool_ptr; +begin + mem := my_mem_ptr(cinfo^.mem); + + if (pool_id < 0) or (pool_id >= JPOOL_NUMPOOLS) then + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); { safety check } + +{$ifdef MEM_STATS} + if (cinfo^.err^.trace_level > 1) then + 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) then + begin + sptr := mem^.virt_sarray_list; + while (sptr <> NIL) do + begin + if (sptr^.b_s_open) then + begin { 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); + end; + sptr := sptr^.next; + end; + mem^.virt_sarray_list := NIL; + bptr := mem^.virt_barray_list; + while (bptr <> NIL) do + begin + if (bptr^.b_s_open) then + begin { 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); + end; + bptr := bptr^.next; + end; + mem^.virt_barray_list := NIL; + end; + + { Release large objects } + lhdr_ptr := mem^.large_list[pool_id]; + mem^.large_list[pool_id] := NIL; + + while (lhdr_ptr <> NIL) do + begin + 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, {FAR} pointer(lhdr_ptr), space_freed); + Dec(mem^.total_space_allocated, space_freed); + lhdr_ptr := next_lhdr_ptr; + end; + + { Release small objects } + shdr_ptr := mem^.small_list[pool_id]; + mem^.small_list[pool_id] := NIL; + + while (shdr_ptr <> NIL) do + begin + 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, pointer(shdr_ptr), space_freed); + Dec(mem^.total_space_allocated, space_freed); + shdr_ptr := next_shdr_ptr; + end; +end; + + +{ Close up shop entirely. + Note that this cannot be called unless cinfo^.mem is non-NIL. } + +{METHODDEF} +procedure self_destruct (cinfo : j_common_ptr); +var + pool : int; +begin + { 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 downto JPOOL_PERMANENT do + begin + free_pool(cinfo, pool); + end; + + { Release the memory manager control block too. } + jpeg_free_small(cinfo, pointer(cinfo^.mem), SIZEOF(my_memory_mgr)); + cinfo^.mem := NIL; { ensures I will be called only once } + + jpeg_mem_term(cinfo); { system-dependent cleanup } +end; + + +{ Memory manager initialization. + When this is called, only the error manager pointer is valid in cinfo! } + +{GLOBAL} +procedure jinit_memory_mgr (cinfo : j_common_ptr); +var + mem : my_mem_ptr; + max_to_use : long; + pool : int; + test_mac : size_t; +{$ifndef NO_GETENV} +var + memenv : string; + code : integer; +{$endif} +begin + cinfo^.mem := NIL; { 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) and (SIZEOF(ALIGN_TYPE)-1)) <> 0) then + 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) or + ((MAX_ALLOC_CHUNK mod SIZEOF(ALIGN_TYPE)) <> 0) then + 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 = NIL) then + begin + jpeg_mem_term(cinfo); { system-dependent cleanup } + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 0); + end; + + { 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 downto JPOOL_PERMANENT do + begin + mem^.small_list[pool] := NIL; + mem^.large_list[pool] := NIL; + end; + mem^.virt_sarray_list := NIL; + mem^.virt_barray_list := NIL; + + 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} + memenv := getenv('JPEGMEM'); + if (memenv <> '') then + begin + Val(memenv, max_to_use, code); + if (Code = 0) then + begin + max_to_use := max_to_use * long(1000); + mem^.pub.max_memory_to_use := max_to_use * long(1000); + end; + end; +{$endif} + +end; + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjmemnobs.pas b/resources/libraries/deskew/Imaging/JpegLib/imjmemnobs.pas new file mode 100755 index 0000000..750fd80 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjmemnobs.pas @@ -0,0 +1,259 @@ +unit imjmemnobs; +{ Delphi3 -- > jmemnobs from jmemwin } +{ This file provides an Win32-compatible implementation of the system- + dependent portion of the JPEG memory manager. } + +{ Check jmemnobs.c } +{ Copyright (C) 1996, Jacques Nomssi Nzali } + + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjdeferr, + imjerror, + imjpeglib; + +{ 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). } + +const + MAX_ALLOC_CHUNK = long(1000000000); + +{GLOBAL} +procedure jpeg_open_backing_store (cinfo : j_common_ptr; + info : backing_store_ptr; + total_bytes_needed : long); + +{ These routines take care of any system-dependent initialization and + cleanup required. } + +{GLOBAL} +function jpeg_mem_init (cinfo : j_common_ptr) : long; + +{GLOBAL} +procedure jpeg_mem_term (cinfo : j_common_ptr); + +{ 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 NIL 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. } + + +{ Near-memory allocation and freeing are controlled by the regular library + routines malloc() and free(). } + +{GLOBAL} +function jpeg_get_small (cinfo : j_common_ptr; + sizeofobject : size_t) : pointer; + +{GLOBAL} +{object is a reserved word in Borland Pascal } +procedure jpeg_free_small (cinfo : j_common_ptr; + an_object : pointer; + sizeofobject : size_t); + +{ 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. } + + +{ "Large" objects are allocated in far memory, if possible } + + +{GLOBAL} +function jpeg_get_large (cinfo : j_common_ptr; + sizeofobject : size_t) : voidp; {far} + +{GLOBAL} +procedure jpeg_free_large (cinfo : j_common_ptr; + {var?} an_object : voidp; {FAR} + sizeofobject : size_t); + +{ 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. } + +{GLOBAL} +function jpeg_mem_available (cinfo : j_common_ptr; + min_bytes_needed : long; + max_bytes_needed : long; + already_allocated : long) : long; + + +implementation + +{ 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. } + + + +{ 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 NIL 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. } + + +{ Near-memory allocation and freeing are controlled by the regular library + routines malloc() and free(). } + +{GLOBAL} +function jpeg_get_small (cinfo : j_common_ptr; + sizeofobject : size_t) : pointer; +var + p : pointer; +begin + GetMem(p, sizeofobject); + jpeg_get_small := p; +end; + +{GLOBAL} +{object is a reserved word in Object Pascal } +procedure jpeg_free_small (cinfo : j_common_ptr; + an_object : pointer; + sizeofobject : size_t); +begin + FreeMem(an_object, sizeofobject); +end; + +{ 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. } + + + +{GLOBAL} +function jpeg_get_large (cinfo : j_common_ptr; + sizeofobject : size_t) : voidp; {far} +var + p : pointer; +begin + GetMem(p, sizeofobject); + jpeg_get_large := p; +end; + +{GLOBAL} +procedure jpeg_free_large (cinfo : j_common_ptr; + {var?} an_object : voidp; {FAR} + sizeofobject : size_t); +begin + Freemem(an_object, sizeofobject); +end; + +{ 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.} + + + +{ 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. } + +const + DEFAULT_MAX_MEM = long(300000); { for total usage about 450K } + +{GLOBAL} +function jpeg_mem_available (cinfo : j_common_ptr; + min_bytes_needed : long; + max_bytes_needed : long; + already_allocated : long) : long; +begin + {jpeg_mem_available := cinfo^.mem^.max_memory_to_use - already_allocated;} + jpeg_mem_available := max_bytes_needed; +end; + + +{ 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.) } + + + +{ Initial opening of a backing-store object. } + +{GLOBAL} +procedure jpeg_open_backing_store (cinfo : j_common_ptr; + info : backing_store_ptr; + total_bytes_needed : long); +begin + ERREXIT(cinfo, JERR_NO_BACKING_STORE); +end; + +{ 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. } + + +{ These routines take care of any system-dependent initialization and + cleanup required. } + + +{GLOBAL} +function jpeg_mem_init (cinfo : j_common_ptr) : long; +begin + jpeg_mem_init := DEFAULT_MAX_MEM; { default for max_memory_to_use } +end; + +{GLOBAL} +procedure jpeg_mem_term (cinfo : j_common_ptr); +begin + +end; + + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjmorecfg.pas b/resources/libraries/deskew/Imaging/JpegLib/imjmorecfg.pas new file mode 100755 index 0000000..fddf3f5 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjmorecfg.pas @@ -0,0 +1,219 @@ +unit imjmorecfg; + +{ 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. } + +{ Source: jmorecfg.h; Copyright (C) 1991-1996, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +type + int = Integer; + uInt = Cardinal; + short = SmallInt; + ushort = Word; + long = LongInt; + +type + voidp = pointer; + +type + int_ptr = ^int; + size_t = int; + +{ 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. } + + +{$ifdef BITS_IN_JSAMPLE_IS_8} { use 8 or 12 } +const + BITS_IN_JSAMPLE = 8; +{$else} +const + BITS_IN_JSAMPLE = 12; +{$endif} + + +{ 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.) } + + +const + 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. } + + +{$ifdef BITS_IN_JSAMPLE_IS_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 $FF. } + +{ CHAR_IS_UNSIGNED } +type + JSAMPLE = byte; { Pascal unsigned char } + GETJSAMPLE = int; + +const + MAXJSAMPLE = 255; + CENTERJSAMPLE = 128; + +{$endif} + +{$ifndef BITS_IN_JSAMPLE_IS_8} +{ JSAMPLE should be the smallest type that will hold the values 0..4095. + On nearly all machines "short" will do nicely. } + +type + JSAMPLE = short; + GETJSAMPLE = int; + +const + MAXJSAMPLE = 4095; + 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. } +type + JCOEF = int; + JCOEF_PTR = ^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. } + + +type + JOCTET = Byte; + jTOctet = 0..(MaxInt div SizeOf(JOCTET))-1; + JOCTET_FIELD = array[jTOctet] of JOCTET; + JOCTET_FIELD_PTR = ^JOCTET_FIELD; + JOCTETPTR = ^JOCTET; + + GETJOCTET = JOCTET; { A work around } + + +{ 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. } + +type + UINT8 = Byte; + +{ UINT16 must hold at least the values 0..65535. } + + UINT16 = Word; + +{ INT16 must hold at least the values -32768..32767. } + + INT16 = SmallInt; + +{ INT32 must hold at least signed 32-bit values. } + + INT32 = LongInt; +type + INT32PTR = ^INT32; + +{ 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. } + +type + JDIMENSION = uInt; + +const + JPEG_MAX_DIMENSION = 65500; { a tad under 64K to prevent overflows } + + +{ 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. } + +{$ifdef RGB_RED_IS_0} +const + RGB_RED = 0; { Offset of Red in an RGB scanline element } + RGB_GREEN = 1; { Offset of Green } + RGB_BLUE = 2; { Offset of Blue } +{$else} +const + RGB_RED = 2; { Offset of Red in an RGB scanline element } + RGB_GREEN = 1; { Offset of Green } + RGB_BLUE = 0; { Offset of Blue } +{$endif} + +{$ifdef RGB_PIXELSIZE_IS_3} +const + RGB_PIXELSIZE = 3; { JSAMPLEs per RGB scanline element } +{$else} +const + RGB_PIXELSIZE = ??; { Nomssi: deliberate syntax error. Set this value } +{$endif} + +{ Definitions for speed-related optimizations. } + +{ 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. } +type + MULTIPLIER = int; { type for fastest integer multiply } + + +{ 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. } + +type + FAST_FLOAT = double; {float} + + +implementation + + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjpeglib.pas b/resources/libraries/deskew/Imaging/JpegLib/imjpeglib.pas new file mode 100755 index 0000000..f30ece6 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjpeglib.pas @@ -0,0 +1,1300 @@ +unit imjpeglib; + +{ 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. } + +{ Source:jpeglib.h+jpegint.h; Copyright (C) 1991-1998, Thomas G. Lane. } + + +interface + +{$I imjconfig.inc} + +{ 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. } + +uses + imjdeferr, + imjmorecfg; { seldom changed options } + +{ Version ID for the JPEG library. + Might be useful for tests like "#if JPEG_LIB_VERSION >= 60". } + + +Const + JPEG_LIB_VERSION = 62; { Version 6b } + + +{ These marker codes are exported since applications and data source modules + are likely to want to use them. } + +const + JPEG_RST0 = $D0; { RST0 marker code } + JPEG_EOI = $D9; { EOI marker code } + JPEG_APP0 = $E0; { APP0 marker code } + JPEG_COM = $FE; { COM marker code } + + +{ 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. } + +const + DCTSIZE = 8; { The basic DCT block is 8x8 samples } + DCTSIZE2 = 64; { DCTSIZE squared; # of elements in a block } + NUM_QUANT_TBLS = 4; { Quantization tables are numbered 0..3 } + NUM_HUFF_TBLS = 4; { Huffman tables are numbered 0..3 } + NUM_ARITH_TBLS = 16; { Arith-coding tables are numbered 0..15 } + MAX_COMPS_IN_SCAN = 4; { JPEG limit on # of components in one scan } + 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. } + C_MAX_BLOCKS_IN_MCU = 10; { compressor's limit on blocks per MCU } + D_MAX_BLOCKS_IN_MCU = 10; { decompressor's limit on blocks per MCU } + + +{ 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. } + +type +{ for typecasting } + JSAMPLE_PTR = ^JSAMPLE; + JSAMPROW_PTR = ^JSAMPROW; + JBLOCKROW_PTR = ^JBLOCKROW; + + jTSample = 0..(MaxInt div SIZEOF(JSAMPLE))-1; + JSAMPLE_ARRAY = Array[jTSample] of JSAMPLE; {far} + JSAMPROW = ^JSAMPLE_ARRAY; { ptr to one image row of pixel samples. } + + jTRow = 0..(MaxInt div SIZEOF(JSAMPROW))-1; + JSAMPROW_ARRAY = Array[jTRow] of JSAMPROW; + JSAMPARRAY = ^JSAMPROW_ARRAY; { ptr to some rows (a 2-D sample array) } + + jTArray = 0..(MaxInt div SIZEOF(JSAMPARRAY))-1; + JSAMP_ARRAY = Array[jTArray] of JSAMPARRAY; + JSAMPIMAGE = ^JSAMP_ARRAY; { a 3-D sample array: top index is color } + + JBLOCK = Array[0..DCTSIZE2-1] of JCOEF; { one block of coefficients } + JBLOCK_PTR = ^JBLOCK; + + jTBlockRow = 0..(MaxInt div SIZEOF(JBLOCK))-1; + JBLOCK_ROWS = Array[jTBlockRow] of JBLOCK; + JBLOCKROW = ^JBLOCK_ROWS; {far} { pointer to one row of coefficient blocks } + + + jTBlockArray = 0..(MaxInt div SIZEOF(JBLOCKROW))-1; + JBLOCK_ARRAY = Array[jTBlockArray] of JBLOCKROW; + JBLOCKARRAY = ^JBLOCK_ARRAY; { a 2-D array of coefficient blocks } + + jTBlockImage = 0..(MaxInt div SIZEOF(JBLOCKARRAY))-1; + JBLOCK_IMAGE = Array[jTBlockImage] of JBLOCKARRAY; + JBLOCKIMAGE = ^JBLOCK_IMAGE; { a 3-D array of coefficient blocks } + + jTCoef = 0..(MaxInt div SIZEOF(JCOEF))-1; + JCOEF_ROW = Array[jTCoef] of JCOEF; + JCOEFPTR = ^JCOEF_ROW; {far} { useful in a couple of places } + + +type + jTByte = 0..(MaxInt div SIZEOF(byte))-1; + JByteArray = Array[jTByte] of byte; + JBytePtr = ^JByteArray; +type + byteptr = ^byte; + +{ Types for JPEG compression parameters and working tables. } + + +{ DCT coefficient quantization tables. } + +type + JQUANT_TBL_PTR = ^JQUANT_TBL; + JQUANT_TBL = record + { 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. } + quantval : Array[0..DCTSIZE2-1] of UINT16; + { 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.) } + sent_table : boolean; { TRUE when table has been output } + end; + JQUANT_TBL_FIELD = Array[0..(MaxInt div SizeOf(JQUANT_TBL))-1] of JQUANT_TBL; + +{ Huffman coding tables. } + +type + JHUFF_TBL_PTR = ^JHUFF_TBL; + JHUFF_TBL = record + { These two fields directly represent the contents of a JPEG DHT marker } + bits : Array[0..17-1] of UINT8; { bits[k] = # of symbols with codes of } + { length k bits; bits[0] is unused } + huffval : Array[0..256-1] of UINT8; + { 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.) } + sent_table : boolean; { TRUE when table has been output } + end; + JHUFF_TBL_FIELD = Array[0..(MaxInt div SizeOf(JHUFF_TBL))-1] of JHUFF_TBL; + +{ Declarations for both compression & decompression } + +type + J_BUF_MODE = ( { 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 } + ); + +{ Values of global_state field (jdapi.c has some dependencies on ordering!) } +const + CSTATE_START = 100; { after create_compress } + CSTATE_SCANNING = 101; { start_compress done, write_scanlines OK } + CSTATE_RAW_OK = 102; { start_compress done, write_raw_data OK } + CSTATE_WRCOEFS = 103; { jpeg_write_coefficients done } + DSTATE_START = 200; { after create_decompress } + DSTATE_INHEADER = 201; { reading header markers, no SOS yet } + DSTATE_READY = 202; { found SOS, ready for start_decompress } + DSTATE_PRELOAD = 203; { reading multiscan file in start_decompress} + DSTATE_PRESCAN = 204; { performing dummy pass for 2-pass quant } + DSTATE_SCANNING = 205; { start_decompress done, read_scanlines OK } + DSTATE_RAW_OK = 206; { start_decompress done, read_raw_data OK } + DSTATE_BUFIMAGE = 207; { expecting jpeg_start_output } + DSTATE_BUFPOST = 208; { looking for SOS/EOI in jpeg_finish_output } + DSTATE_RDCOEFS = 209; { reading file in jpeg_read_coefficients } + DSTATE_STOPPING = 210; { looking for EOI in jpeg_finish_decompress } + + + +{ Basic info about one component (color channel). } + +type + jpeg_component_info_ptr = ^jpeg_component_info; + jpeg_component_info = record + { 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. } + component_id : int; { identifier for this component (0..255) } + component_index : int; { its index in SOF or cinfo^.comp_info[] } + h_samp_factor : int; { horizontal sampling factor (1..4) } + v_samp_factor : int; { vertical sampling factor (1..4) } + quant_tbl_no : int; { 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. } + dc_tbl_no : int; { DC entropy table selector (0..3) } + ac_tbl_no : int; { 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. } + width_in_blocks : JDIMENSION; + height_in_blocks : JDIMENSION; + { 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. } + + DCT_scaled_size : int; + { 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)} + + downsampled_width : JDIMENSION; { actual width in samples } + downsampled_height : JDIMENSION; { 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. } + + component_needed : boolean; { 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. } + MCU_width : int; { number of blocks per MCU, horizontally } + MCU_height : int; { number of blocks per MCU, vertically } + MCU_blocks : int; { MCU_width * MCU_height } + MCU_sample_width : int; { MCU width in samples, MCU_width*DCT_scaled_size } + last_col_width : int; { # of non-dummy blocks across in last MCU } + last_row_height : int; { # of non-dummy blocks down in last MCU } + + { Saved quantization table for component; NIL if none yet saved. + See jdinput.c comments about the need for this information. + This field is currently used only for decompression. } + + quant_table : JQUANT_TBL_PTR; + + { Private per-component storage for DCT or IDCT subsystem. } + dct_table : pointer; + end; { record jpeg_component_info } + + jTCinfo = 0..(MaxInt div SizeOf(jpeg_component_info))-1; + jpeg_component_info_array = array[jTCinfo] of jpeg_component_info; + jpeg_component_info_list_ptr = ^jpeg_component_info_array; + + +{ The script for encoding a multiple-scan file is an array of these: } + +type + jpeg_scan_info_ptr = ^jpeg_scan_info; + jpeg_scan_info = record + comps_in_scan : int; { number of components encoded in this scan } + component_index : Array[0..MAX_COMPS_IN_SCAN-1] of int; + { their SOF/comp_info[] indexes } + Ss, Se : int; { progressive JPEG spectral selection parms } + Ah, Al : int; { progressive JPEG successive approx. parms } + end; + +{ The decompressor can save APPn and COM markers in a list of these: } + +type + jpeg_saved_marker_ptr = ^jpeg_marker_struct; + jpeg_marker_struct = record + next : jpeg_saved_marker_ptr; { next in list, or NULL } + marker : UINT8; { marker code: JPEG_COM, or JPEG_APP0+n } + original_length : uint; { # bytes of data in the file } + data_length : uint; { # bytes of data saved at data[] } + data : JOCTET_FIELD_PTR; { the data contained in the marker } + { the marker length word is not counted in data_length or original_length } + end; + +{ Known color spaces. } + +type + J_COLOR_SPACE = ( + 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 } + ); + +{ DCT/IDCT algorithm options. } + +type + J_DCT_METHOD = ( + JDCT_ISLOW, { slow but accurate integer algorithm } + JDCT_IFAST, { faster, less accurate integer method } + JDCT_FLOAT { floating-point: accurate, fast on fast HW } + ); + +const + JDCT_DEFAULT = JDCT_ISLOW; + JDCT_FASTEST = JDCT_IFAST; + +{ Dithering options for decompression. } + +type + J_DITHER_MODE = ( + JDITHER_NONE, { no dithering } + JDITHER_ORDERED, { simple ordered dither } + JDITHER_FS { Floyd-Steinberg error diffusion dither } + ); + + +const + JPOOL_PERMANENT = 0; { lasts until master record is destroyed } + JPOOL_IMAGE = 1; { lasts until done with image/datastream } + JPOOL_NUMPOOLS = 2; + + +{ "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 } + +const + JMSG_LENGTH_MAX = 200; { recommended size of format_message buffer } + JMSG_STR_PARM_MAX = 80; + +const + TEMP_NAME_LENGTH = 64; { max length of a temporary file's name } +type + TEMP_STRING = string[TEMP_NAME_LENGTH]; + +{$ifdef USE_MSDOS_MEMMGR} { DOS-specific junk } +type + XMSH = ushort; { type of extended-memory handles } + EMSH = ushort; { type of expanded-memory handles } + + handle_union = record + case byte of + 0:(file_handle : short); { DOS file handle if it's a temp file } + 1:(xms_handle : XMSH); { handle if it's a chunk of XMS } + 2:(ems_handle : EMSH); { handle if it's a chunk of EMS } + end; +{$endif} { USE_MSDOS_MEMMGR } + +type + jpeg_error_mgr_ptr = ^jpeg_error_mgr; + jpeg_memory_mgr_ptr = ^jpeg_memory_mgr; + jpeg_progress_mgr_ptr = ^jpeg_progress_mgr; + + +{$ifdef common} +{ Common fields between JPEG compression and decompression master structs. } + err : jpeg_error_mgr_ptr; { Error handler module } + mem : jpeg_memory_mgr_ptr; { Memory manager module } + progress : jpeg_progress_mgr_ptr; { Progress monitor, or NIL if none } + client_data : voidp; { Available for use by application } + is_decompressor : boolean; { so common code can tell which is which } + global_state : int; { for checking call sequence validity } +{$endif} + + j_common_ptr = ^jpeg_common_struct; + j_compress_ptr = ^jpeg_compress_struct; + j_decompress_ptr = ^jpeg_decompress_struct; + + {$ifdef AM_MEMORY_MANAGER} { only jmemmgr.c defines these } + +{ 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. } + + + backing_store_ptr = ^backing_store_info; + backing_store_info = record + { Methods for reading/writing/closing this backing-store object } + read_backing_store : procedure (cinfo : j_common_ptr; + info : backing_store_ptr; + buffer_address : pointer; {far} + file_offset : long; + byte_count : long); + write_backing_store : procedure (cinfo : j_common_ptr; + info : backing_store_ptr; + buffer_address : pointer; {far} + file_offset : long; + byte_count : long); + + close_backing_store : procedure (cinfo : j_common_ptr; + info : backing_store_ptr); + + { Private fields for system-dependent backing-store management } + {$ifdef USE_MSDOS_MEMMGR} + { For the MS-DOS manager (jmemdos.c), we need: } + handle : handle_union; { reference to backing-store storage object } + temp_name : TEMP_STRING; { name if it's a file } + {$else} + { For a typical implementation with temp files, we need: } + temp_file : file; { stdio reference to temp file } + temp_name : TEMP_STRING; { name of temp file } + {$endif} + end; + + +{ 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. } + + jvirt_sarray_ptr = ^jvirt_sarray_control; + jvirt_sarray_control = record + mem_buffer : JSAMPARRAY; { => the in-memory buffer } + rows_in_array : JDIMENSION; { total virtual array height } + samplesperrow : JDIMENSION; { width of array (and of memory buffer) } + maxaccess : JDIMENSION; { max rows accessed by access_virt_sarray } + rows_in_mem : JDIMENSION; { height of memory buffer } + rowsperchunk : JDIMENSION; { allocation chunk size in mem_buffer } + cur_start_row : JDIMENSION; { first logical row # in the buffer } + first_undef_row : JDIMENSION; { row # of first uninitialized row } + pre_zero : boolean; { pre-zero mode requested? } + dirty : boolean; { do current buffer contents need written? } + b_s_open : boolean; { is backing-store data valid? } + next : jvirt_sarray_ptr; { link to next virtual sarray control block } + b_s_info : backing_store_info; { System-dependent control info } + end; + + jvirt_barray_ptr = ^jvirt_barray_control; + jvirt_barray_control = record + mem_buffer : JBLOCKARRAY; { => the in-memory buffer } + rows_in_array : JDIMENSION; { total virtual array height } + blocksperrow : JDIMENSION; { width of array (and of memory buffer) } + maxaccess : JDIMENSION; { max rows accessed by access_virt_barray } + rows_in_mem : JDIMENSION; { height of memory buffer } + rowsperchunk : JDIMENSION; { allocation chunk size in mem_buffer } + cur_start_row : JDIMENSION; { first logical row # in the buffer } + first_undef_row : JDIMENSION; { row # of first uninitialized row } + pre_zero : boolean; { pre-zero mode requested? } + dirty : boolean; { do current buffer contents need written? } + b_s_open : boolean; { is backing-store data valid? } + next : jvirt_barray_ptr; { link to next virtual barray control block } + b_s_info : backing_store_info; { System-dependent control info } + end; + + {$endif} { AM_MEMORY_MANAGER } + +{ Declarations for compression modules } + +{ Master control module } + jpeg_comp_master_ptr = ^jpeg_comp_master; + jpeg_comp_master = record + prepare_for_pass : procedure(cinfo : j_compress_ptr); + pass_startup : procedure(cinfo : j_compress_ptr); + finish_pass : procedure(cinfo : j_compress_ptr); + + { State variables made visible to other modules } + call_pass_startup : Boolean; { True if pass_startup must be called } + is_last_pass : Boolean; { True during last pass } + end; + +{ Main buffer control (downsampled-data buffer) } + jpeg_c_main_controller_ptr = ^jpeg_c_main_controller; + jpeg_c_main_controller = record + start_pass : procedure(cinfo : j_compress_ptr; pass_mode : J_BUF_MODE); + process_data : procedure(cinfo : j_compress_ptr; + input_buf : JSAMPARRAY; + var in_row_ctr : JDIMENSION; + in_rows_avail : JDIMENSION); + end; + +{ Compression preprocessing (downsampling input buffer control) } + jpeg_c_prep_controller_ptr = ^jpeg_c_prep_controller; + jpeg_c_prep_controller = record + start_pass : procedure(cinfo : j_compress_ptr; pass_mode : J_BUF_MODE); + pre_process_data : procedure(cinfo : j_compress_ptr; + input_buf : JSAMPARRAY; + var in_row_ctr : JDIMENSION; + in_rows_avail : JDIMENSION; + output_buf : JSAMPIMAGE; + var out_row_group_ctr : JDIMENSION; + out_row_groups_avail : JDIMENSION); + end; + +{ Coefficient buffer control } + jpeg_c_coef_controller_ptr = ^jpeg_c_coef_controller; + jpeg_c_coef_controller = record + start_pass : procedure(cinfo : j_compress_ptr; pass_mode : J_BUF_MODE); + compress_data : function(cinfo : j_compress_ptr; + input_buf : JSAMPIMAGE) : boolean; + end; + +{ Colorspace conversion } + jpeg_color_converter_ptr = ^jpeg_color_converter; + jpeg_color_converter = record + start_pass : procedure(cinfo : j_compress_ptr); + color_convert : procedure(cinfo : j_compress_ptr; + input_buf : JSAMPARRAY; + output_buf : JSAMPIMAGE; + output_row : JDIMENSION; + num_rows : int); + end; + +{ Downsampling } + jpeg_downsampler_ptr = ^jpeg_downsampler; + jpeg_downsampler = record + start_pass : procedure(cinfo : j_compress_ptr); + downsample : procedure(cinfo : j_compress_ptr; + input_buf : JSAMPIMAGE; + in_row_index : JDIMENSION; + output_buf : JSAMPIMAGE; + out_row_group_index: JDIMENSION); + need_context_rows : Boolean; { TRUE if need rows above & below } + end; + +{ Forward DCT (also controls coefficient quantization) } + jpeg_forward_dct_ptr = ^jpeg_forward_dct; + jpeg_forward_dct = record + start_pass : procedure(cinfo : j_compress_ptr); + { perhaps this should be an array??? } + forward_DCT : procedure(cinfo : j_compress_ptr; + compptr : jpeg_component_info_ptr; + sample_data : JSAMPARRAY; + coef_blocks : JBLOCKROW; + start_row : JDIMENSION; + start_col : JDIMENSION; + num_blocks : JDIMENSION); + end; + +{ Entropy encoding } + + jpeg_entropy_encoder_ptr = ^jpeg_entropy_encoder; + jpeg_entropy_encoder = record + start_pass : procedure(cinfo : j_compress_ptr; gather_statistics : boolean); + encode_mcu : function(cinfo : j_compress_ptr; + const MCU_data: array of JBLOCKROW) : boolean; + finish_pass : procedure(cinfo : j_compress_ptr); + end; + +{ Marker writing } + jpeg_marker_writer_ptr = ^jpeg_marker_writer; + jpeg_marker_writer = record + write_file_header : procedure(cinfo : j_compress_ptr); + write_frame_header : procedure(cinfo : j_compress_ptr); + write_scan_header : procedure(cinfo : j_compress_ptr); + write_file_trailer : procedure(cinfo : j_compress_ptr); + write_tables_only : procedure(cinfo : j_compress_ptr); + { These routines are exported to allow insertion of extra markers } + { Probably only COM and APPn markers should be written this way } + write_marker_header : procedure (cinfo : j_compress_ptr; + marker : int; + datalen : uint); + write_marker_byte : procedure (cinfo : j_compress_ptr; val : int); + end; + +{ Declarations for decompression modules } + +{ Master control module } + jpeg_decomp_master_ptr = ^jpeg_decomp_master; + jpeg_decomp_master = record + prepare_for_output_pass : procedure( cinfo : j_decompress_ptr); + finish_output_pass : procedure(cinfo : j_decompress_ptr); + + { State variables made visible to other modules } + is_dummy_pass : Boolean; { True during 1st pass for 2-pass quant } + end; + +{ Input control module } + jpeg_input_controller_ptr = ^jpeg_input_controller; + jpeg_input_controller = record + consume_input : function (cinfo : j_decompress_ptr) : int; + reset_input_controller : procedure(cinfo : j_decompress_ptr); + start_input_pass : procedure(cinfo : j_decompress_ptr); + finish_input_pass : procedure(cinfo : j_decompress_ptr); + + { State variables made visible to other modules } + has_multiple_scans : Boolean; { True if file has multiple scans } + eoi_reached : Boolean; { True when EOI has been consumed } + end; + +{ Main buffer control (downsampled-data buffer) } + + jpeg_d_main_controller_ptr = ^jpeg_d_main_controller; + jpeg_d_main_controller = record + start_pass : procedure(cinfo : j_decompress_ptr; pass_mode : J_BUF_MODE); + process_data : procedure(cinfo : j_decompress_ptr; + output_buf : JSAMPARRAY; + var out_row_ctr : JDIMENSION; + out_rows_avail : JDIMENSION); + end; + +{ Coefficient buffer control } + jvirt_barray_tbl = array[0..MAX_COMPONENTS-1] of jvirt_barray_ptr; + jvirt_barray_tbl_ptr = ^jvirt_barray_tbl; + jpeg_d_coef_controller_ptr = ^jpeg_d_coef_controller; + jpeg_d_coef_controller = record + start_input_pass : procedure(cinfo : j_decompress_ptr); + consume_data : function (cinfo : j_decompress_ptr) : int; + start_output_pass : procedure(cinfo : j_decompress_ptr); + decompress_data : function (cinfo : j_decompress_ptr; + output_buf : JSAMPIMAGE) : int; + { Pointer to array of coefficient virtual arrays, or NIL if none } + coef_arrays : jvirt_barray_tbl_ptr; + end; + +{ Decompression postprocessing (color quantization buffer control) } + jpeg_d_post_controller_ptr = ^jpeg_d_post_controller; + jpeg_d_post_controller = record + start_pass : procedure(cinfo : j_decompress_ptr; + pass_mode : J_BUF_MODE); + post_process_data : procedure(cinfo : j_decompress_ptr; + input_buf : JSAMPIMAGE; + var in_row_group_ctr : JDIMENSION; + in_row_groups_avail : JDIMENSION; + output_buf : JSAMPARRAY; + var out_row_ctr : JDIMENSION; + out_rows_avail : JDIMENSION); + end; + + +{ Routine signature for application-supplied marker processing methods. + Need not pass marker code since it is stored in cinfo^.unread_marker. } + + jpeg_marker_parser_method = function(cinfo : j_decompress_ptr) : boolean; + +{ Marker reading & parsing } + jpeg_marker_reader_ptr = ^jpeg_marker_reader; + jpeg_marker_reader = record + reset_marker_reader : procedure(cinfo : j_decompress_ptr); + { 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. } + + read_markers : function (cinfo : j_decompress_ptr) : int; + { Read a restart marker --- exported for use by entropy decoder only } + read_restart_marker : jpeg_marker_parser_method; + + { State of marker reader --- nominally internal, but applications + supplying COM or APPn handlers might like to know the state. } + + saw_SOI : boolean; { found SOI? } + saw_SOF : boolean; { found SOF? } + next_restart_num : int; { next restart number expected (0-7) } + discarded_bytes : uint; { # of bytes skipped looking for a marker } + end; + +{ Entropy decoding } + jpeg_entropy_decoder_ptr = ^jpeg_entropy_decoder; + jpeg_entropy_decoder = record + start_pass : procedure(cinfo : j_decompress_ptr); + decode_mcu : function(cinfo : j_decompress_ptr; + var MCU_data : array of JBLOCKROW) : boolean; + { This is here to share code between baseline and progressive decoders; } + { other modules probably should not use it } + insufficient_data : BOOLEAN; { set TRUE after emitting warning } + end; + +{ Inverse DCT (also performs dequantization) } + inverse_DCT_method_ptr = procedure(cinfo : j_decompress_ptr; + compptr : jpeg_component_info_ptr; + coef_block : JCOEFPTR; + output_buf : JSAMPARRAY; output_col : JDIMENSION); + + jpeg_inverse_dct_ptr = ^jpeg_inverse_dct; + jpeg_inverse_dct = record + start_pass : procedure(cinfo : j_decompress_ptr); + { It is useful to allow each component to have a separate IDCT method. } + inverse_DCT : Array[0..MAX_COMPONENTS-1] of inverse_DCT_method_ptr; + end; + +{ Upsampling (note that upsampler must also call color converter) } + jpeg_upsampler_ptr = ^jpeg_upsampler; + jpeg_upsampler = record + start_pass : procedure(cinfo : j_decompress_ptr); + upsample : procedure(cinfo : j_decompress_ptr; + input_buf : JSAMPIMAGE; + var in_row_group_ctr : JDIMENSION; { array of } + in_row_groups_avail : JDIMENSION; + output_buf : JSAMPARRAY; + var out_row_ctr : JDIMENSION; + out_rows_avail : JDIMENSION); + + need_context_rows : boolean; { TRUE if need rows above & below } + end; + +{ Colorspace conversion } + jpeg_color_deconverter_ptr = ^jpeg_color_deconverter; + jpeg_color_deconverter = record + start_pass : procedure(cinfo: j_decompress_ptr); + color_convert : procedure(cinfo : j_decompress_ptr; + input_buf : JSAMPIMAGE; + input_row : JDIMENSION; + output_buf : JSAMPARRAY; + num_rows : int); + end; + +{ Color quantization or color precision reduction } + jpeg_color_quantizer_ptr = ^jpeg_color_quantizer; + jpeg_color_quantizer = record + start_pass : procedure(cinfo : j_decompress_ptr; is_pre_scan : boolean); + color_quantize : procedure(cinfo : j_decompress_ptr; + input_buf : JSAMPARRAY; + output_buf : JSAMPARRAY; + num_rows : int); + + finish_pass : procedure(cinfo : j_decompress_ptr); + new_color_map : procedure(cinfo : j_decompress_ptr); + end; + + {int8array = Array[0..8-1] of int;} + int8array = Array[0..8-1] of longint; { for TP FormatStr } + + jpeg_error_mgr = record + { Error exit handler: does not return to caller } + error_exit : procedure (cinfo : j_common_ptr); + { Conditionally emit a trace or warning message } + emit_message : procedure (cinfo : j_common_ptr; msg_level : int); + { Routine that actually outputs a trace or error message } + output_message : procedure (cinfo : j_common_ptr); + { Format a message string for the most recent JPEG error or message } + format_message : procedure (cinfo : j_common_ptr; var buffer : AnsiString); + + { Reset error state variables at start of a new image } + reset_error_mgr : procedure (cinfo : j_common_ptr); + + { The message ID code and any parameters are saved here. + A message can have one string parameter or up to 8 int parameters. } + + msg_code : int; + + msg_parm : record + case byte of + 0:(i : int8array); + 1:(s : string[JMSG_STR_PARM_MAX]); + end; + + { Standard state variables for error facility } + + trace_level : int; { 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. } + + num_warnings : long; { 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; } + jpeg_message_table : ^msg_table; { Library errors } + + last_jpeg_message : J_MESSAGE_CODE; + { 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; } + addon_message_table : ^msg_table; { Non-library errors } + + first_addon_message : J_MESSAGE_CODE; { code for first string in addon table } + last_addon_message : J_MESSAGE_CODE; { code for last string in addon table } + end; + + +{ Progress monitor object } + + jpeg_progress_mgr = record + progress_monitor : procedure(cinfo : j_common_ptr); + + pass_counter : long; { work units completed in this pass } + pass_limit : long; { total number of work units in this pass } + completed_passes : int; { passes completed so far } + total_passes : int; { total number of passes expected } + end; + + +{ Data destination object for compression } + jpeg_destination_mgr_ptr = ^jpeg_destination_mgr; + jpeg_destination_mgr = record + next_output_byte : JOCTETptr; { => next byte to write in buffer } + free_in_buffer : size_t; { # of byte spaces remaining in buffer } + + init_destination : procedure (cinfo : j_compress_ptr); + empty_output_buffer : function (cinfo : j_compress_ptr) : boolean; + term_destination : procedure (cinfo : j_compress_ptr); + end; + + +{ Data source object for decompression } + + jpeg_source_mgr_ptr = ^jpeg_source_mgr; + jpeg_source_mgr = record + {const JOCTET * next_input_byte;} + next_input_byte : JOCTETptr; { => next byte to read from buffer } + bytes_in_buffer : size_t; { # of bytes remaining in buffer } + + init_source : procedure (cinfo : j_decompress_ptr); + fill_input_buffer : function (cinfo : j_decompress_ptr) : boolean; + skip_input_data : procedure (cinfo : j_decompress_ptr; num_bytes : long); + resync_to_restart : function (cinfo : j_decompress_ptr; + desired : int) : boolean; + term_source : procedure (cinfo : j_decompress_ptr); + end; + + +{ 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 NIL. They exit to error_exit if not + successful. } + + + jpeg_memory_mgr = record + { Method pointers } + alloc_small : function (cinfo : j_common_ptr; pool_id : int; + sizeofobject : size_t) : pointer; + alloc_large : function (cinfo : j_common_ptr; pool_id : int; + sizeofobject : size_t) : pointer; {far} + alloc_sarray : function (cinfo : j_common_ptr; pool_id : int; + samplesperrow : JDIMENSION; + numrows : JDIMENSION) : JSAMPARRAY; + + alloc_barray : function (cinfo : j_common_ptr; pool_id : int; + blocksperrow : JDIMENSION; + numrows : JDIMENSION) : JBLOCKARRAY; + + request_virt_sarray : function(cinfo : j_common_ptr; + pool_id : int; + pre_zero : boolean; + samplesperrow : JDIMENSION; + numrows : JDIMENSION; + maxaccess : JDIMENSION) : jvirt_sarray_ptr; + + request_virt_barray : function(cinfo : j_common_ptr; + pool_id : int; + pre_zero : boolean; + blocksperrow : JDIMENSION; + numrows : JDIMENSION; + maxaccess : JDIMENSION) : jvirt_barray_ptr; + + realize_virt_arrays : procedure (cinfo : j_common_ptr); + + access_virt_sarray : function (cinfo : j_common_ptr; + ptr : jvirt_sarray_ptr; + start_row : JDIMENSION; + num_rows : JDIMENSION; + writable : boolean) : JSAMPARRAY; + + access_virt_barray : function (cinfo : j_common_ptr; + ptr : jvirt_barray_ptr; + start_row : JDIMENSION; + num_rows : JDIMENSION; + writable : boolean) : JBLOCKARRAY; + + free_pool : procedure (cinfo : j_common_ptr; pool_id : int); + self_destruct : procedure (cinfo : j_common_ptr); + + { 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. } + max_memory_to_use : long; + + { Maximum allocation request accepted by alloc_large. } + max_alloc_chunk : long; + end; + +{ 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.} + jpeg_common_struct = record + { Fields common to both master struct types } + err : jpeg_error_mgr_ptr; { Error handler module } + mem : jpeg_memory_mgr_ptr; { Memory manager module } + progress : jpeg_progress_mgr_ptr; { Progress monitor, or NIL if none } + client_data : voidp; { Available for use by application } + is_decompressor : boolean; { so common code can tell which is which } + global_state : int; { for checking call sequence validity } + + { 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++.) } + end; + + +{ Master record for a compression instance } + + jpeg_compress_struct = record + { Fields shared with jpeg_decompress_struct } + err : jpeg_error_mgr_ptr; { Error handler module } + mem : jpeg_memory_mgr_ptr; { Memory manager module } + progress : jpeg_progress_mgr_ptr; { Progress monitor, or NIL if none } + client_data : voidp; { Available for use by application } + is_decompressor : boolean; { so common code can tell which is which } + global_state : int; { for checking call sequence validity } + + { Destination for compressed data } + dest : jpeg_destination_mgr_ptr; + + { 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(). } + + + image_width : JDIMENSION; { input image width } + image_height : JDIMENSION; { input image height } + input_components : int; { # of color components in input image } + in_color_space : J_COLOR_SPACE; { colorspace of input image } + + input_gamma : double; { 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. } + + data_precision : int; { bits of precision in image data } + + num_components : int; { # of color components in JPEG image } + jpeg_color_space : J_COLOR_SPACE; { colorspace of JPEG image } + + comp_info : jpeg_component_info_list_ptr; + { comp_info^[i] describes component that appears i'th in SOF } + + quant_tbl_ptrs: Array[0..NUM_QUANT_TBLS-1] of JQUANT_TBL_PTR; + { ptrs to coefficient quantization tables, or NIL if not defined } + + dc_huff_tbl_ptrs : Array[0..NUM_HUFF_TBLS-1] of JHUFF_TBL_PTR; + ac_huff_tbl_ptrs : Array[0..NUM_HUFF_TBLS-1] of JHUFF_TBL_PTR; + { ptrs to Huffman coding tables, or NIL if not defined } + + arith_dc_L : Array[0..NUM_ARITH_TBLS-1] of UINT8; { L values for DC arith-coding tables } + arith_dc_U : Array[0..NUM_ARITH_TBLS-1] of UINT8; { U values for DC arith-coding tables } + arith_ac_K : Array[0..NUM_ARITH_TBLS-1] of UINT8; { Kx values for AC arith-coding tables } + + num_scans : int; { # of entries in scan_info array } + scan_info : jpeg_scan_info_ptr; { script for multi-scan file, or NIL } + { The default value of scan_info is NIL, 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. } + + raw_data_in : boolean; { TRUE=caller supplies downsampled data } + arith_code : boolean; { TRUE=arithmetic coding, FALSE=Huffman } + optimize_coding : boolean; { TRUE=optimize entropy encoding parms } + CCIR601_sampling : boolean; { TRUE=first samples are cosited } + smoothing_factor : int; { 1..100, or 0 for no input smoothing } + dct_method : J_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). } + + restart_interval : uint; { MCUs per restart, or 0 for no restart } + restart_in_rows : int; { if > 0, MCU rows per restart interval } + + { Parameters controlling emission of special markers. } + + write_JFIF_header : boolean; { should a JFIF marker be written? } + JFIF_major_version : UINT8; { What to write for the JFIF version number } + JFIF_minor_version : UINT8; + { 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. } + density_unit : UINT8; { JFIF code for pixel size units } + X_density : UINT16; { Horizontal pixel density } + Y_density : UINT16; { Vertical pixel density } + write_Adobe_marker : boolean; { 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)". } + + next_scanline : JDIMENSION; { 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 } + progressive_mode : boolean; { TRUE if scan script uses progressive mode } + max_h_samp_factor : int; { largest h_samp_factor } + max_v_samp_factor : int; { largest v_samp_factor } + + total_iMCU_rows : JDIMENSION; { # 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. } + + comps_in_scan : int; { # of JPEG components in this scan } + cur_comp_info : Array[0..MAX_COMPS_IN_SCAN-1] of jpeg_component_info_ptr; + { cur_comp_info[i]^ describes component that appears i'th in SOS } + + MCUs_per_row : JDIMENSION; { # of MCUs across the image } + MCU_rows_in_scan : JDIMENSION;{ # of MCU rows in the image } + + blocks_in_MCU : int; { # of DCT blocks per MCU } + MCU_membership : Array[0..C_MAX_BLOCKS_IN_MCU-1] of int; + { MCU_membership[i] is index in cur_comp_info of component owning } + { i'th block in an MCU } + + Ss, Se, Ah, Al : int; { progressive JPEG parameters for scan } + + { Links to compression subobjects (methods and private variables of modules) } + master : jpeg_comp_master_ptr; + main : jpeg_c_main_controller_ptr; + prep : jpeg_c_prep_controller_ptr; + coef : jpeg_c_coef_controller_ptr; + marker : jpeg_marker_writer_ptr; + cconvert : jpeg_color_converter_ptr; + downsample : jpeg_downsampler_ptr; + fdct : jpeg_forward_dct_ptr; + entropy : jpeg_entropy_encoder_ptr; + script_space : jpeg_scan_info_ptr; { workspace for jpeg_simple_progression } + script_space_size : int; + end; + + +{ Master record for a decompression instance } + + coef_bits_field = Array[0..DCTSIZE2-1] of int; + coef_bits_ptr = ^coef_bits_field; + coef_bits_ptrfield = Array[0..MAX_COMPS_IN_SCAN-1] of coef_bits_field; + coef_bits_ptrrow = ^coef_bits_ptrfield; + + range_limit_table = array[-(MAXJSAMPLE+1)..4*(MAXJSAMPLE+1) + + CENTERJSAMPLE -1] of JSAMPLE; + range_limit_table_ptr = ^range_limit_table; + + jpeg_decompress_struct = record + { Fields shared with jpeg_compress_struct } + err : jpeg_error_mgr_ptr; { Error handler module } + mem : jpeg_memory_mgr_ptr; { Memory manager module } + progress : jpeg_progress_mgr_ptr; { Progress monitor, or NIL if none } + client_data : voidp; { Available for use by application } + is_decompressor : boolean; { so common code can tell which is which } + global_state : int; { for checking call sequence validity } + + { Source of compressed data } + src : jpeg_source_mgr_ptr; + + { Basic description of image --- filled in by jpeg_read_header(). } + { Application may inspect these values to decide how to process image. } + + image_width : JDIMENSION; { nominal image width (from SOF marker) } + image_height : JDIMENSION; { nominal image height } + num_components : int; { # of color components in JPEG image } + jpeg_color_space : J_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. } + + out_color_space : J_COLOR_SPACE; { colorspace for output } + + scale_num, scale_denom : uint ; { fraction by which to scale image } + + output_gamma : double; { image gamma wanted in output } + + buffered_image : boolean; { TRUE=multiple output passes } + raw_data_out : boolean; { TRUE=downsampled data wanted } + + dct_method : J_DCT_METHOD; { IDCT algorithm selector } + do_fancy_upsampling : boolean; { TRUE=apply fancy upsampling } + do_block_smoothing : boolean; { TRUE=apply interblock smoothing } + + quantize_colors : boolean; { TRUE=colormapped output wanted } + { the following are ignored if not quantize_colors: } + dither_mode : J_DITHER_MODE; { type of color dithering to use } + two_pass_quantize : boolean; { TRUE=use two-pass color quantization } + desired_number_of_colors : int; { max # colors to use in created colormap } + { these are significant only in buffered-image mode: } + enable_1pass_quant : boolean; { enable future use of 1-pass quantizer } + enable_external_quant : boolean; { enable future use of external colormap } + enable_2pass_quant : boolean; { 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(). } + + output_width : JDIMENSION; { scaled image width } + output_height: JDIMENSION; { scaled image height } + out_color_components : int; { # of color components in out_color_space } + output_components : int; { # of color components returned } + { output_components is 1 (a colormap index) when quantizing colors; + otherwise it equals out_color_components. } + + rec_outbuf_height : int; { 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-NIL 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. } + + actual_number_of_colors : int; { number of entries in use } + colormap : JSAMPARRAY; { 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)". } + + output_scanline : JDIMENSION; { 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. } + + input_scan_number : int; { Number of SOS markers seen so far } + input_iMCU_row : JDIMENSION; { 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.} + + output_scan_number : int; { Nominal scan number being displayed } + output_iMCU_row : int; { 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 NIL when reading a non-progressive file. } + + coef_bits : coef_bits_ptrrow; + { -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. } + + quant_tbl_ptrs : Array[0..NUM_QUANT_TBLS-1] of JQUANT_TBL_PTR; + { ptrs to coefficient quantization tables, or NIL if not defined } + + dc_huff_tbl_ptrs : Array[0..NUM_HUFF_TBLS-1] of JHUFF_TBL_PTR; + ac_huff_tbl_ptrs : Array[0..NUM_HUFF_TBLS-1] of JHUFF_TBL_PTR; + { ptrs to Huffman coding tables, or NIL 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. } + + data_precision : int; { bits of precision in image data } + + comp_info : jpeg_component_info_list_ptr; + { comp_info^[i] describes component that appears i'th in SOF } + + progressive_mode : boolean; { TRUE if SOFn specifies progressive mode } + arith_code : boolean; { TRUE=arithmetic coding, FALSE=Huffman } + + arith_dc_L : Array[0..NUM_ARITH_TBLS-1] of UINT8; { L values for DC arith-coding tables } + arith_dc_U : Array[0..NUM_ARITH_TBLS-1] of UINT8; { U values for DC arith-coding tables } + arith_ac_K : Array[0..NUM_ARITH_TBLS-1] of UINT8; { Kx values for AC arith-coding tables } + + restart_interval : uint; { MCUs per restart interval, or 0 for no restart } + + { These fields record data obtained from optional markers recognized by + the JPEG library. } + + saw_JFIF_marker : boolean; { TRUE iff a JFIF APP0 marker was found } + { Data copied from JFIF marker; only valid if saw_JFIF_marker is TRUE: } + JFIF_major_version : UINT8; { JFIF version number } + JFIF_minor_version : UINT8; + density_unit : UINT8; { JFIF code for pixel size units } + X_density : UINT16; { Horizontal pixel density } + Y_density : UINT16; { Vertical pixel density } + saw_Adobe_marker : boolean; { TRUE iff an Adobe APP14 marker was found } + Adobe_transform : UINT8; { Color transform code from Adobe marker } + + CCIR601_sampling : boolean; { 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. } + + marker_list : jpeg_saved_marker_ptr; { 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 } + + max_h_samp_factor : int; { largest h_samp_factor } + max_v_samp_factor : int; { largest v_samp_factor } + + min_DCT_scaled_size : int; { smallest DCT_scaled_size of any component } + + total_iMCU_rows : JDIMENSION; { # 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.} + + sample_range_limit : range_limit_table_ptr; { 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. } + + comps_in_scan : int; { # of JPEG components in this scan } + cur_comp_info : Array[0..MAX_COMPS_IN_SCAN-1] of jpeg_component_info_ptr; + { cur_comp_info[i]^ describes component that appears i'th in SOS } + + MCUs_per_row : JDIMENSION; { # of MCUs across the image } + MCU_rows_in_scan : JDIMENSION; { # of MCU rows in the image } + + blocks_in_MCU : JDIMENSION; { # of DCT blocks per MCU } + MCU_membership : Array[0..D_MAX_BLOCKS_IN_MCU-1] of int; + { MCU_membership[i] is index in cur_comp_info of component owning } + { i'th block in an MCU } + + Ss, Se, Ah, Al : int; { 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. } + + unread_marker : int; + + { Links to decompression subobjects + (methods, private variables of modules) } + + master : jpeg_decomp_master_ptr; + main : jpeg_d_main_controller_ptr; + coef : jpeg_d_coef_controller_ptr; + post : jpeg_d_post_controller_ptr; + inputctl : jpeg_input_controller_ptr; + marker : jpeg_marker_reader_ptr; + entropy : jpeg_entropy_decoder_ptr; + idct : jpeg_inverse_dct_ptr; + upsample : jpeg_upsampler_ptr; + cconvert : jpeg_color_deconverter_ptr; + cquantize : jpeg_color_quantizer_ptr; + end; + +{ Decompression startup: read start of JPEG datastream to see what's there + function jpeg_read_header (cinfo : j_decompress_ptr; + require_image : boolean) : int; + Return value is one of: } +const + JPEG_SUSPENDED = 0; { Suspended due to lack of input data } + JPEG_HEADER_OK = 1; { Found valid image datastream } + 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). } + + +{ function jpeg_consume_input (cinfo : j_decompress_ptr) : int; + Return value is one of: } + + JPEG_REACHED_SOS = 1; { Reached start of new scan } + JPEG_REACHED_EOI = 2; { Reached end of image } + JPEG_ROW_COMPLETED = 3; { Completed one iMCU row } + JPEG_SCAN_COMPLETED = 4; { Completed last iMCU row of a scan } + + + + +implementation + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjquant1.pas b/resources/libraries/deskew/Imaging/JpegLib/imjquant1.pas new file mode 100755 index 0000000..f15afa0 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjquant1.pas @@ -0,0 +1,1009 @@ +unit imjquant1; + +{ 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. } + +{ Original: jquant1.c; Copyright (C) 1991-1996, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjpeglib; + +{GLOBAL} +procedure jinit_1pass_quantizer (cinfo : j_decompress_ptr); + +implementation + +uses + imjmorecfg, + imjdeferr, + imjerror, + imjutils; + +{ 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. } + + +const + ODITHER_SIZE = 16; { dimension of dither matrix } +{ NB: if ODITHER_SIZE is not a power of 2, ODITHER_MASK uses will break } + ODITHER_CELLS = (ODITHER_SIZE*ODITHER_SIZE); { # cells in matrix } + ODITHER_MASK = (ODITHER_SIZE-1); { mask for wrapping around counters } + +type + ODITHER_vector = Array[0..ODITHER_SIZE-1] of int; + ODITHER_MATRIX = Array[0..ODITHER_SIZE-1] of ODITHER_vector; + {ODITHER_MATRIX_PTR = ^array[0..ODITHER_SIZE-1] of int;} + ODITHER_MATRIX_PTR = ^ODITHER_MATRIX; + +const + base_dither_matrix : Array[0..ODITHER_SIZE-1,0..ODITHER_SIZE-1] of UINT8 + = ( + { 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. } + +{$ifdef BITS_IN_JSAMPLE_IS_8} +type + FSERROR = INT16; { 16 bits should be enough } + LOCFSERROR = int; { use 'int' for calculation temps } +{$else} +type + FSERROR = INT32; { may need more than 16 bits } + LOCFSERROR = INT32; { be sure calculation temps are big enough } +{$endif} + +type + jFSError = 0..(MaxInt div SIZEOF(FSERROR))-1; + FS_ERROR_FIELD = array[jFSError] of FSERROR; + FS_ERROR_FIELD_PTR = ^FS_ERROR_FIELD;{far} + { pointer to error array (in FAR storage!) } + FSERRORPTR = ^FSERROR; + + +{ Private subobject } + +const + MAX_Q_COMPS = 4; { max components I can handle } + +type + my_cquantize_ptr = ^my_cquantizer; + my_cquantizer = record + pub : jpeg_color_quantizer; { public fields } + + { Initially allocated colormap is saved here } + sv_colormap : JSAMPARRAY; { The color map as a 2-D pixel array } + sv_actual : int; { number of entries in use } + + colorindex : JSAMPARRAY; { 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. } + + is_padded : boolean; { is the colorindex padded for odither? } + + Ncolors : array[0..MAX_Q_COMPS-1] of int; + { # of values alloced to each component } + + { Variables for ordered dithering } + row_index : int; { cur row's vertical index in dither matrix } + odither : array[0..MAX_Q_COMPS-1] of ODITHER_MATRIX_PTR; + { one dither array per component } + { Variables for Floyd-Steinberg dithering } + fserrors : array[0..MAX_Q_COMPS-1] of FS_ERROR_FIELD_PTR; + { accumulated errors } + on_odd_row : boolean; { flag to remember which row we are on } + end; + + +{ 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} +function select_ncolors (cinfo : j_decompress_ptr; + var Ncolors : array of int) : int; +{ 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). } +var + nc : int; + max_colors : int; + total_colors, iroot, i, j : int; + changed : boolean; + temp : long; +const + RGB_order:array[0..2] of int = (RGB_GREEN, RGB_RED, RGB_BLUE); +begin + nc := cinfo^.out_color_components; { number of color components } + max_colors := cinfo^.desired_number_of_colors; + + { We can allocate at least the nc'th root of max_colors per component. } + { Compute floor(nc'th root of max_colors). } + iroot := 1; + repeat + Inc(iroot); + temp := iroot; { set temp = iroot ** nc } + for i := 1 to pred(nc) do + temp := temp * iroot; + until (temp > long(max_colors)); { repeat till iroot exceeds root } + Dec(iroot); { now iroot = floor(root) } + + { Must have at least 2 color values per component } + if (iroot < 2) then + ERREXIT1(j_common_ptr(cinfo), JERR_QUANT_FEW_COLORS, int(temp)); + + { Initialize to iroot color values for each component } + total_colors := 1; + for i := 0 to pred(nc) do + begin + Ncolors[i] := iroot; + total_colors := total_colors * iroot; + end; + + { 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. } + + repeat + changed := FALSE; + for i := 0 to pred(nc) do + begin + if cinfo^.out_color_space = JCS_RGB then + j := RGB_order[i] + else + j := i; + { calculate new total_colors if Ncolors[j] is incremented } + temp := total_colors div Ncolors[j]; + temp := temp * (Ncolors[j]+1); { done in long arith to avoid oflo } + if (temp > long(max_colors)) then + break; { won't fit, done with this pass } + Inc(Ncolors[j]); { OK, apply the increment } + total_colors := int(temp); + changed := TRUE; + end; + until not changed; + + select_ncolors := total_colors; +end; + + +{LOCAL} +function output_value (cinfo : j_decompress_ptr; + ci : int; j : int; maxj : int) : int; +{ Return j'th output value, where j will range from 0 to maxj } +{ The output values must fall in 0..MAXJSAMPLE in increasing order } +begin + { 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.) } + + output_value := int (( INT32(j) * MAXJSAMPLE + maxj div 2) div maxj); +end; + + +{LOCAL} +function largest_input_value (cinfo : j_decompress_ptr; + ci : int; j : int; maxj : int) : int; +{ Return largest input value that should map to j'th output value } +{ Must have largest(j=0) >= 0, and largest(j=maxj) >= MAXJSAMPLE } +begin + { Breakpoints are halfway between values returned by output_value } + largest_input_value := int (( INT32(2*j + 1) * MAXJSAMPLE + + maxj) div (2*maxj)); +end; + + +{ Create the colormap. } + +{LOCAL} +procedure create_colormap (cinfo : j_decompress_ptr); +var + cquantize : my_cquantize_ptr; + colormap : JSAMPARRAY; { Created colormap } + + total_colors : int; { Number of distinct output colors } + i,j,k, nci, blksize, blkdist, ptr, val : int; +begin + cquantize := my_cquantize_ptr (cinfo^.cquantize); + + { Select number of colors for each component } + total_colors := select_ncolors(cinfo, cquantize^.Ncolors); + + { Report selected color counts } + {$IFDEF DEBUG} + if (cinfo^.out_color_components = 3) then + TRACEMS4(j_common_ptr(cinfo), 1, JTRC_QUANT_3_NCOLORS, + total_colors, cquantize^.Ncolors[0], + cquantize^.Ncolors[1], cquantize^.Ncolors[2]) + else + TRACEMS1(j_common_ptr(cinfo), 1, JTRC_QUANT_NCOLORS, total_colors); + {$ENDIF} + + { 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 to pred(cinfo^.out_color_components) do + begin + { fill in colormap entries for i'th color component } + nci := cquantize^.Ncolors[i]; { # of distinct values for this color } + blksize := blkdist div nci; + for j := 0 to pred(nci) do + begin + { 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 } + ptr := j * blksize; + while (ptr < total_colors) do + begin + { fill in blksize entries beginning at ptr } + for k := 0 to pred(blksize) do + colormap^[i]^[ptr+k] := JSAMPLE(val); + + Inc(ptr, blkdist); + end; + end; + blkdist := blksize; { blksize of this color is blkdist of next } + end; + + { Save the colormap in private storage, + where it will survive color quantization mode changes. } + + cquantize^.sv_colormap := colormap; + cquantize^.sv_actual := total_colors; +end; + +{ Create the color index table. } + +{LOCAL} +procedure create_colorindex (cinfo : j_decompress_ptr); +var + cquantize : my_cquantize_ptr; + indexptr, + help_indexptr : JSAMPROW; { for negative offsets } + i,j,k, nci, blksize, val, pad : int; +begin + cquantize := my_cquantize_ptr (cinfo^.cquantize); + { 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) then + begin + pad := MAXJSAMPLE*2; + cquantize^.is_padded := TRUE; + end + else + begin + pad := 0; + cquantize^.is_padded := FALSE; + end; + + 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 to pred(cinfo^.out_color_components) do + begin + { fill in colorindex entries for i'th color component } + nci := cquantize^.Ncolors[i]; { # of distinct values for this color } + blksize := blksize div nci; + + { adjust colorindex pointers to provide padding at negative indexes. } + if (pad <> 0) then + Inc(JSAMPLE_PTR(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 to MAXJSAMPLE do + begin + while (j > k) do { advance val if past boundary } + begin + Inc(val); + k := largest_input_value(cinfo, i, val, nci-1); + end; + { premultiply so that no multiplication needed in main processing } + indexptr^[j] := JSAMPLE (val * blksize); + end; + { Pad at both ends if necessary } + if (pad <> 0) then + begin + help_indexptr := indexptr; + { adjust the help pointer to avoid negative offsets } + Dec(JSAMPLE_PTR(help_indexptr), MAXJSAMPLE); + + for j := 1 to MAXJSAMPLE do + begin + {indexptr^[-j] := indexptr^[0];} + help_indexptr^[MAXJSAMPLE-j] := indexptr^[0]; + indexptr^[MAXJSAMPLE+j] := indexptr^[MAXJSAMPLE]; + end; + end; + end; +end; + + +{ Create an ordered-dither array for a component having ncolors + distinct output values. } + +{LOCAL} +function make_odither_array (cinfo : j_decompress_ptr; + ncolors : int) : ODITHER_MATRIX_PTR; +var + odither : ODITHER_MATRIX_PTR; + j, k : int; + num, den : INT32; +begin + 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 to pred(ODITHER_SIZE) do + begin + for k := 0 to pred(ODITHER_SIZE) do + begin + 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... } + + if num<0 then + odither^[j][k] := int (-((-num) div den)) + else + odither^[j][k] := int (num div den); + end; + end; + make_odither_array := odither; +end; + + +{ Create the ordered-dither tables. + Components having the same number of representative colors may + share a dither table. } + +{LOCAL} +procedure create_odither_tables (cinfo : j_decompress_ptr); +var + cquantize : my_cquantize_ptr; + odither : ODITHER_MATRIX_PTR; + i, j, nci : int; +begin + cquantize := my_cquantize_ptr (cinfo^.cquantize); + + for i := 0 to pred(cinfo^.out_color_components) do + begin + nci := cquantize^.Ncolors[i]; { # of distinct values for this color } + odither := NIL; { search for matching prior component } + for j := 0 to pred(i) do + begin + if (nci = cquantize^.Ncolors[j]) then + begin + odither := cquantize^.odither[j]; + break; + end; + end; + if (odither = NIL) then { need a new table? } + odither := make_odither_array(cinfo, nci); + cquantize^.odither[i] := odither; + end; +end; + + +{ Map some rows of pixels to the output colormapped representation. } + +{METHODDEF} +procedure color_quantize (cinfo : j_decompress_ptr; + input_buf : JSAMPARRAY; + output_buf : JSAMPARRAY; + num_rows : int); +{ General case, no dithering } +var + cquantize : my_cquantize_ptr; + colorindex : JSAMPARRAY; + pixcode, ci : int; {register} + ptrin, ptrout : JSAMPLE_PTR; {register} + row : int; + col : JDIMENSION; + width : JDIMENSION; + nc : int; {register} +begin + cquantize := my_cquantize_ptr (cinfo^.cquantize); + colorindex := cquantize^.colorindex; + width := cinfo^.output_width; + nc := cinfo^.out_color_components; + + for row := 0 to pred(num_rows) do + begin + ptrin := JSAMPLE_PTR(input_buf^[row]); + ptrout := JSAMPLE_PTR(output_buf^[row]); + for col := pred(width) downto 0 do + begin + pixcode := 0; + for ci := 0 to pred(nc) do + begin + Inc(pixcode, GETJSAMPLE(colorindex^[ci]^[GETJSAMPLE(ptrin^)]) ); + Inc(ptrin); + end; + ptrout^ := JSAMPLE (pixcode); + Inc(ptrout); + end; + end; +end; + + +{METHODDEF} +procedure color_quantize3 (cinfo : j_decompress_ptr; + input_buf : JSAMPARRAY; + output_buf : JSAMPARRAY; + num_rows : int); +{ Fast path for out_color_components=3, no dithering } +var + cquantize : my_cquantize_ptr; + pixcode : int; {register} + ptrin, ptrout : JSAMPLE_PTR; {register} + colorindex0 : JSAMPROW; + colorindex1 : JSAMPROW; + colorindex2 : JSAMPROW; + row : int; + col : JDIMENSION; + width : JDIMENSION; +begin + cquantize := my_cquantize_ptr (cinfo^.cquantize); + colorindex0 := (cquantize^.colorindex)^[0]; + colorindex1 := (cquantize^.colorindex)^[1]; + colorindex2 := (cquantize^.colorindex)^[2]; + width := cinfo^.output_width; + + for row := 0 to pred(num_rows) do + begin + ptrin := JSAMPLE_PTR(input_buf^[row]); + ptrout := JSAMPLE_PTR(output_buf^[row]); + for col := pred(width) downto 0 do + begin + pixcode := GETJSAMPLE((colorindex0)^[GETJSAMPLE(ptrin^)]); + Inc(ptrin); + Inc( pixcode, GETJSAMPLE((colorindex1)^[GETJSAMPLE(ptrin^)]) ); + Inc(ptrin); + Inc( pixcode, GETJSAMPLE((colorindex2)^[GETJSAMPLE(ptrin^)]) ); + Inc(ptrin); + ptrout^ := JSAMPLE (pixcode); + Inc(ptrout); + end; + end; +end; + + +{METHODDEF} +procedure quantize_ord_dither (cinfo : j_decompress_ptr; + input_buf : JSAMPARRAY; + output_buf : JSAMPARRAY; + num_rows : int); +{ General case, with ordered dithering } +var + cquantize : my_cquantize_ptr; + input_ptr, {register} + output_ptr : JSAMPLE_PTR; {register} + colorindex_ci : JSAMPROW; + dither : ^ODITHER_vector; { points to active row of dither matrix } + row_index, col_index : int; { current indexes into dither matrix } + nc : int; + ci : int; + row : int; + col : JDIMENSION; + width : JDIMENSION; +var + pad_offset : int; +begin + cquantize := my_cquantize_ptr (cinfo^.cquantize); + nc := cinfo^.out_color_components; + width := cinfo^.output_width; + + { Nomssi: work around negative offset } + if my_cquantize_ptr (cinfo^.cquantize)^.is_padded then + pad_offset := MAXJSAMPLE + else + pad_offset := 0; + + for row := 0 to pred(num_rows) do + begin + { Initialize output values to 0 so can process components separately } + jzero_far( {far} pointer(output_buf^[row]), + size_t(width * SIZEOF(JSAMPLE))); + row_index := cquantize^.row_index; + for ci := 0 to pred(nc) do + begin + input_ptr := JSAMPLE_PTR(@ input_buf^[row]^[ci]); + output_ptr := JSAMPLE_PTR(output_buf^[row]); + colorindex_ci := cquantize^.colorindex^[ci]; + { Nomssi } + Dec(JSAMPLE_PTR(colorindex_ci), pad_offset); + + dither := @(cquantize^.odither[ci]^[row_index]); + col_index := 0; + + for col := pred(width) downto 0 do + begin + { 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. } + + Inc(output_ptr^, + colorindex_ci^[GETJSAMPLE(input_ptr^)+ pad_offset + + dither^[col_index]]); + Inc(output_ptr); + Inc(input_ptr, nc); + col_index := (col_index + 1) and ODITHER_MASK; + end; + end; + { Advance row index for next row } + row_index := (row_index + 1) and ODITHER_MASK; + cquantize^.row_index := row_index; + end; +end; + +{METHODDEF} +procedure quantize3_ord_dither (cinfo : j_decompress_ptr; + input_buf : JSAMPARRAY; + output_buf : JSAMPARRAY; + num_rows : int); +{ Fast path for out_color_components=3, with ordered dithering } +var + cquantize : my_cquantize_ptr; + pixcode : int; {register} + input_ptr : JSAMPLE_PTR; {register} + output_ptr : JSAMPLE_PTR; {register} + colorindex0 : JSAMPROW; + colorindex1 : JSAMPROW; + colorindex2 : JSAMPROW; + dither0 : ^ODITHER_vector; { points to active row of dither matrix } + dither1 : ^ODITHER_vector; + dither2 : ^ODITHER_vector; + row_index, col_index : int; { current indexes into dither matrix } + row : int; + col : JDIMENSION; + width : JDIMENSION; +var + pad_offset : int; +begin + cquantize := my_cquantize_ptr (cinfo^.cquantize); + colorindex0 := (cquantize^.colorindex)^[0]; + colorindex1 := (cquantize^.colorindex)^[1]; + colorindex2 := (cquantize^.colorindex)^[2]; + width := cinfo^.output_width; + + { Nomssi: work around negative offset } + if my_cquantize_ptr (cinfo^.cquantize)^.is_padded then + pad_offset := MAXJSAMPLE + else + pad_offset := 0; + + Dec(JSAMPLE_PTR(colorindex0), pad_offset); + Dec(JSAMPLE_PTR(colorindex1), pad_offset); + Dec(JSAMPLE_PTR(colorindex2), pad_offset); + + for row := 0 to pred(num_rows) do + begin + row_index := cquantize^.row_index; + input_ptr := JSAMPLE_PTR(input_buf^[row]); + output_ptr := JSAMPLE_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 := pred(width) downto 0 do + begin + pixcode := GETJSAMPLE(colorindex0^[GETJSAMPLE(input_ptr^) + pad_offset + + dither0^[col_index]]); + Inc(input_ptr); + Inc(pixcode, GETJSAMPLE(colorindex1^[GETJSAMPLE(input_ptr^) + pad_offset + + dither1^[col_index]])); + Inc(input_ptr); + Inc(pixcode, GETJSAMPLE(colorindex2^[GETJSAMPLE(input_ptr^) + pad_offset + + dither2^[col_index]])); + Inc(input_ptr); + output_ptr^ := JSAMPLE (pixcode); + Inc(output_ptr); + col_index := (col_index + 1) and ODITHER_MASK; + end; + row_index := (row_index + 1) and ODITHER_MASK; + cquantize^.row_index := row_index; + end; +end; + + +{METHODDEF} +procedure quantize_fs_dither (cinfo : j_decompress_ptr; + input_buf : JSAMPARRAY; + output_buf : JSAMPARRAY; + num_rows : int); +{ General case, with Floyd-Steinberg dithering } +var + cquantize : my_cquantize_ptr; + cur : LOCFSERROR; {register} { current error or pixel value } + belowerr : LOCFSERROR; { error for pixel below cur } + bpreverr : LOCFSERROR; { error for below/prev col } + bnexterr : LOCFSERROR; { error for below/next col } + delta : LOCFSERROR; + prev_errorptr, + errorptr : FSERRORPTR; {register} { => fserrors[] at column before current } + input_ptr, {register} + output_ptr : JSAMPLE_PTR; {register} + colorindex_ci : JSAMPROW; + colormap_ci : JSAMPROW; + pixcode : int; + nc : int; + dir : int; { 1 for left-to-right, -1 for right-to-left } + dirnc : int; { dir * nc } + ci : int; + row : int; + col : JDIMENSION; + width : JDIMENSION; + range_limit : range_limit_table_ptr; +begin + cquantize := my_cquantize_ptr (cinfo^.cquantize); + nc := cinfo^.out_color_components; + width := cinfo^.output_width; + range_limit := cinfo^.sample_range_limit; + + for row := 0 to pred(num_rows) do + begin + { Initialize output values to 0 so can process components separately } + jzero_far( (output_buf)^[row], + size_t(width * SIZEOF(JSAMPLE))); + for ci := 0 to pred(nc) do + begin + input_ptr := JSAMPLE_PTR(@ input_buf^[row]^[ci]); + output_ptr := JSAMPLE_PTR(output_buf^[row]); + errorptr := FSERRORPTR(cquantize^.fserrors[ci]); { => entry before first column } + if (cquantize^.on_odd_row) then + begin + { work right to left in this row } + Inc(input_ptr, (width-1) * JDIMENSION(nc)); { so point to rightmost pixel } + Inc(output_ptr, width-1); + dir := -1; + dirnc := -nc; + Inc(errorptr, (width+1)); { => entry after last column } + end + else + begin + { work left to right in this row } + dir := 1; + dirnc := nc; + {errorptr := cquantize^.fserrors[ci];} + end; + + 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 := 0; + bpreverr := 0; + + for col := pred(width) downto 0 do + begin + prev_errorptr := errorptr; + Inc(errorptr, dir); { advance errorptr to current column } + + { 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 := (cur + errorptr^ + 8) div 16; + + { 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. } + + Inc( cur, GETJSAMPLE(input_ptr^)); + cur := GETJSAMPLE(range_limit^[cur]); + { Select output value, accumulate into output code for this pixel } + pixcode := GETJSAMPLE(colorindex_ci^[cur]); + Inc(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. } + Dec(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; + Inc(cur, delta); { form error * 3 } + prev_errorptr^ := FSERROR (bpreverr + cur); + Inc(cur, delta); { form error * 5 } + bpreverr := belowerr + cur; + belowerr := bnexterr; + Inc(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. } + + Inc(input_ptr, dirnc); { advance input ptr to next column } + Inc(output_ptr, dir); { advance output ptr to next column } + + end; + { 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^ := FSERROR (bpreverr); { unload prev err into array } + { Nomssi : ?? } + end; + cquantize^.on_odd_row := not cquantize^.on_odd_row; + end; +end; + + +{ Allocate workspace for Floyd-Steinberg errors. } + +{LOCAL} +procedure alloc_fs_workspace (cinfo : j_decompress_ptr); +var + cquantize : my_cquantize_ptr; + arraysize : size_t; + i : int; +begin + cquantize := my_cquantize_ptr (cinfo^.cquantize); + arraysize := size_t ((cinfo^.output_width + 2) * SIZEOF(FSERROR)); + for i := 0 to pred(cinfo^.out_color_components) do + begin + cquantize^.fserrors[i] := FS_ERROR_FIELD_PTR( + cinfo^.mem^.alloc_large(j_common_ptr(cinfo), JPOOL_IMAGE, arraysize)); + end; +end; + + +{ Initialize for one-pass color quantization. } + +{METHODDEF} +procedure start_pass_1_quant (cinfo : j_decompress_ptr; + is_pre_scan : boolean); +var + cquantize : my_cquantize_ptr; + arraysize : size_t; + i : int; +begin + cquantize := my_cquantize_ptr (cinfo^.cquantize); + { Install my colormap. } + cinfo^.colormap := cquantize^.sv_colormap; + cinfo^.actual_number_of_colors := cquantize^.sv_actual; + + { Initialize for desired dithering mode. } + case (cinfo^.dither_mode) of + JDITHER_NONE: + if (cinfo^.out_color_components = 3) then + cquantize^.pub.color_quantize := color_quantize3 + else + cquantize^.pub.color_quantize := color_quantize; + JDITHER_ORDERED: + begin + if (cinfo^.out_color_components = 3) then + 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 (not cquantize^.is_padded) then + create_colorindex(cinfo); + { Create ordered-dither tables if we didn't already. } + if (cquantize^.odither[0] = NIL) then + create_odither_tables(cinfo); + end; + JDITHER_FS: + begin + 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] = NIL) then + alloc_fs_workspace(cinfo); + { Initialize the propagated errors to zero. } + arraysize := size_t ((cinfo^.output_width + 2) * SIZEOF(FSERROR)); + for i := 0 to pred(cinfo^.out_color_components) do + jzero_far({far} pointer( cquantize^.fserrors[i] ), arraysize); + end; + else + ERREXIT(j_common_ptr(cinfo), JERR_NOT_COMPILED); + end; +end; + + +{ Finish up at the end of the pass. } + +{METHODDEF} +procedure finish_pass_1_quant (cinfo : j_decompress_ptr); +begin + { no work in 1-pass case } +end; + + +{ Switch to a new external colormap between output passes. + Shouldn't get to this module! } + +{METHODDEF} +procedure new_color_map_1_quant (cinfo : j_decompress_ptr); +begin + ERREXIT(j_common_ptr(cinfo), JERR_MODE_CHANGE); +end; + + +{ Module initialization routine for 1-pass color quantization. } + +{GLOBAL} +procedure jinit_1pass_quantizer (cinfo : j_decompress_ptr); +var + cquantize : my_cquantize_ptr; +begin + cquantize := my_cquantize_ptr( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + SIZEOF(my_cquantizer))); + cinfo^.cquantize := jpeg_color_quantizer_ptr(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] := NIL; { Flag FS workspace not allocated } + cquantize^.odither[0] := NIL; { Also flag odither arrays not allocated } + + { Make sure my internal arrays won't overflow } + if (cinfo^.out_color_components > MAX_Q_COMPS) then + ERREXIT1(j_common_ptr(cinfo), JERR_QUANT_COMPONENTS, MAX_Q_COMPS); + { Make sure colormap indexes can be represented by JSAMPLEs } + if (cinfo^.desired_number_of_colors > (MAXJSAMPLE+1)) then + ERREXIT1(j_common_ptr(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) then + alloc_fs_workspace(cinfo); +end; + + +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjquant2.pas b/resources/libraries/deskew/Imaging/JpegLib/imjquant2.pas new file mode 100755 index 0000000..a1e7a44 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjquant2.pas @@ -0,0 +1,1551 @@ +unit imjquant2; + + +{ 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. } + +{ Original: jquant2.c; Copyright (C) 1991-1996, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjdeferr, + imjerror, + imjutils, + imjpeglib; + +{ Module initialization routine for 2-pass color quantization. } + + +{GLOBAL} +procedure jinit_2pass_quantizer (cinfo : j_decompress_ptr); + +implementation + +{ 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. } + +const + R_SCALE = 2; { scale R distances by this much } + G_SCALE = 3; { scale G distances by this much } + 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. } + +{$ifdef RGB_RED_IS_0} +const + C0_SCALE = R_SCALE; + C1_SCALE = G_SCALE; + C2_SCALE = B_SCALE; +{$else} +const + C0_SCALE = B_SCALE; + C1_SCALE = G_SCALE; + C2_SCALE = R_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). } + + +const + 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. } + +const + HIST_C0_BITS = 5; { bits of precision in R/B histogram } + HIST_C1_BITS = 6; { bits of precision in G histogram } + HIST_C2_BITS = 5; { bits of precision in B/R histogram } + +{ Number of elements along histogram axes. } +const + HIST_C0_ELEMS = (1 shl HIST_C0_BITS); + HIST_C1_ELEMS = (1 shl HIST_C1_BITS); + HIST_C2_ELEMS = (1 shl HIST_C2_BITS); + +{ These are the amounts to shift an input value to get a histogram index. } +const + C0_SHIFT = (BITS_IN_JSAMPLE-HIST_C0_BITS); + C1_SHIFT = (BITS_IN_JSAMPLE-HIST_C1_BITS); + C2_SHIFT = (BITS_IN_JSAMPLE-HIST_C2_BITS); + + +type { Nomssi } + RGBptr = ^RGBtype; + RGBtype = packed record + r,g,b : JSAMPLE; + end; +type + histcell = UINT16; { histogram cell; prefer an unsigned type } + +type + histptr = ^histcell {FAR}; { for pointers to histogram cells } + +type + hist1d = array[0..HIST_C2_ELEMS-1] of histcell; { typedefs for the array } + {hist1d_ptr = ^hist1d;} + hist1d_field = array[0..HIST_C1_ELEMS-1] of hist1d; + { type for the 2nd-level pointers } + hist2d = ^hist1d_field; + hist2d_field = array[0..HIST_C0_ELEMS-1] of hist2d; + hist3d = ^hist2d_field; { 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. } + + +{$ifdef BITS_IN_JSAMPLE_IS_8} +type + FSERROR = INT16; { 16 bits should be enough } + LOCFSERROR = int; { use 'int' for calculation temps } +{$else} +type + FSERROR = INT32; { may need more than 16 bits } + LOCFSERROR = INT32; { be sure calculation temps are big enough } +{$endif} +type { Nomssi } + RGB_FSERROR_PTR = ^RGB_FSERROR; + RGB_FSERROR = packed record + r,g,b : FSERROR; + end; + LOCRGB_FSERROR = packed record + r,g,b : LOCFSERROR; + end; + +type + FSERROR_PTR = ^FSERROR; + jFSError = 0..(MaxInt div SIZEOF(RGB_FSERROR))-1; + FS_ERROR_FIELD = array[jFSError] of RGB_FSERROR; + FS_ERROR_FIELD_PTR = ^FS_ERROR_FIELD;{far} + { pointer to error array (in FAR storage!) } + +type + error_limit_array = array[-MAXJSAMPLE..MAXJSAMPLE] of int; + { table for clamping the applied error } + error_limit_ptr = ^error_limit_array; + +{ Private subobject } +type + my_cquantize_ptr = ^my_cquantizer; + my_cquantizer = record + pub : jpeg_color_quantizer; { public fields } + + { Space for the eventually created colormap is stashed here } + sv_colormap : JSAMPARRAY; { colormap allocated at init time } + desired : int; { desired # of colors = size of colormap } + + { Variables for accumulating image statistics } + histogram : hist3d; { pointer to the histogram } + + needs_zeroed : boolean; { TRUE if next pass must zero histogram } + + { Variables for Floyd-Steinberg dithering } + fserrors : FS_ERROR_FIELD_PTR; { accumulated errors } + on_odd_row : boolean; { flag to remember which row we are on } + error_limiter : error_limit_ptr; { table for clamping the applied error } + end; + + + +{ 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 + NIL pointer). } + +{METHODDEF} +procedure prescan_quantize (cinfo : j_decompress_ptr; + input_buf : JSAMPARRAY; + output_buf : JSAMPARRAY; + num_rows : int); +var + cquantize : my_cquantize_ptr; + {register} ptr : RGBptr; + {register} histp : histptr; + {register} histogram : hist3d; + row : int; + col : JDIMENSION; + width : JDIMENSION; +begin + cquantize := my_cquantize_ptr(cinfo^.cquantize); + histogram := cquantize^.histogram; + width := cinfo^.output_width; + + for row := 0 to pred(num_rows) do + begin + ptr := RGBptr(input_buf^[row]); + for col := pred(width) downto 0 do + begin + { get pixel value and index into the histogram } + histp := @(histogram^[GETJSAMPLE(ptr^.r) shr C0_SHIFT]^ + [GETJSAMPLE(ptr^.g) shr C1_SHIFT] + [GETJSAMPLE(ptr^.b) shr C2_SHIFT]); + { increment, check for overflow and undo increment if so. } + Inc(histp^); + if (histp^ <= 0) then + Dec(histp^); + Inc(ptr); + end; + end; +end; + +{ 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). } + +type + box = record + { The bounds of the box (inclusive); expressed as histogram indexes } + c0min, c0max : int; + c1min, c1max : int; + c2min, c2max : int; + { The volume (actually 2-norm) of the box } + volume : INT32; + { The number of nonzero histogram cells within this box } + colorcount : long; + end; + +type + jBoxList = 0..(MaxInt div SizeOf(box))-1; + box_field = array[jBoxlist] of box; + boxlistptr = ^box_field; + boxptr = ^box; + +{LOCAL} +function find_biggest_color_pop (boxlist : boxlistptr; numboxes : int) : boxptr; +{ Find the splittable box with the largest color population } +{ Returns NIL if no splittable boxes remain } +var + boxp : boxptr ; {register} + i : int; {register} + maxc : long; {register} + which : boxptr; +begin + which := NIL; + boxp := @(boxlist^[0]); + maxc := 0; + for i := 0 to pred(numboxes) do + begin + if (boxp^.colorcount > maxc) and (boxp^.volume > 0) then + begin + which := boxp; + maxc := boxp^.colorcount; + end; + Inc(boxp); + end; + find_biggest_color_pop := which; +end; + + +{LOCAL} +function find_biggest_volume (boxlist : boxlistptr; numboxes : int) : boxptr; +{ Find the splittable box with the largest (scaled) volume } +{ Returns NULL if no splittable boxes remain } +var + {register} boxp : boxptr; + {register} i : int; + {register} maxv : INT32; + which : boxptr; +begin + maxv := 0; + which := NIL; + boxp := @(boxlist^[0]); + for i := 0 to pred(numboxes) do + begin + if (boxp^.volume > maxv) then + begin + which := boxp; + maxv := boxp^.volume; + end; + Inc(boxp); + end; + find_biggest_volume := which; +end; + + +{LOCAL} +procedure update_box (cinfo : j_decompress_ptr; var boxp : box); +label + have_c0min, have_c0max, + have_c1min, have_c1max, + have_c2min, have_c2max; +{ Shrink the min/max bounds of a box to enclose only nonzero elements, } +{ and recompute its volume and population } +var + cquantize : my_cquantize_ptr; + histogram : hist3d; + histp : histptr; + c0,c1,c2 : int; + c0min,c0max,c1min,c1max,c2min,c2max : int; + dist0,dist1,dist2 : INT32; + ccount : long; +begin + cquantize := my_cquantize_ptr(cinfo^.cquantize); + histogram := cquantize^.histogram; + + c0min := boxp.c0min; c0max := boxp.c0max; + c1min := boxp.c1min; c1max := boxp.c1max; + c2min := boxp.c2min; c2max := boxp.c2max; + + if (c0max > c0min) then + for c0 := c0min to c0max do + for c1 := c1min to c1max do + begin + histp := @(histogram^[c0]^[c1][c2min]); + for c2 := c2min to c2max do + begin + if (histp^ <> 0) then + begin + c0min := c0; + boxp.c0min := c0min; + goto have_c0min; + end; + Inc(histp); + end; + end; + have_c0min: + if (c0max > c0min) then + for c0 := c0max downto c0min do + for c1 := c1min to c1max do + begin + histp := @(histogram^[c0]^[c1][c2min]); + for c2 := c2min to c2max do + begin + if ( histp^ <> 0) then + begin + c0max := c0; + boxp.c0max := c0; + goto have_c0max; + end; + Inc(histp); + end; + end; + have_c0max: + if (c1max > c1min) then + for c1 := c1min to c1max do + for c0 := c0min to c0max do + begin + histp := @(histogram^[c0]^[c1][c2min]); + for c2 := c2min to c2max do + begin + if (histp^ <> 0) then + begin + c1min := c1; + boxp.c1min := c1; + goto have_c1min; + end; + Inc(histp); + end; + end; + have_c1min: + if (c1max > c1min) then + for c1 := c1max downto c1min do + for c0 := c0min to c0max do + begin + histp := @(histogram^[c0]^[c1][c2min]); + for c2 := c2min to c2max do + begin + if (histp^ <> 0) then + begin + c1max := c1; + boxp.c1max := c1; + goto have_c1max; + end; + Inc(histp); + end; + end; + have_c1max: + if (c2max > c2min) then + for c2 := c2min to c2max do + for c0 := c0min to c0max do + begin + histp := @(histogram^[c0]^[c1min][c2]); + for c1 := c1min to c1max do + begin + if (histp^ <> 0) then + begin + c2min := c2; + boxp.c2min := c2min; + goto have_c2min; + end; + Inc(histp, HIST_C2_ELEMS); + end; + end; + have_c2min: + if (c2max > c2min) then + for c2 := c2max downto c2min do + for c0 := c0min to c0max do + begin + histp := @(histogram^[c0]^[c1min][c2]); + for c1 := c1min to c1max do + begin + if (histp^ <> 0) then + begin + c2max := c2; + boxp.c2max := c2max; + goto have_c2max; + end; + Inc(histp, HIST_C2_ELEMS); + end; + end; + 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) shl C0_SHIFT) * C0_SCALE; + dist1 := ((c1max - c1min) shl C1_SHIFT) * C1_SCALE; + dist2 := ((c2max - c2min) shl 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 to c0max do + for c1 := c1min to c1max do + begin + histp := @(histogram^[c0]^[c1][c2min]); + for c2 := c2min to c2max do + begin + if (histp^ <> 0) then + Inc(ccount); + Inc(histp); + end; + end; + boxp.colorcount := ccount; +end; + + +{LOCAL} +function median_cut (cinfo : j_decompress_ptr; boxlist : boxlistptr; + numboxes : int; desired_colors : int) : int; +{ Repeatedly select and split the largest box until we have enough boxes } +var + n,lb : int; + c0,c1,c2,cmax : int; + {register} b1,b2 : boxptr; +begin + while (numboxes < desired_colors) do + begin + { Select box to split. + Current algorithm: by population for first half, then by volume. } + + if (numboxes*2 <= desired_colors) then + b1 := find_biggest_color_pop(boxlist, numboxes) + else + b1 := find_biggest_volume(boxlist, numboxes); + + if (b1 = NIL) then { 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) shl C0_SHIFT) * C0_SCALE; + c1 := ((b1^.c1max - b1^.c1min) shl C1_SHIFT) * C1_SCALE; + c2 := ((b1^.c2max - b1^.c2min) shl 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. } + +{$ifdef RGB_RED_IS_0} + cmax := c1; n := 1; + if (c0 > cmax) then + begin + cmax := c0; + n := 0; + end; + if (c2 > cmax) then + n := 2; +{$else} + cmax := c1; + n := 1; + if (c2 > cmax) then + begin + cmax := c2; + n := 2; + end; + if (c0 > cmax) then + 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. } + + case n of + 0:begin + lb := (b1^.c0max + b1^.c0min) div 2; + b1^.c0max := lb; + b2^.c0min := lb+1; + end; + 1:begin + lb := (b1^.c1max + b1^.c1min) div 2; + b1^.c1max := lb; + b2^.c1min := lb+1; + end; + 2:begin + lb := (b1^.c2max + b1^.c2min) div 2; + b1^.c2max := lb; + b2^.c2min := lb+1; + end; + end; + { Update stats for boxes } + update_box(cinfo, b1^); + update_box(cinfo, b2^); + Inc(numboxes); + end; + median_cut := numboxes; +end; + + +{LOCAL} +procedure compute_color (cinfo : j_decompress_ptr; + const boxp : box; icolor : int); +{ Compute representative color for a box, put it in colormap[icolor] } +var + { Current algorithm: mean weighted by pixels (not colors) } + { Note it is important to get the rounding correct! } + cquantize : my_cquantize_ptr; + histogram : hist3d; + histp : histptr; + c0,c1,c2 : int; + c0min,c0max,c1min,c1max,c2min,c2max : int; + count : long; + total : long; + c0total : long; + c1total : long; + c2total : long; +begin + cquantize := my_cquantize_ptr(cinfo^.cquantize); + histogram := cquantize^.histogram; + total := 0; + c0total := 0; + c1total := 0; + c2total := 0; + + c0min := boxp.c0min; c0max := boxp.c0max; + c1min := boxp.c1min; c1max := boxp.c1max; + c2min := boxp.c2min; c2max := boxp.c2max; + + for c0 := c0min to c0max do + for c1 := c1min to c1max do + begin + histp := @(histogram^[c0]^[c1][c2min]); + for c2 := c2min to c2max do + begin + count := histp^; + Inc(histp); + if (count <> 0) then + begin + Inc(total, count); + Inc(c0total, ((c0 shl C0_SHIFT) + ((1 shl C0_SHIFT) shr 1)) * count); + Inc(c1total, ((c1 shl C1_SHIFT) + ((1 shl C1_SHIFT) shr 1)) * count); + Inc(c2total, ((c2 shl C2_SHIFT) + ((1 shl C2_SHIFT) shr 1)) * count); + end; + end; + end; + + cinfo^.colormap^[0]^[icolor] := JSAMPLE ((c0total + (total shr 1)) div total); + cinfo^.colormap^[1]^[icolor] := JSAMPLE ((c1total + (total shr 1)) div total); + cinfo^.colormap^[2]^[icolor] := JSAMPLE ((c2total + (total shr 1)) div total); +end; + + +{LOCAL} +procedure select_colors (cinfo : j_decompress_ptr; desired_colors : int); +{ Master routine for color selection } +var + boxlist : boxlistptr; + numboxes : int; + i : int; +begin + { Allocate workspace for box list } + boxlist := boxlistptr(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 shr C0_SHIFT; + boxlist^[0].c1min := 0; + boxlist^[0].c1max := MAXJSAMPLE shr C1_SHIFT; + boxlist^[0].c2min := 0; + boxlist^[0].c2max := MAXJSAMPLE shr 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 to pred(numboxes) do + compute_color(cinfo, boxlist^[i], i); + cinfo^.actual_number_of_colors := numboxes; + {$IFDEF DEBUG} + TRACEMS1(j_common_ptr(cinfo), 1, JTRC_QUANT_SELECTED, numboxes); + {$ENDIF} +end; + + +{ 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 } +const + BOX_C0_LOG = (HIST_C0_BITS-3); + BOX_C1_LOG = (HIST_C1_BITS-3); + BOX_C2_LOG = (HIST_C2_BITS-3); + + BOX_C0_ELEMS = (1 shl BOX_C0_LOG); { # of hist cells in update box } + BOX_C1_ELEMS = (1 shl BOX_C1_LOG); + BOX_C2_ELEMS = (1 shl BOX_C2_LOG); + + BOX_C0_SHIFT = (C0_SHIFT + BOX_C0_LOG); + BOX_C1_SHIFT = (C1_SHIFT + BOX_C1_LOG); + 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} +function find_nearby_colors (cinfo : j_decompress_ptr; + minc0 : int; minc1 : int; minc2 : int; + var colorlist : array of JSAMPLE) : int; +{ 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. } + +var + numcolors : int; + maxc0, maxc1, maxc2 : int; + centerc0, centerc1, centerc2 : int; + i, x, ncolors : int; + minmaxdist, min_dist, max_dist, tdist : INT32; + mindist : array[0..MAXNUMCOLORS-1] of INT32; + { min distance to colormap entry i } +begin + numcolors := cinfo^.actual_number_of_colors; + + { 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 shl BOX_C0_SHIFT) - (1 shl C0_SHIFT)); + centerc0 := (minc0 + maxc0) shr 1; + maxc1 := minc1 + ((1 shl BOX_C1_SHIFT) - (1 shl C1_SHIFT)); + centerc1 := (minc1 + maxc1) shr 1; + maxc2 := minc2 + ((1 shl BOX_C2_SHIFT) - (1 shl C2_SHIFT)); + centerc2 := (minc2 + maxc2) shr 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 := long($7FFFFFFF); + + for i := 0 to pred(numcolors) do + begin + { We compute the squared-c0-distance term, then add in the other two. } + x := GETJSAMPLE(cinfo^.colormap^[0]^[i]); + if (x < minc0) then + begin + tdist := (x - minc0) * C0_SCALE; + min_dist := tdist*tdist; + tdist := (x - maxc0) * C0_SCALE; + max_dist := tdist*tdist; + end + else + if (x > maxc0) then + begin + tdist := (x - maxc0) * C0_SCALE; + min_dist := tdist*tdist; + tdist := (x - minc0) * C0_SCALE; + max_dist := tdist*tdist; + end + else + begin + { within cell range so no contribution to min_dist } + min_dist := 0; + if (x <= centerc0) then + begin + tdist := (x - maxc0) * C0_SCALE; + max_dist := tdist*tdist; + end + else + begin + tdist := (x - minc0) * C0_SCALE; + max_dist := tdist*tdist; + end; + end; + + x := GETJSAMPLE(cinfo^.colormap^[1]^[i]); + if (x < minc1) then + begin + tdist := (x - minc1) * C1_SCALE; + Inc(min_dist, tdist*tdist); + tdist := (x - maxc1) * C1_SCALE; + Inc(max_dist, tdist*tdist); + end + else + if (x > maxc1) then + begin + tdist := (x - maxc1) * C1_SCALE; + Inc(min_dist, tdist*tdist); + tdist := (x - minc1) * C1_SCALE; + Inc(max_dist, tdist*tdist); + end + else + begin + { within cell range so no contribution to min_dist } + if (x <= centerc1) then + begin + tdist := (x - maxc1) * C1_SCALE; + Inc(max_dist, tdist*tdist); + end + else + begin + tdist := (x - minc1) * C1_SCALE; + Inc(max_dist, tdist*tdist); + end + end; + + x := GETJSAMPLE(cinfo^.colormap^[2]^[i]); + if (x < minc2) then + begin + tdist := (x - minc2) * C2_SCALE; + Inc(min_dist, tdist*tdist); + tdist := (x - maxc2) * C2_SCALE; + Inc(max_dist, tdist*tdist); + end + else + if (x > maxc2) then + begin + tdist := (x - maxc2) * C2_SCALE; + Inc(min_dist, tdist*tdist); + tdist := (x - minc2) * C2_SCALE; + Inc(max_dist, tdist*tdist); + end + else + begin + { within cell range so no contribution to min_dist } + if (x <= centerc2) then + begin + tdist := (x - maxc2) * C2_SCALE; + Inc(max_dist, tdist*tdist); + end + else + begin + tdist := (x - minc2) * C2_SCALE; + Inc(max_dist, tdist*tdist); + end; + end; + + mindist[i] := min_dist; { save away the results } + if (max_dist < minmaxdist) then + minmaxdist := max_dist; + end; + + { 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 to pred(numcolors) do + begin + if (mindist[i] <= minmaxdist) then + begin + colorlist[ncolors] := JSAMPLE(i); + Inc(ncolors); + end; + end; + find_nearby_colors := ncolors; +end; + + +{LOCAL} +procedure find_best_colors (cinfo : j_decompress_ptr; + minc0 : int; minc1 : int; minc2 : int; + numcolors : int; + var colorlist : array of JSAMPLE; + var bestcolor : array of JSAMPLE); +{ 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. } +const + { Nominal steps between cell centers ("x" in Thomas article) } + STEP_C0 = ((1 shl C0_SHIFT) * C0_SCALE); + STEP_C1 = ((1 shl C1_SHIFT) * C1_SCALE); + STEP_C2 = ((1 shl C2_SHIFT) * C2_SCALE); +var + ic0, ic1, ic2 : int; + i, icolor : int; + {register} bptr : INT32PTR; { pointer into bestdist[] array } + cptr : JSAMPLE_PTR; { pointer into bestcolor[] array } + dist0, dist1 : INT32; { initial distance values } + {register} dist2 : INT32; { current distance in inner loop } + xx0, xx1 : INT32; { distance increments } + {register} xx2 : INT32; + inc0, inc1, inc2 : INT32; { initial values for increments } + { This array holds the distance to the nearest-so-far color for each cell } + bestdist : array[0..BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS-1] of INT32; +begin + { Initialize best-distance for each cell of the update box } + for i := BOX_C0_ELEMS*BOX_C1_ELEMS*BOX_C2_ELEMS-1 downto 0 do + bestdist[i] := $7FFFFFFF; + + { 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. } + + + + for i := 0 to pred(numcolors) do + begin + 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; + Inc(dist0, inc1*inc1); + inc2 := (minc2 - GETJSAMPLE(cinfo^.colormap^[2]^[icolor])) * C2_SCALE; + Inc(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[0]; + cptr := @bestcolor[0]; + xx0 := inc0; + for ic0 := BOX_C0_ELEMS-1 downto 0 do + begin + dist1 := dist0; + xx1 := inc1; + for ic1 := BOX_C1_ELEMS-1 downto 0 do + begin + dist2 := dist1; + xx2 := inc2; + for ic2 := BOX_C2_ELEMS-1 downto 0 do + begin + if (dist2 < bptr^) then + begin + bptr^ := dist2; + cptr^ := JSAMPLE (icolor); + end; + Inc(dist2, xx2); + Inc(xx2, 2 * STEP_C2 * STEP_C2); + Inc(bptr); + Inc(cptr); + end; + Inc(dist1, xx1); + Inc(xx1, 2 * STEP_C1 * STEP_C1); + end; + Inc(dist0, xx0); + Inc(xx0, 2 * STEP_C0 * STEP_C0); + end; + end; +end; + + +{LOCAL} +procedure fill_inverse_cmap (cinfo : j_decompress_ptr; + c0 : int; c1 : int; c2 : int); +{ 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.) } +var + cquantize : my_cquantize_ptr; + histogram : hist3d; + minc0, minc1, minc2 : int; { lower left corner of update box } + ic0, ic1, ic2 : int; + {register} cptr : JSAMPLE_PTR; { pointer into bestcolor[] array } + {register} cachep : histptr; { pointer into main cache array } + { This array lists the candidate colormap indexes. } + colorlist : array[0..MAXNUMCOLORS-1] of JSAMPLE; + numcolors : int; { number of candidate colors } + { This array holds the actually closest colormap index for each cell. } + bestcolor : array[0..BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS-1] of JSAMPLE; +begin + cquantize := my_cquantize_ptr (cinfo^.cquantize); + histogram := cquantize^.histogram; + + { Convert cell coordinates to update box ID } + c0 := c0 shr BOX_C0_LOG; + c1 := c1 shr BOX_C1_LOG; + c2 := c2 shr 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 shl BOX_C0_SHIFT) + ((1 shl C0_SHIFT) shr 1); + minc1 := (c1 shl BOX_C1_SHIFT) + ((1 shl C1_SHIFT) shr 1); + minc2 := (c2 shl BOX_C2_SHIFT) + ((1 shl C2_SHIFT) shr 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 := c0 shl BOX_C0_LOG; { convert ID back to base cell indexes } + c1 := c1 shl BOX_C1_LOG; + c2 := c2 shl BOX_C2_LOG; + cptr := @(bestcolor[0]); + for ic0 := 0 to pred(BOX_C0_ELEMS) do + for ic1 := 0 to pred(BOX_C1_ELEMS) do + begin + cachep := @(histogram^[c0+ic0]^[c1+ic1][c2]); + for ic2 := 0 to pred(BOX_C2_ELEMS) do + begin + cachep^ := histcell (GETJSAMPLE(cptr^) + 1); + Inc(cachep); + Inc(cptr); + end; + end; +end; + + +{ Map some rows of pixels to the output colormapped representation. } + +{METHODDEF} +procedure pass2_no_dither (cinfo : j_decompress_ptr; + input_buf : JSAMPARRAY; + output_buf : JSAMPARRAY; + num_rows : int); +{ This version performs no dithering } +var + cquantize : my_cquantize_ptr; + histogram : hist3d; + {register} inptr : RGBptr; + outptr : JSAMPLE_PTR; + {register} cachep : histptr; + {register} c0, c1, c2 : int; + row : int; + col : JDIMENSION; + width : JDIMENSION; +begin + cquantize := my_cquantize_ptr (cinfo^.cquantize); + histogram := cquantize^.histogram; + width := cinfo^.output_width; + + for row := 0 to pred(num_rows) do + begin + inptr := RGBptr(input_buf^[row]); + outptr := JSAMPLE_PTR(output_buf^[row]); + for col := pred(width) downto 0 do + begin + { get pixel value and index into the cache } + c0 := GETJSAMPLE(inptr^.r) shr C0_SHIFT; + c1 := GETJSAMPLE(inptr^.g) shr C1_SHIFT; + c2 := GETJSAMPLE(inptr^.b) shr C2_SHIFT; + Inc(inptr); + cachep := @(histogram^[c0]^[c1][c2]); + { If we have not seen this color before, find nearest colormap entry } + { and update the cache } + if (cachep^ = 0) then + fill_inverse_cmap(cinfo, c0,c1,c2); + { Now emit the colormap index for this cell } + outptr^ := JSAMPLE (cachep^ - 1); + Inc(outptr); + end; + end; +end; + + +{METHODDEF} +procedure pass2_fs_dither (cinfo : j_decompress_ptr; + input_buf : JSAMPARRAY; + output_buf : JSAMPARRAY; + num_rows : int); +{ This version performs Floyd-Steinberg dithering } +var + cquantize : my_cquantize_ptr; + histogram : hist3d; + {register} cur : LOCRGB_FSERROR; { current error or pixel value } + belowerr : LOCRGB_FSERROR; { error for pixel below cur } + bpreverr : LOCRGB_FSERROR; { error for below/prev col } + prev_errorptr, + {register} errorptr : RGB_FSERROR_PTR; { => fserrors[] at column before current } + inptr : RGBptr; { => current input pixel } + outptr : JSAMPLE_PTR; { => current output pixel } + cachep : histptr; + dir : int; { +1 or -1 depending on direction } + row : int; + col : JDIMENSION; + width : JDIMENSION; + range_limit : range_limit_table_ptr; + error_limit : error_limit_ptr; + colormap0 : JSAMPROW; + colormap1 : JSAMPROW; + colormap2 : JSAMPROW; + {register} pixcode : int; + {register} bnexterr, delta : LOCFSERROR; +begin + cquantize := my_cquantize_ptr (cinfo^.cquantize); + histogram := cquantize^.histogram; + width := cinfo^.output_width; + range_limit := cinfo^.sample_range_limit; + error_limit := cquantize^.error_limiter; + colormap0 := cinfo^.colormap^[0]; + colormap1 := cinfo^.colormap^[1]; + colormap2 := cinfo^.colormap^[2]; + + for row := 0 to pred(num_rows) do + begin + inptr := RGBptr(input_buf^[row]); + outptr := JSAMPLE_PTR(output_buf^[row]); + errorptr := RGB_FSERROR_PTR(cquantize^.fserrors); { => entry before first real column } + if (cquantize^.on_odd_row) then + begin + { work right to left in this row } + Inc(inptr, (width-1)); { so point to rightmost pixel } + Inc(outptr, width-1); + dir := -1; + Inc(errorptr, (width+1)); { => entry after last column } + cquantize^.on_odd_row := FALSE; { flip for next time } + end + else + begin + { work left to right in this row } + dir := 1; + cquantize^.on_odd_row := TRUE; { flip for next time } + end; + + { Preset error values: no error propagated to first pixel from left } + cur.r := 0; + cur.g := 0; + cur.b := 0; + { and no error propagated to row below yet } + belowerr.r := 0; + belowerr.g := 0; + belowerr.b := 0; + bpreverr.r := 0; + bpreverr.g := 0; + bpreverr.b := 0; + + for col := pred(width) downto 0 do + begin + prev_errorptr := errorptr; + Inc(errorptr, dir); { advance errorptr to current column } + + { 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: prev_errorptr points to *previous* column's array entry. } + + { Nomssi Note: Borland Pascal SHR is unsigned } + cur.r := (cur.r + errorptr^.r + 8) div 16; + cur.g := (cur.g + errorptr^.g + 8) div 16; + cur.b := (cur.b + errorptr^.b + 8) div 16; + { Limit the error using transfer function set by init_error_limit. + See comments with init_error_limit for rationale. } + + cur.r := error_limit^[cur.r]; + cur.g := error_limit^[cur.g]; + cur.b := error_limit^[cur.b]; + { 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. } + + Inc(cur.r, GETJSAMPLE(inptr^.r)); + Inc(cur.g, GETJSAMPLE(inptr^.g)); + Inc(cur.b, GETJSAMPLE(inptr^.b)); + + cur.r := GETJSAMPLE(range_limit^[cur.r]); + cur.g := GETJSAMPLE(range_limit^[cur.g]); + cur.b := GETJSAMPLE(range_limit^[cur.b]); + { Index into the cache with adjusted pixel value } + cachep := @(histogram^[cur.r shr C0_SHIFT]^ + [cur.g shr C1_SHIFT][cur.b shr C2_SHIFT]); + { If we have not seen this color before, find nearest colormap } + { entry and update the cache } + if (cachep^ = 0) then + fill_inverse_cmap(cinfo, cur.r shr C0_SHIFT, + cur.g shr C1_SHIFT, + cur.b shr C2_SHIFT); + { Now emit the colormap index for this cell } + + pixcode := cachep^ - 1; + outptr^ := JSAMPLE (pixcode); + + { Compute representation error for this pixel } + Dec(cur.r, GETJSAMPLE(colormap0^[pixcode])); + Dec(cur.g, GETJSAMPLE(colormap1^[pixcode])); + Dec(cur.b, 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. } + + bnexterr := cur.r; { Process component 0 } + delta := cur.r * 2; + Inc(cur.r, delta); { form error * 3 } + prev_errorptr^.r := FSERROR (bpreverr.r + cur.r); + Inc(cur.r, delta); { form error * 5 } + bpreverr.r := belowerr.r + cur.r; + belowerr.r := bnexterr; + Inc(cur.r, delta); { form error * 7 } + bnexterr := cur.g; { Process component 1 } + delta := cur.g * 2; + Inc(cur.g, delta); { form error * 3 } + prev_errorptr^.g := FSERROR (bpreverr.g + cur.g); + Inc(cur.g, delta); { form error * 5 } + bpreverr.g := belowerr.g + cur.g; + belowerr.g := bnexterr; + Inc(cur.g, delta); { form error * 7 } + bnexterr := cur.b; { Process component 2 } + delta := cur.b * 2; + Inc(cur.b, delta); { form error * 3 } + prev_errorptr^.b := FSERROR (bpreverr.b + cur.b); + Inc(cur.b, delta); { form error * 5 } + bpreverr.b := belowerr.b + cur.b; + belowerr.b := bnexterr; + Inc(cur.b, 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.} + + Inc(inptr, dir); { Advance pixel pointers to next column } + Inc(outptr, dir); + end; + { 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^.r := FSERROR (bpreverr.r); { unload prev errs into array } + errorptr^.g := FSERROR (bpreverr.g); + errorptr^.b := FSERROR (bpreverr.b); + end; +end; + + +{ 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} +procedure init_error_limit (cinfo : j_decompress_ptr); +const + STEPSIZE = ((MAXJSAMPLE+1) div 16); +{ Allocate and fill in the error_limiter table } +var + cquantize : my_cquantize_ptr; + table : error_limit_ptr; + inp, out : int; +begin + cquantize := my_cquantize_ptr (cinfo^.cquantize); + table := error_limit_ptr (cinfo^.mem^.alloc_small + (j_common_ptr (cinfo), JPOOL_IMAGE, (MAXJSAMPLE*2+1) * SIZEOF(int))); + { not needed: Inc(table, MAXJSAMPLE); + so can index -MAXJSAMPLE .. +MAXJSAMPLE } + cquantize^.error_limiter := table; + { Map errors 1:1 up to +- MAXJSAMPLE/16 } + out := 0; + for inp := 0 to pred(STEPSIZE) do + begin + table^[inp] := out; + table^[-inp] := -out; + Inc(out); + end; + { Map errors 1:2 up to +- 3*MAXJSAMPLE/16 } + inp := STEPSIZE; { Nomssi: avoid problems with Delphi2 optimizer } + while (inp < STEPSIZE*3) do + begin + table^[inp] := out; + table^[-inp] := -out; + Inc(inp); + if Odd(inp) then + Inc(out); + end; + { Clamp the rest to final out value (which is (MAXJSAMPLE+1)/8) } + inp := STEPSIZE*3; { Nomssi: avoid problems with Delphi 2 optimizer } + while inp <= MAXJSAMPLE do + begin + table^[inp] := out; + table^[-inp] := -out; + Inc(inp); + end; +end; + +{ Finish up at the end of each pass. } + +{METHODDEF} +procedure finish_pass1 (cinfo : j_decompress_ptr); +var + cquantize : my_cquantize_ptr; +begin + 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; +end; + + +{METHODDEF} +procedure finish_pass2 (cinfo : j_decompress_ptr); +begin + { no work } +end; + + +{ Initialize for each processing pass. } + +{METHODDEF} +procedure start_pass_2_quant (cinfo : j_decompress_ptr; + is_pre_scan : boolean); +var + cquantize : my_cquantize_ptr; + histogram : hist3d; + i : int; +var + arraysize : size_t; +begin + cquantize := my_cquantize_ptr (cinfo^.cquantize); + histogram := cquantize^.histogram; + { 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) then + cinfo^.dither_mode := JDITHER_FS; + + if (is_pre_scan) then + begin + { Set up method pointers } + cquantize^.pub.color_quantize := prescan_quantize; + cquantize^.pub.finish_pass := finish_pass1; + cquantize^.needs_zeroed := TRUE; { Always zero histogram } + end + else + begin + { Set up method pointers } + if (cinfo^.dither_mode = JDITHER_FS) then + 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) then + ERREXIT1(j_common_ptr(cinfo), JERR_QUANT_FEW_COLORS, 1); + if (i > MAXNUMCOLORS) then + ERREXIT1(j_common_ptr(cinfo), JERR_QUANT_MANY_COLORS, MAXNUMCOLORS); + + if (cinfo^.dither_mode = JDITHER_FS) then + begin + arraysize := size_t ((cinfo^.output_width + 2) * + (3 * SIZEOF(FSERROR))); + { Allocate Floyd-Steinberg workspace if we didn't already. } + if (cquantize^.fserrors = NIL) then + cquantize^.fserrors := FS_ERROR_FIELD_PTR (cinfo^.mem^.alloc_large + (j_common_ptr(cinfo), JPOOL_IMAGE, arraysize)); + { Initialize the propagated errors to zero. } + jzero_far(cquantize^.fserrors, arraysize); + { Make the error-limit table if we didn't already. } + if (cquantize^.error_limiter = NIL) then + init_error_limit(cinfo); + cquantize^.on_odd_row := FALSE; + end; + + end; + { Zero the histogram or inverse color map, if necessary } + if (cquantize^.needs_zeroed) then + begin + for i := 0 to pred(HIST_C0_ELEMS) do + begin + jzero_far( histogram^[i], + HIST_C1_ELEMS*HIST_C2_ELEMS * SIZEOF(histcell)); + end; + cquantize^.needs_zeroed := FALSE; + end; +end; + + +{ Switch to a new external colormap between output passes. } + +{METHODDEF} +procedure new_color_map_2_quant (cinfo : j_decompress_ptr); +var + cquantize : my_cquantize_ptr; +begin + cquantize := my_cquantize_ptr (cinfo^.cquantize); + + { Reset the inverse color map } + cquantize^.needs_zeroed := TRUE; +end; + + +{ Module initialization routine for 2-pass color quantization. } + + +{GLOBAL} +procedure jinit_2pass_quantizer (cinfo : j_decompress_ptr); +var + cquantize : my_cquantize_ptr; + i : int; +var + desired : int; +begin + cquantize := my_cquantize_ptr( + cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, + SIZEOF(my_cquantizer))); + cinfo^.cquantize := jpeg_color_quantizer_ptr(cquantize); + cquantize^.pub.start_pass := start_pass_2_quant; + cquantize^.pub.new_color_map := new_color_map_2_quant; + cquantize^.fserrors := NIL; { flag optional arrays not allocated } + cquantize^.error_limiter := NIL; + + { Make sure jdmaster didn't give me a case I can't handle } + if (cinfo^.out_color_components <> 3) then + ERREXIT(j_common_ptr(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 to pred(HIST_C0_ELEMS) do + begin + cquantize^.histogram^[i] := hist2d (cinfo^.mem^.alloc_large + (j_common_ptr (cinfo), JPOOL_IMAGE, + HIST_C1_ELEMS*HIST_C2_ELEMS * SIZEOF(histcell))); + end; + 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) then + begin + { Make sure color count is acceptable } + desired := cinfo^.desired_number_of_colors; + { Lower bound on # of colors ... somewhat arbitrary as long as > 0 } + if (desired < 8) then + ERREXIT1(j_common_ptr (cinfo), JERR_QUANT_FEW_COLORS, 8); + { Make sure colormap indexes can be represented by JSAMPLEs } + if (desired > MAXNUMCOLORS) then + ERREXIT1(j_common_ptr (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; + end + else + cquantize^.sv_colormap := NIL; + + { 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) then + 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) then + begin + cquantize^.fserrors := FS_ERROR_FIELD_PTR (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); + end; +end; +{ QUANT_2PASS_SUPPORTED } +end. diff --git a/resources/libraries/deskew/Imaging/JpegLib/imjutils.pas b/resources/libraries/deskew/Imaging/JpegLib/imjutils.pas new file mode 100755 index 0000000..eb147b9 --- /dev/null +++ b/resources/libraries/deskew/Imaging/JpegLib/imjutils.pas @@ -0,0 +1,232 @@ +unit imjutils; + +{ 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. } + +{ Source: jutils.c; Copyright (C) 1991-1996, Thomas G. Lane. } + +interface + +{$I imjconfig.inc} + +uses + imjmorecfg, + imjinclude, + imjpeglib; + + +{ 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). } + + +{$ifdef FALSE} { This table is not actually needed in v6a } + +const + jpeg_zigzag_order : array[0..DCTSIZE2] of int = + (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 + jpeg_natural_order : array[0..DCTSIZE2+16-1] of int = + (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} +function jdiv_round_up (a : long; b : long) : long; + +{GLOBAL} +function jround_up (a : long; b : long) : long; + +{GLOBAL} +procedure jcopy_sample_rows (input_array : JSAMPARRAY; + source_row : int; + output_array : JSAMPARRAY; dest_row : int; + num_rows : int; num_cols : JDIMENSION); + +{GLOBAL} +procedure jcopy_block_row (input_row : JBLOCKROW; + output_row : JBLOCKROW; + num_blocks : JDIMENSION); + +{GLOBAL} +procedure jzero_far (target : pointer;{far} bytestozero : size_t); + +procedure FMEMZERO(target : pointer; size : size_t); + +procedure FMEMCOPY(dest,src : pointer; size : size_t); + +implementation + +{GLOBAL} +function jdiv_round_up (a : long; b : long) : long; +{ Compute a/b rounded up to next integer, ie, ceil(a/b) } +{ Assumes a >= 0, b > 0 } +begin + jdiv_round_up := (a + b - long(1)) div b; +end; + + +{GLOBAL} +function jround_up (a : long; b : long) : long; +{ Compute a rounded up to next multiple of b, ie, ceil(a/b)*b } +{ Assumes a >= 0, b > 0 } +begin + Inc(a, b - long(1)); + jround_up := a - (a mod b); +end; + +{ 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 } +procedure FMEMZERO(target : pointer; size : size_t); +begin + FillChar(target^, size, 0); +end; + +procedure FMEMCOPY(dest,src : pointer; size : size_t); +begin + Move(src^, dest^, size); +end; + + +{$else} { 80x86 case, define if we can } + {$ifdef USE_FMEM} + FMEMCOPY(dest,src,size) _fmemcpy((void FAR *)(dest), (const void FAR *)(src), (size_t)(size)) + FMEMZERO(target,size) _fmemset((void FAR *)(target), 0, (size_t)(size)) + {$endif} +{$endif} + + +{GLOBAL} +procedure jcopy_sample_rows (input_array : JSAMPARRAY; source_row : int; + output_array : JSAMPARRAY; dest_row : int; + num_rows : int; num_cols : JDIMENSION); +{ 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. } +var + inptr, outptr : JSAMPLE_PTR; {register} +{$ifdef FMEMCOPY} + count : size_t; {register} +{$else} + count : JDIMENSION; {register} +{$endif} + row : int; {register} +begin +{$ifdef FMEMCOPY} + count := size_t(num_cols * SIZEOF(JSAMPLE)); +{$endif} + Inc(JSAMPROW_PTR(input_array), source_row); + Inc(JSAMPROW_PTR(output_array), dest_row); + + for row := pred(num_rows) downto 0 do + begin + inptr := JSAMPLE_PTR(input_array^[0]); + Inc(JSAMPROW_PTR(input_array)); + outptr := JSAMPLE_PTR(output_array^[0]); + Inc(JSAMPROW_PTR(output_array)); +{$ifdef FMEMCOPY} + FMEMCOPY(outptr, inptr, count); +{$else} + for count := pred(num_cols) downto 0 do + begin + outptr^ := inptr^; { needn't bother with GETJSAMPLE() here } + Inc(inptr); + Inc(outptr); + end; +{$endif} + end; +end; + + +{GLOBAL} +procedure jcopy_block_row (input_row : JBLOCKROW; + output_row : JBLOCKROW; + num_blocks : JDIMENSION); +{ Copy a row of coefficient blocks from one place to another. } +{$ifdef FMEMCOPY} +begin + FMEMCOPY(output_row, input_row, num_blocks * (DCTSIZE2 * SIZEOF(JCOEF))); +{$else} +var + inptr, outptr : JCOEFPTR; {register} + count : long; {register} +begin + inptr := JCOEFPTR (input_row); + outptr := JCOEFPTR (output_row); + for count := long(num_blocks) * DCTSIZE2 -1 downto 0 do + begin + outptr^ := inptr^; + Inc(outptr); + Inc(inptr); + end; +{$endif} +end; + + +{GLOBAL} +procedure jzero_far (target : pointer;{far} bytestozero : size_t); +{ Zero out a chunk of FAR memory. } +{ This might be sample-array data, block-array data, or alloc_large data. } +{$ifdef FMEMZERO} +begin + FMEMZERO(target, bytestozero); +{$else} +var + ptr : byteptr; + count : size_t; {register} +begin + ptr := target; + for count := bytestozero-1 downto 0 do + begin + ptr^ := 0; + Inc(ptr); + end; +{$endif} +end; + +end. diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/adler32.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/adler32.obj new file mode 100755 index 0000000000000000000000000000000000000000..4ea3e7625bc4e3a1c07577ee291db9eb7c301387 GIT binary patch literal 726 zcmZoLV9;~6ig652EJ{@ft}HG|%`J}c$xL!d%_+#pjEMp=W2$n1L}E%#YLT&#UNS?6 zFarYvzf*ouPGVk)g0r@^f~lS<Lx(v7!wXW);%hF{XXr3tV0d}EFGXL6D2G%f=jWyA zWiWIY1GS|!73ymfrw!-{kS;TzE|9e(`<fZ3kztw!18aPUYeWc(v%iZgknQ3a;>ZFb z*j(I${6hm+;+=wnnSq#L20Oz_3r0pJ1~7|(frS;uV&`CBn8n7x_MiDb6T<{P1_nlU zXy^h1mSKSnBO?PtX!o1ou<&kIj_y*P?jPMZ4!##?eEk1E14H9;5ZU^zlxqi28N*99 zAlV%%(0Yl#?F*3K?JLmubAu=YLosW&tH5j49U!)*t3dM)0sfwOKn2E^j8Arnb@?$e z-!i_${G0h^R~QqJx&;*VV+Qe!FLi~nz(iPKB5W`bc9;kUOoS6A!UYrIhKcaNM0jB$ zd@vDykVx|}0p{P$FTk$rP5S@;f9rt~9-srnm~Xi;|Mn>TvhyR*v)zY{FMa>mc^Sev j4(413`mXf=e>)FQQ}^+M4+OXm$Hzs-9%kqU1~>x%-Bbno literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/compress.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/compress.obj new file mode 100755 index 0000000000000000000000000000000000000000..e2b153ee2f4f166afb3dcd7af7ec19ebf07caa3a GIT binary patch literal 763 zcmZoLWYBlEig652EJ{@ft}HG|%`J}c$xL!d%_+#pjEMp=W2$n1L~?#^K~ZXPv0gGm zhcE*J1HV&#QBGoBih{GYwt}giDMN<^1H%iF4co)>T#uo{gn{AZ?Y<O!9ip64m7JfK zrkBCcVGPuk)>NpkO`JBMH$b|~fx1A}lJ0M2piYKq8Vs!QA+8Z2EYAKeu0XbnV~8UQ zh+uPZ5AqKUV2O7M4rT^oh8gS(D<3j2GBJQz91JY15EcUiI|l>9EH(zV|IGiH7<yC~ zcv4c+auQ2YJ@Yb4;u+YXJO(Z(!!<93fnkCG0|O%`EFO%2alkNvA1K5D6IuZh5dn(u z!bF_%OY>4r0EHJkWng4r2<?8;eB?xMSa&H;x2r&>>xu4Ak<QRF-M$>1zE`@%x({`_ z-mtz<!oK78|NsAA7aCvc41HmIveWmC@qtd)4_Ou&AUnH5C4zc=PXxXIahi_^bl+&b zUBc3R__bpeOK<3jAgDM;_l@S?j3tu2t|tP(LXoYvOQ9x<162!mhu-NFgBujteWUwu zTy*!-*uxVh0Nt7OzuQ$LpgU9`sN0t(u-la*;DsH~QO!q0fC_-}K#}eX2Ve0pUpV-J zkNHCLA@1%&v4<JviZVQL3}IU07sj|GEsSYNWf=34>0!(a3wRiSv0$iYq-O~B1CYZ2 E05aqcjQ{`u literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/crc32.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/crc32.obj new file mode 100755 index 0000000000000000000000000000000000000000..25d86be35fde37b99bfbc39937ab3eb6487cece9 GIT binary patch literal 11970 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/deflate.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/deflate.obj new file mode 100755 index 0000000000000000000000000000000000000000..25bbf9db63a589c0f8d26e89af16d5fbee7b8571 GIT binary patch literal 8912 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/inffast.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/inffast.obj new file mode 100755 index 0000000000000000000000000000000000000000..cfaf0b61d7597123f8968e6a92bfc155c6f823bc GIT binary patch literal 2709 zcmbVMUrbw77(e|3Yk;LVT|%NEUGM>RCuIUtR}<+!2bD8w=~XjZNog;m@zQGVEk>T) zI!rGo8u#GhpO_F|j1T%Kap?mEBCI8*gvOLXl;Bi4`Y_FcY(eXHZd(={I_Sea{q>yl z`+mRko$q|_w4i45X_xV3Pl(g?zAcE{kl^a}_ggsrjX}Tb96;9y4}(82FyIlyW-p39 zj1bbAf+5}$@afD9qib(&N71JcT0dy4&Cj3Eqv$DwvI*{3^TCV^7Kk5LHtP3H<UJzS z3`Fpny>q!ruix*{#Sq5{lzAGMsPVbRCxK~q^qg@RvF`b<Zfmd0)Z^$jp0&8lCbp~F zVs*5;y3U%rS&P-hg+hTKB^!$5)&r0Q&J*$umdzG@aCwydbWywI0NO*`5T!m-q@Fwg zwT};a%QX#D4=>+SS;~SKDpwh({=j+4F6W;60%4vno9yET<O+MXnC-TMWLpq@z%T#U zC3YK#FTd~8p8)X(_k<B|Fd&Pz0&U>pn|fw{v|>q8G4eIPU$L<0m$OUT1#KMre)Xlk zr2-<l(ugYhoYq&IYIBdp3fN+F8r5{Dw48DDuy%Eyskc`JANuGJ`f^IGP$C)Gk-DOQ z5IUqm=#xrRnOEhN=mY47iW=yTw7c|CG3bwIT&-3h#FAWOCf55Bv1*9@F^SzKSVI(@ zl68x)8UmwD9m!Ux<|KTZPT(41ssYeRGQ?5KBre=kKoDk0m6F`IBsNDNfK4aibs0P^ z;Ttk&l<=wyPDpq~1}zfK$e>NaX&G1~oRUGOgy&_@eHCJ(#WkD6&dHGAZPI0zCuRtn zj@;D>$MfKkM%dDRb7XN%!Db@Xv}VsqLKWv45`;~WnlTAawl0sk<0QE_lxcFO>7U8b z<#2V!7&{fM&E_Dho71)0OIHABC-9VnW09Y=qs!tckTzT&KDxI^vSb?M8Wt36tRzsT z;TdXp{=W>%4ksW@+KKQK*eh5&xwkc(HY`2JHn_GmER;N}{vhlvl3}hJe$+-PL56(K zB*_wCZ$JX-pj2j!b+ne?x@MJ+0>~!lCU(U}@apKbu#Vt$CaGE=IA&N9Mw;9)Q^yNq zRZ&g&Pm*GO*cgOu;K>ZzRCtw1GVayVX1r@Ny8=6H#2qsvg;wHT-L*&~)x-KdrP_b5 zgMr8$<#4^JW30*))$EK>O=KvhDy+V>2EU`%w3mK|gLIgvO?g3~efOur%n_)IG(9dT z?kmz4_eJ{fUZgRAo6{ZP2C}<mXtB{O%#%khOYCH}^6z{ep4_aduAXY+?O6p)^+x~y zkyAx--Cz0q=Cn?%manLq@{Sko4VoL=6#DEi`qkuA{`M7ocb!T!{vA>vb@4hkjuH%w z;I<(lR1=&t#I+Yc-KK8R@tZ0MY8%Iz=!RH)P1VGvi;px7FZ37-j}7v-Wt6Jk>r?=i zY&AQ<4inl=wwOdDUa|2|VVX(2_$~A=bdG_7huvyfP&iE39Fw{*Mf=PKM94Amt8&jt zLCcctwX`Q#Gm^1|whWw>Y;CW-N`7%qqj4vKm+KD<dw9Q3=kp7qC*b9DVlb#1@Pu^z h9`Eb-uX=+%ZfBPFi(JUV>v=A4UL4fPYluKl^bdsI_Xq$0 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/inflate.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/inflate.obj new file mode 100755 index 0000000000000000000000000000000000000000..6b1536110c7d32c7c65d76d90761fcbcf51dc03b GIT binary patch literal 11402 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 ZW-xcTzg#ibSf!!}*^2N1#t~xK{{!;u9t8jZ literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/inftrees.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/inftrees.obj new file mode 100755 index 0000000000000000000000000000000000000000..4357da25e08863ae8c9f220302179288a43e411c GIT binary patch literal 2737 zcmbVNZ%kWN6hH5^D;>ffIJU73XALpVb@FE&*`F?vLa!U9Z%p91Mcc6s`UbJUY+o^( z*&^1OzTU9RFJ_u(miPtDqTj61FO=z87Gh9PSwLhNGhi{Ph7h(jtLHw-VvNQCC+~6Z zyXSX*=lssOw{KKJdF_6u?Wo)5F*Tm?E1nL&)6ss??(v>(Yj?hcL+4p9zS=uw#pm(( z%UU439sq=zE}z%k*=njan@v?^RS@0_Ftg&#My|A#LwFCs?45UV%U2jm8-({G61G3P zeHSKiq@n&`gH1F&SLYy&&YJoLhwZT4Sz9C3IqamN%2{`~)*;!6)8q4Xc9kL6ngv@{ z0QP&_zLvICqm@<^lc7;9E%1sJaCdk*7<6A2TDbyfv$v~dwLv4)?e(mh*{}Fk%Z$+W z&QlD%nmRXk2E5)?gIhhapMei#)!w}lwf#yf;#Z&cGI}?Xk6&ml--+b^e@4%?baiT^ z7KF)fzFKa6I9RqN85#Z5`><pIrCn2(9bR^fS9o~w9?>BZ-rWvsnw!Y6CVg$ao#5DR zYqIIt%V0m)P%j<PH`g>a>hOU}Iq>lddX5Kew3E}XSsDT47y;flfFYyH@NlshIKB`s zEU#PfG`Dn}KI3aY)u!O31s96|xcucM#eLH2VV^JS^&Egi=Yn5{E&Yuz9;IS|O47Zg zPmbeqhNOf2JeAVHyF&NxmVPSbSrgp_LUc=TfUlM_c2}P@@mh+KVp`BkuBp;|oGc0m zl!JRAur4Dm2K(<*X)$=0^UqN-k3;J~w}FyHRhxvq4;X`q%<MNP$d?!h^pa0hlAt7^ zZID|NBne*QMzD3bTc<Ikndetocip%UtuRl9#?NUxy^cF&qYk8l_l53XLgVL4C^3fm zm3$#e`sfYwSnwXFXx<`v%W)C`h!btsCH`I#k!djvBxKz-Sv|(H4Ot*4g)1GrC-`on z6s|ngl|VJj)RmCwRyj1TY!ag5GF4n*rc79fPSQY$tm=<fGc*eI1I7`Y;e2|fD7lA< zC8{{AilcEd#C}(j`{QJ^0tY=EqdZnRPKG<R+3?bA2r%R126VcDpK*0g`!|%qKUGR= zdQhpzvVgJWs^XYskV^Ban2M8W=0e5!kz1;3j*4@2syIbOV_jeaPI~Sbu$g?hUobsq z4<IoH)~mN+86sjTLee{8Ve&Ipgt~@gY80X|>(z6)*T@9OsyLZ^ii$(l*!hiA9FFAK z!r}xKM`fzF$tsylGPe_`?<CtaO-ag}&}c@c=yfe#7SYVpWQK|<IysW0;*9PF702kg z3H88xI13F1ZX&WSi%Rm}&4f~cyI9M$G*MUjhc1=mEelLtO44%1fo)6K3vt`<3l$fw zG2yMBFf&X*h#D!$6QX&CR2B@b&g1Ia8<{v{s`MHar<u1wo2Nqk0RwuVfb@EnJ~qX3 zWDHLO%VhXL3dwas)K2=$W7ugzjcy!Qbpsl;@7D)dj$r>A5B-begDg66s`Oh`oTj9R z;$%8p6vlVNH6M0{ITXa42o0Xm1!KC{9c;mAS#@0EXI=L*85Fo0YZQ@EI|i^!OIn8( zl#{4vXi#}_DKTl<I-qQ}46<a%Jf3gt6Rk%O%8Cmg6vFLD6My>>KA++9c@qzpC2Tl7 zB7PkCjbhs3gqnBpzlt&ZeCeM{#Rx3~KBSneRK(=ssaVV?IZJ=@@t5gs*HyS&!R|3r z_9`+}mhCF5G}SKORi?^)`>LMVRZ(FviEiI3CR?l5<1+#1Ko4uc06AcUwU7&sKt2di z2<u@JY=y_66wF`&E7U?Gw16Li@DW^vTfnonhJt@sAtISQnzi+~53{1IZGa^!00-2d z41AV92f6bRw;7&*O0a<&dLRZI*TZo<r{naTfirS>Tme_inYaqBnmh4#_GQLpCdRQ5 ZZ4+8C+GeyZXj{=r&>lmpMgxk1!C#cqv~B<Z literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcapimin.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcapimin.obj new file mode 100755 index 0000000000000000000000000000000000000000..1979f24ebab7e4e922ffda7855d03eb23fd0ca6d GIT binary patch literal 2721 zcmbtVUuaup6#v?!-DRdUT~f)mg2geYOv<{uqPF|f^d?>Zq-&bnqz$IKOOvn7t@+2j zH;g*P&AUQxKb2xdL=b!^4#5Y(C*MLh41H4f<df{N8j*r9iWK8HH@CIJ3QK%Q@_pYq zzw<li_nq_Q&MTx>=!<!$;|lE*msE{r)L1~C^U-uZCF5!|PZwfod2SJZ7ZdTkoRPEL z39{lMgtQLklyp3s>=e$O>+J3BB`a45*{(BW&%0-P$VxvUztH=kGkh&+QANoq-6^ti z3D6(5eDvbQTF_9LoYfG18Q=qf(>*n5lF2DFF~6`+-}mO(o?7WaUxdF3^rY)**Llc3 z7@D{i@{V^7jRgE+Y;YnJ@Lu=Dgu(Dgz~>M3#zw9Sfw0dX%g_u$U$dY+H9)0wE`eB- z9BEPVXQc&bJWcCnRyFgw>R|?`oHf<12Q6ji<!au2R@E13s9M#Ma;{#<R2o+jsk+fx z((00W5|^@tbQ-Da0w?LbieQ{BY!qzoXKfS-Gz0wqDBD6Kc?Zm9O|zBA!F`*}f}{QQ zH+MT~8f`h7NEec3n<E}ysG9JHo=Y2Q_i-0#lBWDKQ+8p(=ZCe=JLR?GSF`V0Xd)c6 zOM{|lgAe)S82K_~YiK0qiV%C#{^}S<$fwPuxnip{lJ`bQGduw*)rfOZ&dQnuyPUEl zWfm0Tz(-q3QW%-s5;<<};&VAgBQDD~uDn4N>6RjER3T*T6d?^R(@julT%$rRlUJy! z!c$MyI>6X|IHHuW%s-&{AFN4nV0?_m(|~oGShbKhLrPkFE=|>)r>*l)f|@$O`WgW1 z0c@LzHS?4*X3{T%GKV-Vla!+IBvrmb<G*my+>%E3jxtqq@xUE@p0)vJnEfV3ruib9 z<yhFk3QqmcD~4d#PBL3*!0mvXO9LMGt0P9RLu9VXiRj(GQh>7@+N%zDV&p9c#<Saw z6do-SaT^?n{?K9Y#%;%Ug3~&Q@<l`7_(z85;kS%2A76Z59OkzMjN|&IUEj68^#`8J zm?z*c>a>o3=ZgT+Zz7*~z9d{EWany0a3_I9y7Bt#1E`1jxu8iKoqe!AMmhB-Fw z&^L$Kh2<w&^U4Qz06$=Hluf(7IkRtU12(2OtDd;o_x$+=Fsvbm7(oFAx4uiYCVh9l z@Df|cUSVUM{XeR$rkDcks}hQgU4k%`NiJG`$3!h?w@e>5VnnwQ^oZ=aN?WviQ)Ig7 z>x}YG%9okIEf+PHk+-w;$KY!OIgyQV;0anYeHfz#?fOIRUa`>1T%|!b^afZk))3PX z(!Zm8E3$PnXo!neQ(5)4+$(D9a2~)AAx9L#dMcC7X!Qo8mbR17ELiE=ZfF&zV~NYf z!U?PL7oc+Ol)k&5wI5l{H1j)x2OMr_3&xO-c|C@IuiU~vT75nwR>FHwz!3XhFIASe z3ctd=;!Rd{M)F!|nco?N<_>}EW_6Ruyx%JhFmWNaTD5q9`6({E(&T@XBQjeh2nsRL xQvoF{r4CroB6!dPxX5QG$woZVu%Sj8H-3uPHnt=7FU~~l_s1hmgu$eezX91beNzAc literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcapistd.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcapistd.obj new file mode 100755 index 0000000000000000000000000000000000000000..b6b51c1db76649751e0ff721491bd3b4f8be5816 GIT binary patch literal 2133 zcmbu9PiWIn9LIlIyII-9$}%15p(x^DFr9O#+tjsboO7M)l66|vEo5oBrKD*|QboNe zov`jHh!YV(@GuZ>!cN}m9~|Dqvu9ECAb8Njp#EOkp$CN}9{T$7@;>kTec$}vyM7FM zxxQ5Rf^4cG@rq@uIV%;@W}~WJnAd3UTtS^n>Dp|T{$~}rpjmdhOM%5!0N5VMo4RbI zLtJNPsJE*Z7Eb|u^0;K9?_dZP`vE?yPlWwFZfvQhnK!%UVQ~QIyW8&VJL-nUooR+m z@so%T#`bi($|RRJRV6dGA>ViPK(||Y%$MTFQ64%@cN|9dabavo2#<!&499pe6&Vv^ z;q%cH7fB4qqP)<X8a~g(5>Y;tQ*#tOQAfL7ppu?fC`%)!vNZC|$|5T3s%K@(c0QLE zWkfZ@N!tr885ynSyRW|MBQAE0rLX0^MrNt9smyz(?R33Lycm~^qOMbEPjFhzSQMtG zixvtk@68s9gqb1#AKR|eRD3U<O;=}2$>aBSiiIBSXR9~%x;omLq3Ff5Q|6SXH<T;> zkL%J$v%B8`jnV{val+4yMS1K;!xLdYZJMG(!dN2imm;F*!yi1@0#Cm)OanMQ0Dt2~ zZ%Y8+VG}e}ebolI)deiQnkB1PD442cNwz$zs}{6nHAAx{C7-Je=VZLxO@KSQ0hrbr zV$0_58F-z;ojv%nw%%n^vsKAbWJA{s)w1c^-)QZvDPNY-vMnEG03QEfuuYJ!BAE^l zjxGI6+ik?RHpsGw5|J&(+eOk`-H}}4TNWyIa~8x;RI~ItAd=Rq5KM|>q`f*GNY=*j zu{3~7R)fi&_vPhxIQ4l3o2Ak-Y!@m;X1F}UPEEf;&2e#(6oVVW>OX`@i7rCKQ2$Gl zCCzJL_8LBCt0M7L<9_r(xWLVdy^}a%<+z`Wgvw{p9Tz|oWSP2II@hxB)^6VL7WXF! z7peiBFZ<tE4dh!-<#uAd%&$)hWVB@*XAaU`P=O#xtTWEygkPVTN;=-rdOfhrCVaR5 z&38}bOVOM6)eo4!i;q6S2m8NW+D=;6BJ3_4s(mTKx5P!Ve3tqbXh4{{AH@R9s*~iK W8H^tuZH1LXlg!G{WCIYaSNH|~{HMkM literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jccoefct.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jccoefct.obj new file mode 100755 index 0000000000000000000000000000000000000000..505b1a3356d98e0f97e109b2b803d186cfe06f45 GIT binary patch literal 3615 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jccolor.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jccolor.obj new file mode 100755 index 0000000000000000000000000000000000000000..7e8d7dc02777f69019fef0f9ff3f7613671a4862 GIT binary patch literal 3330 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcdctmgr.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcdctmgr.obj new file mode 100755 index 0000000000000000000000000000000000000000..4dc9d104dda93d42dcf28adc3f5efeb6291c12d4 GIT binary patch literal 3337 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 b=9Kaqnfvgeo%fY{KX?4Vblg)SOfK;|Gtd1G literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jchuff.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jchuff.obj new file mode 100755 index 0000000000000000000000000000000000000000..fe396b80084dc58b35402e3409a54e4e78faf98b GIT binary patch literal 5176 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcinit.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcinit.obj new file mode 100755 index 0000000000000000000000000000000000000000..6a6d0a4b979474e4ef46e167c1e301db1b2cc8de GIT binary patch literal 1970 zcmbtV%WD&15T7*9T71;{wn(rPD}`XKL9L4BWr@b7W|P%m%Cg=4(rnyE*xgw4q7n}! z>!DZ?Q3MYQ>a8kx@E}D6@5Q^<mij-$@7rub#Y=ZRBs<K^Z+<hgGqVq(FrJtw#cr@F z3R8DA9Z6a#Bg`g|SgQy`x>7@PB~h5Il4q3@WI>N`urL4sJL8HfvN9h|jE;t*kti&j z2Uv3zF#qY;5G<SnSVwPiLyl5Hs;a1wibcNuWc$;jr<@dos}f|Lz%O9(OPRxCQ73Ro zQ4v?3>(Ec!8y<5?k9$ktXD~gCTpT%p>8Eqq$z1GO_;NavqDt{>E)$zdmJ;zoI+IN0 zqNVgyB2!4FN)nO?^sEgXa{y&Tg(Fzfazaa5zExTbWkuwgS<|i8<%StU1=;Gh8#E)A zg?8Q(cGV{xRITZ}ptx<BhFF!WxJK)|Jtb}uXXLsl5^7gq9+fo$#;a`&3K{RM4hn;N zhVlPVwv8s_gLpR`?G~rt_iYUeS?%SI4+kBswjgt2owvrE3>&#nj_y+TRC-D0o{fN? z$)|4SeTi%`h2>-{AM=sFKyosdElm5EI7NB!51t3%-52omfF%L={2eJ60(cRCK+D_e zfycjKUzMC=hGQfaUv8Db73-=Z!d_dih>E4WgH&vW{<fM|=4FkQY9dzc??kAnsMf&~ zgY8>Z)Ol9r8J^Q&pzT|$)XQZC$(+Jtzuj%+U%GBkPC@0(LAwTt739qZJG8{Aw{aQs zszB0(#X$g1w-yPfsA+bo`0E3&zk&y#VEm?xnNSs|HKJ^M4>cRhSa`HjKiF0k?Z+k; zThm;6Pdr!kexvV4Y!8kGl;LgdDsCKVF8yqqdTrm0pe-22TWm?+-_dM*ATBF)-}vf= z|1&}7+CDgmf5|B0s;_nZK#?*U8SIN$Zq0mySI5EZqdn#ky2l)*y=H{=nWt&L$<YB* Vq=V)n9Wr0iy{1WT0|So)`~mT%aDxB< literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcmainct.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcmainct.obj new file mode 100755 index 0000000000000000000000000000000000000000..e9cbdfe3f757c5af8190dbc921234258ea094364 GIT binary patch literal 1885 zcmbu9-A~g{6u?he8P(J<fkYCCSujK*2@1HVfV$C+4;c*FjgL5NMpvLqw~@9>)M$c^ zhix}AL=7<}8sCg>B)<71AA#^Fulhg?4-)(Xcp$0wj*<9CXVUhb-g|%deB5)+!cFjS zoe|%7R9D>MW5ZMuMkJt4^NKb<rxNM@yfPcn)af`G<8mUZCS|i#hHNbWRQIHGEt-tE zxt12Ur_}@5y8yq-JkssB;)ZM&z`pWHxZGjKEJfE-dg~lyyD|Oh+?&Q5c4+LGnluT1 z2jkwr#Wq_pNu+c|o|!#S-}$7e&8~VJUxMGl>Y@2w^L1?fh!E@(d_(Tufq-9(^aO=~ zZ<vp8J>h`>?-x9gfnhEX=KYa`k|5~q653`1m9&&hSQ2ugmW2GYYB3tsl(ID&X7Rep z(Tq_wS&Vi$w3M7tkL~U(dEIWqYr~AGsd7VRC{bOWE1NcBr7S7Oa4DJAG@`mJIHt@P z1SZu*5{0DqX%j`lF~j_SxLu-&@&;U+wrWdG;rlKo3n}fDFV7onl{W5mS~^xtbHbAj zWsCo(T{=kYmYbnc3i&5OG#BLkSmu2pA5A_^zE22-M`)=>6dm}5w^i_2rl<-iN&s}_ ziB!b^ymNxna#$6xbP0gE5Lc6`DajJ)&Jy<7)Ki+K=s$6b{y0lf0Ach;L}3OEu?t^U z@GPJgUOb(?*6|}Z!H^HT>2*P_zb>;+8E3b=bO3L9Ftv^<w!C#TmIYK})lA}<C34o- zIC|znX$E~D#~i4LV>5@0;f^x}krfJq^`ppNH!_aGtT2XN@_3ImN@q42j`yW|3t~N4 z=s#It7Qdyd^PCG0SFP$z&P5$G=0(@yj_I_j@Go?1=LT7eV-S9pcd<QPYkFRKSh8o7 z7&OJiy>UuJ9BsLB8DiHK-UpC>O+3zQu!}qCP=1JA+&0f6N69S_)nc~}x;wTDkLq(n zN66q0nouvhy6YH0V-2gn90ECWkhVsd%!VshmD!>#|50W$g#1TH?Cx57`W>4wIkgV& R&%j=Lh}t_0QGoEh!C!%!S|0!a literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcmarker.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcmarker.obj new file mode 100755 index 0000000000000000000000000000000000000000..d65aed34f8bfa77291d87d0d67c67948dd52b7e4 GIT binary patch literal 4002 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 ToW#?G=M0`uw}nvr`6K@Y07h=9 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcmaster.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcmaster.obj new file mode 100755 index 0000000000000000000000000000000000000000..7e7d2e15a3df609ef183fcf94418a2ba2b781132 GIT binary patch literal 4593 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcomapi.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcomapi.obj new file mode 100755 index 0000000000000000000000000000000000000000..655eb62ea27e78fc34946682d95b58b2bb5b581c GIT binary patch literal 1758 zcmbtU!E4iC6o1{at;ob}PA^U<PN6U;&aR?U(KcykU0qAEPQ}cSCSTW#G?{67m=~vW z&>4{-BI3!LhzA9aUIrWLu3r5I9D49SsPDBMh^HkUl6<`P{oe1rU*3BwXJLY$l#@49 zhef32n#-y+IcJmvW-e6>I$c|03$kgH7U{dF*;RGPh-uI`3;>5xwqvT69^uEvB8gZ6 z8kYcm1dBK}kR64_1%O@lMjVa%Ns2j+?Zhh3xQOUy2cMttRT9=^ST4aa`Q0nIW8(=w z<W<{YT6tlQe)8U_aliDaF2T<sJwz`@Pa}On%uk8Q>yb=0mzLyIUd$zD1es42vpFFx zCgki4pDPM!xyq^pJ>P+j`+zE@tr3<aCt8wxzqANdO%|BB=C*!U5N3oLR!i+5XvHcU z?Yt*DRiE)ub<NccJE&weraD?BFxu5ST@oa5#j2YoQ3nF+tXv~7O&2u^wfBA-MM2FF z{~u*{&_q6hv*~NLG#meSt66BYKYV;J;%l@GOEc?wtIdg?Hk42Ly=B==(!Gd6k20IS zIm_{RA&qAtIh*9@)hkSi`C@@nQj*k-54`Myx9=d-1+5bR*Ryx(3j@6Bh2Ca&vkNu` z0YV38Gb?Jzc3fP^uz`y?)Zen{45!6jzDm~PXg%7#Y2x%Mck8O<Dy~{GnMwW;^dI@l zU#XYNZTAy&zkUxPfTH(Hn)B*mFT<^Ex)Yz!1Zc=`&BDpm?eO&4_xrye&ka2K<_><z z41Yy$d}sY>KVJLUhXx%DcW%#bZBQI<KJ4)nCM<ap*A%0-;ydxp^=WRMAMV_#JVd}E kc<FG#D{#%B)$RVzD=TP&c8tmpM*i17NJ-6afrr|`A03D!qW}N^ literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcparam.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcparam.obj new file mode 100755 index 0000000000000000000000000000000000000000..4de111311e2d181f360d3f849938ca7326b81dc1 GIT binary patch literal 5494 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcphuff.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcphuff.obj new file mode 100755 index 0000000000000000000000000000000000000000..cacf644a807428b0c02347566b05d4546bace903 GIT binary patch literal 4664 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcprepct.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcprepct.obj new file mode 100755 index 0000000000000000000000000000000000000000..744687d5607fdf1231a56a257e352da2d4a13c0f GIT binary patch literal 2957 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 CxgQ(= literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcsample.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jcsample.obj new file mode 100755 index 0000000000000000000000000000000000000000..1bfaf5b1cc4056c1cf7f853ae63f462495732869 GIT binary patch literal 3998 zcmbtWUu;w775}b3liV^R8=8ltL~6RND^er`7*vK?NgUr$AP$M08_JrC7ZOt(<qxvs z0vQRg+jPD@MMY$qmUf+Z>ckV1T1DDR6+2Nvs8%^{#Jt983^J0VM3Yx6;uUXq=eu?S zR6OA9;oA55yT9}2cYfy_UwWPFsoU39d#v5-u5zCD^}Bm~Z4I82Hh1@#(;jwr_>B9V zwr<bKF81Bk;cM?X)9v2gK>{xlLX6fvZ+CldXH{Kwb=98jdr067LQ?BAX`!!Gk${De zMfaZ_KQR{*mfP#?^KL&)0{antZPV|5yt^1QX6EVbXYgMjyryC6&Z0Q!>GQffPQA10 ze&4{WJBxLX{4)4nbWgS&*tP?sZ*m+x=%}?<)gNjQoNd;lj)vMsTU(t~Jk($l9DCXh zHP$tVHleM@-NT?iFF<z|0d;lvbucWWoVjI`Kd)PawxbT#>)F?@m1{ko5$fsH*j^9X z)qBd5k9%Js>s>`;?d$LK^sSdNpS#`LaeCe8{?39*))Tm^*WcaET(1k<=|1ISVAfqs zC`{hZD-;)UhWP&@?E;!Puf*Ogs<%4&u-=+jSZn{~v&)r5wYI0Xqubx9$(-TYgenUE z#JHr4yWejk8(b~ITP^y!qc#EWZM7}6diGIbJLotnHtAhfr<3AE{!mJ8xL@FOM7twI zzhU*Rlqck)5>ld4RYyK3Bb&QA`p%qp<tMYt+jrJS$klQ}xXoRjUQfTPv+rzg{?I#D zO2~D+o+CtrWohE-)vNg3+<FXzG7wGGVMWM+JvTfvw9Bl5z(X1YgC#roQz*e>8V<gx zf|whQ4)FoL=J0UzV)mdsrPtoM5O<w8juz3LJee^^ooLk0!}~4hOA&K$!X}R!6d?m* z8QfOvSp-3LX)l@udp2}W2c<L&o55agym`bBx~rqJjwWX-2pQERPnzUuy)0z$%IH-g zCr`8h2HLvc629<->%`k|Uz*VT&M~iO*v>;!W#}GN#5jlsxE#Y(1;6A+S01s)G>pdC z4-+t87+q0WP*W>C8AV8gn4bOqJe8vu@-hhVm47xtbEU%xc2jdI`d?MWP|V{<k~yk~ zS+JKuND<~1uP|%koOv8J;(<M;2uVdufIW#OEVq1}<{9>W3WOAh8B2I@7mHp@VZxZb z{x={dXxJRJd^cn;&s&4#HS|l%cNa4%zFt6F_zHw&8UlN4bY(Cd;8Y=|3Q5kN03iut z+!FTw5$qYxo>PUaEX=WrKoioCwagknJgKT`fuwxBkyd{hNIcY4Ho*v^!qG)qyNH%0 zJ`s7cT(iI^bik3yW{}xiO|7Y~{-bel;_X%K$dhTtIkJjskxX#7GIW=+@`_m+b-<g5 zy`*8CscU(x#vgpGi0J}<nWY0r3kYeX=^;p2AeINP#rLvt>>OvFuMd{X<2t%P{~`a2 zn|%~`$%?O79(eCE9SCVn607OvEVCCse^L+2&x#EtnE7FB4|9)e%VJwD#Fk$A6>0*> zH_v~8LME^g@Qi>%uVA1Uh?Mb}^MMxPG>+|~3&ybljxNYy9sO2*NJEcwfr?rz4NGn? zV_ES2O%|5<LIO_)Kk`(a<jGh;&sZzJX;so0EH#~JzS5X`TtIZ?Xin|GEI|Go<NPbB z!C)gO*U&wED51YncCGq0+)?aFMM!8BAfId?pO&|B`Ej6E#8@Ha_~Is<IjAbwsMs;} z7)UW-r!mai$7AZ|d6sdo91i7Js%!Zw64GQGJI@6x*za$!k0Ip2IJTF4oWm+&DOUmP zDXdeZ6i6vfN+C{Xo)1>YAv{<Gvv;O+cnZ=o45o}X2NTAdQj#v9EA6NB<q2|$kkBJj z;AIv2SIWpsXp=ELJa2se22!zF9cJ}8LOrKG73+ZZIPmOXkE?}fhqSkbiVauAKG6dG z?J?}pq60OK)gZexW*!e(5$)h;jDCZY2I&$~oRGrbo7G@F9v$N7ta&Eb$k7O$<!}<Q z>4tO)NT-;j5hi%1`Sj(mM(6jYz-C#9<s}+l8z<Ob#t-jd8~;G25jLm|Y@$4^(r0P( z<9|QaDB)H$`T|pDC{d!=6N(Vm1geOUwE<g>y@vyqDH3yw03PvR&1n-m0#?0(4LZ&m zRay>mwZ^fn#<4A`u#AeJUxSpU5#yK{&+vdgU?QUr;G1eBi+-y}Nk#*(FDXtZP>dTi z3bLjE#R<&%0{6omIi*vufraew_`AoDSPuI+jV{povu#VU=Q7SDa7QzH_6DXP#ey8% zuD(UTq_c{cT<J-Gm;^hPN{rL+T4cBYaQH=V_&uKWT5<l4J1V~q^P?VDF&iNgKP9Yi zUAwzDC)DZLM#WW(D=4Rou>FiHpIrBF9O0+gkR!OBrfc+Xu{$w0W#vf_KEsFiX8Mgl zr)$b8@WBZ$s#}hmXV|Rw*${E%oMAD=AoV<qu!U<0`*%a5A-_8;xBRcc2pdhq(f%#d zrF%;>w^Ow|x5&D5x-^Tf7ypzuK*!fiR6J?IfN9{XHXKk(??Nk2qX3$#S^o~GH!v7m zHt6{k0L>YOJY9Mn|9fGCQ#CxdbYQwP!Qiih%#foiL;P^m|583md@|ru!A%6umyGbE z>DscN;jI&>YC4YpMA(v@YQYT$NA_b!e6SIlH$QfV$NRSi#g+G>xL+Bp17Tenut_gZ m@`bh`pS(hnx)v^Jz{`Wzukrd6uRD0%Z{hB5!lD2MN&W|%Ks;vv literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jctrans.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jctrans.obj new file mode 100755 index 0000000000000000000000000000000000000000..90204b13b31efe709e35943bb6afaac19b22701b GIT binary patch literal 3432 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdapimin.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdapimin.obj new file mode 100755 index 0000000000000000000000000000000000000000..e427785da96c77b4b9c218f18de95c5803297038 GIT binary patch literal 3230 zcmbtVZ){Ul6u)nM>&mEZV+D#%T>z5_I0tWwV=!3vI$&F2UEfxy44z&4#ya~i?@t6F zGV&nq^=fRKF~)?14>QOI6B56Wn3x4g%S=SG=r=!*Xu@WRAu%z7OzJsrU04(&txe9o z_nmWo_x!&df0eZGZ9}fZL0PI7PARGsQ-(Z|VYd`bjz;j*lax*jMI*ywxQ~T{$w({` zZw!%%T0%%wXF`q!<KcRK>(=^~#uhTMhmg4vuiW?6i`&V>E<)y|%f3xqF>Xn+oRAww z$;56zfAG|Y8=H$kLu8S-itu)TcX*!NUQ|wE30Vq_oLDyB24NIy9`cLuRxnQ*_BJ$u z_g-J$exK`L{l0EbmoU`X=kvG@xrg}9!ETSc%hxj0eTerAy1RyAQVgLx3()OFK*eYx zgjh^Dn#Ghqsab#qqf*J171b<PDV6~miJRFj1ue!$B1?X^6{6lzMAV8JjwDJ=nIZ+{ z&}hkMHC*VDQXCiKS~QBLO9F?b5e30GUDznt-cQ;nBIFG4|0C=I8jaV%Y!)?Jp#;?1 zY!)2tt6!X}D{8bO@laF?n{AGGe4&cMuXrvkn7fY}NVz!Bb!33$``le{cDn{#EG|~} zeqZ09mlZn&!2(1+t{_($%b7A_o(N&fmroToLO!t)Yu=JCBkv9qD?9;;q>}2fNIas7 z@Rg8HiLnzhsV#7V@*9#Y%2F^a$z<IUGZIf~stEZ}<wP_Jd^^7IVpxLtN}M8<=2vhy zA*+O>>j+`inj9W(Q6;w&O2m?~q$uzwK+?~Hc>PK^T(hk311dbin>Vfy|AON6fUh?3 z=%YAlI(i#IPj3Lp8dCzEDq2huO~1X6&Ga)Mt2JfIvBZ*5NtFl-!bdKR1{E=;Mb!uh zMI{uBE4xwQIY6&7(Ib#HWpss4CqaIRWf?*S>0QBpn46}@Y;@30wQBvr9)oAq4b+nD zaM;23Y=;y6@`&NB6KHLIP2k+atAHygtX)z-j*%=VP>!Cqk0NV~2y>u7^c!}AGfvwx zd^NMg32@T}Z{ubS!O2a(H<<<{Zl=Si)UUJp0(<6nWL{ckax+FxbwTnQ0ChXuUAU*P zWqHR<P#2FMJ@$1PVxnC(uW35)`0swfj}h~m52!WzT$%d9fvWSEj`=VwS-t^Q*tX#| ze-zf#B!Tt(tZD}#l!nZI3rmXHkUOIuO6jZMw2hHj_34}wbfC0k{t7J1`WN9$E!-?* zRYvF}0t~NnEHBX2`F^&br{A#UG)ixvHbeUdsPP#b^#w+&)gP2;PwV$B+Ny$X&Z95* zkiQ@KohBdc=9(#|XPkz%NVP@7O1l;fFG~j(jUF3aopm`9pfkn@)ROnIV6Si+glmEm zsYx@cS+zGs(aG_7Mz%hjQCH{IfGTtj$+f}(rj_Jg`HzrL2}7zVAi;=vK_xd0#pfFg zVOy4O0wa*8X56?LC~$8p<cWvp@ZR1G-$63Gix|8+!yjNWd^b=JXnWAv4DV&K`~cR* z5Y!BR1ULbp*U0gsSot*ef?!tK(T)wG*0s;zw$^NK2OO#3fGS`OY68L>yn!S@zt?|d z<R-PeM_rYB=^DC~+n73NheB7GI1V^V!%%3yYv>HNo@ozTi~ED}j`G%gKCdrSzIi4! zu0q%3H(xjl8*3?J%pYNXhR+{?ZUKD`Gzlco$u!u^``Ca$d!6}yTL9V{-4>{BN~#0& zp8k;0+Jw2LKl6DMe}hhL`O5Tl(@MSE<3ptxb5zpg;!ZLZ^fOb6zijGDzh&yWpPl;2 zUp{r$Z@ujDTkk{$m^(?Jvp_n~J3tiZ{Q-tt+QBe{{acF9RJ6~6GjIQkT{D7IM~#`S zmF2+*o(x<44=fI&TG4(BHmx}&bex~nUZ%Y#DgXMen@Sa}&2~C?kQJ&-^>NIvm&L@P zueomMf^!ePLpTiA60e2!I?mlkvAX54-IA+)l;;c6S~)ijel685)l&n_a$BIhGw*dm XU%@RTaFqcCa<QCzy4i0f6no|`pKAfM literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdapistd.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdapistd.obj new file mode 100755 index 0000000000000000000000000000000000000000..ba183e825947e8f7c573803e19450f23671eb4b4 GIT binary patch literal 2606 zcmbtV-*4Mg6uz#V)?%HSE}mAlJOmJ;VxppS70^(q<CwK1Yn`|mM9px$#9bZoqw)_3 zP_b1_rKXoj6`K%3LgHc4#KS~UUqDPK0z?G^M!SEaC_JziG*D4E*G{z#ZD<J(iBEj| z-Sd6lImbEoG#V6!QvA!Rp#|hOO-sw0si=NBtmWorbrQWar=3aV^wU{#&1Td&-Lx`& zX|(J^2z7;uMoukc0>aUwfx*5(wER3m+bteBPCphv%Rz*G)h@>$8EA%<W*9}IZx$^- z2k7_qUwrsjGiXqz7c2rF2KY$yNPknF<co%uo;kBye(0@7`<pEf{u1~zP#*QZ(EB8? zm*Nx0<NS%hi(^qyPK74oQGPs}5<-cwXjqI7rpCsFXd*18@>-ripKU<<n}900Vwzxy z<)kdJ{C>*<RLyBES2iuDU9D6Gs9tc~ZUwCrW^|YLP($@oO{j*^*Nd%MW@@UDo^2Uz zWg1h`3UQ@i=W?WUOW=$)V-hggE;1-&yzh5V6z~l2|3h{IO^W+rH=Ek6bP@XP3=3K9 zwe<ymQ>(2P(m6Zhj5)!RH&j#jJ+Diec=vuU@+gzyE0c^c5f&j1^OHP7zS_gb;}Z#q zQ9`mz!v}rPfj+rHQ*FqJ5Mn&LQ3s3AhwZ4nPS@MeyNA)CtX|M9C9lHU+fXuEx|p9c zG}A<A)sGN$ki5B}3#wt=k%aVNNc)_$p{W_gOsj>QUeL@k1<3~?dEj=^P~T88s-<$k zXtf7&-Fwb0+SZ(HT_vg8uo<9b&MIy$0Qx`vjD~{~_%|7!VzIyt{{Vj>_CxYETu^dF z9w~2Gof`so9e%D+Q>VUx_!`7m<`(`Uf&CiBt8l~$?=4p?8O!X_menES2wU$?%6O6F z>fYp1)%FZj0l#>w&3eLp>c-+N+M-A8Yvf^`!7&C4EFNWpoAaOJ3w{7n5?i~1h55>2 z8A{axIKnOb`6GnZK}>IqaGcAeBK634AE~$94tJ7JW_6WlI@u8AKuR*caE`D!kR15F z5-EWaa=$iR8-lt^Rm)qE7%Wd$B(^~xo2BzYDT|~m0xao3J<cqC>&1~$@TScSRBHm3 zOE@we+%&F&R_Any0oivHauX{OTi&v-fAKTaToL$k)uzTO5pIoo)uBuH#&(<V6FhSd ziC8EB$KcmS58k)Ib8o|w*}9DBx&+Kvpv1dke87z8iA+~UL65*f69%@<i#wIX4nE<H zNjT9Nmoau!#T-@Q4h4scJ5$Mj`$ssp%4h1{eDFF4o?_A4_33&KM8V%*@4~)~5O)ab zxgEt~rv!E`JKlGKLKq}syoWMN_&Z{q+h@D8(P}cNmVfv^hGglFv!1WuJme1Niqs7= z{s4~52XDSZV{^W;PKHh1{_^j7J?S=Q&we<q=@=%C*hk#`;J`+w^)V(1L-h-!YRAYf rk;r=du9I5x-DNkhJP!>4P;m3?{=MvO?A<{0kq2G!oum+k-A4Za!0A$c literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdatadst.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdatadst.obj new file mode 100755 index 0000000000000000000000000000000000000000..22be293e7e7d0fb563ddf5cdfd1f2ebe945dfe1d GIT binary patch literal 1786 zcmbu9&r2IY6vyA1HRd2q)SxN#5D=>%h_(%FwYEt%QLL#+ck`p6(IsX#X2T|t-Azk> zL5m0p(}Sl{3WZXH(woqGX{nHZM8QH2o}{<*%_h<w-R)s^hMD(yKfe2BUS5X)H<t@# z@|x@yp6G^L(Q`3nBO<F?C55i;ZpoWDRoN)hxh&<4yrdh`1!xQbfID2%)O=O)b5m3P zz;pl_w*iiAx)dA}erU`C{FFcOV^^)DC2Lwun=V0P0qJ1u(yXPTDm6_m6gMe<3;DUH z<FnVSL`P*%Ad25W947Bf&LE!P<4b(#o_}#U78P>gI3EkGMsi#@wH%8?`9N-Ym5Ze! z(OgBYQ1oUOowb09YOO$7su>MSH9K!uMDwa_JF;$=dfSPNs8Ti6wnK~6qSDEGu3PmL z3svieq}1$6rptM)P_j)MQnyR&BraC#s!BuKf+e}AQ<%0lZ4}zx=WP@bHADVC%I?xM z_$b~@OLwbK!~bnI3%%O=UtWz`y4p&$pw=a`%_&cx7)$&=@128c_hu4$#boqBlI7x& zD6S)+WQe67XJm<wrxL6f76b<lc-se`#~7vu%oPCJd$#Ix0lag9v+Zd2!0Q2U79VSh zA>&+B>v{=bcNl<i(-)VDuhc|I){R4aZ$C^h3_yzf64Dt`cae2p2|NI0CQTOEwrBgG zpZsn<^EKyN97_+x^@nRec1`<^IpMsCE<yw!e~dn1`@pk6jSV-s&<c6SaVt%nZBG_0 z_L=5T*O%$4<10g6xQOWcN-4szQztv8k1i9z%csi>S@GRJb31>6KKoih(`SZ^v<I_f z#oIZ~Vk)P>K@VDtw8C8rcY7fFH)pcuBAjpMTisJ0ErUP=wmpa?HaQ<|w}Pl5Kr7fk rV@{suIFVF;GvURW;r&I}TS+r}A8~w7_Z+#B%#j<%aFPLn{{;R3LWCob literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdatasrc.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdatasrc.obj new file mode 100755 index 0000000000000000000000000000000000000000..0b20f8b99efd1a0338d28298c73db6c031c139d3 GIT binary patch literal 1806 zcmbu9O=uHQ5XWbmZ5pw(p{+GykOf;z4Hjz++SZRGo1|1@6Y^DyX_hqEGzpt**=(p4 z{Gu%+J`X*K2!aPeya{^n;Gtki=+#rjtA&alJbPK^Z6bPX;^F1ZzT~(6nSbWZgA3r` zC*!WUq#|<R)siX}N^yT~(IZOB*&L0oEsINWDYuxXdp@01lO-iJnu1C{0Q9<LMM@Ua z9B;RCj!_3xt^n+}`O2u_6bF?lfN$cf(8=*u(h?O#Rz|Z>xs0^ae|)T^q6)GirZP*E zzl8kco#C<bt*E0jXb{COA`Z5zwzG%_LxJg#>jvkW@q5E@cOc|<&3fXzJ38a{ctei( z%q;JZdc5(1SfJ=c6CG;-6(l)DS?U>;rJfyB7SW_6wyj)Jb-&wDMl@H{-EM~#ikVy^ z@5yG>=US+`q^5InyOx#2q>{?EO{?i<m$Z|(P%KLlm2L}8i<uIIX?xQ~q3wOpMiJ06 z<o~1WCQZeycsE<RTPYd;Z@pRQ)qeB#p|z!}ohzoKa$0Y5%F_|k693P<Gf?lI*q~F0 zcyC5nKH%|U>~Te0Ed4NgrbB^fkQLnFumKl5?Sj`M4ATL61i*IwiMmVx&x~Ny3|a>~ z=Abj9h{-ez(h(~tVrjLQ5>%N+YEn@FHje->J^HtL{|Y#PKj8FzafV?4qT~nhnuya< za}F^JA2;zi93y2DNmvMP4Q9a%kY`Ak3+*EVYuo0Ht(A#K*N4`3)Lvq&I|okVaf}RV zee<NJ?i#p_k9jRdj9QRg+cpuNtASQ?GN0dLEr2ho_E&kf#t*?8CUFZDHyfIBWTBhf zHIWs|#+Q3XzaO&i&h5*Zrp*%{JGFcNCv##X!3sKoIsWBnu0G4gR(UvY%VQ!jR_j~a z?k2m{gr$)^9{O0#jE7a8!@9;ljqlWXbP=G=cVm6Hnb-AgpmAt<hdc4&@2%A^TRluw e;rTImX^$~qe?^!*2A2s}Z>(c)Fv0+#wfzRX+BhBn literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdcoefct.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdcoefct.obj new file mode 100755 index 0000000000000000000000000000000000000000..79348f7e1b30152c369ea8d75b92a851a808c12f GIT binary patch literal 5655 zcmbtXe@v9;9e<8{h0|R<TG^x<TWHHNH?pSfY3jUo^?(-vdG+9Uai&zF{HRa_21kDl z6+BR$`+AWz+R#Q*|LE2(anYtt>z1qn5<RS0k=cw!vRoarM~*ZuA(f_Vx6k)^0d2NF zge7qA`#$gUeV)(r{eHfm@AvI^T&&C8P~qLSv!y1}|59t9W>0HHaow)Gnug}xb$nOU zT=Q&2L*1@=zSdV)HPzHs1s<*vJ*$KeuAHWphMkSonYr27nd=^2CwhJ^L~Mymc0W+@ zkm&IUF<J9g=|k?tz^Z9!X=-_Rx9E8S*00=i^ue`@vBt^j8Ut+pB+NG!fA7&n`D9O1 zOHEbnv-8_;c=?BqE_QqPm(AB>d$H!JHID$juXO9CQm>JjUs$aBD{{7$7JIkkRpjQF zg~fS#>AH%-ExE;Jo?fx1W)GYGY|ix2MVMALG*z)JyPUUWm*4HSFx}Zuv*evy11elg z*%_vFjmq0gv94^at+Q}%m^<}ji#T;_pt`PUsV-}++1XOHd&$;;>N%Dy1$bp+TSEhH zy(Hu6n%Y)2=ImmkFum`VD3$OT?EjBr&sp>48OY5=<yKV_;;qQSseR_%&WuH+c3opt zLtC|?Ioq>BEz155xwNzIUR@)WRhH?$DARJc=IOZ4^Okuv{z=Z;RJzslX_YyCza9Ug zFGc+B_bywKP<KLT%jWM=974RFERqxUM3OkXMl7#?xu&J5vUX>SaMf2eHNRB33)%Tx zWlPfwc-PX@)>vKH)+|Kt4}`F-uCHsX3shEDvXJAaG0@V~&`{HIe5DX4HO(f3NmHad zsJ93A)RTqZ!hOpx=49YT#D7dZd{gM@>A>UjE2(1*Puu8}IT>*$^{6Lr0iN8$?tWR6 zCUerCIp1ro=^mB6Z03vgB@&74*0{f%o^jB-YPbB<?*71oA%9`lXdulWOHLMIdsly8 z`Ne$a@f7Tyf}0Ud2=}n09xSxuX={z(&}D!5wguGEoF&6So$CRuK<d+EIw+)IOXDW# zv*b&cW^}TbksGt*O_TZznKNWed#<<+HS#b!U?Xk@uBVi$Ihno~HWa}j2br_M!Ay)g zN~p6Ba}quc!(IJt_u~~AQCXDEo0v1wm*(!D3}T9V6p+m+X~eK{xR<>o&C8R2!-`~F zru##=kHG$0<bT3uPEGc!VI1H%=8<2c;$BS}Bg81DJ%g>wX!F{*oi^u5W1Ms+&(M0w zjJi(yvj(mw*$1UQM#hZm^yZDyxQa076Y(15gbrR=zv+pzxG<lL3HuewE%1!&T}ykf zy1yiS+;em9nr&o`;e1o}Kl3@tdNND>eA<LL5*$ji|0*kA&1)Yc|60JM%~=DVCE1Lz zgg!y|^RM>|w*NYz$AbE0O~rvZL}tu0=sL0zfluHZ#tiAxo`~z<EB{U;;5N9YC@rB! zRdh1oL`339CBM@hi4Kl6@Fxx^{2}WUh!DrrbE)-SMi<Nb7dUkmX(r;UFu9aM9N<KW zbNg9PMpW*2n*IU0Fc;nZ^Sp_;hryh1TjWmiNMoAA)W>0pd&v&})!WFNpqHH%$rym^ z^k3nl3G!+j_2L5sC9v_4PfL_$!c8(cp-%@dXu<P$`T)_}e_HV=UFuVl%avnh329=? zOpaq!_cadJbl8kY6ESpzI~7;-X#6D(*b0dq;bY%g*J<#}jKObA=`k9S=6L)`GRKLZ zr|c0*qO=||VgsM0z^V2t?%{A=UuOIsnc6wX_;Z8(nwQ!pXy01T$L*5|9cEZ~J&6<d zh>2IrY!h#k*?x0>xy_OV&A92gvj2TPls={f^{A#irb4Uo=140di=Be~nLf+?DmO~z za^pK3S!94OosX~2NH20D-pX9y?BrC%C>U`lmDgEd1hfeG3PwyZBW}zyf=@t7OccWu z5k^1+#64E}_1zEwGzboj&B?|YB|n_>yiySaBo3UM;EF@3+q?VQQjp7CDqs#idQm&| zb^(RwQu`;20jKb`Q%DUBwBfi89<?BLGJ!oK&!2(<n$tXvtR11(hA<r7Qh?A&BRcs7 zo|F){x<0{ey`WqtyOKg|?m8U-!=jXGDUVl6_?TxD;TZ?444H*EIA8^C#aB~qCbc@< zqqn|<@_-UJf?c**+OC8J!dP}dli;;JPO0FX8KWywzp0qVB=SV|ZiN)M6YhR`3>q2# zK9nI=T0W-+6WG4<LI<!x6F89g^JjPlTRxqagcw-Gbb;g`_K<~lm^eXwTw@-cFJn)5 zvy`a0j@UTvh9jYzMb<ALUd48xHN{HpWp^gCis3Y3=)Oj6j?lpfu+WDbOH_>&^y*<e zcaN&beax@6HRCCrwm`7#VP|1Uy{(+`wM1%g$SF59<29@Fv;;v9a%gbm$wO?1+K^K` zM}dPFs12?cfrn~?FD8jV1}>>0@NHZ&MPMZ^_lZCXE}Cf5K=JK#kYRw)1J@n)L2`5t z@8xmt9{>}>)4@RvsYvVP23p^p2qe90^IAi}ua@nDh)$;`kX8Cj8Rf%O2(jzPbL^_~ z3yQ{));Tnd+(0p960-;9iy*rXMQ|p*i;04_ilXFe_grdw+`_;n4VczF8n6}0@QYR; zczS0CV^sXJWWYhk__h6#XW)gWu$qs0REFF;pCP|Q&Hk^A+S54FDwjgOd%H$mT`yn} z)-H>V%Gu`7<k#qN$QTNFQ4L*PH~5JepvcYQ(7Kw;Au4iODrkpNMla|eiug}?I49G3 z3C90Zk$M6VTTc2A`JDW0Od!cH-DbH62kRbv-oSR`b=veO(#?!=Zt*ILeFeAbk$?IW z3szZY!8h|xB4-p9Rr|&-=;uWIM_5CT`gkSu0aKJ0DzK!mNK2HcqM-9c0Du$@QFAjE zABftis8||j!`enWlJ2bQgH&n0M+R=ZNm4&UChm$-rSZ0U>C2SnN$c%>Qh(EWi|*hQ zp9*{Ss?^R4X;SYe)5$|x2(qJJ=JZCtFd(}F9AJK$fTo7`YjCqQhMmVk#<{NHfQ$4P z+y`fjN%JZ?yRb6y&Px4i$ovrf*(#V9VwNW|H*4hfENnhP=2h1@)T^V?=#$2A)F{_E ztluNe!_w>}eZ+OnA;ax}B8?7dbaCB-mLZNA?okjHG6bbWAx5|2I`ls3dc7@tj7g3_ zjFrR?KXhYGv=L<9gzo)>3~Z<fEA^wm?K-C#1!?rC&6E<$W4s~l(7La=x;V9KgMa!G zONzF=WB^o8cEokscn>iRJBPc6+aDngz?p$)3McOXU3%@-SKZh471Bq4<m~;+b!gfX z^wC-M5G{y*V}AC+{MC2PUvYc>t+86!&HK|12P2XeA`KWt+kP_pNbkrVL8*U_F5QXg zZ8G0p45r)j?-COupdC)ebi~4x#+Y2)o#MnY@mpD*Bu>6#w+Sa+S}^S+WE{2+XJ28o zVdtpjypv>}Tny0L^Y0QU!=fFj4D^@<G}!`b={9J0G#kjevae7#hu*Hd8Fs#6;Y0qs zxfp!6=ienhMngN&?r`J`(~TX;o@2`WZ_k|~^VDLH-JXA!$QTEiky0zCNWbhuUJa=x zHevR2XJPh9^EjylCjGF~dr4(4sdTm6DfNy{eXIj#>C~@Uml3`wY0R=25!Ctl40RnW zn#Go^w^;+xB%6MjzY&!1XbWm|xD(A=LZ1riLuIUJzAPvhcYf1f!FJ(WpjVXyt|_WJ z82M&*FxTlXbO*H8Q6H$cZKF-7^>h<T^8r^#sK>?RGN8Sf?`Xh9<~N5-Cs!xtJXfb# zdPB8n`TP|buOL4P$xP@7w;9k#)Rq~jw)OE?N}HV}Xt@?X6Mf{zH^}CUd8Kmr>TPov zd}+a<bX0zsgzAn~XqH={tyw4wp2+^!FXn48eD%+L>W0e;WO-`*$=w3gknOjkmY}D0 zQpDXKF3+T~jRSvAv41MR%T2?~iuNyHAGPey@G<&je(GGOAWl3WZVZ&!ZbZs#Z>=h~ bzm-*P|Gd1+_W3jTN5}v9i87lY*oyxEFj?Kj literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdcolor.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdcolor.obj new file mode 100755 index 0000000000000000000000000000000000000000..bbc03e3c1bdf20492753cf18f9d71ef0cad9175d GIT binary patch literal 3065 zcmbtVUrbYH6#p*0V4XEB&^e}!Vbd*S7y=?@TAhXVsuU`<<s!t^2HPU-3MFl?Ml+pS zxAgYRh5e0L;$pUCX1>gX#JE3kqHGMu1Dj+c#s{+D(ylDS$wPH4cjvnWB=};xhwpNF z&i6aN^Y`QUMzX=U*-^WvJ>)I59}Y*nU13L~Z@<YK=;`!P$=u^T;0XBk`{`f5#~lm= zLltfkD<*`f>w=*`d$*_5SYBRQQ&B@=TM0?z<}smS*N~W=kfis#ZB2DHL3u+VjH#2v zwjlcL<?lY5RZ5tbuRB8F7<|&uxT>lq8}hDT$m{MnFh{@n$a7WM(qnZg{6(ZEW!uUs zk=|-++F`4;ly0wY<m`^RCR=0eE|bGp$JaNSI9rXQewVS4H*t<GZx@AbnuS(n0d)m} zZi=NPr?j->k4uZt_JB8M=5R!AS1!y5^>xeD&IRr2?(k*e-aMQ2`Yf^zM?Ah@t|be5 z+e7ZooY4`_?3CmZxT{+T1SoY*V2`&WOu=-!=%CQ?e%wKEVa*W#KhmB>Q*s^dW>&l9 z4r0IMVWF#ivG0T~tJU^(y90tp9&<`hU#P6;A3l}}TDtekNWQC?Yi(wYO(qUyQ*CoC zOFsps9kwRk%DU?8b_E`CW(oOhAfL%2vP1}*KPN3w67oR-DUcLW9(hAc3jOY2&tX@% zy{jkSb%lb5!i4m$CWLv)@9XwOT=IME@_4Zn2fd+)H<Z9w&ao^*2oIC?77V1a3 zldy#NnHJd2N}A|^N;E3xRV}PkB&$^Os$Hy9D62H{s(GNc`s$a*B}ww#NOmyfL_axB z2!xgW_cfyGb>EGTzI~`v%_%GDB?-cr3%{SIvNi*(LyTdog#T@heZFt6^LlSD#vL6P zwYTg6UI`~@>|l0?mMMsrQrW5$`AM*<L_PsljmS@cK`V0O$uH0aa4K+WK+sf6paD(; z+LKgeU_p0A7L>5zqJ(-*3na>ES@Gz!D2$5EF_FIw{4_YH^f$vHz#q;r^`#>9rB$MH z2Ard^kOt=@2x-vuo!(Fw*R76~fO8y#88}LHAWSH9SJ2Y`kLI*iwzo3iOwbFuzPNT} z%$gUys*t#8i5r!;F^QXJ4818?O4Q;@SX0-&y>Q2QU9Q12wvSUn&ss1zL~bUvyZR@Q zPlH7T{USFFd|Lnav5jDv1_pQyu)V-*p$IH##gAZ_d2Q&RDqa#>X;A!J|Ju+YBuz)T zX%@J&e(Kd5SOc0q(jLB$VU+7m>8uVLSW0&Lv6xybI>uirV5|u&FNqG6$g#}^=15i2 zs%pT3m5wj0YxR)+?!n^t%2;b&td)tCVB)oP`n!kzq>Mi-==1O+eV$y<XY4Tcc|z{< zxZLO4$$O92XZ9kk5k@Cyz@U1h?Ng#K3<5TPM0Add{21_4*j@cib@UzT9~HFe^8+w4 zHvrg9+UzTIU=_E@@5OahbPR4U7=v*f1KEy)Fn5Jc!UVmurSB^l`b+t(xb|rr3E+q0 zMKP-)dX<&9DTy1FxRK0&%!hQc3kO4<5cc7O$o-=0LiYvx;Z*ud{EQupne~P#k(|vZ zpEk8H<lOfRL)bpLNrMIB_&$KKY-eQo&S)Ypw3>d_r|QAJ)*NYtWrMZaConZTC?ri= zY6q&L4-}DYiwzaBfsH)B*zhEEbX2`a4fb{BNTE5>5&a`ieX0)$l7Hc6(CERKXgJX{ z6UmRJ6+-3UcD54V#mm%?S^*;f11R<Xswf{ZoYvaDGrd(wkVAj%mEV##DOfRR(5^zk z4$Gv%w&XV`qF>B1<5wXRqsz<mLgM}dNoq@;r6zlsSx}*&6}gf?H4SCa`+34r)P0TI zsUr&<7fVHLDIa>pCp2?0U(n#ia|pI4KBit%W;qM?p@U6OJXp6NvQ!xu*CDU|nxKY; zwPJ&3j!M0VD&Al|H56GoXzY}WPyj}cXzZj*L(V=!CYCfa6BqFO+sxcqf#(H0c04cR Md9RrvfKieE0Okoo#{d8T literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jddctmgr.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jddctmgr.obj new file mode 100755 index 0000000000000000000000000000000000000000..ad7d4bb3919a656152995cf2f274c38ea4c73a0d GIT binary patch literal 2645 zcmbuAZERCj7{^b$%T`{NvN5yi2q|-hC1cCF(M(shy<P8EN6SjvTShG2^yO~5OV^V2 zP9_WjT-05UfEZ#3Q8Af;sfjO{fq)<O(vb?rWRQJ;xL{3+uIVBRX|U01J*V5G@`=_a zw>{@M&+j}h_kT{0Gy_#@i-X+l46q98>p`CF4?2vTOV4`u^>TRBv5)O>csW-e{_XR4 z+<bpepxzCJHvj<SI)A|H^m!DmwY3UWy$TG!48VMzTYmpZodOKM06^?FVv9nVQ(A7m zz84Iu5zAh*Jlm8D%LW4e0H$9=^n1l;s<!1qqponiOz@Wwt~FLFbLwZmKft=Xdsf1? z9C%KdYk2f-F#bFW4{9_u4JdoFrE|N5G%4B)M#}2Yby|$%PQ9a5M;nZK%A#@@cD5R6 zJ>}?U`!R}Jj#1q5Cq)^r-;G(^a?IkEKPih)r<ct;a*)rwu6!aRl=Ee}oe$0Uy1Avi zTb8SC%%SQa@8SIUwk*gx1Mc3uY2LG}l6(?pd;?xD4xJb5VY`DE#-|Gl1<U(Mg~Fho zA^v}qy-efa8`0U!>1?_E=zV9zf{*rBV@EgUbhJ62+dJUN$Q<+dcFT#+x@rq>@7}Kg zs~8*AWg}WU^%NTGNgGMvuR{HHODAn67@gHxfF5wR2z)vrl}N$T2oS55MgSDa0613& z3eyE?DR^f;D8|=1!=YQ9Q4T7zr;US++0$)<+kpI$GZ*yw_y5Px?F{nShHkIl$%8_4 z#ROR%tYa8{AmH-1Wvt-c3IHS}eVmWu8P4|_8wj!t%Ap-){PDC@0ssvcgz1^4TTR!k z_T7+@!6P3at4JqA(_+&tkxmH{1T@Lu>1p&YB+^Ndio}x01DcYMtZwH^+j)gBFXgui z^8~Moy@kBO)FS!@m`=pbWErlDrbJB0GE9h6B0d0V8SKytF-f3^-(cVs@%oh=GVAp6 zEKDU(To9)o%~2H~uEPA9Fj>}sW~c%)T%Ru86W@fhmV~usMc7OyVMn#tUh(fbG({k_ zC`!=E+Z_$Zugi~@<Ep3#G}k~=LVZ(yyf9-(LbFk%7och34dnD76C(mqe=>eJ>ot6> zf%n9wGE2za+%y4iKzc!b`5EzPtuRRlQ&J(R9l9U;Cd);n=dPeBN)0>8$CdIg?;Z^S zBpWy|H~Hr}VNxn?!X!0EXfb{#J_xC$?L~SXcQ+cQW{;pPnjS)W{=~*(SMUVVvm$*P z?Ruh2C%@bx9-2X${7B8fL$|eUbMm>>!ldj@r4S;7zhy&r(^OQTW@V6$3V+CkF2$e1 z5|BOKOo56JX(}a9NgM7eSq<rwdP;uk_rKC<=G9l=y}co%qg_}*1F}9_pTrK^i1d14 zdY$+tmQxtzwIqntiNUS#Bdn#ksu!T8Y~NP%k_kYo*)oq2v_rTkjXpv+lRyZ?Pr<fo z^>;&)DDa-WA@E@xh}!LvXpdbQ9kdrj&)SLT1^cS#C41q$LpJHX3H1D6lm60UlUz7w zFG8{hST@>gUFWU39YAYn9H|;Dxz@G$U@GY9z1B5Ot!+45ao(B=lKnQ<dFvQS)|Hm) zcDQ<NuESlr9h=oxYeHp@^M=N8YK+{hHZ&gF(p(uS9;sT}K-OKY8L4Wn94|e&dbH$l zMQM4cY_ud&d~)?q1TGGMMa_L@``2dWL(L7p>GT)BCN!Uwc<;Mr9%xq9)#Dc@xayeZ YTSfTbfn7gq_I@6?OOM{v0EmnD2k1PN761SM literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdhuff.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdhuff.obj new file mode 100755 index 0000000000000000000000000000000000000000..75e2a82bfefd277de78d1e1e6a572cb7138ca54e GIT binary patch literal 4839 zcmbtW4@?{96~BXnT!||?iM!IKX`>~TkWjibPNcM~3jdA}YA43vKsK01z$q{S5!h8* z0Zn%H%JFcE7FtEssz$52WLhUh>XOm4s+<(@CylxkqKd7kWfff=nRV`hA}8^3`@ORv zbX%*!5a0Rk``-KBd%ySI@B0pK6Kq9~))Z9LwR_iBJl)~*9_*;GH|;O>wj63~;<vIx z-Y05Wn)WyIZ*xPV|G<HV>V=*)f*_a++uB>|S{v3E<>s#6_Ruz==ZAt2S=NT$@3}V! zJ$Zs~<#$&1hGn?anAG;3a4k*S+uq*R{t&d``jubaIq?0hOMwHJG`0Fz{V}NL+t)t4 zZ7J#p+uFVL2cDQW|L8LhKD<=($PBA*hj}4qN6u!Lce?iMbQL((m+Z1D6*Yx>T=s(9 z#Wh8R?p^j`#kH+w*X|;_yI83?=sn1)kEB!|UV^HprLCTIIbm#;6ZT!rLba~NyKKuH zJ|kVru?*FwRs-$jXnR@@G$s9hG!^xhB}CofYiMd)E@U0vy7v0UWo!EyQdP1X$33n7 zmKHX>Eae98feu#Y?&3n>^8T)c;sIvR|3AV`X|wVB(3?x@t@<|P+b9-p?Ne_axo=6W z-PBs&;%_j@oQsY5Z5gZvqD^PyUdj<NJe5jyrBt-1SiyU7L1lr&kIdqou03w2<SDGE zNXHP4tq^|Ozfw#SjJLyrl#zTBgcY(Nyqqay>gjr#(6>%l&6MtGt@HWY>smZ*?G4^` zArqv~;q?hZ|NVj>-fc)6toxbQ)8N5!)05r?k8gjAQ-r`;2v`jP^hJwjf0NI%ABTgt z{ihOE@5ZXdSmhG~%Jppx-X8eUe-BnxH#fCHe$i69eWf5Amn7`$rdf3;Qn9y+YGr!! z7-mRq!7~{4KR`zh;Z41<!*3<>@#L64i`1u#WuKMSj1*d*!O_&LSI|nmTvCT*n;t5$ zYDz40GzU_edoEf=jyV#^X{FL^%@Ge3zm(&2HH#IIacHVT7IMeWeU_H~k$Qf+iyZOt zW}zbT$JCM%vyE$(ovIR(RAo*g+K5f_u6Mp+3x{@>YDyxc7PA>gL{nx%FCBwgw45BX z^aussW(TM%LZw!+KcAzSVV^(k&u`cE<n)$q4vhI$52uM#x^b8J)v!7(_7rACSJ=Y% z=}FU-M4_rgV0Km_M}pLRwmjPTYumVOOxtVGcAJJvWMGwmO|4|3=O;g=&uic7?+2bo zhA!RE^@_a&*lI1ok2^Een^I>sDKZHo`PMD0QJqb0*+p6Us(rnRDG>IV@J)R$ALkIV zShVlhDHeoncOp3M|3-DnM5^v;>_@hnMPt)4NnfQ7nY2vGd>38|ni98#0W;#zdi#ut zIRbXoqAlE;j%1NL798)iaH5o0`sk7C0@S+iVvn*$3aqGP0UkNYqOaZup-|w?%eQnr zD$_?j7G0UkCr1n^(or3MFDX)QS-MY<GH3gEIFP||0=egGVa+{je)lwgYsy^IjycGL zk`keT7a6f=mUgdtUg8~#%<sy1U-|w)9}IE?6VrL`J!|-r1^^9z=Fv<4xC9&Z4j85a z<Nn_uJ&rjeZc^eQr(JVL5O}CS?6m~OeVaH)*#1?BEs`UWJE5(zjrUqaOya-q#fv=e zj%a0r$b%y?QZT6Fk87bMpJN-uwj=q)x^5O41vFnXu!(2%!%Jrb50tbCMeCk-ArW$l zaA8D=aUfhu2)j~TrMV{!C=EX}Wf~^wg65csR+BP)mP5?_h-xQEpbHbzbm6Q4!)J5> zdDLpBf@4%04-KUC!+}shW$WFmwasc+qS~oly=!~!Eewu5`=zcRG1EYF5}=YY!+|1M zc^`JYT%s!x^_FZFUda$nIQFI`=|FmZKY~qGq@BZv8Q87Tln9Chodwmcq(sbTKoF?w zxcM8(1eRd{wn&*p+gx1K9?UhUZ=;|ON&Nbhg`Vp~fWfdiIKU{mE$WSX%%KnQ9=zsX zTSa@a^>Wj`K-iD|yz*xj*_n+U4oSPYgo_^2G3DQpl vT??|m|KGB{%V?SvgRB;n zHN$vx9l1^CK9g6T*A$bgSY&co)a&<{`<?+J(Osli;a1-H?j7_3vt)0940rCQyTO5e zY}2e)kvYkM^c)Ke=gZ<-pkIzA_riqNG@@9HPes<a`L%Wsi%g11E09ld<HDX+W&)9< zXr$aAWeas>=iThyR8;{{C--+`5KRQ)gn3yV)vpX2He@Q3HHV4etTyF-N`4c)pX{Am z^e1v#C#Fj`#6r?pkY=<v{hE4H?8*fTBWAF!Tpp=k*fay1Mt$tiq8%Tn_hEk`4w_sU z10g3Q&=RHP#h!iBaNVTp@E+mFTD7VBex56|kOkm8X@KJ-IdwpUqln(haN5t7Qa|%i zNZMjPQ!KAS?E4TrC=}$ma2%Cbg5jsr)$odQ@pL*m0BFTRE4qBQrGixc)SWVyhGYvz zx7fyU{zPTlSfM6!HzZGt4?DkHU}`#Ql-jsoVgf%UF5s;WolKQxKj%+Fe8|rcCir03 z_W;LRwh<h67lL#~s!$d41!+PTV!5|W4vd+D*Kmno9F$Q5g>15D?m;S*G{-w{K@e*R zdJ2h3P_+fms!-R-&>`#9*F7LfB@UfXZ}L#P$Ryp*hnnWTWb`49yvxWK6UF2ehhb-u zx1S@&WfBc0zf5I0nqyYIDR=z<M`=|^8FWT|k(jR-?w?v#><Uxu5KG-LqRb#%A-Xm6 zc$|de1L>k5k4(fL<mTX}ANV=bi{_pQj{7XRwKHgH^O--0Og+)fP`-oZlo`}!%msUn z!SaamFSci;STNkh7XOJ0Qqp4Zn$MMYwR`>ElxJ6OF81ugf?Vz=C^&dQAEcPCY{Pl7 zo`xwQ?|j!vGM&!HYx-z3jYjD!QZ5<nzJ`>GB&7R-Cp8T4`2lSUIWF}UWCg<Jz$Sz_ zqC_~@+wiytU6IzWIiV~#k%DLXHaxd+nO(qTI*H3P*l{r~(@9*Wfy?YXE}M)z+{WcE z*g1&{4WElk>fca3zD7}r%tOT9@j;brcZTxBg33}MijXyk(2*;^veg;A6YOmca(Ux$ zGc#MeBkGNObKj?+f-5ta|1YpOw*_BwCwsx26j)Q=4%So>tf~J1YbpuW)MBs%BP+MT zGN{}r+eKjIB*9w8^~7Mkc6}Z!rh~2|xVfK1??ABrHS>Yu=!+xJ4d`KAiL1&*9Fdn) z<ua~&lOQ&kdilDCb3Jd={KBh98h54tBo%_X1~nX^?`Wksxe^-s*qzXRgqcL>X{*uR zS6|11Ub^v=n7R`(oq^37-vFY_xJ44EQe}!6!3>`HQasbRH-t*J(>ddUDQl&%LhzS* zL+Xs-KoDM8C(InHOq+SFGVSxXF@`ZF_ljRMR*GM=Rf@v#fPk{)KgOS5nQHhNVZm6B xk&kg0<68x=%$T5kbpWY<h4EL6a~MZ3L>{OP5s-*+j2Ol=Mi}ETU-j7XzX4=}S6To7 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdinput.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdinput.obj new file mode 100755 index 0000000000000000000000000000000000000000..6f66c5413606b84167fb7fc2cbd54cc2a6296a96 GIT binary patch literal 3084 zcmbtWUrbwN6#q&utbt<7Xcpo+ZCp(-Tb3bUaAmQV{uy%}&|c^yV>YamQBqn~+GQBV zoEHq2!y>VZSxk(3a5Fxb8E2x2KCq2&w-;l0@x=!<TLP&sCS<UQxt{agB8yLJP55rl zcfRkO^E<zD&KKT&jWu}=^@%6?(=mrMK9-4%jP(WLr@gVn=un)bH%DV<`V#Te!}L2m z5KoR~GY6t<rj{{gX-lOO{mB7`XWu?Y^MPhIbC|J}(mb}onZ0br#n@`>^U&VLVnD^x z=~Vi_5SwX5>kq51yi`<5m{&ZRA#()3>JIE~Xf7uBNGctR4xZTxf9Rc;8;T8&?2>s4 zhG+F})E~s~U7^k+A@R7we>C8e`r0}}0kOl|=V|LX8u0o;&3#8ZJb@msuWuwaLZ+`5 zOdE<YjU-Z0vL(tXEK&ZfVbQcd5i2?JScaFYl*nirPja?Pv5q7M<2vp`g{)sKBI~it zKs;3{Wn;1ababd>>&!r*OG*JANoErX3SE-%Kx}Y~jA^>4QK-G2wNWC-8QTAkv<uc0 z+>WzZ)NDmlSa04eG}>R?x@0eEwByNWB0Ip_oWj!!RaE%x$I?jDeN@lNBi+8Yx&=?C z*N3uK>=p(3RCtesI(xc=NSh=Xaj~mA*cS`shBC$_#)R@MX@`lik1JS(X4J~qTpg>% zC;40?oysN$BH2+^fe%7-beysIJ&YN44abx5OoYGL5hNp%P9+kt^aOglCI|+`df*Q! zd;(6Hz~eali^kZ+Dv<1#o{%`X3yiAU_6ol9s=JOf64YutdjS?uB|kQ0>i7i<?jT1x zrK?VYDy^m$JWhF|a?;MvpQm%liHEA&UWpt1jw)Lb0RAymwiTpC4Q?%>CO-^*)AYim zP2LbLS|DtmUY;~Thgp6sOj@|po!pNOhy37u*FC(GqfOojVUxl|;J(q+bsJCl22cHF zg^NezrEY}gdNT2WD%YVUlFPP0tX?8)ekbXGgT4K$u?y+3+zgvSE2w4iqDk@F0SN!? z-;6<IaXo47+CFpWfI@H#tvXD>?UQ;P4El4RKeO&n@$YZEzvMq?P&xwWo5S-sT^p9U zjHYRj<8gjBqC3a7E7^|GL3Yv?bGb87IScm?+|>Py8RU6XFS)ITd?n<#+-7Bww@;qB zJCC&w7K+b}+p~iJ_2eaJS8^>Rhz><=ML`vt@QXjB#UIk)HK!7EH!iE9mv2P}oHegM zf;-a-*)mvC#CANB-FQaFN{()=?#7Rf70X+W<(+(SyGe4CgWe|sniP8JY3jFWmpQ<v znb&F80XHryIf{H)ri75n#z%^8bM*s)-LnamAet1h2}H9Zwm`7Sa`Pu8*a~9Pdfcgq zm=lP07;jo%FC?QXqL-8o7;kx6*$(5az*97;)bLKIRoiUU|6<SA>ZcmNqkJP12k^73 zYSYUXANXb8rT{=myZ*GyMX|(KnO-an)ka=i)2q|CfY2}JC=sP+EuVzrYtR7x0MdLC zCau%jgjLpr3so@n{X9lS^-fM{6K<6AIFU3ZL+{~k*dW0%L93A$&5Cb>t3KJouDim9 z>nCuX#Ra&22pevEjqAs-fn8fP7?{8czPq4jW2*#aIKF{xs`)l$-<klvP2lCj{+q@u z;9(&R2_F|}<=ekCtyfwJV2ZsG$X78J&5BZPh9rDbcy9vG7DPcQqXy+PfX@W;)VONd z95%(NyW3CJEmF_=Yo-=5mEYl0$yVp^gHErI(~!^$8=%T7hV;2id3&ZgpPCKZrj|3+ z(#rn75XX|=#_}g;JFHXpv-+d5mzxt^(uzn>^Q}w%8pCXd4QJ^Ekjd|CS2g-@LX|4o z@IMd?XT(gf(4Gm%O`*bd=R$=dlLV?hXgm+%L7zsIQ6e~#xN30Ouqw4$i0A;0yrvgR zE>kXrF(upKx?zmv5i~1RkB_zPNOcQte9u+4sRiAtZu9y7ct<A;%lz4M;j#zscN-pd T;<}3KQ(QN2ec5ebK=l0s8TIfN literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdmainct.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdmainct.obj new file mode 100755 index 0000000000000000000000000000000000000000..2e170859e973d0dc092d9ac026f0f20f14fa7ba4 GIT binary patch literal 3280 zcmbtVZERCj7(Q(;ThU4<E~s(QMQ0?L;lp+?>5#6s7ueQ9zhSb`jIBUtw?Wsd!7x<r zAiKO4rVs)oBES6j<4}I^gM@T!VW2;p{4pUjnPy6{i`Fq&iPh`--gRUef6$ty?YZZ? z?|Gj0InR0T+pAd>zow~nOG`9V?%mfN3w3liIl?>ap~#-yVH(x%3GHf%gm<*le|uX; zOSrQ&wyc%)J;oR_+Pb2Vmd>_vzOu5sYFQQQdxf!Nfm?=kFO;)B3uCuKAA6oN<wGkJ zjdn$s?Ph&7nEn1EC!RIulZ};yJ7bi-7SpYcr&i{rNk>;S)Vg!m9Q>L$pI@0TJnBp7 z)d<g)u3NeS*}FWOH+X8D<?HJlg15=G+2g3)WN+ea{yK+U@KiO`ZQ>n%yU^4T>Y&t@ zbEzxyNDW51S}B(#Cs>mFL18hqB@!waxjUvFS0Tul8tzopE+jkHxidV=drhwDm-48( zJJuHNDpa!WP)oFRcfr}Qwwy}}As*}$BN2iwD7h`PvzwCXc9BuY-VbV&AZmvB|D)_& zHi4Jn-OTH5wRYiot7f6Aeg48bWqDm~>~#^bO|?1YQ-{hce_p%Pk#;ASvIRk(u+_)$ zo9zOQ?X|vIj(&>l8$6r+E-q;EdUg0^r;6F9Lq!@bQ%8((3+6_}ddAKau_8sMXj%W$ zjA<Tk4|j%R!L}fE=OES`i*`jKp=c6w&T*WEF+WU7!_(ft7HHOk)QdB+kcHz<(7>O8 zh6>&F!SA*1c8wQ!PAP#j2w7>^An&OLZv{BBgFk3>Q}W&#(_N5;vGL2obm~>`PlL^X zzccL>r)MtFW{?3PBPF<*Uv-JWajlCs^m58#_!|6KM8G|DgHT^BbDsm^I<wLh&N%Qb zZGQ#aH8S_bdP$h}0bgNB@Bc8keH;APJk;Ocuks%w{(>I*iN7f!`?KKGgRB-IWWcY7 zgE?*p7PskTA$fZo$r4X7bDt^&o}r}EGk?G+77EQ7uvSCaDH>aAfIAO&WMLA7Bw(Rw zSsVj#67j@w5b<duRR-b&^o(0!mAo2y#^lwKFvdy3IDRL%_+G7XV9IiJ|4maOwHBtJ zCv6<~O_x^87vp>Xf}TlS)urP#7q`o+aYB8<7+j`UT*X-H>@rO0?&3z5ObN^Afklw6 zoC1qnbHQpEeQQb*lAMBClUk^Ws{)0$j2e#_6-D`%ZpjDM?e;)Ha-m431Zra$mBl1# zCr%#N5#Os(OH<4jARVlnLf&&zxoXq+UpX}=1c`>_>(a1J4W(y%=s_*a7Xu(BgQPZ9 zEeVqxDsc9{s<+OoUiD1cbbWsH=o#6G9z_$WR`I7(`@o5APm?Pe4aR}36>vm#^3ds- zduVgh4O+CmICBTSJak>u!dEEtp@g_7UaOp*>6x~o*JRs9)jBi=`@w3mc3HnE5%)nh zwJ>hh!x}m^5U17i{6%G+o;x>Z#`2Bv*z5C{tA6(q%Tr`^@EfF`HPHq2vD#D}^qs@0 zBW``~jo33IJa>X)%#|F=DoP_-l((9$%MErKIa0;AycCzO@+;T}3)!ukKFzIrJ`FoJ zqSY{tnQq8l^S0(8j7ngmz!B(2YD+PJxqvApjW{!V^+-Wc4`+$Y5q?E)LVPmr)q4VP zv=3(#w?P`#A4<e(5J&@;RrQ2YSDZJ4zr=%);54|mxG4TzHJ+1(%YZjqGH)y-ZYmM< zZ1WHvnHSIcA5azGwSwb#wL1WZRXEoyoU!+JWJ-_<?*OlzB=X04>WzoKBNIDI-5`|A zk}!CnVWZnMZzGk5OXcA;<078G!H+PFgtH@ScnE}2%Qd6)9!-)^aFpt*lN&Yy!ksL` z2g?<)IFS70Pn3Ryx8p?qs8KWSCS^3VQ7fIH)NR#}CUBDAZ1;hsK`lZf489N;NkuIY zIl4$<(zl(_2R-A0eziIeRW}=#YyG0W9UF20)?<gqMhqGGGM(s1@p*hX9T5z@iI@?V zjM!8Q1YnW!Xd@g}-R2^<EuWP@x~J_iLeYq|bU9K9z!GI~V<7q2JtUtCK&sB^)IvB+ zC8$>@zClmjIs<3v)}>YfZ<u)v-x;b`WbEC}jB&s@?FPOK=T(vcPVwla;S#x68o@@F fm4<xyx`6c+mTUVHxdunBo-p_{KRt}D22$p4K$*{I literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdmarker.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdmarker.obj new file mode 100755 index 0000000000000000000000000000000000000000..877b3e2e987ed652ada8448f21076aa060c336de GIT binary patch literal 9817 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdmaster.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdmaster.obj new file mode 100755 index 0000000000000000000000000000000000000000..615bec6d13279b6787e3f5639655344adf6af661 GIT binary patch literal 3924 zcmbtVTWlNG5xq-N(psWJDrRgo76K+w0s(dv%e3m&tW!}d(UPdj6j$Du3SFAvu0%3? zncR(=z)fV3F1R}}Ak;+>1aaU3Y|{b-?7%JH_^0v%Qj`>^qyDsCef&fQ+m8Y*{AdGq z?(R~d>lBDofOmO!X3osqb7yAf_mctcK*aY#tRT7hSBtWgDMlueFAYoSd?HDA<9TT= zl1{#qqW>u|6GNLqUz}975kee8xk5UY72VvfUG9Ot0aAIMkhM*^+z)e4lZuy++tPcX zr#%}<ODYs{g}wx-d=t?>*!sh#_H6`>Crf5!3O|VO!HJ#y8{#CBD@gI#xkmT{uRYVh z(eTJGg?}BxliqLj?!oMXp~HtlzHhtzV-q8MWaw~c!Z$e_;f9WkO$?8O1|nmV+{BUL zkw`|$Q0O;IX#WPF(R40Ov6ONemQwy@!y+`6mNp%^DC^g?naBuDW_7kVgN|lrlSbYL z%v0~(z^RL}n9OaKvZ53##1orF%c5B&n@K#HRnlo1dQ)Ignk`Z=?Jim<w7kD;p+u20 z#Qz`1HqkVAH}>X+dMlp8`__wvw)UmVr@J@Q+R1D@t%!P=Q#_qe8^Sk@ODpB>N4=ya zIz4i9+QuCo9>M*vZ`x<0kJjNsp~FXlw&)PgTXB)M+Q`q(w=ou??+CHAG<Iz)A#b;m z*1ENBA#d(rwx-17E73wu$%;`WPg-#z6eU^zkLO<{kEN2?q#W(f$BM=12_=@5ldnmI zhIsFPiMJcp8L2QQQFp~yCZ9(09fmxfOXmtvQ9@o{kqY=FAPz(Ekfj*S=ZbO^4dp^E zO<i;vmPv{%N>MQ`lWm45pHOCJb+01YJ!%LakeT5{^e4?WJ6%^~V##c?f-F2m2(y!Z zwxT$y;?bNU=M_0BCNomDn9OC1zrYXJLKhl!>PA`VB;xUSnOOdAE2^GAmFeF2>ggF~ z@&&0_OtcYl!DeF!IRby>VT9F!qvchlU8}UCth^$(cottgb_0UFrTbQ;3r2fN_pFK& zg1bxiEeZ>xBEC0_7H(oDFwU0lS(ImCJTj)ZLA!*qF=Ypg+fn?61ICY!DO*7MB^5si zoauo|7e@8|LFt5<?|MI0+Trcrpq=+))9@05xhyO!&|HNV;1~;>>)=OdAwLGz<w2JV zx4^s9U>6S^_0BMGd#?S-3j99r^^^T676RQ<YiL4BH(8a7pn!7+KNlQdF46G#Bey~K zwCCDiED+e|{p4g{$_89_lW_zEygT#|8ta_Yf)LnaT>);N_d`TlfbVWHjG!RkZc4<A zYoo~R7zI;9*D7zyu}g@j4wospwNcP>tXZs!rMv9+_JY7VF5a@P0yr^m$3@Ty{N#<V zfM7Qju4cvS%c{TUP-$rzxE}A_Q}3@k9)=S2fOG|QpF)r0VsqO5UI6Op+Mlgc;J4q& z_S|{7i9G*q-6`MWy?^Rg5ZFB?ZyhV$wiKN7;i~Oi=0pUOS{H6`f{>a6H~NWNF%y*& zHGaIjDz|;0TUj9>s(}Q*HuqV*9xgA+9Z;gdRW4m?TjtW#DEghIS^$Mj%;R85F#`B) zb!X|O9e#iM2E9x5T{k`xbeke)OQc?}H?Gm{S%lwJEQ3P_>*(<Hf1bWEkN;Qb<Lo*5 zT(Z<rN$^fj{Ub4NcmVp^xIKG<n!)#Jh#<o8XuMex_6PFAQoT$a@N0wgNc`Fe=7IGY zx%09Jx4hS72Xrj^Tz>S%gSGAqdsbD!&ach>8_g<yr)R~ps7|_6f0s#=78oqADE1}p zC|TmdxMqmPjh7c4<udxvcptK9_<U~NC-5S6vAhjIOatUuQ3G=A3E;^2uafGTz*O%E z%uk-hwIA1UTqgwPr#o<c4cFfU%X`n^8W*hO!jG*CvB_r^<yLgY)H*djh(YmXhN;<T z3@vaJCy3KvBn+jWApxr2rSUU3V%oGo_zSdnK1^NNrA<0fyT!!T9x*rOpT^AujV-ov zLw0TjPl_3I>QrJZlgirT+PM!6)w;hn69lk;hMLb=UQ{|Xeh@?3Jd3rKD-`~D2qUSE z)cWnRJOpg@sje^&CF-FbWW&I-XIAAGjhmS^<XX^)86DC<PhbTLneRF43YD%~YB-mA zfMf9jMr{Dy=)`gHBXyL;n-AKr&?Yg6Vn!jrsy=7k|HUPx`C04`&yt_T0_WizWx4(+ z@{+Rf0B_Q_PV=o)rf=9C>}@(tj8YnbfP7{}d0yj!=x4hg6ia8aO9ko*{W7&DHSVb1 zhJs7g1JsP#>O(I^y<*^Z^uWjnROoJs4Qread5vfSlAyGsqDmE=l(Y+U=L{PuIJHT3 z3Wa*=G`*|(@h4d<ack-NXCc(-2cbIskorOBA(26>z!4mG$l3>V`s#cr%ou7=YDG$& z`o+rb#$|V6QroWTwt*Q8!HlCV6Liv!H9v#!7VTCoNQY8K5a#vF@Q&-8y?N|3+M0fc zev7gH$9G8YOu1L%!|3iwop1SZ@OP>Di5qtovWMv<K18iPNc6mpv8nD9!vklGIJHER zTqZJ3zU4vNE&SLo4YF9Hzy23a4a}3AA0X9d1Y4C8TB<RjwVD#zswV`tdP>+*JtMSN e-xoTnR|R|ZkHXgK--Sm$El)F_!ZbsGlJg(x3?EPc literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdmerge.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdmerge.obj new file mode 100755 index 0000000000000000000000000000000000000000..dddbf74e8f9c55fb4471522022288a7775c65445 GIT binary patch literal 3088 zcmbtVZD><x6n<}VtC`JA)NEukVwfH4uoSzto3qN=CRgd&*feRX#oT5!No_)!l-yj0 zgA_G>+}mSD(IIZ&5MjvnXCH!&ZAgoqZsMRy8U4{89n=wLe`F%Alj7ZZZ*Qxx51Q=9 z&3o?2`<(Nf=bZPw2VW;OjxGL%UERsB-FrYth5H15OJuJz9P96mQ0wjf@IHSmvNuZC zXs9oo+!wA45@`(~#MGEb#=7GnyJO=<du?Sck+u<%D=uPE|B9VRb%fjvU+`?GE`(G# znM@=rdx^9a(I=n%;H82}!nz{y6on)CyY($=t7;2L-j_&*gFX8e(r<b1<*Gu{qq-En z8Pk*Ux5_tRdbg)_ho`}1-`>>1dHs#8o|cB4PQRnk*VN+VJhlF&osJftlk@k5`zZ8H z4O&$IG!RP!DV9o3(^AQoniiqmv2f9ug_PQ^VlpE%5?9qO1|5j^MCN&K(W-u}fU1R5 zD3T~vG9lcZ4E7d{PKC57DTa6;F2-Usby46@xJRI1db;SK(D7dCpaf7e#Q%@7H8f3b z!?Rh?*$O7G-|Dc?qy5FjL$-pBb|fB*i6M2&X?l7?6_ozTWvQpqoh~OOfp%_pJL_n5 za%ejn+8bE<v&^}})9Q1xfkv-aj}JMsoP3%qVRS^b2w_VWtmOtmK3YbWDSAamPFTpQ zXfV-#ARu)2^~b`2Wa3?ckfC*iFwaLL@klD5-q)dk*gwA`8N*2DSe7Bg2Q%Id5DoD0 zIehCSEFn>*1Gck@IWuaM9p*(>2dk`BU6w`HPF7i^x~z+?MWC&E_?v@@qC{@o?P18F z5ps|a5X>Wgnq}j$$c^*gKXF<XoRv+A0)kcD@aqL?+vb6d=&IkQ;Kyd*^R7Mo)uAEe z4L$=LrmU~kml5#1BfBg3tLV>+&U!m`0cU`;4SaSuL-!0i%8m@DD!^Ori84`=_1eJ% zoOR?Eo&JZmDDmVlHpz@yui##C%~3|~wO>Ku2{<b1^80m=hvgf`>V6h<a36fu%wq<O zhh^97ej~nt^DrxObN5z2vt4%0A)3i@vu^O2VMQBoMzjnb#cRxrnwd9V6)Fas%Z~gg zmH=l~NT6#^XD1e<^}$AI^JZ9y#9)XL*SoB3P}&AAW1Cy?&=?A~T@^vCb)44fGSFn4 z5k904`YM<~wxcbJQy^GmagypPPQV_U%#VW=Y^P*C54%~_n1Ol&ScYU@j%vzTz~cvU z>o?gq4Zi84w@t^+Qzd*erc0|8ZU*?A>C#%6f1q$V;HN-*0Je)VKL;2%{QzoC0Y9Oy z`4;%ek;i7RoRWRB;LEGnJf!EcwpD|+I?0M_)`DiWw3`8bcF^*o<kn?|^@Z$JTpGjU zT{vn;{$Zn^N%@hgN^<o)&!v+@ddTx18M=_+3S@{4gFD)s-K5&1VlGvC-U45#@}p<K z3@26j(-SaYk<;TaV3pHju-_(&H{o~KtbYhKmt}EAvs0H@S<FE{3pMY{;xz4tQIcI# zPzny5tvnrzdjGAQ%uNC}d34xxY$a_pH*LB^6mA+TTxiJ@s{d3=ZmKQG06(U-WSq8y z&B)v=aM(_wkh3M^lV>^@a&CrU2ur&=m))uxw5^w3$l7#S7R_p@Q|%CIl^S%J5xoXo z;$9pUq==KE#f5ZUT92V|)uIQq=)mVChc3hAF~h&1<{=>FrLMoB6qhE>@%xW)8aTtl zdOu@2a)J)8rV&|~h^1<Tw2}XRDpEZRuG}C4E*x#$ps|#eQJOXCaHVXGRZ=tliQ|Ju zEB|DI7LL3jy6LGK7bax&Ccdu!TD^!21h)JCh=*iHIh?j&&ohs7;)d%Eb`1Z%?l5k~ zj|w!Gw`n2Vta=~Y@2$Zt5^l#W-{DeyTlU&Ja_b%`N+-QL$l2$}9cX9ne1^}L`22*= LcsoOYUV484E8<x7 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdphuff.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdphuff.obj new file mode 100755 index 0000000000000000000000000000000000000000..97294890442d07b46730182d9de6be86ae096e4d GIT binary patch literal 5335 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 Q1db?<5RQGC#(*^c3y;S#e*gdg literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdpostct.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdpostct.obj new file mode 100755 index 0000000000000000000000000000000000000000..ea40e463ae3df157c2126d1862fa559e2c023f82 GIT binary patch literal 2329 zcmbtUPi)&{6#txfotX@^AW(}%C;}lm1ywDj0$Tnwb=<CP(mHnHP&J#^ChnRjO)A?d zf`J4|hnl_(Dzr@qiEStT+>p3%LTy?R^+?5mBSI4tg~M)uR4KgAO`!-OE#YAMv)=o? z_j|wh-uL!%WI!5Bg_3iY8PHz09dpS}MY8i@Grzo;rKgEy^I|HWozKy4F0)*;owU=F zCTkB7LOMr^R(`IK2}r?UV4!D!ti4Rgew!gzyiWzl+7KbXn4hXo^tVFGw5+1lvq;un zMDz#uUwi!7R?t{kw%}0sO9&s1obGE$lcl0%rWY<Ar5}9b$-Y+8qrMdWJf<hzuXOh! zdqtfdQ$x|f=y*idQX|u9Bs3LHNh68zNLW?}QsYxnBoUTVOXd=VzR-mBwE#8p#WclI z$!S_D`Mst^=v>}xJG1S$?P@18LbC-|?RL;cVIh0Sd$3vcGc8nYJDF^;UCC^7&Pp$~ zjdn84DQSneQ7Gl}G<93xjJaS_Fx@UXD0IB<bx;h{4DtV?>?WEf_v3E1v|H&S_S+p6 zy4qiS`HsJ()y@{u`BKImbBd=oR7?0{*QJ+gccYt}Fyiv7aaNiR%Xkil;vts4I>KY> zbV6Z`5l!>rLq0r7K4-m*hqxy~*b_&mlN=!*b&!sRx8WgIcyeFPDwYZvqqI!O#$$vq zUAb%_>lhh>Ue*Rm;8?|c-n1MHcAN7uge2fsO@|T(H?QCy$b5O9vADC&{8?wX{^q(q zvAXA+sY&ctc*nyee|fjGl4C#;lpFz#Q+1eP8!>-*$5)Z~`YgPAn?_OCipJKXFv+2R zCv?<?eJtX2c%<>*3<m2(r+UwSn!lswPI#aeYQj1i!c6~OMH4HU@BHk!?Tw8M<Q>!W zB)sK6Mm_*t!76CNP<6!v)&AW|^gy{PA1I*lDnxxTkOc)2JhFa|@@&M|@{S*<L)X@i z9*BzGZ&i?ZMTZ-oQM-zE0ip*?r*VFE<{x?gQcMyrZ<bD@CYqqaB<7Gf#OrKBZK?v7 zM5@<SO+-779DK+DeWQF#Kh&$CxI83r=y&@}uvU=|AaS7P$kxvuM0o#5qMyr$`U#Nm zQ%a{WC}`qP^)-%>d|2zGctq0eDN4h^Ew`ynirBEfDJCI#jG`@oBnI!&&~gAy1-FL2 zu|04X#x8*73sw*RbddcZ91}|1`oV+JdaFtj-ggO7K?QVUdKsHm{cnO)#3sKIm-FWs z9Y)!P0N?j-R@@Qn->cz*5K@u2ipH0B`1PrY)m=x2uYK5($|Yf4=2thJr)e=sn7R<# zr{3iqPJz+XZ-opFCJjm(Ae47R3{!s;CWY9MwQ1v!D2^;jc5a(|{4lxG9cS*8@VORe Qe!3oKKH1h80=OpRPg2?VfB*mh literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdsample.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdsample.obj new file mode 100755 index 0000000000000000000000000000000000000000..279e125ba585ca0376b3051a7f79e905d6a53c63 GIT binary patch literal 3392 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 Qa9rwO-pX{c1TYiwZy2-#W&i*H literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdtrans.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jdtrans.obj new file mode 100755 index 0000000000000000000000000000000000000000..98ce9983a33c13c65c3ed1dc9ac3758878afa378 GIT binary patch literal 1976 zcmbu9-)qxQ6vt2cV~sd<KZ=S@7*1g@9L`zM>9B2D?X=pi*{0Qw&?TwM$dZxXg32D0 zI_P>8h7%D%@IetDM8OvkgrVZxe2~q5Vp5+)1gYn?9pam*56L~;oX`E9b8^nTb_fm% zeW}2?w57I-SF%K%%%-B+SV+~U#x-W0no=*NbZu;ctrH5d(nhvhhC(X<Yzdl{o;H+r zp{uLi*X@HsKfqdjC;ocl?0~{ifUoM4L`Sckpj68;t?qFs97Fo{raQZBrG$BD24Oe` z|J5Je+2gY#K51I2oVm0v-*<Iyk6n4JF2fI_JaiuK+>i3{MC@cDaHjplKr}3-g0Vz2 za5j_@g2MyRP&naB4V)FC!=Z3$Qk`Vzks8`#1C?}BW-Kc?lVv6US6M{Ux>~n#mQ;RM zJ<5n`My1;I(2|kSs&V(#vOZuV>nu?;v)+<r)wCs#*G&_p)+O}>E*UvpXVP`SikitX zm`xXJ6xQDV+9(p%4Eg^^yGAqdHk?gcvn8AOzbnncM*GFf>ut71TQg)mr&QXU$+H{E zmi(W~(#cA9w-embQ26|iONfQSXomtr0T+8Ugia=6!*Q1s6h$XKaL)rzFLRs&Di(mt zy>5AUfcp*5P<ECba8rQR3C+-mG&P>fWF$qEO+~e!rMmgo-nQzVBFUzj!IwcSQ`gZM z=5_&aTiN|CS!%lKkkMpKHAogOd3ZR@aR9^gn@C4_DtP;saWl7BTziIVa#8TE;7UdB z^`FehU7)A=nI+;8slb<;k|Ld-L%iIQoLR`ZdlxQ_zRUk`kbP9-XI9CV{0}E_MslAJ zDhFJ2(1jF@@JCmsU(xwC1i3h0dWIsaOa#T*N<96anN(}>wExBuX{7lAdQyKA9!{N$ zey^7~i{9Dzc9Cu@HzzByb91#EJ_vrUm|sGl8w;eFwk`&}h(p!2w4<2+SpBp?Ttfqr zB~Q(m;HpOcJugzxRZeWj^k^l$K|YC*&n%E=alV4Dl8sc{bIY5^zjc(h(LD1iMw&2O zH}03*ubCtwy-TwZ7o{xKEzg%{BYf}bY`k&N-^9LSKJZ`@Jo-MuJt~hl9`hqkpx71g E8>{1T{{R30 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jerror.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jerror.obj new file mode 100755 index 0000000000000000000000000000000000000000..543ab46d9f354dc145389db76ee9f7085b7f73be GIT binary patch literal 7729 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jfdctflt.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jfdctflt.obj new file mode 100755 index 0000000000000000000000000000000000000000..9a866178c462de445c694db67c357ef1f0992c5e GIT binary patch literal 2306 zcmchXOK4MB7{|Y~w+{wO1s@30#YIsog*qJ#&UD81rpZw%>4P+7vdATg$%#$5O-hn3 zW>%3RRDvKX5(FU&!3Qn`yC~u!Vizt%MY=c#Tx1co14Rqdg#%*!zLOMOxahqDv-ooU zIm!S0&-tB4;RLw7{bA1^u`Jgo{><h1OfKwCo%L~Rb}B{c%q%|_wo+%Oshv(H;`yYN z?~TL4E&%8p%4DrrI-z@edUSWM8x{rtRviv`vH7qL3%>&V{Xsjd_t}{i&Z>6`7Jfr7 zza2Pm%#N35vzaWpe@FL~j%)jm+i@e6R9c0fM7!btvCkIInM{_)ljpYL`~N!FXEz@A zhU~v!Jai9s|Aez24~(4-ct-S7!+tgq9vTbyJ)^#`cW83h?_&XXczD$7pY*Zt44)w@ zAtx&#->x-cW#Z%|<m4sf+x4O~W^u>KxxCsgC&_3{r4?-_?npYBs?WQ>vFe{~tU8xZ zq%uyC<#;R`pK|QZCmN^3$;2b+Im@DGN9F`i=EzL9iw+7M@9iFx2r@(ee^_?IP2pX5 zH*MXmcn0@d9TvLU4<4THvURmn>9{qQP{*A7^t##dfAiEf6T9o(&=Lu<P*C%Z`50=S zC+N}Wqt$mhFg7`^MTRCOnz5kN26q>mTuq=9fY!35+S&nbv_k7<^JWuV-V5OBq=!91 zVUeVjiRJNChw@UB3t+OiHov_1s0}-cBkjc}5`@tu&Cl3Iv8_r&6UIJiej*V?QIkfy zFm_9GgG3a?sx)>8W2ZFNNkmbsN%mQ=b!omK!BM=D?2}*{(i9{(iWSM;2qxq{YPpc1 zosz8x_DXcz!X-)bxkSyL%TPNBjspL=CfKSPbycvM8muN*wJ{i95gm7w_pxlbk3x)K z!eePZlPwR_R~#R|RwNT!Da(>o1zVQp5($oCQL;tBp2^T|5*)=2$(97G$dE>Yqv)Ei z=w~jx?!5Hk%jRbBiK2(V3RR?7XIHLE#HWiwMRg-VU6kt@AsV7kR{hA)5aqf?lu=PA zsD7jw73I2i0<@&8-)Cwxn3gIO^?zb5l~rP$Y;=pP-XL45;;-`+O~JiKaNn9w;UCRC zxaZ^3ftBJ9gL5Zu55mnOP`es*)m~x=toK;|23>EleRl}!DAoYhc+dq!+<Ev4Pi1p8 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jfdctfst.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jfdctfst.obj new file mode 100755 index 0000000000000000000000000000000000000000..dca0149f5159e174a6cde12cdbf1c37be375acf6 GIT binary patch literal 2216 zcmchXU1$?o6o7Ag$2Lf%l?_rb4}&5W2Flj8sI_X-B-$>C8Iq1I7#-7OVn@@YBoV3* zP>PcAFj{uSmEDK!+p=%MzSOq{1;K(#*M~waHuwV-ium_X+hRN?jX(P$oe08Y?q9w+ z_kQ=>dwv&db+v_@z2TH52G6E5+F&~5)%)FAd}u(Y+RsDU$xvMHKSkxKSTvG}r8CVD zmivJ*R^ORS#lwlH=#nI{wYin$_A@qL;U}-~?IO$VVeI(}VY}F3Wm-6^<^h)5i>&rn zVCzp-SS^)GrigAw`j-vYw(PV*qZK_-3ce5V4)5j`OFjpaDJ>E^`7wOkxos_0!{go% zz8k}{rUOks;_T&s|4_i`6AyNKJ;6|?Kj3v9c86S@O1IbT3ABc~54*gI+Y=hp1_>qQ zgp%^LqG~)DA(oUAOUl>EA{vfs6-Q2IO50UQWJL8u$#x~QnuzJE^KL7zdWVHor!!GK zS*fzL7EVP5DyB2ha+XvwaW!!!9;eV1!BH)iCYVkaB?_hYS`$S@XUKoUvdc6Dci`Ey zbhaW%-0xDd(9yp6&jp92qpc?*@iWm<niEgAn<f5>tCo|y>rHH(+T-c#5nO(^2kUNU zk5iymjr&l*ugHSh84Pm#u^YAQ?@QHH)vQ!wOj!4^R%>JI_Zn7H<i%<>x{0x>db-$E z3a09@a5{sxIvcxOUB#FJ3nO=CgGYM7W4r5_t;GRofIbJ@gV!Jn(i=VrGrX@+k{b<W z0c0C2@nO4>U4db{G;Js^L9U~&1|z!&!wuB+7L-NEF7heqU&A*Kz7@!>@JVUTY~LtN z8M_U+0Tk?>=QF1L57Qnr?Z4uaU$XIgMwbl-BrF@j_Y#!1e2U6r3m9S&3;sx2K3(k3 z7i~uNDJ<n53w+wN|7O~IA^ViRFb&J5<4=6@Pq;LPo%~^b*~aJKomqF)tn-)NSM%>| zW_yeD3ho=(g1K`&1{|^*U3H)o_ya>0z*mqajV{N?gjn>=f--yYzk2i8`(p8bl5P}8 z7J4d4>Jwpf0z3E&$RdnWm1Ll@4j3P!0c@>&YL(rcfbsL?u|<%}t+hxTCG0C6J61jB rCg^Y9SLMwyjA?Y8DBBcQZRQHtJ2D}B7J3VXs>hGQvtzdzU>dUzhw2%{ literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jfdctint.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jfdctint.obj new file mode 100755 index 0000000000000000000000000000000000000000..c03cc6008fb326380ec7de909a56063b52fa0f40 GIT binary patch literal 2527 zcmd5+VQ3p=82;Mtnr5Vufk6sJ2+CODpjg+U)~QL8xOOCKNg5YeV%jFx^+uYEBx6xH zSSUh|kJI5qihr!X#2<pNANG&Xv%--42;E?^uGrL6sZb<hplLIAzV9`i^UvtTKSFZ% z-1pvd@B2Q_`+e75CIjxli0k}hMsNh*&E|yJY{Vx{d4%NU84=Oh%fiJ-Qk=Sk_e;}> zcuq{^dgEmNX+nr~D4j`8rV<V}&pQTs2gv*zglxC?$s6>fgUr85$Zx-MCmnr8%7UtT zXUP0(z!km-yl~10SIA`28DyUZ_D4^B{CvL=HmHJn0R45KJAKdg8Pan$oe|>G7Z1}9 zzIUR}XnODl>90U~((`7|i%|VoVDwDD<#)U_;`0V0L!$wo>zpUz9tw^4Jl?=SWaOON z7xH)`v%)M=QF5fB<VU5(lIb{dQF7#><VWQKbuuZmoH?7*?P?`6P{ow4b}Q^yYFa#~ zd$8H{O9r~m<`QDMRmrl#WF|h-GCP-OPDv|;$5L04Nle`mIU!7Ek&L?w2L;FbsDly% z&4B+O+HSHjxgGYVp}iGPgWvkF;MV^7X2EV~Ym2FP@=8J<bL8XcW{CgTQA<&~PkKm4 zY}^|j=iH+nFJO;r+{NL=<T(=<4UKWJp<s~0pDc8eFTbU2?L<d}a2<!J(@e;xCSua4 z)=sWJLr9wy5B3-)i;3A}`YN32WKpGUgoN16-1qCjx6d=L`IdLR69UZ4!gjXG{^0+l zcUYKXMLOKghr8uym4(eLZ>C`zpSQ_Ujpe&&{<thX0Kg-tGwA`{m!%!J^FPX=ZN+Bc zi!8cB*VIb8+zp7=QTR9uZPQh@rvA*)JBsZF{B+2o+pMA3V`_z?8*0VGZ_xX6t*}Bk zLez#uh63*G-cvU@T2#82x=Hz>y4k@O>1yFS{hfV_Jl4(pH5RSXA`8`J{}#Q={Plz9 zXpR0P-ab{)H088K)IL8!h%A*wss6z+M0HWB-E<+UiBffZ3Q?6kRQj)~zaFEzO8++i zMQQ7=uQW}Tw%8t)VxlFb>q97sFR7dDe2F!7?^AzSv45skC=X3^&<!Tl={~(n_dtfi zEi9Bq%c^FP^L8fHsI)~J(4`(|_KGnZld2di6;!>qJqt=O+$D#(xn+muUuXXH_kXY| zXCG?Xzx-P$qmPd)bg-pmTqx4g0pd4wGo5T{5!Ru+ypz|VZt4VGx(3U-uM>O|Kf&6$ zm3Fmp=x+020l#3>V<&oSmBY>_c#QAtTjlG^`2GL6%}}duvsGRyW4Vtz4KZ*UuBry% mMLqn;Y+T;6q87swa@aAq%>85bb6|GtoeS)j=n7%5`^n!yv94|a literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jidctflt.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jidctflt.obj new file mode 100755 index 0000000000000000000000000000000000000000..0d0cc684cf404ebd7934cd6ddffb16e6fc1df2ac GIT binary patch literal 2770 zcmbtVUr1Y582_%hQE3&0i3s)K?oX*uaJp93xprQ?ITc+}LripFu2JJnO}Qp6e_-pA z5(b*vvN6WkCW4HC^v$sr^58=@9d)A*eemHN5RyT<SqMd_4-RzMcW#tDEDt7q_|Ey| zeD`<0@BF^+oXo!qT}rpNcXTqMNyDE;W7=%gYY$G@G<|L+$kE`OcH655r|$6iPH@^E z3+S<SKP<im01bWNh&~ybmXynvrLOiaSo{EBufi=8ufP2kEWQWu{DpW)>MSQM%&L6` z7T-s%w&r^CN;zCD5(!5*`#Q3}tpDnbtL3m!m0-vWego;U{X%D1dd`L;nm=&+G<^5n zi=E|$$J%iEH4G1JJ#8JBz0)<+@9K3(HwWxw*xNVcviIJyd6hoTfZax1UEYCPirr%) z-dSyyQ@P}v$|XN5)u)I3oXaKWTrT-pxk#PVwTdH0V`jN3iHy`>$W*%$wl5S27W3{t zIrTeboH`nt4u&h0EUHaL{4*7^W78*HQc2>zP+Zq}=!(eGS|G~F{C4q1;q86aM)9F$ z$bW@npRjrGR=k^K-7S9@>uoj*zuN0REwq+(wSytQ9-lVbob&nXRv!Pqr`E!?`??LR zJ~#QuEh<Acg4ovU?iKk^ZR>Xpd7PrJZ+O^(2H(`c16dTRz(fF|^%T|A0(@5u)dfqT z3hp)IE`eBBhdQ4x7LQDYZ|dR67{C|J073(Q@qPTk_XUc}0IPy10C<f2!q)mfg~IU2 z==~KA3{q=&>Wq|xUV}6mF6YAIdHjEPOi~yS0*Ox=A$}Jp3F1+lIv}p&Btu-o$qDgs zoF*VXiqjm#of$}^tLVAJFCtSP8jgoc=lSrVWR9{qPVmv-K}@iE3eu+_iy*NnK2Kmy z8KCEwdX$kRIw&*sFeA$}IgcMO^&lfp&}7=g`-~h>a=_GG4m1RmkwZ%MnfjCiZJ*Y6 zhyYZ3%G4c3b}2z{!~tJHI;CU>_vb)EfSWhcL5ZpPjNCy7Cz!gIk)1TTYKF^Y<T;wm znmEfyj*>m5Zg8L>*k&Y8NtUTw9B2sAjBHV|!BqTsi)tIiWXvSDDM{1%tC$;8pD^n+ zo}?p9wI}>6*6ZfaZPXMMr34o-`5kl5oSAA46Ely4ar34KS$4tHAj_7`1i1`3HYS-e z<k)hWXH5=x%40GInDX+fxwubBo-NOxU@gzaCa?|#N6K)dKKrfV^B2d3La}YGq?bs_ z@H82}FFDqk#USfcX*4<v(qci$cbSFy8~O2+TB@}08X)kulv@L)G*P9FD~rI7V4Eo- zRa&{S2>b}ruc+)9HQhr^u(QRV4=oo9c&U?3HH*zfnM=7wYRvA-mGlY4N|f|f7b{cJ zS5+)lNuQMwUc8b%k^40Ctq{l^yM<>-v~SVANBhw&{JDUZLwkvK<QD!q?-tg&M}##G z+Q(@3(H@}viuT8d0IU5V9HR*TQ)I-F*pvz+l_2XPwoKyh+J<}W_{jLi$Bbv{CcG3M b^^7O-Jv&CSCw}8n8dj|E1K^GZK6vmiQ3xzE literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jidctfst.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jidctfst.obj new file mode 100755 index 0000000000000000000000000000000000000000..a237e9941412aec77efbc640f0664df007eb11c6 GIT binary patch literal 2699 zcmcgs|4Um}6u;&@HB`6AR)*cemVINw1E=exo9#L^$@<Llsw8HL%WKqpsZXMYBoAC6 zpakJv&qB)>L-tGkZS*Gx{R7l)Fr4Dt@CO6MICP*8h6Sl^I<s?Mv~y!Wm<$HVJNKS@ zKlgLa`J8)4FOddoqqn)UHyX6KKZ(VIgE6mD>9YmHk$#1Z9FgEnZ&>LYV0J(W_~W5i zyw*>&X9*#umf>i)cPL=73WBAfwt;9@3E3+0NbAdQzDBfn3EBRYf5TE=NLUzEZ9ma2 zBR2Sz>(z^eV1v=<aFn61Ao{bXK7ZxyLeN+hWk@G~58+MD7wQYD=iqQO=nvgIp>MqP zdVQhvs11X^gY=~4{hB(AzTMS!&DAVfK4^8?-QJcqm$Ui0&1-G(v^s5eSA)0py4C5i z*}a3oK?Y?dXHZu1vqF90VL!vNk~1tT`B||D?F|QuW{$;mxr$*%s4}EiyBM@@D5T`$ zZamKVtpc)+#RJN4u_cQId!zpTqS5idahDVmxNk@ehZ%KI;6N}GV_<f>SfjA^K5L`+ zux5z=4{0Bx8MztnW<ht$Ka6_o&BCts*Z0TF1zl}r$RAb%dYdymd)*4d|Mk>ztae}2 zkWycVy{m(_w%P1Bwl#M&^XyY*yXI>1wDZ0ex0}O1nJg#w`uGw9(MN>vr6;3uBOzaw zk+K|@Gmtya5mI7e54(@O>pmsa8;j$sPNsOigb)v8$G%-W%H`Z2b%NapcP!`}doYR{ zsAmN{Rba0Kv7Jh*sd2XLJNq)8PD06BRYVP<u@LnJnmVF(qG=*(JDPT)wxa1FY73eO zQ7@Zj&Vjs|d|)t5S5PArEmUlxVh`1HCT)uGX|wuw9luHYDxaL?w{K%Kc__n4ho%{a z1<x@}f7nE&eVjGvGZ<w^X%`(mdB?2o*a2x5<U=kE(jln3!V;+aoVr8RO&maCQ&4x5 z#{<YQcVy%a1a*@p4w*Yc<(-uIX<>=V`}t$5@~jprWs#%~XOLJm%|zQVlsx7ADWo*b zbVp3nch?!ayp=N73y(m`LShRu=YD`hR!D5o#KY8;3SogJwm^QU%rky&O?mA9BbS4N zxeZ>(%xxI(b7Y+R&6qN0<_?ThUdO%qDwj!_@8S;<>$o6f&~kgoAnXZCDf0y?XE9@( za~WElA7MI^EFzHixi#8lK`X4$$ywdC%cW^o6Ue(lnodsXt}IA9+(KR&9D<z1V8xze zO>)vQNXz5%rrUcMaK0-p{zN^?;5iT9Q)v-Ai|`Cc)!dID^5gSrH;6_wZV)TcI6<sJ zV+Zj(nyVmIqqzj48BHBv#-(FF=kP9oyfRT)p`FtlC7Q#aIXKP1YmQRQQKmV{le3oX z2`n2JAyNftVToIVh!Kvsk!qUQgpq0iE3)}ST7AYH{JXdq2t+C&4I^ee<q<}Snu$EB zHNp!c=KoDCNUY2ql#Xq%<R{E8Y0saC_s@myaug7q+}hvvcbDk8f_nOx7q#v()E1pw z`qXZ88+6HmOod1Z;iXfc$_rTMyzblr%J4)IriX(+>4?)SsE5AD<?PEz`yvmX<>c>1 V)2I8`A-)?Q!!PnT1n{LIe*)U2@q_>X literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jidctint.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jidctint.obj new file mode 100755 index 0000000000000000000000000000000000000000..25fba0b5bc4b4331796c0f4252d5abd45c1107f9 GIT binary patch literal 3151 zcmcIkZERCj7(VT}>xOJ;VG9!7kH$~pO%P;@im3Z&Wy4-}X?Fo*Yd5+MF6r8(A0d!j zv!*WT-LX19;@7YF%lN}!!ViCzi%T4HKQ>$#AR*nT2+3jy5E!#i-}hb!LlJ0=Nqg>l z-t#{1=X1`$i=?r2qqliScQk1C?2pBReKD^~-sK2}`uE5vZRrp0_J-tLd-1zh?g_-@ zaC~imq-zNw)|N;#)E(}zxAMHbacv_>zedRUGF`5+KmQa-zf8!5Ke%V@>q|)svRb=` zq+bDS@EiA&8%n_jqtQqd(VGDMam}YsG?apdu3$d^{wlzmT#v6Wsh@q3XfV*b`*!$^ z2cBMEYIv9p!e4^$WX<bq)<O27yJNGvS+Kv+?s9s(Egf!G^HztqwMA-oIh^iBZ~N9( zm*jAI`+|K4#g-!!TmG<6e<%__EVdl6*z$+P0<=36EIV>6u8*sn$N-hYdbi6#`@_Am zk@v=1RliU|)v<Vw94WVDv0!&Ju%~Qvyyq56%1PWGPJ}`jx-4){us4Qayj@5Xr1!%T z#Sc9L{C_C>78--s!`&?DZUrJRZ=Ec7wZA`<tS{+m%i%yM(WBEG@%XxxhQIUFQtaJl zYe<E^)48*gYwd72L3T8EHgmXG9Gl%8lF0d6JRS;v@<k>2_8E)GOmvA5uHv>-Sw+ZK z7Gf#VqL~b@BE)3Hhux3wx?hfkBKzP~CnFqZB1B^I$<fi9#iD2X4(6<4!*)<6j|_l; zDorrKrV1yRvzm#bDotkw(efwf7z7|pI?<atU?YilnE=v$J4wV1-8z!kYv`ItqRY^U zB(dGlb&-V2(Djo<3+Sxb6-=169$cZ#nN^!A+EuYh6}!}7J&EBFOlA16zUE*}IZv2V z&S@@nn7iPC>cN*ayT#5VaIvLH74qPcCGytnwl3Z6M+jv?o{qDV+F6dC%+!AgKV4+w z*G_7)939V84{5WMAJ=9p_;Gfe{+wxuYUen5NjqoZFF^=;)tX&NFEbxRsa2&ss8G(j zo2&;v8Ndj>JUykItx<gi5P3sfU{Zk|SEVcP<>7mtNegsJl`g`Uhwr>z{<eN2(Hyu@ zrGinAs24Oef;wRVYm%m!uVBr#uY)?VtJZAPrW-|}_yu}~PRYM+I9n`Yc8VVPCd5*5 z^3l#+ke`yHi*oVnr@^0cT2=}lEJLXvD+`C3QCeU(GY$K+bIa(pOv4W#%1Zvmci>LR zv&Cztv^fi%$<%$W&2jt;Op2e8mE3!m@jOSR(@Z&SQgWG1kC~JLQ}R^FnZP3mGthS_ zM^7>73cXB^Cyx-05N&a}3b|k5m+XhJd?sC_Q+I4csO%!YM4|Hls%64F9Zzbyuj|Xz zwkA+uPnYP@*g7Ua%L{jz0xM8=m+1hQF5y*C7-PcN;8E*`kKQO24c2csrb?i#V=%T+ zCX6y5?P{FmBon#8V~GT7s~S9-2s5!7cDq@d4R&2jtb^TKtZg;y+)S*8-J7h6NfRl} zrd_W{&KBX`rw-eP>K<XjWEz}Krni~&)vT{nUpalP&{vDTR$dU0{9+Z3TU>1rJdrPz z<7DJHvx#tbsBWWI*&+~qr;Yx}|Dyd~qs`R-O;qUlgSgyQuL=dI7;W3CaZ3FhodVMS zosskmu+bpv13781_kU+5^G)dFBxWGz%H$l5WvH%}`6kn~KvX!^xq6RdPbF{^;Yiba zottzHGhyN%=f<>Qp3vKQ=+wWO02R=LzmExU1742v_wxc&LX&%W(HUr@!xf!lDd#B1 Zq_NaumGynR0{(a2X21D<Bn&7={sP4-$3Fl7 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jidctred.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jidctred.obj new file mode 100755 index 0000000000000000000000000000000000000000..5ca86acf14732f3db301bcb7ce5aff67ae47a9be GIT binary patch literal 3159 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 OGv+Y)`w8HG9r70+^`)i& literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jmemmgr.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jmemmgr.obj new file mode 100755 index 0000000000000000000000000000000000000000..2667f56cca391a190d03d31e464a30157133d417 GIT binary patch literal 5150 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jmemnobs.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jmemnobs.obj new file mode 100755 index 0000000000000000000000000000000000000000..7860f549aa7f27863abb44bcb8fe30ad9a005873 GIT binary patch literal 1900 zcmbu9-)qxQ6vt10tm}ibRp&1pQXFDoP*!Jg)?aIzIxBH4=?@iBVw&EznQ2m!Oqfsl zR;Uj}L_`pLD2Q)@|A5d3|CJ5=AL==2CyG8S@gd30J@<3I_nv#sd2tm|xwM#hAe$^! z-nDGjwM1TRa7^oUR2tpuv9_qGjcxjFcUf0A8dgGq{s{nxWDQf3^=2%$vJy)rQqaEz zu;<a`e&A9J`Zoafg-CGuipy<TyH=tD{p*OcPr~w=8=RS@VG>Q^uNU52a3zj`QFWW} zn}~1l=aTM1YM4xEwGZUe&o3t3%H#DAzK-$`za3x2vzLV8rjWTGyHnuvWieY6_{<h3 z=CaiS&*g=bSlG((RW2{$g%Wygh9+I0l4dBxQpib`LVj3TL}iV6R<>;Cy1XbOs_IU( zz0i{0Qm6Y)&rW^W#i=d3sTy8GW--}RI-Y5}Im;3+8<+GQO(SVfaFey1+NA2DM4|LP zY@$dQGvxo{*fW~Mm#{Wn)s|x5eLKlQrTyjW%OzK(t?G)l({$3DcxrI2_}{v1fMWMH z4s%i^|F9Cw6}dc)xlAP!q*sXB6pGbSP|B9e0es-yJbdng&kxQBAUJn0nhyiK4?$=W znE2sU1VUX|!*YaR%VZ2-coM*Ol)AmtX0~LJ6+1l)PvLyTnWu$QLzE1E0-9{LS?nKx zV^}4_S@d+=@q`DI<Y%&~$qkKdknKF$PCGWE$8@P7D^IXoF$=Iu-lYYLxM1NItm>-$ z`0r_(ncWUfe|!o)fa>7KAb+H^hZFFqJ|24dkJiTy7al~vXBU&`sx*K}bmnjE+thD; Z3?GicC|vW6&eZ&)IDXe_ei-0Az)v~HP?`V$ literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jquant1.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jquant1.obj new file mode 100755 index 0000000000000000000000000000000000000000..5fa2e4179d54b4555ba10129614ea7cb372edbbd GIT binary patch literal 5131 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 I2*vOJ08{=-y#N3J literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jquant2.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jquant2.obj new file mode 100755 index 0000000000000000000000000000000000000000..91adacbf0d29410d00e841ea56f280faf11137d7 GIT binary patch literal 6662 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 SjMiJOV(scLc3LE2O#Ls!1Utb1 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/jutils.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/jutils.obj new file mode 100755 index 0000000000000000000000000000000000000000..84003fa50ffb55596326514f26d12d72077ccb4d GIT binary patch literal 2162 zcmb`I*-sNu6vj^%OVJ|WzMxjzPy|J-3oh7YL`<=zrKq7eDeWLrr=4b|)nMWlVuHDv zG-`|*O?V(0eKPUE=ZZ1@4L-TNXyS`c#z<1XJAl4A)h55r<ed5D+<WdhXRdf?r@uY! zJ(18PcXUM0OBp>Llm`Qnk{g!!-Laf>Ca%bXX?{%?^0K10CTVOT5!L!sO-W=^Zhv#L z+tccyu>(Y(D|+GJ;}$oK?I-#oJ&CL{wIoT?RIPQ`2>#_(&7H<wW;TRf$=N)YA4KrO z!If<uGxCh8Ny(uzv*Ft>Y-lqZ9=YZ6y$DZD9ZlO2J{$>kM!enbBV9ou8ux`FLGSTE z-0zEZ1p`9F6Yo0i55@vQJR@bebl;4$&4g4`)FjvPl=HAW<$oI%(u5*a99hpB$5lyW zNad`N?Ml>Qc1WIHcl*q)cbM3<o=?eYr76>;gq9qxSe;MJR7oWZ7qbOL;h`%Mr=%gB zi_zLfq44tl+d>hMGwA<e+cRn&yaByws<)CV&f6#!-rCP!Tx~Gb+Hy9j6jDZ+^J3!@ z%G6x?Jt}QHb9b9)j@T=l?6vzt0Ri^`Z?D(R{~UqNNGKMzi@s>o2IyWDJ$*qID;al( zE%rIncSKcAq6ZFglx<}z-KZxA20$`5VqA48PgGnE-2y(U#cU#9&=QKMYAH#>u%_Z7 zB1=s=C7%;DwUAATg`8oogSk3AYr;cRTn1BpI;rMHL_LwoDH0Ut_3hlX1g_d?*C6J= zS?;;Q9n-J5AZe;Nl+Yf+H2#t-L^1Z2b-S40w3UwD|NdiOvREv_7G(o2*6qCMFWcU- z(*5s0S*iI|KfLhuMEg##LLD1$GNIwRFvVhcbW!Ppm0es``e?O%v%R}kzO;(jMknoT z$><w9>z?WfM?NDajlOn;u9Yu;5>NM^nvKPTy3+fqFpD`OSW3_Aa|4rnZGtPDChq!U zrbjh2akkGgaT`4Av(k8gp9cS7Lxe*h?3StlH{*P1E~p2{9J#@AumH>hc7P)!7vP`G z6<{rB0z9XiK_gfRc=~IA15|<eU@>R_>%bP!2Jp2YjD6Yxn!$Ro3gCFC7Ays;!3MAu p>;!0kLM78K&;mAsHDD8H1s;G#BXkSx2HU_Ma0qn#2Z-@({Q{iPRx|(r literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/libtiffpack-win32.a b/resources/libraries/deskew/Imaging/LibTiff/Compiled/libtiffpack-win32.a new file mode 100755 index 0000000000000000000000000000000000000000..0d3f9c67e9e2f45c27237e4c089f974994f58314 GIT binary patch literal 895446 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�t6_KLV*?v z^a(9(ZAu#~&`N{`3sTZFO&`#-HIF`&wkexp*Ch}Y??tbQ4^&iCRD@ndNJ}M^TfM19 z4H6(?yk^~82;K;}QtAJD&dl$zkA&8Xe*W8TcHVR5%$YN1X3m_sv8rz2x=P7aQM1iU zbVC)5y{@WiQ*wysBiEv;id9uLUPx)xhE?k}V9Lx^T3Ul0Gv8NHQ&EjoGaI?IeuE!p zUPmwAR9|=F+Kn}JOK!Z%DPO(5zIJW-rj#<}fpoVBk(r$G8h|KYS-;vTU%zp~nyT75 zv7%PFmI9EO%D1fBuxjHLFH3oC-Nu?KzlieH6}5GN*!97*HDaO7f?K|}el=B8Z!6zi zRZ|P9E}b@Q`m7}UyD2MOQ4Xpqud1o3t_3CK>#H`bsawm-@T(}aeAPPOwC=hZWfg?t zGOb>@t`1CzzheEmH5-i6fL^{5!o4FYu?DQ$uv*%3%=X=;PM=}Sz6I>NZGgj`q;#2X z-z^`0+(}$1Y2+i^v&i|!zZzdkJ-yT&{1iKrGrk%>$HL~Om2_p%Kd>3MLf1^F^LL8F zVTT*@4;q%;RQeV+B4pdDLjzEMBGT*(V8$R%HiuOGs9mG;o0^a6+D60LPzh6e0Eg6p zvgAQ^OpcZuPzM^ZE$W!5TW=(uquLEP7Dr2-)Tq*E$zF9V4H)@SC?3<`WvRiBvM+wp zmMi5kT0nO-0(;dQ6E>gvA|<`KLq^6*9%BnkG)8v|u3{xm>T9<p4|A6Eb=?~5VSkMB zs&$`C^(Tl!r&Nq)IrUi9=nrvV^$kVNp8R+x?3f&pF7%(mjcf2ptZJclN2+?llB<q8 zp1o|r)t>bZu=OOj_dGTU<<Kk$&;K0(k0v8v?=d65Z|0?p0k4^lNHc8%*`XYwXZ{aZ zcqADM=;y~u+GQJ4j~*Y9{mr{aFfEWQR`!S&(KEZ`;AU>?b)qVGri0Ccx<Y8#GgQ{u z>LAKE@r!9stfWOLuPd_sfKy+p1s#%tLUMbRg1W<zaZE$FCzf=pRW5Wu(M-kCA>(9E zex$kwBVEu4TYHaKYg00~x~yKu0Rb}dDW$ptv9d#<wu?)8wD1EH<ubCV;wg<khAJ$y zu@cG!w7X5YijHH*oeba}Q)#U1KvIKiryqY~6Y~;tGa-jz6G_2WhM?;1EUq38+wx<* z)ZDled}k_bdv_>&_sYzXagh;IdLUC78EVfElmgJJASzQ~3Xq7{86xBVZm=8a;u=`} zG&62spFOGetop<lpk+($sEk)(R?v{Ftc9RtL$V3(@xpYDqePd{#mDHbDw&HI4qO(d zL{<uV*HlIZYsNq|1+dxKB^|Y#2yow~EqvEURN<vpEF}qR>V6Y7apsfmeO*}-#l}Ey zG7B5WJtVb5L>t70Ouu^$PJvo*7`=F$LU$NiN{mYKs#-&~sk(M_tPHI6OG-WJPmnb! zKvPo(I>{)&&Qu2m?&dT{4y<6`Y2Aj+$P}xJL;|VUP+3(}w{c_9>WZ48l@*8;X|XD! zxS?3Bt*WV5f0>bGiZm{hweGWEBqH4pciIr;jq3_lfyR}-Ds^R-O7i$0S(VC1Rxs=? zrG3&@Fe0N+b>Q=lTfvB2lKv4sDj%AiaRoDh@Sx02%hy-bRT(if5Mjg9q`Ap62eH<B z`Haa%ob#>q=CMz4kpPp)S}*HKk>>Wy)CA6Ke*PKKToGezDZ4tqX00k8wxXP3XGKP7 zuFk@y2{oOy*6ZEcWE&iN7VfRh7f7Qb8e-m&SjiFBiYy*6Gs__!!<Yj-`I}-)@d)?( zU<25P6IY}wWKRSqOEFr%mM3dYs-R>x46LibX|S#$FH8Z~baa8nHZ@O%BnVQyOaNN1 z?o$57O66~~bTNN1`pWz)w62~5csQ*zh5+o{xPp`mz}EVk(aH5%E_7xfrK7D&_VNhR zqmjoDVQqmEQVO&;ioa|U>mArWIXekh6wXU<Cn0a&KsE}Mh|0uXwlh0Ym4}$erP`DE zJ7R|z><VL}fQpVLZszcn>mr~SIJsImp6s*s*CCS7h1}ugitBXvR`UH~EX@iR24t+@ zVX;!~L*b5k)?a&*8j-ISm-CY?E*E$fm)j^wTIy0ui<ayQK>)%`aO@VB6?AUr3ea+w z_Yi%>Z_wzmv9mV9n8a0EZ_*4%5sR3%Wx*@O8)y!T1U<hox@vm6`GyI`E}OMDEOOge zU>CI59z>WE+dUvhiqq>U!aHqOce+?&88pbqx!0uDzQ!mXeSIdAQERcKV_Ztdd!^dM zDiY-HWP%ycJ7IZpd8uf#?P%_(<)u!2CMgrq0E?g4axT=0kxJ3(9nks7Eyo1R(OM!0 z@{J+|jc6@iyh3GKTua&UlZLexm#$zoQP(nIwt8)i7D8FV6^x#Pl3e{F4Wg_1VmFL8 z(#ByRc;4?j8E{+y4%l=?<TC^dxkzlhn!g%00V3hXA+h7VhT>I2a8RrrWVr0WBjE(D z=x#yX6T+Wh%0MpO2d2cNi&PW5FGgijkzbO@gt6;*w~2B~nJh9u+8tnM!ARYCdLfNE zO<N`l!bLgX*h%uPE@AW&s$lS7qbMb3F=J9q9V>1)P_?kB)!@lqdjKr~^P(og^foWp zFwy$AcmfuSP_0aA(<I3tF$gr06~CKxlBED^c~tKru2&{?q3K-M$XV*j!-a`I7W8)H z>P0Zupuzo!iY9}uulKU-A2_joWUORwt0Vl|6yaW7&A01+E@C0jK||TPS+JMubb4J3 z2P^kB#HlEYXD%z29BM6j(qKf3K0Ex;DGbb5_HNnkJK9A(I;)5=TU%rN3=$+RRZ}-P z@iBNJ)txM!x)BGXWhi8#mO~9~?n1sjQqq=-<&yB`L#DJy*D#vAZP)hpb7ELD+$4-g zKoG{GzX9XCv9i6<t6&Z7drBVWuh_gO`MMS(_U&^SQBG6Y!;qeT5mR%tmb7b8CBiX5 z60qwCxD5s;zT$ivt_-tolonY8y~<3b9+^M^^9rf8E0;|GZouvPQ*KjU{yQ2k+x?5= zr74x`()4$na#38QD;ycfb(3&@X5iB#PmyF_b$fQCx(!)bleS=_twli^4}l=$?~RqU zFn=V@Gl8>9T57=%+@w(Uc?q=D)o67sfdRf3>ET$gy*DPtJCH9R{r2-^{})VnmU?7` zA^kEe8OiZs)Mu&U$mz#J6=m%Jtu{6xHs-#TLDBOd<!Y<*&v&|ldjMA_wS23aOqN9q zO^StcO7}%x3CYq$>T%n}Wd1Htmr$ZNDE?M>tj#~i4hjJ@&jVELZwFyqm<A(5A%9QF zHwYICE+ya8ygDXi3?1v3kadO!^?Q#QYIi6L)Yw9?=nX&q3zbNHY^$l&U_0I&TuTex zKEy4XbzcoOM9!p-izk(}N9BH*0G`qh1#UfN4E&$rPhg6`R^#y;mld_@g3GPYHtf%C zTZ?s9_8@m@#ZU(P`(R}IL2exEcM}Ncx0}-S24@|23$!a_t$?H0%(N{rc6ZCThUO+M zL`hJ%yNr?I!i!5<5?PvLZ|sJm*tPkR_Bokx;qd&3cwwhda(IL2TrHq5R(4Rt0CRgh zg(XwyMA^AMsQaJ-(8VPOWr@PjH^?uh1v&8+^bH7XEiR^I>S77*cQ{jpbofsRvdrc_ zGPIT)G^r2jEJO>t-Ahs~_Z0k?dT1vruDWRgv)k{qo@u;Dn^6=K1AYRb-pJb<y|!2_ zDYl5wxE7}lR7gM6d|VU#y*SXK#>Nj9*tQ9L9!)T-reQ(O58N7|nG`HUDd%{xz=V>D z1rcUEEFg#eld$j~D4u9#Nmv-~VgY3yzwKry-tO_O`^bA-!@anW>6|O`?i5^ZacySl z*%X&Nvw46SDrYKTY~=6z>qwbRMCb!aB0GqLalF$jleNsJ@t=(G==o`J9+OC=vxiIo zGK@qxI^Q|U%3D0@LU2*fzOF(Y?`o6FpWM@0*{Yjc+8(6R`gtNk@+yQF%*N57Q?w3l zrci!6?K~yylaNgG>qYIr!_DOfxP!On+<0t_x#nqiyn-$x5V@`i^Szk^F*%7$E# zncE^=Bji-4JyO*+pt%q{oOpy}9!-2iavu{8hsg|#pryMdu%hh46kOegKuchjT)7&{ zpPD?B)9Yq)?W%5m;s&i0<qAj4nd}uU&C~S$8x5e@3U>V{#JFJ;G%WragH=rB5-Y4e zA;LbwS}kr36v59+{9}@&G|_9+SBX2eMj66zh1UFCKqr<PT!OzlSatkbt+HJW_6sl0 zKo)YIDxFcE6CbCkX^UB%3xBY4QQZc^7v1?ueI4RsouRW8*<T?W5rGLMR?;OHqy~uy zOcA&{2+?B|spevTx`_Xa7Gnt}z6YP34DJ%=F?IYX+wtc#HHUOvOxst`4>@#Nj`sdJ zy1`BGs-)m>le{)V@T~+}NgeoB%src8wy1z!yecwq_GXcx>F9@&u-yqRrQ3_wnikUr zXw^xq_qzFMQXE?oly<3ltvgSx4L{N7bg`$Poe2RmP^c`jf9C3FUMb*a(YUNcsF}c6 z>00?)d@BZvBJ%#o;00*`t=XjtIn_l5#%%siGPoOp;9|X&D1UVmkoGOL^HtLWVij6a zFIp;pHEE<}Q}ovH1+2+sZJsBbLPm}V7_EWjABW44?N2%`-O3zK5q5p^2}Yp!0+@tz zI`MCr>SD4PNt>3Yp3ShH!`Ohl9`q<D#ouMU!D+~rQO%s@d~Y?+AZ)okG1*enbgDRp zu1GZ7f|M;;_e5D|r{HwS_n12(Ez%oT#fJl@tusHo`|Cmk?yCApmiEU`udNB=3DSaR zh5`b1Zpra?Nl5&hA$_4CxtuLM9eyIaR1dvdEDvnqeCQy=W7t_Q`7PL^>uWraM;<50 zSwn!_TGB!#bgocv#I4@Y?k2Cse`Vr49wd!8#u|4<T23?#9>HiCA)P2g#n?u|y+XTn zRj6&&zRh9^kRhIg)5K!s_Q7F^5X}=)Ue`-urAMP>2kDhxC5^^KZsAzF4c~RS+5*yz znX}-XU)0UQz2PSsHbFY8{VqMC$a2TUhfT7f2dCMB+}rf1J2;o=;1mNJ(Ci9<b^oSl z9Yge1hUnt)x+yFBEDTRb#yp*rGCa+Zx(mktP=(^oFojawt;Nf%C?BYI84*pk-c>4E zt$+xd=8VA|`s*Md(Ef}d%r)d}Im9LdLSR<TiC4H`$jdTm<b?132CiM(J7g9J-~Bl} zD%~8f+!HG~B<zd4{&dC;98Vv0NqN{{G`czOxHo6__{Ipr8r;*9_(O=m@dx?i1p}x- z@Nx0Eu<)u!8(Bh)roA4gyOxrVVK61`mE5q*^ZTfsCCKjoP$(XamOKJr@LX8Kx5H0h z#;)XB;m5HyU-Ag4LkFJ{qJcgBSF{h+jPo#3^{h$4yA{8DHQE>INaD)mZ6jWoa|ruU zZCj=b*n~>*>UQh4EVfyYWP^;Ht09SLmWyv-COU?;#b>kkXDfAU5dczZQJ8euYU*Xy z@I)tagIc>;$|jT(|8EFhLMg|b(sL*g=vMKV1;Bw|!eS&{%dU+rc6U&HKA0+OSI>*I z6js4X4#kUPW%vnv9;d7(*D)YKI<R<=V7pbEc4@pQZT_@tiDHvpQDvMKQ%bZ78^xhE z<0H!}Hrjm9?!-E$TDau|k@}aVRhL8<?sR>c(1T#rBgDT+i`|L)BOIq3LyHr4$4WfX zWzzfqjzHqbG&<L@M0mo5Y3wlRr;UPkpY*SiG1&?Dt!+OwP3baxu<z0MxvF|@!C zkWYK;cnUG55rd^Y8-1L#*Xppv3DfUzm`#@f7)ukhx6H=aL9KBiT4v$9n=MTiFT9vV zhp0mW%p{^}Iqlt>s(`jT31@Fv@w0fcq(4WqPM&DWkYg-Lnn0<dPIe%nTXQyavKy*t ziY2#^%4LC-5WW#BUTBWRFrlcrnS@9r@&t-K?n(=vuAC&2A}y0(w07y<SQn~cSa*sp zIS_t)jHtRDMAhw}Q~$E57OBFV^~n4fzm&$rC-TQ4?L2LqR}lLk#Zp8E%Vk)UW_inC zLywKZjWQv7eCQ8&j)4|9EYbyR$HbCDGIh!Z<{{~Lqx}G9VtY;lSZwelz#+$Oe4@8m zH-RXNBt1I>3v5dsl5NT037{-^jwKyJh#o*usW4<c5|LvwMV8pfn}n2lY&$S*va#B( z5>BDN7XNY*@rcS`hV?hR((v|NTF5e+MoC*&d6<on;5aZ#{;{$FX2n-I9o7z%t%$_j z3;@V}BqxQWa}wv`P+R1u8{^ln6KdWd`y;OS_kM5gG9LHFkv3}rC}oU^yfnMyP+ge> za^yL7%XQXu`esqe$gZ1h66x?&r#zux?Ln&q_FatPfg^TXpiEJ?Gte&3w#w1yL^%SL zid-_wMWfh~8P0d2I|Wzzqedxkz51nBI9R9DJRp1Xw;UI0ct#R5C&BNWFcS?zk8`wS z*%TYGw^%yHJTImk(i{_y*xQgH{A79OPqBKEyy^u$+?+a9L*tVo+aGh}t#3w_V!M8n zEW_o&$tTe`WS#hA<iPrX<xM@<W|Y!y*;%K2Tyk7#H7bztCCZ%h8yy<>nWPcz-wbLq zCJOU@gImSd19s24VP^fGCYqmI^(4R(<qxOsHV*5}kv&GjC&FF&V~eKCUp8{}6vIgK z0o&pvpR5U{qs$V!d6N9SX7a@Uh%LzrJ?~OqG|%j1sX79iL8gv;`{ruIHKc{P(##+q zNCR1D4BYlhpPu0A1(;=u39DZ+)K05vk%(#yG;K9?JS+VgM~e$FLJAe>sJ<E%3Y3(D zj5L=l`oN(qYHoW`lzWUp8|b<}Fs{(x6pf%fs42<Hd#bXIC5RbruN*-~Otsa}32><e zMtelfMi&9G)gkeF8wDWA%rfdoYsmoBIf?g+S`Hw$8Rdh>_9LM!-fd-eJ~2HAHS=ht zoSAzm8(ZplO-R<xPU1v;4DVu8*h-FAy?69oY9an3S)0Eo^_3lwgX00Kt;7gPdPCIe zn|;`2E|B_wHj&|350@Q4@gg!XVvBt;_XbTjAU%3cxbX`>K$2EfB$TwWo31%@qx%&+ zR>z1!x&~xICt!{XJLgzObY=%>1F;)s?pd1CtNCYgB488S0-H*KO7d#FLL`NGTWGv8 zx|Y&}SekgE%pUiB6y+;vjCo|+i7gq3>{}b8_tmQOMOQLxvjvG13L6o!5o7sB3{%x^ z6ASs)FHTy?Zeq4edvmo2^C}%>6)1fw>HXw4c2-dxI_VGea~Kt1P0mk}$Rnk1EXf(R zOvfn6f%#GLlrF?KM=4JT*Yd$+rZQf+1^f5rc?}tgxa6D)Hit{tobCP&?N&*rfJ-t_ z_Zw8VPDc`%<+=IzzmeIqgC`1A)PTyNDI`tn%Pm#FR=K8N{83~=qpf$Y0nF4LThi+# zFf~n)n&{%0N_Ungc)J!7BYJ>nGzkHq2JY`?0M=qKziYoJg3fBI*{BLoCg@4>%xc?m zs|gYk=*y90B3<tr7?!h`T0NckULgq-x)*Ee(ql|rs;Sk2$T5_?UhTY2);zwNo4sZt z2$l!VJ(<CK)*HA~Q<J#>Dg#FI@^*l855m(fRpJ!_6N*vGsBI7!*0dFy0lipdw3d^k z7bfbmiR-<>ghq8my{{`@@N`7DMz~(a5dwZ<ybUQ7HghDl$E>R*O5XbLX7-;x)7-pn zL*>SrnySjWqP2SGv8ZxwRplK{{f0X>Y}~S;sB&ZVrkbi+xffVeT~)Vs<0?;+-dsQ| zs$KWtDlfS$1J&;}wMBBJ)Gf58vZ#7pt+~JGD&6aSzoNQro2tAr%}u}}6JwO|IT8~^ z{_2Vi+ls7u{>B<#$-j16^~#OwYrPUgb>F5l5O}q%t8??$RBfuNsB@C@Tylv(Sj#of zB5r10vT^k#=02jEKlzG4a@p>cgXGZU9H5d5l5S~kjzL0Gt}eJ;w5p=6!gi3~Z|<E1 z?KkI}Z0n3<J2RIJ->3L4gcF{{Hv9}uEoO6wbOmQ&S94tVAm4L1WSYjeh$E7He4pbR z;mGAK)@=EO?0>Rb^)bG;AnA``8SrZ)=AGv_&SOQ6^9tX~&vl$9__px<Dc?+=6>{Q} zTgo@zDafsL#*CnV+)aEZ189qrKLQ|gSMV*u{{?;}eFNWO{9VM;pxkAA0cd2ibMlDU zxVQ41iNDPmJ%R@0uH`!c|D*g;K<*yCi}3f6pY4j=rF;vVQ*wV#{)=(b`MJ}HKS+KM zle>!Vc>E9ZE9o2gF2vtMekt!(z93=b15Uw+8*$&scMkpz+QU9a?s~qH@bBd}kMBKv z7vmowKU)g9H}D-p{a>d3^KjSjol5)x>ZgNpSMmjcBOl^-7~i}2UWLD#{2(y*7QP&4 zkG!Ay7vTOd-z)LAQ$HP+dpqBY@IS_{;PYdAuf^X_e%Nj9O?*#vifPKpBc|cr%=dEQ z4^ck^k-M7j1^6G~SMYr|->dQWl3$4FHokQJ$j?%LCGH)3--ExC`e9_b8~9#||4Du! zvfO+5z7PKq$MpaE@sFnc5!?SC!as%h{gf~8R`4B%|4aNbCFJhnJ0Jfm#0xw(^Mw#c ze!}+uHvF^j|HAhFI{Xvye~({*H_G=K{C_6D^k*60Q=I(VVIkB1_4sEH|D5grD*Wf; z{|3K;uibo0@c)VY(*Mi(G5|(?#`gbq{B!aD*7koj{!8%xkYAyvCcaDX|CRjG|3Wh- z)Ba~{|JUNDA4WcF`(JQ+9{#WLE9oPA7vTRd@=O1JkS};1`7gHrKZ1WY{$JbvzXLyX zJ@RpWg`Q%3ufzWa`K3J{;Cou2|F_`3g7}}?{$GRtLj2$6SL*vH-}mBwmHdLwa=wh) zk)OByzZ3sF{4d%5--!P*{7>;K^mHHJ>+!#B`+u<UzmM{z|5xxWBK;Tnb@(>$Eyn*l z;-&wW@h!yvaohh}@z2D6*!KTg{1fnhmtX0xJ$x78f1Ui&|AUSHpW6OkMVj%X|2n^t zzLD=j{BiP2dAIT%i~rNM|L?><2mkZ7|JUQ6g#X|9&EtCy-^KX<m;BP7gN^^6*#2Kh znsZ726@G{Dy^HTv`2RqD>5p6Zo`L^Uw*Nnj|4RJ7vi*NM{)_PcfM3Dq$M{~0|1ad1 z{vT}o|IGIPYSLUl`fu?o_`aL()%gFL{L=ro@jV;==WPG)!2cfnFWUa!fd5kbKj!x& zzW4HdAO63+!}x!O@&(=szT-&$Wqw0^ck!K%|M$cTJU8<_9seg&#{VyE|F0u{BJtnn zSKy8Ey$1jPkYD<9u<`#Nw*RY0b3W<6$*<sRH{TNc3Gz$-FXwv}{(rOmza9Tv{4d!4 zuf~4~{vYu>if<F&CHUVWzx4lL;~zZ_<^PqWIgj*T<5$v0_%6WzNAgSme~|B)`2W@R z|3~o8#{V1J|99ZO82=Oep2#=G_d5J<l3&_0*!cfX+y84wb0O)!!>`o$QNHiR{~GxP zpYHg7(DuJO{$IBJzmYVTk^X6Z!+h`Kdp-W6PJT|vIgR_AC+CcI&ddqrkIu<)PR|Kt zpOQ1*IV&eroS&2B6y}76737R_#^!{I#^j82&d3Sno|?1EDF*cXoMGhOMgG$zf8pqy z5t4uSDLECAe?@*yw&c$$$eAMfCy&V)CHeDC&Dm%2qe(MHSc)^k8Hv*92`JQ_h^3f3 zC(9Xy7W+x;bpi39pG>zv*D^%uElRr^*LZckNw=M&t`i|R<9Y?x`GgfXi)o2*?eJV* zfv`=w3qkn>n(k)L^(R~=b`%6?TsM2JPvN>yb4;RpOze%G>j_+@ylMuQah(TQ7}xJT z*FIR4fvOjmiS2`Yjq42tvT-$oAX7&_E)ymSN(O@Apx4Ci@?58!?3Vl*E)!c`;JP06 zTo;_;I1{uTvv8SoJ5F_7zc|fxy&vXpVt<0m<mm9i7Qq@!*avZ$bltcnYRQ+txJ}q+ zVPVGA#*c9|z;;ZF9>!%_H0oS8_T!!_i*7dQCgC#az6bIQ{(k4VZYPn6z1wqr<{~%U zW1j0X7rS9{sRm%7rgqOIn+7JV({pusu5QoO<GFf0SD)wV_gn*>>xk!)D;%bdY|kZ= zj|t23T=|}>z;hLPuCbo0$a9VNToXLkB+oV3b4~YLGd<TF&sFTX7J9Blo-5;BYq@cJ zX(hPyKknELIhpTTOUiM4|HfC-NPyaedmotcLzwNHld0VgfxkQXjCwx=F73=p3Ry?U zt2TrmKAZWjpd45O97sM9zAG4~e#plOITNpxoaJCcN3T-%<pk?`kThLMxxPurA!yTw zhuOec0%iIkKO-arUHT!vC*+SwA%7ucHY99n$K!;+#8Wl|(D|H1$Sn*nznmgMzLXSl z2_Zj%%KbF+2oW*Kgrwhc-%K2{THy1KyX8I{E+`xz`Lh@Z<pZiS-f}-C`&wyMu*%b{ zX_cqh;F4NDU~6|A*S^;3q!2M~AGk{I5=OwDl_eDo@d!tKQ$4S1Bg>|>2DGftTDDrf zZfn)5x{8(StFYF@1_OS~LQR>D)hMhhSl<k%ynYi~5>>7#s*N=m%@Xrb)4a;ms+BGK zy~)<NY-oy&E<dengR6GihRWKiikeC+U%7b*O7fI$T(<#hS2o(1&$6Df4FFZWPHb}7 z5HU2G36HL2F`LYoF2GZV{j^%wV8N7WGq0FBdD@IA7$x98AM<z0<jI#`etA*R)EU#J zYH+~%lw4(G8sw91ed-XT)=A8f5t=}`z(2!YK{2%S@NiS7`?q)ydj&lfHb0%(E7;ZB zOo)5pfP%o0)vXIl?uVnXo6jLhcyq6N7{liCfIi|W=P~+-Cl5-M+#lJ`vmx?ptO{|u zQuFIrNuiwMkhM>Ew`j)qg`c2YbQ|l2B~Eak{H*^Yo3b2HiH1X;9zgXu^(D`iJXZW{ z$qzzUE@v$qD=COMO^dQ)Wd+d_Vr7rXX-#@ap}7aTSDF`p6~4=kyNo-jx<62Lo)~{2 zh)Qezpq2O6QhC1u+a)=`g=Reea8(!R(7b5PPAut)Z0~Zmov@cZ{iE?1Rd^`+mBj2g zRgWh)S?UXMn5U_#NVU3k_hvkP!JH1uv5uB~Pe~ndZZ)BUtD}u&5@L%!Y4a~#?6aXB z#J(Vsv$kLmK!%^VUkgMHWOdY(9e!fJcG_6km><w5F4g*z^wT5kMN`fQxeReyg7;|H z<8FzSw4j2*fv`|};>y;N1I0LYRUa6}w1EO|gZf&G?@8tXCvggiS_mdq{N$C^x0ZAe zhHpTA6vv#<&~>WV(u)-&EYq75ha^3{;azE!&HPSo!>P5Wy&yJJKa!^}monRK!Hz@j zuzDVd?<@tP&GriXUvYy+*+}n}lZibgKXB1A)}`0kB~NZIgt|y*5NBd|_2%(JMRFL( zB=wH@X`zZtbQc)`dqmG5SP+>s`hZ4ju_sX73YU<(IcyZzlA75bD+!tui;8x+9QdG8 zqH1?!;y(T_|3qlO_K-gI{g^y?*XGny#1Sx+Tx=qX3oojd(lM@$qpa5>+uI$vG}}(n zaiX%?9^x5Vj{dcq%<whjt{US<bX2>fDN4x@$Pva(eSWhj4TT%!U6J_Z{K<?3k4>19 zIeEb0?kr8cmnMxv`}0t^@y`%|*paGd5JD2&kcp@Tit3Y#En_Y#sc~Him=W<kf@s)^ zKR$O3OB6&f*`<Tmq>!P0I2p+yDC-z#>Z?k(23l1Of_TyX*!RjX8B9HUhABDwA-#L9 zVps<gEukIw<-~@gvenV6hQ&X?VSM}s{s@~(p;(ttI=u5Vx1VjXown`*;ev&7ij}V{ z@2Afc*%@yLi`_#S=gwl*%3=L7f>riNR@f)9vJSIAJlQ$TITI=CJnRckL$X`o+~`y~ zwMe{QcAjy5@7X>Q>)gSAqsc4(#@K%+Q<D5Eu>VeR%)flB^s>tY-x!9REHKC#e-t@S zcgA5!d%jcQ>~i)YzwQcQ#ycyN9U30W4dsRMLxrKD(Bx2YXa(85y4^b6x}=s<`F9%s z3i)?B|IXmwnfx2ezq9yP%)d-~1Q)_nSU`i78J=sg=ep5zWn9M?iK9|rb3ezjj$wK0 z6!T@~03Y&Ux#=|U^+RUhYe@>3k55>yNs}&>i-aO^{^uVjl@|j?`e(5X8cZseXm+^t zBajYrZs%_XN!gX~)~~Fnt-5sD)Tx(?bi1`qoL3;*y1_HWI;sgywuI`6hQ1}#+be3; zaY*WlhecLCyka_1U?%x+)~snpJPgQ(ZBPT-E_`xALHuh_hClxd`LK(g5V<t?8B9KW z68M83u}-=xbk;2=4ZHjN{H%h}W3o@oEy(G}I&JuGL#L1UgEKaFqjUDi%bjs2oZ*~z z;wWci9{X~mvYfn8A?LpE&RfRkpFi({(SM$F;g~y%CY=25r(INV*XWB+c`fgfQ@=R! z($g**F}d*8oGGUd&z^QhR@U@0i-%o4_Pe2(XDtlPI{Poq>~o%Q<`jL*dC$1D&b)Kq z=M<ec+Zi`<lJk}Ge|Jmq1&bHWzi`x*3nsjN>B5QUTu^e+v~#Y$_~FwRO)5O)noG8f zzV_0(ldik$trM3_9vJz)DHXZbPaQsD^0c4jOr8Ex_S6~S>}i)*W=+4Meb|hdw+_2} z)<EcrD<2BYoV_A6YtH$hE9bu9%zn>*ICJNH)0tazpR;fNm=Bg-mA&Ns3ntCKVPVG= zH<t8Vw)E;b6K{I&w(~x)=;gCNaLw1xSaj|CPFr-{_=0N}e|Ge>OYR6?_rCX!T5|p4 z)L(o;+xw?3TXw_PB{$!A?}A&FzBv1XWv@=Z^`>uMy8Hv5p^Q_{@3?tWS@|trU%cXj z&o8XF_1yPVE+0R$^0vpOF2CutOJ82`@J&?}S6{z+<saU=rt&}MuU$2FPUZ63u2{Ev z$_=-#2`|25?X0WURNgi}e*1+tt-s@srPXV8U$>$9U&}V$aee8l8}o13RDEt)-^Rqv zANtT)w*>n&=+4CR(s`LXI($|Zq;DDQGOMS4{&D=}V20YzX14$g#b3^_X$*g1GbG4g zf~@BDO;u~kVG69CofB@atk|@U#iz?2-ibR*oiT$oq<qBMo#DD~vr03yr>`DmPv|jV z*-IMv$U^NO!5uD}uEl$Q*uZrjvUR$FGU~BOj1nVDgVtgN9;KI3^QyAbT3jGJPE=TM z7jhzK+^W<!R)a-C%Egw%(Y9UN^S?k>`B~W$_OeQhNskdHHGoG(FRR8F9WlLV)hoJ7 zl7(M|i3Gm#lIn~h4;ihVJRHv!=S%5<B(24)-?d8M_FPNFWp%SeahwwRBl|I>)9j|! z0L-s6i+X{v9J;+fYe#!prm?A+irfG-H>#PaptV7yW+`1g>+#4(J)7{%b3G6EG0*qB zxNNedC@v5ntbS1;A#MW)4=O-oY>zC&k4QBM)l4osLK|Z=30=m>_5nw%Q(&jLtandy z1BvX~GiwKHPm}9fs$%I9*+16gQe`yB)o*f*239vo(cnqEf*-Z;3X!1<7#XM%8>hy( zx_+TJoBM%8!^C`t>J$t0mDtb}T4|77HMC7#(o4*8i(6Bt+FO#B!wD@>s4R9vf!a)f zj(w_&KSc^_2{t!o%JfLm?Qrwtt1O$1T9}ZG)ri#kW1q+a7GuFtAo2<jh#DFc&TNm2 z?NH%rnb$<wx|g&8z|5Wn*`?^&VEv<8biM2X={H;}5oClPFCHa#0jIhN8?|P1(#_yh z{1nED(FpbeQm9FLr$PFGwcj|+pj|c3b*`?P?mE{P<@jSlv*c+o;1tNFj@p$GCI9RI zEN+TIR>BE5OTKCVw;07mp?iq$Xj`NtUlv??=e7VSe-ju8v41$|YJE;vUjXzZ08ArU zrVW}zh=0{_;toO&_8Lmw0m=utZZ`(-oR;1TKz%Ti6d>miGF}RtpYvB3cY!19sQ}k- z=R3GK*2A|Sm$1_U=Wi@ljOzgNnGkV-lR=hn=TcvoAf-EZhn&IBcgB<MOVp-maGO(x zA0&myd?eC=pJp{7RY<me$en~_F^Bje_Yv|`QpiJu%%c^48aaho>4l`rKd%v&yutcz z$v;%=JSTtn0A$7!4T<}e{IMTi3`u(pM55wu0>QJo-%1M^3I5E-O(TzP`XLwLOB0nA z@XSSB)dH($>XfVew2Id*YBp};Aqk^?YJ)3Q@`grgU~T;-RZ%sIJI*V#gw^XdtmBo9 zlmfOOmsRnMM$H}ax`!sTLFGnMH6?K6wmQxY0_p6TNLnj4RMhc4$@+3V0wD*r^IS(& z^~RcQ<<)E4x@@y-iP{=3t-@5U(9}38H`dmzI##)H%Jf;YiuB{ljnnD!d_{h`+$i@P zA0w_v$bygb=s!zt%(HcB#r*N)Mx+wtjed7tI3C{Cz(ebGCwo(4G9x=Krl-<CaN=#% zxj=vgFkTjK!^JI;5qpI{ph{gOF-(M46{JfhdcEM7C$=QHrG{BbCFO!0rfMejVx9bT zzuTXAVc-Z9Qxt0=#Kb-!({7SjaYNByGKHP)^Joy#o+lF|&8DSA8Y88eOR@A2_wf4V z;Dxh~d=6@eQn{mHD<d2*jdj($ChlWoo}q11`^+T}*O!XG)yFxzB_q%i-iAWpUI9J+ z3k&~08kG%93l5g_b&Fnb=u4+E|7nkBmikiU0E9k!m+1`2WE-w_W^<#iZ;<)9jGAO; zvVgr&Gk*>`+X@HzyJp)~O7>^KEAfeDi3v%c0Y3mYGII(E_497dfY;kHhnfNXZ4P(l z+GKMHUmWIq6ki(8=b3@@*~m;MwUrec)^q0U&PFD*reaIEsPJbfDs2wa3B;Yfn6YYk zpg{K%n8DV%)51IMHqM+p&1^yj_7mF18fgoeAZ@p?`Ajn}EF+D4Pz$5|@BUjznf4PF z+B&sj{&@2OiwQ0~i8K$xk@MV;teZH`&E3Pwotuihbi@guSxSi)Y5o$8V9AoF`w3=| zlqWk61!j?iTSl6R4l<sG`x&FQuHt-CG8+k@w$<<o_ikj~tdhR%oQL%_H9HM5aH%{m zF;Xg(J80}9aK%&qa_wv1FJCbUB(ym1eA+f3CK3J(hXxhxZrQp)U4^jnNsMS&tGG6= z2K1(#T*!2r$wVn$n%Qph>yfZ#5=)$|Zib?{Z&O7*$Jj{)e$B?q<#oy_5GD-#;sZV8 zaN@58K$9DBrgqj#k}grE0RlA_uni<>P+#Q&F*wvRYqI>2HAjJ)nw3~x{h)>ZH%DJ4 zG9xP7R&rqO^z75LZjiL9=i6>}S@ltlUKSJ#fXRJYE}b)QbcA${aIit)sB#M$v~j#4 za59ES0!^Ia=CM(81<JI(Inhf6BqLZo*UdM8?Hf2TxxC-$!!bZlc@mYmydRNDsauVW z>o(g1Lf{N3T@?>R2O?cNW7BsFf}(xVef!?djrQ#+`6Iuv1^E;Eq6_k)1N#QBla<eG zydZy8-<>DLZp_1lxrqgN(ORM-T@BIw(SuX;s(9X>{KQP6=N{eZI6HFUFH3EbZ+qr^ zSP%K5ms5&up~~2TJo%-Xv2u*RU@Q)LWw)-17YGKU&+d9#Fc`k~F8;<A2sPdsTOb37 z`@Bir{)9Y;v!GB5D%2$PFOng7(0oD@4KiZtCGtk{7D%HDTNmgkiS~i^=z^m7Z-Kx- zgPDv41@d;pf`a$~7cT5`Q@aEfCKeb-=VXR-RAxv6RFshCc^1ehS&_Mj=QKe|V?BK6 z$oQH*@sot2`%STmq5>_y{yDdK^u_r{kB)F`cVK#4y2JEu_}*(7xsd~71NZ(#AhAre zPkMsk{rRLC6^L^+D6gqm<Z0Vs!n{r(-0qjfzh$CyA9W#ngBY&w)zX(iQ;gXUsAq-o zu+iA7RzdnA`}0Yx_5+J4!Ce*jdmDXPBF{-Q>rzB6gUa45IotqRaH|$8rb8$e-bk^s zKdHqc$)pwQZM5YRDI0C~&DZ@i9tV(Fls4kZ7a>SpN<r!p$<Kt1MJLg?ibTk_v(h4M zrJ0qw)ZIlgC;(f|U08ns5o~+nTIBC=^?XPfDw3h3B{o`W(<s&BrBulx)*xa3Ucf@- zjFQ}n_3_UlnE|J#RlrOrYB*@vu-X7Ai(}c%){0qKM`XYDlit#1Zt4_WuczA-=vAPF zsqn9&p3^Orm$gerEp29+?20Yvi0sRYE!>kWMr@epiKehz>2%CCn{|h|89kZ)v^(tb z=tDMzOV6GzL&s+GIx_wOm736|YXP@Ax^$1Mp>hc(JedD^=#IDdoE_5%XRx}p1gdyk zl9cqLxnZEwYJ`FkGu~VdVY=C*@IbbmCL4BM0@<VmR)r~2-HWzduTzKiTqelo>4@ky zAdBjg7Np3793w4Ui0@B~%bYihyoam0g*Fdw@6p%me(km_rTza4{#ZShw1?Vqnt_g8 zV3xc7O99cg!#wEKy#r|?&<+whFf}*h7Y5I`9V@*yqx5RiBI>gK=;G#VKs-r~QMhN_ zEjmZxPqhel1tZCDP65ut+q+fo=Wv7v6qUFpfgN;p!uN@C%w9oNhQXsG+t5X%89bfd z;K3CfJkynyyupJ>V+N0;(ZPdvk@4EW;{}+(W0J!`<bXy;2C}BUv_V(_VJgd-K_N^A zrX~_quQ$Rv7-0yu7~jn<eSrR!ZrjScL}a&v$aH1hnThk>T;7u*Le(S<RqgzW#tX+p zc#U7LqnQ`f+Jv1gmciG@kdL;7&`ZO_2_j&|;q}8eWqA2qAR~-7rGpY~{3Q4ubZk^= zv7I@GNaR)=7&;4yPVCIw#`g?b=t(Js()8eXeC-Viy~xi^Y%scwXoxXOXe&MvHY`KF zwWLduxlcfb8y~}$dB`=BpX5?$jF&x2x-GSa8yg1Ab1QjN>&)#K;fhP3e7Yp(QL@>= z2#Mc0V(*U$VB_g|ew14dHu#5|QqABuBW94n-}x79rVhX!ta}eN03Rjo;q9u>T5ktn zpXle^`xO;k?aV2V&XiFyG@%(R1^Mv%DLy!WCn!ys*-ylYSYQBSuffiKxPk+?Rw=+6 zz^up^z>-D>FkS|*c<li80?Yum$(1Nwi9-jTXr1<hb<ANwKHhlM8PT2T<Skd`II$hB zyh{zH0JqFh_5F(GAj|6C`Ga6<fr}QJ)-p*ZwYJ-9Ev{f|chk?3*3R|a+FD7Yt;I{J z;<c^y0!(X7a%wW{s!JBzunNO0J$BWhz+_}s*qsx0#ayrKs$FzNQ`nV;8FqzKKwT~$ zGZ885O6EA>h&>*Lhp0lNKTy6st%~Fbt-`$Dn!*u{vEy!)H!^cXXoaU;cbwreDEdCY zqrRY4-hIxP3D)qQJy1=0%fpSYYk2!%HU#RJ{jhDUHs=u~hRN1xpq`o36YtgJg9sX@ zDkR;5G1E6+rvN#edvkfFPPm=g6%3Y7LxAyJN=dBsxn~M<{RGian+HQmbgkqbocAsz z+T*>lO<^W@!(TpNMOu~2Eq@uR+cLA~*ZY$B%QEd1GvW-9cYz`!Aar;m4p(r*o%}yZ zBd$Y7oTSkahnEp2UOVEv05jrDa&N?;57I}KPJAW@fx$V}O4=$Z_>%S^0l4gtm9&ka zN0qk^NS$Q|JfXTHw&YN<P_0)lL>xC!NY{I^jc9FVhe1T^r(ToRQZ|+PqV@0SS|%u$ z@7W>;7qGWdib!ok2-szkL<Q_XWv+k?I~;`W&o<c*umjnQfZZvGjZ}9dV0RBHV88UL z%-Eems*tXlW_A(zjso_V$a}b|)6B7EQXFKC-AYQ8lhN=WLcs160XxtRX@C{5CF2+H zW|qu5MZmThJptSLg90`~rgNzIz3sncen-c>)2T=G{h&&#&=&M6r})B~;SfG=X1yTn zA4143^{(hxPWzkIl3qb;+&tZFuxgoX@3DxT3jij8=|{KcDJv+ejVIdhi(V#GUxE(8 zgaobBUH9*PF#=P7wZ7PENpl<+^jKF)U3*ECIQPxv(4n6urJ(zzAQcyr2yZBHv4Si~ z#Ajuzp~S^Y$OF-7cQf9LbP+A$;(=6gvG+K{#or{3m6X3lN}8$S;yaPkE-BR|rwHUg zKgmPs_nN(2OH~MvA}+oYfe5<k#X#4ctDvN=0%fHh6ssoa$ud1hbyk#Y5Qg9>PEFB* zlN6^&%xUic5gd*t?KG(=(gs;kvGpPgD3l$Kq70dsZQOIXmYcXtD@#uFHxwY{fmz9r z;0P{pf^jeB-n_f#^%aVhtg<1MVb3eP`>+MIK?EGx6K&CeUY!HXT9JEXJ>eaylPCus zQ*91MY@4aYT66Z{DsQp-)g!Y8%#H?ea)LHpHb*7Muf=5LH(L?!o;mZ}=#rkuJ`6#A zOj_^f4(1@E^LOpIg&RLlR@n%UBcK06gLJnpwLuOo8LUA?!3G`YUf(q;oc#TJeS&l= zzuV<UYw;q%Ven|m?UgpdQyXD5UO9bg777=_a;{=)Q7J4}?xDMD7wE$5cjmL->26Ib zy%o6uGM!;4FT4XmvG3UiS)G4KjKyWt_qv_aTha0w?vyeJPQz8;e9d!x9T?0#m>+nq zpL?#~danQST>CkoGo^eIwP)jc59gAmltrE^qlTECjwT&hCuxXf(l!TR)vp#D$q6;h z;d3G1_u}+JW)SjiL^MBS0U<K*{g7pZ$nf_=stJ*C?}tPPk<ssme3}s90e;9=2$A0N zL%vH$Hf@)XVNUuPdJ}QlnWQtHck>MWSzD$EV_Bim)|dPvjLH3E(jt3_$lw`zRW&M7 zM#0J|Yi+8i<-lBY=hv23VV7_fx>uSC3xsmkj!#3&t1Iiv7cMAYy{@J<IdFcaKvLFJ zQH7M|=O<};s8enZ^$jFOiz^6EO;QU~FU+d?-(Fd{v1)Z?oqII@PMZGQAh#ERe2uLG z?EepTPy7L>N;aKVkVZaup@e_V$^2qlr&i1#?_?gij~TTAexvs^am(QcSAusiG4PnY zY#%s@Ik;nD*p>J^iSG%amtCNKNuej@M>vd4@Vp6WOxR+%-ze5xlOCnd7Z`#((bnFN zSm5jF%f$0RJ$=z9+BV7)ZL?&qw40H!;$l{K-s~?UUTO}gO>1%4cK}^t2X4*a>sb$) z;aUbrV##r2STcePW3_V7m7&~bZ7lD9uTtC?T$_dJc!NuKYuTb!F9Eao%x57JU$5J} z`107<jf))>yVz_k=@CrEy9EC<T_gZ2ZXD8znkAy_Q0#_mSxo3MlZ)@|wRGblUXD%- z*WX3@;s5~)!{0$JlN;c8CgAB}3&|dE#O2z82zSM~yyZ}X0EE!kC0YlhUCf(CO+S+k zSi&L$0gxl5Yy?_UPj=H-J()&+3w5w9wRn7CY)K101+gWa{OChrh=Y>amkL?69A%X& zaLz6{uw#>PUR-iupE>P15L?nGFO+lUMC<l=S_py>glO52N1u+92-Q#H97=Eij(R2E zb4}N>j?=75KgJu-V^&A=ja+N$>)m-EL0^f-R>#t~FGEeNjikcFq)b)yy<;suHFboT z%oXQyFS(=}@tT_Fa8MJzFRB|TJp|c-DRTFHC%M}wDkdOO$U$3SP!AntnbWl^I0j^d z(<h?cvR8p!ETRGQ4Jm~0epGjSJL~~xgiUWrU0g7`q$_;iZWBqTyPG1y0c2$E<9J1- z^T>f!AJpgVRXO2Kh0_o?bmTJ&UX)^mB-Kl3@lvi+$G@nAt%9o9Q-)6SqQB%tt(YA1 zi7)(}=fqpz8F@B8R`5N<C1FhtFIVM3H`?WLtZpnM@5OL|Qs<MkTqXg5443u<+M77t z(hvVoAb9h2Ilt7+#(LC!Y*KY#0O2upa5v`B*pf$MC65KNw8?Gz9o&0qi<LcUHeK4Z zk!GNEno;(My-^>3T3hSi<=$&fT@PvnGQK+G*tWAN&l?^GVP8uglMzB&^>mp&%{Pzl ziaF=hA@T@UAOxhPzzL!1wfQ!oSUpoc=*#X0BP|W=@?etS8}=9^QOMDqC2lU6^BBxG z)HsCne-}gi&aOy_y#CQ4RC3P@WyBW0;oUE5%i2O9YrBu5H=`vFvG2%nS^PVLHoYT< zqUL$K!WJj64QW6M+?Mwwmc>dQm24pkuV-K37#s8Gon4ebW<`*3954f|dv?h~HD54I z6++;-mGFI6QCEb`q)5pjs)j%p^D1!ozET|x(hu2$3UXzGDstdXS8?JdSo{hl2+QJK z_OMz4%X&Tb+^(f&D`B=<vhPr`7oU=Sd#px>wMY3>m67r(H@fgsErxx0Orm$^Jvxqc z2072=-8zH(g@U{pMA=vT;{>IH*be(MQ$m~D6SoSV<GcpmA6wELdQQv{pi00A(Y{wk z$UzqKg-%NBFO~H*T@}(LO=r`d27!J_r|1am&yFtqc&->}fLN5h_w4+I=?%QX_I7?c zwKd`UmMaC)zQ2p*-C$TA*=54jD^&lhJaiDo_j41Of$<&WpvE%36(79K=X{7dD856- z{=$2$AUb8??P*y=n}tT`qgF?Bn;W(taLlUNqFM`rO?D+dV#{1sz{nJ~)+)cEnTW5* z)xM()C;F_$z$J(on-p^=5B}9L)h;w|a!P6sG^OREa9$?4ZrKya{+M(JbAd<w%tV{% zfCT+-__b<ah>0z)6ps<J7p{yxaSqJQj@Wj6vWHoyQ@Vd~Gu_Yd+TJ;G#|wgj;$i_P za$vEJq>L-nKagM~-{H=w;LW(+>$%#QN@PV-;JgR<%ecCM&bU6y`p&pMN=B3S5I-!} z$njsEE7NM!t_Uhg>V3l2r7JsN$n-gfK<QY2^*0%x2%~<;JVHd;^h1^sBI2YUQbou@ zAe4|yYsP<N5h3dc|MPCH8KnjC8EVZKHA(pE>RsQT6!IXx;F{5_&ekniP*z^EaZ7Nu z#!a89)sA=H$LLx6UR^4dn`ND+RlTvkZc{xANVCqX)FSw^fvZRB*Q0J7toim2SJiC9 zB$!;~so7Y+VHLK|L?63y<NA#??xN4v$DW?q`olD}5mv-UR(%i|pXn^yPf=I8KK2oM zPX>f)hVd)QpMS)v&$Lv5&_6<N9gxP8cI5|%j;#7J>22o@wmO_31HNGoWNSC4-2E$> zkYVii>b?|jE9AO+p|V?ptm#eo`anegx|Y~tibM^oKL^rIc|U^atN+D|W@ra;BYO3( z`^9Bvp{=>!X&@2OEoCt31Bno;DC1}CylP{rfPT}Wr+w3lrV|6vQpK+QA;8-AN;vYT zkgWnH5=dmKIDEfX2BS7D`Z9L5vC)LMy=!~_QScwWw~v`cmxC%Vr=YhlwhZPCheZzU zjn7up<?$+Yl*dNsfDU=Vhsn0UPP@8l%#XCN(HjgzCM8go!hw{BA|(Z(rvSlGRaYkB zD35q@4mA)(I1JhAwJiDY4O8Di=4%9_ER_73@}{Cdj9sP<IVS4TE;C+zl)f_u*2J9{ ziASAf-$4Ez)ce~smv&7sD_!BH1czh=`EG!l^^nc#MVVUZ0XL<|3eMdClQr^eXm)*X z_^z`=f~ziMiYau$(I>!p<iG@1OjFDkB@;H9B#8W7KJq^XrkK(#@(a^24{8cRPYXU1 z%fpXng&*&0THs-^qiKPM!}iECpb#nl%4i?J#_GF5eS*af4~u;ujrO#ASd;+Up4QOp z?Y%Xh_uH~oT6Ul)sV$RI+Y+Rr5522Yq@<N6@D~W?k^R_K@JZ_}!J0=}ZF`00Cn6Rq zSsC=0z-a5l-}t2FCq&ukVCc_6n#Ear7&#r;u_f)H*C9I7(P*lBcoSkXbdXMcOWHJ3 zkoqL&F%aL9LpG;Re3J9nY43<on@4--l+$06lKKnmnK9m{j`6ZtfpImL*d`|v+e|_4 zkCf8#o3hUdKYprQnduEZ8+j%)dwE|?!mVr@kGJJ#sA#dvx`nGGZZ<`gP+x3j)L)%) z7a)ma>{DiuI3;>8)EE72=&$;yB<wxZY6e-&f0_F4d^}Q;hw5iR$b;i^0XS4bHuglk z6DgUA(&ff}K?d6t%`)4-z+0UKAYCyuAu^B?{^&=AxUzQ(CXSksI$kUna%2v#W9Lo8 zAazSS%(g&cM?$wGib<I@bjqcP8?-J`W)Ge6qJ*+-c_3=%kd-^lT4Jd}zXBo!dJY6! z=obt0-Ug=3z$wscZrUD_6H2y5vu}VV6MBwWOz<#y83;7V$Hf1dIucj7*|L(d6=~t1 zX>58$@sVVVXA6WvnE;k`On?omH?PBG{|Lb12ejWzUu3G}Asc@@<)^k#wAV7UQ0h+% z*}@CS7(X`vLH04VFgvM*uehii1daP`ytMFG%TI0Lmk?zV)7?JIar;nP=uuk+^d=T1 zu9jT#+%kPQfv53c6ZjXafBdZdeuFb)Hv}ZLuv^_cmYG$aq&{fBf96-d-$$0cmAJp~ zEo1lNH+-)kERzk1yGSoYJ=}Q!m#n{rJ2#QfxIT%?#D3j#ecy9E=eZ_PgGm?jTw*!j zggxT9P67%OR_(dAc&>XrSHI^1Ae)0(!MZ5Ux`aoW@<d)YF4>VYE*ZtfC8#v6BF{D6 zb4~DElRQ`ERi>;!k`|fAbmn<BrOJ?y)gg4l=Q6&+2K<ouga}djA+pL8R^o@OAw>G# z52+_a_^%&w7a@ymNT!wOTVPgj<$vC-m1uBfBYl+>lC_QWQ1VC4VC6$YGM;cgM0jvL zShHqj`L@bRb6)6_yIxk0o2%rcQUcc0REP-+8&X-l?GDLuhaV&fxGKE9$`9I5zkYp+ z&%V3nE>mT}>840tq~2b+s<IAi6Yg@<JA)i#H9BMZ6#;8w(sK3#*r1S;?o79X?}1o5 ziMf^YZ24Tw_n)yE?V_HwDP2}Ph^^{H7B;fz$DUOpx9{qm1hor8Ymhy6mDuz2{mL$L z(}*3;PLXWn(1Rc`bH%|I3#USnAH~o?w-gx{QV@H`x~DGNV%bf*I}p{_)n!p4Cr{mW z>q@Q*?lXe^iYV58cT0Z6cf_U1vCsZkGVOk0+u3iv5bZlW5PD|T|J*q;+VSZ(-;O?$ z$Wc-2%@-1dgT+M$LM=@Np=gKfy|;!UN9XPMH?tL*#{-n1lCi)syQC<5S1Drut^?9| zSHKfw6eT00Pg~CBH0N)HrsXWi4KfH)br{rf0?6o>#>6AqqGV+BxX9=gWc0hpXqrCE z$dAR(H-1jj_!1=6c%OG(`f0R7+BE$6oED0GY2Wjo>!zsx2~XNBzpF2Cilwd3gd%Uv z+wmi_E$g+FBPrVYA#JO)&2F$pdUYexcDcL7Y<C!Rc4MQoH~tdbTlS}m$M5UX_fc_9 z2(`_X*LT8=a*S%6_x&8e<_?3Cgd0clSDia=n=2-*!;RmkUUlxgVD2#7;l@8nnvh1y z3o&y;8oGhM@frr)+$;jajSon^;p*He8AO^2H+~#ve6gfDfj{BKizG^%lrFDSg&Su` z2oLJuyibbA*3dOldV%B{4$Gw+iNeGt&gTS%EOlOrQ>~rPJ&|D@Zj^hv>ZCRKU?bdE zDT&2Nec2lFqToRu*a|mZDpjAP*b!qg@rM)?#OQG2XC&1L>a3PR<vGG|V~IeuLsE@E z`6b+VrKA!krROoY!;K%7#G@F$;l_K!Ib1_8BVGJz$rpw-g&V&qQR3X0Kldap=}swG zZ2QgSjX_d8BPsILDetGorJop>tPdFHu8Na;0516kjg+^e295j^%^o!JXG29kI#lE* zhl<=hROHP=Ma~&2@|>X}hYc0^2YP2vBpe(n@|#0N-aAy}hlYw=I#lEpLq(o3ROH_v zq(R%+HB_Wf<DfaeI#gtIsL1t0MP5Ht<kX=e#bDQ<&<-fCA7miCFjS=M2k6Wz;)5(* zFW^rNj|U8^yFBr}!75CYW{LqqCvmwOBla~JrECh6^w5C;Wtu7Yq|Sg=PcxPYB!ndx zoXf_d)^WplPcK${6Vk1UHtXcTiD-w|uittaf3tRC7=v90P9@bMCaR_vxTRn2m(Fbu zb^w#WNXn)gu|1$8-1s$efs%0JBHAeGO5<IUv~^@3n>wo=mF_D*ot62V?s9XZ8e|V| z<)*EFWr^L^VRD{Y)+b8*7{nJ7MV#UFu?N~WPn1BV&s{{vzv0q|Tkg9C6&qQDLxm^- ze2w79aUKYNXTRBz8#pllX-f*EP5{#5PzN^nv+BSGe^&lE<Q;x@3mAz_u13W2AmnU8 zC*yN<ctgwT@UKf+Uakhj?iaIv(m?K)P~qwF+%M)F)UfA!UV8oc<xj>+p0o`^ttPU) zYs8Mv>A~vu?h!ja<~LIwV)FIJGLgK9NgRo*6eP+B7$!KG>ybeny5el3?fqE*NZ(uQ zQ*uSGST$8dulRqd)JjlFNIJ8M<y#$yU_t|m+^JR}P$RbN4mia>S@z(KS6$~6Uo-W~ z=RQ38C(Fit<VfwxwLe+*bjgX6cN9K&<B{6=)k{}Cc;oly&%b2MX;)pha_!QUJC<E_ z-MHC5obXIRYW{1cK3($t`Qv6^Gxe?uuQ;=7)R)gaaq|32emLRF=U#E<(_`)(`S9o+ zg=0>8rr_bxU8C+D`LaUjIDPY8t622i(kXAudv4>BkAM4*{qw#tdi`rF+x|Qc6!7Vr zH~Z<YwX7Q-pZBAp){lJn{lA;H{F^l|l|6iTo)c=g+N6=cMa90`xH9c#h**fg!X8FG z^^1n*aOV<SvL`THPjTh^Z@9CDFr!J`h)Xn|hdW=yB|8Sg)y^cU(YRh94Ap2{1Gvsq z*D1(*vY#;AnTm@eE?n2+l2!h2XFaZS)D^{5q^_^x8mF#jaGk5JE?noS>u<QmtLqHJ z#q-s51+EL!wG`Kd>iQ6_3F^8R*F<&68`Kx6OZI?dl{nn_1FlKx8V2jVL|y0Lx>Q|r za9yS@c>z&Y=);}OxP+jGJKw@3ml%dSt+=MC>vy=OtLs|COa2r4a}hMvB|9}H?2{sg zYS@FG>ua9t1zaXwDMGMuW!hu0>*`7h@;*|kd~}zC9?tZU9+4sH?}SJzW&HRd3v5QZ z((c3Dh^v9GAF>wTsX!>XGVPzlh!Z^fpMTu`Nt<6u!1WR9hHNtKpGfrGHjSgL5YMO5 zLLN#B`F>i+{<M(8X(3%{A+M!{yqy*jrf1B@)ki2w3z?D@a#dQ$jcFmP(n9LfLUyNx zd@3zO?gu49_=B_%*-TGP^UJi5SJFaWPYaQCYjQcG(?Z6jg-l5cDNYL!GZe`s-<B3q zoffhqE#$tWkT2j1?$NE@ShJ;~W))WjmOAADPx<Qg8!OnO^L72pSDO*e?SqO9x+7P6 z>5R!UX8Hcr6_s^tv(A`&IS+*xg%P(zvL;n)?9UQfK4t3^63c@CQhxc=t-QkLd#7#X zojl(=ee3j~cg9xU*Yjg9-^yDhzW0i)0)|)etgV3Rd#6m^I%RTF0FWesGG*%4DM`Rg znYMLG5;#+)Z<Y7{0)@`lIwc9DDVJ}Zk^~e1yqprqF+CYdDosv5Ik}B9lQD34Dt?jx zrUAjaW~RV$MGEx5nv|W!Bw=WJGU(HiA)T5G*_7mtTvxkZl#%>~uUn11#$bRUxZ5k& z)~{ae?#>pyqqfkL$?j`0{vDBXFEI0Mrv2H^jyHEiR>^eDM<k7Z#Qtn1Eud0cr&i1# zyYI?I<3gqcb`tmOJ|KpWw&y!rxu2My<=;<~Y22)k<UNPf#ZtqMC7)BrX(5ZPeEX0< z0!?b;70F!GFOZQPe*rdhnff9Ys}{Y<=}Nz{^`9#Jij8?hd7j}?_0)c8oxRhXSSO0C zoTaz9`Y(N~mYl7?De^CShs@T_4)54agbUM3|AhGzePR&(_-Gl1BYXGUpYu!hIb|t< zNuIJkAViZ|?fB&H_;}N&Oe94=D@FCQ7`0o{;m3DNT==OLwrE|+e0Ab961jzUNAKT# zNIC%R%s@#3`%^7k45&kgvzvR}KayDhgBrh&cFfv;r+31f0eZD7)ss>fcL(;cI?=Tm z$jlWS2vP-cu1dMvqa*f;2!$mZ(ez*$Yypppn4q|&rID7=$>?QQ8*>otJ2C&yW_=%5 z;zdj%4YzcMf)-T7?okZr;;Ap1*PM(7E|kcHkLOSgqd?Igu@@aW!uM>@U1cF;b6>&L zNhs1gswf|DK}eoQh+%12ZWQ?Dc>$?kG+uI55ll}Ta<R><TEIz5u&rrDZFP&{RDna; zt-5U?3jo)vstU0iOggWg(oNzlW#vyrby|n$mKVjAqX>{2+mt6mu-=qeq-9QY%fxHT zG|fPlB=B?|*S5wzLl7z*&+e5a3C1Cz6NM&s<TG(uJw`iF;5y&*r|M)FYpuCrt+iiN zPiRQCnwS$T7%x=Ll_1rL{~|=}<8G~0e5;=)3B8h(_=hg%=A?3p_WeFL@@j76?GfSo z%&oB8sA@SyH|1G%Y;>Yj*(3>5`2kEB?0cBPn}U5Vix;nl26N&fiyk}X^1$53GGVTG zlN!3sjb6LS$6cL^yE?^P9k`R%Ph@1tWgHh_?2dsTzW^6yLuCID*#mg0hT^5*WkNL3 zP5G)eX7zF8f=MpRFCExBwY3VtCn&=2J+2V(>I`QEO0f2hP>}8iE2dsqAbIt~PfKAh zKk3$=#r4@lV!}7FU+=CnIT+nW<_PpI6G}A_o68I#w--R8kgvxv!cVoyP+5{6%ie>+ zd(ha<>og@WJX7nF{XWL~AoXF#@ObOn<<}?kpRI3LVzQ_UsioGZS_JFLoc%i_*OCnl zDzvBa*&||D|H2#ZI@TH*z#AKhjr;f&WNAS3S#u1R#Yx)Xw|Bo$|7PT^lXe8T%%F50 z{q4S2%&@&P1!HaW+U^(XV9#zXw<clqK{Z#)8%6?>d?aCWqjfaw!I4tptCkKLB$0en zGc2GJCRBfip}7<GN`m-f{E>14yh5mWNS<>ElcG<x^-0-<xc)Txo%vQAqlA7LQ`{L? zue0jLsS>c6QGlk%+>>Q5Zs&`^5h1E!(x_Rc<%SCC@3f(<27}4t+B9#7as6qr>nJGj zxj|B^hRAxGn{{i<wRUl7deaI7^Ip?PD);QPX$i4SpVZ;4USBGmsxs{yfzDdx=u)>V zmGvkds0&qTI4YBx`ef4JTB5SQhAwL69`88>ac~PSL>~&*1ar%(OHla`sC3Wme@=3C zU%g{^YhS5DiZ1JREt$5f7xS9-IxIY*!!>f$8T}1Y9{B7+v6)d`5PfSRQ=<5ycj9}Q z)BKz_Ux>VBw51a{s5>5hYxfI`u(wa#;fW{16_Au8fwow&T(hxUNP*AeqU7#3>Q0Fc zq(t06&zPfrP$vibdf!uw##%HXnclETL=Huo`0DI@>mqHDhcvZR(k!?nC|U|15FF~I zO}x;gc&#NXFJQb*M3UUjpmSW^0_{}Q8w=X8GaZSoxnM`)P3_2TuOo5Uj_j6>e8EzG zgQzU2?gmW{P)e$`O*wH=UZAAQHRX6V8M)o-b$`%=W%%h1dMTIgia$?2x^c{#FCZw= z+iyiX-b4$zMU1r=|MC_$?YFd!Zo^igL>46q>{fV6;#^TGq!o7x<L)D+Axlm0CF#Id z^CNGEcfRm6a5;%AR5&cD96e!3QV~_~?52X5rf{EWYQfj&m~DyP=@OELp38>6A|!%| zAaEcTboDmD?l*Q!mU5IUYnOL(xh&W!HRCNph8Fao%MuD@wGbpRd>Kg0c4v^p=yXU- zde_z`Bqm<pJfR6NBqqr%iJ2Q-JTZtkOA#K|=u@|$1$0(iu7MquAhF?1B{mfIE!V(h ziLFnFZ3F%1a=Q7*2p-K?meIpPNqGXiqu0<#^6adoJYuYBf^ct?_V4_z1rQ@TLG<Cv zfIbUg5PdX*whO$9KD=N{ycT_4fI*)mx9A)1-L>A;J4eTYhe2h5W{7fJ8N?diRIG8s z+hPru#af48ZKfp-J4BU{+9BGeIFK`IE^muk5x)m5UzVr`mJyYh6;LTZgpO1`7y0!> z^69?aZ)_X(=1bh32HS5(AN$-v_<H2+P<>W(dAI74x9`~hG<3`Ayi<N)+OcL4D#W3) zj@&Tq^6H9TDi|>3AOYnv`r?HG0~*rWm>;N7O<I^Td^Pg-(zCAtUZ+t4>oP-I<F2q7 zjYw0{WdVvD4e!XAy}WzJgMht*nX!}pi?W8r5<*+_&yiO`d-CI4ToQl<nKLLx?#-}T zf`9}Lf23=Ia$pz)UvIXp-q#c6T1m7onJhKqDQdhh1LWH&QPHKsm<PF{)!;aM?@z(| z?xQ=|!Rd6wDi+9X0B8fG%Q`^45$rnUjzve*`fOU|D0LvucFf$bqhPWb4x`Y`RUpi? z)sZwr@MRbxmKuXY1e#@t2yt4lWQd5@H^*oK%n*^}Iz)CIkYSNDkEw7X^4cb1)NVPD z;Wd;DIpx6y9=Ny!#enwcGfW%9nf<HVGfw}?vdApj2d)Gl-jpSGLHO~;$-1W@%aejw z_M?8st8{oV7Az4qt;j1^nvoGGyxkWPG+kCwI)T^@kT#r7T&}`0o;=nD86r0q5C)So z%uS~`2F4#rFPBUTF^JZe&2Ec8Rv2q&q2p43yBAxGK5%c6k#Pd;@7xz%-X6e@fWVyR zi$^Ee1;{(##|(+FlNFf8LJv=cvFry@K*=(o*msgPrX%PTEHe61`}+)?V{#d+h&*-g zOG@i9K}y2|Ri!ri(@6#8SbpnCME=!&=`8m`O`-6>19mnjM$z=3@bS{TDCR3l^P^>L zJZ2C@$}BC6a=E&6tndo1Y%345l<3NGm*@;2Oc~3J?o`%<Artt>&oK(?cZjfAcbn}? zbb(YaYH^Fzh`&yr0aF7#Omptr2YInu#aKB00_u=OwHgoajV;{`qwjB;zMJBdc({Rv zm5qEXQOMJkD$iP2zKRUz!*WQ|r_8Ocq}>~(Tz6GrMO-u&76(M!1HOEFP=#BzT@T6I zJ7sNRw*-+KP|FzI$ST-m9}B0A+PBQQs2Xu?k%zY+`;aNqpi$WeUbw|E&9Eyxol{Eo zCf9!4z9qpj+vE8lEqSWNTPJ}slQMLz^RSo=r7&h^wl9Jr13ux0@kbPongWVqWU5mr zN;*a<3i^SfI+dch{trcUD@FAvMM<|ql}${p)P1@kW^b|L?l>AgrF)VtJZ*jtfb49k zP4G<*fNy#LeA5F+aKPT9J;3eWB7f;ONeT+G)(m`AnCh@MUr_1wIM!29UyleS?3mdS zviz@Im{0sOinjK5;D0#?28j(~n5r;G*qtrOGPSq`E(167@aMAn0REJTg?tDLlOc@o z1_RtmdC)Bo{O+S-HwJ#8^jL_wA^%RG@D>=9M{GKTUB2*sqtOgsnCa?$(U4xYxlzrr z>vn58(=6Kic6Lcwb}>k2F%U#2S{Hlbb$N$DUXXY6Nk%c=w2*Z%wCC{-ESD4nc?bS{ zmv<lpFKsS>ZiZ$b6fKnb6ZU!}t*b__!niVwgQis&hbTzr8B0N-WyzE60OeF_6+4q6 z9F2nv_+?f@iYa+o>Jg_zWfSN{?cU2`+>5n_O2B4MYvb<6SYF9T?2_!_u?EOt#~;{g zG^Zfq6{agGL|yE;&&;;U$BApm?=?p+QNoqz&XhowYdet`Q$lqr=wV+oSnapz=nNG> z2`Ra$h!tc?pj31&p={sO4PIeiu)?p(q1U!i&9+JLg|s%<Nz$sa{-!+D^kB<0^jwUy zfWD@&Uchf-V?xgfC-DaanOdb6q@-A*qF!mgKYG7<640eY2;ya9H*+dK&F+?MKNla? zG4Iz*!q5ZdO0e{=lq(UYTu3@&DnE^s8y|<50M2ZA;I-KIMFxuewfqEaP|CKCn=kzM z(wwK+xZWc_0Xq5$h%F+c+Sy5LrRYe{Fy{Y6wwSeUDtns6vmQZm_VC~8<thcoVNIoP zH!UHuCv;HuFUiFfYQG9{adTg{y(Ih;svag)2MT1+YYE*_LZD=x2H#yT)LyMtp7X=^ zRpAqOI1Chc3;=2Hn9?jf`i#<O+C>heglvCg^%BT%>5);Bm}}RF^7wqW#W07J_3(`D z*b*k1-q3TA{iEfyj$=|Vv1@zxQ8Mn}d2fZTXvdEcXrFBuEye9RAfw(r!h`%hwn<k9 z=4jR;x`6*~4<l9>fk5Q(2;G_^#PEous7}8b{gNUsx<bkr2eamTH>nhk6{LhwZ&QX_ z0RL=~(`6Tx^%c^EEn_`qkt;`|%-Y?82+2qvE0dIJWzOZ)8hJ?V`sx!)q;jP(o3fux z4!4qL9cCP`A)9MZuz^C%?>5OPYYG%ae+Lu=FnYwtbSPx2#jOM=JoasOk;C`>nBirQ zV(2mKB++EQ-_(pV1!H+P_tR%XF~+2F?qATOB%LRBg&U2y2~Ie9WEk;wTUqy>()C)H zMSdp3&S#+^n5fbUvj2>hUE*fVA%ZkmH#KLMaStU?Y!ZEfLv36bKA|T_f)zOgvTUPa z+k*%pltgZN$r@mWE8QiDwcrAxjLaP^W#_*yxPOkPk#zD&j19Crdj&$8YfL&DT~c?N z4x<nyr4DLL`Upj7wFU#>-3bj~&KDAtD#DHbN{~K5Bxa>Et8F0M6-qGy8Anhss99-u zW=%oE5N4&_9Khn~5hSMRpvd*eu}=nt*jS@mdSqwDL$w(j>t7U34P2tjuJVtz-4K1a z@kO`oLiq1S+x3_b5@3DilYrZjOl=pHeIY5uoaR>c6Ocn(24mZvH+0(tThuL?Hg$@8 zA}S3G9&d2HtRhva*=j=*RtmJsq1IxVzLHF@x<q{s;U1<vQ(nSSWy1B%QD0Z&QD)*6 z(+?4SP$ExC_5uv2z{YrgWFK}K_ZWo0Kt;nmDXC>DCMl*s_vA?nF<hOs;|tX84Sv~| zXY9xrlh2@*tWRdRoKJ!R=LyuSjq6vQ>oVBO1seMTE))BP=c+r$Xhat{fAw4&i(F0Y zk9e*Fo~zAs#XZ+wJlDE$Zr*y&^}%!9dOvWU>k5x|(`C|IR(-QdC+Q<fH}V-uZ&~^} z`6l=kgp7lG`FhJ&6S6xgMD&)w1F`yPRuJ-woM6ZXLQZE*<fpll5V0KYhX_HnB!!4> z$>b5iG>;NepA;g`V%?M*O!G5BE*=>Sks&S|-LLD<ga}{vLq;$%g`xW)XAvT7-4B^Y z$PY0A=ZDDkMq#~v$W4UEG~|b@C8Q9|89!tjA;P@<kb4OE7ii88d5931+WZh%&B$Q# zLw-ugub>GD$)q3tX{bcB0sYUrr5`ROzDVuzq3bj1ewSBQRo7P4X&Of%l#t3z+X5lt zRGCl1q!mXgHK@(2>Xp?McW_z0d{tG=y3JLq%Ij9Hcgk1QS8pm`iDqS0O*!_%gI?Ew z_{w#4wGwA+j*Eww)$8kP*Ot3c0iT;|71#T>Rj=Gg9h$Ks#c%2a3@O8=soJ=*rhbF< zSi9w3&8|P$68XDt$$aC6s`8cVH&)&O9KOYKVFt9Cm&Iwen8GVJA#3X}uU?)x&zg$5 zwHO&Nx~*%fHk5DNRJX3WVtsjC#me=9!$4_n+JIWMslcd9i`#;eVp>nsm@C!;uL5L? z#<rfkfqop?vQwv~SyZ0{N0%ElnY3jYENW5xpru~e0<`(hv8eu-ty4kckJXlq^omlH z3?p~iwoaz9Hk@ppia6;C0Rd;`Y=MN7Ac|?M%9sqj5)!WVn5`$)a1a-l5qF+sCeUA) zJR@`i1yxN+XWr$^4ixi_wTl2OJDBd1Bgi0)pg@m4bW4#rIn&K47^>RpDA0RCTn%S# zp2c6-Gp@z_mD#Mo`2c@K$ELv9#9vug7C77a3ya3}1b>D56*$l7Uvd2kmy}M)tJguv z<|9u^uL)Vo8)DuZmePNcJN-=tS&0f&`JZ=_>t0}s9gOQr^c1gZ_dd%tX(21pLTb}O zWJWe0x10yv0LBc1jL&0fAwo;$<EHtY8{p;oYf^~Zze(cx!U8aMQoRY*SzBI>T79y~ z6W1t;jS{;kV!acFWpgGQOR>SLHg4HaTT#79jC{H&%c)|Mn?Dc{yyaojCcg+_1C!XB zSEyJe%}{71@UGjCA)>mX<_@N@ExdsNZ16xym*G{3>6z_|PMJD2V9`YxiL(hAO~y+m z9(X@pC;fISY2+h4`Oo5ki)@`*F@HQB$Z-q&VJoDddwU|s8|GIxEO8^-c%D`%F1sG+ zf3`RumdOrZp1v6&4;is;AVKyPiuP~FN}W=Cfc1DUEKCp98yCS)O5`|0z{v$1Uah!R zJ$K>}mWIanCnSQkO4H4}*vg7UEa?|={#78a6Ug;}rL0D|*T6}b0LNCB&jnKoM|b#s z3!c7ep*67XNj1*TAzwebf%(h^C{E|gxd6-BeAz-3JcJVOPh9UMsbi~5ld$shl1RgR z+_{#jIe^L~BKTAXk2Bo$CL1u31Jlj68OIHv$=!u<SAfNUoxTF>6<lFqyji-EeJyuO zH_)Lus`8*)8Qw*S3|*9{wL2P8qC5c5m$*rt%qp_Tj9m+kQK7*w{^96hao)ml9|7aT z-}$9`5Sa=fplOd~v*^aAwA_;JTc)MTE$N*Pnl@*C^GH@YEKvB0*%af-G>_QPTS-#y zb1dtxB2o+C`V#(?_&!fvgGu<GVCc%|^*`_CTqEt3&tP-SZNvrnc0dFa6gV)~jg`W| zS5?$il-I5k)~hVgXTPZoF;JXvL1BxoEclLgt)|X|Kg!3yH+2z00Sj|!Lnt^?OUPJ6 z2jPO<B0}(CS?T<<xL}*DQ!D0AG8dF5(E4Re8ja?Dxkr&F`}tfrK{JS9&!9yT6R+85 zvNuuOyCdE)B#X>c#%Fg`Rpb;=1=<&ZaR<Bk3~{5<q#^4GaT61>Z-5cp5{Nga@~Ei? z1F!HiDMJ@ceA&`s)4ErlyZ7&EUG9M<+>6(T;rg0P|uQp?Xft0Jr-7@D_c2a_&(s zjgy{Xf=04E+1!MW*J8J0`M#H#q=7g&!RX*uID#=-qRkYAb2g8hI$q$U4#~(FNvI{# zl9e!%fC++Oc&|A8kvyOuJuP{UgA>U>+zjGeu$K-FHug5yZ`6v>U+&r0qbd`&Gvp!y zfyt=tBrvUgpQnA#YV+Xq{kFTnd_Fto%3$S^ji`RBEiZb3bHlv+gq#~_=vdP?{Q`*J zH`76rq+qu?L*GylD3!AnL20{1=>d~RXzAa8)S|$>Hg}5XG5FW!c)!NG;>MY2U!>*a z01EeL^Mkb5g${{Z)VSoL0{MPODzDh*Ne@U4r+c<)|5lmblOMHNfK473Zl`VL*eGOi zxybFy@lB}c$knrF<@m}t<;2ZiAd|x=wVV>(@oP5==dUtPd8gw#HM&amO7ZqNN71yE z-S=k~=5!_<$(Zm$!Vk$r72L<|+xf+mL2|yHr~skdoqMXSX^8@PP<<^S-d>^aEr`F8 z{th?BZQmX8`|f29L_vZMfdFd%La=RC{OhC+RK?;F!J{vbc&e0vlqA>+K6)#U+ndXF zU!aKa<2S%je!<m}r1A3?JsR@xOX)L}K-A<O2o(Z5Od79(N42ct6z=qGlDN$c-@C+T zOUn4nroS2gBovk)lbkI+S8)I{=pro8u0E3bLo7(2Ul)Mr?9#>2wh*|MQ#zl>_S)Fu zg>$*n>g<%Yc&xZsJv%t#WT2rPc##1>+=Y@%!~M0g=2;Xd^^mKTy)Y2C#I3405IEBf zTpS2&BD*CZEn4L(1Z`2|@<Cstm7oWeYndI+WPVrTrU-V>WCGKLt1r386MX;H$M+pt z1o)N{3ixg)IHP<d7#V=<X9FJm@t8$j;KYlAh~$=hj`Y${cl^P1@vNXmrcj|(#-H8l zV6M_gdoF4s3%$r52CKyeXQTm;uB3|e)0Rq|S#8CE)OjvIQcWPdAN2+gHd_z5SCJ~I zwnL<|2vwR6AoL8u4LBANm7-+GOt(RVxIr-rkW_)7t+wvwwbZ6Shy$}rC=vufe1Ce* z_oP76<=1nMO)WByh32a^=vqJMZ>T;CG*|mUf3iWcdto#FCOy>w8$=>E)prRZ1GAKH zxa4W{Q(oMk(&DyxadIM>oNu{}d!j+66`8&PHUO2>_e8_1X>mUyUkaATqz4^E0Xo>3 zQjRTriA^o5c^kCG2F>?_o}vJ$W|bfGO&he)4|>1`t@ML-+aTEtv&Gk>m-QDLL?SoU zE9pW1Nf2ekiZNiGRfnOsoVwEz7YSHUk(;l8M2U$KC@U9R<xv!+5*+m<QRANoH0yM> zNOx_|{~KI!vnSiiCV*5HSkxyAuu>6{5U*1Nx@r=EE~1l@-$`vQgOYM}uM&YtfD*aU zLi!ZRm>^q=#}X!iP+1QyCMtJZ^$mmy6VaC(F&5d-t-=$K`fWKeS>pD{^`V{^?>JJc zt?}E`r~;ELR(k@A)nA!P%non8OeDZEpjFCSe)Pkl$UttrZ0g1uWFX_K8~$v`YffSd ze?uwkzb7GDV6Z{i38pPL3vuqqmK=tg2W$VO=z!H-+OYjKr*48Ik!oZjTLJ{?W_jZE zsJ{sej0R$v*nNoWPLZ_FnY_ZW?uTO~R!iVd{lJ|PC~}q8Qj>4LpX63aV!+ZOTl}c` z5_Pf^xz7)rL|{^ZiaHNpZl`{dN!0B-X`Lkx<{5#-wn*e^fqfrT7H_5wi{eDRWC=`9 zNuuCITArPX*bxy?5Fk0#Oqv*ql=7(A&Pyhx@P65i3Qi$!aM>e$BHCsrqUJ7A>x4HJ z6YpsJDabFwQv?n-)>uQ7#w-Fs?vN4OJ+Ij{(|2F@q9#aGV4VC5QEuy1#*x<RQV>-m zR49Y*xgR2}5||v~{}L!XjMlGn%RqO)U{IGVlyQcOqU5svit4+Yv`AJrZc@X7g4O;f zNxf>nkkYhYqfr^`Z+F+Lh}KI|EzZ+>_<!WR3wWGWwKqPKPSO+xCe%@a=1715trYAH z+S=9*wF3cLPbdvmq=a5+r8m+9Q*AC~63H+P4T|T0pBK;`tw%gmPfw|cnv~E4=+RW7 zmSe<-IgnwZ28_07wg2C5?aTYVGn0~X@&A0!_dcC;zw6!Cwbx#I?X}ll8;cv(4u@U< zCwuUH2HVRFL;^_@%Kta)We6<z6>xJ26+C7_;d4y9!u2L3JZ6MllAl1ObT%rJ=X=hE zerrR&v!SCl<i`xGQk-r>+4WbVcR6MK)w@xfb@=&x#Xjoulx@VncpzL_KI4IW5N}aT z1x~6`K<-5b4rDPPD?AnjHvzKS16czI{mnV4HUn}6Mv(*A4oHUwaxWlbPIJfk8X)Wn zC)IvH*d-2xEno*bkmmtmM>`OzlG))7Bp0;7j&~sE073$AAd>+3JbH|fY<v3eLC;ZB z<veet#_vj$&WAE#wi~|};=4;lr+3!3R++tXrLoid;8S_cx7i_Ibvs-m$$t8(wKlYT zrGZth)1fV!uv;zhwrnzKH*Z;8y=qg11Ng+6b!#e?Z{7@FPHKyO)ut7z383#P*+qex zQMOdpu3iqSfi$4Heq6=lHeiJ})|$%Y8`5F$4z@8p8dFwPF2{ei0NEekf?f6XTaAv> zySL*)9`&2t*vimvVlQWMF(37J%H+v%f84BDvt|g`b&K503w)pxKAHN>W9KTr{bKBn z7?bgPJM^2XNvwl<it%3${tTS2-Z_m?znRT{)G*??AFBry?MS4k{a7JZXKKA%EH71A z*UP=x*UM#;FcPK8*??Nfw9h(F33UaQ>P7r3(|qWWZiM4y5rG$Hz!w10*+7nue_-2q z;f%|d<S{gl2_iFFf)Xfmmv&%Qd*vtPa}hO#w!8zJLBR8@Rik{>HNbhV(ZqFX#f)sN z_>x?qYgIOr`iPbw<&wncYeweHnIHf8Uaw|Z;yN|sM_V(#B)h=8SQ+B0iTR&e&B&NW zTeB>2otp8Ztr=gEm+6`jXSn*|l#zzXXw+5964$93KiaDCCHXl_Yl0`~PN?$Q6oq6^ z_K38G3`SkKEODL6@uRIAUy^@D6;0*fLK>I1wae?M#{s9a(e`PUxK7>p(bkPG$s2Xu zNNrC7pej1*nvr#lwq{x4IyK`*TQj~S+dy+JterBojXQ;^Wr^!ljUR2*_>#QPQ#D-K zck1K@fojx<%M#bA9Y5OI@g;c|*ocETsog0f4|3Y5tCuCNQ$2pP)#FQ&?&wYRs0juq zIn}XB#l~o<E=ydec3MNCW-GoVug$F8DI<^eSfj08mbgy!_|YPdFG=3=LF7ed@<JFH zu<^qaPm!HKoE~-kvcz@j$B(vtd`X_J>!+rOz=*7_N*a-6%7<V->bhl#>(q@OZQb~i zsvN0IEEq;zW|p{4nfz$W<cm=Xu;f;XM+|72nX0*bp*l!&W!cF{e3sGdcCWFd$-H^N zon>6K(NdT-MCoa$?eSTo#%gFbtWo3RqW06GR#RJ@Tm4l~H$t^29D1sy03suL6pT9G z*s-ynT<N}AvQu`affRLC$n{F&A=m3{@9QM*D|9;U$gI1QwiRzikvEoSLxFbI(}I<v z9vc<&*i}$Y+{wLbevT}F8bmzjMIs1=1-1~_!O1CnEy2Z<R<U{>69byoZtPxB%(lB% ze969hHG(vbJBmfFjle$V092jOFsh?$bVO=PFCC$DsxqtOSQUv%)1o4(#ih3Eoby#| z7A{pht?qJZ3)0YKzG@ffZt+5s2AW(w%cQlqv*9>W0D(-^*}xEAv_gtGQDZxpyh~z0 zX$MtPU9qU|@^PDEJ$9G?gi%_Z+Qtn6=$s3sX{xG?tImVQ42vbh3~Qk*Oj`NI%(PV+ ztKod)o6^pDu6y_d#zaO{Cky|_S#TPVKm*P8q{bXxJ;a*G3McdkwsP4`P{DC)A8PHf z`zf)2swgJUI#7SEM3y@7-jw{%BRJU`9RI-4*W%rvt?y*yH7IAu(+cAXYSe|XjkMfa z9L!pnJ1-PAL2y+f=%3(kn{}Y>EEN_<C#+5CJc%L^acdf*gd|FdiGoiUT?+Af(5Yo2 zHf|~4vb#f%w9~ZDJsBc%;Tf$`V<MFU)TCUXQ=%2wQi66~r%XC9MadXIPh`)6>?Cp0 z!s*)+S-@y+ge)fG`@zU`Ig|-&3TM9|w6k3dGe#~T4H^0I)*xDM%?P&-F2zU_^5_C7 zf#=x8d>HTiz|C(2Rai#1^*zyW2ykr}6$wKvfKdgzCu~v7qX9zxLD~B7w=}h30svz% z_8%9SbN4Xt>6hEN`#XIY98mqrVc~goFVSd(rSqng!TO)1F16WVv=6dAsZdKfM#Z3* ze|V$6sK-(AA(2dBwS_bK^h;fbqmzAsx~aNdZvF!w8KU7pbBj2tB#P5qU8wSjj7Ea; z#l1o=9Wd2!M?$LFTW_x(viO66g~{8=kCs<;(1i>t<1uD7JKDQ#w5bI<u#f3eK9L3O zVYwrb_}3mscmqAS6(Ggp?aSGrQMb&=4N8;ncOGjEKNm-~y*F0dGq1idR@?2X8IQlG z)x%BkA@LM`=rqSuIL-MLg0i>mh_CuG<m9VbVlaL36e-lA50_DmF1G25)5wBUUo2@2 zC@O1z0VlFSYHVB8`}Lvht)tACuCJm4$*)T59G46@*UzKfThXL$U)843RWfxoCR2MV zRI->I?dAyS#?Jlh=%LZJ)|(9|{t&KS#5e&Q{<lN-ES7P@(W2<#W$b7sA4C&~f@*YP z&%A1MVz;jv#DB;)+G5Qp!_J<I+GWV~8b1dXttY2-lSq!0n~HAx24=_-Ye7Zs19PuX z%Obl|tF}$6K5S|NsR2eF`7Mi%`MWNiNw&pKu~N!q(rX?W$;%bmQu9q#ml@UpODtG# zk3Nr1EnpXr2n_q{jB-)}Ut*1Y$)Dp-B6@6g^mx@xiSpxAU5&A;PPkG}EIJl{y8VSQ ze*DRymGm%uam4cz*9PLHfyA}>@lu3?@zP-8+Jbm#LE_qQyfmD+wlH2=n7FnmURp%= z=J24DEpK{-Ld_^-Vg}7gm1d76tj@&BmXnBu$|cbJ!dUb;=AL81kmX>~?C7!3&O#6# z3qv(p*?sP<IYA8_9(M{dP@^i;NW0u>=Cq-9JL3l&^!U5P%4blyX>Tn+0<+U7gjNb> zLOF@N^0v06{&p#x4jtfcCO~<M-^JxExSqr8s;(ql5EE}*Dm(+P#6Y~2`?m6xB0{|N z7V}yxudDEy>_%nv?W#(C4U9&9%`$}`OIV{x#_qPUs6-jsHjGBeg=K0})zvCBw+z?+ z3{gA9S_*YSy)o^lFE(U4u?<6PHc7|s6pJOk=&r*2gPvh!c<o)&BlseaFd%9*pmg%_ z))GL#a1!FTE8aRmy>>z?AZZ3VBXevhqC*LKErLe^ELZWp)Ii~CuY3SvWBl-Atjf_> z;^5bVVu?JCMMU5te2@X0SP*n|irX=j91WVx5T0bN)|^NK?5a|=CcwE>Ep|O?jL-73 znT@m0^n%tAq|8idN+IS{<S&IgG=%$#d^C|KFTgtsciAKi5Dz>I0yLsQ2t!mv80u1l z1rnJfqAY+B7ZBno#(+kCB7#F0K>5rOQ9jd!@=|7_J88~{DdxCRfTocmqDOZt+lA2$ zlS?f1xOx_ygI3+R7j&r$DD;B@nL<0+zgtq5=eLw1*Wq~JZsY`-mP<J5f2sz#rdb>g z3GIAF?7ji%2PQ!T)1>?OA~~~rWNF*Kp9Y-tN`wDGAAuKLa=5sdR(Kef;#0$%^&$lv z7LD&k9tc#PDOW<uO}3XP(&I8iTc$F&+fH5N8JJhVcu=!u;sa|X*O|*5nV@X~;wBW* zdSka{#=g&hLi;}ZQ^beWuJ=e2%57jAOjJrD_t%Zx&+Q&#Gl_zTfI!KKpGqy|Y>=2y zq?M0VCriMp6SRkOO+jDsYUV6qe{!~j#q{KF$}W_s_>!^LCBHTMg~qgVz7(jMkZ!jj zpQPqe7vR~23sw44gwc$nK)$klVeOSln7pm9z0P8lQ?Y=lm{WXOdo|*+6_0_0wx}7l zk?oeFXU=j+!7zeNS4)LcR=k)ZHTQ@p>>k!yI%&WU9m0LGlQi61ruJBAETgnU<P3?$ zVfyt0S@FCTUJK#iPt$Lq;EwdmPLh{VR5Cm9gM0)*EX6iP4T!2491L=0?5tFUyCXQ| zDpIM&>K-jBGTl;?x*|2*iOS0qoXAyBc(FH8arUb_S*yG|z3Wsq{p4FQ5~tIWtW1ff zcq_5OQhdLGMT`^|c<7&WDF1mCIv6<v;|QF=4vm4LloD~YI3uz%$@_r#g#?n;B3~#T z>oL<QlPxTCdEJj*`mz@~MPgEobA%F)3E<dz_8qXNY}Tr&0X!(E^vj76ia!>&G7L4k zQaDRu1_J#KF$_eg78cnM#dB80$wSdFr8d#7K=)Lbg1QCs7-7f=fTx}~1N$fZ{U?O& z1Na@ruMfXJ;P-d@vYixv1?kVlFYKeY4i!q+(4#hVAqKDddYuim+0dX3Ed$e1@!~f0 znhkMdOnv>F4gJW5F2i1<`pR8L6*^=?$86{}?3${t|7t_ypd?qY%WY`44fWViX}*b9 zWkdZoRDkU}mCI5aYP6wm1BdLJJm2F8QOlC&dj=t%5zF)a4??ux%JU@=q8NjFMiFAa z=K02=UqH7A@jgnpB|+%FFv+UY{$N8>u@#|U)i!j%h6+%W`a0i+o<U}ut@C_8v!P$x z5I*RyoTyYhG*^d$Hbgp9U)e$xVhdG>ZBU`?)^1{jvl5Z)gDT78_rVGaRFv&`AAk=7 zyNm-8!E+UU=KyC8q#Tf6ATtND29Pu0g1~`P12O^bDjY})AkV^3&w+di5Hb@7@(3Vg zDh}iafRMR35DxGJa7jouTQXV*O-3B&d8=&6+%`e%xiS7?{AJI1pQL`p;K*uk#?Qk# z4*$bI#WT*;BML&P$vg@|Up?kg5Lpvwkb*4Bh*OyX`HY1ShrF57Wx}N#q^w-Nex(Yn zTDM{K#x3hMZG`2TIIfXTuo2scGqA32^ir@6?o{9&r+Txm;*RC(*Wrll>WVEZE7xs? zbzJ4@Rn;q<*Db5puf|1^VuQAMIjz%RHijZL)AbDv({w)cWw5+*H7~ssPcMi;e=l+@ zm*u$KwrHmc)@6ZuH@*on8@X|LRdpq*yr~lQbW?TnJiuw$fUw6CM0jGF`y@R;b4R2H zXs)~T0FmXYb$3)$ZmQn6ssinTzl2R2mTz8X4C$O37Duq8n>qdRV)ZyDg?rJnYSiau zoD>e@+g?LV`v14Xk}jKtT$iOkRj{>?qg12L|K_AVhE*NoDUSz`6C*tI=$6rA8^lGL zuO7E4U}mNpfhyX$V_U2X2^ne#>Y_Ne^TR59a7%kUY+MCdi^XCC!I9MvnS)$%iI4%C zWV()6lEFs(PQcSw97>(94zN-Xa6~RCMAD5%wMI(EL#QfUHt8T6w^x+nVfMP;HJ{W- zU@P1YXT%NoDPGDi6Y<;Sfn15VUJG%bH)p0}{G=oA^H%X&=DJj8t@!O(-h%MPHfQjz z<tsK-g5k&*T?L0wm78d|>bPdQy?Xh^D&zLa1sA_3${Um`PmDz2Poo}*Gp0{-d2iw@ z0J~Ald=n&;Y14WL%_4>$$9&0$ivG7_B)aD>DF{g2jlwq5d>#)akBIM<tTwH?b=@SQ z#G4$!^)+&PWTAFs$}={roZW250~|cEZkI)utgc+^_*iXFwnc(2Pn^Y0qM%VSxmW(d zSX<a`2^i0@gQB&>x)MuuSwkQbta%6HIY#?ggJEcs6>u{GyR_k!(y5P~+myO4-gWfl z_P^xDyV{SPI}z$ss9|^vs2&IKhe+MqYllwM!!CgD49JS2fKYr?P!>qlDR-)<k6sfG zmdXBg?eM(1JLQvR2o5*E0t{!)u>>XaSqsL-^HJhW7`ZvzK{aV7v@|yIdQW6j(XQK6 z<Q1+XS#oS1<;d9-Ac!Y?M=IofQb=aZ%o2RIgxY*TR6Y?EtIc0UeB`fEFH`Ez+}{kF zfCrA&;*@@V>WuyTa7i>D#&>9RG~Zmqs4B_IVQKhvXvO2aDo`qdXzoMF7vrM~g;^X1 z2k9^v*Yc1(lVK>AHFiT=lZ3Goz#0A_l&;Q~?`w0;lNZ$eFVB<Ljl)fpV30n?$N}Q9 zCTzE21zrUb<Ke&==tY&`{uF!YVP$PB)zOZdU2{*Ytp;@rUJ1F4b)^{tQ)wDLN9_&d zHls8~yG2J^<c;<!Isz$TZM@tk_zuYI%qs`~yYl*P6)AUz&MTs$o$HPEb=#?fYLYv} z0gZYjB1<q1WwXJYEuzyV)QKG2!535_^OqpL)qf+T_1_3_MJVkcQ8Dd@nO4#EGitk@ zgz{a~*NpHPn7J{?_1XM`xUJIy9z<dQ;38bd*rNcsdVq%NaPb{P1@5WvkrxUOeDHEm zd^V{NS05@wH4Q@vIgcnNM_S<^NuIS4dl16D3JN$v7#?{c$LAw2eG=3!{m#olIqS>R z2OC}nScOuI0N9#NIkxN9;oma?!^91!#0V_HcX4IVvE0yIcQa+6iEEA%eZ(v=tdk@V zOg7>V;pj92--2LjQyTDQ0&gUc9jkM=hQKI+ob@ztI)Rf3M8E*CNb@;bJuENz0qoOK zh%?54;Wt2s^zl)KJP*n_R_1=NVTcvLtOGo3Km{=@VaS?W2wWt94I5cHE3!~y1{5u7 z`Jqm70VJY-ZW|KuQea%ItFIy~3XH3DU$RKb(&g*SHF{l{FE+_fDRW{ivLx2F)YS3n zH<3*)2ZqUL{u|0D`F`|rIwmt4Yw?#)Iu%?6ZD>SHW;E6&$%->uh={1FLllfS^E6h~ zaHO6cDWl|kq?5*yvUJbt;o$1o51A%rttCEYC7X=KE;13{KsKJne(cR;#KH}UD6Tvb z)<$Yrl0E(UShGm~znVl;FXp<jrU-;sv*bGp+SfJ_`LZRPueNm)5gNN{%c(bR6>6qX z+M6ZJHabm!AQ;iZyj*B<icHUJGt&5Mxc=)7t3O?cFO$`yY{Ou&-vH)KIf$a0GRJ`T zL1kQ4j_n#TMl3Ia0Y@yF&wT?eMiyu@lGF#ixg9@++-Rtgh#sFE4Q_i3xtM7PvqqqP zsZ=Q#x8;c5p!|4Z(Q#!zVUxbZ3OGi8%2YzP1Wi&6K$D~abbwEJ5jzT80Tf`W<Wfuo zAT@~G0`Pc|jv>jIy3J&*A`7LvPJOP#LT8NHj}!kE)#E@7oQ_lH<4d3pejkUF_aBPv z!*m`tgh`X`C$P_if*bC~K!plL8Ki#^FX-^v#rv5D<mb~uiy+0TjbeV3x&{)gexiW@ zxA_VhFZUd%MpKMAfwdG2zFCGXOt#HI8t}$QiH&gXPf=iFV?s@5D*lp?28EjFe48l6 ze=d{`De-uaveG$_Batg2g*hbgzf3_)L^%fk3(Lj-T7;2QS3?lhsdUyh)x5M+YM@K1 zvqqnaxnkwW8uH5E5o&14!nO-*>aO9xIjKwyb^C%Qm4>@hZL+B>6{?!JQTC#ZB+?EF zmd!`}pOLkz@!`>@ay9;WY&h%2qm9x%y4o3K@w-OZZj~xYEjp{ElW=8$DDJkr+o{#b zy5E~9;Z!O;k-;r{#GB{_Cy}m>Y`~{u&70^vo9HC{;mVl}9H~FNId{VQ2N26kBuKhs z%^bE2Q*xbv-!%N@;>V)-<Bk&+^s=k=0sKD)zZv)u&eKlrw6pLY#cw8l{LaNV)kxUR zk$gXj|I6_EAb!+&@!N&?G5!MlsF}PKKMFpq&jt9gtO$PB;kOpQ+wr>zzbW`d@cRgU z%$Mo5;#Y&;YWzy^W4sFdR^Yc8zg74#-b(yfH`amWydOWp+&_M!LW{eJ^V7aD9@FvL z%q-vdoq1+U=Pu*_&+w%n)^;$v=kWVIeq-=nh+id$;wSij(pc;QV(jEW`7wt?;`8VF z<zbkQKW9uJH*akIxYLj%$3JFFPGC%KE?0;CTKVb=oRvHNyeU`az2my&W6!@mJazlM z59MDl|2ysPTzq(_WZH_#JI9^&uCoizdH01ET|8mp`=(!h#f+J=KJcLh(W|a5z2@5M zmz3Y|(WM`|apkJjYi?b;?h{+8s_*z@&DKxF8k;_IS9ARC&wlQ{&;QH6-v7WCzWTLY z|MvB7?EdDzfA>H4{@1=o_kZuP15b23d9btVsi%MXY|qbr{);2e{m*Y+_-+6HzWCBW z^5s`v{p0YT{(SV<Yk#3G+dNrYloJSy35;cn{5hXgF<gJq{IdeN=S|5Qf8}*!m*4)5 z^QUgl56{2%q3^VhyI|Vlp?9vRJbc=_N~Ztj!dKWtY2oM^c;_2xp>3e6eXiPO1hefl zd;zg8#4qf-5!~uLgz|iM*%0q~SFofF-J4_XmH9407fad;$C%L72&okRY(pDu=nfnD zoDKaS8+y!!4%pE5L8mIePEe={QL&-+`2#jYIa|RfXR8qTt_pF=S0O3_RH(>?ifyRG zh9=n1Bpb@ME2DM|Rssvoqjuvk9(>q&^*eBPh&boNNg0jBLk>ypg9G6jZYCg-55M|0 z;rE~i(hNurYUsrIG9bn)4L;!6gWsoh9932uATtnTDg`+V2$hsh=>vdJUFkp&-FJ%z zayB5;RXTCr4+s^M4x|(iswy4G3P2(r$fp6Jp3;fK{pw!;;<RfwAYTJDIFNQgDnJzu z<mZ4~iz$o{B*ybwKz4W_e*ok~59IHF+=+=z<&(Ml^)x09&JE7<R_%VdcWb2W{ufYf z&cWX2Y~Yv=nm?ko!TCCl4{cY$4|pJ%ci7OP$h8Nich6R>s_^WcZK+ySvA%l4x{b>> zVrO4};ypI}uySqXrYxWIuH2gC)$6M&Zdbl#^zIyX{43V4+qimpCG%RpuF7oN={MR1 z;QOtv=r-PR)XlYjCpB>1VOIcqgbLh(W2<}F^ed)gcVF(zNr%G}fYEVDjwd=c?j?FU z9J~BFcUPO4KWqX_{%K!eLZC}X`!LHXBkjvfSF<q*5`Sox2c0W7;f{zco0qSI>mvC; zjJknv41{KheFZHi@Iq4yWFui-5~N;LV&&T${?2dZl@+i6$wCrx*NyIZb&rmu&2|S4 zEjhB>|1g3*!_--qUtTPaW6!Vy(g&qaaDxnchGs}7d-0`=+cS7D_(ARem^*Oh=rW~X z{PEf|G;9MC!E%}Voz9V9@*u8VT{>^R@^}DD2PfliXN0PAL_&yir9^K4E3y`cb3{FX z^W!CnWd$jm$t#e<3se`-*PblNaDfA@UAeXv+X=Nj!0cjA_-8K*Z+j}%QKD`{_$aSn zUdUSLD)%O*f)5~D>gB^~YxD@ex3U(AD6VmzuAn`*q#S3zV0?QOPC+AxJenB?yFj>i zbMf@VqTZI<^An2-660EmTBJ3Lg8pX`i+Xsw1ZozHSD}VCVgJjT%NEe~y2lUgfn4{1 zOTVY<cU;PiAOOHq&qPE3072AeGPa*K$R|WVC0Nl}2WoFLx3A71^nO$QQi();OmCvP zKe1pBYY;?@1Xo;ne-XL?-p0ygRA%+p|Byk008<ZlVbzPI<Q=B@MTzlfz1#rEdo*|l zhTVWgQLYB}+Ac5BS7aAa`KFo`YWg>!0eqSrEe^&06>qv#QaNl>^rbc@qPP_u9o{FW zVGnT6>oICC9yp4nUJtN^jql<(HoS@ixwPzWmo&fu(x_7p{q_>;LO&f2r{RRI%!!j& zv`<uG3NKB}v%EN7kSY|4nH>#pfmTRY_s)Gx)Uoj6H&=+Zj-(b5JgLz!XtAo)O*}=o z+z=nK3dJZscuBDzOMP(wg`>Zk4*PIbJB}=BIz-S-hd62tIvmVShkYZ{AqoL;q5q3# z9jLA`>e5wa3;7y;!;!M+(9t9DgRB$w^p~_k$2FK(hO<`p=Ob%pJXV3gaD@uSC#QAm z#loKBBW=>l|2Mrl)O*=>5JR6G52zN%PTq0O2SHWOK8^<fCa1O##1Vn(Sk%!Nl?1>! z7!ylOOC;&iIgITLh`~fP>Yh0usQB>K4D^G1ncUPuGGrN{rY{N2q8D7l0XILOD`qB^ z4aS}zZVD2M3h|$y#mfOa!vKYdu2aS|v4HXbKx<~g*}^s$cnw-cNs4fQnvbg1Yo?$x zK(!5p-YXOu(HE;b*U>8i1+nRra$7m5L8~##;2s?+Q8}N8!PRH9nCM2(tI}eEnl-Y8 zj>N;lg+kAg4^AtV_i4Jv&_v~pn-%WQ>Vx*;xB=sp$g9kM;OO6FSZbB2x3FHD9V&*f zy|a7(*OMa!SPK5L_6Z<lQex^Rkg&Cq86bj?5^WgrcUiw?z5XsRgdBb{m)GN*k_}5L zI8JC{hA2kGR<u2Y!`9or=uSBxhxPiYzg>v&LL3-O`f-m0w)4}@>K`IE?wIWDA|7NA zlN@_WWQ4iZSR_PHHx55Rnx1W-2V>VztIYl;EwKsgK}EZ;FU*I1Asm!|$^u0wLUryy zH^?S=k3{7cuBg)bdMI_PbbBCyj<!^Uj)r^olXmpb$vc{qun4|iv)%b~k4970yXd$M zq{b`kn*<)DLf?SE#Fb3g_XKo36ln5%&)d*38_K%URg&FGB(jftr8|b0&hiukb}N3) znv5fr7<3?4;f>gKARh<h2YR)~o`#kRzgtj4#>r+Aeit=Y64U&7t5#{uFQGG4t2AOW z^Z6GGVhuCbTi?WY_j=29>k!3exCE`)yd0LnD>hYCZQ4+=deyBE@$toT8<7{n`{P#E zzz%X{<!aht>vw&<C>99nTDgqN;@s7M5%T7(73zLD*d~8+3+<BCt1*YPHyDi|$4{R& zV@k0+jvW74sDqV3VWu3v8LMr|@iVD1#zRW{N6GOex=bk;f3zGg0*eU8n16W_nv!|t z#BE*Fa8FEn4)=s<A}8yDmgza6eRU@=)un>*p_a}5_-oDiv6R2*m-V<ThIgNUD+af9 z#5xL$WE2}zvm7T(ZhJ(BSsh^@v_zrTZO4`o*H%zAFOchl<iN4>Ev)LGt_BMx7BmMi zJrxRr6&FSNBzQ1U-cOMf2V5zaaKq1awVPtX?%Tu~^qy#$urgK4BQ$ZN7#$Q5QY`@x zhHQHzv~NMqO}uu1o-|yrH3B;XZw{!|p?I__5$%c}Xn*-!fA`^I@o0zo&_nSQ%Od1$ zre7l|fP(|W6w;T8A=oj6C!{@|{E?vSn4Oo13t+QPyD!k*Q<8TgT4z*Du}n8Yn!~2| zao3zidr|Zyuf?DP7U>Qi8-w2!f4sb(SJCW);b5<liiY8%V%rP0Zx3yIB(bQk<x0Ou z4INkv`MY5b+11Rc4c(6$Yz8o;&=19(m16RD$R9m!T?K%pK^Qz9+WCHu-J=&FOK?RW zR(qJzL_MT-ldXWMaioyr^iuqA=n<gYo0A8%VWKt%vzv6d(0_48Kcn)P#f3ai-bdfV zE}Oye3inW9*MQ=ku|s?p{*d)G&L%F3A8V=chxWZAwD0Mb>k$}_)4Kkr{jVPWOLIZ& z&;HmG@aLi3VhmK`jI+K}NMUcNzBSfSY=)P#qa>pp5byd&)s9yn|LS_7-!LGP_oJo= zX3$zjn*hKSL^dAVIhks7w38ZDU+PLrDxrO2Li@T~K7_UkbsY%p%OjN?Xt~OOy8Xx6 zkL7@lj*emb4*~_+rAdF~qd<X1i5@ld46^eE5D{1VdJQJ10NZkiLLKxbDvA`x!HNzS zLlTcI*Fuskh`k&zVkabaks$l@MLc5Ocw5_^T(C_JH5sr+&@5i}67BWk>~fKxGH?bC z;iUr~hkpjvNG`c(%RDbGC1#EXRv7OR40gx6fO-d+V-+&5HXsabFe;$9+d(jJj!q!X zoo>+C0mux&+fDD73=Iew_E1>?w%21aHs4$s8xYt4q92j<1LSV5(!nU`j;Or5Wj>e( zFmoi=*8prDE5Nl}?K!bd>GLNbiucC`V2&{$_k6zP?S-oy(@Ia1TdsyZx@vvP)wUU~ zR<~TGA^@Z2Gcf5S`jCOXiMGc!h_@?Ws3`z)c)Qzf5#edOfx3I#)pqmQ+javTx$RDO zC#t)f^Rf?UGF5saa}Vf9#(<WgNDmffDBsAT+;Z)4uDGaqqiztIw|KB*U_34{vLp8~ z(3_9KPJ7=t3}Fjz@5=*eYHptVF1Ukx+^V^j<JLPAPcmq|OmU`2N<v-Tn)91sCIb?f z9*7@lS*np}M_6c|8ijh?A{s^wG?N3=h{(#cn_~(hM^F3!eD0w^hcM8N5ZDv%68{-J z+YiVn`%3}urc}NhSss?HGRg*1pYj$RJLr#fq9)`WC#^s~7u2=_9N@Hz8g&I?8wUO2 z;-x_~PmMI)ILTWC6gDn1ZvYMRi2qoK38II4W|$)jYv?{4?c8MEro&if*}NIVkhyFI zW*{>T#9Kwd13d>~ksb!H7T%~rVvR3n=br@ti)g!0r^y7<yOiMR@^11=ioC>$3l_}K zP(lTgpZEfo?++aPtBA5gqVvR8cmR~I-&H!#zngjJI0`Zp%-KvFy$wL|(8Bw6F$$U? zOZGy&TRk6)7K)1M4+w%de?fnSRX-zlE@%yY6KhGl@jyWmMgt7%FD*GR_I^n~{r)!i zqN_ev=XJKaNytRZ#pIWyfCJX8B3=pdV(3V<=%-~W^%i>st4mXC%v0=tBR8u0dLdT! z(^+NphfyqEpiRNc2fby}7Fx$mEMkTHPsa|FHO~*~G!F2V=J|P!-R8worl+SemIk4n zOF%|$yuFVXDOE$-LP8wATx~PSyhQ0_`id@Kmex06-&7cvV1bC${2Uu9fYhsC@3W!n zZRjo=ddP-;Vnf-q8G3!Y62;hOG}?@d5F2U<keAtErUewofCIS(Z|njG!kv6ljssx| zl86JT2ZY`1Kw1GINjQ+*fRHvFNCzOV!d8cnY+8{`KpI!q&hu7kMSh{PMI}evmwv$( z%^Lm*Z*Jv-O9$fIa&I*umFsR@Tcs5tTX5aIJIXp7?{vSfsb0T+%eqgk_5jyTy<<u` zU}`2{%Z5#xs@8hoHoGj}lvc$lywcO8tojr=N^jID&N=Uy=B^a)jVaTnPll$2kE2iN zLpK%)E;IEh4}vDBPg$$G8l(?;_{Z#vF7c9=PwP`~@f|tBsUGX$+`~p9Emx*Gh`A#I zos4m@gKP`MvyTC|ZKv<V7((jK23f<atiYCTMMx_(u(E)P!kjnaDZ)rt9b`qANBkBm zLv+6J-Fhw(<%hQH<{oa4?gi0EdT>KnSRj4)B8<O)-z4FPgm01X0)|@)!5#Q&eUG;m z83^YzesVQFGJ%TxG+=9+Gz~?SBFoUeR^{e1*xbtb*DR`QSX}vF>_AB((2B`s71prs z)3s17@y1s!<oe7pOQ&qinK|wZD9a!k`Y68vg;0g$)Ds`{=~ph(A;pa0nOKXQg{XhR zoMm80pJh<!r%4tgDD*Fsa{%&1d${&=g<eTB^9J%iu~J8t)SM;BV4yssGG~bUs_+|? z&mjLIj42Rd{6$!z$X`$wv~ZNXYTm_~*W;5LZ#?X%Dtd)e(Lqy7FOe8*izEU!O=S>6 z#LenrvpxMGOvBK}g&qx-VV0Iqplr>ocKS_HrCfT_Qe<1Uwtrq7Gyqr{MTeJS;jmQU z7yDtMoyXM-4L?Sh7buI-87toWc$?#wlbawIz9CU_Yf?%h4bfUnKrdSm)%pTTY0#sZ zXoG9wt%DYpr=kJ&d%*NSliOYK0U5YQGiq`eK-7!Ay9@<cH~9F(5*5wMXBrE-Vj@U+ z6+FPj5gHm<1JIA+3AzT-kK*DD3PYwdZXD}C0>?4#S-aF6gpL35ARt%{&pNOrEb&E_ zBEjH*NjNp$1zV_Kk`Ea!s)!s57}BK+6VRp8G9<?liqRrA2^F?g22J7s4wK^pvGM_< z4!q&|?Sm8sr0@n*P$XM4rbJa%mI-*(b;pv(jQ>wC2`O?xLi{iv;yZCyoWM=si-YlJ zp}d7JRrMBn<Y~TE0<riQpuYLl#CQyMX_V-MwAF+`GxClv`21d_nA6y$C+cRBI0sc< z^2?~Byuo4RaB?%=^qNxFF&UBw>D4@h9bc?*hDB!#eQVDa3}6CNdGdcLw8wftB_9*q zpul|SWh>D~eHc_+i6Q<%Y=SQa*GnvD+KWX%Vo|?1l!aOji0^8DVJsJhP+ZAWlY$>c z_)&z$7wX~Omb)8}aePs4d`Z&|t9a|!_RH4P9FqXYdN7u8Q+fc_0H6}+)th$UKNPov z@R$9GC7<6BdUX7ncmY+GDjT)~Benj3vO2-*94t#hJ7-XLCrVBSm!X}%HUjs3#8f2E zQoh5jy@v-yI7C4$;F}xtjsR?ng8NZ*Uuv<^S8GzCE6BU*B)21-5usU_cVPVo)P?CK zd*}7)BQOap04{7(i1FLdLhMKRzU>_(MZG&Kz9EF7WL8*!FPW>yAtAz{cq>U4xasue zK!j!>5?H80avDqt3?NQkzM4-lJ2NjH;0f>(y3}wEqprtFY`DE|EN7`f*>pxH!tZSW z`_4gG$J7pwtNSHSLS(aqz6Nzyd!tvOnzTR*`#z3PSV9L7Qmd2SBgB2Ju<vY0DrZY* zGD3wCnv2jm61ou~E`7tkVhn1U)93l#XG5Prh?8E}mu+>Tq{Wr^?mX_*$w*ek6A=9Y z{QN%3+I+6YF9$Utggi%`lxXxosu>3;b>e&m5cZ4%p&`<XV+|zJM)u2Sj}R2%spqX+ zXHd4~gKCes&fsp4dQ97YW&jo&0~wINc_5j4w*34~^tgIV&X;CD=4U`|$bj7Df!u*N z_ew}iD%I7FD>toPvu54Ob*nd8>rdIEg_EtSbuf>GcPz)3me9)TjVm{;T8+CS=@AZI zwdi(>ww%g^R>itSA1de8u6Y;lS}X+{$D1}_iy6+gHdR+`uEtKXJ3mG-p0#*cWJZ0r zRBu+PGi-d*tXI>GR9yz7Uc*woVV&mHOK)T^2LjessK(;zO0^CX3*a=E_wLGyPkd^1 z<)(@?%PW1KSOtGuuvj)rF}t%p(n@UF)ak|Ya3zKzfu{p2o{?5!eHbI$YCMkdz=w;Y zw__!ydP0hjKa=qHag=!j3&aD(iLAu3dFC3>b(4rXp7Ha86RjL%^q^2y1!GgC)1l@? zhYQP0Sa^1kfE^(Qmz}qPu$`o2l_n{_sQ5hdo1Rp@m=mrL7Rc)JnM7E=%EqvK#>F1C zh<6Y{D4*pYgl|;*=ZfNpQ~I2)nJGlH<J?fv8Q^TDuA$)_&bSjp){QAy%rVLY9Yu=7 z`=s<FKgSox2bs49p2~>BG9wzh;*AUv0Wwm;JLxaM9Xz4NF903%w%l|H*gMOOOaMfM zCNY$}SbcD0fBGv$G&29>LPWtUK604}0eq`^qhCVof?<IOhqeByGilw)M4UrXWljbu zS-x_;T%}5tj)!w)mBCM~Hz%d~gEVu(N{Y4?t;r{KVp+X$%YGO|sI?rtTo524gM7~2 z!^i+(c{c2?#+@5OIlQi6a7;BjUAHV%jjxO4>(%mgzWFLm_$c%D#o7u*$v;2|38XWC zJ5?yL<wy|XC7ek~M6~aA+dkD4;x@A>QUM+i@+BWp2^`ekfsc6epf<-rEk~I_E!oPO zMlCaG)gJIBFn3isDF+ui5mNwaC_}9PDlg8InBZ<;ayno-0M8BlYUxb;e=rhZ+JliR zRTc|$W(Oms9CZgH*Qo!lRi76UDFUY)iNcro6zz3JNIL4%QuW`*)PFa+mQB`gad>VQ zVpm;(JRqaaeTpykE-eaV{J@?uN}IGLm9T>w5Ko9~nAANE4$7;}XlhH%#aL&*rlxx> z3op0Xy1U#AWh7$TfIaK<PxA~_$4gmZC^tfP-NSqWP57%k&m<ZiBo46ra;;m_G+Dq@ zn2ls!D!Q!$S|CG!ev?_5|ACPLf=KW*5~LQUt~KqsGJEROsUXfYP6_4Vxt%DZPWjpn z&;2Q1P9-<p7}!^y<>GdxN8gHkJ?_0hDgTWsihhBG{1`9QHvlWBKm_Y0Y@R?dRl~oL zTd>r(dsR_HB-FDkN99{HIu2+j7E;$qCgHh1VG)WWsSg}9ESs^aE|E-;=5<LnRh$w( z=4qXeO`fr8Nhg!Cd?hx)!w*YAP>5pLZWN++7L+9$mORVB9$N8x62Bjh@Ox3iv#pN4 zg;uCl4{@nhJ!L?OTJ><9q*gsO2&q*My;-Xj&PB*qg+7iD*I0QzdWKdoyvoWUPo#UU zA@Y0_P*jM2Rj9y*!ZyUx)mO@6DpYJkB{oFPr@m%cZ5fHsv)mfZYKwb8l<B!jB1`c3 z=HXZ8fspmwt08LrwHc7Rz?Gd;cLFj4m~kNg0!SFftPX_uU+;l*0McP0nJcni1H)Pv zL`*(!)r!n5d!iv&kxjy1N}S$@axfn>E$ga{t1YYxB#zJG@Fo53Kwis$oC%Do$6)@V z49Ij3B!ah$)ys#(Xm-JBvj(y!?ZlmE#j5lV;&apk)~g&BjBB$E=FA@Q$B9M!!P-e3 z)`NlWmg)_wE7on4#g;6CBrFTD_3)3ZBd%S(rD6k)|E=4+p8k~O0H4IxzK~s&^R!FW zPeAOZ%B>X}ZncXfy&z(Rq%IQ86uc4MQ`d8SXHFV=+^LGARarOT#OG+&O;e}NnpG^1 zvx9XSD3YT;+sVF}pya*$ggAWId2h$MDcgEz5M{d`yMv{~@9<pCuhkTWj0}LFnn~n7 zQm}P$e_^#r5+XhW_6%Ucn!|G$3^g$u0u1vmXvED=yG$(JU{N6Pl|W;6ozi9kB?kDa zPm}2&xew_f7UWdt@Ms5S9OknKkTF%b$P5y*2m`pFqAU<Q;HQ%$<#h>=NdBw?+s@ZT zQi8<3A-WfT$Raz$ucl{N!?o@X4Df$0P5uZTK5+ZA6hNBat1#%c)CbWS3eHFi4h+ zCb1Yb+giB?pw37Q5@@_KqPlL1XiaWcA5_CHV|uxbBeA$fdxX-jw<ujEzq+$dL~6|X zL#tPTf!G1@+KHUG$>mttI`b=%L7QskWa7moPlWEgO=B#J;BeCBsgIy79772;6{}qE zdL`!hvHLmo;$j+#Hk|6`hxP1k7w?oeMDRH^+02oIDy^VW$3oC5%Bx&}qbhbSCzO|x zMhm%}BGz5vvc5hA60sZ)ZNb7!%+q;H%p)eVio;atW1Yfa8u1V_r$&XY?y8jfb5;Ag zjZS`BqvegXo9Ju}EB=sZ;Eu1D;$rbz09tZ{P)aol^&UbMFI>3yi+UFqb{xqt%tRv~ zs*vA69b{B?HFb((muH*lFPYm+9!5qfR0@x5!$F7IaZ=Vcxc6lMwH1zmFxh85_7T#S z0GXmXsZcZ6V&;)$7@@biQH_)lv-$E?irLuM(F`xqK!%6wgT)h^Vcu*rPYJu3KjiFe zvNPpjb^dAu>67#78<IX(qbDdMD#>yd%K0`pt0Ogj1dy-dqXSt1$TJYy97r`F6;}Gp zlp|+|jEPA0d8_0|W*FDGqDon1M1H<c$MK=1IX;d+kHkZt@5+qGB@=0Nw!TL~-Buyu zlZa%Lr|5*jBd1j^|6~O&vskXBAeE)jin~QG4f9k&iCs$l?yAOVkejHpHe%E3TK-%y zYpTnyJh9^QeE|%CE3RyE(<*$R+%$<y0}rL}f0W!*sLPas@h44gg0c;)4Hggqu&7HV z1m2}qs91yBH8&TI9BiI7H<PJ5e`OZO-Na(KI~+ev8Kesfk+Tg&OuJ_{Mndx6nA24S z*uIZnRe%6)){K+D7(>n`Q^EOt0DLLfU<f-}h-oeU#P+|Q0I-_#0C^O*VGXb<)%45i zvES$S)S3K#{|*!-nIx{t$3?5-j>NCZXQ{BG(WP!ZTi$Igs>gO-74$W*azPvxxGISM z=hDf1=$=KKR_O;F`qyLeW3j&aL{SsU@-;7esr|3{&C6cIUqqjHBz`~=7R1}1eE}uU z>fUxW{+rk>r3YsnsXZrtbC5YS!0vfp^u;ONRO<zs3sSduKD=Z<Y(I)B@j!~MP5lgn zkWrD7OGf0d^UWzCjzcPAV;x1tvRLm5H?UKTE_WBgN-`6{_H_tAiwMbnRFJ(#MSLkt z7`QEfcj#5ABm)bE(JuOyRs|q3QS&xX1%a1qx#>@LoY<TXA}OV~{0LH+?F2`d*Zah< z{FMQRBvK$!7=Tk^wdlaa?*$4W5j&N^)w{MbRgh~@KIUDNUu4P$-_0oBZqZYf&n^0$ zrwLKedn~Gp_9GOup#og0CepaR;~nNq)AfW}D&;Gz_e=OVl7Z;4nbD8K@~H6X0JGsY z$VNYbGRpepR6kyEm*WX6El?9*ax>-<WB<PhVN}E#`^%X`ZFo)dhcdQM8()|A>du0| zA(krriSi@t1o4;9ul!^ra8!||7i1AGQ=f|<9-x}<VuhKH?StgkP$$HVwfSAqkBZ;K z9-uG>=I?Oq@6GLqd>)9^4*05IJ#FfNs>a$-wc%9h;nf4%@wEY?UnB`ift(SEy=<Z{ zGJ%Z`dO#UN$c~EYKzA!{LAhwfSGkzt(SzJJWC?-hbZoLbM%;1`$8iJ_Mj&<X$Y?88 zawZ|gnh_G#g7fvn+Q!&}_!?t$Q=7uLz&C%kZ`keNZc23cOy0WVuJ$)^$_vy21Dk<y z(ZR#Dg1G8mDtkU$Q$FVCGK@Y|4DDU8d!h_2G>9dqeaCnZ*9y{Ju^-fiFhZUSgyL~% z?ID~%bQUvk8?r#YkBXG(tb(PUjFh<@Avw}YOvo07cIK=AH_o6vHAYorCbT5<OBzKp zMG5*Ypl#3z<-&otFsBjQZMZ%Y*vn1+4>Cx71PC5OiytYr3LyIXctbR{ll4od{J$NO z(jGv|bN7>?#6(oa_6zs~lQ1<X-2MBMe2`uZHBxHucK^Rime^#(7mN$(NsQYf8Mljs zlDl&I@KF;a8nkWf{vQB<Md@?+Psa0`A5&jJR+(ceD7C|w8j*Zo#<8Z?Ctrt$^}z^C zG%iA-IKGWh`P=vro<{6dce{9dFVyloL#DBg1<IXLwyDlxx;U)jX1c#Y6F@|UQpvgT zs=5!81t$*<78i%Wex;rYQ&)3;(&^;C{aG4Ixys9wF?Cs2AVFN2K0fp)e6b9Sds+q_ zZ~sOyYTymmf6EzkvB6ULq{bY2AJLqa40u~|wzPymnLcoXGFpOH_w+%hi;{~PbNvuT zIOh5UX2)Ewyk<1}hyy0GRs7P}IdU|Cg{3Ya{|Sq@1Y%Xa*%x{g#<U=VoYZONI92^G zBsVj#SIKz7E->bokp?zS><{ub829kG8@HCq0KZ%^a0rP-D}P-eDLIhV9eUKqO57;p zHpkU(QI*LYo_v=$5Edm!=GcSe_^%sXMGnpweCOdc4NiUygCqH6{E?j9b75*&ss|xt zAe|$0t4|PJa41gazYO`>7v5u($*n}5dHor2z&#_Tij%*AY?03SLM?X#Iuf-hnsBJC z&r5CJ1kPyGsi_Tij-WQYx~XjkL^tVWMQyO?90HPj$z=j}`brBfLSHVyrH5(Dd#se% z4J?WHa!9O64_WaA7Nk9~_WTjVms|hHKA`y0C%L_j_%ajGVc@DbbytS?a*llNkJa{z z_<}5@9v<<fAD4BXiuls!?yTeuP!43W+J|11Rw(i1&`HIYeu^(?ZPnrn6OJst^izE4 za*HoI7bCvln<KuszG0+;E27dF;>*jg$N+%&(myUbaJW_wNAZQ{hr2|40b5g9Y4Jrl zC(<A#zL@W{m*&{RSQJ`-d~gUR%NY8;<g3B}5EK2OImqD2M1wL_v)tq}NN7X@`Z55I zZ3YO;X0)P#xBvb{Dv~A?zkn42cn?H_zH~;=iBFc`+`R2S<bz-=n`p38vNWOrSh;CC zMS6+`hx}=x!Qcp@!JDJkuLJv;^!mylG`)^U8b6U3lx$B%8qQ>bbNS*9gt7oAfcLz4 zyDQXk9I}Ara%w(>AbT%_Q8$+pfln`lJuBBx<2`16)T>vySh|688#?cUsY+6ZzL6Vb z!bsSzxycJ9@jx?ql~B?IywJcUM{UmNg|IqvATKgkBQMUjvko906&mU_cI=G03ciuu zQ&}ltE<-*m)26zZ!zTclevSDaW7Hr&eH2Q?5RqS<hjM9k%%WbosdFvrq3aRDEYxKO zi!L8%e*w;Q<kYWjI#7ZoK|jwZZJllEd52R^NFaq8Tm-1De&*H%;|MM(;WtV|XKs*r z{4pkn=BN}7Zq&TQ=D|9?>H{Y$m*$8DC)tX1mjbq;(H@5bk8~mP6OeZljUy8KRCEH* z$buaZ)&^6IY}6RV2S|r*$!fsOlY695e4P)?ZSo;`)s03v9rbrmEhH&9sg#exQXoZR zK&wmP^(wZoDYnAMp+sIt1hHfD>IEqdnJR*$!a<P|+`^xvFIdY9q1rH%(T7Z#(xvX9 zeCTLc&X=;)fC)A66p;WFPC`v>0)Q(`>K%CKss?+YM?!TMF%~I%s;y2r&Ne8Dac>6> zYE1>EShN(vH8xEG>cm?FNvS$Q6-nf3dzQ}Gj>Ot=Sm|ysL09ULl}(5QWfu0-jBZH* ztfYW4ZF{}SCWyPvr%>Y~x-FUrLXAo)MC-YfedDbcWmS6Xjp8a>>x<m2KSfp7nL^f} z1Fh;pNUORKN>_Ek(ATKCCb+xi@hn}V848^i>=vqm(}L5%*vxVYB!LwGdID}*TXo?I zP-FlcgxEq4)j>EMIM~2d_U-O2sH!rRAX{8q0;Xkli@j*V?KZ;KKuyBnz<VG`AUj{` zB2yoTQP3^~aPp|r#}$t*d|us4X6lKQ@MP6bo_ZqN9#a%0(zYzm_XwtycOew^y&vpA zg=%q}<qY|{!-jU-(4#gqHEiM)Av2Zs-S|(1zJ&i&E?M`nWS?UtN^u^y71c=o0jd7~ z1b)sw*2Q=;KJC#2JTvhlb93S>0)+g{fvf<8T+M;h07Ay*K<)vA{KkP$*FmyyAQUlH zqNRjnJ5kRa7w)n+&s(`4CL0UL8mpEO_rnf4MWcq9R#ea8yL)r$RyYV>v6YrZ>ih)v zr&ft;!A&c9@&m^|s+RLIW%Z$A!^-N41s{UD@X9S-;FZ~ch*`NBMoGxfl^#(uq3+`+ z*kzMFGk2A;95CVM<tyNkkUMj$wp5vowAVYgF=fV-j1%;mfmBj*wtch(pe|~}f<!!? z)A0L8?V}x2L@wp>Pu6`jXCm?yWSfYF<MT0{;Z!sKa>;_jqya{iy#aH1nlLO(565v< zRiYkeR2*BCsQbyb^j?Buu!uU$Tq7lPay0}VWp+)dLdJRN>Wi5rr&f+p9WEsHB<!AB zQ8$PM$g7xKQmI7j89w<?1St<9DWVjaV)(`b9#|}@^n)}4Q4Li2>HR@QDwf!g!%EyO zK?8w2)96UxF=9n|cRZ#Wn-(Pu-++n5@N!Ko(X94(;@T2{1tuPjKOHx=#u`VMD@DLC zk^%)#v&rO03b5A^d4n|}PVJP3Q1CiI7(xX+d??Kb;=Hlx3-KxsQKrD>Y?~Hl!!aA0 zUY!Y}t<FMIiQqO9Dp=>Pj?}9_<R(;A%h^!sq1#~dQ6LTkmUae8h~NQaA<C;D(HP`v z3dd5V8zboFQ=prIPPuZ*$wazY`OrtM1v+b?Euk<_1P^&;wZ)*TYD{;S9XbDEy2Fnm z9p}!l?|%^@Z_o4bmJk(s&4#Klvnp7f4K>@)0fbb}S!YMZy;dS~`;2CGJP)ziFV6fl z18*c32g2{{F$c05ZzLoK@<~A0mky)_kOwscRmVg5F&{z3$u{#{2|mS<?L2Sg%=c}b z?MO2p4ma{13LT3z%sj1Gv8Hl0+pZqQ*^i)mE|fWE6(&5~XQEC>D$9-48!&shpsP1* zuG(shNLEy@Swrhs1%z>J`Uf1kOiM2i#`NSWRwS_YS+_~x+S0^Cb5~U6yDgO~M?K%+ zB9dZx9DCc#fJ)MJw)w6f{ml99VLkUyu6r}{ot`#Xn0*?2liRY{+upCsG!gB5htceC z>Vueb+;f+mkRUq`lfytX_f@aKl!Wc55|{#5gOW>-amMVUCaF)`Pg&RKn41pz)D}%K z)Q<;!&!H?14wNFdcF2)KS_jCiGlV)SvtP}q)q)a};mOK5n9GKrIGBsU=X*{y87vNu z4o3nk(mjRQw#)q@bqiA1GoXdcM4H$k5VX^bfaz`cd43@0b<dXpyib(*lPnTuqgpO- zCbDv=LqIKgM>TU~3MSGNyF!Lw0*0;nHDliUCuue3y>oyKR2jIF<O~&3$G~Rrs#wk( z6jO8p{%=K<Y}4ZiM36Z>j(WCM6D}$dY}b`|omyk21j42IkQlPhN54Z>pk>TB!#3nc zvlZ-j2nFTqQ5)j6w1S<E5V4i#d#8nc&4y?RsNy|pL+uEu6wg@L2(uM=k72eb1Dn3` zSI;1*O}2-)B$_ybs6l=y-q^_wB!ahg59FhG<HHmx)f$9I5Kb!2UnB<y(g?^_4MAh@ z+=t%`1Q{pW{PhC9k<U5L8=Jqf@jBR9>TLOSj2}zNEYG(sj5T}`Zy9sbmQB@_E3uZc z(~<$m?M>>$hMA89#M#3O-mq0gxNQ2g=~J-gx^3NN9YdKn%G_kKbxld=a@UN+H+Su{ z_2ibV8&_6TZL*6-tjy>oAoI?r%i+>Yc{TI;HoM}A$uo=Pab~KLvlZQBovAQ|?FFVD zR!cXb={GY|*)DJeMx3d1nI@`trV@i}<5x&4-?d+%A`qr8HIIC+wE;l}8j1mQyB3l? zaP3;CNV^tNu!UH72+O03A{H)dG>$C8^T1ee=)8dtJb2-PiH9}Jl0izmA0?U<9IPR$ zCIgDu@)|u`GR=o^;0CXpN5I+kQV)zvRv7*@<|!$qSruZ6UhB%bN5XSd!(`{%X{U0+ zDl4zPWC~HR`z)H-;p7wYhN#>((Jou?jSEAUM=^XC)g9JV+?kcHW}<`rAZCOfae9MU z7XeJkVM=?x<ZH+@&BqXH$g0v6QYlQ;U`N=6?ML8ZI Avnvsa_^~BmoDnrV%oOEa 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/libtiffpack-win64.a b/resources/libraries/deskew/Imaging/LibTiff/Compiled/libtiffpack-win64.a new file mode 100755 index 0000000000000000000000000000000000000000..e7899e73771009d06035456f130874599c3dfbdc GIT binary patch literal 1076500 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 y_nCP1qXIkpvzd;} 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_aux.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_aux.obj new file mode 100755 index 0000000000000000000000000000000000000000..e98da5586619ffd54fb80eb0c70fda5579be63fb GIT binary patch literal 3844 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 BAyxnY literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_close.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_close.obj new file mode 100755 index 0000000000000000000000000000000000000000..b85ecdcfba66416d8198a442a04518cebf308209 GIT binary patch literal 1700 zcmbVMUuauZ82@f=mfX5@&1N5pzI2E~*htr<YTM+cO`17;XtpHR71Leg%}o=%y$SbE zq{zlb4Ah;0g^3i|#vWR5Ft$M-Ht=DUjXIyCI8d1QCbU#k5Oh%J_4`h))V`&CIOm@8 zJLmWPzCY)?7dv6UT`4K5UbVD{3bWk+5FV|Xdci36CXOEMeLXUGg2QV&Zsv?9ahN>_ zu>Sj{@8TU~qgXK`Wtbg840@{3{={%TZC0nu!gOz{qH9*(Gn*exX7U4(<M~sDsyjt3 zZ`s9yIkh*gVwBJ}aR*5^ZA6dnMz>NW>LH}QTKhi!0)|nlYo_%d`0eGxF%GI#qeS2V zyeVVo0lG;**aIX2TGrn19Er5;$h0<1w%#NnX0ESoqM}z-V(vcYNG!DS##J$IyHK_4 z?MfqaH<dOk2CsD7i*=ywSgjeW&z`Aiyg0nrn5y=Rtr(83Q?B+vEovpMI_y>UMB7d) z__mR~H{FtI*xY4o%Jn<%9gc;l&FCpscn+!iR_Z_U?!&iNBb65squ$)v)gn3m#cQxn z$&Q`P`V-^HF<d7{vLk+)0?CtS#&c=EGMdSVIAA^qx8?=G2Ye-ge_v|_5R?GE3_!pY zT_0SDLPz#gDwWb5s|*ME($utM#ne>0YR*81LRbkg2;!qL)ae?)Tql4KQ1nv8DB|A+ z=CG-R;QN+VFx7I50PII$KQR-!Rxq5}1p=|t1g@5#f0ZKu+`J~>oMXRb<abyu%$#mE z7GgiK;jX(|;`7*My~lN=jGOCeJeHcvj-Nf+ki=hNn;)%3mz8txPCoLe=R<5V+<ayq zV}Gq~b=`Hk#eZaE{*Sn_aj3a`A;8Xei(7B6ZZ@AePj{M_lo}sPU<Wg66wyS+y&A{l zO?X?D=xHw7ucTXt8!xdxS>V&gS9rs%OD#}teHcg4*`HY>i|0t|{5#R<vx6QPH<JCb zyHqCfQjyO#%~xwUHy(!?Uw@7ZsGp9y8$+xyhZ{EVpzD4r)ObGP9QnZS*286Tk_#dM z#lnc~aN}f*TQMKHOJtyR<^{8zIW{J-`A<mT9=VWS`(w*>CuH}B2}&+p?}vrYWMSck y>|3}kiwoc2c!FbH7JnVc`WA;||KeM+xHyiZh~q<9gqtBiOWDFyFS!T|H}Ef|uQam& literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_codec.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_codec.obj new file mode 100755 index 0000000000000000000000000000000000000000..6963428659a26e980c6fcfde5cac916d08e3e77e GIT binary patch literal 2396 zcmbVMT}T{P6h7mwyPLSW8)LyhA2v{-A*ou|wpOL!{%F@4-MZswlQo;o?yNg>omu9` zDkZ6@U8<c(eJY_OkOu?lOH<lHTj;}rVv?tl(2_uCp)dhcr4$Njsh~ag&a4~T7xlu- z_ucQFbMBdY?wRRos8WSYA|<8yRAUNe>j1zTkmOuaO#6bzkNaL~Y-^$N>JH2I)%%?= zdkEn1?-%cM9w0H&g4~#e*;i46dddG{ust4?r7<}<?&}tEyb`y<#si^PytT189uZSH zEzQRjHJy~l_UD@rGZ>rrwvm0KqQ7}B`w9|qtwq;{{12T6@iQ_LysVVOTOS?mpkhiA zGeoRGOHRDq=^&Ow@LGb42xhdGtwb7koya&pPGY@69P#9ucI}wRNh#vF_cKSwq<L<1 zS3I{p?5e2cN~7mqDlH2lt#qgLt*yKMR%J?QQm67fEe<U<9@XxPoe;HLj-1s#T+@7p z?%Hluwq@5zC-82g_x|?Hq(qgbj7|CY^Rq`g+@#IORTStMlI~lXxKFze?_Qqlau_wz zn>)MOMiT$zd8kaV;Stsz90-Mxhq~A<J9#-mJ%a;7QF|f~i`npj%TD;Lonb0~vPH&T zX)*w(3*b`+IJRtC6>!PMc-csIcSID_kT#w_iGLBODk*)ZhGBa-noPYN5L5*YmzLSA zCZ>5gmP(4?E{Xm82wR@@M#DYj5W!%CWe5AhaHwQmQilH{btfnN|Cd_Jshx^M%W<Q^ zWK!;v#-OGYH`Ws&^J+5h7Q{3;uOK*;Jl<Y4W9Ao@Wl0WCssIbs0E{D%%b+FxJz)WV z5{&bl!YAcawu1;qfT^fWkZ1u-$f<%z1IDN^)|RR6m((CFt0wd5pNPw|C_h8<QK)fw z*}(u@dyT>OkiH({hV>z@uGQ&bxBftH_uSmH{oS!KQNN|RVp~J?Gs~_*QM2nyYcAU_ z9UJeg`iqG(r$?78*Yj?D)LVF{p3wiiyXm>9)!F{y(DNzBm9^SJ@q$A?UuWBV<L*Y` zq4r}r#LO}#ppUvTdT=DVN<QZ;{SVJKILS_8e)B`?gH)Yab{C$gcD-iJHh*&c&Fs6l zxaHsaO2rFi8F>B0GpF@OG&BD_{~bN-np*%|zcp>g_XUhSH|wQyw>AG3qX2ctptZF> z`C5N*bV-l83inKU@gH)cs?)M3t8lde7C+`{j4N!#;&skre22Kgd5txeF*aD{x`(TI zbc|)-+6~eK573`%M#j(hU^me5E8){7{|(`o#h)PGe9zQ35S%8ZK^(|QJ{}B+q3Jk+ z+%|3U2Et7We&lwGcOmCLG<|!Ke`(ndBFCPjBY}M0(zD1_v?oJCu36kv%y+*e@<7^j zyE~t5O4^IG1qX8J7Zi0W$NT7%ww;f3;x=m<ws?;$X?dT2$E+11u>oIiAkqUNK9ftT zJana{b9`yC-GEl#nb6V-4QXgld?{%>FY}5bNTN^C@_9*CeS+c>B^8&*zmPNyz|}c0 zfUPiG9Amf<>$nP|9?{3yjOP$faCV~^v5l)VIuS#h!+0GriX~?e<DAnti<sqHMh;Qs v4j7Y&)7(Mh1H?;cw}5z=t0LFNSVX*m_yy-SzDE3>t2T;=x4CN2NoD^96AI|b literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_color.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_color.obj new file mode 100755 index 0000000000000000000000000000000000000000..26b2cd037b694b6f85bc45ad398d699c0edcfefe GIT binary patch literal 4056 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 I6qk+s4{uHnuK)l5 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_compress.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_compress.obj new file mode 100755 index 0000000000000000000000000000000000000000..26a919a87d8987dd2526bbcbe6a25a2167a168f1 GIT binary patch literal 3597 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_dir.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_dir.obj new file mode 100755 index 0000000000000000000000000000000000000000..4282fee209c9efdc221f1705ef1b060e1c63ecc8 GIT binary patch literal 13015 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_dirinfo.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_dirinfo.obj new file mode 100755 index 0000000000000000000000000000000000000000..dcf31dc0cd6905a87beff50c966ccc36b2aa544c GIT binary patch literal 13115 zcmbW84|EjOy~pqDF3AEx76=d&Wl^HBrW6QTG@MGZNgy>LOLkd81>>+gn;qEQS?5pw zw9-ROk)%snv_9pu+J~=*w)M62>7nVV4@1lGPis%Lk9V|B->Yo_qv7><s?So5?EC)i z%xq?-_7qF}W@kS4cmMtFy}!AWz3!RKX6bUWJ0YubHmNL+v!P1HSfEi+(_%Ie3|)6! z@cQLzR<WT4jE%c&d%pPcS~j$Xu{T+jQnw;>)3&gx^r&Jc*dnJTZJSldw#KH&w$;m5 zY-`QN(?&wtCaG#xS)O7;H(FU&UUXJX64iLBm^YlQ`xtrkL|WcKdFP<sKYr=?wbQMx z7t2F8K2~|6+VhX)?an6QBFf9%hkjhV`!qcvbN9_fH>z{LS~~;XNcKvqM&%KF_A5*4 zIgBgWB*9AnW7+4{mXRfafCc;n%+hWEVra<niyn-Ol%cXdO&Q_2PhE7z?z9r8oF9Mu zkx?}L#%o1$KSr&Zt`=+km@@~fgOBuDtFNmsy06-R_9=ZJC-K4g_-my*YoycE>VF_> zLQ3*lZ?Z;q)kQbD8*>b~|FL?L@vP39j7ak4_itZXUqQ=^rlQP!NUQIe-k166!`#bJ zE%U)BoVbILmzsWm4V%^-ZSIQtLLE)bxNd5QHu&gQS=0K?j?H0TcVi^th1kJz_MK(r zf`{>|$tAvN<`rY*e#X90#>xubf`{!NWaXF**?1<$rgukMTUwH;B(Vy*YF1T6ZSL1u z87#<P4mSv;r1-8*Nlar3vcME-H_5a+G)2`}c`}Eoq9@r*Zjmt)ts5z$NifkY6Lw%0 zV?tSXIw@xp2)gV5mZ?y_!;EqD1gZec>eowpi!7xRt%%ZJq@=}2n$IgiQggqYEGFMW z$(JH|9#4))x{)g;9xhF6m((7qnD|=(ITs=4TyjKd+7U|Q?V+?5G`X1VabHP=Ve&vR z@g<DK0n7wp7UgRab#aTF&{I1wvL@z@Bp#7JFFi&0GFVrdZ5K1Sv=mjMVowxnXaQAp z1yUFORT~-yHi{YPW(@a%g)j)125{IGSx+JR?<mF5d@F@AEh$6NRmilM*GXOLP&=g_ zS<@x8*pKaGwFFjG+$y9>qAoRLm2A=C2rLfyeDKre{JBVMOMY`8Z&c=+EAp@8Z<_ss z3*PhfAM{og41c7sxoYsFfA5&#%l~Y`@BN?p5BkRI#=392b^Fh(th+1n+XH)F)ob(b zymVpq4~$ChpJT}B$oR~}+`VI8EX(h%^j^5-r4RPLYMh!XBH!$PmL0)Rk3x)>$`K*~ z8$DlPA!?)HA3S~lL&Ws0b%lu=^LNute(~t+AB8Fg&o>xX@AMV4K<Wv2eVob~JXwKu z2f+C4)`<DBzW(@q$f!PDI7S7Su<qBx_Y*C^-l&L;pDgCDO1*#rQ=|A*IHubfP<GJ= zXl{OUMSgCc<^B0f>wlw{U-ygr2{>r5-_HzR-SZP=`Oo^V+u-MrdyTSGk-JwEdW#J- z{Tvwl0?##uTrK|VLjIzw^F|=Q-4EYZM8=od?c2R5mYSpnzDW&y!qLEhm2ngK4ufR) z>&AxfK8POVzeQc$`)co%srwM&cKW>A0}<03G0St6!`!<Qi!2NH#ZC&CN!C~xi&Ix% zs-^azpi=+TjZI|o+^Hvi@A2MGz8>B@S@&xF_Q38NMtt8a===PI)%#-O)krbNYEk|_ z^6ir_AE~_8`||Ld!RAR{;cjYd2)XY3dFp1&Sd8*9LXP1ZJQ>J02QVO8i?h=3KYfat zbi>HLEMiv}UbDJZQTOV^ZPw)R9W3Mmd-`WH-9Pfc4`DY^HuAtRe0J{Xzl7k|j+pRk z-^1so**^+bbQW^{*-wYdI%hw<!MCZfA~Ld1N1CI8+E6`ru=)*qieR`0&(G?+<tuwW zkDyUlws#00KA+Dkc=#{DBaDK47y2ReZRkO06urfY7)5940}+Kj{QRSC%w8&1$)}J3 zCI22DDB3Fs{1-}|i+nz4CiEdv-zzQop@-S&eAILLThOngp2IK4gu`cJp2Pne^E~2- zdCpuF^_;1RdLF5Zc^}yq^PM?~`{!|gD&{>?h<c99iA_7Z6rZ)Rva_M6=j;YtM`GTy z-;WAMPQ=R3zKZ*EG5@3OG5;G%RCuEo8iMvi2cY|)gV6KPi_q_(v(Q`6AE7@*1$L;$ zD=;+wZ#&_=b<uKt2F53P;w^9EM7ed9%&&_j3(x5x`n809mD8`tMAzW^KBIheCY&{~ zX%tZgWe>hD^p%eyGO~%!48HHdHMx3X)#!9bG7ZVZ5;K`xJu%N7mvU}3YJOmq51t(V zF(Ns`@*CMbj+Kikia2rnaZJm19vNvLA8wz_=V?9eeO|w;evYwl@0dQ*B=rq`!=E3d z#EEtJCur^LeGj2zY+}{$=JDa?x9yei1g(VF$mYq;@v09Bg~5}RD8!fdPk8E{ALfPU zPhb}2&+yq|H)*7K+zJY46;?Ji@c0;SSNCnh&68%$DCwFlSRdB9p@+c2z3-ukU#E7} zt5Z$9IX}poh^GXaxN>;+`C;Sj)E)5E$k(ZXJAIwuk@m?=<2SxP)kcOkjt%oZPPMQY zt9G(2vGHRU$Q%zPJTew-Nj*$~h8}DsfhLgunZ3?RbI&Z4<UU2Ye|j;w5$sU;jgHE% zGEor2$=kD2h{VQk;SQ%~4E5Sm+6<#&XC-&sR^EEDxNT?UPG&xYP{?py^4XB@!vcPn zeOSN)csSz6bwvYvCUlALOlY>SGiViXe)tQ4y#-^Y^#DUr^J1xYs3+E=joM^7fj!jB zPCtt!Ix8xi>Bcf0z>+;070wD+nq_>sde7dB>n2=x#JorTEjIngv#}XR-ib|r)Eo1> zaY@wk=z`cx+8tovE}+8#ez5k~G<;G>wHeKRVy{z}wd`zm4h%{7&&*4!<^u;KJ7Q&c z1r$o`1UARPOgn*H4(5R!MzlbT7U{JPM(rc^X)uHe*a?j6XfZZB7}*iq1XgC%OLnx- zw>ucw5$gjhci2rwtM38xI~eukpTVZP*muBYIM@u7_axXXFzN@~%NmgPIM^jF_6xAt zF7_H&z{P$CHpgPrW^9Pr8xBT&Bla%XT-ecg5StAN^B9}&V0h1Fv%nTujLM@SyWGJj zFR@R6Ep*h2-75REgOMGv^<Y)7!&2hoHW&480bAr?^T3i|ms<5w%=Cg8V3)brm%tWV zjOw620F8iM4(9af!(dl9^7>HTcfppp*ppyED=+nz{v8Kf>S8|!yVA-_^^zxl19p{V zXS27!Y8-Y8U^fYNEtm&CWVaA(CK_Jru%q%8fPK<YUM*M+*h-7p^W{dcRbZ9)u~{Qn zz02+vu(dAM3HBK<Y76<9=3h4$?W^#@S!6rG)`78ye#uS;urpW&t%2I0+aVnqf*yp9 zLf93Yeg!&*(8Hig2)l+eYoRcdfcl~R&>`qa=sD;$=xu}}A5;a^LhGO?l!ESr4nT*Y zQRoHeb?Duw=M6u!7+L`}L0wQ9+5_DOJqA4my$GF#&PP3FSo6_Tp`o}G-QR<*Hdrnp zRt+``)(A$+g{B3$`2hzbJ7N!mg<N*ub1>WP7+4eRC@k5_^aNP5gVA#NrGrskVy}UH zmfIBy*k`hp2$&NNM&%Lv6Ih$o7RuWI{ROPu%1iBCPQey`zt~t}C$Q;YVK94&Qr-m) zh9q9z60l9UaW1tQuuh9ny?87MH#ivOB^CmUIP8MpVK8j(czD1qTMB<~2ixpmSAu20 zw(z_k!h^gm!fvoG+|U@&2CV_Q+rcP&6Z<Czqeld>!(dxod7p4FJMT{&%+C7~*c>?1 ziyw0dG4?vxTrk^z*PxC+fz1P>_K{y|ATRzhT?u9{wQG@9>0mUL#A?8vg58IOss647 z`@^28^59d}0(KhA4zEG54zM#|_T0S+tjocuEyUtrXB~FSz_JcTcEtL@D({*quK^|B z1vcNs?g3i>=A0W3gDrHiAAnU^d1?Di<&A+Y0t?{BX8#Fxsf)b{cF<MchhRS$G<`+> z!}tp2_`6E$-KGAk1e5of{orA(0^uqMt%f#0+n^luW#}OEI5Y;Gg2tgqgsK3v3|a%V zLAOIXGz2{e9feLpuR!MzrWjNS)j(^ZFqDA$q5aSy=t<}~=r!nVgef0X1=T|9peU4r z?t~6NhoMpE1?Y9?T|2D#t?sic&{YjxrRh^{xuh1XddNA9jI9B?`CgOJD-bP>X0U08 zO6}Ug=01bBNc>>caJwF`c`l}cRf0LEz+SNVF7|+fndJ$><6sLcJF5=i1lU3_dkInd zeg#%#+0hgQ6Mh4>2+Ur7WOokiQp?Vs78FlyX6K#hVAgUL7Jyx5<)vL4)v*+8v5Tz& zyBzF7;X=XoeaOMAI@m_AD_}SE)J6CK+XhB`v%^RdY>8!Om)GZDc6q}NX6OATSkRUC zUmeWO`|l2B=lvzvQskv^v-|tsU{|`>TVPkY*q_0cx!4SBIjddla<HpiY&qC97PEbS zGgytq>@jM0Fx&1{uxnxG^r;N?2{7`h)eqJOcAdrS{tkiFy4d|-pR}0m`^UhRyX;26 zJ_Y8S<1c`%0HgI`*YR7hm9D(wV5?m0FJN^pHWPmlUkyfe2-f^t2zI??XOHF8V4rg^ z9@hK_Rg0k&P!rSzrJ+60eb8gjQ_zdhY3MvcRRt7;Rzn-0ZBP#SGIS7n92$d8LF3RQ zLRA1-2CaeGpxYrG8iF2#jzTA)SD<qUQw*wvYM`}H7)n6>(0=F;^d$5g^cwWG9oFzh z%-9X+>aC8h5^D$B4n|K6%S|i+mIreVkM3ZmogfT>?YHdg5cwe32pA2cJq3<BnC<qT zfPK}<YuO3UfPKwX-mk$9Sj=8le*nA3W%oYVy)L^7e6jEi>nUkYDS<5md&tFBf?a8a zb+df}yAf;|n9~QXVAoj8vJ<w0)mY5*fgohS*a6cA7Gqxl^MW~FIPM4Yx!9v%!ab#V ze+=dUn+kIRdx`9<$ByY2fsK=$#q9pR59W2*`SD$n&&8_1rn&Nd0<6r%>cPq_X8Y<E zFu#j+flYU@1lSB$c{{->!N||HPkX`cw3zMFyTI;tvHf5NEau@$Twn*m?s2higWc<5 z$H3U3Qa}G3OaPmDR0{0Bz&sW+=c&NXIGF8&{{!>7?515l#caFzU_RK*!jIkNYOtCA zQaZjjfK`BzUu+*Vf?eXWyA^D<i`@Yhu$Wyh4{JVzsw$`!S_eg;6m%za06GkfLN7qC zL+>I?`Ju(o3aAO{g3{0)=sxH%=qcz$=rnX5p{fE3LaU(-&^9OseHl6kJr0dQr=W3Y z5}_&pErZrTZP4wI4h=yMLPw#K&@0e6JFErvp{teX>eQ|Uf9M9A?{djeu!Sy{90z0H zHr;M7(-*)57ke4Z1Lhox-;v#6Gq3I9b7bdYAAosX%#Sabd=|5p>3pzhF1xG2%3N$M zSh*{2JDA^Pw-s!<i|qiL;bIxEnPASo^?_Bm?C4wAAlOp;c;L1MERsjS_;;hFUmUZf zq6U-n-F1+T7zIUD6$g;zqW?vV9duZRz5r&<>yC87dE1&1dnEqdGIL+HN!cDfWn$~P zQP#Dvq(=A`%&hb-q-1icq-nB}WzOqxO3{^!q^okgHH+_tVWf*XS^e#lXbL5zQc603 z6C^AorPF2;>B?#f4ojsw)P$t6CM9lUq^wTgN}H{1lC-!g=XfRU;x37`D+wvhB4|Ud zBbmf`0<H9>LzPj0*($Sh)XlKmk6fKfAMZ0|F1_L9H4f-fNHMZ@k&gRzIcpXc6VnD7 zA@)0OyE-LJNgKS8TOHS793d0c5I^`~sFtTNNW6PpVMUY8EXAu99Kx`BMc0lNtjDB5 z(K#inNt?5>&YU1*sJ#-Sn%P5}*lC7PRSDHVYoRcdfcl~R&>`qa=sD;$=xu~4A5;a^ zLhGO?l!ETGLrbOEL0)&5I$>mXV8}vBTESrfN5i73nAMUvIbdYtynhiTsrQMh#F}to zASz>M8&n;K3)lul(`kMgm`iL+O4g;Yf};}*2PiV4IuJ^!N(Nqw!%dQA*3>TM*ak73 z)RnBX0k#^8VriIDs`Sd5Tm&7Lws#3Tyz9EfvTAK<qQyoZt=U~TLqZ=ka2hjv-jM{* zFlF^lG24UA*%wh9ij#DH2n5~2K^j%GCSvK030o8uePj{W42!s?SST&$!XnmimwiP8 zq5YlPt)<3)?{-HBlU2fbxHzpLjcpBqzzG@!30l4!*t3;Dr-Xr)vT>;~Eyj28A^b?z z)!r6{0le5Q>bgW(F&RW2(s;0Hl5%!{GB0UF+*jPP1?RyWQI2ly@s;{@LzOI#M$Nk| zs+gmgp)r0=r6U=WrM{4&)0knZp*gDDzXhi~6Hz4+$C}HQvJi$`6*2j81FGDU(pje* zPtoC;)^Idr#yu^i<XCf8YYQfeIlowIC=|q5Pzu{@eK<0e5Yd&5G;Tb8DmfIrLr%;3 zz?4PP#`Q5AWu&Nz%SLz&YbRkNjQSmhWH!nSg>7^q5GRKce;0;Y8Ym(m#$m}a+_i}V zilMVMIg3-J^5=Pgii$hXW<H#4Qm>TmlyW%e(<EoGJEU*#s-}`0Rv&%YB&Bt6gCmWD zn7Pz>>CVe0DJdFhJwgW@S)-)sn+$YJA25NH8d8-ULVS-5@0j)%OXzf!GtWofbJv)2 zVx=8$Xo9R_rguYV(GD|`Bb`qT2U>->pD=jPv&(RSRjeT^rj?#X5d#rNAk-pwrf!rB z1c~%V-tjfnwUsrBnuIkdHTUNf4YRxmHY&0<br(sAYA%asH_`_rbv;e!2o|qB%66n~ zlQUpu4<eE(i)kyKV=ZZP#*UfMw=r#~onk^ZG_+io8`83<;bCd+Cs0{J>9fXX<9dX= zUPO3W*jPimBz2>rOA#?C=>w>#=(yH&+Q`URF{_gcKLW#zVjq=aPWIL&W{$X+2FGT> ze12X8<1J~xc=SpQIIhMVm+hjqOG*$$$M_&%$=WN{1YuROfH{~9Pg!(acNkjauPp6B z;t#tP%d<AL&WN)}PKwj8N8oWh;d(LES!<*NSW0waEoOT(){)Z{DNS~A9Qz15<BY)2 zBF1&BU^?_@#|3niKU!_C6rZ(CSSvb(2TbP-b`KQTOLrvp4o^y0yFhcZ6iFF~)GBX| zx$9{_xY7;0Bq9tfRwWZQ@~B8N&b+sK*@%f!+$@<n?JRhr>_nHEum#|Ypj|>e=q|-G z)<!`#q8KVxM@w4NQmlmnoE74&tb8<!Q;coaBs{n>v~#3qc0`JE$4^}ry)uQS{K9L= z$^Et~c?^i;L^ObVq6@)~hKRPgw2gG%Z*`?R?mMM4#wCI7&}mVGHC|V97Kcs;F#Z^1 zF^zp5B6>Dn+})WS=8@XDvE**x)Val+qO|*SY|$)F(bIu%!t8Nr%_gLN#^)S7F6k*H zfhS{HvRodCOIh<xgPs=jtXA<3U})I)cS^{rVj|f^@$usE$qr}Ssbczb3aqJG=h(Go z>#omaP*sNUN<w0ep4NN|uwIn2n3TGzq%lqLDPngR5s_{-ek!UpNxgDh!gh?-hx6sY z>aRVi%sm<3FchN*PQeDv7qrqN!SDtzE@<SO?`cU9Zb>kD*1KNl=oZxnci<@WuAq_( zRwvAiTF}bW!t(}oyzS9>X-ooV$>tkiwRTf*2IDV=w^px8Y!6mnpEhcPxU_#2FRs^s z_%Ce+i*{~qW_Wl<noBPEq2#IazUB@C51SzVZxcnW4I0^9S*0%<)Zv8{{nh=vV|-6U cCxp#6E_$i^8(FHc(?-tf1iiNA>6Q2Y0av}Fx&QzG literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_dirread.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_dirread.obj new file mode 100755 index 0000000000000000000000000000000000000000..2e3c2d3ab718dced5b50bae394d4513e01465478 GIT binary patch literal 17601 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 G<^C^ll1jJ$ literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_dirwrite.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_dirwrite.obj new file mode 100755 index 0000000000000000000000000000000000000000..687b5f095bf14d5f7601b30970bb2b6674c8dac7 GIT binary patch literal 11692 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_dumpmode.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_dumpmode.obj new file mode 100755 index 0000000000000000000000000000000000000000..98dbf0779fe11237a201626750631eb8b358ff42 GIT binary patch literal 1803 zcmbVMU1%It6h4#LY_@iqj3sCg!6~#_*|1HzQDZg_Hrb5?e-gSsR@^vUXJ@kmvop*5 zqz%|LvMj9LJ_G_qszM)H!51UsMSTh;Bry+_f?%K^hy+t2BJD$kV8(NArfErEHhDNR zXU^R3JO6h|gjPo@$a%L^E}3~XqCmYJ0K@^)(sM>$8VrY}mm<BVq5d?$#@@qeW2GPJ zPXgTe`{SSb4>2aqiWH&#D%zmKjCK$9Wk)S@+{%@tAx&59tk0Ysh$pi>k*@5pq3CX2 z&Du^rXN?~WD;ovmrr2KUchrb>?Z>ZXQs_~Ho+$s^{{*H{m{2YIfpPiMV=-n_Ort=J zt#~qnsR!vvY7l)xh#Gd-UiJftG#p4tEm5vlC=e@ms$rn4n+iqlzvc+c?bMBV#k%do ztL;4OG$QxYX-hL$r~Ca}dm7Hwp0rOnlVz1Phcz2Z)p)aI!_{?Ks{=suYJqv}^E-RG z;h<%lZA2f;Z$U8}7BcSToo~(`iwShi=qPHehIIRuCVpkxhqG6vUY<f5-Oa~#o2Ri~ zISI{jDse6q7#xWwP>-KUoe9uKF#h`4k@RRl9!Ms66!@$KzW#&bnt*9t5NO`f09rx- zSA!7rc&`aAcfw&gH9RzgGpv*+p_S@Gx@#BXIVaZ*0{u={mT4s}Isns00Jxy67c?V} zmkmtgLUAo0*lNyFiZN=y%Z3vvXxPx4I9^U~;pH^V0bKi&!)KcOmCU?D(jsx&NkSlN zq_1suoBubqHPPX@p`@4Ys4j+T^KO7FY=-#XVp|tBqVw|m?~N_^&`Sat6Kl7fPIB+o zcH6Am&fm`<@)6+D=F!@GB}gu{^V@IS+N#}lf7wGJlCe<DtEl8YnK_RoBCGsOQay)S zY^4$)|Ag<7YL<Q_1m_5{3!@t}+v=jg%~3lyM^T(BgtvDHVlx!{p_pXn-o^Nvt1I5- zp_=#6=ig!4@Gt}7iZt=DP>hjOp7Z9`@I;Y1@S@(FgG9bJF*?8bJegsU{OBCcoZS<~ zi;RKs`voFtfh0m?wQaWkCXKL8v2C-{^n=uC(Baij*qfnPQ4z2_^(_AS4<lHfI=-uQ z?rIWh754Y!uGY1y^`e%=6nskfvKy{{pW&|mo@rWtCe?J~g$#e=wG6lZMT%Sh3gugr zA5a!iZlbKEIJnjW$ns!b;;MpE(<i-cI+ALb?s!qk<2XtM(~@i@XXu)tN+)mv34-8% D5U^4) literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_error.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_error.obj new file mode 100755 index 0000000000000000000000000000000000000000..72ef9d5bee0113af9004a20c9a6f040189e98080 GIT binary patch literal 1640 zcmbVM-%C?*6hC*H+qI@%b;T$GC8EHXKPz+VLES9rLFsNQXzRANJ6&Aw<nCU}2c?pM zbrbBNh@dd&rI)0?AVD#E3L!`kdPzYsBIuz&*gD_u?ljSd%?syp_?+`O=X}rkp6|Uf zIH)G#f~Y7-#TA8d2LNy#Nkxjtv6{}hx|%bt)+P$C?eGl93mzD+1X%xh|C6Vb#E2yn zS3iuOM+}_F=ElypP_L5gQz8R3T?t7{g^aMF4lf^SaW#ax<*1~^#866&MU=k%aRoV! zu?e@8<n5K48;Z%BND^ulQcnzi_8h=t#D_#Bl?OjQs&!K^nv~-NEJx2E9^Fbj3BVZu zOMqOovqmAVf(scC2S}`QgovH1FPJDu$tYnK-*Y5Ja($y&v2R6WO{v?HMrJXSRuVFu zw8uDWOTlMN4X5hW;X#oOhYlNCRrs(4S(7Bvt9?*oVw`4eGbY<qaM1$ZZDj8+Z#*ii zv}9b$^>;UG-FC8O<SHg;56Sk;4}GQEhj(v~WT{4s?B>p`Hj~1ioq`g<@4M!=boO|C zsC(P}?H2O0dM{q-3G`Zo4xTroz;heCJ<c#Dpqd3NB{>bi#sa*sf;DT-n&7bmDg}Rc zR~LOgF5#0Qi3-4E831Dyq<BJ(;d26$xHXLJR!WR0(SA1pjsjzH>|*-vs?_}mx>u91 zyr}xf!(>DrCDdbRJiLby$xnzK7m_I@cFKH1AnXZa--6KbJEBu383rJr|KNjH^#G@9 z4&7(hm-M!Z>5TcWdvnN{)mT0oaE{Hf=^4$U&u*~hZ|==IYt1vljqBlAgF0f@!(4h< zJ+1#<%v4Nk4)dQNGXEpaZ&amcMy>jY!<@OixS3wozHEnhjirDdU>_bL3XQr4#0Pba zod|Pd7l;?A<$PAN2eXtjHfPVn9EEQ~-oS0uscZI$up{qB=-h64We&#Tjlh%-FB@Uy tL6BL|QJ(ruE3Z)Aqy7P90re%6U#Mq-%&N_AT6OwOFvS919{TA-`3JzH82JDI literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_extension.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_extension.obj new file mode 100755 index 0000000000000000000000000000000000000000..717df59c23739db51cdecd1fa778107b47600103 GIT binary patch literal 1730 zcmbVMOKTHR6h6r$>8R1B8nEi7E|h{2we?Y(ENs(Mii*V~qhjhbPA2Wh<W87Jwa|)M zf{9nrm53<1u%H|L11^FUZC6qd6a=?cD{fpU6dKQ+Old{Lw2PU!^PT&B=XKA$+yM2a zk`?8Prc4Y)BOwWjK>!GJYPy;xnWpHD9ZmZ}Z7oo21ep12cals-px6NL<@=SX$SS6l zQS{It6b~W>jauuTXnU$x*9P=-uBls5Wh3QCr#idhslB1ysU9M!Rz^-4W+tr<tc)v? zENU}u8}r*sT6ZtUuc9&PdZcd4zm2TMG_pgoZY+Rru5S(7prny31J~hB0aN#}oeT&# z0MCG`+I}aHP|bnl<Q&sFz=&A6T{RO$Rg)NV`87vkx>`5eE7olpUJY}x)5u&-r*(zc zosKxU?yWgjW7ybb4(DaNId-$LRJAu-B$leOTCISZk+ZhfcBiu~H3u!?*+%xt{AML$ z+9Bg$etFivHSA|&#*U(5*N{!$>!J7d^x@gdGcOwvW3&19baO2DJI&w~6R}eXPxN?K z4A)&9i4G5Y`MM6DIG*hFh@J5`hY#GXgQpi=E;raK0FQTJRmTI|^MS9zRorl6JNWQC zNVz<$5feS#-MO@ihsy?4nN*GWVSw9f0bD*&%_<~=7Y*FTS#s5#Gvu@`4Tc%88G#K< z;;?Kc(gQ~o!;EScF)#hY7$c@We2W>kAT!9A9g-v)29l4-=Af1_UN9?OV6Fy7H>xT! zF?&c>oBG2Pm(4|!%LVXgguRpWTU<CvlL2Z4Y0OVQ(DwT0^W3lS+)!i1;^UQM<Jc>{ zP_{gDVwUGVh378Jw3fxQXZj`_>ZqUg1qvU{o%F}_eEoAP$o&+MxrF$7c736I$wx<n z-29R0xxz>5-J%4I@nhv?difPbYiqJ7lKj}q=0bUd4_{^Yh<9wV(l>F_e~4b4#D5wr zQn$02_uj`2I?+(ek9zd(6NF~nbPI+R5Gn>ggCwZt@f9&Ije2I?t>q<ISOGf9(}({d z@)%qRxsmgJTk<s4>l)*fIH3w0>aBiDQGTgon)H|Ao|3I^7Y`n6g2$f)*LV{??Sgy! Qh`^1<@!KzWfTAM&28Rzt%K!iX literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_fax3.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_fax3.obj new file mode 100755 index 0000000000000000000000000000000000000000..3818b96a9a33255f711946836bcb0e54a6cebc85 GIT binary patch literal 23904 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 H1n~a`gwQCd literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_fax3sm.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_fax3sm.obj new file mode 100755 index 0000000000000000000000000000000000000000..806ecc748486113494f990a98194cee68cf98e9b GIT binary patch literal 100394 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 E0}_2^4FCWD literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_flush.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_flush.obj new file mode 100755 index 0000000000000000000000000000000000000000..2a4cfcaffb7771e7df7f0653a0272fcb46a7ce91 GIT binary patch literal 1468 zcmbW1O-K}B7{{M+)^)5^H`~J=^<W6b`mt8#?jhZELpmtU?pm}qO|vsPFwV$)7!pb% z3(|BDf`}jp@=%w&)@?ztI)xAz2%T4qItUpEPR~1zU$<QkyS)5)fB)y}edb9WoHu2O zlN8Gsj|i|30)Sw@rYpRfZ%VYaHC>7Hbh&cr)Xj|gB@PQ00JeV5eT~-=kG!l$#$n+) zY7o|<or&Juu&#~i{6teqRzxG`+2;C_Y_2=fksDM6#mb90!_4#g*x6W4mC&28JtXe1 z8tteguB;KX5z&^(Z}D?jhBPJW#-aH6X>-gK1x=NRSdTZeSh}CwBtp;=7$O|FUG)-) zl%2?gI6=Iw5fD4qUN*=nnn1|PdyXJ~&^I?K_N{`bhIu?`BrBP;F01aO<6f=ZW!Gv< z8|~)wr05RE9X7VA{9$vdr6{CVXF&6!<VNlFCfikZ(j2~Rq|X*t5>(So8JBYF!-M8n zfUFsLin7~7vVCh)8}9bu+nXd&E}}+u^Ym8JB=HxmP|c+W?xcN*q2vIblYQwvANlhq zZ{8lt4Ewl#mZk9rFKgia4p3F#o&bE+ho>3_;FTZz4((LIvleJf52jKhx@?L`Sr-LU z)2E@HXeshVN%AJ&39wiPK>0aElGQwZUtkf}h^l#Lh`cV0$B2MyMAaT)vqZw8sk$SH zIDJC-%`Q*?8T%KT9kDY(+X~qO0sDvD+pxS(|BLNSg&m7wolJOsl_?Z0pS`lp&_82) zk4n)ZcmLk#iigew?9pIh)4Xi&tnW80TOoQki{uG#ZM(5hob}r?A$tGT`d(qv`hLQ~ z9%beqGhkj~ORbpJU5r&7Eu#P5M1V1aYzf5^>PJi(yy%G9iNL!Xhrnvk49^Jx=a1vC c&hs!f{n*X(K@wiq!qVF;wX~hB0vj*Ef9(G5$p8QV literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_getimage.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_getimage.obj new file mode 100755 index 0000000000000000000000000000000000000000..acd6476fafe2fb5e0b99fe2ab5e97475c8e14344 GIT binary patch literal 35495 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_jpeg.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_jpeg.obj new file mode 100755 index 0000000000000000000000000000000000000000..b7fb8678ca611c61b06cea5eabb05f04c93fd629 GIT binary patch literal 13364 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◊tSEfoa!5F>#k4&tW{T!}H@}(yr~Ib=QmH@Lg|- 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_luv.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_luv.obj new file mode 100755 index 0000000000000000000000000000000000000000..2baa44337ec18f46495ccc31a66e62faa7b54866 GIT binary patch literal 15799 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 E7fC3l-~a#s literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_lzw.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_lzw.obj new file mode 100755 index 0000000000000000000000000000000000000000..6baebbb19df8d7e88f12ddb108d4be81d6af5c7e GIT binary patch literal 7571 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_next.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_next.obj new file mode 100755 index 0000000000000000000000000000000000000000..f241b3069afe0d2e5ec06637f16233586f543c7d GIT binary patch literal 1877 zcmbVMUrbwN6hF7Olq*)LtTAZNbjh|zSQuMzGD<R6xEOQ=w7qqSWnFD=p-H)y+}`5Q z6frj>wBHaOj4>uAdr%W!wz(&L1504b9^8zHizLQnN+6e*8Ji44!}WY0-5=k8hkNt8 z=bqm==lh*=z6mGP=&58>k<aVRahP%gfXlCH>6nu6bT&14UT$tX22=Y0Rv&kilxrO@ z^$ft;UmsoXc!EYqq_pNCm^y(rs8_v5JKH1unmVY(hCSV>w48}lqDK5(!ANU!OQc7M zr?Uw;lF<_}ZE$bgsFK9kl-ox0_AB0&-Q-QFlv;<>gCjq7P$80|vX*&ZTq^AMF=Jd+ zlGOM#dWP}n4(dq_u8KjRhV5v_D}^-gxR7Can8tdA5>dG$J0?ccYMe56uQ?K9+q$u= zsM{{GX7op$M&@oht)&#!>5j@-TX%fc%vk1#J~kq==CEd?R6B2WRLQ2(RI5Eu6LOMe zZLf6p*p7>i;%p;(Z+Vk(MQ0`BPOe=#x8LWaYeq*g#WbYbw>bI>+diDV5t@aLGTqJp z?KDs8{`4iNiiX59A$w<Emx#FQROpnQz8qbzz11i6+oS$qkjDo;tA=l4HqHhNwSc{9 z8v#@c0G~U+Veys?E;PWC(NIr!H_mT-WDJ}Xi<+it;&~ll_9+0)5ltskN&+txn8oGc zs^87XF)cpiqXxWGI7aGGQhKj^CUhKAUX*PdfJD}V;kSw8B3U;Po#ZxYuf4p*Z}>Jx z>#eL1w4{1tL71G&+DXwA_}_e+@2z_0qG#V3C|0OBCmC=}F6)QMKex7OFK6BS{V*~g zBQBbClXK$^l5_K0Z`|6PT+aUdD1@=V3c%DiNx+p4lui4h&GM5#*$~Ut<n<5#CXzs| zMzCTE3g-}@7sIC+F6G4yBj<wJj~nnn-sQrJ-4;3&ja5#oHdasSP39<(%08)7d*NC1 zu(BmAaATEfWh^-PY>5O)je+vo5*oa7-k%DNLYSNXUGOgRKNQZ|=I;t<3-kQ1(;wJ< zxpM8yO>~@g^EBej0^;Cwul-G4+~|K8^L}HlY`*WEqh-F{mJh5GX@lID|JPyM;f=c| z^MQNj33BaK`nASsHF{l-{#=jlF%J|1_vY_7rfSUR8L|sCri&pPA_pR&#!qe-MO0?I z$|tUC-{ULeGIC|mJ06_f{Dg?5M$tv25+`kIGxNumWs!i8|CT8!3aChamWufuN&@B~ ztkk-C8}Sf+0(x3Ui6~fCbabg4%f-4z{$#DkpGy-B_;VcHmnIH8M29Pg#T_WZ7can< zn_=#&zA(44FJ$}rP}sKO4{<A}@d<}GxY!EFpi|i;$JK<~?&(!^kF2QK!68o~rpG)< WRr6%xF(sW+WY57g8*(5>g8u+<1(KWq literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_ojpeg.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_ojpeg.obj new file mode 100755 index 0000000000000000000000000000000000000000..f460b3e6f8eb090a9cb4612df9ac57e72b6fcf98 GIT binary patch literal 1257 zcmZqRW#BH!OpDLYDo9P&OJ?X0VPIeoaLO;rNz6-8aMspVFx9g#0t>%DQMtSHx(!2z zAOpk8ca0})I6-ElWESaVFmzY~#TX>=jSZcxVgict(~A;w72Gm&Qj24t#>P0g1jm@@ z8N_(zCFhi;q{bANq$C!llczU6FAZomNUsIR-hez~15)hG%m--|0%}z)IB&xR^hR1) zYEdyv@OYP^HCQk?KQ9d=$OlvzmUqI24WtqzAOIEM00}@$GlM!rk7$SFrsjgoG6!h{ zhMYdp8sl^FlR-L34|AZ#L`b-SZ3TuK3APrOAYvM*la!b)%FF}Dv<=j+CPe$SxUyKk zq_Q9t968{~1_l-JksY5`nv(+xD>AgEq^5ywwSva15z&s02c~VHZu0C+OU^3+I~kZM zU#?3~wB`Y2Gf+~@1cxCg_Z=&{4$ghRv{wMKMHnas%A2UUni=HqX&MZy@gc4eAuP`R zF0Mefi(`l*3y5HIaS!qj4Pc3P3JzulVul&)3@hsx7?~KrECvP^Rv3$&gMncd8w1;a z=Ko9#6L=XI7}?@;(lYZ>faM0m1Yq`HWKSwiO)N^zum%b&XkuVwU<mDg6C4)a9V*aW zD$?!B(|x1cihun-<{#F-%Ow7nas>Ymm1sV~(R!eirTg$}4(5B-zsp}3ABc~Ojy(+3 zTFcWNE6{qoM5p`n#ee+kOGTK!hXHjWX*~8?sP#Y{TX(Gp^FQy4zgusYUV$3H&<*qk F0|1tfrnLY7 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_open.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_open.obj new file mode 100755 index 0000000000000000000000000000000000000000..5731976aa3243b74620fe4142a740c5337f007cc GIT binary patch literal 4558 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_packbits.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_packbits.obj new file mode 100755 index 0000000000000000000000000000000000000000..65f2b8bed416c0166851dc742c17c641f960f3d9 GIT binary patch literal 2935 zcmb_eZERCj7(TaOyNp2#4n)I`qGBK=V^Bm~M*{2a1hHe#k3n25U3<%_-R*LF@AwEU z9i`g#h>)0w!I1D{{4h}=CMJd$vpC!sKU5}(L=5{OOIo@R5|&X|-Ss`E-6V=+5PzI| z-@fO(&-=Vz=QeIY^QB<G)8+NO+a8o6H9nN8MhF=i!eYoP_>ESR$@ogmhP5d5EJE3T z9(D?s>QQO|Lb=;LL-lhAl0PWcbfDC0phHW-<~7#3wiYqGSM+uon}Q)e(pE-pYiP8$ zt*fbR+bZ}%vY&5@NPe%l_vvgsLIALd?FQnvMKISsiQizDm@Wj<<y}A3S3($pZeEN` zD+e>nEL7<W3jv~>4<}s^x{jPA3PV}J5=9AZeVItjtci5;odoMuVhEXAJ!?ZxDC{Ga zPo8ry^p^5Qy+Yoez-vVMyV79!B$XC}0<Cm?Io5Tvjx};1vRXRO#na-@VneECUu=&c zhe9M*Phslk1Jr9>xw30#P1*y!4c1TRH{cT_N*U%d_vM~tmMSu4q!oiShh+K&x__n9 z2ff!tyetMCnazLAwwiGNcm>jW9NaF4#=5<c1G#atW3z@lbd9g?-0p1AcpB_>H7xW= z1^R5amQf)peU3ERl7vtNi_j?@(kW_1g+ADa=6W1ko0>YkA?PWjC$a-Za%Xs-J$R5u z2BNV0yh11_@KiI0o)3sTkLHujrjQ)zX!J_nHKpIJUQvK9;r2_@(0r$_>j0`EhdEIU zi)0I(n1>Lf^MnFH!4LNqIsvoERJ2EUujuQr5Cz;@40XR%2udw*&o#h3SJWVMMx$X6 za^f+&s}(y9Sgyug6~2b+^yjA31<Pdj5=Cb1igQWgBAe`!HMl>|s&7~(_hrp}p10p} z_m@qhRoHDvUYAzkyH}?4=j3YjJr`I$WH^vtnCy$`aI{)Iwe8Ae^16KWAqebd-_c|V z`Q7Qn7DzQX)0+Fh!Gv|5Mp|Gm?ctBeURsscPb)B0FyBBFs6+>IEIvUYhNX&~B|ExJ z$tEsl%|j&ED2rtSc30t(4uDjj+yzhkej9;z7R|$HTQ==1Bq#JA)_}@9l$mQy+s2v) zxpCqtuT{A5W@~aHHpe`idB)-#lWrf|qO#bE`XfhRKM~C`k`A6~d54KBq`5noE0`!c z^Zp(HY%TGEDA~j%CHDt_mYnD3VrL;!nPL^Lun9ZwB)B_Da`^Bqe4J9V74pj;gY)SO z4t8b}w-|AeB)p+NIs}y9Wq{pWD?Ad>aVAzj$Yp^E>WPlW;T2mvfO<A%Y-1^QQ%@&V z<yI<+l5vj5zgH%HOBu?WxkZ4G<_ZG{ZOS;uKo3BmLb)P5W}sop9tZv7)T5v;fDyz$ zrOH%7Uauq`9EL6!f?AXu<xWj|So;{(kw-!2+aE<a|0dxp*TwfwDe^jOD}dJpi3hRh z3XtQBDzgA8e>rjgu≷=d~9|m}zG=vqed5naWjyqjC~+Q-3N7bj$H!c;lZ<CrP!K zlcdyXT6WrjFfn+Pw&4!ag3zeYg3+qvM5$qMW!hGZt-|i2<wi_}Zv-$`-C}QrIm$Kw zY~rFJ*%#G9zvSnsCrx*g6Nj%(!|pCOccF|Mg;B1;C(7-NZ7lAgT)0B+3!3Ai{=4TD zuBg}wJ(F>C`Z?5_axuNVE>-W>E_LtkF3nlVr9JztON-9983w^sfez_<@(41JydG^% zOvd#`-vi~n>sHH1%xD_1cfc<MF)6j!$tqt-5ISvgpp8dNlb8dSsN^!QPU)KPFNuEA z+T;1n$F?z+`_UInBUXFI#{Wb)=25J6MJ~F^uvO~2rb)(KT<I<(q6HLVlZyU02Q5ny z>ntXfG>xAG4<kt9kFB8Gu0a$X2Z<vatE?fM&p%Ap+?UtRB({`UI5QYu3L*UfeLWa| zX+~N#BUOVG1I)qr5|A!a$p{i*I~ZR9QkdG-mL=-J-H%RhL^oe_sCw7CbT@<WKIGE% z9&=UP90qyR#g4KLW^^H}rLbOxRST;QRwJxt2ZPR>M}RnesW<Yzu%E9p`hyXlSM<Zz z!?--;H?|*;_=r&o8@=7(ApDdA0bVqQ;UAtT3;(Zib67I+LRjA0Vf4eN%oqrZM&Lrf MdV~u}I}k>H0Zr>7#sB~S literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_pixarlog.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_pixarlog.obj new file mode 100755 index 0000000000000000000000000000000000000000..34314f1f1d205c185d2e1c8b03815a09f2391c65 GIT binary patch literal 15694 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_predict.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_predict.obj new file mode 100755 index 0000000000000000000000000000000000000000..1a2d2276c829ad04b28b4df2f62cca6213b3d0ad GIT binary patch literal 6382 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 bnN|0i4zuFM5_sVB?RjLd(rF+Ri$?wfEDi5} literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_print.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_print.obj new file mode 100755 index 0000000000000000000000000000000000000000..3bdc51dcb58e3ff2fb8a62eaa06d4b567619e87c GIT binary patch literal 9579 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 DBWzOD literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_read.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_read.obj new file mode 100755 index 0000000000000000000000000000000000000000..34546af2fb907573d51baedd8b5f9aeebae977da GIT binary patch literal 6820 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_strip.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_strip.obj new file mode 100755 index 0000000000000000000000000000000000000000..0fe67ceb1b650be017d6af4375e45dc819c409b3 GIT binary patch literal 4135 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_swab.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_swab.obj new file mode 100755 index 0000000000000000000000000000000000000000..6da888d1e40190a2818184ead1959d197f7e314a GIT binary patch literal 2463 zcmbVMXIPS57(ToplA)3-Q!8zkrsm4jhC3^h1kDN~L{JLB7?PDXh(g|m?VYx_wzI8S zmTg;Bw#(9N@3Q1O=M}#C`te=bkN3Uq>%O1+d7kr}F_jL?6e1y4uHq%R^MTd|0Kf>5 z%EUZLvU8}btFy1WzZcM215opOV7%mP5YV;)p!W5&@}L$dL$XNbo(i;mAP4NF-n~Kt zI82!|MaD~Wju43ja*iP@CnSu;@p1R$L`nE!WwL-HS0wXfDSzkXN`x>s^7^B?Oo_K= zbLxtu$Z7?xozpJ|p+N{U1TwjiJfm|7BuKtgB1EJGj6_SPqDVwA3<M1kjoJJRjkq_t zku*UX%GDbg;p9A;Y~+fieB^BY%%PFjI5(myoLe)h$`!wM8akV^(=w5S*y$j{T78<l zR(Yn}Ly?&-AU22CY&fc>Z#Gw=6pPWU{=%9p5E4}b3_I)9<fggsY(w|o)fMt33Zi9r zCu<AFxCGMCHA6>HM9dJ~zOsx;;`YI_myW8Sql^ydf9^CH^}VDUFyXSp6WEl{n6Pk& z!-Cnt6!fEpMaIU&F)7>-7K;o&u*?)}b;C#)AP~SPCI$pdX#lLC0;-;@$3U(*03<3` zEEGwS;j001;q4%qCdman89y};5wLNRIXfyMf(2g}R;pB{$VN1LOh|MiJwz%=$wjOq zWNnOWuuR6wj1dx^0;INus@)$dTP8|}4@)J|9iVRghmKMM5)^HrXkiq?q{<|*z!&jO zkheARhLVjk8_8{;Y=z`Vfg(hth!te8dC;mwnjO?Ekp@}#VhErv&x1m~CkFp<_)Av& za6FEID{b&_I(`ceuq<pKe+{h9u-7YTEPb54`V>u5tfb(jbu{w*z<O1UcQJSLsKio( zHH(fXGBi~RH~d3IgJq%8hWsTSI)5{k)mdqZv#EHN4Y^@(MZKm<dHq)kJe;Oul5{lf zK$4b0BqbO!V#r#Cj*01@G7!@;NrsZRl8#5y>MZdRbqR*N_SyvK#mm%XdQGva=HK3a z4<w^RFcABnKu463qGOU#prJO*3_VPeG5cL(c07?b7Gu>M)$8>*8D~-8lya<dLMg_< zpyG+NITSx8t7a(-f_j}f5lMupOKJEe)Nr}#g(0Oniv|>?MevM(I+G%F92ysV7%_M> z9nMAljh2z-U8S?vIXP(UAxI!0lt4ldBOrz#nSf*jDFmb-XhJ{}1gQk1B4|oLQ*|*- zU5g5+^6JbW-&kS*ep_9l*Ho!q!F7Sv=Ae238>?OpzYS~*<Wn$s^}y5t#m@O-9x`&7 zS3Gy6zl9rjw%eA}8vA7lxBQDHe5K3?JJo4J@>82d?CU=J6d&ochg@*mA%5w!igP#U z=;R;R^pKMs)+If*S`d5HYme-`nQqXz9^1yga9A<&PQc-b->EYr%DQY4KC@dAf79<k z#%GJULoc}(NM5&FGx~nN<Jk?QX~88fYk7}a%^z0LYxnqfEpnpDyKfcMwpl*ncArC; z-%K>&r#o*HRNF3&yWzWE`N@1v^u?Y#(_Xb*op`VBv8<nDb!chF_53H+3t88^_sT!C z#0Q^u-9E0)apkD0z$26DO=m@(>AE@PdF!RaD|;VQeX*PubJ?R%`lfvz_d)*?Q}lp@ zktrrrQ(6nNmUMFqOGYazYa3g;*7j{29G%*>YwzsRp<}1cUAlJb-ow?cr@Mz|FE4K& zU%%e|eF6f5`u6KTAUGs6EIcA|VAP<&L!yVqFo(sm*m3d06Gn_2#Yr5^9m7lF!>2hV zRWx>-I87o=A1{+Dlo_fCnG+{X&dQ!Lb=q|G49(10+8iA|d(PZ>x$_q+T(o$}(q+q6 ztX#EvO<w-mb?Y~5+_ZVi)@|E&>?|nUwR_Lreftj_EIM@f$kAiRPZXD&EIoC)?9AEn zbLTHyyma|W#no%qZ``cBb^A`$-Fx>RJbd){$<yj*&ueO5)V+N5`pw&S?>~I}^!dxz QZ{O>G{A|zz936sx06Mjt>;M1& literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_thunder.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_thunder.obj new file mode 100755 index 0000000000000000000000000000000000000000..d3909ab482d5b1988966b1bc18a8ff0cdb6120cc GIT binary patch literal 2100 zcmbVMZ%7+w7=JH!Cf7D?QtO5tGR|#6hpTm_+pKYa{1b=UY-PD*Eyku<axsB;Czm_h z!nBq%5c|sT(-?z7KMnR}rC&xr#wxmfnk{2<mX<M5%rZvlzy{*HJ@4y@Yhe(5;k|qA zd7t0!dEV#Gjk(Z4Eg27M30;m!YC{xds}Mr&c12A^<e1P=UoX7T(A<Qw&m*+<jBQZ< zz7=H;Bb5K^>JP2^2v01jHY8B?6zEX3;(5KJCDf}b18QVY=t`!fbjTtOwRieMCmR|= z-EuUg$D~kNi$&Cdy<x+09JmR#nfUFMJ&n8Zn^Xw25}<WMKeq0NYs7~oHNCBzzgFv| z%BUj8iSiIwlB?T^B~iF71y2-PY;RhDG?X34pfpIh-XcKA+=;S*;gk|3$ld20fRU}d zQLm7<U3g7vJCz3HZYr%NWm@T0>#iruzH9n&`h<3QNTS7|#fDUsU+l1~r&1(Wdw|BI zIQ80MRko?@pu^DHfZm(mcvRMC$S{`qZ!gq(on*{ND<)|U$@I+)|3aq^dT)q$c@A`> zL7$y$mW2MrF;o#2eP>0k<4mUy<jyv+jUx|x=ldU@3G{N|cE6v6jlOiCZ&qxK4N>V5 z;wrWzgd99VU)hn}WKA2IbRnnM-PPq&RYmn((h!<Dh!A5Br{YOD27ePY1tY^aK21vz zHJb1e#S5S~LO|VeQWLj+KK^J#o&{$&@-_yc0RGb-`~(NwSg*o9Ctk!YuA7@|(OVj> zHg(=_2C9u&K9kcqJX7G=hu+e~HBT;lzOR4Af{r+GzdLhZdkz16chhxKuVSAB0r?-` zT%j_P8@1z+Dt7b3yQR#1{ihuYW47oI;vOgV@pvi(aZLm^sC<FSMNiJNo|hm}9N2Ju zS84h-%$_1%y1JZ$3mOGRtuYGkY5}A0o@;!8sz9g#`+Orjp3K1mX$N@5le@;Wb$w89 z;Kk`>?!XND*sHro4rT6*?$0u9#viuKy({bOI@di``^fmsm46I4yfj_njAhm+*|Zl~ z?mDOte%J-a>obg2!82_ZUG^w3wUukSW#=0I9Qd|rf2Oo|EZU2BVfuG2d$=GIV%Au8 z7$uIz0Dr;%kHNA#1*FvOY0{UrNsr<?_~|2ub!3bb(da(NjB9do(=<sTdNwAqq%K4b z6ep%gUBEzl$JE_WINrRq2stLnblfIwSg5$Iz^gaWNFojJW)n5i0Ja7iWX#rha$rOX zbH4A~xt&IG<9;6N&Wt(wvYBoU`qv)(W17aSlXM?Ij$q>>AgM31k22<!2b5sD5h1ww zu^KoXhdn>`iX~B-{uy-rI9fR_+E&iO9*2Dk_OGIC^)&1N>^{-<wH#zt7er=tNo3HC z&k!(>MWQ6jghk%un;gNA(*+TlkSMyW0qc~aN=#}I>e50iqD6#=CZu8JQb}17>QXV` iSY1jNYTpJ~e-)ipG(nOTeIS8EMG*$|XacfEJp2o}+2i>D literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_tile.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_tile.obj new file mode 100755 index 0000000000000000000000000000000000000000..1194a0f5f5fd079befb41dbbf320f8df3923d623 GIT binary patch literal 3520 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 b1eYj}h?p&W{yxwObG)C&yD>NdKnnREmP9M< literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_version.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_version.obj new file mode 100755 index 0000000000000000000000000000000000000000..4b3920df3a6a89128105d0b4560c1b151011a69c GIT binary patch literal 1414 zcmbVMJ!lhg6o2U@X)OJSB}&KZAXFN!O;c;qWNAJcf`tb2p<wD6FL%w!^)B2;B}1#C zf#5BjL<FI$i(MTHk*bpv1cic=LrcZQL7~w2{+AP_E=h+w{``2q_kQp5!4M4SqQI{5 zswOHj!$HFb0N$9QN+r2W$H&L%%S>nr8s`D_PuLdZcTs4Z1vvQr@O|_Q@lzI6rUH#H zVlbiv1M%5nMpc&8(h5B<O1xII9T#JXY;l^IET&~nGRk~W)5|4w`E*=X7SK20hDh9u z9GvVXuBZ^|Fj6m6KScX54S|&75BT=UXv6|JMHUD+h?XREH?bsu*9JTUXnUKng)kip zS>acRuPcOzm7D08$Vv)FnBCVLiKTYktf*MGE~0As-%ca5n@+2uY;`(n=Q`bSu3Al- z&}&uRYL3-xELG>tW@ST?NUcsmE%SmEb=L0eRL7!OoNZ*EF0R1Ix+NLc^5E6&(MUg8 zGct;zRYS6UJFB0p?Zertk|^g8BLjMJvne9|=@_`!T=GWF8DC5!@tv5<%{j@>ok(3< z%x9czESsh9gJ-?)@-aAiz<S*Ur>p$}==A_RcZ0h{wR&J<0Knm9B|((ScspPN7sSzf zN8?K>SBVe+1vv(D>G}B-uNT^vW*za(1~>rn=C^F&x|#QyhR;m)o1e|up{--;SLA4Q zq-A)rt^CNNH=g>o;WV3v9_mZv=-z&Co4s{&scBQ!`pqS8eNXqBKX#9YwhSNjvw+Nh zh&zYF_3d@Hx#pveukIez_l%FWi`PVP6JEIB>-}60Y|4P?E~H~bt)I5V(m^K7O!vi= zYE2cFD>^;KU8DoyQ0P)19G;=Gc-<CwL6CU$KbvsCwh<)}yGEzfQney-nxDqY#{ifZ G4t@hk*we@W literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_warning.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_warning.obj new file mode 100755 index 0000000000000000000000000000000000000000..226c2e15aeadc1103fecff9d88a33fe93cf60013 GIT binary patch literal 1658 zcmbVM-%C?*6hC*H+pVTvZOR^sl!yXj{;bTc2X(Wg2c^5spxJG0ce=RV$=$u04@xD2 z)lIO6B7(x8mtK<of&|6vDTE+F=p_Zkh@gi8Ve5RqyDKptHV5Bv_}ugPobx?DzIV!@ zRE@`k(Xb-N<pEa&(hdONIueQ$mZLSDb#*moT&+!zt_IlH<r|cjJdmyc*!+I?gXaLz ziN+PzAf(SD2F^rtV`p2aS4j*g;h~zYxFjY+hH$9E%ZFNA4WVv1B56@ElvJZ(Wnh0? zL5`s|;kJ^vy>fFyF>&JwLajvViQ$i)gLsYDh^Qp<;QI%)ZVE;ca*Tjw=t-__C7uM} z41gs-PVKCbh^yd4hQuMFb&e3Ra`goh1t}3B%;IZ~#Bi=|G%D7uh^R^RU#F2-Os5qx zX_&FcxNA$ncTJ8Z>(#Mgkv4}m8%tGqvjtg`BocWa)TkJvQQM5pHWi$-fU}M4{l$$% zWR<3jYq|O6My=aU){Kl|oYs(R-{Qz;x_vl%!z4--Vq`#fZ?>7F{^S&t2!7vHzooOs z>qFby?r*n{KdblRm7YMaMd;vpGah(qgV)Cy#st)|fTbj70oYi8XI8Lg%~=yXtcJsa zzq_l;ctkGY!y$<Zz;r19V-=)WT#n+S0@JuTjO}Jp3@edAHvx_UW2)YR>$|N|!Vx4? z5oumj`*Kf|p*}(A$MCar&(A3Eh<M;uGlz(W!G1+RtPfMR14QSqh|ip47=VEOjSu$e z0Z!K(y3ek!=xyb*8S`)V_J}j9v3xe*oLpd2bDBk;-(t;Q+}ooY&2z%_YyI;Eb=<D^ zbE#GJwElBBQ$DLX%)f%j+(BI2s!YvISoLv-Idge=JGH8P`X})cQvp4|-p4cnsOlaN zAJjE=s-K&@K)gUL<FlGQn5CS_1$!RmD0~a@AGp7H>Y9D3-;wttbnbt8MGnT~4PnZM x=Z&y-FUYLvcpm#rYcKG;Mf)9|CA3%Y{6ISsWY%qd)4J1df*BTYkmye*#vgO`AWHxM literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_write.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_write.obj new file mode 100755 index 0000000000000000000000000000000000000000..7b22eb8973fe32023f3d928472048e75e261fea4 GIT binary patch literal 6475 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 a=q6JLVoB*`Q$ZNN)4B!^jm>mw@BahdIo8|& literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_zip.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/tif_zip.obj new file mode 100755 index 0000000000000000000000000000000000000000..cba16eff1479470f7457d0c9849ffcd229cc7d13 GIT binary patch literal 3748 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/trees.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/trees.obj new file mode 100755 index 0000000000000000000000000000000000000000..b0bf941734103250f8a3a58a884ddb4ce02bb35d GIT binary patch literal 12422 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 literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/uncompr.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/uncompr.obj new file mode 100755 index 0000000000000000000000000000000000000000..2d22cbbbec21fc11360fe3e82043478cd79b8ad8 GIT binary patch literal 681 zcmZoLV9;~6ig652EJ{@ft}HG|%`J}c$xL!d%_+#pjEMp=W2$n1L}^}fer`dLUNS?6 zFarYvzf*ouPGVk)g0r@^f~lS<Lx(v7!wXW)n#1#4kD<eaf#K!tz7%~Oq8w6{oS&Db zm%-3s4Ahp^RH&~_oHn2*K)TF;x<J;F>}zJAMuuq`46N}Xt`Q+D&i*d0K(>oxh$9P# zU~_Q~@(&GQiFXPPW(H!08SD%zcQG(BF@RYd3@oe=76Su22Lr<_HU_r;%>S7fdQ=#A zGV{`M5=&A&^D;}~8Q7sb1}-SWH7|vMVS*q710xqS5>ktcfl<J)U>^e`14C%{o8}`Y zg2TF9MY>CQx<dszLr-*H=nOq$eW-+M$M66D|G(@7D(?2>==8nPE!KUg)Afe+g%WnK z)az2?OP!%Fj8As@zR9x40GZnBdLrNjh|zq6r~5|h?Gl#m!>|3aSb9TG1VP0)x^Fc9 zW-L*ID~)Xa%~ZnE{QGw)Uw7!4)=Q=AJN^PKek~5vD%>4<r&Fxg_e3CAcVzdC?!$4> z-A`i=Gt3obc;XYnv?Mf)aY=3%(~^cT<|V7cm>CxEFaU$iP|rxu5af_<Acp|}(Estq literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/Compiled/zutil.obj b/resources/libraries/deskew/Imaging/LibTiff/Compiled/zutil.obj new file mode 100755 index 0000000000000000000000000000000000000000..5dc64c82b4eddb520233199a37ea033b9b55fe4e GIT binary patch literal 1784 zcmbVM%WD&15T9%|4>X$ifLc`8gNH~YeFn4&Hm{aK+SD|u54MEu?w599vs-t!LgS^l z6ap)flZaU9p$7%gOApmku$OxBaT2_E=%H<T@IR>YrD^n-<na4;zS;TB%+Bo0au}|~ zhxq7SK_}eQl3|jf!KdWe1W^{|W&Qz*d`Y3lqA4q35z6NPz#r3eMNlO!9tv@ha0JQ` zfZg^y-PyR<1LYxrJqX?o_G1#0ne=ETI?j!ZrIJ&8ES*V3ClY)-mK#eYl9>oUHW5$d z5=oxux~hfep?tGJ9%zGX5J4B`J7${_7K5^{H)8j;5xYo=lzO8<?Q4UYS2VHHq>(Bp zq~pkjsdpL~sj@mp*`4$`uP!P|$7G2V3`)M$sP<qx)iz8C`JI3EF?$e$|9U3%v^Zm; zrq+WFAg%D^VNVEiZecVVza$N2zgJofRv&0A5T(&!Y%#~<J>WzdJP(2^pH1G&vhj2x ziE<*Ejk5G`Cq^^r+$5WiO-(rw@WKOc55U1dU0Mg$btD0JyZ|rV;68L7GVs(7?xLXJ zKMQO@Cj_A4N0zsg$FVOObGUt=au&dGlCGs`q8qZN)|FkT3{YiUD=x?i8Bv5e16@Pq z6uxnnlDe+xuju_W-aRE83XHPx&p@Q{e&T)Y4>$leU!%BHsSH#<u_#&ljM+sGkG0R5 zu9|na=C!I!&1qGgAL|BtjM;7B;j#8u(^ETPRat7{r6#_nkM5?Ax=DbVaMYx?>E-f& zUVK({xaM;$V+J*stBMaysgkn~HnEam=e(iMHoJl%%-{>u+sS~P^V#Fxl`p2--VSX$ zzxzLY%j{B<`%N3)<dFg$to)eSg4Hvy<H#~Q@3PGL*J;Q4?`a0s-T=l3A`h1x#1RQZ z8o?uICmtiph*gA2h{Q><XktGEeF<>(iBRN-DghL*9US&g)0^u0MG{P*p_f%-u~3jj znW!d*i&8@uv#23@(?G<&3Z^`Zz8)R-y)WD!?!yGG!VX~iEW(TMAp(f=hzp2I2o7;2 J%YaP__ZPC4BAWmJ literal 0 HcmV?d00001 diff --git a/resources/libraries/deskew/Imaging/LibTiff/ImagingTiffLib.pas b/resources/libraries/deskew/Imaging/LibTiff/ImagingTiffLib.pas new file mode 100755 index 0000000..8cf1094 --- /dev/null +++ b/resources/libraries/deskew/Imaging/LibTiff/ImagingTiffLib.pas @@ -0,0 +1,663 @@ +{ + Vampyre Imaging Library + by Marek Mauder + http://imaginglib.sourceforge.net + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +{ This unit contains image format loader/saver for TIFF images + using LibTiff C library compiled to object files or LibTiff DLL/SO. + + Supported platforms/compilers are now: + Win32 Delphi: obj, dll + Win64 Delphi: dll + Win32, Win64 FPC: obj, dll + Linux/Unix/macOS 32/64 FPC: dll +} +unit ImagingTiffLib; + +{$I ImagingOptions.inc} + +{$IF Defined(LINUX) or Defined(BSD) or Defined(MACOS)} + // Use LibTiff dynamic library in Linux/BSD instead of precompiled objects. + // It's installed on most systems so let's use it and keep the binary smaller. + // In macOS it's usually not installed but if it is let's use it. + {$DEFINE USE_DYN_LIB} +{$IFEND} + +{$IF Defined(POSIX) and Defined(CPUX64)} + // Workaround for problem on 64bit Linux where thandle_t in libtiff is + // still 32bit so it cannot be used to pass pointers (for IO functions). + {$DEFINE HANDLE_NOT_POINTER_SIZED} +{$IFEND} + +{.$DEFINE USE_DYN_LIB} + +interface + +uses + SysUtils, Imaging, ImagingTypes, ImagingUtility, ImagingIO, + ImagingTiff, +{$IFDEF USE_DYN_LIB} + LibTiffDynLib; +{$ELSE} + LibTiffDelphi; +{$ENDIF} + +type + { TIFF (Tag Image File Format) loader/saver class. Uses LibTiff so + it can handle most types of TIFF files.} + TTiffLibFileFormat = class(TBaseTiffFileFormat) + protected + procedure Define; override; + function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; + OnlyFirstLevel: Boolean): Boolean; override; + function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; + Index: Integer): Boolean; override; + procedure ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); override; + end; + +implementation + +const + TiffSupportedFormats: TImageFormats = [ifIndex8, ifGray8, ifA8Gray8, + ifGray16, ifA16Gray16, ifGray32, ifR8G8B8, ifA8R8G8B8, ifR16G16B16, + ifA16R16G16B16, ifR32F, ifA32R32G32B32F, ifR16F, ifA16R16G16B16F, ifBinary]; + +type + TTiffIOWrapper = record + IO: TIOFunctions; + Handle: TImagingHandle; + end; + PTiffIOWrapper = ^TTiffIOWrapper; + +{$IFDEF HANDLE_NOT_POINTER_SIZED} +var + TiffIOWrapper: TTiffIOWrapper; +{$ENDIF} + +function GetTiffIOWrapper(Fd: THandle): PTiffIOWrapper; +begin +{$IFDEF HANDLE_NOT_POINTER_SIZED} + Result := @TiffIOWrapper; +{$ELSE} + Result := PTiffIOWrapper(Fd); +{$ENDIF} +end; + +function TIFFReadProc(Fd: THandle; Buffer: Pointer; Size: Integer): Integer; cdecl; +var + Wrapper: PTiffIOWrapper; +begin + Wrapper := GetTiffIOWrapper(Fd); + Result := Wrapper.IO.Read(Wrapper.Handle, Buffer, Size); +end; + +function TIFFWriteProc(Fd: THandle; Buffer: Pointer; Size: Integer): Integer; cdecl; +var + Wrapper: PTiffIOWrapper; +begin + Wrapper := GetTiffIOWrapper(Fd); + Result := Wrapper.IO.Write(Wrapper.Handle, Buffer, Size); +end; + +function TIFFSizeProc(Fd: THandle): toff_t; cdecl; +var + Wrapper: PTiffIOWrapper; +begin + Wrapper := GetTiffIOWrapper(Fd); + Result := ImagingIO.GetInputSize(Wrapper.IO, Wrapper.Handle); +end; + +function TIFFSeekProc(Fd: THandle; Offset: toff_t; Where: Integer): toff_t; cdecl; +const + SEEK_SET = 0; + SEEK_CUR = 1; + SEEK_END = 2; +var + Mode: TSeekMode; + Wrapper: PTiffIOWrapper; +begin + Wrapper := GetTiffIOWrapper(Fd); + if Offset = $FFFFFFFF then + begin + Result := $FFFFFFFF; + Exit; + end; + case Where of + SEEK_SET: Mode := smFromBeginning; + SEEK_CUR: Mode := smFromCurrent; + SEEK_END: Mode := smFromEnd; + else + Mode := smFromBeginning; + end; + Result := Wrapper.IO.Seek(Wrapper.Handle, Offset, Mode); +end; + +function TIFFCloseProc(Fd: THandle): Integer; cdecl; +begin + Result := 0; +end; + +function TIFFNoMapProc(Fd: THandle; Base: PPointer; Size: PCardinal): Integer; cdecl; +begin + Result := 0; +end; + +procedure TIFFNoUnmapProc(Fd: THandle; Base: Pointer; Size: Cardinal); cdecl; +begin +end; + +var + LastError: string = 'None'; + +procedure TIFFErrorHandler(const Module, Message: AnsiString); +begin + LastError := string(Module + ': ' + Message); +end; + +{ + TTiffFileFormat implementation +} + +procedure TTiffLibFileFormat.Define; +begin + inherited; + FFeatures := [ffLoad, ffSave, ffMultiImage]; + FSupportedFormats := TiffSupportedFormats; +end; + +function TTiffLibFileFormat.LoadData(Handle: TImagingHandle; + var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean; +var + Tiff: PTIFF; + IOWrapper: TTiffIOWrapper; + I, Idx, TiffResult, ScanLineSize, NumDirectories, X: Integer; + RowsPerStrip: LongWord; + Orientation, BitsPerSample, SamplesPerPixel, Photometric, + PlanarConfig, SampleFormat: Word; + DataFormat: TImageFormat; + CanAccessScanlines: Boolean; + Ptr: PByte; + Red, Green, Blue: PWordRecArray; + + procedure LoadMetadata(Tiff: PTiff; TiffPage: Integer); + var + TiffResUnit, CompressionScheme: Word; + XRes, YRes: Single; + ResUnit: TResolutionUnit; + CompressionName: string; + begin + TIFFGetFieldDefaulted(Tiff, TIFFTAG_RESOLUTIONUNIT, @TiffResUnit); + TIFFGetFieldDefaulted(Tiff, TIFFTAG_XRESOLUTION, @XRes); + TIFFGetFieldDefaulted(Tiff, TIFFTAG_YRESOLUTION, @YRes); + TIFFGetFieldDefaulted(Tiff, TIFFTAG_COMPRESSION, @CompressionScheme); + + FMetadata.SetMetaItem(SMetaTiffResolutionUnit, TiffResUnit); + + if (TiffResUnit <> RESUNIT_NONE) and (XRes >= 0.1) and (YRes >= 0.1) then + begin + ResUnit := ruDpi; + if TiffResUnit = RESUNIT_CENTIMETER then + ResUnit := ruDpcm; + FMetadata.SetPhysicalPixelSize(ResUnit, XRes, YRes, False, TiffPage); + end; + + case CompressionScheme of + COMPRESSION_NONE: CompressionName := 'None'; + COMPRESSION_LZW: CompressionName := 'LZW'; + COMPRESSION_JPEG: CompressionName := 'JPEG'; + COMPRESSION_PACKBITS: CompressionName := 'Packbits RLE'; + COMPRESSION_DEFLATE: CompressionName := 'Deflate'; + COMPRESSION_CCITTFAX4: CompressionName := 'CCITT Group 4 Fax'; + COMPRESSION_OJPEG: CompressionName := 'Old JPEG'; + COMPRESSION_CCITTRLE..COMPRESSION_CCITTFAX3: CompressionName := 'CCITT'; + else + CompressionName := 'Unknown'; + end; + + FMetadata.SetMetaItem(SMetaTiffCompressionName, CompressionName); + end; + +begin + Result := False; + SetUserMessageHandlers(TIFFErrorHandler, nil); + + // Set up IO wrapper and open TIFF + IOWrapper.IO := GetIO; + IOWrapper.Handle := Handle; +{$IFDEF HANDLE_NOT_POINTER_SIZED} + TiffIOWrapper := IOWrapper; +{$ENDIF} + + Tiff := TIFFClientOpen('LibTIFF', 'r', THandle(@IOWrapper), @TIFFReadProc, + @TIFFWriteProc, @TIFFSeekProc, @TIFFCloseProc, + @TIFFSizeProc, @TIFFNoMapProc, @TIFFNoUnmapProc); + + if Tiff <> nil then + TIFFSetFileNo(Tiff, THandle(@IOWrapper)) + else + Exit; + + NumDirectories := TIFFNumberOfDirectories(Tiff); + if OnlyFirstLevel then + NumDirectories := Min(1, NumDirectories); + + SetLength(Images, NumDirectories); + + for Idx := 0 to NumDirectories - 1 do + begin + TIFFSetDirectory(Tiff, Idx); + + // Set defaults for TIFF fields + DataFormat := ifUnknown; + + // Read some TIFF fields with basic image info + TIFFGetField(Tiff, TIFFTAG_IMAGEWIDTH, @Images[Idx].Width); + TIFFGetField(Tiff, TIFFTAG_IMAGELENGTH, @Images[Idx].Height); + TIFFGetFieldDefaulted(Tiff, TIFFTAG_ORIENTATION, @Orientation); + TIFFGetFieldDefaulted(Tiff, TIFFTAG_BITSPERSAMPLE, @BitsPerSample); + TIFFGetFieldDefaulted(Tiff, TIFFTAG_SAMPLESPERPIXEL, @SamplesPerPixel); + TIFFGetFieldDefaulted(Tiff, TIFFTAG_SAMPLEFORMAT, @SampleFormat); + TIFFGetFieldDefaulted(Tiff, TIFFTAG_PHOTOMETRIC, @Photometric); + TIFFGetFieldDefaulted(Tiff, TIFFTAG_PLANARCONFIG, @PlanarConfig); + TIFFGetFieldDefaulted(Tiff, TIFFTAG_ROWSPERSTRIP, @RowsPerStrip); + + // Load supported metadata + LoadMetadata(Tiff, Idx); + // See if we can just copy scanlines from TIFF to Imaging image + CanAccessScanlines := (PlanarConfig = PLANARCONFIG_CONTIG) or (SamplesPerPixel = 1); + + if CanAccessScanlines then + begin + // We can copy scanlines so we try to find data format that best matches + // TIFFs internal data format + if (Photometric = PHOTOMETRIC_MINISBLACK) or (Photometric = PHOTOMETRIC_MINISWHITE) then + begin + if SampleFormat = SAMPLEFORMAT_UINT then + begin + case BitsPerSample of + 1: + if SamplesPerPixel = 1 then + DataFormat := ifBinary; + 8: + case SamplesPerPixel of + 1: DataFormat := ifGray8; + 2: DataFormat := ifA8Gray8; + end; + 16: + case SamplesPerPixel of + 1: DataFormat := ifGray16; + 2: DataFormat := ifA16Gray16; + end; + 32: + if SamplesPerPixel = 1 then + DataFormat := ifGray32; + end; + end + else if SampleFormat = SAMPLEFORMAT_IEEEFP then + begin + case BitsPerSample of + 16: + if SamplesPerPixel = 1 then + DataFormat := ifR16F; + 32: + if SamplesPerPixel = 1 then + DataFormat := ifR32F; + end; + end; + end + else if Photometric = PHOTOMETRIC_RGB then + begin + if SampleFormat = SAMPLEFORMAT_UINT then + begin + case BitsPerSample of + 8: + case SamplesPerPixel of + 3: DataFormat := ifR8G8B8; + 4: DataFormat := ifA8R8G8B8; + end; + 16: + case SamplesPerPixel of + 3: DataFormat := ifR16G16B16; + 4: DataFormat := ifA16R16G16B16; + end; + end; + end + else if SampleFormat = SAMPLEFORMAT_IEEEFP then + begin + case BitsPerSample of + 16: + if SamplesPerPixel = 4 then + DataFormat := ifA16R16G16B16F; + 32: + if SamplesPerPixel = 4 then + DataFormat := ifA32R32G32B32F; + end; + end; + end + else if Photometric = PHOTOMETRIC_PALETTE then + begin + if (SamplesPerPixel = 1) and (SampleFormat = SAMPLEFORMAT_UINT) and (BitsPerSample = 8) then + DataFormat := ifIndex8 + end; + end; + + if DataFormat = ifUnknown then + begin + // Use RGBA interface to read A8R8G8B8 TIFFs and mainly TIFFs in various + // formats with no Imaging equivalent, exotic color spaces etc. + NewImage(Images[Idx].Width, Images[Idx].Height, ifA8R8G8B8, Images[Idx]); + TiffResult := TIFFReadRGBAImageOriented(Tiff, Images[Idx].Width, Images[Idx].Height, + Images[Idx].Bits, Orientation, 0); + if TiffResult = 0 then + RaiseImaging(LastError, []); + // Swap Red and Blue, if YCbCr. + if Photometric=PHOTOMETRIC_YCBCR then + SwapChannels(Images[Idx], ChannelRed, ChannelBlue); + end + else + begin + // Create new image in given format and read scanlines from TIFF, + // read palette too if needed + NewImage(Images[Idx].Width, Images[Idx].Height, DataFormat, Images[Idx]); + ScanLineSize := TIFFScanlineSize(Tiff); + + for I := 0 to Images[Idx].Height - 1 do + TIFFReadScanline(Tiff, @PByteArray(Images[Idx].Bits)[I * ScanLineSize], I, 0); + + if DataFormat = ifIndex8 then + begin + TIFFGetField(Tiff, TIFFTAG_COLORMAP, @Red, @Green, @Blue); + for I := 0 to 255 do + with Images[Idx].Palette[I] do + begin + A := 255; + R := Red[I].High; + G := Green[I].High; + B := Blue[I].High; + end; + end; + + // TIFF uses BGR order so we must swap it (but not images we got + // from TiffLib RGBA interface) + if Photometric = PHOTOMETRIC_RGB then + SwapChannels(Images[Idx], ChannelRed, ChannelBlue); + + // We need to negate 'MinIsWhite' formats to get common grayscale + // formats where min sample value is black + if Photometric = PHOTOMETRIC_MINISWHITE then + for I := 0 to Images[Idx].Height - 1 do + begin + Ptr := @PByteArray(Images[Idx].Bits)[I * ScanLineSize]; + for X := 0 to ScanLineSize - 1 do + begin + Ptr^ := not Ptr^; + Inc(Ptr); + end; + end; + end; + end; + + TIFFClose(Tiff); + Result := True; +end; + +function TTiffLibFileFormat.SaveData(Handle: TImagingHandle; + const Images: TDynImageDataArray; Index: Integer): Boolean; +const + Compressions: array[0..5] of Word = (COMPRESSION_NONE, COMPRESSION_LZW, + COMPRESSION_PACKBITS, COMPRESSION_DEFLATE, COMPRESSION_JPEG, COMPRESSION_CCITTFAX4); +var + Tiff: PTIFF; + IOWrapper: TTiffIOWrapper; + I, J, ScanLineSize: Integer; + ImageToSave: TImageData; + MustBeFreed: Boolean; + Info: TImageFormatInfo; + Orientation, BitsPerSample, SamplesPerPixel, Photometric, + PlanarConfig, SampleFormat, CompressionScheme: Word; + RowsPerStrip: LongWord; + Red, Green, Blue: array[Byte] of TWordRec; + CompressionMismatch: Boolean; + OpenMode: PAnsiChar; + + procedure SaveMetadata(Tiff: PTiff; TiffPage: Integer); + var + XRes, YRes: Single; + ResUnit: TResolutionUnit; + TiffResUnit, StoredTiffResUnit: Word; + begin + XRes := -1; + YRes := -1; + + ResUnit := ruDpcm; + TiffResUnit := RESUNIT_CENTIMETER; + + if FMetadata.HasMetaItemForSaving(SMetaTiffResolutionUnit) then + begin + // Check if DPI resolution unit is requested to be used (e.g. to + // use the same unit when just resaving files - also some ) + StoredTiffResUnit := FMetadata.MetaItemsForSaving[SMetaTiffResolutionUnit]; + if StoredTiffResUnit = RESUNIT_INCH then + begin + ResUnit := ruDpi; + TiffResUnit := RESUNIT_INCH; + end; + end; + + // First try to find phys. size for current TIFF page index. If not found then + // try size for main image (index 0). + if not FMetadata.GetPhysicalPixelSize(ResUnit, XRes, YRes, True, TiffPage) then + FMetadata.GetPhysicalPixelSize(ResUnit, XRes, YRes, True, 0); + + if (XRes > 0) and (YRes > 0) then + begin + TIFFSetField(Tiff, TIFFTAG_RESOLUTIONUNIT, TiffResUnit); + TIFFSetField(Tiff, TIFFTAG_XRESOLUTION, XRes); + TIFFSetField(Tiff, TIFFTAG_YRESOLUTION, YRes); + end; + end; + +begin + Result := False; + SetUserMessageHandlers(TIFFErrorHandler, nil); + + if not (FCompression in [0..5]) then + FCompression := COMPRESSION_LZW; + + // Set up IO wrapper and open TIFF + IOWrapper.IO := GetIO; + IOWrapper.Handle := Handle; +{$IFDEF HANDLE_NOT_POINTER_SIZED} + TiffIOWrapper := IOWrapper; +{$ENDIF} + + OpenMode := 'w'; + + Tiff := TIFFClientOpen('LibTIFF', OpenMode, THandle(@IOWrapper), @TIFFReadProc, + @TIFFWriteProc, @TIFFSeekProc, @TIFFCloseProc, + @TIFFSizeProc, @TIFFNoMapProc, @TIFFNoUnmapProc); + + if Tiff <> nil then + TIFFSetFileNo(Tiff, THandle(@IOWrapper)) + else + Exit; + + for I := FFirstIdx to FLastIdx do + begin + if MakeCompatible(Images[I], ImageToSave, MustBeFreed) then + with GetIO, ImageToSave do + try + GetImageFormatInfo(Format, Info); + + // Set Tag values + Orientation := ORIENTATION_TOPLEFT; + BitsPerSample := Info.BytesPerPixel div Info.ChannelCount * 8; + if Info.Format = ifBinary then + BitsPerSample := 1; + SamplesPerPixel := Info.ChannelCount; + SampleFormat := Iff(not Info.IsFloatingPoint, SAMPLEFORMAT_UINT, SAMPLEFORMAT_IEEEFP); + PlanarConfig := PLANARCONFIG_CONTIG; + CompressionScheme := Compressions[FCompression]; + + // Check if selected compression scheme can be used for current image + CompressionMismatch := (CompressionScheme = COMPRESSION_JPEG) and ((BitsPerSample <> 8) or + not (SamplesPerPixel in [1, 3]) or Info.IsIndexed or Info.IsFloatingPoint); + CompressionMismatch := CompressionMismatch or ((CompressionScheme = COMPRESSION_CCITTFAX4) and (Info.Format <> ifBinary)); + if CompressionMismatch then + CompressionScheme := COMPRESSION_LZW; + // If we have some compression scheme selected and it's not Fax then select it automatically - better comp ratios! + if (Info.Format = ifBinary) and (CompressionScheme <> COMPRESSION_NONE) and (CompressionScheme <> COMPRESSION_CCITTFAX4) then + CompressionScheme := COMPRESSION_CCITTFAX4; + + RowsPerStrip := TIFFDefaultStripSize(Tiff, Height); + if Info.IsIndexed then + Photometric := PHOTOMETRIC_PALETTE + else if (Info.HasGrayChannel) or (Info.ChannelCount = 1) then + Photometric := PHOTOMETRIC_MINISBLACK + else + Photometric := PHOTOMETRIC_RGB; + + // Write tags + TIFFSetField(Tiff, TIFFTAG_IMAGEWIDTH, Width); + TIFFSetField(Tiff, TIFFTAG_IMAGELENGTH, Height); + TIFFSetField(Tiff, TIFFTAG_PHOTOMETRIC, Photometric); + TIFFSetField(Tiff, TIFFTAG_PLANARCONFIG, PlanarConfig); + TIFFSetField(Tiff, TIFFTAG_ORIENTATION, Orientation); + TIFFSetField(Tiff, TIFFTAG_BITSPERSAMPLE, BitsPerSample); + TIFFSetField(Tiff, TIFFTAG_SAMPLESPERPIXEL, SamplesPerPixel); + TIFFSetField(Tiff, TIFFTAG_SAMPLEFORMAT, SampleFormat); + TIFFSetField(Tiff, TIFFTAG_COMPRESSION, CompressionScheme); + if CompressionScheme = COMPRESSION_JPEG then + TIFFSetField(Tiff, TIFFTAG_JPEGQUALITY, FJpegQuality); + TIFFSetField(Tiff, TIFFTAG_ROWSPERSTRIP, RowsPerStrip); + // Save supported metadata + SaveMetadata(Tiff, I); + + if Format = ifIndex8 then + begin + // Set paletee for indexed images + for J := 0 to 255 do + with ImageToSave.Palette[J] do + begin + Red[J].High := R; + Green[J].High := G; + Blue[J].High := B; + end; + TIFFSetField(Tiff, TIFFTAG_COLORMAP, @Red[0], @Green[0], @Blue[0]); + end; + + ScanLineSize := Info.GetPixelsSize(Info.Format, Width, 1); + + if Photometric = PHOTOMETRIC_RGB then + SwapChannels(ImageToSave, ChannelRed, ChannelBlue); + // Write image scanlines and then directory for current image + for J := 0 to Height - 1 do + TIFFWriteScanline(Tiff, @PByteArray(Bits)[J * ScanLineSize], J, 0); + if Info.ChannelCount > 1 then + SwapChannels(ImageToSave, ChannelRed, ChannelBlue); + + TIFFWriteDirectory(Tiff); + finally + if MustBeFreed then + FreeImage(ImageToSave); + end; + end; + + TIFFClose(Tiff); + Result := True; +end; + +procedure TTiffLibFileFormat.ConvertToSupported(var Image: TImageData; + const Info: TImageFormatInfo); +var + ConvFormat: TImageFormat; +begin + if Info.RBSwapFormat in GetSupportedFormats then + ConvFormat := Info.RBSwapFormat + else if Info.IsFloatingPoint then + ConvFormat := IffFormat(Info.ChannelCount = 1, ifR32F, ifA32R32G32B32F) + else if Info.HasGrayChannel then + ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16Gray16, ifGray32) + else + ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifR8G8B8); + + ConvertImage(Image, ConvFormat); +end; + +initialization +{$IFDEF USE_DYN_LIB} + // If using dynamic library only register the format if + // the library loads successfully. + if LibTiffDynLib.LoadTiffLibrary then +{$ENDIF} + RegisterImageFileFormat(TTiffLibFileFormat); + +{ + File Notes: + + -- TODOS ---------------------------------------------------- + - nothing now + + -- 0.77.3 ---------------------------------------------------- + - Lot more platforms than just 32bit Delphi supported now. + - Workaround for problem on 64bit Linux where thandle_t in libtiff is + still 32bit so it cannot be used to pass pointers (for IO functions). + - Support for libtiff as DLL/SO instead of linking object files to exe. + Useful for platforms like Linux where libtiff is already installed + most of the time (and exe could be make smaller not linking the objects). + - Removed problematic append mode. + - Renamed and refactored to be based on common Tiff base class + (for shared stuff between other Tiff implementations (WIC, Quartz)). + + -- 0.77.1 ---------------------------------------------------- + - Renamed unit to ImagingLibTiffDelphi since there will be more + Tiff implementations in the future, cleaned up interface units + and obj file a little bit. + - Updated LibTiff to version 3.9.4 and added EXIF tag support. + - Added TIFF Append mode: when saving existing files are not + overwritten but images are appended to TIFF instead. + - Images in ifBinary format are now supported for loading/saving + (optional Group 4 fax encoding added). + - PHOTOMETRIC_MINISWHITE is now properly read as Grayscale/Binary + instead of using unefficient RGBA interface. + + -- 0.26.5 Changes/Bug Fixes --------------------------------- + - Fix: All pages of multipage TIFF were loaded even when + OnlyFirstLevel was True. + - Loading and saving of physical resolution metadata. + - Unicode compatibility fixes in LibTiffDelphi. + - Added Jpeg compression quality setting. + + -- 0.24.3 Changes/Bug Fixes --------------------------------- + - Fixed bug in loading and saving of 2 channel images - Imaging + tried to swap R and B channels here. + + -- 0.23 Changes/Bug Fixes ----------------------------------- + - Added TIFF loading and saving. + - Unit created and initial code added. +} + +end. diff --git a/resources/libraries/deskew/Imaging/LibTiff/LibDelphi.pas b/resources/libraries/deskew/Imaging/LibTiff/LibDelphi.pas new file mode 100755 index 0000000..e9daa13 --- /dev/null +++ b/resources/libraries/deskew/Imaging/LibTiff/LibDelphi.pas @@ -0,0 +1,214 @@ +unit LibDelphi; + +{$ifdef FPC} + {$MODE OBJFPC} +{$endif} + +interface + +uses + SysUtils; + +type + va_list = Pointer; + +{$IFNDEF FPC} +{$IF CompilerVersion <= 18.5} + SizeInt = Integer; + PtrUInt = LongWord; +{$ELSE} + SizeInt = NativeInt; + PtrUInt = NativeUInt; +{$IFEND} +{$ENDIF} + +const +{$IFDEF MSWINDOWS} + SRuntimeLib = 'msvcrt.dll'; +{$ELSE} + SRuntimeLib = 'libc.so'; +{$ENDIF} + +function fprintf(stream: Pointer; format: Pointer; arguments: va_list): Integer; cdecl; {$ifdef FPC}[public];{$endif} +function sprintf(buffer: Pointer; format: Pointer; arguments: va_list): Integer; cdecl; {$ifdef FPC}[public];{$endif} +function snprintf(buffer: Pointer; n: Integer; format: Pointer; arguments: va_list): Integer; cdecl; {$ifdef FPC}[public];{$endif} +function fputs(s: Pointer; stream: Pointer): Integer; cdecl; external SRuntimeLib; +function fputc(c: Integer; stream: Pointer): Integer; cdecl; external SRuntimeLib; +function isprint(c: Integer): Integer; cdecl; external SRuntimeLib; +procedure memset(a: Pointer; b: Integer; c: SizeInt); cdecl; {$ifdef FPC}[public];{$endif} +function memcpy(dest: Pointer; const src: Pointer; count: SizeInt): Pointer; cdecl; {$ifdef FPC}[public];{$endif} +function memcmp(a, b: Pointer; c:SizeInt):Integer; cdecl; {$ifdef FPC}[public];{$endif} +function malloc(s: Longint): Pointer; cdecl; {$ifdef FPC}[public];{$endif} +procedure free(p: Pointer); cdecl; {$ifdef FPC}[public];{$endif} +{$ifndef FPC} +function _ftol: Integer; cdecl; external SRuntimeLib; +function _ltolower(ch: Integer): Integer; cdecl; external SRuntimeLib; +function _ltoupper(ch: Integer): Integer; cdecl; external SRuntimeLib; +function _ltowlower(ch: Integer): Integer; cdecl; external SRuntimeLib; +function _ltowupper(ch: Integer): Integer; cdecl; external SRuntimeLib; +function fwrite(ptr:pointer; size, count:SizeInt; stream:pointer ):SizeInt; cdecl; external SRuntimeLib; +{$endif} +function strcpy(dest: Pointer; src: Pointer): Pointer; cdecl; {$ifdef FPC}[public];{$endif} + +{$ifdef FPC} +function fwrite(ptr:pointer; size, count:SizeInt; stream:pointer ):SizeInt; cdecl; {$ifdef FPC}[public];{$endif} +function __udivdi3(a,b:int64):int64; cdecl; [public]; +function {$ifdef CPUX86}_imp__isprint{$else}__imp_isprint{$endif}(c: char): integer; cdecl; [public]; +{$endif} + +{$ifndef FPC} +var + __turboFloat: LongBool = False; + _streams: Integer; + +{$else} +type + // from mingw - stdio.h + cIoBuf = record + _ptr:Pointer; + _cnt:LongInt; + _base:Pointer; + _flag:LongInt; + _file:LongInt; + _charbuf:LongInt; + _bufsiz:LongInt; + _tmpfname:Pointer; + end; + pIoBuf = ^cIoBuf; + +var + _imp___iob:array[0..2] of cIoBuf; cvar; // stdin,stdout,stderr + iob:pIoBuf; cvar; +{$endif} + +implementation + +{$ifndef FPC} +uses + Windows; +{$endif} + +{$ifdef FPC} +function __udivdi3(a, b: int64): int64; cdecl; +begin + Result:=a div b; +end; +function {$ifdef CPUX86}_imp__isprint{$else}__imp_isprint{$endif}(c: char): integer; cdecl; +begin + if (c>=#32)and(c<=#127) then + Result:=1 + else + Result:=0; +end; +{$endif} + +procedure free(p: Pointer); cdecl; +begin + FreeMem(p); +end; + +function malloc(s: Longint): Pointer; cdecl; +begin + Result := AllocMem(s); +end; + +function memcpy(dest: Pointer; const src: Pointer; count: SizeInt): Pointer; cdecl; +begin + system.Move(src^,dest^,count); + Result:=dest; +end; + +procedure memset(a: Pointer; b: Integer; c: SizeInt); cdecl; +begin + system.FillChar(a^,c,b); +end; + +function memcmp(a, b: Pointer; c: SizeInt): Integer; cdecl; {$ifdef FPC}[public];{$endif} +{$ifndef FPC} +var + ma,mb: PByte; + n: Integer; +begin + ma:=a; + mb:=b; + n:=0; + while Cardinal(n)<c do + begin + if ma^<>mb^ then + begin + if ma^<mb^ then + Result:=-1 + else + Result:=1; + exit; + end; + Inc(ma); + Inc(mb); + Inc(n); + end; + Result:=0; +{$else} +begin + Result:=CompareMemRange(a,b,c); +{$endif} +end; + +function __sprintf(buffer: Pointer; format: Pointer; arguments: Pointer): Integer; cdecl; external SRuntimeLib name 'sprintf'; + +function sprintf(buffer: Pointer; format: Pointer; arguments: Pointer): Integer; cdecl; +begin + Result := __sprintf(buffer, format, arguments); +end; + +function __snprintf(buffer: Pointer; n: Integer; format: Pointer; arguments: va_list): Integer; cdecl; external SRuntimeLib name '_snprintf'; + +function snprintf(buffer: Pointer; n: Integer; format: Pointer; arguments: va_list): Integer; cdecl; +begin + Result := __snprintf(buffer, n, format, arguments); +end; + +function fprintf(stream: Pointer; format: Pointer; arguments: va_list): Integer; cdecl; +var + m: Integer; + n: Pointer; +{$ifndef FPC} + o: Cardinal; +{$endif} +begin + m:=sprintf(nil,format,@arguments); + n:=AllocMem(m); + sprintf(n,format,@arguments); + {$ifndef FPC} + WriteFile(Cardinal(stream),n^,Cardinal(m),o,nil); + {$else} + FileWrite(pIoBuf(stream)^._file,n^,Cardinal(m)); + {$endif} + FreeMem(n); + Result := m; +end; + +function strcpy(dest: Pointer; src: Pointer): Pointer; cdecl; +begin + Result:=SysUtils.strcopy(PAnsiChar(dest),PAnsiChar(src)); +end; + +{$ifdef FPC} +function fwrite(ptr: pointer; size, count: SizeInt; stream: pointer): SizeInt; cdecl; +begin + Result:=FileWrite(pIoBuf(stream)^._file,ptr^,size * count); +end; + +procedure init_iob; +begin + FillChar(_imp___iob[0],sizeof(cIoBuf)*3,0); + _imp___iob[0]._file:=StdInputHandle; + _imp___iob[1]._file:=StdOutputHandle; + _imp___iob[2]._file:=StdErrorHandle; + iob:=@_imp___iob[0]; +end; + +initialization + init_iob; +{$endif} + +end. diff --git a/resources/libraries/deskew/Imaging/LibTiff/LibJpegDelphi.pas b/resources/libraries/deskew/Imaging/LibTiff/LibJpegDelphi.pas new file mode 100755 index 0000000..3326551 --- /dev/null +++ b/resources/libraries/deskew/Imaging/LibTiff/LibJpegDelphi.pas @@ -0,0 +1,548 @@ +unit LibJpegDelphi; + +{$IFDEF FPC} + {$MODE OBJFPC} +{$ELSE} + {$DEFINE DCC} +{$ENDIF} + +interface + +uses + SysUtils; + +const + + {$IFNDEF FPC} + JPEG_LIB_VERSION = 62; { Version 6b } + {$ELSE} + JPEG_LIB_VERSION = 80; { Version 80 } + {$ENDIF} + + JMSG_STR_PARM_MAX = 80; + JMSG_LENGTH_MAX = 200; { recommended size of format_message buffer } + NUM_QUANT_TBLS = 4; { Quantization tables are numbered 0..3 } + NUM_HUFF_TBLS = 4; { Huffman tables are numbered 0..3 } + NUM_ARITH_TBLS = 16; { Arith-coding tables are numbered 0..15 } + MAX_COMPS_IN_SCAN = 4; { JPEG limit on # of components in one scan } + C_MAX_BLOCKS_IN_MCU = 10; { compressor's limit on blocks per MCU } + D_MAX_BLOCKS_IN_MCU = 10; { decompressor's limit on blocks per MCU } + DCTSIZE2 = 64; + + JCS_UNKNOWN = 0; { error/unspecified } + JCS_GRAYSCALE = 1; { monochrome } + JCS_RGB = 2; { red/green/blue } + JCS_YCbCr = 3; { Y/Cb/Cr (also known as YUV) } + JCS_CMYK = 4; { C/M/Y/K } + JCS_YCCK = 5; { Y/Cb/Cr/K } + +type + + PRJpegErrorMgr = ^RJpegErrorMgr; + PRJpegMemoryMgr = Pointer; + PRJpegProgressMgr = Pointer; + PRJpegDestinationMgr = ^RJpegDestinationMgr; + PRJpegSourceMgr = ^RJpegSourceMgr; + PRJpegComponentInfo = ^RJpegComponentInfo; + PRJpegQuantTbl = Pointer; + PRJpegHuffTbl = Pointer; + PRJpegScanInfo = Pointer; + PRJpegCompMaster = Pointer; + PRJpegCMainController = Pointer; + PRJpegCPrepController = Pointer; + PRJpegCCoefController = Pointer; + PRJpegMarkerWriter = Pointer; + PRJpegColorConverter = Pointer; + PRJpegDownsampler = Pointer; + PRJpegForwardDct = Pointer; + PRJpegEntropyEncoder = Pointer; + PRJpegSavedMarker = Pointer; + PRJpegDecompMaster = Pointer; + PRJpegDMainController = Pointer; + PRJpegDCoefController = Pointer; + PRJpegDPosController = Pointer; + PRJpegInputController = Pointer; + PRJpegMarkerReader = Pointer; + PRJpegEntropyDecoder = Pointer; + PRJpegInverseDct = Pointer; + PRJpegUpsampler = Pointer; + PRJpegColorDeconverter = Pointer; + PRJpegColorQuantizer = Pointer; + PRJpegCommonStruct = ^RJpegCommonStruct; + PRJpegCompressStruct = ^RJpegCompressStruct; + PRJpegDecompressStruct = ^RJpegDecompressStruct; + + TJpegErrorExit = procedure(cinfo: PRJpegCommonStruct); cdecl; + TJpegEmitMessage = procedure(cinfo: PRJpegCommonStruct; MsgLevel: Integer); cdecl; + TJpegOutputMessage = procedure(cinfo: PRJpegCommonStruct); cdecl; + TJpegFormatMessage = procedure(cinfo: PRJpegCommonStruct; Buffer: Pointer); cdecl; + TJpegResetErrorMgr = procedure(cinfo: PRJpegCommonStruct); cdecl; + + RJpegErrorMgrMsgParm = record + case Boolean of + False: (MsgParmI: array[0..7] of Integer); + True: (MsgParmS: array[0..JMSG_STR_PARM_MAX-1] of AnsiChar); + end; + + RJpegErrorMgr = record + ErrorExit: TJpegErrorExit; { Error exit handler: does not return to caller } + EmitMessage: TJpegEmitMessage; { Conditionally emit a trace or warning message } + OutputMessage: TJpegOutputMessage; { Routine that actually outputs a trace or error message } + FormatMessage: TJpegFormatMessage; { Format a message string for the most recent JPEG error or message } + ResetErrorMgr: TJpegResetErrorMgr; { Reset error state variables at start of a new image } + { The message ID code and any parameters are saved here. A message can have one string parameter or up to 8 int parameters. } + MsgCode: Integer; + MsgParm: RJpegErrorMgrMsgParm; + { Standard state variables for error facility } + TraceLevel: Integer; {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. } + NumWarnings: Integer; { 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. } + JpegMessageTable: PPAnsiChar; { Library errors } + LastJpegMessage: Integer; { 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. } + AddonMessageTable: PPAnsiChar; { Non-library errors } + FirstAddonMessage: Integer; { code for first string in addon table } + LastAddonMessage: Integer; { code for last string in addon table } + end; + + TJpegInitDestination = procedure(cinfo: PRJpegCompressStruct); cdecl; + TJpegEmptyOutputBuffer = function(cinfo: PRJpegCompressStruct): Boolean; cdecl; + TJpegTermDestination = procedure(cinfo: PRJpegCompressStruct); cdecl; + + RJpegDestinationMgr = record + NextOutputByte: Pointer; { => next byte to write in buffer } + FreeInBuffer: Cardinal; { # of byte spaces remaining in buffer } + InitDestination: TJpegInitDestination; + EmptyOutputBuffer: TJpegEmptyOutputBuffer; + TermDestination: TJpegTermDestination; + end; + + TJpegInitSource = procedure(cinfo: PRJpegDecompressStruct); cdecl; + TJpegFillInputBuffer = function(cinfo: PRJpegDecompressStruct): Boolean; cdecl; + TJpegSkipInputData = procedure(cinfo: PRJpegDecompressStruct; NumBytes: Integer); cdecl; + TJpegResyncToRestart = function(cinfo: PRJpegDecompressStruct; Desired: Integer): Boolean; cdecl; + TJpegTermSource = procedure(cinfo: PRJpegDecompressStruct); cdecl; + + RJpegSourceMgr = record + NextInputByte: Pointer; + BytesInBuffer: Cardinal; + InitSource: TJpegInitSource; + FillInputBuffer: TJpegFillInputBuffer; + SkipInputData: TJpegSkipInputData; + ResyncToRestart: TJpegResyncToRestart; + TermSource: TJpegTermSource; + end; + + RJpegComponentInfo = record + { Basic info about one component (color channel). } + { 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. } + ComponentId: Integer; { identifier for this component (0..255) } + ComponentIndex: Integer; { its index in SOF or cinfo->comp_info[] } + HSampFactor: Integer; { horizontal sampling factor (1..4) } + VSampFactor: Integer; { vertical sampling factor (1..4) } + QuantTblNo: Integer; { 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. } + DcTblNo: Integer; { DC entropy table selector (0..3) } + AsTblNo: Integer; { 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. } + WidthInBlocks: Cardinal; + HeightInBlocks: Cardinal; + { 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. } + DctScaledSize: Integer; + { 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) } + DownsampledWidth: Cardinal; { actual width in samples } + DownsampledHeight: Cardinal; { 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. } + ComponentNeeded: Boolean; { 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. } + McuWidth: Integer; { number of blocks per MCU, horizontally } + McuHeight: Integer; { number of blocks per MCU, vertically } + McuBlocks: Integer; { MCU_width * MCU_height } + McuSampleWidth: Integer; { MCU width in samples, MCU_width*DCT_scaled_size } + LastColWidth: Integer; { # of non-dummy blocks across in last MCU } + LastRowHeight: Integer; { # 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. } + QuantTable: PRJpegQuantTbl; + { Private per-component storage for DCT or IDCT subsystem. } + DctTable: Pointer; + end; + + RJpegCommonStruct = record + Err: PRJpegErrorMgr; { Error handler module } + Mem: PRJpegMemoryMgr; { Memory manager module } + Progress: PRJpegProgressMgr; { Progress monitor, or NULL if none } + ClientData: Pointer; { Available for use by application } + IsDecompressor: Boolean; { So common code can tell which is which } + GlobalState: Integer; { For checking call sequence validity } + end; + + RJpegCompressStruct = record + Err: PRJpegErrorMgr; { Error handler module } + Mem: PRJpegMemoryMgr; { Memory manager module } + Progress: PRJpegProgressMgr; { Progress monitor, or NULL if none } + ClientData: Pointer; { Available for use by application } + IsDecompressor: Boolean; { So common code can tell which is which } + GlobalState: Integer; { For checking call sequence validity } + { Destination for compressed data } + Dest: PRJpegDestinationMgr; + { 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(). } + ImageWidth: Cardinal; { input image width } + ImageHeight: Cardinal; { input image height } + InputComponents: Integer; { # of color components in input image } + InColorSpace: Integer; { colorspace of input image } + InputGamme: Double; { 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. } + DataPrecision: Integer; { bits of precision in image data } + NumComponents: Integer; { # of color components in JPEG image } + JpegColorSpace: Integer; { colorspace of JPEG image } + CompInfo: PRJpegComponentInfo; { comp_info[i] describes component that appears i'th in SOF } + QuantTblPtrs: array[0..NUM_QUANT_TBLS-1] of PRJpegQuantTbl; {ptrs to coefficient quantization tables, or NULL if not defined } + DcHuffTblPtrs: array[0..NUM_HUFF_TBLS-1] of PRJpegHuffTbl; {ptrs to Huffman coding tables, or NULL if not defined } + AcHuffTblPtrs: array[0..NUM_HUFF_TBLS-1] of PRJpegHuffTbl; + ArithDcL: array[0..NUM_ARITH_TBLS-1] of Byte; { L values for DC arith-coding tables } + ArithDcU: array[0..NUM_ARITH_TBLS-1] of Byte; { U values for DC arith-coding tables } + ArithAcK: array[0..NUM_ARITH_TBLS-1] of Byte; { Kx values for AC arith-coding tables } + NumScans: Integer; { # of entries in scan_info array } + ScanInfo: PRJpegScanInfo; { 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. } + RawDataIn: Boolean; { TRUE=caller supplies downsampled data } + ArithCode: Boolean; { TRUE=arithmetic coding, FALSE=Huffman } + OptimizeCoding: Boolean; { TRUE=optimize entropy encoding parms } + CCIR601Sampling: Boolean; { TRUE=first samples are cosited } + SmoothingFactor: Integer; { 1..100, or 0 for no input smoothing } + DctMethod: Integer; { 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). } + RestartInterval: Cardinal; { MCUs per restart, or 0 for no restart } + RestartInRows: Integer; { if > 0, MCU rows per restart interval } + { Parameters controlling emission of special markers. } + WriteJfifHeader: Boolean; { should a JFIF marker be written? } + JfifMajorVersion: Byte; { What to write for the JFIF version number } + JFifMinorVersion: Byte; + { 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. } + DensityUnit: Byte; { JFIF code for pixel size units } + XDensity: Word; { Horizontal pixel density } + YDensity: WOrd; { Vertical pixel density } + WriteAdobeMarker: Boolean; { 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)". } + NextScanline: Cardinal; { 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 } + ProgressiveMode: Boolean; { TRUE if scan script uses progressive mode } + MaxHSampFactor: Integer; { largest h_samp_factor } + MaxVSampFactor: Integer; { largest v_samp_factor } + TotalIMCURows: Cardinal; { # 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. } + CompsInScan: Integer; { # of JPEG components in this scan } + CurCompInfo: array[0..MAX_COMPS_IN_SCAN-1] of PRJpegComponentInfo; + { *cur_comp_info[i] describes component that appears i'th in SOS } + MCUsPerRow: Cardinal; { # of MCUs across the image } + MCUsRowsInScan: Cardinal; { # of MCU rows in the image } + BlocksInMcu: Integer; { # of DCT blocks per MCU } + MCUMembership: array[0..C_MAX_BLOCKS_IN_MCU-1] of Integer; + { MCU_membership[i] is index in cur_comp_info of component owning i'th block in an MCU } + Ss,Se,Ah,Al: Integer; { progressive JPEG parameters for scan } + { Links to compression subobjects (methods and private variables of modules) } + Master: PRJpegCompMaster; + Main: PRJpegCMainController; + Prep: PRJpegCPrepController; + Coef: PRJpegCCoefController; + Marker: PRJpegMarkerWriter; + CConvert: PRJpegColorConverter; + Downsample: PRJpegDownsampler; + FDct: PRJpegForwardDct; + Entropy: PRJpegEntropyEncoder; + ScriptSpace: PRJpegScanInfo; { workspace for jpeg_simple_progression } + ScriptSpaceSize: Integer; + end; + + RJpegDecompressStruct = record + { Fields shared with jpeg_compress_struct } + Err: PRJpegErrorMgr; { Error handler module } + Mem: PRJpegMemoryMgr; { Memory manager module } + Progress: PRJpegProgressMgr; { Progress monitor, or NULL if none } + ClientData: Pointer; { Available for use by application } + IsDecompressor: Boolean; { So common code can tell which is which } + GlobalState: Integer; { For checking call sequence validity } + { Source of compressed data } + Src: PRJpegSourceMgr; + { Basic description of image --- filled in by jpeg_read_header(). } + { Application may inspect these values to decide how to process image. } + ImageWidth: Cardinal; { nominal image width (from SOF marker) } + ImageHeight: Cardinal; { nominal image height } + NumComponents: Integer; { # of color components in JPEG image } + JpegColorSpace: Integer; { 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. } + OutColorSpace: Integer; { colorspace for output } + ScaleNum,ScaleDenom: Cardinal; { fraction by which to scale image } + OutputGamme: Double; { image gamma wanted in output } + BufferedImage: Boolean; { TRUE=multiple output passes } + RawDataOut: Boolean; { TRUE=downsampled data wanted } + DctMethod: Integer; { IDCT algorithm selector } + DoFancyUpsampling: Boolean; { TRUE=apply fancy upsampling } + DoBlockSmoothing: Boolean; { TRUE=apply interblock smoothing } + QuantizeColors: Boolean; { TRUE=colormapped output wanted } + { the following are ignored if not quantize_colors: } + DitherMode: Integer; { type of color dithering to use } + TwoPassQuantize: Boolean; { TRUE=use two-pass color quantization } + DesiredNumberOfColors: Integer;{ max # colors to use in created colormap } + { these are significant only in buffered-image mode: } + Enable1PassQuant: Boolean; { enable future use of 1-pass quantizer } + EnableExternalQuant: Boolean; { enable future use of external colormap } + Enable2PassQuant: Boolean; { 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(). } + OutputWidth: Cardinal; { scaled image width } + OutputHeight: Cardinal; { scaled image height } + OutColorComponents: Integer; { # of color components in out_color_space } + OutputComponents: Integer; { # of color components returned } + { output_components is 1 (a colormap index) when quantizing colors; otherwise it equals out_color_components. } + RecOutbufHeight: Integer; { 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. } + ActualNumberOfColors: Integer; { number of entries in use } + Colormap: Pointer; { 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)". } + OutputScanline: Cardinal; { 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. } + InputScanNumber: Integer; { Number of SOS markers seen so far } + InputIMcuRow: Cardinal; { 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. } + OutputScanNumber: Integer; { Nominal scan number being displayed } + OutputIMcuRow: Cardinal; { 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. } + CoefBits: Pointer; { -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. } + QuantTblPtrs: array[0..NUM_QUANT_TBLS-1] of Pointer; + { ptrs to coefficient quantization tables, or NULL if not defined } + DcHuffTblPtrs: array[0..NUM_HUFF_TBLS-1] of Pointer; + AcHuffTblPtrs: array[0..NUM_HUFF_TBLS-1] of Pointer; + { 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. } + DataPrecision: Integer; { bits of precision in image data } + CompInfo: PRJpegComponentInfo; { comp_info[i] describes component that appears i'th in SOF } + ProgressiveMode: Boolean; { TRUE if SOFn specifies progressive mode } + ArithCode: Boolean; { TRUE=arithmetic coding, FALSE=Huffman } + ArithDcL: array[0..NUM_ARITH_TBLS-1] of Byte; { L values for DC arith-coding tables } + ArithDcY: array[0..NUM_ARITH_TBLS-1] of Byte; { U values for DC arith-coding tables } + ArithAcK: array[0..NUM_ARITH_TBLS-1] of Byte; { Kx values for AC arith-coding tables } + RestartInterval: Cardinal; { MCUs per restart interval, or 0 for no restart } + { These fields record data obtained from optional markers recognized by the JPEG library. } + SawJfifMarker: Boolean; { TRUE iff a JFIF APP0 marker was found } + { Data copied from JFIF marker; only valid if saw_JFIF_marker is TRUE: } + JfifMajorVersion: Byte; { JFIF version number } + JfifMinorVersion: Byte; { JFIF code for pixel size units } + XDensity: Word; { Horizontal pixel density } + YDensity: Word; { Vertical pixel density } + SawAdobeMarker: Boolean; { TRUE iff an Adobe APP14 marker was found } + AdobeTransform: Byte; { Color transform code from Adobe marker } + Ccir601Sampling: Boolean; { 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. } + MarkerList: PRJpegSavedMarker; { 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 } + MaxHSampFactor: Integer; { largest h_samp_factor } + MaxVSampFactor: Integer; { largest v_samp_factor } + MinDctScaledSize: Integer; { smallest DCT_scaled_size of any component } + TotalIMcuRows: Cardinal; { # 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. } + SampleRangeLimit: Pointer; { 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. } + CompsInScan: Integer; { # of JPEG components in this scan } + CurCompInfo: array[0..MAX_COMPS_IN_SCAN-1] of PRJpegComponentInfo; + { *cur_comp_info[i] describes component that appears i'th in SOS } + McusPerRow: Cardinal; { # of MCUs across the image } + McuRowsInScan: Cardinal; { # of MCU rows in the image } + BlocksInMcu: Integer; { # of DCT blocks per MCU } + McuMembership: array[0..D_MAX_BLOCKS_IN_MCU-1] of Integer; + { MCU_membership[i] is index in cur_comp_info of component owning i'th block in an MCU } + Ss,Se,Ah,Al: Integer; { 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. } + UnreadMarker: Integer; + { Links to decompression subobjects (methods, private variables of modules) } + Master: PRJpegDecompMaster; + Main: PRJpegDMainController; + Coef: PRJpegDCoefController; + Post: PRJpegDPosController; + InputCtl: PRJpegInputController; + Marker: PRJpegMarkerReader; + Entropy: PRJpegEntropyDecoder; + IDct: PRJpegInverseDct; + Upsample: PRJpegUpsampler; + CConvert: PRJpegColorDeconverter; + CQuantize: PRJpegColorQuantizer; + end; + +procedure jpeg_create_compress(cinfo: PRJpegCompressStruct); cdecl; +procedure jpeg_CreateCompress(cinfo: PRJpegCompressStruct; version: Integer; structsize: Cardinal); cdecl; external; +procedure jpeg_create_decompress(cinfo: PRJpegDecompressStruct); cdecl; +procedure jpeg_CreateDecompress(cinfo: PRJpegDecompressStruct; version: Integer; structsize: Cardinal); cdecl; external; +procedure jpeg_abort(cinfo: PRJpegCommonStruct); cdecl; external; +procedure jpeg_set_defaults(cinfo: PRJpegCompressStruct); cdecl; external; +procedure jpeg_set_colorspace(cinfo: PRJpegCompressStruct; colorspace: Integer); cdecl; external; +procedure jpeg_set_quality(cinfo: PRJpegCompressStruct; quality: Integer; force_baseline: Byte); cdecl; external; +procedure jpeg_suppress_tables(cinfo: PRJpegCompressStruct; suppress: Byte); cdecl; external; +procedure jpeg_start_compress(cinfo: PRJpegCompressStruct; write_all_tables: Byte); cdecl; external; +function jpeg_write_scanlines(cinfo: PRJpegCompressStruct; scanlines: PPointer; num_lines: Cardinal): Cardinal; cdecl; external; +function jpeg_write_raw_data(cinfo: PRJpegCompressStruct; data: Pointer; num_lines: Cardinal): Cardinal; cdecl; external; +procedure jpeg_finish_compress(cinfo: PRJpegCompressStruct); cdecl; external; +procedure jpeg_write_tables(cinfo: PRJpegCompressStruct); cdecl; external; +function jpeg_read_header(cinfo: PRJpegDecompressStruct; require_image: Boolean): Integer; cdecl; external; +function jpeg_start_decompress(cinfo: PRJpegDecompressStruct): Byte; cdecl; external; +function jpeg_read_scanlines(cinfo: PRJpegDecompressStruct; scanlines: Pointer; max_lines: Cardinal): Cardinal; cdecl; external; +function jpeg_read_raw_data(cinfo: PRJpegDecompressStruct; data: Pointer; max_lines: Cardinal): Cardinal; cdecl; external; +function jpeg_finish_decompress(cinfo: PRJpegDecompressStruct): Byte; cdecl; external; +procedure jpeg_destroy(cinfo: PRJpegCommonStruct); cdecl; external; +function jpeg_std_error(err: PRJpegErrorMgr): Pointer; cdecl; external; +function jpeg_resync_to_restart(cinfo: PRJpegDecompressStruct; desired: Integer): Byte; cdecl; external; + +implementation + +uses + LibDelphi; + +procedure jpeg_error_exit_raise; cdecl; {$ifdef FPC}[public];{$endif} +begin + raise Exception.Create('LibJpeg error_exit'); +end; + +{$ifdef FPC} +function jpeg_sizeof_compress:Integer; cdecl; external; +function jpeg_sizeof_decompress:Integer; cdecl; external; + +procedure jpeg_create_compress(cinfo: PRJpegCompressStruct); cdecl; +begin + jpeg_CreateCompress(cinfo,JPEG_LIB_VERSION,jpeg_sizeof_compress()); +end; + +procedure jpeg_create_decompress(cinfo: PRJpegDecompressStruct); cdecl; +begin + jpeg_CreateDecompress(cinfo,JPEG_LIB_VERSION,jpeg_sizeof_decompress()); +end; +{$else} +procedure jpeg_create_compress(cinfo: PRJpegCompressStruct); cdecl; +begin + jpeg_CreateCompress(cinfo,JPEG_LIB_VERSION,SizeOf(RJpegCompressStruct)); +end; + +procedure jpeg_create_decompress(cinfo: PRJpegDecompressStruct); cdecl; +begin + jpeg_CreateDecompress(cinfo,JPEG_LIB_VERSION,SizeOf(RJpegDecompressStruct)); +end; +{$endif} + +function jpeg_get_small(cinfo: PRJpegCommonStruct; sizeofobject: Cardinal): Pointer; cdecl; external; +function jpeg_get_large(cinfo: PRJpegCommonStruct; sizeofobject: Cardinal): Pointer; cdecl; external; +function jpeg_mem_available(cinfo: PRJpegCommonStruct; min_bytes_needed: Integer; max_bytes_needed: Integer; already_allocated: Integer): Integer; cdecl; external; +procedure jpeg_open_backing_store(cinfo: PRJpegCommonStruct; info: Pointer; total_bytes_needed: Integer); cdecl; external; +procedure jpeg_free_large(cinfo: PRJpegCommonStruct; objectt: Pointer; sizeofobject: Cardinal); cdecl; external; +procedure jpeg_free_small(cinfo: PRJpegCommonStruct; objectt: Pointer; sizeofobject: Cardinal); cdecl; external; +procedure jpeg_mem_term(cinfo: PRJpegCommonStruct); cdecl; external; +function jpeg_mem_init(cinfo: PRJpegCommonStruct): Integer; cdecl; external; +procedure jinit_memory_mgr(cinfo: PRJpegCommonStruct); cdecl; external; +function jpeg_alloc_huff_table(cinfo: PRJpegCommonStruct): Pointer; cdecl; external; +function jpeg_alloc_quant_table(cinfo: PRJpegCommonStruct): Pointer; cdecl; external; +function jdiv_round_up(a: Integer; b: Integer): Integer; cdecl; external; +procedure jcopy_sample_rows(input_array: Pointer; source_row: Integer; output_array: Pointer; dest_row: Integer; num_rows: Integer; + num_cols: Cardinal); cdecl; external; +function jround_up(a: Integer; b: Integer): Integer; cdecl; external; +procedure jcopy_block_row(input_row: Pointer; output_row: Pointer; num_blocks: Cardinal); cdecl; external; + +{$IF Defined(DCC) and Defined(MSWINDOWS) and not Defined(CPUX64)} + // Windows 32bit Delphi only - OMF object format + {$L Compiled\jmemnobs.obj} + {$L Compiled\jmemmgr.obj} + {$L Compiled\jcomapi.obj} + {$L Compiled\jerror.obj} + {$L Compiled\jcapimin.obj} + {$L Compiled\jcmarker.obj} + {$L Compiled\jutils.obj} + {$L Compiled\jdapimin.obj} + {$L Compiled\jdmarker.obj} + {$L Compiled\jdinput.obj} + {$L Compiled\jcparam.obj} + {$L Compiled\jcapistd.obj} + {$L Compiled\jcinit.obj} + {$L Compiled\jcmaster.obj} + {$L Compiled\jccolor.obj} + {$L Compiled\jcsample.obj} + {$L Compiled\jcprepct.obj} + {$L Compiled\jcdctmgr.obj} + {$L Compiled\jcphuff.obj} + {$L Compiled\jchuff.obj} + {$L Compiled\jccoefct.obj} + {$L Compiled\jcmainct.obj} + {$L Compiled\jfdctint.obj} + {$L Compiled\jfdctfst.obj} + {$L Compiled\jfdctflt.obj} + {$L Compiled\jdapistd.obj} + {$L Compiled\jdmaster.obj} + {$L Compiled\jquant1.obj} + {$L Compiled\jquant2.obj} + {$L Compiled\jdmerge.obj} + {$L Compiled\jdcolor.obj} + {$L Compiled\jdsample.obj} + {$L Compiled\jdpostct.obj} + {$L Compiled\jddctmgr.obj} + {$L Compiled\jdphuff.obj} + {$L Compiled\jdhuff.obj} + {$L Compiled\jdcoefct.obj} + {$L Compiled\jdmainct.obj} + {$L Compiled\jidctred.obj} + {$L Compiled\jidctint.obj} + {$L Compiled\jidctfst.obj} + {$L Compiled\jidctflt.obj} +{$IFEND} +end. + + + diff --git a/resources/libraries/deskew/Imaging/LibTiff/LibTiffDelphi.pas b/resources/libraries/deskew/Imaging/LibTiff/LibTiffDelphi.pas new file mode 100755 index 0000000..c73308b --- /dev/null +++ b/resources/libraries/deskew/Imaging/LibTiff/LibTiffDelphi.pas @@ -0,0 +1,1510 @@ +{ + LibTiffDelphi + + Original: Aware Systems + Modifications: Marek Mauder, Do-wan Kim + +} + +unit LibTiffDelphi; + +{$IFDEF FPC} + {$MODE OBJFPC} + {$DEFINE VER403} // libtiff 4.0.3 +{$ELSE} + {$DEFINE DCC} + {$ALIGN 8} + {$MINENUMSIZE 1} +{$ENDIF} + +interface + +uses + SysUtils, Classes, LibDelphi; + +type + tmsize_t = SizeInt; + tsize_t = SizeInt; + toff_t = {$ifdef VER403}int64{$else}Integer{$endif}; + poff_t = ^toff_t; + tsample_t = Word; + // Beware: THandle is 32bit in size even on 64bit Linux - this may cause + // problems as pointers to client data are passed in thandle_t vars. + thandle_t = THandle; + tdata_t = Pointer; + ttag_t = LongWord; + tdir_t = Word; + tstrip_t = LongWord; + +const + TIFF_NOTYPE = 0; + 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) } + TIFF_UNICODE = 14; + TIFF_COMPLEX = 15; + TIFF_LONG8 = 16; + TIFF_SLONG8 = 17; + TIFF_IFD8 = 18; + + TIFFTAG_SUBFILETYPE = 254; { subfile data descriptor } + FILETYPE_REDUCEDIMAGE = $1; { reduced resolution version } + FILETYPE_PAGE = $2; { one page of many } + FILETYPE_MASK = $4; { transparency mask } + TIFFTAG_OSUBFILETYPE = 255; { kind of data in subfile } + OFILETYPE_IMAGE = 1; { full resolution image data } + OFILETYPE_REDUCEDIMAGE = 2; { reduced size image data } + OFILETYPE_PAGE = 3; { one page of many } + TIFFTAG_IMAGEWIDTH = 256; { image width in pixels } + TIFFTAG_IMAGELENGTH = 257; { image height in pixels } + TIFFTAG_BITSPERSAMPLE = 258; { bits per channel (sample) } + TIFFTAG_COMPRESSION = 259; { data compression technique } + COMPRESSION_NONE = 1; { dump mode } + COMPRESSION_CCITTRLE = 2; { CCITT modified Huffman RLE } + COMPRESSION_CCITTFAX3 = 3; { CCITT Group 3 fax encoding } + COMPRESSION_CCITT_T4 = 3; { CCITT T.4 (TIFF 6 name) } + COMPRESSION_CCITTFAX4 = 4; { CCITT Group 4 fax encoding } + COMPRESSION_CCITT_T6 = 4; { CCITT T.6 (TIFF 6 name) } + COMPRESSION_LZW = 5; { Lempel-Ziv & Welch } + COMPRESSION_OJPEG = 6; { !6.0 JPEG } + COMPRESSION_JPEG = 7; { %JPEG DCT compression } + COMPRESSION_NEXT = 32766; { NeXT 2-bit RLE } + COMPRESSION_CCITTRLEW = 32771; { #1 w/ word alignment } + COMPRESSION_PACKBITS = 32773; { Macintosh RLE } + COMPRESSION_THUNDERSCAN = 32809; { ThunderScan RLE } + { codes 32895-32898 are reserved for ANSI IT8 TIFF/IT <dkelly@apago.com) } + COMPRESSION_IT8CTPAD = 32895; { IT8 CT w/padding } + COMPRESSION_IT8LW = 32896; { IT8 Linework RLE } + COMPRESSION_IT8MP = 32897; { IT8 Monochrome picture } + COMPRESSION_IT8BL = 32898; { IT8 Binary line art } + { compression codes 32908-32911 are reserved for Pixar } + COMPRESSION_PIXARFILM = 32908; { Pixar companded 10bit LZW } + COMPRESSION_PIXARLOG = 32909; { Pixar companded 11bit ZIP } + COMPRESSION_DEFLATE = 32946; { Deflate compression } + COMPRESSION_ADOBE_DEFLATE = 8; { Deflate compression, as recognized by Adobe } + { compression code 32947 is reserved for Oceana Matrix <dev@oceana.com> } + COMPRESSION_DCS = 32947; { Kodak DCS encoding } + COMPRESSION_JBIG = 34661; { ISO JBIG } + COMPRESSION_SGILOG = 34676; { SGI Log Luminance RLE } + COMPRESSION_SGILOG24 = 34677; { SGI Log 24-bit packed } + COMPRESSION_JP2000 = 34712; { Leadtools JPEG2000 } + TIFFTAG_PHOTOMETRIC = 262; { photometric interpretation } + PHOTOMETRIC_MINISWHITE = 0; { min value is white } + PHOTOMETRIC_MINISBLACK = 1; { min value is black } + PHOTOMETRIC_RGB = 2; { RGB color model } + PHOTOMETRIC_PALETTE = 3; { color map indexed } + PHOTOMETRIC_MASK = 4; { $holdout mask } + PHOTOMETRIC_SEPARATED = 5; { !color separations } + PHOTOMETRIC_YCBCR = 6; { !CCIR 601 } + PHOTOMETRIC_CIELAB = 8; { !1976 CIE L*a*b* } + PHOTOMETRIC_ICCLAB = 9; { ICC L*a*b* [Adobe TIFF Technote 4] } + PHOTOMETRIC_ITULAB = 10; { ITU L*a*b* } + PHOTOMETRIC_LOGL = 32844; { CIE Log2(L) } + PHOTOMETRIC_LOGLUV = 32845; { CIE Log2(L) (u',v') } + TIFFTAG_THRESHHOLDING = 263; { thresholding used on data } + THRESHHOLD_BILEVEL = 1; { b&w art scan } + THRESHHOLD_HALFTONE = 2; { or dithered scan } + THRESHHOLD_ERRORDIFFUSE = 3; { usually floyd-steinberg } + TIFFTAG_CELLWIDTH = 264; { +dithering matrix width } + TIFFTAG_CELLLENGTH = 265; { +dithering matrix height } + TIFFTAG_FILLORDER = 266; { data order within a byte } + FILLORDER_MSB2LSB = 1; { most significant -> least } + FILLORDER_LSB2MSB = 2; { least significant -> most } + TIFFTAG_DOCUMENTNAME = 269; { name of doc. image is from } + TIFFTAG_IMAGEDESCRIPTION = 270; { info about image } + TIFFTAG_MAKE = 271; { scanner manufacturer name } + TIFFTAG_MODEL = 272; { scanner model name/number } + TIFFTAG_STRIPOFFSETS = 273; { offsets to data strips } + TIFFTAG_ORIENTATION = 274; { +image orientation } + ORIENTATION_TOPLEFT = 1; { row 0 top, col 0 lhs } + ORIENTATION_TOPRIGHT = 2; { row 0 top, col 0 rhs } + ORIENTATION_BOTRIGHT = 3; { row 0 bottom, col 0 rhs } + ORIENTATION_BOTLEFT = 4; { row 0 bottom, col 0 lhs } + ORIENTATION_LEFTTOP = 5; { row 0 lhs, col 0 top } + ORIENTATION_RIGHTTOP = 6; { row 0 rhs, col 0 top } + ORIENTATION_RIGHTBOT = 7; { row 0 rhs, col 0 bottom } + ORIENTATION_LEFTBOT = 8; { row 0 lhs, col 0 bottom } + TIFFTAG_SAMPLESPERPIXEL = 277; { samples per pixel } + TIFFTAG_ROWSPERSTRIP = 278; { rows per strip of data } + TIFFTAG_STRIPBYTECOUNTS = 279; { bytes counts for strips } + TIFFTAG_MINSAMPLEVALUE = 280; { +minimum sample value } + TIFFTAG_MAXSAMPLEVALUE = 281; { +maximum sample value } + TIFFTAG_XRESOLUTION = 282; { pixels/resolution in x } + TIFFTAG_YRESOLUTION = 283; { pixels/resolution in y } + TIFFTAG_PLANARCONFIG = 284; { storage organization } + PLANARCONFIG_CONTIG = 1; { single image plane } + PLANARCONFIG_SEPARATE = 2; { separate planes of data } + TIFFTAG_PAGENAME = 285; { page name image is from } + TIFFTAG_XPOSITION = 286; { x page offset of image lhs } + TIFFTAG_YPOSITION = 287; { y page offset of image lhs } + TIFFTAG_FREEOFFSETS = 288; { +byte offset to free block } + TIFFTAG_FREEBYTECOUNTS = 289; { +sizes of free blocks } + + {matched with tag reference up to this point} + + TIFFTAG_GRAYRESPONSEUNIT = 290; { $gray scale curve accuracy } + GRAYRESPONSEUNIT_10S = 1; { tenths of a unit } + GRAYRESPONSEUNIT_100S = 2; { hundredths of a unit } + GRAYRESPONSEUNIT_1000S = 3; { thousandths of a unit } + GRAYRESPONSEUNIT_10000S = 4; { ten-thousandths of a unit } + GRAYRESPONSEUNIT_100000S = 5; { hundred-thousandths } + TIFFTAG_GRAYRESPONSECURVE = 291; { $gray scale response curve } + TIFFTAG_GROUP3OPTIONS = 292; { 32 flag bits } + TIFFTAG_T4OPTIONS = 292; { TIFF 6.0 proper name alias } + GROUP3OPT_2DENCODING = $1; { 2-dimensional coding } + GROUP3OPT_UNCOMPRESSED = $2; { data not compressed } + GROUP3OPT_FILLBITS = $4; { fill to byte boundary } + TIFFTAG_GROUP4OPTIONS = 293; { 32 flag bits } + TIFFTAG_T6OPTIONS = 293; { TIFF 6.0 proper name } + GROUP4OPT_UNCOMPRESSED = $2; { data not compressed } + TIFFTAG_RESOLUTIONUNIT = 296; { units of resolutions } + RESUNIT_NONE = 1; { no meaningful units } + RESUNIT_INCH = 2; { english } + RESUNIT_CENTIMETER = 3; { metric } + TIFFTAG_PAGENUMBER = 297; { page numbers of multi-page } + TIFFTAG_COLORRESPONSEUNIT = 300; { $color curve accuracy } + COLORRESPONSEUNIT_10S = 1; { tenths of a unit } + COLORRESPONSEUNIT_100S = 2; { hundredths of a unit } + COLORRESPONSEUNIT_1000S = 3; { thousandths of a unit } + COLORRESPONSEUNIT_10000S = 4; { ten-thousandths of a unit } + COLORRESPONSEUNIT_100000S = 5; { hundred-thousandths } + TIFFTAG_TRANSFERFUNCTION = 301; { !colorimetry info } + TIFFTAG_SOFTWARE = 305; { name & release } + TIFFTAG_DATETIME = 306; { creation date and time } + TIFFTAG_ARTIST = 315; { creator of image } + TIFFTAG_HOSTCOMPUTER = 316; { machine where created } + TIFFTAG_PREDICTOR = 317; { prediction scheme w/ LZW } + TIFFTAG_WHITEPOINT = 318; { image white point } + TIFFTAG_PRIMARYCHROMATICITIES = 319; { !primary chromaticities } + TIFFTAG_COLORMAP = 320; { RGB map for pallette image } + TIFFTAG_HALFTONEHINTS = 321; { !highlight+shadow info } + TIFFTAG_TILEWIDTH = 322; { !rows/data tile } + TIFFTAG_TILELENGTH = 323; { !cols/data tile } + TIFFTAG_TILEOFFSETS = 324; { !offsets to data tiles } + TIFFTAG_TILEBYTECOUNTS = 325; { !byte counts for tiles } + TIFFTAG_BADFAXLINES = 326; { lines w/ wrong pixel count } + TIFFTAG_CLEANFAXDATA = 327; { regenerated line info } + CLEANFAXDATA_CLEAN = 0; { no errors detected } + CLEANFAXDATA_REGENERATED = 1; { receiver regenerated lines } + CLEANFAXDATA_UNCLEAN = 2; { uncorrected errors exist } + TIFFTAG_CONSECUTIVEBADFAXLINES = 328; { max consecutive bad lines } + TIFFTAG_SUBIFD = 330; { subimage descriptors } + TIFFTAG_INKSET = 332; { !inks in separated image } + INKSET_CMYK = 1; { !cyan-magenta-yellow-black color } + INKSET_MULTIINK = 2; { !multi-ink or hi-fi color } + TIFFTAG_INKNAMES = 333; { !ascii names of inks } + TIFFTAG_NUMBEROFINKS = 334; { !number of inks } + TIFFTAG_DOTRANGE = 336; { !0% and 100% dot codes } + TIFFTAG_TARGETPRINTER = 337; { !separation target } + TIFFTAG_EXTRASAMPLES = 338; { !info about extra samples } + EXTRASAMPLE_UNSPECIFIED = 0; { !unspecified data } + EXTRASAMPLE_ASSOCALPHA = 1; { !associated alpha data } + EXTRASAMPLE_UNASSALPHA = 2; { !unassociated alpha data } + TIFFTAG_SAMPLEFORMAT = 339; { !data sample format } + SAMPLEFORMAT_UINT = 1; { !unsigned integer data } + SAMPLEFORMAT_INT = 2; { !signed integer data } + SAMPLEFORMAT_IEEEFP = 3; { !IEEE floating point data } + SAMPLEFORMAT_VOID = 4; { !untyped data } + SAMPLEFORMAT_COMPLEXINT = 5; { !complex signed int } + SAMPLEFORMAT_COMPLEXIEEEFP = 6; { !complex ieee floating } + TIFFTAG_SMINSAMPLEVALUE = 340; { !variable MinSampleValue } + TIFFTAG_SMAXSAMPLEVALUE = 341; { !variable MaxSampleValue } + TIFFTAG_CLIPPATH = 343; { %ClipPath [Adobe TIFF technote 2] } + TIFFTAG_XCLIPPATHUNITS = 344; { %XClipPathUnits [Adobe TIFF technote 2] } + TIFFTAG_YCLIPPATHUNITS = 345; { %YClipPathUnits [Adobe TIFF technote 2] } + TIFFTAG_INDEXED = 346; { %Indexed [Adobe TIFF Technote 3] } + TIFFTAG_JPEGTABLES = 347; { %JPEG table stream } + 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. } + TIFFTAG_JPEGPROC = 512; { !JPEG processing algorithm } + JPEGPROC_BASELINE = 1; { !baseline sequential } + JPEGPROC_LOSSLESS = 14; { !Huffman coded lossless } + TIFFTAG_JPEGIFOFFSET = 513; { !pointer to SOI marker } + TIFFTAG_JPEGIFBYTECOUNT = 514; { !JFIF stream length } + TIFFTAG_JPEGRESTARTINTERVAL = 515; { !restart interval length } + TIFFTAG_JPEGLOSSLESSPREDICTORS = 517; { !lossless proc predictor } + TIFFTAG_JPEGPOINTTRANSFORM = 518; { !lossless point transform } + TIFFTAG_JPEGQTABLES = 519; { !Q matrice offsets } + TIFFTAG_JPEGDCTABLES = 520; { !DCT table offsets } + TIFFTAG_JPEGACTABLES = 521; { !AC coefficient offsets } + TIFFTAG_YCBCRCOEFFICIENTS = 529; { !RGB -> YCbCr transform } + TIFFTAG_YCBCRSUBSAMPLING = 530; { !YCbCr subsampling factors } + TIFFTAG_YCBCRPOSITIONING = 531; { !subsample positioning } + YCBCRPOSITION_CENTERED = 1; { !as in PostScript Level 2 } + YCBCRPOSITION_COSITED = 2; { !as in CCIR 601-1 } + TIFFTAG_REFERENCEBLACKWHITE = 532; { !colorimetry info } + TIFFTAG_XMLPACKET = 700; { %XML packet [Adobe XMP technote 9-14-02] (dkelly@apago.com) } + TIFFTAG_OPIIMAGEID = 32781; { %OPI ImageID [Adobe TIFF technote] } + { tags 32952-32956 are private tags registered to Island Graphics } + TIFFTAG_REFPTS = 32953; { image reference points } + TIFFTAG_REGIONTACKPOINT = 32954; { region-xform tack point } + TIFFTAG_REGIONWARPCORNERS = 32955; { warp quadrilateral } + TIFFTAG_REGIONAFFINE = 32956; { affine transformation mat } + { tags 32995-32999 are private tags registered to SGI } + TIFFTAG_MATTEING = 32995; { $use ExtraSamples } + TIFFTAG_DATATYPE = 32996; { $use SampleFormat } + TIFFTAG_IMAGEDEPTH = 32997; { z depth of image } + 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. } + TIFFTAG_PIXAR_IMAGEFULLWIDTH = 33300; { full image size in x } + 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. } + TIFFTAG_PIXAR_TEXTUREFORMAT = 33302; { texture map format } + TIFFTAG_PIXAR_WRAPMODES = 33303; { s & t wrap modes } + TIFFTAG_PIXAR_FOVCOT = 33304; { cotan(fov) for env. maps } + TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN = 33305; + TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA = 33306; + { tag 33405 is a private tag registered to Eastman Kodak } + TIFFTAG_WRITERSERIALNUMBER = 33405; { device serial number } + { tag 33432 is listed in the 6.0 spec w/ unknown ownership } + TIFFTAG_COPYRIGHT = 33432; { copyright string } + { IPTC TAG from RichTIFF specifications } + TIFFTAG_RICHTIFFIPTC = 33723; + { 34016-34029 are reserved for ANSI IT8 TIFF/IT <dkelly@apago.com) } + TIFFTAG_IT8SITE = 34016; { site name } + TIFFTAG_IT8COLORSEQUENCE = 34017; { color seq. [RGB,CMYK,etc] } + TIFFTAG_IT8HEADER = 34018; { DDES Header } + TIFFTAG_IT8RASTERPADDING = 34019; { raster scanline padding } + TIFFTAG_IT8BITSPERRUNLENGTH = 34020; { # of bits in short run } + TIFFTAG_IT8BITSPEREXTENDEDRUNLENGTH = 34021; { # of bits in long run } + TIFFTAG_IT8COLORTABLE = 34022; { LW colortable } + TIFFTAG_IT8IMAGECOLORINDICATOR = 34023; { BP/BL image color switch } + TIFFTAG_IT8BKGCOLORINDICATOR = 34024; { BP/BL bg color switch } + TIFFTAG_IT8IMAGECOLORVALUE = 34025; { BP/BL image color value } + TIFFTAG_IT8BKGCOLORVALUE = 34026; { BP/BL bg color value } + TIFFTAG_IT8PIXELINTENSITYRANGE = 34027; { MP pixel intensity value } + TIFFTAG_IT8TRANSPARENCYINDICATOR = 34028; { HC transparency switch } + TIFFTAG_IT8COLORCHARACTERIZATION = 34029; { color character. table } + TIFFTAG_IT8HCUSAGE = 34030; { HC usage indicator } + TIFFTAG_IT8TRAPINDICATOR = 34031; { Trapping indicator (untrapped=0, trapped=1) } + TIFFTAG_IT8CMYKEQUIVALENT = 34032; { CMYK color equivalents } + { tags 34232-34236 are private tags registered to Texas Instruments } + TIFFTAG_FRAMECOUNT = 34232; { Sequence Frame Count } + { tag 34750 is a private tag registered to Adobe? } + TIFFTAG_ICCPROFILE = 34675; { ICC profile data } + { tag 34377 is private tag registered to Adobe for PhotoShop } + TIFFTAG_PHOTOSHOP = 34377; + { tag 34750 is a private tag registered to Pixel Magic } + TIFFTAG_JBIGOPTIONS = 34750; { JBIG options } + { tags 34908-34914 are private tags registered to SGI } + TIFFTAG_FAXRECVPARAMS = 34908; { encoded Class 2 ses. parms } + TIFFTAG_FAXSUBADDRESS = 34909; { received SubAddr string } + TIFFTAG_FAXRECVTIME = 34910; { receive time (secs) } + { tags 37439-37443 are registered to SGI <gregl@sgi.com> } + TIFFTAG_STONITS = 37439; { Sample value to Nits } + { tag 34929 is a private tag registered to FedEx } + TIFFTAG_FEDEX_EDR = 34929; { unknown use } + { tag 65535 is an undefined tag used by Eastman Kodak } + 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. } + TIFFTAG_FAXMODE = 65536; { Group 3/4 format control } + FAXMODE_CLASSIC = $0; { default, include RTC } + FAXMODE_NORTC = $1; { no RTC at end of data } + FAXMODE_NOEOL = $2; { no EOL code at end of row } + FAXMODE_BYTEALIGN = $4; { byte align row } + FAXMODE_WORDALIGN = $8; { word align row } + FAXMODE_CLASSF = FAXMODE_NORTC; { TIFF Class F } + TIFFTAG_JPEGQUALITY = 65537; { Compression quality level } + { Note: quality level is on the IJG 0-100 scale. Default value is 75 } + TIFFTAG_JPEGCOLORMODE = 65538; { Auto RGB<=>YCbCr convert? } + JPEGCOLORMODE_RAW = $0; { no conversion (default) } + JPEGCOLORMODE_RGB = $1; { do auto conversion } + TIFFTAG_JPEGTABLESMODE = 65539; { What to put in JPEGTables } + JPEGTABLESMODE_QUANT = $1; { include quantization tbls } + JPEGTABLESMODE_HUFF = $2; { include Huffman tbls } + { Note: default is JPEGTABLESMODE_QUANT | JPEGTABLESMODE_HUFF } + TIFFTAG_FAXFILLFUNC = 65540; { G3/G4 fill function } + TIFFTAG_PIXARLOGDATAFMT = 65549; { PixarLogCodec I/O data sz } + PIXARLOGDATAFMT_8BIT = 0; { regular u_char samples } + PIXARLOGDATAFMT_8BITABGR = 1; { ABGR-order u_chars } + PIXARLOGDATAFMT_11BITLOG = 2; { 11-bit log-encoded (raw) } + PIXARLOGDATAFMT_12BITPICIO = 3; { as per PICIO (1.0==2048) } + PIXARLOGDATAFMT_16BIT = 4; { signed short samples } + PIXARLOGDATAFMT_FLOAT = 5; { IEEE float samples } + { 65550-65556 are allocated to Oceana Matrix <dev@oceana.com> } + TIFFTAG_DCSIMAGERTYPE = 65550; { imager model & filter } + DCSIMAGERMODEL_M3 = 0; { M3 chip (1280 x 1024) } + DCSIMAGERMODEL_M5 = 1; { M5 chip (1536 x 1024) } + DCSIMAGERMODEL_M6 = 2; { M6 chip (3072 x 2048) } + DCSIMAGERFILTER_IR = 0; { infrared filter } + DCSIMAGERFILTER_MONO = 1; { monochrome filter } + DCSIMAGERFILTER_CFA = 2; { color filter array } + DCSIMAGERFILTER_OTHER = 3; { other filter } + TIFFTAG_DCSINTERPMODE = 65551; { interpolation mode } + DCSINTERPMODE_NORMAL = 0; { whole image, default } + DCSINTERPMODE_PREVIEW = 1; { preview of image (384x256) } + TIFFTAG_DCSBALANCEARRAY = 65552; { color balance values } + TIFFTAG_DCSCORRECTMATRIX = 65553; { color correction values } + TIFFTAG_DCSGAMMA = 65554; { gamma value } + TIFFTAG_DCSTOESHOULDERPTS = 65555; { toe & shoulder points } + TIFFTAG_DCSCALIBRATIONFD = 65556; { calibration file desc } + { Note: quality level is on the ZLIB 1-9 scale. Default value is -1 } + TIFFTAG_ZIPQUALITY = 65557; { compression quality level } + TIFFTAG_PIXARLOGQUALITY = 65558; { PixarLog uses same scale } + { 65559 is allocated to Oceana Matrix <dev@oceana.com> } + TIFFTAG_DCSCLIPRECTANGLE = 65559; { area of image to acquire } + TIFFTAG_SGILOGDATAFMT = 65560; { SGILog user data format } + SGILOGDATAFMT_FLOAT = 0; { IEEE float samples } + SGILOGDATAFMT_16BIT = 1; { 16-bit samples } + SGILOGDATAFMT_RAW = 2; { uninterpreted data } + SGILOGDATAFMT_8BIT = 3; { 8-bit RGB monitor values } + TIFFTAG_SGILOGENCODE = 65561; { SGILog data encoding control } + SGILOGENCODE_NODITHER = 0; { do not dither encoded values } + SGILOGENCODE_RANDITHER = 1; { randomly dither encd values } + + + { 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. } + TIFFPRINT_NONE = $0; { no extra info } + TIFFPRINT_STRIPS = $1; { strips/tiles info } + TIFFPRINT_CURVES = $2; { color/gray response curves } + TIFFPRINT_COLORMAP = $4; { colormap } + TIFFPRINT_JPEGQTABLES = $100; { JPEG Q matrices } + TIFFPRINT_JPEGACTABLES = $200; { JPEG AC tables } + TIFFPRINT_JPEGDCTABLES = $200; { JPEG DC tables } + + + TIFF_ANY = TIFF_NOTYPE; { for field descriptor searching } + TIFF_VARIABLE = -1; { marker for variable length tags } + TIFF_SPP = -2; { marker for SamplesPerPixel tags } + TIFF_VARIABLE2 = -3; { marker for uint32 var-length tags } + + FIELD_CUSTOM = 65; + + {added for LibTiff 3.9.4 by Alex (leontyyy@gmail.com) Dec.2011} + TIFFTAG_EXIFIFD = 34665; { pointer to the Exif IFD } + EXIFTAG_FOCALLENGTH = 37386; { focal length } + EXIFTAG_FOCALLENGTHIN35MMFILM = 41989; { indicates the equivalent focal length assuming a 35mm film camera, in mm } + EXIFTAG_EXIFVERSION = 36864; { version of exif format } + EXIFTAG_DATETIMEDIGITIZED = 36868; { date and time when the image was stored as digital data } + EXIFTAG_DATETIMEORIGINAL = 36867; { date and time when the original image data was generated } + EXIFTAG_EXPOSURETIME = 33434; { exposure time, given in seconds } + EXIFTAG_FNUMBER = 33437; { F number } + EXIFTAG_EXPOSUREPROGRAM = 34850; { class of the program used by the camera to set exposure } + EXIFTAG_SPECTRALSENSITIVITY = 34852; { spectral sensitivity of each channel of the camera used } + EXIFTAG_ISOSPEEDRATINGS = 34855; { ISO Speed and ISO Latitude } + EXIFTAG_OECF = 34856; { Opto-Electric Conversion Function } + EXIFTAG_COMPONENTSCONFIGURATION = 37121; { meaning of each component } + EXIFTAG_COMPRESSEDBITSPERPIXEL = 37122; { compression mode } + EXIFTAG_SHUTTERSPEEDVALUE = 37377; { shutter speed } + EXIFTAG_APERTUREVALUE = 37378; { lens aperture } + EXIFTAG_BRIGHTNESSVALUE = 37379; { brightness } + EXIFTAG_EXPOSUREBIASVALUE = 37380; { exposure bias } + EXIFTAG_MAXAPERTUREVALUE = 37381; { maximum lens aperture } + EXIFTAG_SUBJECTDISTANCE = 37382; { distance to the subject in meters } + EXIFTAG_METERINGMODE = 37383; { metering mode } + EXIFTAG_LIGHTSOURCE = 37384; { light source } + EXIFTAG_FLASH = 37385; { flash } + EXIFTAG_SUBJECTAREA = 37396; { subject area (in exif ver.2.2) } + EXIFTAG_MAKERNOTE = 37500; { manufacturer notes } + EXIFTAG_USERCOMMENT = 37510; { user comments } + EXIFTAG_SUBSECTIME = 37520; { DateTime subseconds } + EXIFTAG_SUBSECTIMEORIGINAL = 37521; { DateTimeOriginal subseconds } + EXIFTAG_SUBSECTIMEDIGITIZED = 37522; { DateTimeDigitized subseconds } + EXIFTAG_FLASHPIXVERSION = 40960; { FlashPix format version } + EXIFTAG_COLORSPACE = 40961; { color space information } + EXIFTAG_PIXELXDIMENSION = 40962; { valid image width } + EXIFTAG_PIXELYDIMENSION = 40963; { valid image height } + EXIFTAG_RELATEDSOUNDFILE = 40964; { related audio file } + EXIFTAG_FLASHENERGY = 41483; { flash energy } + EXIFTAG_SPATIALFREQUENCYRESPONSE = 41484; { spatial frequency response } + EXIFTAG_FOCALPLANEXRESOLUTION = 41486; { focal plane X resolution } + EXIFTAG_FOCALPLANEYRESOLUTION = 41487; { focal plane Y resolution } + EXIFTAG_FOCALPLANERESOLUTIONUNIT = 41488; { focal plane resolution unit } + EXIFTAG_SUBJECTLOCATION = 41492; { subject location } + EXIFTAG_EXPOSUREINDEX = 41493; { exposure index } + EXIFTAG_SENSINGMETHOD = 41495; { sensing method } + EXIFTAG_FILESOURCE = 41728; { file source } + EXIFTAG_SCENETYPE = 41729; { scene type } + EXIFTAG_CFAPATTERN = 41730; { CFA pattern } + EXIFTAG_CUSTOMRENDERED = 41985; { custom image processing (in exif ver.2.2) } + EXIFTAG_EXPOSUREMODE = 41986; { exposure mode (in exif ver.2.2) } + EXIFTAG_WHITEBALANCE = 41987; { white balance (in exif ver.2.2) } + EXIFTAG_DIGITALZOOMRATIO = 41988; { digital zoom ratio (in exif ver.2.2) } + EXIFTAG_SCENECAPTURETYPE = 41990; { scene capture type (in exif ver.2.2) } + EXIFTAG_GAINCONTROL = 41991; { gain control (in exif ver.2.2) } + EXIFTAG_CONTRAST = 41992; { contrast (in exif ver.2.2) } + EXIFTAG_SATURATION = 41993; { saturation (in exif ver.2.2) } + EXIFTAG_SHARPNESS = 41994; { sharpness (in exif ver.2.2) } + EXIFTAG_DEVICESETTINGDESCRIPTION = 41995; { device settings description (in exif ver.2.2) } + EXIFTAG_SUBJECTDISTANCERANGE = 41996; { subject distance range (in exif ver.2.2) } + EXIFTAG_IMAGEUNIQUEID = 42016; { Unique image ID (in exif ver.2.2) } + +type + + PTIFF = Pointer; + PTIFFRGBAImage = Pointer; + + TIFFErrorHandler = procedure(Module: PAnsiChar; Format: PAnsiChar; Params: va_list); cdecl; + LibTiffDelphiErrorHandler = procedure(const a,b: AnsiString); + TIFFReadWriteProc = function(Fd: THandle; Buffer: Pointer; Size: tmsize_t): Integer; cdecl; + TIFFCloseProc = function(Fd: THandle): Integer; cdecl; + TIFFSeekProc = function(Fd: THandle; Off: toff_t; Whence: Integer): toff_t; cdecl; + TIFFSizeProc = function(Fd: THandle): toff_t; cdecl; + TIFFMapFileProc = function(Fd: THandle; PBase: PPointer; PSize: poff_t): Integer; cdecl; + TIFFUnmapFileProc = procedure(Fd: THandle; Base: Pointer; Size: toff_t); cdecl; + TIFFExtendProc = procedure(Handle: PTIFF); cdecl; + + TIFFInitMethod = function(Handle: PTIFF; Scheme: Integer): Integer; cdecl; + + PTIFFCodec = ^TIFFCodec; + TIFFCodec = record + Name: PAnsiChar; + Scheme: Word; + Init: TIFFInitMethod; + end; + + PTIFFFieldInfo = ^TIFFFieldInfo; + TIFFFieldInfo = record + FieldTag: Cardinal; { field's tag } + FieldReadCount: Smallint; { read count/TIFF_VARIABLE/TIFF_SPP } + FieldWriteCount: Smallint; { write count/TIFF_VARIABLE } + FieldType: Integer; { type of associated data } + FieldBit: Word; { bit in fieldsset bit vector } + FieldOkToChange: Byte; { if true, can change while writing } + FieldPassCount: Byte; { if true, pass dir count on set } + FieldName: PAnsiChar; { ASCII name } + end; + + PTIFFTagValue = ^TIFFTagValue; + TIFFTagValue = record + Info: PTIFFFieldInfo; + Count: Integer; + Value: Pointer; + end; + +function TIFFGetVersion: PAnsiChar; cdecl; external; +function TIFFFindCODEC(Scheme: Word): PTIFFCodec; cdecl; external; +function TIFFRegisterCODEC(Scheme: Word; Name: PAnsiChar; InitMethod: TIFFInitMethod): PTIFFCodec; cdecl; external; +procedure TIFFUnRegisterCODEC(c: PTIFFCodec); cdecl; external; +function TIFFIsCODECConfigured(Scheme: Word): Integer; cdecl; external; +function TIFFGetConfiguredCODECs: PTIFFCodec; cdecl; external; + +function TIFFOpen(const Name: AnsiString; const Mode: AnsiString): PTIFF; +function TIFFClientOpen(Name: PAnsiChar; + Mode: PAnsiChar; + ClientData: THandle; + ReadProc: TIFFReadWriteProc; + WriteProc: TIFFReadWriteProc; + SeekProc: TIFFSeekProc; + CloseProc: TIFFCloseProc; + SizeProc: TIFFSizeProc; + MapProc: TIFFMapFileProc; + UnmapProc: TIFFUnmapFileProc): PTIFF; cdecl; external; +procedure TIFFCleanup(Handle: PTIFF); cdecl; external; +procedure TIFFClose(Handle: PTIFF); cdecl; external; +function TIFFFileno(Handle: PTIFF): Integer; cdecl; external; +function TIFFSetFileno(Handle: PTIFF; Newvalue: Integer): Integer; cdecl; external; +function TIFFClientdata(Handle: PTIFF): THandle; cdecl; external; +function TIFFSetClientdata(Handle: PTIFF; Newvalue: THandle): THandle; cdecl; external; +function TIFFGetMode(Handle: PTIFF): Integer; cdecl; external; +function TIFFSetMode(Handle: PTIFF; Mode: Integer): Integer; cdecl; external; +function TIFFFileName(Handle: PTIFF): Pointer; cdecl; external; +function TIFFSetFileName(Handle: PTIFF; Name: PAnsiChar): PAnsiChar; cdecl; external; +function TIFFGetReadProc(Handle: PTIFF): TIFFReadWriteProc; cdecl; external; +function TIFFGetWriteProc(Handle: PTIFF): TIFFReadWriteProc; cdecl; external; +function TIFFGetSeekProc(Handle: PTIFF): TIFFSeekProc; cdecl; external; +function TIFFGetCloseProc(Handle: PTIFF): TIFFCloseProc; cdecl; external; +function TIFFGetSizeProc(Handle: PTIFF): TIFFSizeProc; cdecl; external; +procedure TIFFError(Module: Pointer; Fmt: Pointer); cdecl; external; varargs; +function TIFFSetErrorHandler(Handler: TIFFErrorHandler): TIFFErrorHandler; cdecl; external; +procedure TIFFWarning(Module: Pointer; Fmt: Pointer); cdecl; external; varargs; +function TIFFSetWarningHandler(Handler: TIFFErrorHandler): TIFFErrorHandler; cdecl; external; +function TIFFSetTagExtender(Extender: TIFFExtendProc): TIFFExtendProc; cdecl; external; + +function TIFFFlush(Handle: PTIFF): Integer; cdecl; external; +function TIFFFlushData(Handle: PTIFF): Integer; cdecl; external; + +{added for LibTiff 3.9.4 by Alex (leontyyy@gmail.com) Dec.2011} +function TIFFReadEXIFDirectory(Handle: PTIFF; Diroff: toff_t): Integer; cdecl; external; + +function TIFFReadDirectory(Handle: PTIFF): Integer; cdecl; external; +function TIFFCurrentDirectory(Handle: PTIFF): Word; cdecl; external; +function TIFFCurrentDirOffset(Handle: PTIFF): {$ifdef VER403}int64{$else}Cardinal{$endif}; cdecl; external; +function TIFFLastDirectory(Handle: PTIFF): Integer; cdecl; external; +function TIFFNumberOfDirectories(Handle: PTIFF): Word; cdecl; external; +function TIFFSetDirectory(Handle: PTIFF; Dirn: Word): Integer; cdecl; external; +function TIFFSetSubDirectory(Handle: PTIFF; Diroff: {$ifdef VER403}int64{$else}Cardinal{$endif}): Integer; cdecl; external; +function TIFFCreateDirectory(Handle: PTIFF): Integer; cdecl; external; +function TIFFWriteDirectory(Handle: PTIFF): Integer; cdecl; external; +function TIFFUnlinkDirectory(handle: PTIFF; Dirn: Word): Integer; cdecl; external; +procedure TIFFPrintDirectory(Handle: PTIFF; Fd: Pointer; Flags: Integer); cdecl; external; + +function TIFFGetField(Handle: PTIFF; Tag: Cardinal): Integer; cdecl; external; varargs; +function TIFFGetFieldDefaulted(Handle: PTIFF; Tag: Cardinal): Integer; cdecl; external; varargs; +function TIFFVGetField(Handle: PTIFF; Tag: Cardinal; Ap: Pointer): Integer; cdecl; external; +function TIFFSetField(Handle: PTIFF; Tag: Cardinal): Integer; cdecl; external; varargs; +function TIFFVSetField(Handle: PTIFF; Tag: Cardinal; Ap: Pointer): Integer; cdecl; external; +function TIFFIsBigEndian(Handle: PTIFF): Integer; cdecl; external; +function TIFFIsTiled(Handle: PTIFF): Integer; cdecl; external; +function TIFFIsByteSwapped(Handle: PTIFF): Integer; cdecl; external; +function TIFFIsUpSampled(Handle: PTIFF): Integer; cdecl; external; +function TIFFIsMSB2LSB(Handle: PTIFF): Integer; cdecl; external; + +function TIFFGetTagListCount(Handle: PTIFF): Integer; cdecl; external; +function TIFFGetTagListEntry(Handle: PTIFF; TagIndex: Integer): Cardinal; cdecl; external; +procedure TIFFMergeFieldInfo(Handle: PTIFF; Info: PTIFFFieldInfo; N: Integer); cdecl; external; +function TIFFFindFieldInfo(Handle: PTIFF; Tag: Cardinal; Dt: Integer): PTIFFFieldInfo; cdecl; external; +function TIFFFindFieldInfoByName(Handle: PTIFF; FIeldName: PAnsiChar; Dt: Integer): PTIFFFieldInfo; cdecl; external; +function TIFFFieldWithTag(Handle: PTIFF; Tag: Cardinal): PTIFFFieldInfo; cdecl; external; +function TIFFFieldWithName(Handle: PTIFF; FieldName: PAnsiChar): PTIFFFieldInfo; cdecl; external; +function TIFFDataWidth(DataType: Integer): Integer; cdecl; external; + +function TIFFReadRGBAImage(Handle: PTIFF; RWidth,RHeight: Cardinal; Raster: Pointer; Stop: Integer): Integer; cdecl; external; +function TIFFReadRGBAImageOriented(Handle: PTIFF; RWidth,RHeight: Cardinal; Raster: Pointer; Orientation: Integer; Stop: Integer): Integer; cdecl; external; +function TIFFReadRGBAStrip(Handle: PTIFF; Row: Cardinal; Raster: Pointer): Integer; cdecl; external; +function TIFFReadRGBATile(Handle: PTIFF; Col,Row: Cardinal; Raster: Pointer): Integer; cdecl; external; +function TIFFRGBAImageOk(Handle: PTIFF; Emsg: PAnsiChar): Integer; cdecl; external; +function TIFFRGBAImageBegin(Img: PTIFFRGBAImage; Handle: PTIFF; Stop: Integer; Emsg: PAnsiChar): Integer; cdecl; external; +function TIFFRGBAImageGet(Img: PTIFFRGBAImage; Raster: Pointer; W,H: Cardinal): Integer; cdecl; external; +procedure TIFFRGBAImageEnd(Img: PTIFFRGBAImage); cdecl; external; + +function TIFFCurrentRow(Handle: PTIFF): Cardinal; cdecl; external; + +function TIFFStripSize(Handle: PTIFF): tmsize_t; cdecl; external; +function TIFFRawStripSize(Handle: PTIFF; Strip: Cardinal): tmsize_t; cdecl; external; +function TIFFVStripSize(Handle: PTIFF; NRows: Cardinal): tmsize_t; cdecl; external; +function TIFFDefaultStripSize(Handle: PTIFF; Request: Cardinal): Cardinal; cdecl; external; +function TIFFNumberOfStrips(Handle: PTIFF): Cardinal; cdecl; external; +function TIFFComputeStrip(Handle: PTIFF; Row: Cardinal; Sample: Word): Cardinal; cdecl; external; +function TIFFReadRawStrip(Handle: PTIFF; Strip: Cardinal; Buf: Pointer; Size: tmsize_t): tmsize_t; cdecl; external; +function TIFFReadEncodedStrip(Handle: PTIFF; Strip: Cardinal; Buf: Pointer; Size: tmsize_t): tmsize_t; cdecl; external; +function TIFFWriteRawStrip(Handle: PTIFF; Strip: Cardinal; Data: Pointer; Cc: tmsize_t): tmsize_t; cdecl; external; +function TIFFWriteEncodedStrip(Handle: PTIFF; Strip: Cardinal; Data: Pointer; Cc: tmsize_t): tmsize_t; cdecl; external; +function TIFFCurrentStrip(Handle: PTIFF): Cardinal; cdecl; external; + +function TIFFTileSize(Handle: PTIFF): tmsize_t; cdecl; external; +function TIFFTileRowSize(Handle: PTIFF): tmsize_t; cdecl; external; +function TIFFVTileSize(Handle: PTIFF; NRows: Cardinal): tmsize_t; cdecl; external; +procedure TIFFDefaultTileSize(Handle: PTIFF; Tw: PCardinal; Th: PCardinal); cdecl; external; +function TIFFNumberOfTiles(Handle: PTIFF): Cardinal; cdecl; external; +function TIFFComputeTile(Handle: PTIFF; X,Y,Z: Cardinal; S: Word): Cardinal; cdecl; external; +function TIFFReadRawTile(Handle: PTIFF; Tile: Cardinal; Buf: Pointer; Size: tmsize_t): tmsize_t; cdecl; external; +function TIFFReadEncodedTile(Handle: PTIFF; Tile: Cardinal; Buf: Pointer; Size: tmsize_t): tmsize_t; cdecl; external; +function TIFFWriteRawTile(Handle: PTIFF; Tile: Cardinal; Data: Pointer; Cc: tmsize_t): tmsize_t; cdecl; external; +function TIFFWriteEncodedTile(Handle: PTIFF; Tile: Cardinal; Data: Pointer; Cc: tmsize_t): tmsize_t; cdecl; external; +function TIFFCurrentTile(Handle: PTIFF): Cardinal; cdecl; external; + +function TIFFScanlineSize(Handle: PTIFF): tmsize_t; cdecl; external; +{$ifdef VER403} +function TIFFScanlineSize64(Handle: PTIFF): int64; cdecl; external; +function TIFFRasterScanlineSize64(Handle: PTIFF): int64; cdecl; external; +{$endif} +function TIFFRasterScanlineSize(Handle: PTIFF): tmsize_t; cdecl; external; +function TIFFReadScanline(Handle: PTIFF; Buf: Pointer; Row: Cardinal; Sample: Word): Integer; cdecl; external; +function TIFFWriteScanline(Handle: PTIFF; Buf: Pointer; Row: Cardinal; Sample: Word): Integer; cdecl; external; + +procedure TIFFSetWriteOffset(Handle: PTIFF; Off: toff_t); cdecl; external; + +procedure TIFFSwabShort(Wp: PWord); cdecl; external; +procedure TIFFSwabLong(Lp: PCardinal); cdecl; external; +procedure TIFFSwabDouble(Dp: PDouble); cdecl; external; +procedure TIFFSwabArrayOfShort(Wp: PWord; N: tmsize_t); cdecl; external; +{$ifdef VER403} +procedure TIFFSwabArrayOfTriples(tp:PByte; n: tmsize_t); cdecl; external; +{$endif} +procedure TIFFSwabArrayOfLong(Lp: PCardinal; N: tmsize_t); cdecl; external; +procedure TIFFSwabArrayOfDouble(Dp: PDouble; N: tmsize_t); cdecl; external; +procedure TIFFReverseBits(Cp: Pointer; N: tmsize_t); cdecl; external; +function TIFFGetBitRevTable(Reversed: Integer): Pointer; cdecl; external; + +function _TIFFmalloc(s: tmsize_t): Pointer; cdecl; {$ifdef FPC}[public];{$endif} +function _TIFFrealloc(p: Pointer; s: tmsize_t): Pointer; cdecl; {$ifdef FPC}[public];{$endif} +procedure _TIFFfree(p: Pointer); cdecl; {$ifdef FPC}[public];{$endif} + +type + TUserTiffErrorHandler = procedure(const Module, Message: AnsiString); + +procedure SetUserMessageHandlers(ErrorHandler, WarningHandler: TUserTiffErrorHandler); + +implementation + +uses + Math, +{$IF Defined(DCC) and (CompilerVersion < 20)} + Windows, +{$IFEND} + LibJpegDelphi, + ZLibDelphi; + +var + { For FPC 3.0+ these must be marked as exported } + _TIFFwarningHandler: TIFFErrorHandler; {$ifdef FPC}cvar; export;{$endif} + _TIFFerrorHandler: TIFFErrorHandler; {$ifdef FPC}cvar; export;{$endif} + +type + TCompareFunc = function(a,b: Pointer): Integer; cdecl; + +function floor(x: Double): Double; cdecl; forward; {$ifdef FPC}[public];{$endif} +function pow(x: Double; y: Double): Double; cdecl; forward; {$ifdef FPC}[public];{$endif} +function sqrt(x: Double): Double; cdecl; forward; {$ifdef FPC}[public];{$endif} +function atan2(y: Double; x: Double): Double; cdecl; forward; {$ifdef FPC}[public];{$endif} +function exp(x: Double): Double; cdecl; forward; {$ifdef FPC}[public];{$endif} +function log(x: Double): Double; cdecl; forward; {$ifdef FPC}[public];{$endif} +function fabs(x: Double): Double; cdecl; forward; +function rand: Integer; cdecl; forward; {$ifdef FPC}[public];{$endif} +function strlen(s: Pointer): Cardinal; cdecl; forward; {$ifdef FPC}[public];{$endif} +function strcmp(a: Pointer; b: Pointer): Integer; cdecl; forward; {$ifdef FPC}[public];{$endif} +function strncmp(a: Pointer; b: Pointer; c: Longint): Integer; cdecl; forward; {$ifdef FPC}[public];{$endif} +procedure qsort(base: Pointer; num: Cardinal; width: Cardinal; compare: TCompareFunc); cdecl; forward; {$ifdef FPC}[public];{$endif} +//DW function bsearch(key: Pointer; base: Pointer; nelem: Cardinal; width: Cardinal; fcmp: TCompareFunc): Pointer; cdecl; forward; +function memmove(dest: Pointer; src: Pointer; n: Cardinal): Pointer; cdecl; forward; {$ifdef FPC}[public];{$endif} +function strchr(s: Pointer; c: Integer): Pointer; cdecl; forward; {$ifdef FPC}[public];{$endif} + +procedure _TIFFmemcpy(d: Pointer; s: Pointer; c: tmsize_t); cdecl; forward; {$ifdef FPC}[public];{$endif} +procedure _TIFFmemset(p: Pointer; v: Integer; c: tmsize_t); cdecl; forward; {$ifdef FPC}[public];{$endif} +function _TIFFmemcmp(buf1: Pointer; buf2: Pointer; count: tmsize_t): Integer; cdecl; forward; {$ifdef FPC}[public];{$endif} + +function fabs(x: Double): Double; cdecl; +begin + if x<0 then + Result:=-x + else + Result:=x; +end; + +function atan2(y: Double; x: Double): Double; cdecl; +begin + Result:=ArcTan2(y,x); +end; + +function rand: Integer; cdecl; +begin + Result:=Trunc(Random*($7FFF+1)); +end; + +function sqrt(x: Double): Double; cdecl; +begin + Result:=System.Sqrt(x); +end; + +function log(x: Double): Double; cdecl; +begin + Result:=Ln(x); +end; + +function exp(x: Double): Double; cdecl; +begin + Result:=System.Exp(x); +end; + +function strchr(s: Pointer; c: Integer): Pointer; cdecl; +{$ifndef FPC} +begin + Result:=s; + while True do + begin + if PByte(Result)^=c then exit; + if PByte(Result)^=0 then + begin + Result:=nil; + exit; + end; + Inc(PByte(Result)); + end; +{$else} +begin + Result:=strchr(s,c); +{$endif} +end; + +function memmove(dest: Pointer; src: Pointer; n: Cardinal): Pointer; cdecl; +begin + system.Move(src^,dest^,n); + Result:=dest; +end; + +function _TIFFmemcmp(buf1: Pointer; buf2: Pointer; count: tmsize_t): Integer; + cdecl; +{$ifndef FPC} +var + ma,mb: PByte; + n: Integer; +begin + ma:=buf1; + mb:=buf2; + n:=0; + while Cardinal(n)<Count do + begin + if ma^<>mb^ then + begin + if ma^<mb^ then + Result:=-1 + else + Result:=1; + exit; + end; + Inc(ma); + Inc(mb); + Inc(n); + end; + Result:=0; +{$else} +begin + Result:=CompareMemRange(buf1,buf2,count); +{$endif} +end; + +procedure _TIFFmemset(p: Pointer; v: Integer; c: tmsize_t); cdecl; +begin + system.FillChar(p^,c,v); +end; + +procedure qsort(base: Pointer; num: Cardinal; width: Cardinal; compare: TCompareFunc); cdecl; +var + m: Pointer; + n: Integer; + o: Pointer; + oa,ob,oc: Integer; + p: Integer; +begin + if num<2 then exit; + + m:=AllocMem(num*width); + + if compare(base,Pointer(Ptruint(base)+width))<=0 then + Move(base^,m^,(width shl 1)) + else + begin + Move(Pointer(Ptruint(base)+width)^,m^,width); + Move(base^,Pointer(Ptruint(m)+width)^,width); + end; + n:=2; + while Ptruint(n)<num do + begin + o:=Pointer(Ptruint(base)+Ptruint(n)*width); + if compare(m,o)>=0 then + ob:=0 + else + begin + oa:=0; + ob:=n; + while oa+1<ob do + begin + oc:=((oa+ob) shr 1); + p:=compare(Pointer(Ptruint(m)+Ptruint(oc)*width),o); + if p<0 then + oa:=oc + else if p=0 then + begin + ob:=oc; + break; + end + else + ob:=oc; + end; + end; + if ob=0 then + begin + Move(m^,Pointer(Ptruint(m)+width)^,Ptruint(n)*width); + Move(o^,m^,width); + end + else if ob=n then + Move(o^,Pointer(Ptruint(m)+Ptruint(n)*width)^,width) + else + begin + Move(Pointer(Ptruint(m)+Ptruint(ob)*width)^,Pointer(Ptruint(m)+Ptruint(ob+1)*width)^,Ptruint(n-ob)*width); + Move(o^,Pointer(Ptruint(m)+Ptruint(ob)*width)^,width); + end; + Inc(n); + end; + system.Move(m^,base^,num*width); + FreeMem(m,num*width); +end; + +function _TIFFrealloc(p: Pointer; s: tmsize_t): Pointer; cdecl; +begin + if p=nil then + Result:=AllocMem(s) + else + Result := ReallocMemory(p,s); +end; + +function strncmp(a: Pointer; b: Pointer; c: Longint): Integer; cdecl; +var + ma,mb: PByte; + n: Integer; +begin + ma:=a; + mb:=b; + n:=0; + while n<c do + begin + if ma^<>mb^ then + begin + if ma^<mb^ then + Result:=-1 + else + Result:=1; + exit; + end; + if ma^=0 then + begin + Result:=0; + exit; + end; + Inc(ma); + Inc(mb); + Inc(n); + end; + Result:=0; +end; + +function strcmp(a: Pointer; b: Pointer): Integer; cdecl; +var + ma,mb: PByte; +begin + ma:=a; + mb:=b; + while True do + begin + if ma^<>mb^ then + begin + if ma^<mb^ then + Result:=-1 + else + Result:=1; + exit; + end; + if ma^=0 then + begin + Result:=0; + exit; + end; + Inc(ma); + Inc(mb); + end; + Result:=0; +end; + +function strlen(s: Pointer): Cardinal; cdecl; +{$ifndef fpc} +var + m: PByte; +{$endif} +begin + {$ifdef fpc} + Result:=system.strlen(s); + {$else} + Result:=0; + m:=s; + while m^<>0 do + begin + Inc(Result); + Inc(m); + end; + {$endif} +end; + +procedure _TIFFfree(p: Pointer); cdecl; +begin + FreeMem(p); +end; + +procedure _TIFFmemcpy(d: Pointer; s: Pointer; c: tmsize_t); cdecl; +begin + system.Move(s^,d^,c); +end; + +function pow(x: Double; y: Double): Double; cdecl; +begin + Result:=Power(x,y); +end; + +function floor(x: Double): Double; cdecl; +begin + Result:=Trunc(x); +end; + +function _TIFFmalloc(s: tmsize_t): Pointer; cdecl; +begin + Result:=AllocMem(s); +end; + +{LibTiffDelphi} + +var + UserTiffWarningHandler: TUserTiffErrorHandler; + UserTiffErrorHandler: TUserTiffErrorHandler; + +procedure SetUserMessageHandlers(ErrorHandler, WarningHandler: TUserTiffErrorHandler); +begin + UserTiffErrorHandler := ErrorHandler; + UserTiffWarningHandler := WarningHandler; +end; + +procedure FormatAndCallHandler(Handler: TUserTiffErrorHandler; Module: PAnsiChar; Format: PAnsiChar; Params: va_list); +var + Len: Integer; + Buffer: array[0..511] of AnsiChar; + Msg: AnsiString; +begin + Len := snprintf(@Buffer, 512, Format, Params); + SetString(Msg, Buffer, Len); + Handler(Module, Msg); +end; + +procedure InternalTIFFWarning(Module: PAnsiChar; Format: PAnsiChar; Params: va_list); cdecl; +begin + if Assigned(UserTiffWarningHandler) then + FormatAndCallHandler(UserTiffWarningHandler, Module, Format, Params); +end; + +procedure InternallTIFFError(Module: PAnsiChar; Format: PAnsiChar; Params: va_list); cdecl; +begin + if Assigned(UserTiffErrorHandler) then + FormatAndCallHandler(UserTiffErrorHandler, Module, Format, Params); +end; + +{tif_read} + +procedure _TIFFSwab16BitData(tif: Pointer; buf: Pointer; cc: Integer); cdecl; external; +procedure _TIFFSwab24BitData(tif: pointer; buf: pointer; cc: integer); cdecl; external; //DW 3.8.2 +procedure _TIFFSwab32BitData(tif: Pointer; buf: Pointer; cc: Integer); cdecl; external; +procedure _TIFFSwab64BitData(tif: Pointer; buf: Pointer; cc: Integer); cdecl; external; +procedure _TIFFNoPostDecode(tif: Pointer; buf: Pointer; cc: Integer); cdecl; external; +function TIFFReadTile(tif: Pointer; buf: Pointer; x: Cardinal; y: Cardinal; z: Cardinal; s: Word): tmsize_t; cdecl; external; +function TIFFFillTile(tif: Pointer; tile: longword):integer; cdecl; external; //DW 3.8.2 + +{tif_dirinfo} + +function _TIFFSampleToTagType(tif: Pointer): Integer; cdecl; external; +procedure _TIFFSetupFieldInfo(tif: Pointer); cdecl; external; +function _TIFFCreateAnonFieldInfo(tif: Pointer; tag: Cardinal; field_type: Integer): Pointer; cdecl; external; +function _TIFFGetExifFieldInfo(size : plongint):pointer; cdecl; external; //DW 3.8.2 +function _TIFFDataSize(TIFFDataType : longint):longint; cdecl; external; //DW 3.8.2 +function _TIFFGetFieldInfo(size : plongint):pointer; cdecl; external; //DW 3.8.2 +function _TIFFMergeFieldInfo(tif: Pointer; fieldinfo : Pointer; n : Integer):Integer; cdecl; external; //DW 3.9.1 + +{tif_dirwrite} + +{tif_flush} + +{tif_write} + +function TIFFFlushData1(tif: Pointer): Integer; cdecl; external; +function TIFFSetupStrips(tif: Pointer): Integer; cdecl; external; + +{tif_dumpmode} + +function TIFFInitDumpMode(tif: Pointer; scheme: Integer): Integer; cdecl; external; + +{tif_compress} + +function TIFFSetCompressionScheme(tif: Pointer; scheme: Integer): Integer; cdecl; external; +procedure _TIFFSetDefaultCompressionState(tif: Pointer); cdecl; external; + +{tif_dirread} + +{tif_dir} + +procedure TIFFFreeDirectory(tif: Pointer); cdecl; external; +function TIFFDefaultDirectory(tif: Pointer): Integer; cdecl; external; +function TIFFReassignTagToIgnore(task: Integer; TIFFtagID: Integer): Integer; cdecl; external; +procedure _TIFFsetString(cpp: Pointer; cp: Pointer); cdecl; external; +procedure _TIFFsetByteArray(vpp: Pointer; vp: Pointer; n: Integer); cdecl; external; + +{tif_aux} + +function TIFFVGetFieldDefaulted(tif: Pointer; tag: Cardinal; ap: Pointer): Integer; cdecl; external; + +{tif_color} + +procedure TIFFCIELabToXYZ(cielab: Pointer; l: Cardinal; a: Integer; b: Integer; X: Pointer; Y: Pointer; Z: Pointer); cdecl; external; +procedure TIFFXYZToRGB(cielab: Pointer; X: Single; Y: Single; Z: Single; r: Pointer; g: Pointer; b: Pointer); cdecl; external; +procedure TIFFYCbCrtoRGB(ycbcr: Pointer; Y: Cardinal; Cb: Integer; Cr: Integer; r: Pointer; g: Pointer; b: Pointer); cdecl; external; +function TIFFYCbCrToRGBInit(ycbcr: Pointer; luma: PSingle; refBlackWhite: PSingle): Integer; cdecl; external; +function TIFFCIELabToRGBInit(cielab: Pointer; display: Pointer; refWhite: Pointer): Integer; cdecl; external; + +{tif_close} + +{tif_extension} + +{tif_open} + +function _TIFFgetMode(mode: PAnsiChar; module: PAnsiChar): Integer; cdecl; external; + +{tif_getimage} + +{tif_predict} + +function TIFFPredictorInit(tif: PTIFF): Integer; cdecl; external; +function TIFFPredictorCleanup(tif: PTIFF):integer; cdecl; external; //DW 3.8.2 + +{tif_print} + +{tif_error} + +{tif_strip} + +function _TIFFDefaultStripSize(tif: Pointer; s: Cardinal): Cardinal; cdecl; external; +function TIFFOldScanlineSize(tif: Pointer):Cardinal; cdecl; external; //DW 3.9.1 + +{tif_swab} + +{tif_tile} + +function TIFFCheckTile(tif: Pointer; x: Cardinal; y: Cardinal; z: Cardinal; s: Word): Integer; cdecl; external; +procedure _TIFFDefaultTileSize(tif: Pointer; tw: Pointer; th: Pointer); cdecl; external; + +{tif_warning} + +{tif_fax3} + +function TIFFInitCCITTRLE(tif: PTIFF; scheme: Integer): Integer; cdecl; external; +function TIFFInitCCITTRLEW(tif: PTIFF; scheme: Integer): Integer; cdecl; external; +function TIFFInitCCITTFax3(tif: PTIFF; scheme: Integer): Integer; cdecl; external; +function TIFFInitCCITTFax4(tif: PTIFF; scheme: Integer): Integer; cdecl; external; + +{tif_fax3sm} + +{tif_jpeg} + +procedure TIFFjpeg_error_exit_raise(errcode:Integer); cdecl; {$ifdef FPC}[public];{$endif} +begin + raise Exception.Create(Format('jpeg error code %d',[errcode])); +end; + +function TIFFcallvjpeg_jpeg_CreateCompress(cinfo: Pointer; version: Integer; structsize: Cardinal): Integer; cdecl; {$ifdef FPC}[public];{$endif} +begin + try + jpeg_CreateCompress(cinfo,version,structsize); + Result:=1; + except + Result:=0; + end; +end; + +function TIFFcallvjpeg_jpeg_CreateDecompress(cinfo: Pointer; version: Integer; structsize: Cardinal): Integer; cdecl; {$ifdef FPC}[public];{$endif} +begin + try + jpeg_CreateDecompress(cinfo,version,structsize); + Result:=1; + except + Result:=0; + end; +end; + +function TIFFcallvjpeg_jpeg_set_defaults(cinfo: Pointer): Integer; cdecl; {$ifdef FPC}[public];{$endif} +begin + try + jpeg_set_defaults(cinfo); + Result:=1; + except + Result:=0; + end; +end; + +function TIFFcallvjpeg_jpeg_set_colorspace(cinfo: Pointer; colorspace: Integer): Integer; cdecl; {$ifdef FPC}[public];{$endif} +begin + try + jpeg_set_colorspace(cinfo, colorspace); + Result:=1; + except + Result:=0; + end; +end; + +function TIFFcallvjpeg_jpeg_set_quality(cinfo: Pointer; quality: Integer; force_baseline: Byte): Integer; cdecl; {$ifdef FPC}[public];{$endif} +begin + try + jpeg_set_quality(cinfo,quality,force_baseline); + Result:=1; + except + Result:=0; + end; +end; + +function TIFFcallvjpeg_jpeg_suppress_tables(cinfo: Pointer; suppress: Byte): Integer; cdecl; {$ifdef FPC}[public];{$endif} +begin + try + jpeg_suppress_tables(cinfo,suppress); + Result:=1; + except + Result:=0; + end; +end; + +function TIFFcallvjpeg_jpeg_start_compress(cinfo: Pointer; write_all_tables: Byte): Integer; cdecl; {$ifdef FPC}[public];{$endif} +begin + try + jpeg_start_compress(cinfo,write_all_tables); + Result:=1; + except + Result:=0; + end; +end; + +function TIFFcalljpeg_jpeg_write_scanlines(errreturn: Integer; cinfo: Pointer; scanlines: Pointer; num_lines: Cardinal): Integer; cdecl; {$ifdef FPC}[public];{$endif} +begin + try + Result:=jpeg_write_scanlines(cinfo,scanlines,num_lines); + except + Result:=errreturn; + end; +end; + +function TIFFcalljpeg_jpeg_write_raw_data(errreturn: Integer; cinfo: Pointer; data: Pointer; num_lines: Cardinal): Integer; cdecl; {$ifdef FPC}[public];{$endif} +begin + try + Result:=jpeg_write_raw_data(cinfo,data,num_lines); + except + Result:=errreturn; + end; +end; + +function TIFFcallvjpeg_jpeg_finish_compress(cinfo: Pointer): Integer; cdecl; {$ifdef FPC}[public];{$endif} +begin + try + jpeg_finish_compress(cinfo); + Result:=1; + except + Result:=0; + end; +end; + +function TIFFcallvjpeg_jpeg_write_tables(cinfo: Pointer): Integer; cdecl; {$ifdef FPC}[public];{$endif} +begin + try + jpeg_write_tables(cinfo); + Result:=1; + except + Result:=0; + end; +end; + +function TIFFcalljpeg_jpeg_read_header(errreturn: Integer; cinfo: Pointer; require_image: Byte): Integer; cdecl; {$ifdef FPC}[public];{$endif} +begin + try + Result:=jpeg_read_header(cinfo,Boolean(require_image)); + except + Result:=errreturn; + end; +end; + +function TIFFcallvjpeg_jpeg_start_decompress(cinfo: Pointer): Integer; cdecl; {$ifdef FPC}[public];{$endif} +begin + try + jpeg_start_decompress(cinfo); + Result:=1; + except + Result:=0; + end; +end; + +function TIFFcalljpeg_jpeg_read_scanlines(errreturn: Integer; cinfo: Pointer; scanlines: Pointer; max_lines: Cardinal): Integer; cdecl; {$ifdef FPC}[public];{$endif} +begin + try + Result:=jpeg_read_scanlines(cinfo,scanlines,max_lines); + except + Result:=errreturn; + end; +end; + +function TIFFcalljpeg_jpeg_read_raw_data(errreturn: Integer; cinfo: Pointer; data: Pointer; max_lines: Cardinal): Integer; cdecl; {$ifdef FPC}[public];{$endif} +begin + try + Result:=jpeg_read_raw_data(cinfo,data,max_lines); + except + Result:=errreturn; + end; +end; + +function TIFFcalljpeg_jpeg_finish_decompress(errreturn: Integer; cinfo: PRJpegDecompressStruct): Integer; cdecl; {$ifdef FPC}[public];{$endif} +begin + try + Result:=jpeg_finish_decompress(cinfo); + except + Result:=errreturn; + end; +end; + +function TIFFcallvjpeg_jpeg_abort(cinfo: Pointer): Integer; cdecl; {$ifdef FPC}[public];{$endif} +begin + try + jpeg_abort(cinfo); + Result:=1; + except + Result:=0; + end; +end; + +function TIFFcallvjpeg_jpeg_destroy(cinfo: Pointer): Integer; cdecl; {$ifdef FPC}[public];{$endif} +begin + try + jpeg_destroy(cinfo); + Result:=1; + except + Result:=0; + end; +end; + +type + jpeg_alloc_sarray = function(cinfo: Pointer; pool_id: Integer; samplesperrow: Cardinal; numrows: Cardinal): Pointer; cdecl; + +function TIFFcalljpeg_alloc_sarray(alloc_sarray: jpeg_alloc_sarray; cinfo: PRJpegCommonStruct; pool_id: Integer; samplesperrow: Cardinal; + numrows: Cardinal): Pointer; cdecl; {$ifdef FPC}[public];{$endif} +begin + try + Result:=alloc_sarray(cinfo,pool_id,samplesperrow,numrows); + except + Result:=nil; + end; +end; + +function TIFFInitJPEG(tif: PTIFF; scheme: Integer): Integer; cdecl; external; +function TIFFFillStrip(tif : PTIFF; Len : longword): integer; cdecl; external; //DW 3.8.2 + +{tif_luv} + +function TIFFInitSGILog(tif: PTIFF; scheme: Integer): Integer; cdecl; external; + +{tif_lzw} + +function TIFFInitLZW(tif: PTIFF; scheme: Integer): Integer; cdecl; external; + +{tif_next} + +function TIFFInitNeXT(tif: PTIFF; scheme: Integer): Integer; cdecl; external; + +{tif_packbits} + +function TIFFInitPackBits(tif: PTIFF; scheme: Integer): Integer; cdecl; external; + +{tif_pixarlog} + +function TIFFInitPixarLog(tif: PTIFF; scheme: Integer): Integer; cdecl; external; + +{tif_thunder} + +function TIFFInitThunderScan(tif: PTIFF; scheme: Integer): Integer; cdecl; external; + +{tif_version} + +{tif_zip} + +function TIFFInitZIP(tif: PTIFF; scheme: Integer): Integer; cdecl; external; + +{tif_codec} + +function NotConfigured(tif: PTIFF; scheme: Integer): Integer; cdecl; external; + +{DW +const + + _TIFFBuiltinCODECS: array[0..17] of TIFFCodec = ( + (name:'None'; scheme: COMPRESSION_NONE; init: TIFFInitDumpMode), + (name:'LZW'; scheme: COMPRESSION_LZW; init: TIFFInitLZW), + (name:'PackBits'; scheme: COMPRESSION_PACKBITS; init: TIFFInitPackBits), + (name:'ThunderScan'; scheme: COMPRESSION_THUNDERSCAN; init: TIFFInitThunderScan), + (name:'NeXT'; scheme: COMPRESSION_NEXT; init: TIFFInitNeXT), + (name:'JPEG'; scheme: COMPRESSION_JPEG; init: TIFFInitJPEG), + (name:'Old-style JPEG'; scheme: COMPRESSION_OJPEG; init: NotConfigured), + (name:'CCITT RLE'; scheme: COMPRESSION_CCITTRLE; init: TIFFInitCCITTRLE), + (name:'CCITT RLE/W'; scheme: COMPRESSION_CCITTRLEW; init: TIFFInitCCITTRLEW), + (name:'CCITT Group 3'; scheme: COMPRESSION_CCITTFAX3; init: TIFFInitCCITTFax3), + (name:'CCITT Group 4'; scheme: COMPRESSION_CCITTFAX4; init: TIFFInitCCITTFax4), + (name:'ISO JBIG'; scheme: COMPRESSION_JBIG; init: NotConfigured), + (name:'Deflate'; scheme: COMPRESSION_DEFLATE; init: TIFFInitZIP), + (name:'AdobeDeflate'; scheme: COMPRESSION_ADOBE_DEFLATE; init: TIFFInitZIP), + (name:'PixarLog'; scheme: COMPRESSION_PIXARLOG; init: TIFFInitPixarLog), + (name:'SGILog'; scheme: COMPRESSION_SGILOG; init: TIFFInitSGILog), + (name:'SGILog24'; scheme: COMPRESSION_SGILOG24; init: TIFFInitSGILog), + (name:nil; scheme:0; init:nil)); +} + +{LibTiffDelphi} + +function TIFFFileReadProc(Fd: THandle; Buffer: Pointer; Size: Integer): Integer; cdecl; forward; +function TIFFFileWriteProc(Fd: THandle; Buffer: Pointer; Size: Integer): Integer; cdecl; forward; +function TIFFFileSizeProc(Fd: THandle): {$ifdef VER403}int64{$else}Cardinal{$endif}; cdecl; forward; +function TIFFFileSeekProc(Fd: THandle; Off: {$ifdef VER403}int64{$else}Cardinal{$endif}; Whence: Integer): {$ifdef VER403}int64{$else}Cardinal{$endif}; cdecl; forward; +function TIFFFileCloseProc(Fd: THandle): Integer; cdecl; forward; + +function TIFFNoMapProc(Fd: THandle; PBase: PPointer; PSize: {$ifdef VER403}PInt64{$else}PCardinal{$endif}): Integer; cdecl; forward; +procedure TIFFNoUnmapProc(Fd: THandle; Base: Pointer; Size: {$ifdef VER403}int64{$else}Cardinal{$endif}); cdecl; forward; + +function TIFFFileCloseProc(Fd: THandle): Integer; cdecl; +begin + FileClose(Fd); + Result:=0; + { + if CloseHandle(Fd)=True then + Result:=0 + else + Result:=-1; + } +end; + +const + SEEK_SET = 0; + SEEK_CUR = 1; + SEEK_END = 2; + +function TIFFFileSizeProc(Fd: THandle): {$ifdef VER403}int64{$else}Cardinal{$endif}; cdecl; +begin + Result:=FileSeek(fd, 0, SEEK_END); + {$ifndef VER403} + if Result<>longword(-1) then + Result:=0; + {$endif} + //Result:=GetFileSize(Fd,nil); +end; + +function TIFFFileSeekProc(Fd: THandle; Off: {$ifdef VER403}int64{$else}Cardinal{$endif}; Whence: Integer): {$ifdef VER403}int64{$else}Cardinal{$endif}; cdecl; +begin + if Off=longword(-1) then + begin + Result:=longword(-1); + exit; + end; + Result:=FileSeek(Fd,Off,Whence); +end; + +function TIFFFileReadProc(Fd: THandle; Buffer: Pointer; Size: Integer): Integer; cdecl; +begin + Result:=FileRead(Fd,Buffer^,Cardinal(Size)); + if Result<0 then + Result:=0; +end; + +function TIFFFileWriteProc(Fd: THandle; Buffer: Pointer; Size: Integer): Integer; cdecl; +begin + Result:=FileWrite(Fd,Buffer^,Cardinal(Size)); + if Result<0 then + Result:=0; +end; + +function TIFFNoMapProc(Fd: THandle; PBase: PPointer; PSize: {$ifdef VER403}PInt64{$else}PCardinal{$endif}): Integer; cdecl; +begin + Result:=0; +end; + +procedure TIFFNoUnmapProc(Fd: THandle; Base: Pointer; Size: {$ifdef VER403}int64{$else}Cardinal{$endif}); cdecl; +begin +end; + +function TIFFOpen(const Name: AnsiString; const Mode: AnsiString): PTIFF; +const + Module: AnsiString = 'TIFFOpen'; + O_RDONLY = 0; + O_WRONLY = 1; + O_RDWR = 2; + O_CREAT = $0100; + O_TRUNC = $0200; +var + m: Integer; + DesiredAccess: Cardinal; + fd: THandle; + InvalidHandle: THandle; +begin + m:=_TIFFgetMode(PAnsiChar(Mode),PAnsiChar(Module)); + if m=o_RDONLY then + DesiredAccess:=fmOpenRead + else + DesiredAccess:=fmOpenReadWrite; + + case m of + O_RDONLY: DesiredAccess:=fmOpenRead; + O_RDWR: DesiredAccess:=fmOpenReadWrite; + (O_RDWR or O_CREAT): DesiredAccess:=DesiredAccess or fmCreate; + (O_RDWR or O_TRUNC): DesiredAccess:=fmCreate; + (O_RDWR or O_CREAT or O_TRUNC): DesiredAccess:=fmCreate; + else + Result:=nil; + exit; + end; + +{$IFDEF DCC} + InvalidHandle := INVALID_HANDLE_VALUE; +{$ELSE} + InvalidHandle := feInvalidHandle; +{$ENDIF} + + if DesiredAccess = fmCreate then + fd := FileCreate(Name, fmShareDenyWrite) + else + fd := FileOpen(Name, fmShareDenyWrite or DesiredAccess); + + if fd = InvalidHandle then + begin + TiffError(PAnsiChar(Module), PAnsiChar('Cannot open file: ' + Name), nil); + Result:=nil; + exit; + end; + + Result := TIFFClientOpen(PAnsiChar(Name), PAnsiChar(Mode), fd, + TIFFReadWriteProc(@TIFFFileReadProc), TIFFReadWriteProc(@TIFFFileWriteProc), TIFFSeekProc(@TIFFFileSeekProc), TIFFCloseProc(@TIFFFileCloseProc), + TIFFSizeProc(@TIFFFileSizeProc), TIFFMapFileProc(@TIFFNoMapProc), TIFFUnmapFileProc(@TIFFNoUnmapProc)); + + if Result <> nil then + TIFFSetFileno(Result,fd) + else + FileClose(fd); +end; + +{$IF Defined(DCC) and Defined(MSWINDOWS) and not Defined(CPUX64)} + // Delphi Win32 + {$L Compiled\tif_read.obj} + {$L Compiled\tif_dirinfo.obj} + {$L Compiled\tif_dirwrite.obj} + {$L Compiled\tif_flush.obj} + {$L Compiled\tif_write.obj} + {$L Compiled\tif_dumpmode.obj} + {$L Compiled\tif_compress.obj} + {$L Compiled\tif_dirread.obj} + {$L Compiled\tif_dir.obj} + {$L Compiled\tif_aux.obj} + {$L Compiled\tif_color.obj} + {$L Compiled\tif_close.obj} + {$L Compiled\tif_extension.obj} + {$L Compiled\tif_open.obj} + {$L Compiled\tif_getimage.obj} + {$L Compiled\tif_predict.obj} + {$L Compiled\tif_print.obj} + {$L Compiled\tif_error.obj} + {$L Compiled\tif_strip.obj} + {$L Compiled\tif_swab.obj} + {$L Compiled\tif_tile.obj} + {$L Compiled\tif_warning.obj} + {$L Compiled\tif_fax3.obj} + {$L Compiled\tif_fax3sm.obj} + {$L Compiled\tif_jpeg.obj} + {$L Compiled\tif_luv.obj} + {$L Compiled\tif_lzw.obj} + {$L Compiled\tif_next.obj} + {$L Compiled\tif_packbits.obj} + {$L Compiled\tif_pixarlog.obj} + {$L Compiled\tif_thunder.obj} + {$L Compiled\tif_version.obj} + {$L Compiled\tif_zip.obj} + {$L Compiled\tif_codec.obj} +{$ELSEIF Defined(FPC) and Defined(WIN32)} + // Windows 32bit FPC - COFF format lib + {$LINKLIB libtiffpack-win32.a} +{$ELSEIF Defined(FPC) and Defined(WIN64)} + // Windows 64bit FPC - COFF format lib + {$LINKLIB libtiffpack-win64.a} +{$IFEND} + +initialization + TIFFSetWarningHandler(@InternalTIFFWarning); + TIFFSetErrorHandler(@InternallTIFFError); + +end. + diff --git a/resources/libraries/deskew/Imaging/LibTiff/LibTiffDynLib.pas b/resources/libraries/deskew/Imaging/LibTiff/LibTiffDynLib.pas new file mode 100755 index 0000000..0f2a9d2 --- /dev/null +++ b/resources/libraries/deskew/Imaging/LibTiff/LibTiffDynLib.pas @@ -0,0 +1,803 @@ +unit LibTiffDynLib; + +{$IFDEF FPC} + {$MODE DELPHI} +{$ENDIF} + +// We prefer dynamic loading of the library (GetProcAddress/dlsym way) +// so that we don't get a crash with "libtiff not found!" message on startup +// if libtiff is not found in user's system. +{$DEFINE DYNAMIC_DLL_LOADING} + +interface + +uses +{$IFDEF MSWINDOWS} + Windows, +{$ENDIF} + SysUtils; + +type + va_list = Pointer; +{$IFNDEF FPC} +{$IF CompilerVersion <= 18.5} + SizeInt = Integer; +{$ELSE} + SizeInt = NativeInt; +{$IFEND} +{$ENDIF} + +type + tmsize_t = SizeInt; + tsize_t = SizeInt; + // typedef uint64 toff_t; /* file offset */ + toff_t = Int64; + poff_t = ^toff_t; + tsample_t = Word; + // Beware: THandle is 32bit in size even on 64bit Linux - this may cause + // problems as pointers to client data are passed in thandle_t vars. + thandle_t = THandle; + tdata_t = Pointer; + ttag_t = LongWord; + tdir_t = Word; + tstrip_t = LongWord; + +const + // LibTiff 4.0 +{$IF Defined(MSWINDOWS)} + SLibName = 'libtiff.dll'; // make sure you have DLL with the same bitness as your app! +{$ELSEIF Defined(DARWIN)} // macOS + SLibName = 'libtiff.5.dylib'; +{$ELSE} // Linux, BSD + SLibName = 'libtiff.so.5'; // yes, SONAME for libtiff v4.0 is actually libtiff5 (and libtiff.so.4 is libtiff v3.9) +{$IFEND} + + TIFF_NOTYPE = 0; + 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) } + TIFF_UNICODE = 14; + TIFF_COMPLEX = 15; + TIFF_LONG8 = 16; + TIFF_SLONG8 = 17; + TIFF_IFD8 = 18; + + TIFFTAG_SUBFILETYPE = 254; { subfile data descriptor } + FILETYPE_REDUCEDIMAGE = $1; { reduced resolution version } + FILETYPE_PAGE = $2; { one page of many } + FILETYPE_MASK = $4; { transparency mask } + TIFFTAG_OSUBFILETYPE = 255; { kind of data in subfile } + OFILETYPE_IMAGE = 1; { full resolution image data } + OFILETYPE_REDUCEDIMAGE = 2; { reduced size image data } + OFILETYPE_PAGE = 3; { one page of many } + TIFFTAG_IMAGEWIDTH = 256; { image width in pixels } + TIFFTAG_IMAGELENGTH = 257; { image height in pixels } + TIFFTAG_BITSPERSAMPLE = 258; { bits per channel (sample) } + TIFFTAG_COMPRESSION = 259; { data compression technique } + COMPRESSION_NONE = 1; { dump mode } + COMPRESSION_CCITTRLE = 2; { CCITT modified Huffman RLE } + COMPRESSION_CCITTFAX3 = 3; { CCITT Group 3 fax encoding } + COMPRESSION_CCITT_T4 = 3; { CCITT T.4 (TIFF 6 name) } + COMPRESSION_CCITTFAX4 = 4; { CCITT Group 4 fax encoding } + COMPRESSION_CCITT_T6 = 4; { CCITT T.6 (TIFF 6 name) } + COMPRESSION_LZW = 5; { Lempel-Ziv & Welch } + COMPRESSION_OJPEG = 6; { !6.0 JPEG } + COMPRESSION_JPEG = 7; { %JPEG DCT compression } + COMPRESSION_NEXT = 32766; { NeXT 2-bit RLE } + COMPRESSION_CCITTRLEW = 32771; { #1 w/ word alignment } + COMPRESSION_PACKBITS = 32773; { Macintosh RLE } + COMPRESSION_THUNDERSCAN = 32809; { ThunderScan RLE } + { codes 32895-32898 are reserved for ANSI IT8 TIFF/IT <dkelly@apago.com) } + COMPRESSION_IT8CTPAD = 32895; { IT8 CT w/padding } + COMPRESSION_IT8LW = 32896; { IT8 Linework RLE } + COMPRESSION_IT8MP = 32897; { IT8 Monochrome picture } + COMPRESSION_IT8BL = 32898; { IT8 Binary line art } + { compression codes 32908-32911 are reserved for Pixar } + COMPRESSION_PIXARFILM = 32908; { Pixar companded 10bit LZW } + COMPRESSION_PIXARLOG = 32909; { Pixar companded 11bit ZIP } + COMPRESSION_DEFLATE = 32946; { Deflate compression } + COMPRESSION_ADOBE_DEFLATE = 8; { Deflate compression, as recognized by Adobe } + { compression code 32947 is reserved for Oceana Matrix <dev@oceana.com> } + COMPRESSION_DCS = 32947; { Kodak DCS encoding } + COMPRESSION_JBIG = 34661; { ISO JBIG } + COMPRESSION_SGILOG = 34676; { SGI Log Luminance RLE } + COMPRESSION_SGILOG24 = 34677; { SGI Log 24-bit packed } + COMPRESSION_JP2000 = 34712; { Leadtools JPEG2000 } + TIFFTAG_PHOTOMETRIC = 262; { photometric interpretation } + PHOTOMETRIC_MINISWHITE = 0; { min value is white } + PHOTOMETRIC_MINISBLACK = 1; { min value is black } + PHOTOMETRIC_RGB = 2; { RGB color model } + PHOTOMETRIC_PALETTE = 3; { color map indexed } + PHOTOMETRIC_MASK = 4; { $holdout mask } + PHOTOMETRIC_SEPARATED = 5; { !color separations } + PHOTOMETRIC_YCBCR = 6; { !CCIR 601 } + PHOTOMETRIC_CIELAB = 8; { !1976 CIE L*a*b* } + PHOTOMETRIC_ICCLAB = 9; { ICC L*a*b* [Adobe TIFF Technote 4] } + PHOTOMETRIC_ITULAB = 10; { ITU L*a*b* } + PHOTOMETRIC_LOGL = 32844; { CIE Log2(L) } + PHOTOMETRIC_LOGLUV = 32845; { CIE Log2(L) (u',v') } + TIFFTAG_THRESHHOLDING = 263; { thresholding used on data } + THRESHHOLD_BILEVEL = 1; { b&w art scan } + THRESHHOLD_HALFTONE = 2; { or dithered scan } + THRESHHOLD_ERRORDIFFUSE = 3; { usually floyd-steinberg } + TIFFTAG_CELLWIDTH = 264; { +dithering matrix width } + TIFFTAG_CELLLENGTH = 265; { +dithering matrix height } + TIFFTAG_FILLORDER = 266; { data order within a byte } + FILLORDER_MSB2LSB = 1; { most significant -> least } + FILLORDER_LSB2MSB = 2; { least significant -> most } + TIFFTAG_DOCUMENTNAME = 269; { name of doc. image is from } + TIFFTAG_IMAGEDESCRIPTION = 270; { info about image } + TIFFTAG_MAKE = 271; { scanner manufacturer name } + TIFFTAG_MODEL = 272; { scanner model name/number } + TIFFTAG_STRIPOFFSETS = 273; { offsets to data strips } + TIFFTAG_ORIENTATION = 274; { +image orientation } + ORIENTATION_TOPLEFT = 1; { row 0 top, col 0 lhs } + ORIENTATION_TOPRIGHT = 2; { row 0 top, col 0 rhs } + ORIENTATION_BOTRIGHT = 3; { row 0 bottom, col 0 rhs } + ORIENTATION_BOTLEFT = 4; { row 0 bottom, col 0 lhs } + ORIENTATION_LEFTTOP = 5; { row 0 lhs, col 0 top } + ORIENTATION_RIGHTTOP = 6; { row 0 rhs, col 0 top } + ORIENTATION_RIGHTBOT = 7; { row 0 rhs, col 0 bottom } + ORIENTATION_LEFTBOT = 8; { row 0 lhs, col 0 bottom } + TIFFTAG_SAMPLESPERPIXEL = 277; { samples per pixel } + TIFFTAG_ROWSPERSTRIP = 278; { rows per strip of data } + TIFFTAG_STRIPBYTECOUNTS = 279; { bytes counts for strips } + TIFFTAG_MINSAMPLEVALUE = 280; { +minimum sample value } + TIFFTAG_MAXSAMPLEVALUE = 281; { +maximum sample value } + TIFFTAG_XRESOLUTION = 282; { pixels/resolution in x } + TIFFTAG_YRESOLUTION = 283; { pixels/resolution in y } + TIFFTAG_PLANARCONFIG = 284; { storage organization } + PLANARCONFIG_CONTIG = 1; { single image plane } + PLANARCONFIG_SEPARATE = 2; { separate planes of data } + TIFFTAG_PAGENAME = 285; { page name image is from } + TIFFTAG_XPOSITION = 286; { x page offset of image lhs } + TIFFTAG_YPOSITION = 287; { y page offset of image lhs } + TIFFTAG_FREEOFFSETS = 288; { +byte offset to free block } + TIFFTAG_FREEBYTECOUNTS = 289; { +sizes of free blocks } + + {matched with tag reference up to this point} + + TIFFTAG_GRAYRESPONSEUNIT = 290; { $gray scale curve accuracy } + GRAYRESPONSEUNIT_10S = 1; { tenths of a unit } + GRAYRESPONSEUNIT_100S = 2; { hundredths of a unit } + GRAYRESPONSEUNIT_1000S = 3; { thousandths of a unit } + GRAYRESPONSEUNIT_10000S = 4; { ten-thousandths of a unit } + GRAYRESPONSEUNIT_100000S = 5; { hundred-thousandths } + TIFFTAG_GRAYRESPONSECURVE = 291; { $gray scale response curve } + TIFFTAG_GROUP3OPTIONS = 292; { 32 flag bits } + TIFFTAG_T4OPTIONS = 292; { TIFF 6.0 proper name alias } + GROUP3OPT_2DENCODING = $1; { 2-dimensional coding } + GROUP3OPT_UNCOMPRESSED = $2; { data not compressed } + GROUP3OPT_FILLBITS = $4; { fill to byte boundary } + TIFFTAG_GROUP4OPTIONS = 293; { 32 flag bits } + TIFFTAG_T6OPTIONS = 293; { TIFF 6.0 proper name } + GROUP4OPT_UNCOMPRESSED = $2; { data not compressed } + TIFFTAG_RESOLUTIONUNIT = 296; { units of resolutions } + RESUNIT_NONE = 1; { no meaningful units } + RESUNIT_INCH = 2; { english } + RESUNIT_CENTIMETER = 3; { metric } + TIFFTAG_PAGENUMBER = 297; { page numbers of multi-page } + TIFFTAG_COLORRESPONSEUNIT = 300; { $color curve accuracy } + COLORRESPONSEUNIT_10S = 1; { tenths of a unit } + COLORRESPONSEUNIT_100S = 2; { hundredths of a unit } + COLORRESPONSEUNIT_1000S = 3; { thousandths of a unit } + COLORRESPONSEUNIT_10000S = 4; { ten-thousandths of a unit } + COLORRESPONSEUNIT_100000S = 5; { hundred-thousandths } + TIFFTAG_TRANSFERFUNCTION = 301; { !colorimetry info } + TIFFTAG_SOFTWARE = 305; { name & release } + TIFFTAG_DATETIME = 306; { creation date and time } + TIFFTAG_ARTIST = 315; { creator of image } + TIFFTAG_HOSTCOMPUTER = 316; { machine where created } + TIFFTAG_PREDICTOR = 317; { prediction scheme w/ LZW } + TIFFTAG_WHITEPOINT = 318; { image white point } + TIFFTAG_PRIMARYCHROMATICITIES = 319; { !primary chromaticities } + TIFFTAG_COLORMAP = 320; { RGB map for pallette image } + TIFFTAG_HALFTONEHINTS = 321; { !highlight+shadow info } + TIFFTAG_TILEWIDTH = 322; { !rows/data tile } + TIFFTAG_TILELENGTH = 323; { !cols/data tile } + TIFFTAG_TILEOFFSETS = 324; { !offsets to data tiles } + TIFFTAG_TILEBYTECOUNTS = 325; { !byte counts for tiles } + TIFFTAG_BADFAXLINES = 326; { lines w/ wrong pixel count } + TIFFTAG_CLEANFAXDATA = 327; { regenerated line info } + CLEANFAXDATA_CLEAN = 0; { no errors detected } + CLEANFAXDATA_REGENERATED = 1; { receiver regenerated lines } + CLEANFAXDATA_UNCLEAN = 2; { uncorrected errors exist } + TIFFTAG_CONSECUTIVEBADFAXLINES = 328; { max consecutive bad lines } + TIFFTAG_SUBIFD = 330; { subimage descriptors } + TIFFTAG_INKSET = 332; { !inks in separated image } + INKSET_CMYK = 1; { !cyan-magenta-yellow-black color } + INKSET_MULTIINK = 2; { !multi-ink or hi-fi color } + TIFFTAG_INKNAMES = 333; { !ascii names of inks } + TIFFTAG_NUMBEROFINKS = 334; { !number of inks } + TIFFTAG_DOTRANGE = 336; { !0% and 100% dot codes } + TIFFTAG_TARGETPRINTER = 337; { !separation target } + TIFFTAG_EXTRASAMPLES = 338; { !info about extra samples } + EXTRASAMPLE_UNSPECIFIED = 0; { !unspecified data } + EXTRASAMPLE_ASSOCALPHA = 1; { !associated alpha data } + EXTRASAMPLE_UNASSALPHA = 2; { !unassociated alpha data } + TIFFTAG_SAMPLEFORMAT = 339; { !data sample format } + SAMPLEFORMAT_UINT = 1; { !unsigned integer data } + SAMPLEFORMAT_INT = 2; { !signed integer data } + SAMPLEFORMAT_IEEEFP = 3; { !IEEE floating point data } + SAMPLEFORMAT_VOID = 4; { !untyped data } + SAMPLEFORMAT_COMPLEXINT = 5; { !complex signed int } + SAMPLEFORMAT_COMPLEXIEEEFP = 6; { !complex ieee floating } + TIFFTAG_SMINSAMPLEVALUE = 340; { !variable MinSampleValue } + TIFFTAG_SMAXSAMPLEVALUE = 341; { !variable MaxSampleValue } + TIFFTAG_CLIPPATH = 343; { %ClipPath [Adobe TIFF technote 2] } + TIFFTAG_XCLIPPATHUNITS = 344; { %XClipPathUnits [Adobe TIFF technote 2] } + TIFFTAG_YCLIPPATHUNITS = 345; { %YClipPathUnits [Adobe TIFF technote 2] } + TIFFTAG_INDEXED = 346; { %Indexed [Adobe TIFF Technote 3] } + TIFFTAG_JPEGTABLES = 347; { %JPEG table stream } + 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. } + TIFFTAG_JPEGPROC = 512; { !JPEG processing algorithm } + JPEGPROC_BASELINE = 1; { !baseline sequential } + JPEGPROC_LOSSLESS = 14; { !Huffman coded lossless } + TIFFTAG_JPEGIFOFFSET = 513; { !pointer to SOI marker } + TIFFTAG_JPEGIFBYTECOUNT = 514; { !JFIF stream length } + TIFFTAG_JPEGRESTARTINTERVAL = 515; { !restart interval length } + TIFFTAG_JPEGLOSSLESSPREDICTORS = 517; { !lossless proc predictor } + TIFFTAG_JPEGPOINTTRANSFORM = 518; { !lossless point transform } + TIFFTAG_JPEGQTABLES = 519; { !Q matrice offsets } + TIFFTAG_JPEGDCTABLES = 520; { !DCT table offsets } + TIFFTAG_JPEGACTABLES = 521; { !AC coefficient offsets } + TIFFTAG_YCBCRCOEFFICIENTS = 529; { !RGB -> YCbCr transform } + TIFFTAG_YCBCRSUBSAMPLING = 530; { !YCbCr subsampling factors } + TIFFTAG_YCBCRPOSITIONING = 531; { !subsample positioning } + YCBCRPOSITION_CENTERED = 1; { !as in PostScript Level 2 } + YCBCRPOSITION_COSITED = 2; { !as in CCIR 601-1 } + TIFFTAG_REFERENCEBLACKWHITE = 532; { !colorimetry info } + TIFFTAG_XMLPACKET = 700; { %XML packet [Adobe XMP technote 9-14-02] (dkelly@apago.com) } + TIFFTAG_OPIIMAGEID = 32781; { %OPI ImageID [Adobe TIFF technote] } + { tags 32952-32956 are private tags registered to Island Graphics } + TIFFTAG_REFPTS = 32953; { image reference points } + TIFFTAG_REGIONTACKPOINT = 32954; { region-xform tack point } + TIFFTAG_REGIONWARPCORNERS = 32955; { warp quadrilateral } + TIFFTAG_REGIONAFFINE = 32956; { affine transformation mat } + { tags 32995-32999 are private tags registered to SGI } + TIFFTAG_MATTEING = 32995; { $use ExtraSamples } + TIFFTAG_DATATYPE = 32996; { $use SampleFormat } + TIFFTAG_IMAGEDEPTH = 32997; { z depth of image } + 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. } + TIFFTAG_PIXAR_IMAGEFULLWIDTH = 33300; { full image size in x } + 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. } + TIFFTAG_PIXAR_TEXTUREFORMAT = 33302; { texture map format } + TIFFTAG_PIXAR_WRAPMODES = 33303; { s & t wrap modes } + TIFFTAG_PIXAR_FOVCOT = 33304; { cotan(fov) for env. maps } + TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN = 33305; + TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA = 33306; + { tag 33405 is a private tag registered to Eastman Kodak } + TIFFTAG_WRITERSERIALNUMBER = 33405; { device serial number } + { tag 33432 is listed in the 6.0 spec w/ unknown ownership } + TIFFTAG_COPYRIGHT = 33432; { copyright string } + { IPTC TAG from RichTIFF specifications } + TIFFTAG_RICHTIFFIPTC = 33723; + { 34016-34029 are reserved for ANSI IT8 TIFF/IT <dkelly@apago.com) } + TIFFTAG_IT8SITE = 34016; { site name } + TIFFTAG_IT8COLORSEQUENCE = 34017; { color seq. [RGB,CMYK,etc] } + TIFFTAG_IT8HEADER = 34018; { DDES Header } + TIFFTAG_IT8RASTERPADDING = 34019; { raster scanline padding } + TIFFTAG_IT8BITSPERRUNLENGTH = 34020; { # of bits in short run } + TIFFTAG_IT8BITSPEREXTENDEDRUNLENGTH = 34021; { # of bits in long run } + TIFFTAG_IT8COLORTABLE = 34022; { LW colortable } + TIFFTAG_IT8IMAGECOLORINDICATOR = 34023; { BP/BL image color switch } + TIFFTAG_IT8BKGCOLORINDICATOR = 34024; { BP/BL bg color switch } + TIFFTAG_IT8IMAGECOLORVALUE = 34025; { BP/BL image color value } + TIFFTAG_IT8BKGCOLORVALUE = 34026; { BP/BL bg color value } + TIFFTAG_IT8PIXELINTENSITYRANGE = 34027; { MP pixel intensity value } + TIFFTAG_IT8TRANSPARENCYINDICATOR = 34028; { HC transparency switch } + TIFFTAG_IT8COLORCHARACTERIZATION = 34029; { color character. table } + TIFFTAG_IT8HCUSAGE = 34030; { HC usage indicator } + TIFFTAG_IT8TRAPINDICATOR = 34031; { Trapping indicator (untrapped=0, trapped=1) } + TIFFTAG_IT8CMYKEQUIVALENT = 34032; { CMYK color equivalents } + { tags 34232-34236 are private tags registered to Texas Instruments } + TIFFTAG_FRAMECOUNT = 34232; { Sequence Frame Count } + { tag 34750 is a private tag registered to Adobe? } + TIFFTAG_ICCPROFILE = 34675; { ICC profile data } + { tag 34377 is private tag registered to Adobe for PhotoShop } + TIFFTAG_PHOTOSHOP = 34377; + { tag 34750 is a private tag registered to Pixel Magic } + TIFFTAG_JBIGOPTIONS = 34750; { JBIG options } + { tags 34908-34914 are private tags registered to SGI } + TIFFTAG_FAXRECVPARAMS = 34908; { encoded Class 2 ses. parms } + TIFFTAG_FAXSUBADDRESS = 34909; { received SubAddr string } + TIFFTAG_FAXRECVTIME = 34910; { receive time (secs) } + { tags 37439-37443 are registered to SGI <gregl@sgi.com> } + TIFFTAG_STONITS = 37439; { Sample value to Nits } + { tag 34929 is a private tag registered to FedEx } + TIFFTAG_FEDEX_EDR = 34929; { unknown use } + { tag 65535 is an undefined tag used by Eastman Kodak } + 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. } + TIFFTAG_FAXMODE = 65536; { Group 3/4 format control } + FAXMODE_CLASSIC = $0; { default, include RTC } + FAXMODE_NORTC = $1; { no RTC at end of data } + FAXMODE_NOEOL = $2; { no EOL code at end of row } + FAXMODE_BYTEALIGN = $4; { byte align row } + FAXMODE_WORDALIGN = $8; { word align row } + FAXMODE_CLASSF = FAXMODE_NORTC; { TIFF Class F } + TIFFTAG_JPEGQUALITY = 65537; { Compression quality level } + { Note: quality level is on the IJG 0-100 scale. Default value is 75 } + TIFFTAG_JPEGCOLORMODE = 65538; { Auto RGB<=>YCbCr convert? } + JPEGCOLORMODE_RAW = $0; { no conversion (default) } + JPEGCOLORMODE_RGB = $1; { do auto conversion } + TIFFTAG_JPEGTABLESMODE = 65539; { What to put in JPEGTables } + JPEGTABLESMODE_QUANT = $1; { include quantization tbls } + JPEGTABLESMODE_HUFF = $2; { include Huffman tbls } + { Note: default is JPEGTABLESMODE_QUANT | JPEGTABLESMODE_HUFF } + TIFFTAG_FAXFILLFUNC = 65540; { G3/G4 fill function } + TIFFTAG_PIXARLOGDATAFMT = 65549; { PixarLogCodec I/O data sz } + PIXARLOGDATAFMT_8BIT = 0; { regular u_char samples } + PIXARLOGDATAFMT_8BITABGR = 1; { ABGR-order u_chars } + PIXARLOGDATAFMT_11BITLOG = 2; { 11-bit log-encoded (raw) } + PIXARLOGDATAFMT_12BITPICIO = 3; { as per PICIO (1.0==2048) } + PIXARLOGDATAFMT_16BIT = 4; { signed short samples } + PIXARLOGDATAFMT_FLOAT = 5; { IEEE float samples } + { 65550-65556 are allocated to Oceana Matrix <dev@oceana.com> } + TIFFTAG_DCSIMAGERTYPE = 65550; { imager model & filter } + DCSIMAGERMODEL_M3 = 0; { M3 chip (1280 x 1024) } + DCSIMAGERMODEL_M5 = 1; { M5 chip (1536 x 1024) } + DCSIMAGERMODEL_M6 = 2; { M6 chip (3072 x 2048) } + DCSIMAGERFILTER_IR = 0; { infrared filter } + DCSIMAGERFILTER_MONO = 1; { monochrome filter } + DCSIMAGERFILTER_CFA = 2; { color filter array } + DCSIMAGERFILTER_OTHER = 3; { other filter } + TIFFTAG_DCSINTERPMODE = 65551; { interpolation mode } + DCSINTERPMODE_NORMAL = 0; { whole image, default } + DCSINTERPMODE_PREVIEW = 1; { preview of image (384x256) } + TIFFTAG_DCSBALANCEARRAY = 65552; { color balance values } + TIFFTAG_DCSCORRECTMATRIX = 65553; { color correction values } + TIFFTAG_DCSGAMMA = 65554; { gamma value } + TIFFTAG_DCSTOESHOULDERPTS = 65555; { toe & shoulder points } + TIFFTAG_DCSCALIBRATIONFD = 65556; { calibration file desc } + { Note: quality level is on the ZLIB 1-9 scale. Default value is -1 } + TIFFTAG_ZIPQUALITY = 65557; { compression quality level } + TIFFTAG_PIXARLOGQUALITY = 65558; { PixarLog uses same scale } + { 65559 is allocated to Oceana Matrix <dev@oceana.com> } + TIFFTAG_DCSCLIPRECTANGLE = 65559; { area of image to acquire } + TIFFTAG_SGILOGDATAFMT = 65560; { SGILog user data format } + SGILOGDATAFMT_FLOAT = 0; { IEEE float samples } + SGILOGDATAFMT_16BIT = 1; { 16-bit samples } + SGILOGDATAFMT_RAW = 2; { uninterpreted data } + SGILOGDATAFMT_8BIT = 3; { 8-bit RGB monitor values } + TIFFTAG_SGILOGENCODE = 65561; { SGILog data encoding control } + SGILOGENCODE_NODITHER = 0; { do not dither encoded values } + SGILOGENCODE_RANDITHER = 1; { randomly dither encd values } + + + { 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. } + TIFFPRINT_NONE = $0; { no extra info } + TIFFPRINT_STRIPS = $1; { strips/tiles info } + TIFFPRINT_CURVES = $2; { color/gray response curves } + TIFFPRINT_COLORMAP = $4; { colormap } + TIFFPRINT_JPEGQTABLES = $100; { JPEG Q matrices } + TIFFPRINT_JPEGACTABLES = $200; { JPEG AC tables } + TIFFPRINT_JPEGDCTABLES = $200; { JPEG DC tables } + + + TIFF_ANY = TIFF_NOTYPE; { for field descriptor searching } + TIFF_VARIABLE = -1; { marker for variable length tags } + TIFF_SPP = -2; { marker for SamplesPerPixel tags } + TIFF_VARIABLE2 = -3; { marker for uint32 var-length tags } + + FIELD_CUSTOM = 65; + + {added for LibTiff 3.9.4 by Alex (leontyyy@gmail.com) Dec.2011} + TIFFTAG_EXIFIFD = 34665; { pointer to the Exif IFD } + EXIFTAG_FOCALLENGTH = 37386; { focal length } + EXIFTAG_FOCALLENGTHIN35MMFILM = 41989; { indicates the equivalent focal length assuming a 35mm film camera, in mm } + EXIFTAG_EXIFVERSION = 36864; { version of exif format } + EXIFTAG_DATETIMEDIGITIZED = 36868; { date and time when the image was stored as digital data } + EXIFTAG_DATETIMEORIGINAL = 36867; { date and time when the original image data was generated } + EXIFTAG_EXPOSURETIME = 33434; { exposure time, given in seconds } + EXIFTAG_FNUMBER = 33437; { F number } + EXIFTAG_EXPOSUREPROGRAM = 34850; { class of the program used by the camera to set exposure } + EXIFTAG_SPECTRALSENSITIVITY = 34852; { spectral sensitivity of each channel of the camera used } + EXIFTAG_ISOSPEEDRATINGS = 34855; { ISO Speed and ISO Latitude } + EXIFTAG_OECF = 34856; { Opto-Electric Conversion Function } + EXIFTAG_COMPONENTSCONFIGURATION = 37121; { meaning of each component } + EXIFTAG_COMPRESSEDBITSPERPIXEL = 37122; { compression mode } + EXIFTAG_SHUTTERSPEEDVALUE = 37377; { shutter speed } + EXIFTAG_APERTUREVALUE = 37378; { lens aperture } + EXIFTAG_BRIGHTNESSVALUE = 37379; { brightness } + EXIFTAG_EXPOSUREBIASVALUE = 37380; { exposure bias } + EXIFTAG_MAXAPERTUREVALUE = 37381; { maximum lens aperture } + EXIFTAG_SUBJECTDISTANCE = 37382; { distance to the subject in meters } + EXIFTAG_METERINGMODE = 37383; { metering mode } + EXIFTAG_LIGHTSOURCE = 37384; { light source } + EXIFTAG_FLASH = 37385; { flash } + EXIFTAG_SUBJECTAREA = 37396; { subject area (in exif ver.2.2) } + EXIFTAG_MAKERNOTE = 37500; { manufacturer notes } + EXIFTAG_USERCOMMENT = 37510; { user comments } + EXIFTAG_SUBSECTIME = 37520; { DateTime subseconds } + EXIFTAG_SUBSECTIMEORIGINAL = 37521; { DateTimeOriginal subseconds } + EXIFTAG_SUBSECTIMEDIGITIZED = 37522; { DateTimeDigitized subseconds } + EXIFTAG_FLASHPIXVERSION = 40960; { FlashPix format version } + EXIFTAG_COLORSPACE = 40961; { color space information } + EXIFTAG_PIXELXDIMENSION = 40962; { valid image width } + EXIFTAG_PIXELYDIMENSION = 40963; { valid image height } + EXIFTAG_RELATEDSOUNDFILE = 40964; { related audio file } + EXIFTAG_FLASHENERGY = 41483; { flash energy } + EXIFTAG_SPATIALFREQUENCYRESPONSE = 41484; { spatial frequency response } + EXIFTAG_FOCALPLANEXRESOLUTION = 41486; { focal plane X resolution } + EXIFTAG_FOCALPLANEYRESOLUTION = 41487; { focal plane Y resolution } + EXIFTAG_FOCALPLANERESOLUTIONUNIT = 41488; { focal plane resolution unit } + EXIFTAG_SUBJECTLOCATION = 41492; { subject location } + EXIFTAG_EXPOSUREINDEX = 41493; { exposure index } + EXIFTAG_SENSINGMETHOD = 41495; { sensing method } + EXIFTAG_FILESOURCE = 41728; { file source } + EXIFTAG_SCENETYPE = 41729; { scene type } + EXIFTAG_CFAPATTERN = 41730; { CFA pattern } + EXIFTAG_CUSTOMRENDERED = 41985; { custom image processing (in exif ver.2.2) } + EXIFTAG_EXPOSUREMODE = 41986; { exposure mode (in exif ver.2.2) } + EXIFTAG_WHITEBALANCE = 41987; { white balance (in exif ver.2.2) } + EXIFTAG_DIGITALZOOMRATIO = 41988; { digital zoom ratio (in exif ver.2.2) } + EXIFTAG_SCENECAPTURETYPE = 41990; { scene capture type (in exif ver.2.2) } + EXIFTAG_GAINCONTROL = 41991; { gain control (in exif ver.2.2) } + EXIFTAG_CONTRAST = 41992; { contrast (in exif ver.2.2) } + EXIFTAG_SATURATION = 41993; { saturation (in exif ver.2.2) } + EXIFTAG_SHARPNESS = 41994; { sharpness (in exif ver.2.2) } + EXIFTAG_DEVICESETTINGDESCRIPTION = 41995; { device settings description (in exif ver.2.2) } + EXIFTAG_SUBJECTDISTANCERANGE = 41996; { subject distance range (in exif ver.2.2) } + EXIFTAG_IMAGEUNIQUEID = 42016; { Unique image ID (in exif ver.2.2) } + +type + PTIFF = Pointer; + PTIFFRGBAImage = Pointer; + + TIFFReadWriteProc = function(fd: thandle_t; buf: tdata_t; size: tsize_t): tsize_t; cdecl; + TIFFSeekProc = function(fd: thandle_t; off: toff_t; whence: Integer): toff_t; cdecl; + TIFFCloseProc = function(fd: thandle_t): Integer; cdecl; + TIFFSizeProc = function(fd: thandle_t): toff_t; cdecl; + TIFFMapFileProc = function(fd: thandle_t; var pbase: tdata_t; var psize: toff_t): Integer; cdecl; + TIFFUnmapFileProc = procedure(fd: thandle_t; base: tdata_t; size: toff_t); cdecl; + TIFFExtendProc = procedure(Handle: PTIFF); cdecl; + TIFFErrorHandler = procedure(Module: PAnsiChar; const Format: PAnsiChar; Params: va_list); cdecl; + TIFFInitMethod = function(Handle: PTIFF; Scheme: Integer): Integer; cdecl; + + PTIFFCodec = ^TIFFCodec; + TIFFCodec = record + Name: PAnsiChar; + Scheme: Word; + Init: TIFFInitMethod; + end; + + PTIFFFieldInfo = ^TIFFFieldInfo; + TIFFFieldInfo = record + FieldTag: Cardinal; { field's tag } + FieldReadCount: Smallint; { read count/TIFF_VARIABLE/TIFF_SPP } + FieldWriteCount: Smallint; { write count/TIFF_VARIABLE } + FieldType: Integer; { type of associated data } + FieldBit: Word; { bit in fieldsset bit vector } + FieldOkToChange: Byte; { if true, can change while writing } + FieldPassCount: Byte; { if true, pass dir count on set } + FieldName: PAnsiChar; { ASCII name } + end; + + PTIFFTagValue = ^TIFFTagValue; + TIFFTagValue = record + Info: PTIFFFieldInfo; + Count: Integer; + Value: Pointer; + end; + +{$IFDEF DYNAMIC_DLL_LOADING} +var + TIFFGetVersion: function(): PAnsiChar; cdecl; + TIFFOpen: function (const FileName: PAnsiChar; const Mode: PAnsiChar): PTIFF; cdecl; + TIFFClientOpen: function( + const Name: PAnsiChar; + const Mode: PAnsiChar; + ClientData: Cardinal; + ReadProc: TIFFReadWriteProc; + WriteProc: TIFFReadWriteProc; + SeekProc: TIFFSeekProc; + CloseProc: TIFFCloseProc; + SizeProc: TIFFSizeProc; + MapProc: TIFFMapFileProc; + UnmapProc: TIFFUnmapFileProc): PTIFF; cdecl; + TIFFClose: procedure(Handle: PTIFF); cdecl; + TIFFSetFileno: function(Handle: PTIFF; Newvalue: Integer): Integer; cdecl; + TIFFSetField: function(Handle: PTIFF; Tag: Cardinal): Integer; cdecl varargs; + TIFFGetField: function(Handle: PTIFF; Tag: Cardinal): Integer; cdecl varargs; + TIFFGetFieldDefaulted: function(Handle: PTIFF; Tag: Cardinal): Integer; cdecl varargs; + TIFFReadRGBAImageOriented: function(Handle: PTIFF; RWidth,RHeight: Cardinal; Raster: Pointer; Orientation: Integer; Stop: Integer): Integer; cdecl; + TIFFReadScanline: function(Handle: PTIFF; Buf: Pointer; Row: Cardinal; Sample: tsample_t): Integer; cdecl; + TIFFWriteScanline: function(Handle: PTIFF; Buf: Pointer; Row: Cardinal; Sample: tsample_t): Integer; cdecl; + TIFFScanlineSize: function(Handle: PTIFF): tmsize_t; cdecl; + TIFFDefaultStripSize: function(Handle: PTIFF; Request: Cardinal): Cardinal; cdecl; + TIFFNumberOfDirectories: function(Handle: PTIFF): Word; cdecl; + TIFFSetDirectory: function(Handle: PTIFF; Dirn: Word): Integer; cdecl; + TIFFWriteDirectory: function(Handle: PTIFF): Integer; cdecl; + TIFFReadEXIFDirectory: function(Handle: PTIFF; Diroff: toff_t): Integer; cdecl; + TIFFSetErrorHandler: function(Handler: TIFFErrorHandler): TIFFErrorHandler; cdecl; + TIFFSetWarningHandler: function(Handler: TIFFErrorHandler): TIFFErrorHandler; cdecl; + +function LoadTiffLibrary: Boolean; +{$ELSE} +function TIFFGetVersion: PAnsiChar; cdecl; external SLibName; +function TIFFFindCODEC(Scheme: Word): PTIFFCodec; cdecl; external SLibName; +function TIFFRegisterCODEC(Scheme: Word; Name: PAnsiChar; InitMethod: TIFFInitMethod): PTIFFCodec; cdecl; external SLibName; +procedure TIFFUnRegisterCODEC(c: PTIFFCodec); cdecl; external SLibName; +function TIFFIsCODECConfigured(Scheme: Word): Integer; cdecl; external SLibName; +function TIFFGetConfiguredCODECs: PTIFFCodec; cdecl; external SLibName; +function TIFFClientOpen(Name: PAnsiChar; Mode: PAnsiChar; ClientData: THandle; + ReadProc: TIFFReadWriteProc; + WriteProc: TIFFReadWriteProc; + SeekProc: TIFFSeekProc; + CloseProc: TIFFCloseProc; + SizeProc: TIFFSizeProc; + MapProc: TIFFMapFileProc; + UnmapProc: TIFFUnmapFileProc): PTIFF; cdecl; external SLibName; +procedure TIFFCleanup(Handle: PTIFF); cdecl; external SLibName; +procedure TIFFClose(Handle: PTIFF); cdecl; external SLibName; +function TIFFFileno(Handle: PTIFF): Integer; cdecl; external SLibName; +function TIFFSetFileno(Handle: PTIFF; Newvalue: Integer): Integer; cdecl; external SLibName; +function TIFFClientdata(Handle: PTIFF): THandle; cdecl; external SLibName; +function TIFFSetClientdata(Handle: PTIFF; Newvalue: THandle): THandle; cdecl; external SLibName; +function TIFFGetMode(Handle: PTIFF): Integer; cdecl; external SLibName; +function TIFFSetMode(Handle: PTIFF; Mode: Integer): Integer; cdecl; external SLibName; +function TIFFFileName(Handle: PTIFF): Pointer; cdecl; external SLibName; +function TIFFSetFileName(Handle: PTIFF; Name: PAnsiChar): PAnsiChar; cdecl; external SLibName; +function TIFFGetReadProc(Handle: PTIFF): TIFFReadWriteProc; cdecl; external SLibName; +function TIFFGetWriteProc(Handle: PTIFF): TIFFReadWriteProc; cdecl; external SLibName; +function TIFFGetSeekProc(Handle: PTIFF): TIFFSeekProc; cdecl; external SLibName; +function TIFFGetCloseProc(Handle: PTIFF): TIFFCloseProc; cdecl; external SLibName; +function TIFFGetSizeProc(Handle: PTIFF): TIFFSizeProc; cdecl; external SLibName; +procedure TIFFError(Module: Pointer; Fmt: Pointer); cdecl; external SLibName; varargs; +function TIFFSetErrorHandler(Handler: TIFFErrorHandler): TIFFErrorHandler; cdecl; external SLibName; +procedure TIFFWarning(Module: Pointer; Fmt: Pointer); cdecl; external SLibName; varargs; +function TIFFSetWarningHandler(Handler: TIFFErrorHandler): TIFFErrorHandler; cdecl; external SLibName; +function TIFFSetTagExtender(Extender: TIFFExtendProc): TIFFExtendProc; cdecl; external SLibName; +function TIFFFlush(Handle: PTIFF): Integer; cdecl; external SLibName; +function TIFFFlushData(Handle: PTIFF): Integer; cdecl; external SLibName; +function TIFFReadEXIFDirectory(Handle: PTIFF; Diroff: toff_t): Integer; cdecl; external SLibName; +function TIFFReadDirectory(Handle: PTIFF): Integer; cdecl; external SLibName; +function TIFFCurrentDirectory(Handle: PTIFF): Word; cdecl; external SLibName; +function TIFFCurrentDirOffset(Handle: PTIFF): toff_t; cdecl; external SLibName; +function TIFFLastDirectory(Handle: PTIFF): Integer; cdecl; external SLibName; +function TIFFNumberOfDirectories(Handle: PTIFF): Word; cdecl; external SLibName; +function TIFFSetDirectory(Handle: PTIFF; Dirn: Word): Integer; cdecl; external SLibName; +function TIFFSetSubDirectory(Handle: PTIFF; Diroff: toff_t): Integer; cdecl; external SLibName; +function TIFFCreateDirectory(Handle: PTIFF): Integer; cdecl; external SLibName; +function TIFFWriteDirectory(Handle: PTIFF): Integer; cdecl; external SLibName; +function TIFFUnlinkDirectory(handle: PTIFF; Dirn: Word): Integer; cdecl; external SLibName; +procedure TIFFPrintDirectory(Handle: PTIFF; Fd: Pointer; Flags: Integer); cdecl; external SLibName; +function TIFFGetField(Handle: PTIFF; Tag: Cardinal): Integer; cdecl; external SLibName; varargs; +function TIFFGetFieldDefaulted(Handle: PTIFF; Tag: Cardinal): Integer; cdecl; external SLibName; varargs; +function TIFFVGetField(Handle: PTIFF; Tag: Cardinal; Ap: Pointer): Integer; cdecl; external SLibName; +function TIFFSetField(Handle: PTIFF; Tag: Cardinal): Integer; cdecl; external SLibName; varargs; +function TIFFVSetField(Handle: PTIFF; Tag: Cardinal; Ap: Pointer): Integer; cdecl; external SLibName; +function TIFFIsBigEndian(Handle: PTIFF): Integer; cdecl; external SLibName; +function TIFFIsTiled(Handle: PTIFF): Integer; cdecl; external SLibName; +function TIFFIsByteSwapped(Handle: PTIFF): Integer; cdecl; external SLibName; +function TIFFIsUpSampled(Handle: PTIFF): Integer; cdecl; external SLibName; +function TIFFIsMSB2LSB(Handle: PTIFF): Integer; cdecl; external SLibName; +function TIFFGetTagListCount(Handle: PTIFF): Integer; cdecl; external SLibName; +function TIFFGetTagListEntry(Handle: PTIFF; TagIndex: Integer): Cardinal; cdecl; external SLibName; +procedure TIFFMergeFieldInfo(Handle: PTIFF; Info: PTIFFFieldInfo; N: Integer); cdecl; external SLibName; +function TIFFFindFieldInfo(Handle: PTIFF; Tag: Cardinal; Dt: Integer): PTIFFFieldInfo; cdecl; external SLibName; +function TIFFFindFieldInfoByName(Handle: PTIFF; FIeldName: PAnsiChar; Dt: Integer): PTIFFFieldInfo; cdecl; external SLibName; +function TIFFFieldWithTag(Handle: PTIFF; Tag: Cardinal): PTIFFFieldInfo; cdecl; external SLibName; +function TIFFFieldWithName(Handle: PTIFF; FieldName: PAnsiChar): PTIFFFieldInfo; cdecl; external SLibName; +function TIFFDataWidth(DataType: Integer): Integer; cdecl; external SLibName; +function TIFFReadRGBAImage(Handle: PTIFF; RWidth,RHeight: Cardinal; Raster: Pointer; Stop: Integer): Integer; cdecl; external SLibName; +function TIFFReadRGBAImageOriented(Handle: PTIFF; RWidth,RHeight: Cardinal; Raster: Pointer; Orientation: Integer; Stop: Integer): Integer; cdecl; external SLibName; +function TIFFReadRGBAStrip(Handle: PTIFF; Row: Cardinal; Raster: Pointer): Integer; cdecl; external SLibName; +function TIFFReadRGBATile(Handle: PTIFF; Col,Row: Cardinal; Raster: Pointer): Integer; cdecl; external SLibName; +function TIFFRGBAImageOk(Handle: PTIFF; Emsg: PAnsiChar): Integer; cdecl; external SLibName; +function TIFFRGBAImageBegin(Img: PTIFFRGBAImage; Handle: PTIFF; Stop: Integer; Emsg: PAnsiChar): Integer; cdecl; external SLibName; +function TIFFRGBAImageGet(Img: PTIFFRGBAImage; Raster: Pointer; W, H: Cardinal): Integer; cdecl; external SLibName; +procedure TIFFRGBAImageEnd(Img: PTIFFRGBAImage); cdecl; external SLibName; +function TIFFCurrentRow(Handle: PTIFF): Cardinal; cdecl; external SLibName; +function TIFFStripSize(Handle: PTIFF): tmsize_t; cdecl; external SLibName; +function TIFFRawStripSize(Handle: PTIFF; Strip: Cardinal): tmsize_t; cdecl; external SLibName; +function TIFFVStripSize(Handle: PTIFF; NRows: Cardinal): tmsize_t; cdecl; external SLibName; +function TIFFDefaultStripSize(Handle: PTIFF; Request: Cardinal): Cardinal; cdecl; external SLibName; +function TIFFNumberOfStrips(Handle: PTIFF): Cardinal; cdecl; external SLibName; +function TIFFComputeStrip(Handle: PTIFF; Row: Cardinal; Sample: Word): Cardinal; cdecl; external SLibName; +function TIFFReadRawStrip(Handle: PTIFF; Strip: Cardinal; Buf: Pointer; Size: tmsize_t): tmsize_t; cdecl; external SLibName; +function TIFFReadEncodedStrip(Handle: PTIFF; Strip: Cardinal; Buf: Pointer; Size: tmsize_t): tmsize_t; cdecl; external SLibName; +function TIFFWriteRawStrip(Handle: PTIFF; Strip: Cardinal; Data: Pointer; Cc: tmsize_t): tmsize_t; cdecl; external SLibName; +function TIFFWriteEncodedStrip(Handle: PTIFF; Strip: Cardinal; Data: Pointer; Cc: tmsize_t): tmsize_t; cdecl; external SLibName; +function TIFFCurrentStrip(Handle: PTIFF): Cardinal; cdecl; external SLibName; +function TIFFTileSize(Handle: PTIFF): tmsize_t; cdecl; external SLibName; +function TIFFTileRowSize(Handle: PTIFF): tmsize_t; cdecl; external SLibName; +function TIFFVTileSize(Handle: PTIFF; NRows: Cardinal): tmsize_t; cdecl; external SLibName; +procedure TIFFDefaultTileSize(Handle: PTIFF; Tw: PCardinal; Th: PCardinal); cdecl; external SLibName; +function TIFFNumberOfTiles(Handle: PTIFF): Cardinal; cdecl; external SLibName; +function TIFFComputeTile(Handle: PTIFF; X,Y,Z: Cardinal; S: Word): Cardinal; cdecl; external SLibName; +function TIFFReadRawTile(Handle: PTIFF; Tile: Cardinal; Buf: Pointer; Size: tmsize_t): tmsize_t; cdecl; external SLibName; +function TIFFReadEncodedTile(Handle: PTIFF; Tile: Cardinal; Buf: Pointer; Size: tmsize_t): tmsize_t; cdecl; external SLibName; +function TIFFWriteRawTile(Handle: PTIFF; Tile: Cardinal; Data: Pointer; Cc: tmsize_t): tmsize_t; cdecl; external SLibName; +function TIFFWriteEncodedTile(Handle: PTIFF; Tile: Cardinal; Data: Pointer; Cc: tmsize_t): tmsize_t; cdecl; external SLibName; +function TIFFCurrentTile(Handle: PTIFF): Cardinal; cdecl; external SLibName; +function TIFFScanlineSize(Handle: PTIFF): tmsize_t; cdecl; external SLibName; +function TIFFScanlineSize64(Handle: PTIFF): Int64; cdecl; external SLibName; +function TIFFRasterScanlineSize64(Handle: PTIFF): Int64; cdecl; external SLibName; +function TIFFRasterScanlineSize(Handle: PTIFF): tmsize_t; cdecl; external SLibName; +function TIFFReadScanline(Handle: PTIFF; Buf: Pointer; Row: Cardinal; Sample: tsample_t): Integer; cdecl; external SLibName; +function TIFFWriteScanline(Handle: PTIFF; Buf: Pointer; Row: Cardinal; Sample: tsample_t): Integer; cdecl; external SLibName; +procedure TIFFSetWriteOffset(Handle: PTIFF; Off: toff_t); cdecl; external SLibName; +procedure TIFFSwabShort(Wp: PWord); cdecl; external SLibName; +procedure TIFFSwabLong(Lp: PCardinal); cdecl; external SLibName; +procedure TIFFSwabDouble(Dp: PDouble); cdecl; external SLibName; +procedure TIFFSwabArrayOfShort(Wp: PWord; N: tmsize_t); cdecl; external SLibName; +procedure TIFFSwabArrayOfTriples(tp:PByte; n: tmsize_t); cdecl; external SLibName; +procedure TIFFSwabArrayOfLong(Lp: PCardinal; N: tmsize_t); cdecl; external SLibName; +procedure TIFFSwabArrayOfDouble(Dp: PDouble; N: tmsize_t); cdecl; external SLibName; +procedure TIFFReverseBits(Cp: Pointer; N: tmsize_t); cdecl; external SLibName; +function TIFFGetBitRevTable(Reversed: Integer): Pointer; cdecl; external SLibName; +{$ENDIF} + +type + TUserTiffErrorHandler = procedure(const Module, Message: AnsiString); + +procedure SetUserMessageHandlers(ErrorHandler, WarningHandler: TUserTiffErrorHandler); +function IsVersion4: Boolean; + +implementation + +{$IFDEF FPC} +uses + dynlibs; +{$ENDIF} + +var + UserTiffWarningHandler: TUserTiffErrorHandler; + UserTiffErrorHandler: TUserTiffErrorHandler; + +procedure SetUserMessageHandlers(ErrorHandler, WarningHandler: TUserTiffErrorHandler); +begin + UserTiffErrorHandler := ErrorHandler; + UserTiffWarningHandler := WarningHandler; +end; + +procedure SetInternalMessageHandlers(ErrorHandler, WarningHandler: TIFFErrorHandler); +begin + TIFFSetWarningHandler(@WarningHandler); + TIFFSetErrorHandler(@ErrorHandler); +end; + +const +{$IFDEF MSWINDOWS} + SRuntimeLib = 'msvcrt.dll'; +{$ELSE} + SRuntimeLib = 'libc.so'; +{$ENDIF} + +function snprintf(S: PAnsiChar; N: Integer; const Format: PAnsiChar): Integer; cdecl; varargs; external SRuntimeLib name {$IFDEF MSWINDOWS}'_snprintf'{$ELSE}'snprintf'{$ENDIF}; + +procedure FormatAndCallHandler(Handler: TUserTiffErrorHandler; Module: PAnsiChar; Format: PAnsiChar; Params: va_list); +var + Len: Integer; + Buffer: array[0..511] of AnsiChar; + Msg: AnsiString; +begin + Len := snprintf(@Buffer, 512, Format, Params); + SetString(Msg, Buffer, Len); + Handler(Module, Msg); +end; + +procedure InternalTIFFWarning(Module: PAnsiChar; Format: PAnsiChar; Params: va_list); cdecl; +begin + if Assigned(UserTiffWarningHandler) then + FormatAndCallHandler(UserTiffWarningHandler, Module, Format, Params); +end; + +procedure InternallTIFFError(Module: PAnsiChar; Format: PAnsiChar; Params: va_list); cdecl; +begin + if Assigned(UserTiffErrorHandler) then + FormatAndCallHandler(UserTiffErrorHandler, Module, Format, Params); +end; + +function IsVersion4: Boolean; +var + Version: PAnsiChar; +begin + Version := TIFFGetVersion; + Result := Pos(AnsiString('Version 4'), Version) > 0; +end; + +procedure CheckVersion; +begin +{$IFDEF UNIX} + if not IsVersion4 then + WriteLn('Warning: installed libtiff seems to be version 3.x. TIFF functions will probably fail. Install libtiff5 package to get libtiff 4.x.'); +{$ENDIF} +end; + +{$IFDEF DYNAMIC_DLL_LOADING} +var + TiffLibHandle: {$IFDEF FPC}TLibHandle{$ELSE}THandle{$ENDIF} = 0; + +function GetProcAddr(const AProcName: PAnsiChar): Pointer; +begin + Result := GetProcAddress(TiffLibHandle, AProcName); + if Addr(Result) = nil then begin + RaiseLastOSError; + end; +end; + +function LoadTiffLibrary: Boolean; +begin + Result := False; + + if TiffLibHandle = 0 then + begin + TiffLibHandle := LoadLibrary(SLibName); + {$IF Defined(DARWIN)} + if TiffLibHandle = 0 then + TiffLibHandle := LoadLibrary('@executable_path/' + SLibName); + {$IFEND} + + if TiffLibHandle <> 0 then + begin + TIFFGetVersion := GetProcAddr('TIFFGetVersion'); + TIFFOpen := GetProcAddr('TIFFOpen'); + TIFFClientOpen := GetProcAddr('TIFFClientOpen'); + TIFFClose := GetProcAddr('TIFFClose'); + TIFFSetFileno := GetProcAddr('TIFFSetFileno'); + TIFFSetField := GetProcAddr('TIFFSetField'); + TIFFGetField := GetProcAddr('TIFFGetField'); + TIFFGetFieldDefaulted := GetProcAddr('TIFFGetFieldDefaulted'); + TIFFReadRGBAImageOriented := GetProcAddr('TIFFReadRGBAImageOriented'); + TIFFReadScanline := GetProcAddr('TIFFReadScanline'); + TIFFWriteScanline := GetProcAddr('TIFFWriteScanline'); + TIFFScanlineSize := GetProcAddr('TIFFScanlineSize'); + TIFFDefaultStripSize := GetProcAddr('TIFFDefaultStripSize'); + TIFFNumberOfDirectories := GetProcAddr('TIFFNumberOfDirectories'); + TIFFSetDirectory := GetProcAddr('TIFFSetDirectory'); + TIFFWriteDirectory := GetProcAddr('TIFFWriteDirectory'); + TIFFReadEXIFDirectory := GetProcAddr('TIFFReadEXIFDirectory'); + TIFFSetErrorHandler := GetProcAddr('TIFFSetErrorHandler'); + TIFFSetWarningHandler := GetProcAddr('TIFFSetWarningHandler'); + + SetInternalMessageHandlers(@InternallTIFFError, @InternalTIFFWarning); + CheckVersion; + + Result := True; + end; + end; +end; + +procedure FreeTiffLibrary; +begin + if TiffLibHandle <> 0 then begin + FreeLibrary(TiffLibHandle); + TiffLibHandle := 0; + end; +end; +{$ENDIF} + +initialization +{$IFNDEF DYNAMIC_DLL_LOADING} + SetInternalMessageHandlers(@InternallTIFFError, @InternalTIFFWarning); + CheckVersion; +{$ENDIF} + +finalization +{$IFDEF DYNAMIC_DLL_LOADING} + FreeTiffLibrary; +{$ENDIF} +end. + diff --git a/resources/libraries/deskew/Imaging/LibTiff/ZLibDelphi.pas b/resources/libraries/deskew/Imaging/LibTiff/ZLibDelphi.pas new file mode 100755 index 0000000..9beb4f8 --- /dev/null +++ b/resources/libraries/deskew/Imaging/LibTiff/ZLibDelphi.pas @@ -0,0 +1,89 @@ +unit ZLibDelphi; + +{$IFDEF FPC} + {$MODE OBJFPC} +{$ELSE} + {$DEFINE DCC} +{$ENDIF} + +interface + +uses + SysUtils; + +const + + ZLIB_VERSION = '1.2.1'; + + Z_NO_FLUSH = 0; + Z_FINISH = 4; + + Z_OK = 0; + Z_STREAM_END = 1; + +type + + PRZStream = ^RZStream; + + RZStream = record + NextIn: PByte; + AvailIn: Cardinal; + TotalIn: Cardinal; + NextOut: PByte; + AvailOut: Cardinal; + TotalOut: Cardinal; + Msg: PAnsiChar; + State: Pointer; + AllocFunc: Pointer; + FreeFunc: Pointer; + Opaque: Cardinal; + DataType: Integer; + Adler: Cardinal; + Reserved: Cardinal; + end; + +function inflateInit_(strm: Pointer; version: Pointer; stream_size: Integer): Integer; cdecl; external; +function inflateReset(strm: Pointer): Integer; cdecl; external; +function inflate(strm: Pointer; flush: Integer): Integer; cdecl; external; +function inflateSync(strm: Pointer): Integer; cdecl; external; +function deflateInit(strm: Pointer; level: Integer): Integer; +function deflateInit_(strm: Pointer; level: Integer; version: Pointer; stream_size: Integer): Integer; cdecl; external; +function deflateReset(strm: Pointer): Integer; cdecl; external; +function deflate(strm: Pointer; flush: Integer): Integer; cdecl; external; +function deflateEnd(strm: Pointer): Integer; cdecl; external; +function inflateEnd(strm: Pointer): Integer; cdecl; external; +function deflateParams(strm: Pointer; level: Integer; strategy: Integer): Integer; cdecl; external; + +implementation + +uses + LibDelphi; + +function deflateInit(strm: Pointer; level: Integer): Integer; +begin + Result:=deflateInit_(strm,level,PAnsiChar(ZLIB_VERSION),SizeOf(RZStream)); +end; + +{$IF Defined(DCC) and Defined(MSWINDOWS) and not Defined(CPUX64)} + // Windows 32bit Delphi only - OMF object format + {$L Compiled\inflate.obj} + {$L Compiled\crc32.obj} + {$L Compiled\adler32.obj} + {$L Compiled\inftrees.obj} + {$L Compiled\inffast.obj} + {$L Compiled\deflate.obj} + {$L Compiled\zutil.obj} + {$L Compiled\trees.obj} + {$L Compiled\compress.obj} + {$L Compiled\uncompr.obj} +{$IFEND} + +end. + + + + + + + + diff --git a/resources/libraries/deskew/Imaging/ZLib/dzlib.pas b/resources/libraries/deskew/Imaging/ZLib/dzlib.pas new file mode 100755 index 0000000..37a0da0 --- /dev/null +++ b/resources/libraries/deskew/Imaging/ZLib/dzlib.pas @@ -0,0 +1,523 @@ +{*******************************************************} +{ } +{ Delphi Supplemental Components } +{ ZLIB Data Compression Interface Unit } +{ } +{ Copyright (c) 1997 Borland International } +{ Copyright (c) 1998 Jacques Nomssi Nzali } +{ } +{*******************************************************} + +{ + Modified for + Vampyre Imaging Library + by Marek Mauder + http://imaginglib.sourceforge.net + + You can choose which pascal zlib implementation will be + used. IMPASZLIB and FPCPASZLIB are translations of zlib + to pascal so they don't need any *.obj files. + The others are interfaces to *.obj files (Windows) or + *.so libraries (Linux). + Default implementation is IMPASZLIB because it can be compiled + by all supported compilers and works on all supported platforms. + I usually use implementation with the fastest decompression + when building release Win32 binaries. + FPCPASZLIB is useful for Lazarus applications. FPC's zlib is linked + to exe by default so there is no need to link additional (and almost identical) + IMPASZLIB. + + There is a small speed comparison table of some of the + supported implementations (TGA image 28 311 570 bytes, compression level = 6, + Delphi 9, Win32, Athlon XP 1900). + + ZLib version Decompression Compression Comp. Size + IMPASZLIB | 1.1.2 | 824 ms | 4 280 ms | 18 760 133 B + ZLIBEX | 1.2.2 | 710 ms | 1 590 ms* | 19 056 621 B + DELPHIZLIB | 1.0.4 | 976 ms | 9 190 ms | 18 365 562 B + ZLIBPAS | 1.2.3 | 680 ms | 3 790 ms | 18 365 387 B + * obj files are compiled with compression level hardcoded to 1 (fastest) +} + +unit dzlib; + +{$I ImagingOptions.inc} + +interface + +{$DEFINE IMPASZLIB} +{ $DEFINE ZLIBPAS} +{ $DEFINE FPCPASZLIB} +{ $DEFINE ZLIBEX} +{ $DEFINE DELPHIZLIB} + +{ Automatically use FPC's PasZLib when compiling with FPC.} + +{$IFDEF FPC} + {$UNDEF IMPASZLIB} + {$DEFINE FPCPASZLIB} +{$ENDIF} + +uses +{$IF Defined(IMPASZLIB)} + { Use paszlib modified by me for Delphi and FPC } + imzdeflate, imzinflate, impaszlib, +{$ELSEIF Defined(FPCPASZLIB)} + { Use FPC's paszlib } + zbase, paszlib, +{$ELSEIF Defined(ZLIBPAS)} + { Pascal interface to ZLib shipped with ZLib C source } + zlibpas, +{$ELSEIF Defined(ZLIBEX)} + { Use ZlibEx unit } + ZLibEx, +{$ELSEIF Defined(DELPHIZLIB)} + { Use ZLib unit shipped with Delphi } + ZLib, +{$IFEND} + ImagingTypes, SysUtils, Classes; + +{$IF Defined(IMPASZLIB) or Defined(FPCPASZLIB) or Defined(ZLIBPAS)} +type + TZStreamRec = z_stream; +{$IFEND} + +const + Z_NO_FLUSH = 0; + Z_PARTIAL_FLUSH = 1; + Z_SYNC_FLUSH = 2; + Z_FULL_FLUSH = 3; + Z_FINISH = 4; + + Z_OK = 0; + Z_STREAM_END = 1; + Z_NEED_DICT = 2; + Z_ERRNO = -1; + Z_STREAM_ERROR = -2; + Z_DATA_ERROR = -3; + Z_MEM_ERROR = -4; + Z_BUF_ERROR = -5; + Z_VERSION_ERROR = -6; + + Z_NO_COMPRESSION = 0; + Z_BEST_SPEED = 1; + Z_BEST_COMPRESSION = 9; + Z_DEFAULT_COMPRESSION = -1; + + Z_FILTERED = 1; + Z_HUFFMAN_ONLY = 2; + Z_RLE = 3; + Z_DEFAULT_STRATEGY = 0; + + Z_BINARY = 0; + Z_ASCII = 1; + Z_UNKNOWN = 2; + + Z_DEFLATED = 8; + +type + { Abstract ancestor class } + TCustomZlibStream = class(TStream) + private + FStrm: TStream; + FStrmPos: Integer; + FOnProgress: TNotifyEvent; + FZRec: TZStreamRec; + FBuffer: array [Word] of Byte; + protected + procedure Progress(Sender: TObject); dynamic; + property OnProgress: TNotifyEvent read FOnProgress write FOnProgress; + constructor Create(Strm: TStream); + end; + +{ TCompressionStream compresses data on the fly as data is written to it, and + stores the compressed data to another stream. + + TCompressionStream is write-only and strictly sequential. Reading from the + stream will raise an exception. Using Seek to move the stream pointer + will raise an exception. + + Output data is cached internally, written to the output stream only when + the internal output buffer is full. All pending output data is flushed + when the stream is destroyed. + + The Position property returns the number of uncompressed bytes of + data that have been written to the stream so far. + + CompressionRate returns the on-the-fly percentage by which the original + data has been compressed: (1 - (CompressedBytes / UncompressedBytes)) * 100 + If raw data size = 100 and compressed data size = 25, the CompressionRate + is 75% + + The OnProgress event is called each time the output buffer is filled and + written to the output stream. This is useful for updating a progress + indicator when you are writing a large chunk of data to the compression + stream in a single call.} + + + TCompressionLevel = (clNone, clFastest, clDefault, clMax); + + TCompressionStream = class(TCustomZlibStream) + private + function GetCompressionRate: Single; + public + constructor Create(CompressionLevel: TCompressionLevel; Dest: TStream); + destructor Destroy; override; + function Read(var Buffer; Count: Longint): Longint; override; + function Write(const Buffer; Count: Longint): Longint; override; + function Seek(Offset: Longint; Origin: Word): Longint; override; + property CompressionRate: Single read GetCompressionRate; + property OnProgress; + end; + +{ TDecompressionStream decompresses data on the fly as data is read from it. + + Compressed data comes from a separate source stream. TDecompressionStream + is read-only and unidirectional; you can seek forward in the stream, but not + backwards. The special case of setting the stream position to zero is + allowed. Seeking forward decompresses data until the requested position in + the uncompressed data has been reached. Seeking backwards, seeking relative + to the end of the stream, requesting the size of the stream, and writing to + the stream will raise an exception. + + The Position property returns the number of bytes of uncompressed data that + have been read from the stream so far. + + The OnProgress event is called each time the internal input buffer of + compressed data is exhausted and the next block is read from the input stream. + This is useful for updating a progress indicator when you are reading a + large chunk of data from the decompression stream in a single call.} + + TDecompressionStream = class(TCustomZlibStream) + public + constructor Create(Source: TStream); + destructor Destroy; override; + function Read(var Buffer; Count: Longint): Longint; override; + function Write(const Buffer; Count: Longint): Longint; override; + function Seek(Offset: Longint; Origin: Word): Longint; override; + property OnProgress; + end; + + + +{ CompressBuf compresses data, buffer to buffer, in one call. + In: InBuf = ptr to compressed data + InBytes = number of bytes in InBuf + Out: OutBuf = ptr to newly allocated buffer containing decompressed data + OutBytes = number of bytes in OutBuf } +procedure CompressBuf(const InBuf: Pointer; InBytes: Integer; + var OutBuf: Pointer; var OutBytes: Integer; + CompressLevel: Integer = Z_DEFAULT_COMPRESSION; + CompressStrategy: Integer = Z_DEFAULT_STRATEGY); + +{ DecompressBuf decompresses data, buffer to buffer, in one call. + In: InBuf = ptr to compressed data + InBytes = number of bytes in InBuf + OutEstimate = zero, or est. size of the decompressed data + Out: OutBuf = ptr to newly allocated buffer containing decompressed data + OutBytes = number of bytes in OutBuf } +procedure DecompressBuf(const InBuf: Pointer; InBytes: Integer; + OutEstimate: Integer; var OutBuf: Pointer; var OutBytes: Integer); + + +type + EZlibError = class(Exception); + ECompressionError = class(EZlibError); + EDecompressionError = class(EZlibError); + +implementation + +const + ZErrorMessages: array[0..9] of PAnsiChar = ( + 'need dictionary', // Z_NEED_DICT (2) + 'stream end', // Z_STREAM_END (1) + '', // Z_OK (0) + 'file error', // Z_ERRNO (-1) + 'stream error', // Z_STREAM_ERROR (-2) + 'data error', // Z_DATA_ERROR (-3) + 'insufficient memory', // Z_MEM_ERROR (-4) + 'buffer error', // Z_BUF_ERROR (-5) + 'incompatible version', // Z_VERSION_ERROR (-6) + ''); + +function zlibAllocMem(AppData: Pointer; Items, Size: Cardinal): Pointer; +begin + GetMem(Result, Items*Size); +end; + +procedure zlibFreeMem(AppData, Block: Pointer); +begin + FreeMem(Block); +end; + +function CCheck(code: Integer): Integer; +begin + Result := code; + if code < 0 then + raise ECompressionError.Create('zlib: ' + ZErrorMessages[2 - code]); +end; + +function DCheck(code: Integer): Integer; +begin + Result := code; + if code < 0 then + raise EDecompressionError.Create('zlib: ' + ZErrorMessages[2 - code]); +end; + +procedure CompressBuf(const InBuf: Pointer; InBytes: Integer; + var OutBuf: Pointer; var OutBytes: Integer; + CompressLevel, CompressStrategy: Integer); +var + strm: TZStreamRec; + P: Pointer; +begin + FillChar(strm, sizeof(strm), 0); +{$IFNDEF FPCPASZLIB} + strm.zalloc := @zlibAllocMem; + strm.zfree := @zlibFreeMem; +{$ENDIF} + OutBytes := ((InBytes + (InBytes div 10) + 12) + 255) and not 255; + GetMem(OutBuf, OutBytes); + try + strm.next_in := InBuf; + strm.avail_in := InBytes; + strm.next_out := OutBuf; + strm.avail_out := OutBytes; + + CCheck(deflateInit2(strm, CompressLevel, Z_DEFLATED, MAX_WBITS, + DEF_MEM_LEVEL, CompressStrategy)); + + try + while CCheck(deflate(strm, Z_FINISH)) <> Z_STREAM_END do + begin + P := OutBuf; + Inc(OutBytes, 256); + ReallocMem(OutBuf, OutBytes); + strm.next_out := Pointer(PtrUInt(OutBuf) + (PtrUInt(strm.next_out) - PtrUInt(P))); + strm.avail_out := 256; + end; + finally + CCheck(deflateEnd(strm)); + end; + ReallocMem(OutBuf, strm.total_out); + OutBytes := strm.total_out; + except + zlibFreeMem(nil, OutBuf); + raise + end; +end; + +procedure DecompressBuf(const InBuf: Pointer; InBytes: Integer; + OutEstimate: Integer; var OutBuf: Pointer; var OutBytes: Integer); +var + strm: TZStreamRec; + P: Pointer; + BufInc: Integer; +begin + FillChar(strm, sizeof(strm), 0); +{$IFNDEF FPCPASZLIB} + strm.zalloc := @zlibAllocMem; + strm.zfree := @zlibFreeMem; +{$ENDIF} + BufInc := (InBytes + 255) and not 255; + if OutEstimate = 0 then + OutBytes := BufInc + else + OutBytes := OutEstimate; + GetMem(OutBuf, OutBytes); + try + strm.next_in := InBuf; + strm.avail_in := InBytes; + strm.next_out := OutBuf; + strm.avail_out := OutBytes; + DCheck(inflateInit_(strm, zlib_version, sizeof(strm))); + try + while DCheck(inflate(strm, Z_NO_FLUSH)) <> Z_STREAM_END do + begin + P := OutBuf; + Inc(OutBytes, BufInc); + ReallocMem(OutBuf, OutBytes); + strm.next_out := Pointer(PtrUInt(OutBuf) + (PtrUInt(strm.next_out) - PtrUInt(P))); + strm.avail_out := BufInc; + end; + finally + DCheck(inflateEnd(strm)); + end; + ReallocMem(OutBuf, strm.total_out); + OutBytes := strm.total_out; + except + zlibFreeMem(nil, OutBuf); + raise + end; +end; + + +{ TCustomZlibStream } + +constructor TCustomZLibStream.Create(Strm: TStream); +begin + inherited Create; + FStrm := Strm; + FStrmPos := Strm.Position; +{$IFNDEF FPCPASZLIB} + FZRec.zalloc := @zlibAllocMem; + FZRec.zfree := @zlibFreeMem; +{$ENDIF} +end; + +procedure TCustomZLibStream.Progress(Sender: TObject); +begin + if Assigned(FOnProgress) then FOnProgress(Sender); +end; + +{ TCompressionStream } + +constructor TCompressionStream.Create(CompressionLevel: TCompressionLevel; + Dest: TStream); +const + Levels: array [TCompressionLevel] of ShortInt = + (Z_NO_COMPRESSION, Z_BEST_SPEED, Z_DEFAULT_COMPRESSION, Z_BEST_COMPRESSION); +begin + inherited Create(Dest); + FZRec.next_out := @FBuffer; + FZRec.avail_out := sizeof(FBuffer); + CCheck(deflateInit_(FZRec, Levels[CompressionLevel], zlib_version, sizeof(FZRec))); +end; + +destructor TCompressionStream.Destroy; +begin + FZRec.next_in := nil; + FZRec.avail_in := 0; + try + if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos; + while (CCheck(deflate(FZRec, Z_FINISH)) <> Z_STREAM_END) + and (FZRec.avail_out = 0) do + begin + FStrm.WriteBuffer(FBuffer, sizeof(FBuffer)); + FZRec.next_out := @FBuffer; + FZRec.avail_out := sizeof(FBuffer); + end; + if FZRec.avail_out < sizeof(FBuffer) then + FStrm.WriteBuffer(FBuffer, sizeof(FBuffer) - FZRec.avail_out); + finally + deflateEnd(FZRec); + end; + inherited Destroy; +end; + +function TCompressionStream.Read(var Buffer; Count: Longint): Longint; +begin + raise ECompressionError.Create('Invalid stream operation'); +end; + +function TCompressionStream.Write(const Buffer; Count: Longint): Longint; +begin + FZRec.next_in := @Buffer; + FZRec.avail_in := Count; + if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos; + while (FZRec.avail_in > 0) do + begin + CCheck(deflate(FZRec, 0)); + if FZRec.avail_out = 0 then + begin + FStrm.WriteBuffer(FBuffer, sizeof(FBuffer)); + FZRec.next_out := @FBuffer; + FZRec.avail_out := sizeof(FBuffer); + FStrmPos := FStrm.Position; + Progress(Self); + end; + end; + Result := Count; +end; + +function TCompressionStream.Seek(Offset: Longint; Origin: Word): Longint; +begin + if (Offset = 0) and (Origin = soFromCurrent) then + Result := FZRec.total_in + else + raise ECompressionError.Create('Invalid stream operation'); +end; + +function TCompressionStream.GetCompressionRate: Single; +begin + if FZRec.total_in = 0 then + Result := 0 + else + Result := (1.0 - (FZRec.total_out / FZRec.total_in)) * 100.0; +end; + +{ TDecompressionStream } + +constructor TDecompressionStream.Create(Source: TStream); +begin + inherited Create(Source); + FZRec.next_in := @FBuffer; + FZRec.avail_in := 0; + DCheck(inflateInit_(FZRec, zlib_version, sizeof(FZRec))); +end; + +destructor TDecompressionStream.Destroy; +begin + inflateEnd(FZRec); + inherited Destroy; +end; + +function TDecompressionStream.Read(var Buffer; Count: Longint): Longint; +begin + FZRec.next_out := @Buffer; + FZRec.avail_out := Count; + if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos; + while (FZRec.avail_out > 0) do + begin + if FZRec.avail_in = 0 then + begin + FZRec.avail_in := FStrm.Read(FBuffer, sizeof(FBuffer)); + if FZRec.avail_in = 0 then + begin + Result := Count - Integer(FZRec.avail_out); + Exit; + end; + FZRec.next_in := @FBuffer; + FStrmPos := FStrm.Position; + Progress(Self); + end; + CCheck(inflate(FZRec, 0)); + end; + Result := Count; +end; + +function TDecompressionStream.Write(const Buffer; Count: Longint): Longint; +begin + raise EDecompressionError.Create('Invalid stream operation'); +end; + +function TDecompressionStream.Seek(Offset: Longint; Origin: Word): Longint; +var + I: Integer; + Buf: array [0..4095] of Byte; +begin + if (Offset = 0) and (Origin = soFromBeginning) then + begin + DCheck(inflateReset(FZRec)); + FZRec.next_in := @FBuffer; + FZRec.avail_in := 0; + FStrm.Position := 0; + FStrmPos := 0; + end + else if ( (Offset >= 0) and (Origin = soFromCurrent)) or + ( ((Offset - Integer(FZRec.total_out)) > 0) and (Origin = soFromBeginning)) then + begin + if Origin = soFromBeginning then Dec(Offset, FZRec.total_out); + if Offset > 0 then + begin + for I := 1 to Offset div sizeof(Buf) do + ReadBuffer(Buf, sizeof(Buf)); + ReadBuffer(Buf, Offset mod sizeof(Buf)); + end; + end + else + raise EDecompressionError.Create('Invalid stream operation'); + Result := FZRec.total_out; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/ZLib/imadler.pas b/resources/libraries/deskew/Imaging/ZLib/imadler.pas new file mode 100755 index 0000000..4438056 --- /dev/null +++ b/resources/libraries/deskew/Imaging/ZLib/imadler.pas @@ -0,0 +1,114 @@ +Unit imadler; + +{ + adler32.c -- compute the Adler-32 checksum of a data stream + Copyright (C) 1995-1998 Mark Adler + + Pascal tranlastion + Copyright (C) 1998 by Jacques Nomssi Nzali + For conditions of distribution and use, see copyright notice in readme.txt +} + +interface + +{$I imzconf.inc} + +uses + imzutil; + +function adler32(adler : uLong; buf : pBytef; len : uInt) : uLong; + +{ Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NIL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + var + adler : uLong; + begin + adler := adler32(0, Z_NULL, 0); + + while (read_buffer(buffer, length) <> EOF) do + adler := adler32(adler, buffer, length); + + if (adler <> original_adler) then + error(); + end; +} + +implementation + +const + BASE = uLong(65521); { largest prime smaller than 65536 } + {NMAX = 5552; original code with unsigned 32 bit integer } + { NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 } + NMAX = 3854; { code with signed 32 bit integer } + { NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^31-1 } + { The penalty is the time loss in the extra MOD-calls. } + + +{ ========================================================================= } + +function adler32(adler : uLong; buf : pBytef; len : uInt) : uLong; +var + s1, s2 : uLong; + k : int; +begin + s1 := adler and $ffff; + s2 := (adler shr 16) and $ffff; + + if not Assigned(buf) then + begin + adler32 := uLong(1); + exit; + end; + + while (len > 0) do + begin + if len < NMAX then + k := len + else + k := NMAX; + Dec(len, k); + { + while (k >= 16) do + begin + DO16(buf); + Inc(buf, 16); + Dec(k, 16); + end; + if (k <> 0) then + repeat + Inc(s1, buf^); + Inc(puf); + Inc(s2, s1); + Dec(k); + until (k = 0); + } + while (k > 0) do + begin + Inc(s1, buf^); + Inc(s2, s1); + Inc(buf); + Dec(k); + end; + s1 := s1 mod BASE; + s2 := s2 mod BASE; + end; + adler32 := (s2 shl 16) or s1; +end; + +{ +#define DO1(buf,i) + begin + Inc(s1, buf[i]); + Inc(s2, s1); + end; +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); +} +end. + diff --git a/resources/libraries/deskew/Imaging/ZLib/iminfblock.pas b/resources/libraries/deskew/Imaging/ZLib/iminfblock.pas new file mode 100755 index 0000000..7ab003f --- /dev/null +++ b/resources/libraries/deskew/Imaging/ZLib/iminfblock.pas @@ -0,0 +1,951 @@ +Unit iminfblock; + +{ infblock.h and + infblock.c -- interpret and process block types to last block + Copyright (C) 1995-1998 Mark Adler + + Pascal tranlastion + Copyright (C) 1998 by Jacques Nomssi Nzali + For conditions of distribution and use, see copyright notice in readme.txt +} + +interface + +{$I imzconf.inc} + +uses + {$IFDEF DEBUG} + SysUtils, strutils, + {$ENDIF} + imzutil, impaszlib; + +function inflate_blocks_new(var z : z_stream; + c : check_func; { check function } + w : uInt { window size } + ) : pInflate_blocks_state; + +function inflate_blocks (var s : inflate_blocks_state; + var z : z_stream; + r : int { initial return code } + ) : int; + +procedure inflate_blocks_reset (var s : inflate_blocks_state; + var z : z_stream; + c : puLong); { check value on output } + + +function inflate_blocks_free(s : pInflate_blocks_state; + var z : z_stream) : int; + +procedure inflate_set_dictionary(var s : inflate_blocks_state; + const d : array of byte; { dictionary } + n : uInt); { dictionary length } + +function inflate_blocks_sync_point(var s : inflate_blocks_state) : int; + +implementation + +uses + iminfcodes, iminftrees, iminfutil; + +{ Tables for deflate from PKZIP's appnote.txt. } +Const + border : Array [0..18] Of Word { Order of the bit length code lengths } + = (16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15); + +{ Notes beyond the 1.93a appnote.txt: + + 1. Distance pointers never point before the beginning of the output + stream. + 2. Distance pointers can point back across blocks, up to 32k away. + 3. There is an implied maximum of 7 bits for the bit length table and + 15 bits for the actual data. + 4. If only one code exists, then it is encoded using one bit. (Zero + would be more efficient, but perhaps a little confusing.) If two + codes exist, they are coded using one bit each (0 and 1). + 5. There is no way of sending zero distance codes--a dummy must be + sent if there are none. (History: a pre 2.0 version of PKZIP would + store blocks with no distance codes, but this was discovered to be + too harsh a criterion.) Valid only for 1.93a. 2.04c does allow + zero distance codes, which is sent as one code of zero bits in + length. + 6. There are up to 286 literal/length codes. Code 256 represents the + end-of-block. Note however that the static length tree defines + 288 codes just to fill out the Huffman codes. Codes 286 and 287 + cannot be used though, since there is no length base or extra bits + defined for them. Similarily, there are up to 30 distance codes. + However, static trees define 32 codes (all 5 bits) to fill out the + Huffman codes, but the last two had better not show up in the data. + 7. Unzip can check dynamic Huffman blocks for complete code sets. + The exception is that a single code would not be complete (see #4). + 8. The five bits following the block type is really the number of + literal codes sent minus 257. + 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits + (1+6+6). Therefore, to output three times the length, you output + three codes (1+1+1), whereas to output four times the same length, + you only need two codes (1+3). Hmm. + 10. In the tree reconstruction algorithm, Code = Code + Increment + only if BitLength(i) is not zero. (Pretty obvious.) + 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) + 12. Note: length code 284 can represent 227-258, but length code 285 + really is 258. The last length deserves its own, short code + since it gets used a lot in very redundant files. The length + 258 is special since 258 - 3 (the min match length) is 255. + 13. The literal/length and distance code bit lengths are read as a + single stream of lengths. It is possible (and advantageous) for + a repeat code (16, 17, or 18) to go across the boundary between + the two sets of lengths. } + + +procedure inflate_blocks_reset (var s : inflate_blocks_state; + var z : z_stream; + c : puLong); { check value on output } +begin + if (c <> Z_NULL) then + c^ := s.check; + if (s.mode = BTREE) or (s.mode = DTREE) then + ZFREE(z, s.sub.trees.blens); + if (s.mode = CODES) then + inflate_codes_free(s.sub.decode.codes, z); + + s.mode := ZTYPE; + s.bitk := 0; + s.bitb := 0; + + s.write := s.window; + s.read := s.window; + if Assigned(s.checkfn) then + begin + s.check := s.checkfn(uLong(0), pBytef(NIL), 0); + z.adler := s.check; + end; + {$IFDEF DEBUG} + Tracev('inflate: blocks reset'); + {$ENDIF} +end; + + +function inflate_blocks_new(var z : z_stream; + c : check_func; { check function } + w : uInt { window size } + ) : pInflate_blocks_state; +var + s : pInflate_blocks_state; +begin + s := pInflate_blocks_state( ZALLOC(z,1, sizeof(inflate_blocks_state)) ); + if (s = Z_NULL) then + begin + inflate_blocks_new := s; + exit; + end; + s^.hufts := huft_ptr( ZALLOC(z, sizeof(inflate_huft), MANY) ); + + if (s^.hufts = Z_NULL) then + begin + ZFREE(z, s); + inflate_blocks_new := Z_NULL; + exit; + end; + + s^.window := pBytef( ZALLOC(z, 1, w) ); + if (s^.window = Z_NULL) then + begin + ZFREE(z, s^.hufts); + ZFREE(z, s); + inflate_blocks_new := Z_NULL; + exit; + end; + s^.zend := s^.window; + Inc(s^.zend, w); + s^.checkfn := c; + s^.mode := ZTYPE; + {$IFDEF DEBUG} + Tracev('inflate: blocks allocated'); + {$ENDIF} + inflate_blocks_reset(s^, z, Z_NULL); + inflate_blocks_new := s; +end; + + +function inflate_blocks (var s : inflate_blocks_state; + var z : z_stream; + r : int) : int; { initial return code } +label + start_btree, start_dtree, + start_blkdone, start_dry, + start_codes; + +var + t : uInt; { temporary storage } + b : uLong; { bit buffer } + k : uInt; { bits in bit buffer } + p : pBytef; { input data pointer } + n : uInt; { bytes available there } + q : pBytef; { output window write pointer } + m : uInt; { bytes to end of window or read pointer } +{ fixed code blocks } +var + bl, bd : uInt; + tl, td : pInflate_huft; +var + h : pInflate_huft; + i, j, c : uInt; +var + cs : pInflate_codes_state; +begin + { copy input/output information to locals } + p := z.next_in; + n := z.avail_in; + b := s.bitb; + k := s.bitk; + q := s.write; + if ptr2int(q) < ptr2int(s.read) then + m := uInt(ptr2int(s.read)-ptr2int(q)-1) + else + m := uInt(ptr2int(s.zend)-ptr2int(q)); + +{ decompress an inflated block } + + + { process input based on current state } + while True do + Case s.mode of + ZTYPE: + begin + {NEEDBITS(3);} + while (k < 3) do + begin + {NEEDBYTE;} + if (n <> 0) then + r :=Z_OK + else + begin + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_blocks := inflate_flush(s,z,r); + exit; + end; + Dec(n); + b := b or (uLong(p^) shl k); + Inc(p); + Inc(k, 8); + end; + + t := uInt(b) and 7; + s.last := boolean(t and 1); + case (t shr 1) of + 0: { stored } + begin + {$IFDEF DEBUG} + if s.last then + Tracev('inflate: stored block (last)') + else + Tracev('inflate: stored block'); + {$ENDIF} + {DUMPBITS(3);} + b := b shr 3; + Dec(k, 3); + + t := k and 7; { go to byte boundary } + {DUMPBITS(t);} + b := b shr t; + Dec(k, t); + + s.mode := LENS; { get length of stored block } + end; + 1: { fixed } + begin + begin + {$IFDEF DEBUG} + if s.last then + Tracev('inflate: fixed codes blocks (last)') + else + Tracev('inflate: fixed codes blocks'); + {$ENDIF} + inflate_trees_fixed(bl, bd, tl, td, z); + s.sub.decode.codes := inflate_codes_new(bl, bd, tl, td, z); + if (s.sub.decode.codes = Z_NULL) then + begin + r := Z_MEM_ERROR; + { update pointers and return } + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p) - ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_blocks := inflate_flush(s,z,r); + exit; + end; + end; + {DUMPBITS(3);} + b := b shr 3; + Dec(k, 3); + + s.mode := CODES; + end; + 2: { dynamic } + begin + {$IFDEF DEBUG} + if s.last then + Tracev('inflate: dynamic codes block (last)') + else + Tracev('inflate: dynamic codes block'); + {$ENDIF} + {DUMPBITS(3);} + b := b shr 3; + Dec(k, 3); + + s.mode := TABLE; + end; + 3: + begin { illegal } + {DUMPBITS(3);} + b := b shr 3; + Dec(k, 3); + + s.mode := BLKBAD; + z.msg := 'invalid block type'; + r := Z_DATA_ERROR; + { update pointers and return } + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p) - ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_blocks := inflate_flush(s,z,r); + exit; + end; + end; + end; + LENS: + begin + {NEEDBITS(32);} + while (k < 32) do + begin + {NEEDBYTE;} + if (n <> 0) then + r :=Z_OK + else + begin + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_blocks := inflate_flush(s,z,r); + exit; + end; + Dec(n); + b := b or (uLong(p^) shl k); + Inc(p); + Inc(k, 8); + end; + + if (((not b) shr 16) and $ffff) <> (b and $ffff) then + begin + s.mode := BLKBAD; + z.msg := 'invalid stored block lengths'; + r := Z_DATA_ERROR; + { update pointers and return } + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p) - ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_blocks := inflate_flush(s,z,r); + exit; + end; + s.sub.left := uInt(b) and $ffff; + k := 0; + b := 0; { dump bits } + {$IFDEF DEBUG} + Tracev('inflate: stored length '+IntToStr(s.sub.left)); + {$ENDIF} + if s.sub.left <> 0 then + s.mode := STORED + else + if s.last then + s.mode := DRY + else + s.mode := ZTYPE; + end; + STORED: + begin + if (n = 0) then + begin + { update pointers and return } + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p) - ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_blocks := inflate_flush(s,z,r); + exit; + end; + {NEEDOUT} + if (m = 0) then + begin + {WRAP} + if (q = s.zend) and (s.read <> s.window) then + begin + q := s.window; + if ptr2int(q) < ptr2int(s.read) then + m := uInt(ptr2int(s.read)-ptr2int(q)-1) + else + m := uInt(ptr2int(s.zend)-ptr2int(q)); + end; + + if (m = 0) then + begin + {FLUSH} + s.write := q; + r := inflate_flush(s,z,r); + q := s.write; + if ptr2int(q) < ptr2int(s.read) then + m := uInt(ptr2int(s.read)-ptr2int(q)-1) + else + m := uInt(ptr2int(s.zend)-ptr2int(q)); + + {WRAP} + if (q = s.zend) and (s.read <> s.window) then + begin + q := s.window; + if ptr2int(q) < ptr2int(s.read) then + m := uInt(ptr2int(s.read)-ptr2int(q)-1) + else + m := uInt(ptr2int(s.zend)-ptr2int(q)); + end; + + if (m = 0) then + begin + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_blocks := inflate_flush(s,z,r); + exit; + end; + end; + end; + r := Z_OK; + + t := s.sub.left; + if (t > n) then + t := n; + if (t > m) then + t := m; + zmemcpy(q, p, t); + Inc(p, t); Dec(n, t); + Inc(q, t); Dec(m, t); + Dec(s.sub.left, t); + if (s.sub.left = 0) then + begin + {$IFDEF DEBUG} + if (ptr2int(q) >= ptr2int(s.read)) then + Tracev('inflate: stored end '+ + IntToStr(z.total_out + ptr2int(q) - ptr2int(s.read)) + ' total out') + else + Tracev('inflate: stored end '+ + IntToStr(z.total_out + ptr2int(s.zend) - ptr2int(s.read) + + ptr2int(q) - ptr2int(s.window)) + ' total out'); + {$ENDIF} + if s.last then + s.mode := DRY + else + s.mode := ZTYPE; + end; + end; + TABLE: + begin + {NEEDBITS(14);} + while (k < 14) do + begin + {NEEDBYTE;} + if (n <> 0) then + r :=Z_OK + else + begin + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_blocks := inflate_flush(s,z,r); + exit; + end; + Dec(n); + b := b or (uLong(p^) shl k); + Inc(p); + Inc(k, 8); + end; + + t := uInt(b) and $3fff; + s.sub.trees.table := t; + {$ifndef PKZIP_BUG_WORKAROUND} + if ((t and $1f) > 29) or (((t shr 5) and $1f) > 29) then + begin + s.mode := BLKBAD; + z.msg := 'too many length or distance symbols'; + r := Z_DATA_ERROR; + { update pointers and return } + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p) - ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_blocks := inflate_flush(s,z,r); + exit; + end; + {$endif} + t := 258 + (t and $1f) + ((t shr 5) and $1f); + s.sub.trees.blens := puIntArray( ZALLOC(z, t, sizeof(uInt)) ); + if (s.sub.trees.blens = Z_NULL) then + begin + r := Z_MEM_ERROR; + { update pointers and return } + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p) - ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_blocks := inflate_flush(s,z,r); + exit; + end; + {DUMPBITS(14);} + b := b shr 14; + Dec(k, 14); + + s.sub.trees.index := 0; + {$IFDEF DEBUG} + Tracev('inflate: table sizes ok'); + {$ENDIF} + s.mode := BTREE; + { fall trough case is handled by the while } + { try GOTO for speed - Nomssi } + goto start_btree; + end; + BTREE: + begin + start_btree: + while (s.sub.trees.index < 4 + (s.sub.trees.table shr 10)) do + begin + {NEEDBITS(3);} + while (k < 3) do + begin + {NEEDBYTE;} + if (n <> 0) then + r :=Z_OK + else + begin + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_blocks := inflate_flush(s,z,r); + exit; + end; + Dec(n); + b := b or (uLong(p^) shl k); + Inc(p); + Inc(k, 8); + end; + + s.sub.trees.blens^[border[s.sub.trees.index]] := uInt(b) and 7; + Inc(s.sub.trees.index); + {DUMPBITS(3);} + b := b shr 3; + Dec(k, 3); + end; + while (s.sub.trees.index < 19) do + begin + s.sub.trees.blens^[border[s.sub.trees.index]] := 0; + Inc(s.sub.trees.index); + end; + s.sub.trees.bb := 7; + t := inflate_trees_bits(s.sub.trees.blens^, s.sub.trees.bb, + s.sub.trees.tb, s.hufts^, z); + if (t <> Z_OK) then + begin + ZFREE(z, s.sub.trees.blens); + r := t; + if (r = Z_DATA_ERROR) then + s.mode := BLKBAD; + { update pointers and return } + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p) - ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_blocks := inflate_flush(s,z,r); + exit; + end; + s.sub.trees.index := 0; + {$IFDEF DEBUG} + Tracev('inflate: bits tree ok'); + {$ENDIF} + s.mode := DTREE; + { fall through again } + goto start_dtree; + end; + DTREE: + begin + start_dtree: + while TRUE do + begin + t := s.sub.trees.table; + if not (s.sub.trees.index < 258 + + (t and $1f) + ((t shr 5) and $1f)) then + break; + t := s.sub.trees.bb; + {NEEDBITS(t);} + while (k < t) do + begin + {NEEDBYTE;} + if (n <> 0) then + r :=Z_OK + else + begin + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_blocks := inflate_flush(s,z,r); + exit; + end; + Dec(n); + b := b or (uLong(p^) shl k); + Inc(p); + Inc(k, 8); + end; + + h := s.sub.trees.tb; + Inc(h, uInt(b) and inflate_mask[t]); + t := h^.Bits; + c := h^.Base; + + if (c < 16) then + begin + {DUMPBITS(t);} + b := b shr t; + Dec(k, t); + + s.sub.trees.blens^[s.sub.trees.index] := c; + Inc(s.sub.trees.index); + end + else { c = 16..18 } + begin + if c = 18 then + begin + i := 7; + j := 11; + end + else + begin + i := c - 14; + j := 3; + end; + {NEEDBITS(t + i);} + while (k < t + i) do + begin + {NEEDBYTE;} + if (n <> 0) then + r :=Z_OK + else + begin + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_blocks := inflate_flush(s,z,r); + exit; + end; + Dec(n); + b := b or (uLong(p^) shl k); + Inc(p); + Inc(k, 8); + end; + + {DUMPBITS(t);} + b := b shr t; + Dec(k, t); + + Inc(j, uInt(b) and inflate_mask[i]); + {DUMPBITS(i);} + b := b shr i; + Dec(k, i); + + i := s.sub.trees.index; + t := s.sub.trees.table; + if (i + j > 258 + (t and $1f) + ((t shr 5) and $1f)) or + ((c = 16) and (i < 1)) then + begin + ZFREE(z, s.sub.trees.blens); + s.mode := BLKBAD; + z.msg := 'invalid bit length repeat'; + r := Z_DATA_ERROR; + { update pointers and return } + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p) - ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_blocks := inflate_flush(s,z,r); + exit; + end; + if c = 16 then + c := s.sub.trees.blens^[i - 1] + else + c := 0; + repeat + s.sub.trees.blens^[i] := c; + Inc(i); + Dec(j); + until (j=0); + s.sub.trees.index := i; + end; + end; { while } + s.sub.trees.tb := Z_NULL; + begin + bl := 9; { must be <= 9 for lookahead assumptions } + bd := 6; { must be <= 9 for lookahead assumptions } + t := s.sub.trees.table; + t := inflate_trees_dynamic(257 + (t and $1f), + 1 + ((t shr 5) and $1f), + s.sub.trees.blens^, bl, bd, tl, td, s.hufts^, z); + ZFREE(z, s.sub.trees.blens); + if (t <> Z_OK) then + begin + if (t = uInt(Z_DATA_ERROR)) then + s.mode := BLKBAD; + r := t; + { update pointers and return } + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p) - ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_blocks := inflate_flush(s,z,r); + exit; + end; + {$IFDEF DEBUG} + Tracev('inflate: trees ok'); + {$ENDIF} + { c renamed to cs } + cs := inflate_codes_new(bl, bd, tl, td, z); + if (cs = Z_NULL) then + begin + r := Z_MEM_ERROR; + { update pointers and return } + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p) - ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_blocks := inflate_flush(s,z,r); + exit; + end; + s.sub.decode.codes := cs; + end; + s.mode := CODES; + { yet another falltrough } + goto start_codes; + end; + CODES: + begin + start_codes: + { update pointers } + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p) - ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + + r := inflate_codes(s, z, r); + if (r <> Z_STREAM_END) then + begin + inflate_blocks := inflate_flush(s, z, r); + exit; + end; + r := Z_OK; + inflate_codes_free(s.sub.decode.codes, z); + { load local pointers } + p := z.next_in; + n := z.avail_in; + b := s.bitb; + k := s.bitk; + q := s.write; + if ptr2int(q) < ptr2int(s.read) then + m := uInt(ptr2int(s.read)-ptr2int(q)-1) + else + m := uInt(ptr2int(s.zend)-ptr2int(q)); + {$IFDEF DEBUG} + if (ptr2int(q) >= ptr2int(s.read)) then + Tracev('inflate: codes end '+ + IntToStr(z.total_out + ptr2int(q) - ptr2int(s.read)) + ' total out') + else + Tracev('inflate: codes end '+ + IntToStr(z.total_out + ptr2int(s.zend) - ptr2int(s.read) + + ptr2int(q) - ptr2int(s.window)) + ' total out'); + {$ENDIF} + if (not s.last) then + begin + s.mode := ZTYPE; + continue; { break for switch statement in C-code } + end; + {$ifndef patch112} + if (k > 7) then { return unused byte, if any } + begin + {$IFDEF DEBUG} + Assert(k < 16, 'inflate_codes grabbed too many bytes'); + {$ENDIF} + Dec(k, 8); + Inc(n); + Dec(p); { can always return one } + end; + {$endif} + s.mode := DRY; + { another falltrough } + goto start_dry; + end; + DRY: + begin + start_dry: + {FLUSH} + s.write := q; + r := inflate_flush(s,z,r); + q := s.write; + + { not needed anymore, we are done: + if ptr2int(q) < ptr2int(s.read) then + m := uInt(ptr2int(s.read)-ptr2int(q)-1) + else + m := uInt(ptr2int(s.zend)-ptr2int(q)); + } + + if (s.read <> s.write) then + begin + { update pointers and return } + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p) - ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_blocks := inflate_flush(s,z,r); + exit; + end; + s.mode := BLKDONE; + goto start_blkdone; + end; + BLKDONE: + begin + start_blkdone: + r := Z_STREAM_END; + { update pointers and return } + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p) - ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_blocks := inflate_flush(s,z,r); + exit; + end; + BLKBAD: + begin + r := Z_DATA_ERROR; + { update pointers and return } + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p) - ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_blocks := inflate_flush(s,z,r); + exit; + end; + else + begin + r := Z_STREAM_ERROR; + { update pointers and return } + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p) - ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_blocks := inflate_flush(s,z,r); + exit; + end; + end; { Case s.mode of } + +end; + + +function inflate_blocks_free(s : pInflate_blocks_state; + var z : z_stream) : int; +begin + inflate_blocks_reset(s^, z, Z_NULL); + ZFREE(z, s^.window); + ZFREE(z, s^.hufts); + ZFREE(z, s); + {$IFDEF DEBUG} + Trace('inflate: blocks freed'); + {$ENDIF} + inflate_blocks_free := Z_OK; +end; + + +procedure inflate_set_dictionary(var s : inflate_blocks_state; + const d : array of byte; { dictionary } + n : uInt); { dictionary length } +begin + zmemcpy(s.window, pBytef(@d), n); + s.write := s.window; + Inc(s.write, n); + s.read := s.write; +end; + + +{ Returns true if inflate is currently at the end of a block generated + by Z_SYNC_FLUSH or Z_FULL_FLUSH. + IN assertion: s <> Z_NULL } + +function inflate_blocks_sync_point(var s : inflate_blocks_state) : int; +begin + inflate_blocks_sync_point := int(s.mode = LENS); +end; + +end. diff --git a/resources/libraries/deskew/Imaging/ZLib/iminfcodes.pas b/resources/libraries/deskew/Imaging/ZLib/iminfcodes.pas new file mode 100755 index 0000000..5a1a781 --- /dev/null +++ b/resources/libraries/deskew/Imaging/ZLib/iminfcodes.pas @@ -0,0 +1,576 @@ +Unit iminfcodes; + +{ infcodes.c -- process literals and length/distance pairs + Copyright (C) 1995-1998 Mark Adler + + Pascal tranlastion + Copyright (C) 1998 by Jacques Nomssi Nzali + For conditions of distribution and use, see copyright notice in readme.txt +} + +interface + +{$I imzconf.inc} + +uses + {$IFDEF DEBUG} + SysUtils, strutils, + {$ENDIF} + imzutil, impaszlib; + +function inflate_codes_new (bl : uInt; + bd : uInt; + tl : pInflate_huft; + td : pInflate_huft; + var z : z_stream): pInflate_codes_state; + +function inflate_codes(var s : inflate_blocks_state; + var z : z_stream; + r : int) : int; + +procedure inflate_codes_free(c : pInflate_codes_state; + var z : z_stream); + +implementation + +uses + iminfutil, iminffast; + + +function inflate_codes_new (bl : uInt; + bd : uInt; + tl : pInflate_huft; + td : pInflate_huft; + var z : z_stream): pInflate_codes_state; +var + c : pInflate_codes_state; +begin + c := pInflate_codes_state( ZALLOC(z,1,sizeof(inflate_codes_state)) ); + if (c <> Z_NULL) then + begin + c^.mode := START; + c^.lbits := Byte(bl); + c^.dbits := Byte(bd); + c^.ltree := tl; + c^.dtree := td; + {$IFDEF DEBUG} + Tracev('inflate: codes new'); + {$ENDIF} + end; + inflate_codes_new := c; +end; + + +function inflate_codes(var s : inflate_blocks_state; + var z : z_stream; + r : int) : int; +var + j : uInt; { temporary storage } + t : pInflate_huft; { temporary pointer } + e : uInt; { extra bits or operation } + b : uLong; { bit buffer } + k : uInt; { bits in bit buffer } + p : pBytef; { input data pointer } + n : uInt; { bytes available there } + q : pBytef; { output window write pointer } + m : uInt; { bytes to end of window or read pointer } + f : pBytef; { pointer to copy strings from } +var + c : pInflate_codes_state; +begin + c := s.sub.decode.codes; { codes state } + + { copy input/output information to locals } + p := z.next_in; + n := z.avail_in; + b := s.bitb; + k := s.bitk; + q := s.write; + if ptr2int(q) < ptr2int(s.read) then + m := uInt(ptr2int(s.read)-ptr2int(q)-1) + else + m := uInt(ptr2int(s.zend)-ptr2int(q)); + + { process input and output based on current state } + while True do + case (c^.mode) of + { waiting for "i:"=input, "o:"=output, "x:"=nothing } + START: { x: set up for LEN } + begin +{$ifndef SLOW} + if (m >= 258) and (n >= 10) then + begin + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + + r := inflate_fast(c^.lbits, c^.dbits, c^.ltree, c^.dtree, s, z); + {LOAD} + p := z.next_in; + n := z.avail_in; + b := s.bitb; + k := s.bitk; + q := s.write; + if ptr2int(q) < ptr2int(s.read) then + m := uInt(ptr2int(s.read)-ptr2int(q)-1) + else + m := uInt(ptr2int(s.zend)-ptr2int(q)); + + if (r <> Z_OK) then + begin + if (r = Z_STREAM_END) then + c^.mode := WASH + else + c^.mode := BADCODE; + continue; { break for switch-statement in C } + end; + end; +{$endif} { not SLOW } + c^.sub.code.need := c^.lbits; + c^.sub.code.tree := c^.ltree; + c^.mode := LEN; { falltrough } + end; + LEN: { i: get length/literal/eob next } + begin + j := c^.sub.code.need; + {NEEDBITS(j);} + while (k < j) do + begin + {NEEDBYTE;} + if (n <> 0) then + r :=Z_OK + else + begin + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_codes := inflate_flush(s,z,r); + exit; + end; + Dec(n); + b := b or (uLong(p^) shl k); + Inc(p); + Inc(k, 8); + end; + t := c^.sub.code.tree; + Inc(t, uInt(b) and inflate_mask[j]); + {DUMPBITS(t^.bits);} + b := b shr t^.bits; + Dec(k, t^.bits); + + e := uInt(t^.exop); + if (e = 0) then { literal } + begin + c^.sub.lit := t^.base; + {$IFDEF DEBUG} + if (t^.base >= $20) and (t^.base < $7f) then + Tracevv('inflate: literal '+AnsiChar(t^.base)) + else + Tracevv('inflate: literal '+IntToStr(t^.base)); + {$ENDIF} + c^.mode := LIT; + continue; { break switch statement } + end; + if (e and 16 <> 0) then { length } + begin + c^.sub.copy.get := e and 15; + c^.len := t^.base; + c^.mode := LENEXT; + continue; { break C-switch statement } + end; + if (e and 64 = 0) then { next table } + begin + c^.sub.code.need := e; + c^.sub.code.tree := @huft_ptr(t)^[t^.base]; + continue; { break C-switch statement } + end; + if (e and 32 <> 0) then { end of block } + begin + {$IFDEF DEBUG} + Tracevv('inflate: end of block'); + {$ENDIF} + c^.mode := WASH; + continue; { break C-switch statement } + end; + c^.mode := BADCODE; { invalid code } + z.msg := 'invalid literal/length code'; + r := Z_DATA_ERROR; + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_codes := inflate_flush(s,z,r); + exit; + end; + LENEXT: { i: getting length extra (have base) } + begin + j := c^.sub.copy.get; + {NEEDBITS(j);} + while (k < j) do + begin + {NEEDBYTE;} + if (n <> 0) then + r :=Z_OK + else + begin + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_codes := inflate_flush(s,z,r); + exit; + end; + Dec(n); + b := b or (uLong(p^) shl k); + Inc(p); + Inc(k, 8); + end; + Inc(c^.len, uInt(b and inflate_mask[j])); + {DUMPBITS(j);} + b := b shr j; + Dec(k, j); + + c^.sub.code.need := c^.dbits; + c^.sub.code.tree := c^.dtree; + {$IFDEF DEBUG} + Tracevv('inflate: length '+IntToStr(c^.len)); + {$ENDIF} + c^.mode := DIST; + { falltrough } + end; + DIST: { i: get distance next } + begin + j := c^.sub.code.need; + {NEEDBITS(j);} + while (k < j) do + begin + {NEEDBYTE;} + if (n <> 0) then + r :=Z_OK + else + begin + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_codes := inflate_flush(s,z,r); + exit; + end; + Dec(n); + b := b or (uLong(p^) shl k); + Inc(p); + Inc(k, 8); + end; + t := @huft_ptr(c^.sub.code.tree)^[uInt(b) and inflate_mask[j]]; + {DUMPBITS(t^.bits);} + b := b shr t^.bits; + Dec(k, t^.bits); + + e := uInt(t^.exop); + if (e and 16 <> 0) then { distance } + begin + c^.sub.copy.get := e and 15; + c^.sub.copy.dist := t^.base; + c^.mode := DISTEXT; + continue; { break C-switch statement } + end; + if (e and 64 = 0) then { next table } + begin + c^.sub.code.need := e; + c^.sub.code.tree := @huft_ptr(t)^[t^.base]; + continue; { break C-switch statement } + end; + c^.mode := BADCODE; { invalid code } + z.msg := 'invalid distance code'; + r := Z_DATA_ERROR; + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_codes := inflate_flush(s,z,r); + exit; + end; + DISTEXT: { i: getting distance extra } + begin + j := c^.sub.copy.get; + {NEEDBITS(j);} + while (k < j) do + begin + {NEEDBYTE;} + if (n <> 0) then + r :=Z_OK + else + begin + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_codes := inflate_flush(s,z,r); + exit; + end; + Dec(n); + b := b or (uLong(p^) shl k); + Inc(p); + Inc(k, 8); + end; + Inc(c^.sub.copy.dist, uInt(b) and inflate_mask[j]); + {DUMPBITS(j);} + b := b shr j; + Dec(k, j); + {$IFDEF DEBUG} + Tracevv('inflate: distance '+ IntToStr(c^.sub.copy.dist)); + {$ENDIF} + c^.mode := COPY; + { falltrough } + end; + COPY: { o: copying bytes in window, waiting for space } + begin + f := q; + Dec(f, c^.sub.copy.dist); + if (uInt(ptr2int(q) - ptr2int(s.window)) < c^.sub.copy.dist) then + begin + f := s.zend; + Dec(f, c^.sub.copy.dist - uInt(ptr2int(q) - ptr2int(s.window))); + end; + + while (c^.len <> 0) do + begin + {NEEDOUT} + if (m = 0) then + begin + {WRAP} + if (q = s.zend) and (s.read <> s.window) then + begin + q := s.window; + if ptr2int(q) < ptr2int(s.read) then + m := uInt(ptr2int(s.read)-ptr2int(q)-1) + else + m := uInt(ptr2int(s.zend)-ptr2int(q)); + end; + + if (m = 0) then + begin + {FLUSH} + s.write := q; + r := inflate_flush(s,z,r); + q := s.write; + if ptr2int(q) < ptr2int(s.read) then + m := uInt(ptr2int(s.read)-ptr2int(q)-1) + else + m := uInt(ptr2int(s.zend)-ptr2int(q)); + + {WRAP} + if (q = s.zend) and (s.read <> s.window) then + begin + q := s.window; + if ptr2int(q) < ptr2int(s.read) then + m := uInt(ptr2int(s.read)-ptr2int(q)-1) + else + m := uInt(ptr2int(s.zend)-ptr2int(q)); + end; + + if (m = 0) then + begin + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_codes := inflate_flush(s,z,r); + exit; + end; + end; + end; + r := Z_OK; + + {OUTBYTE( *f++)} + q^ := f^; + Inc(q); + Inc(f); + Dec(m); + + if (f = s.zend) then + f := s.window; + Dec(c^.len); + end; + c^.mode := START; + { C-switch break; not needed } + end; + LIT: { o: got literal, waiting for output space } + begin + {NEEDOUT} + if (m = 0) then + begin + {WRAP} + if (q = s.zend) and (s.read <> s.window) then + begin + q := s.window; + if ptr2int(q) < ptr2int(s.read) then + m := uInt(ptr2int(s.read)-ptr2int(q)-1) + else + m := uInt(ptr2int(s.zend)-ptr2int(q)); + end; + + if (m = 0) then + begin + {FLUSH} + s.write := q; + r := inflate_flush(s,z,r); + q := s.write; + if ptr2int(q) < ptr2int(s.read) then + m := uInt(ptr2int(s.read)-ptr2int(q)-1) + else + m := uInt(ptr2int(s.zend)-ptr2int(q)); + + {WRAP} + if (q = s.zend) and (s.read <> s.window) then + begin + q := s.window; + if ptr2int(q) < ptr2int(s.read) then + m := uInt(ptr2int(s.read)-ptr2int(q)-1) + else + m := uInt(ptr2int(s.zend)-ptr2int(q)); + end; + + if (m = 0) then + begin + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_codes := inflate_flush(s,z,r); + exit; + end; + end; + end; + r := Z_OK; + + {OUTBYTE(c^.sub.lit);} + q^ := c^.sub.lit; + Inc(q); + Dec(m); + + c^.mode := START; + {break;} + end; + WASH: { o: got eob, possibly more output } + begin + {$ifdef patch112} + if (k > 7) then { return unused byte, if any } + begin + {$IFDEF DEBUG} + Assert(k < 16, 'inflate_codes grabbed too many bytes'); + {$ENDIF} + Dec(k, 8); + Inc(n); + Dec(p); { can always return one } + end; + {$endif} + {FLUSH} + s.write := q; + r := inflate_flush(s,z,r); + q := s.write; + if ptr2int(q) < ptr2int(s.read) then + m := uInt(ptr2int(s.read)-ptr2int(q)-1) + else + m := uInt(ptr2int(s.zend)-ptr2int(q)); + + if (s.read <> s.write) then + begin + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_codes := inflate_flush(s,z,r); + exit; + end; + c^.mode := ZEND; + { falltrough } + end; + + ZEND: + begin + r := Z_STREAM_END; + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_codes := inflate_flush(s,z,r); + exit; + end; + BADCODE: { x: got error } + begin + r := Z_DATA_ERROR; + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_codes := inflate_flush(s,z,r); + exit; + end; + else + begin + r := Z_STREAM_ERROR; + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_codes := inflate_flush(s,z,r); + exit; + end; + end; +{NEED_DUMMY_RETURN - Delphi2+ dumb compilers complain without this } + inflate_codes := Z_STREAM_ERROR; +end; + + +procedure inflate_codes_free(c : pInflate_codes_state; + var z : z_stream); +begin + ZFREE(z, c); + {$IFDEF DEBUG} + Tracev('inflate: codes free'); + {$ENDIF} +end; + +end. diff --git a/resources/libraries/deskew/Imaging/ZLib/iminffast.pas b/resources/libraries/deskew/Imaging/ZLib/iminffast.pas new file mode 100755 index 0000000..400b0fc --- /dev/null +++ b/resources/libraries/deskew/Imaging/ZLib/iminffast.pas @@ -0,0 +1,318 @@ +Unit iminffast; + +{ + inffast.h and + inffast.c -- process literals and length/distance pairs fast + Copyright (C) 1995-1998 Mark Adler + + Pascal tranlastion + Copyright (C) 1998 by Jacques Nomssi Nzali + For conditions of distribution and use, see copyright notice in readme.txt +} + + +interface + +{$I imzconf.inc} + +uses + {$ifdef DEBUG} + SysUtils, strutils, + {$ENDIF} + imzutil, impaszlib; + +function inflate_fast( bl : uInt; + bd : uInt; + tl : pInflate_huft; + td : pInflate_huft; + var s : inflate_blocks_state; + var z : z_stream) : int; + + +implementation + +uses + iminfutil; + + +{ Called with number of bytes left to write in window at least 258 + (the maximum string length) and number of input bytes available + at least ten. The ten bytes are six bytes for the longest length/ + distance pair plus four bytes for overloading the bit buffer. } + +function inflate_fast( bl : uInt; + bd : uInt; + tl : pInflate_huft; + td : pInflate_huft; + var s : inflate_blocks_state; + var z : z_stream) : int; + +var + t : pInflate_huft; { temporary pointer } + e : uInt; { extra bits or operation } + b : uLong; { bit buffer } + k : uInt; { bits in bit buffer } + p : pBytef; { input data pointer } + n : uInt; { bytes available there } + q : pBytef; { output window write pointer } + m : uInt; { bytes to end of window or read pointer } + ml : uInt; { mask for literal/length tree } + md : uInt; { mask for distance tree } + c : uInt; { bytes to copy } + d : uInt; { distance back to copy from } + r : pBytef; { copy source pointer } +begin + { load input, output, bit values (macro LOAD) } + p := z.next_in; + n := z.avail_in; + b := s.bitb; + k := s.bitk; + q := s.write; + if ptr2int(q) < ptr2int(s.read) then + m := uInt(ptr2int(s.read)-ptr2int(q)-1) + else + m := uInt(ptr2int(s.zend)-ptr2int(q)); + + { initialize masks } + ml := inflate_mask[bl]; + md := inflate_mask[bd]; + + { do until not enough input or output space for fast loop } + repeat { assume called with (m >= 258) and (n >= 10) } + { get literal/length code } + {GRABBITS(20);} { max bits for literal/length code } + while (k < 20) do + begin + Dec(n); + b := b or (uLong(p^) shl k); + Inc(p); + Inc(k, 8); + end; + + t := @(huft_ptr(tl)^[uInt(b) and ml]); + + e := t^.exop; + if (e = 0) then + begin + {DUMPBITS(t^.bits);} + b := b shr t^.bits; + Dec(k, t^.bits); + {$IFDEF DEBUG} + if (t^.base >= $20) and (t^.base < $7f) then + Tracevv('inflate: * literal '+AnsiChar(t^.base)) + else + Tracevv('inflate: * literal '+ IntToStr(t^.base)); + {$ENDIF} + q^ := Byte(t^.base); + Inc(q); + Dec(m); + continue; + end; + repeat + {DUMPBITS(t^.bits);} + b := b shr t^.bits; + Dec(k, t^.bits); + + if (e and 16 <> 0) then + begin + { get extra bits for length } + e := e and 15; + c := t^.base + (uInt(b) and inflate_mask[e]); + {DUMPBITS(e);} + b := b shr e; + Dec(k, e); + {$IFDEF DEBUG} + Tracevv('inflate: * length ' + IntToStr(c)); + {$ENDIF} + { decode distance base of block to copy } + {GRABBITS(15);} { max bits for distance code } + while (k < 15) do + begin + Dec(n); + b := b or (uLong(p^) shl k); + Inc(p); + Inc(k, 8); + end; + + t := @huft_ptr(td)^[uInt(b) and md]; + e := t^.exop; + repeat + {DUMPBITS(t^.bits);} + b := b shr t^.bits; + Dec(k, t^.bits); + + if (e and 16 <> 0) then + begin + { get extra bits to add to distance base } + e := e and 15; + {GRABBITS(e);} { get extra bits (up to 13) } + while (k < e) do + begin + Dec(n); + b := b or (uLong(p^) shl k); + Inc(p); + Inc(k, 8); + end; + + d := t^.base + (uInt(b) and inflate_mask[e]); + {DUMPBITS(e);} + b := b shr e; + Dec(k, e); + + {$IFDEF DEBUG} + Tracevv('inflate: * distance '+IntToStr(d)); + {$ENDIF} + { do the copy } + Dec(m, c); + if (uInt(ptr2int(q) - ptr2int(s.window)) >= d) then { offset before dest } + begin { just copy } + r := q; + Dec(r, d); + q^ := r^; Inc(q); Inc(r); Dec(c); { minimum count is three, } + q^ := r^; Inc(q); Inc(r); Dec(c); { so unroll loop a little } + end + else { else offset after destination } + begin + e := d - uInt(ptr2int(q) - ptr2int(s.window)); { bytes from offset to end } + r := s.zend; + Dec(r, e); { pointer to offset } + if (c > e) then { if source crosses, } + begin + Dec(c, e); { copy to end of window } + repeat + q^ := r^; + Inc(q); + Inc(r); + Dec(e); + until (e=0); + r := s.window; { copy rest from start of window } + end; + end; + repeat { copy all or what's left } + q^ := r^; + Inc(q); + Inc(r); + Dec(c); + until (c = 0); + break; + end + else + if (e and 64 = 0) then + begin + Inc(t, t^.base + (uInt(b) and inflate_mask[e])); + e := t^.exop; + end + else + begin + z.msg := 'invalid distance code'; + {UNGRAB} + c := z.avail_in-n; + if (k shr 3) < c then + c := k shr 3; + Inc(n, c); + Dec(p, c); + Dec(k, c shl 3); + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + + inflate_fast := Z_DATA_ERROR; + exit; + end; + until FALSE; + break; + end; + if (e and 64 = 0) then + begin + {t += t->base; + e = (t += ((uInt)b & inflate_mask[e]))->exop;} + + Inc(t, t^.base + (uInt(b) and inflate_mask[e])); + e := t^.exop; + if (e = 0) then + begin + {DUMPBITS(t^.bits);} + b := b shr t^.bits; + Dec(k, t^.bits); + + {$IFDEF DEBUG} + if (t^.base >= $20) and (t^.base < $7f) then + Tracevv('inflate: * literal '+AnsiChar(t^.base)) + else + Tracevv('inflate: * literal '+IntToStr(t^.base)); + {$ENDIF} + q^ := Byte(t^.base); + Inc(q); + Dec(m); + break; + end; + end + else + if (e and 32 <> 0) then + begin + {$IFDEF DEBUG} + Tracevv('inflate: * end of block'); + {$ENDIF} + {UNGRAB} + c := z.avail_in-n; + if (k shr 3) < c then + c := k shr 3; + Inc(n, c); + Dec(p, c); + Dec(k, c shl 3); + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_fast := Z_STREAM_END; + exit; + end + else + begin + z.msg := 'invalid literal/length code'; + {UNGRAB} + c := z.avail_in-n; + if (k shr 3) < c then + c := k shr 3; + Inc(n, c); + Dec(p, c); + Dec(k, c shl 3); + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_fast := Z_DATA_ERROR; + exit; + end; + until FALSE; + until (m < 258) or (n < 10); + + { not enough input or output--restore pointers and return } + {UNGRAB} + c := z.avail_in-n; + if (k shr 3) < c then + c := k shr 3; + Inc(n, c); + Dec(p, c); + Dec(k, c shl 3); + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, ptr2int(p)-ptr2int(z.next_in)); + z.next_in := p; + s.write := q; + inflate_fast := Z_OK; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/ZLib/iminftrees.pas b/resources/libraries/deskew/Imaging/ZLib/iminftrees.pas new file mode 100755 index 0000000..6949a63 --- /dev/null +++ b/resources/libraries/deskew/Imaging/ZLib/iminftrees.pas @@ -0,0 +1,781 @@ +Unit iminftrees; + +{ inftrees.h -- header to use inftrees.c + inftrees.c -- generate Huffman trees for efficient decoding + Copyright (C) 1995-1998 Mark Adler + + WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. + + Pascal tranlastion + Copyright (C) 1998 by Jacques Nomssi Nzali + For conditions of distribution and use, see copyright notice in readme.txt +} + +Interface + +{$I imzconf.inc} + +uses + imzutil, impaszlib; + + +{ Maximum size of dynamic tree. The maximum found in a long but non- + exhaustive search was 1004 huft structures (850 for length/literals + and 154 for distances, the latter actually the result of an + exhaustive search). The actual maximum is not known, but the + value below is more than safe. } +const + MANY = 1440; + + +{$ifdef DEBUG} +var + inflate_hufts : uInt; +{$endif} + +function inflate_trees_bits( + var c : array of uIntf; { 19 code lengths } + var bb : uIntf; { bits tree desired/actual depth } + var tb : pinflate_huft; { bits tree result } + var hp : array of Inflate_huft; { space for trees } + var z : z_stream { for messages } + ) : int; + +function inflate_trees_dynamic( + nl : uInt; { number of literal/length codes } + nd : uInt; { number of distance codes } + var c : Array of uIntf; { that many (total) code lengths } + var bl : uIntf; { literal desired/actual bit depth } + var bd : uIntf; { distance desired/actual bit depth } +var tl : pInflate_huft; { literal/length tree result } +var td : pInflate_huft; { distance tree result } +var hp : array of Inflate_huft; { space for trees } +var z : z_stream { for messages } + ) : int; + +function inflate_trees_fixed ( + var bl : uInt; { literal desired/actual bit depth } + var bd : uInt; { distance desired/actual bit depth } + var tl : pInflate_huft; { literal/length tree result } + var td : pInflate_huft; { distance tree result } + var z : z_stream { for memory allocation } + ) : int; + + +implementation + +const + inflate_copyright = 'inflate 1.1.2 Copyright 1995-1998 Mark Adler'; + +{ + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. +} + + +const +{ Tables for deflate from PKZIP's appnote.txt. } + cplens : Array [0..30] Of uInt { Copy lengths for literal codes 257..285 } + = (3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0); + { actually lengths - 2; also see note #13 above about 258 } + + invalid_code = 112; + + cplext : Array [0..30] Of uInt { Extra bits for literal codes 257..285 } + = (0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, invalid_code, invalid_code); + + cpdist : Array [0..29] Of uInt { Copy offsets for distance codes 0..29 } + = (1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577); + + cpdext : Array [0..29] Of uInt { Extra bits for distance codes } + = (0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13); + +{ Huffman code decoding is performed using a multi-level table lookup. + The fastest way to decode is to simply build a lookup table whose + size is determined by the longest code. However, the time it takes + to build this table can also be a factor if the data being decoded + is not very long. The most common codes are necessarily the + shortest codes, so those codes dominate the decoding time, and hence + the speed. The idea is you can have a shorter table that decodes the + shorter, more probable codes, and then point to subsidiary tables for + the longer codes. The time it costs to decode the longer codes is + then traded against the time it takes to make longer tables. + + This results of this trade are in the variables lbits and dbits + below. lbits is the number of bits the first level table for literal/ + length codes can decode in one step, and dbits is the same thing for + the distance codes. Subsequent tables are also less than or equal to + those sizes. These values may be adjusted either when all of the + codes are shorter than that, in which case the longest code length in + bits is used, or when the shortest code is *longer* than the requested + table size, in which case the length of the shortest code in bits is + used. + + There are two different values for the two tables, since they code a + different number of possibilities each. The literal/length table + codes 286 possible values, or in a flat code, a little over eight + bits. The distance table codes 30 possible values, or a little less + than five bits, flat. The optimum values for speed end up being + about one bit more than those, so lbits is 8+1 and dbits is 5+1. + The optimum values may differ though from machine to machine, and + possibly even between compilers. Your mileage may vary. } + + +{ If BMAX needs to be larger than 16, then h and x[] should be uLong. } +const + BMAX = 15; { maximum bit length of any code } + +{$DEFINE USE_PTR} + +function huft_build( +var b : array of uIntf; { code lengths in bits (all assumed <= BMAX) } + n : uInt; { number of codes (assumed <= N_MAX) } + s : uInt; { number of simple-valued codes (0..s-1) } +const d : array of uIntf; { list of base values for non-simple codes } +{ array of word } +const e : array of uIntf; { list of extra bits for non-simple codes } +{ array of byte } + t : ppInflate_huft; { result: starting table } +var m : uIntf; { maximum lookup bits, returns actual } +var hp : array of inflate_huft; { space for trees } +var hn : uInt; { hufts used in space } +var v : array of uIntf { working area: values in order of bit length } + ) : int; +{ Given a list of code lengths and a maximum table size, make a set of + tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + if the given code set is incomplete (the tables are still built in this + case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + lengths), or Z_MEM_ERROR if not enough memory. } +Var + a : uInt; { counter for codes of length k } + c : Array [0..BMAX] Of uInt; { bit length count table } + f : uInt; { i repeats in table every f entries } + g : int; { maximum code length } + h : int; { table level } + i : uInt; {register} { counter, current code } + j : uInt; {register} { counter } + k : Int; {register} { number of bits in current code } + l : int; { bits per table (returned in m) } + mask : uInt; { (1 shl w) - 1, to avoid cc -O bug on HP } + p : ^uIntf; {register} { pointer into c[], b[], or v[] } + q : pInflate_huft; { points to current table } + r : inflate_huft; { table entry for structure assignment } + u : Array [0..BMAX-1] Of pInflate_huft; { table stack } + w : int; {register} { bits before this table = (l*h) } + x : Array [0..BMAX] Of uInt; { bit offsets, then code stack } + {$IFDEF USE_PTR} + xp : puIntf; { pointer into x } + {$ELSE} + xp : uInt; + {$ENDIF} + y : int; { number of dummy codes added } + z : uInt; { number of entries in current table } +Begin + { Generate counts for each bit length } + FillChar(c,SizeOf(c),0) ; { clear c[] } + + for i := 0 to n-1 do + Inc (c[b[i]]); { assume all entries <= BMAX } + + If (c[0] = n) Then { null input--all zero length codes } + Begin + t^ := pInflate_huft(NIL); + m := 0 ; + huft_build := Z_OK ; + Exit; + End ; + + { Find minimum and maximum length, bound [m] by those } + l := m; + for j:=1 To BMAX do + if (c[j] <> 0) then + break; + k := j ; { minimum code length } + if (uInt(l) < j) then + l := j; + for i := BMAX downto 1 do + if (c[i] <> 0) then + break ; + g := i ; { maximum code length } + if (uInt(l) > i) then + l := i; + m := l; + + { Adjust last length count to fill out codes, if needed } + y := 1 shl j ; + while (j < i) do + begin + Dec(y, c[j]) ; + if (y < 0) then + begin + huft_build := Z_DATA_ERROR; { bad input: more codes than bits } + exit; + end ; + Inc(j) ; + y := y shl 1 + end; + Dec (y, c[i]) ; + if (y < 0) then + begin + huft_build := Z_DATA_ERROR; { bad input: more codes than bits } + exit; + end; + Inc(c[i], y); + + { Generate starting offsets into the value table FOR each length } + {$IFDEF USE_PTR} + x[1] := 0; + j := 0; + + p := @c[1]; + xp := @x[2]; + + dec(i); { note that i = g from above } + WHILE (i > 0) DO + BEGIN + inc(j, p^); + xp^ := j; + inc(p); + inc(xp); + dec(i); + END; + {$ELSE} + x[1] := 0; + j := 0 ; + for i := 1 to g do + begin + x[i] := j; + Inc(j, c[i]); + end; + {$ENDIF} + + { Make a table of values in order of bit lengths } + for i := 0 to n-1 do + begin + j := b[i]; + if (j <> 0) then + begin + v[ x[j] ] := i; + Inc(x[j]); + end; + end; + n := x[g]; { set n to length of v } + + { Generate the Huffman codes and for each, make the table entries } + i := 0 ; + x[0] := 0 ; { first Huffman code is zero } + p := Addr(v) ; { grab values in bit order } + h := -1 ; { no tables yet--level -1 } + w := -l ; { bits decoded = (l*h) } + + u[0] := pInflate_huft(NIL); { just to keep compilers happy } + q := pInflate_huft(NIL); { ditto } + z := 0 ; { ditto } + + { go through the bit lengths (k already is bits in shortest code) } + while (k <= g) Do + begin + a := c[k] ; + while (a<>0) Do + begin + Dec (a) ; + { here i is the Huffman code of length k bits for value p^ } + { make tables up to required level } + while (k > w + l) do + begin + + Inc (h) ; + Inc (w, l); { add bits already decoded } + { previous table always l bits } + { compute minimum size table less than or equal to l bits } + + { table size upper limit } + z := g - w; + If (z > uInt(l)) Then + z := l; + + { try a k-w bit table } + j := k - w; + f := 1 shl j; + if (f > a+1) Then { too few codes for k-w bit table } + begin + Dec(f, a+1); { deduct codes from patterns left } + {$IFDEF USE_PTR} + xp := Addr(c[k]); + + if (j < z) then + begin + Inc(j); + while (j < z) do + begin { try smaller tables up to z bits } + f := f shl 1; + Inc (xp) ; + If (f <= xp^) Then + break; { enough codes to use up j bits } + Dec(f, xp^); { else deduct codes from patterns } + Inc(j); + end; + end; + {$ELSE} + xp := k; + + if (j < z) then + begin + Inc (j) ; + While (j < z) Do + begin { try smaller tables up to z bits } + f := f * 2; + Inc (xp) ; + if (f <= c[xp]) then + Break ; { enough codes to use up j bits } + Dec (f, c[xp]) ; { else deduct codes from patterns } + Inc (j); + end; + end; + {$ENDIF} + end; + + z := 1 shl j; { table entries for j-bit table } + + { allocate new table } + if (hn + z > MANY) then { (note: doesn't matter for fixed) } + begin + huft_build := Z_MEM_ERROR; { not enough memory } + exit; + end; + + q := @hp[hn]; + u[h] := q; + Inc(hn, z); + + { connect to last table, if there is one } + if (h <> 0) then + begin + x[h] := i; { save pattern for backing up } + r.bits := Byte(l); { bits to dump before this table } + r.exop := Byte(j); { bits in this table } + j := i shr (w - l); + {r.base := uInt( q - u[h-1] -j);} { offset to this table } + r.base := (ptr2int(q) - ptr2int(u[h-1]) ) div sizeof(q^) - j; + huft_Ptr(u[h-1])^[j] := r; { connect to last table } + end + else + t^ := q; { first table is returned result } + end; + + { set up table entry in r } + r.bits := Byte(k - w); + + { C-code: if (p >= v + n) - see ZUTIL.PAS for comments } + + if ptr2int(p)>=ptr2int(@(v[n])) then { also works under DPMI ?? } + r.exop := 128 + 64 { out of values--invalid code } + else + if (p^ < s) then + begin + if (p^ < 256) then { 256 is end-of-block code } + r.exop := 0 + Else + r.exop := 32 + 64; { EOB_code; } + r.base := p^; { simple code is just the value } + Inc(p); + end + Else + begin + r.exop := Byte(e[p^-s] + 16 + 64); { non-simple--look up in lists } + r.base := d[p^-s]; + Inc (p); + end ; + + { fill code-like entries with r } + f := 1 shl (k - w); + j := i shr w; + while (j < z) do + begin + huft_Ptr(q)^[j] := r; + Inc(j, f); + end; + + { backwards increment the k-bit code i } + j := 1 shl (k-1) ; + while (i and j) <> 0 do + begin + i := i xor j; { bitwise exclusive or } + j := j shr 1 + end ; + i := i xor j; + + { backup over finished tables } + mask := (1 shl w) - 1; { needed on HP, cc -O bug } + while ((i and mask) <> x[h]) do + begin + Dec(h); { don't need to update q } + Dec(w, l); + mask := (1 shl w) - 1; + end; + + end; + + Inc(k); + end; + + { Return Z_BUF_ERROR if we were given an incomplete table } + if (y <> 0) And (g <> 1) then + huft_build := Z_BUF_ERROR + else + huft_build := Z_OK; +end; { huft_build} + + +function inflate_trees_bits( + var c : array of uIntf; { 19 code lengths } + var bb : uIntf; { bits tree desired/actual depth } + var tb : pinflate_huft; { bits tree result } + var hp : array of Inflate_huft; { space for trees } + var z : z_stream { for messages } + ) : int; +var + r : int; + hn : uInt; { hufts used in space } + v : PuIntArray; { work area for huft_build } +begin + hn := 0; + v := PuIntArray( ZALLOC(z, 19, sizeof(uInt)) ); + if (v = Z_NULL) then + begin + inflate_trees_bits := Z_MEM_ERROR; + exit; + end; + + r := huft_build(c, 19, 19, cplens, cplext, + {puIntf(Z_NULL), puIntf(Z_NULL),} + @tb, bb, hp, hn, v^); + if (r = Z_DATA_ERROR) then + z.msg := 'oversubscribed dynamic bit lengths tree' + else + if (r = Z_BUF_ERROR) or (bb = 0) then + begin + z.msg := 'incomplete dynamic bit lengths tree'; + r := Z_DATA_ERROR; + end; + ZFREE(z, v); + inflate_trees_bits := r; +end; + + +function inflate_trees_dynamic( + nl : uInt; { number of literal/length codes } + nd : uInt; { number of distance codes } + var c : Array of uIntf; { that many (total) code lengths } + var bl : uIntf; { literal desired/actual bit depth } + var bd : uIntf; { distance desired/actual bit depth } +var tl : pInflate_huft; { literal/length tree result } +var td : pInflate_huft; { distance tree result } +var hp : array of Inflate_huft; { space for trees } +var z : z_stream { for messages } + ) : int; +var + r : int; + hn : uInt; { hufts used in space } + v : PuIntArray; { work area for huft_build } +begin + hn := 0; + { allocate work area } + v := PuIntArray( ZALLOC(z, 288, sizeof(uInt)) ); + if (v = Z_NULL) then + begin + inflate_trees_dynamic := Z_MEM_ERROR; + exit; + end; + + { build literal/length tree } + r := huft_build(c, nl, 257, cplens, cplext, @tl, bl, hp, hn, v^); + if (r <> Z_OK) or (bl = 0) then + begin + if (r = Z_DATA_ERROR) then + z.msg := 'oversubscribed literal/length tree' + else + if (r <> Z_MEM_ERROR) then + begin + z.msg := 'incomplete literal/length tree'; + r := Z_DATA_ERROR; + end; + + ZFREE(z, v); + inflate_trees_dynamic := r; + exit; + end; + + { build distance tree } + r := huft_build(puIntArray(@c[nl])^, nd, 0, + cpdist, cpdext, @td, bd, hp, hn, v^); + if (r <> Z_OK) or ((bd = 0) and (nl > 257)) then + begin + if (r = Z_DATA_ERROR) then + z.msg := 'oversubscribed literal/length tree' + else + if (r = Z_BUF_ERROR) then + begin +{$ifdef PKZIP_BUG_WORKAROUND} + r := Z_OK; + end; +{$else} + z.msg := 'incomplete literal/length tree'; + r := Z_DATA_ERROR; + end + else + if (r <> Z_MEM_ERROR) then + begin + z.msg := 'empty distance tree with lengths'; + r := Z_DATA_ERROR; + end; + ZFREE(z, v); + inflate_trees_dynamic := r; + exit; +{$endif} + end; + + { done } + ZFREE(z, v); + inflate_trees_dynamic := Z_OK; +end; + +{$UNDEF BUILDFIXED} + +{ build fixed tables only once--keep them here } +{$IFNDEF BUILDFIXED} +{ locals } +var + fixed_built : Boolean = false; +const + FIXEDH = 544; { number of hufts used by fixed tables } +var + fixed_mem : array[0..FIXEDH-1] of inflate_huft; + fixed_bl : uInt; + fixed_bd : uInt; + fixed_tl : pInflate_huft; + fixed_td : pInflate_huft; + +{$ELSE} + +{ inffixed.h -- table for decoding fixed codes } + +{local} +const + fixed_bl = uInt(9); +{local} +const + fixed_bd = uInt(5); +{local} +const + fixed_tl : array [0..288-1] of inflate_huft = ( + Exop, { number of extra bits or operation } + bits : Byte; { number of bits in this code or subcode } + {pad : uInt;} { pad structure to a power of 2 (4 bytes for } + { 16-bit, 8 bytes for 32-bit int's) } + base : uInt; { literal, length base, or distance base } + { or table offset } + + ((96,7),256), ((0,8),80), ((0,8),16), ((84,8),115), ((82,7),31), + ((0,8),112), ((0,8),48), ((0,9),192), ((80,7),10), ((0,8),96), + ((0,8),32), ((0,9),160), ((0,8),0), ((0,8),128), ((0,8),64), + ((0,9),224), ((80,7),6), ((0,8),88), ((0,8),24), ((0,9),144), + ((83,7),59), ((0,8),120), ((0,8),56), ((0,9),208), ((81,7),17), + ((0,8),104), ((0,8),40), ((0,9),176), ((0,8),8), ((0,8),136), + ((0,8),72), ((0,9),240), ((80,7),4), ((0,8),84), ((0,8),20), + ((85,8),227), ((83,7),43), ((0,8),116), ((0,8),52), ((0,9),200), + ((81,7),13), ((0,8),100), ((0,8),36), ((0,9),168), ((0,8),4), + ((0,8),132), ((0,8),68), ((0,9),232), ((80,7),8), ((0,8),92), + ((0,8),28), ((0,9),152), ((84,7),83), ((0,8),124), ((0,8),60), + ((0,9),216), ((82,7),23), ((0,8),108), ((0,8),44), ((0,9),184), + ((0,8),12), ((0,8),140), ((0,8),76), ((0,9),248), ((80,7),3), + ((0,8),82), ((0,8),18), ((85,8),163), ((83,7),35), ((0,8),114), + ((0,8),50), ((0,9),196), ((81,7),11), ((0,8),98), ((0,8),34), + ((0,9),164), ((0,8),2), ((0,8),130), ((0,8),66), ((0,9),228), + ((80,7),7), ((0,8),90), ((0,8),26), ((0,9),148), ((84,7),67), + ((0,8),122), ((0,8),58), ((0,9),212), ((82,7),19), ((0,8),106), + ((0,8),42), ((0,9),180), ((0,8),10), ((0,8),138), ((0,8),74), + ((0,9),244), ((80,7),5), ((0,8),86), ((0,8),22), ((192,8),0), + ((83,7),51), ((0,8),118), ((0,8),54), ((0,9),204), ((81,7),15), + ((0,8),102), ((0,8),38), ((0,9),172), ((0,8),6), ((0,8),134), + ((0,8),70), ((0,9),236), ((80,7),9), ((0,8),94), ((0,8),30), + ((0,9),156), ((84,7),99), ((0,8),126), ((0,8),62), ((0,9),220), + ((82,7),27), ((0,8),110), ((0,8),46), ((0,9),188), ((0,8),14), + ((0,8),142), ((0,8),78), ((0,9),252), ((96,7),256), ((0,8),81), + ((0,8),17), ((85,8),131), ((82,7),31), ((0,8),113), ((0,8),49), + ((0,9),194), ((80,7),10), ((0,8),97), ((0,8),33), ((0,9),162), + ((0,8),1), ((0,8),129), ((0,8),65), ((0,9),226), ((80,7),6), + ((0,8),89), ((0,8),25), ((0,9),146), ((83,7),59), ((0,8),121), + ((0,8),57), ((0,9),210), ((81,7),17), ((0,8),105), ((0,8),41), + ((0,9),178), ((0,8),9), ((0,8),137), ((0,8),73), ((0,9),242), + ((80,7),4), ((0,8),85), ((0,8),21), ((80,8),258), ((83,7),43), + ((0,8),117), ((0,8),53), ((0,9),202), ((81,7),13), ((0,8),101), + ((0,8),37), ((0,9),170), ((0,8),5), ((0,8),133), ((0,8),69), + ((0,9),234), ((80,7),8), ((0,8),93), ((0,8),29), ((0,9),154), + ((84,7),83), ((0,8),125), ((0,8),61), ((0,9),218), ((82,7),23), + ((0,8),109), ((0,8),45), ((0,9),186), ((0,8),13), ((0,8),141), + ((0,8),77), ((0,9),250), ((80,7),3), ((0,8),83), ((0,8),19), + ((85,8),195), ((83,7),35), ((0,8),115), ((0,8),51), ((0,9),198), + ((81,7),11), ((0,8),99), ((0,8),35), ((0,9),166), ((0,8),3), + ((0,8),131), ((0,8),67), ((0,9),230), ((80,7),7), ((0,8),91), + ((0,8),27), ((0,9),150), ((84,7),67), ((0,8),123), ((0,8),59), + ((0,9),214), ((82,7),19), ((0,8),107), ((0,8),43), ((0,9),182), + ((0,8),11), ((0,8),139), ((0,8),75), ((0,9),246), ((80,7),5), + ((0,8),87), ((0,8),23), ((192,8),0), ((83,7),51), ((0,8),119), + ((0,8),55), ((0,9),206), ((81,7),15), ((0,8),103), ((0,8),39), + ((0,9),174), ((0,8),7), ((0,8),135), ((0,8),71), ((0,9),238), + ((80,7),9), ((0,8),95), ((0,8),31), ((0,9),158), ((84,7),99), + ((0,8),127), ((0,8),63), ((0,9),222), ((82,7),27), ((0,8),111), + ((0,8),47), ((0,9),190), ((0,8),15), ((0,8),143), ((0,8),79), + ((0,9),254), ((96,7),256), ((0,8),80), ((0,8),16), ((84,8),115), + ((82,7),31), ((0,8),112), ((0,8),48), ((0,9),193), ((80,7),10), + ((0,8),96), ((0,8),32), ((0,9),161), ((0,8),0), ((0,8),128), + ((0,8),64), ((0,9),225), ((80,7),6), ((0,8),88), ((0,8),24), + ((0,9),145), ((83,7),59), ((0,8),120), ((0,8),56), ((0,9),209), + ((81,7),17), ((0,8),104), ((0,8),40), ((0,9),177), ((0,8),8), + ((0,8),136), ((0,8),72), ((0,9),241), ((80,7),4), ((0,8),84), + ((0,8),20), ((85,8),227), ((83,7),43), ((0,8),116), ((0,8),52), + ((0,9),201), ((81,7),13), ((0,8),100), ((0,8),36), ((0,9),169), + ((0,8),4), ((0,8),132), ((0,8),68), ((0,9),233), ((80,7),8), + ((0,8),92), ((0,8),28), ((0,9),153), ((84,7),83), ((0,8),124), + ((0,8),60), ((0,9),217), ((82,7),23), ((0,8),108), ((0,8),44), + ((0,9),185), ((0,8),12), ((0,8),140), ((0,8),76), ((0,9),249), + ((80,7),3), ((0,8),82), ((0,8),18), ((85,8),163), ((83,7),35), + ((0,8),114), ((0,8),50), ((0,9),197), ((81,7),11), ((0,8),98), + ((0,8),34), ((0,9),165), ((0,8),2), ((0,8),130), ((0,8),66), + ((0,9),229), ((80,7),7), ((0,8),90), ((0,8),26), ((0,9),149), + ((84,7),67), ((0,8),122), ((0,8),58), ((0,9),213), ((82,7),19), + ((0,8),106), ((0,8),42), ((0,9),181), ((0,8),10), ((0,8),138), + ((0,8),74), ((0,9),245), ((80,7),5), ((0,8),86), ((0,8),22), + ((192,8),0), ((83,7),51), ((0,8),118), ((0,8),54), ((0,9),205), + ((81,7),15), ((0,8),102), ((0,8),38), ((0,9),173), ((0,8),6), + ((0,8),134), ((0,8),70), ((0,9),237), ((80,7),9), ((0,8),94), + ((0,8),30), ((0,9),157), ((84,7),99), ((0,8),126), ((0,8),62), + ((0,9),221), ((82,7),27), ((0,8),110), ((0,8),46), ((0,9),189), + ((0,8),14), ((0,8),142), ((0,8),78), ((0,9),253), ((96,7),256), + ((0,8),81), ((0,8),17), ((85,8),131), ((82,7),31), ((0,8),113), + ((0,8),49), ((0,9),195), ((80,7),10), ((0,8),97), ((0,8),33), + ((0,9),163), ((0,8),1), ((0,8),129), ((0,8),65), ((0,9),227), + ((80,7),6), ((0,8),89), ((0,8),25), ((0,9),147), ((83,7),59), + ((0,8),121), ((0,8),57), ((0,9),211), ((81,7),17), ((0,8),105), + ((0,8),41), ((0,9),179), ((0,8),9), ((0,8),137), ((0,8),73), + ((0,9),243), ((80,7),4), ((0,8),85), ((0,8),21), ((80,8),258), + ((83,7),43), ((0,8),117), ((0,8),53), ((0,9),203), ((81,7),13), + ((0,8),101), ((0,8),37), ((0,9),171), ((0,8),5), ((0,8),133), + ((0,8),69), ((0,9),235), ((80,7),8), ((0,8),93), ((0,8),29), + ((0,9),155), ((84,7),83), ((0,8),125), ((0,8),61), ((0,9),219), + ((82,7),23), ((0,8),109), ((0,8),45), ((0,9),187), ((0,8),13), + ((0,8),141), ((0,8),77), ((0,9),251), ((80,7),3), ((0,8),83), + ((0,8),19), ((85,8),195), ((83,7),35), ((0,8),115), ((0,8),51), + ((0,9),199), ((81,7),11), ((0,8),99), ((0,8),35), ((0,9),167), + ((0,8),3), ((0,8),131), ((0,8),67), ((0,9),231), ((80,7),7), + ((0,8),91), ((0,8),27), ((0,9),151), ((84,7),67), ((0,8),123), + ((0,8),59), ((0,9),215), ((82,7),19), ((0,8),107), ((0,8),43), + ((0,9),183), ((0,8),11), ((0,8),139), ((0,8),75), ((0,9),247), + ((80,7),5), ((0,8),87), ((0,8),23), ((192,8),0), ((83,7),51), + ((0,8),119), ((0,8),55), ((0,9),207), ((81,7),15), ((0,8),103), + ((0,8),39), ((0,9),175), ((0,8),7), ((0,8),135), ((0,8),71), + ((0,9),239), ((80,7),9), ((0,8),95), ((0,8),31), ((0,9),159), + ((84,7),99), ((0,8),127), ((0,8),63), ((0,9),223), ((82,7),27), + ((0,8),111), ((0,8),47), ((0,9),191), ((0,8),15), ((0,8),143), + ((0,8),79), ((0,9),255) + ); + +{local} +const + fixed_td : array[0..32-1] of inflate_huft = ( +(Exop:80;bits:5;base:1), (Exop:87;bits:5;base:257), (Exop:83;bits:5;base:17), +(Exop:91;bits:5;base:4097), (Exop:81;bits:5;base), (Exop:89;bits:5;base:1025), +(Exop:85;bits:5;base:65), (Exop:93;bits:5;base:16385), (Exop:80;bits:5;base:3), +(Exop:88;bits:5;base:513), (Exop:84;bits:5;base:33), (Exop:92;bits:5;base:8193), +(Exop:82;bits:5;base:9), (Exop:90;bits:5;base:2049), (Exop:86;bits:5;base:129), +(Exop:192;bits:5;base:24577), (Exop:80;bits:5;base:2), (Exop:87;bits:5;base:385), +(Exop:83;bits:5;base:25), (Exop:91;bits:5;base:6145), (Exop:81;bits:5;base:7), +(Exop:89;bits:5;base:1537), (Exop:85;bits:5;base:97), (Exop:93;bits:5;base:24577), +(Exop:80;bits:5;base:4), (Exop:88;bits:5;base:769), (Exop:84;bits:5;base:49), +(Exop:92;bits:5;base:12289), (Exop:82;bits:5;base:13), (Exop:90;bits:5;base:3073), +(Exop:86;bits:5;base:193), (Exop:192;bits:5;base:24577) + ); +{$ENDIF} + +function inflate_trees_fixed( +var bl : uInt; { literal desired/actual bit depth } +var bd : uInt; { distance desired/actual bit depth } +var tl : pInflate_huft; { literal/length tree result } +var td : pInflate_huft; { distance tree result } +var z : z_stream { for memory allocation } + ) : int; +type + pFixed_table = ^fixed_table; + fixed_table = array[0..288-1] of uIntf; +var + k : int; { temporary variable } + c : pFixed_table; { length list for huft_build } + v : PuIntArray; { work area for huft_build } +var + f : uInt; { number of hufts used in fixed_mem } +begin + { build fixed tables if not already (multiple overlapped executions ok) } + if not fixed_built then + begin + f := 0; + + { allocate memory } + c := pFixed_table( ZALLOC(z, 288, sizeof(uInt)) ); + if (c = Z_NULL) then + begin + inflate_trees_fixed := Z_MEM_ERROR; + exit; + end; + v := PuIntArray( ZALLOC(z, 288, sizeof(uInt)) ); + if (v = Z_NULL) then + begin + ZFREE(z, c); + inflate_trees_fixed := Z_MEM_ERROR; + exit; + end; + + { literal table } + for k := 0 to Pred(144) do + c^[k] := 8; + for k := 144 to Pred(256) do + c^[k] := 9; + for k := 256 to Pred(280) do + c^[k] := 7; + for k := 280 to Pred(288) do + c^[k] := 8; + fixed_bl := 9; + huft_build(c^, 288, 257, cplens, cplext, @fixed_tl, fixed_bl, + fixed_mem, f, v^); + + { distance table } + for k := 0 to Pred(30) do + c^[k] := 5; + fixed_bd := 5; + huft_build(c^, 30, 0, cpdist, cpdext, @fixed_td, fixed_bd, + fixed_mem, f, v^); + + { done } + ZFREE(z, v); + ZFREE(z, c); + fixed_built := True; + end; + bl := fixed_bl; + bd := fixed_bd; + tl := fixed_tl; + td := fixed_td; + inflate_trees_fixed := Z_OK; +end; { inflate_trees_fixed } + + +end. \ No newline at end of file diff --git a/resources/libraries/deskew/Imaging/ZLib/iminfutil.pas b/resources/libraries/deskew/Imaging/ZLib/iminfutil.pas new file mode 100755 index 0000000..384f0d3 --- /dev/null +++ b/resources/libraries/deskew/Imaging/ZLib/iminfutil.pas @@ -0,0 +1,222 @@ +Unit iminfutil; + +{ types and macros common to blocks and codes + Copyright (C) 1995-1998 Mark Adler + + WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. + + Pascal tranlastion + Copyright (C) 1998 by Jacques Nomssi Nzali + For conditions of distribution and use, see copyright notice in readme.txt +} + +interface + +{$I imzconf.inc} + +uses + imzutil, impaszlib; + +{ copy as much as possible from the sliding window to the output area } +function inflate_flush(var s : inflate_blocks_state; + var z : z_stream; + r : int) : int; + +{ And'ing with mask[n] masks the lower n bits } +const + inflate_mask : array[0..17-1] of uInt = ( + $0000, + $0001, $0003, $0007, $000f, $001f, $003f, $007f, $00ff, + $01ff, $03ff, $07ff, $0fff, $1fff, $3fff, $7fff, $ffff); + +{procedure GRABBITS(j : int);} +{procedure DUMPBITS(j : int);} +{procedure NEEDBITS(j : int);} + +implementation + +{ macros for bit input with no checking and for returning unused bytes } +procedure GRABBITS(j : int); +begin + {while (k < j) do + begin + Dec(z^.avail_in); + Inc(z^.total_in); + b := b or (uLong(z^.next_in^) shl k); + Inc(z^.next_in); + Inc(k, 8); + end;} +end; + +procedure DUMPBITS(j : int); +begin + {b := b shr j; + Dec(k, j);} +end; + +procedure NEEDBITS(j : int); +begin + (* + while (k < j) do + begin + {NEEDBYTE;} + if (n <> 0) then + r :=Z_OK + else + begin + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, LongInt(p)-LongInt(z.next_in)); + z.next_in := p; + s.write := q; + result := inflate_flush(s,z,r); + exit; + end; + Dec(n); + b := b or (uLong(p^) shl k); + Inc(p); + Inc(k, 8); + end; + *) +end; + +procedure NEEDOUT; +begin + (* + if (m = 0) then + begin + {WRAP} + if (q = s.zend) and (s.read <> s.window) then + begin + q := s.window; + if LongInt(q) < LongInt(s.read) then + m := uInt(LongInt(s.read)-LongInt(q)-1) + else + m := uInt(LongInt(s.zend)-LongInt(q)); + end; + + if (m = 0) then + begin + {FLUSH} + s.write := q; + r := inflate_flush(s,z,r); + q := s.write; + if LongInt(q) < LongInt(s.read) then + m := uInt(LongInt(s.read)-LongInt(q)-1) + else + m := uInt(LongInt(s.zend)-LongInt(q)); + + {WRAP} + if (q = s.zend) and (s.read <> s.window) then + begin + q := s.window; + if LongInt(q) < LongInt(s.read) then + m := uInt(LongInt(s.read)-LongInt(q)-1) + else + m := uInt(LongInt(s.zend)-LongInt(q)); + end; + + if (m = 0) then + begin + {UPDATE} + s.bitb := b; + s.bitk := k; + z.avail_in := n; + Inc(z.total_in, LongInt(p)-LongInt(z.next_in)); + z.next_in := p; + s.write := q; + result := inflate_flush(s,z,r); + exit; + end; + end; + end; + r := Z_OK; + *) +end; + +{ copy as much as possible from the sliding window to the output area } +function inflate_flush(var s : inflate_blocks_state; + var z : z_stream; + r : int) : int; +var + n : uInt; + p : pBytef; + q : pBytef; +begin + { local copies of source and destination pointers } + p := z.next_out; + q := s.read; + + { compute number of bytes to copy as far as end of window } + if ptr2int(q) <= ptr2int(s.write) then + n := uInt(ptr2int(s.write) - ptr2int(q)) + else + n := uInt(ptr2int(s.zend) - ptr2int(q)); + if (n > z.avail_out) then + n := z.avail_out; + if (n <> 0) and (r = Z_BUF_ERROR) then + r := Z_OK; + + { update counters } + Dec(z.avail_out, n); + Inc(z.total_out, n); + + + { update check information } + if Assigned(s.checkfn) then + begin + s.check := s.checkfn(s.check, q, n); + z.adler := s.check; + end; + + { copy as far as end of window } + zmemcpy(p, q, n); + Inc(p, n); + Inc(q, n); + + { see if more to copy at beginning of window } + if (q = s.zend) then + begin + { wrap pointers } + q := s.window; + if (s.write = s.zend) then + s.write := s.window; + + { compute bytes to copy } + n := uInt(ptr2int(s.write) - ptr2int(q)); + if (n > z.avail_out) then + n := z.avail_out; + if (n <> 0) and (r = Z_BUF_ERROR) then + r := Z_OK; + + { update counters } + Dec( z.avail_out, n); + Inc( z.total_out, n); + + { update check information } + if Assigned(s.checkfn) then + begin + s.check := s.checkfn(s.check, q, n); + z.adler := s.check; + end; + + { copy } + zmemcpy(p, q, n); + Inc(p, n); + Inc(q, n); + end; + + + { update pointers } + z.next_out := p; + s.read := q; + + { done } + inflate_flush := r; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/ZLib/impaszlib.pas b/resources/libraries/deskew/Imaging/ZLib/impaszlib.pas new file mode 100755 index 0000000..555634c --- /dev/null +++ b/resources/libraries/deskew/Imaging/ZLib/impaszlib.pas @@ -0,0 +1,520 @@ +Unit impaszlib; + + +{ Original: + zlib.h -- interface of the 'zlib' general purpose compression library + version 1.1.0, Feb 24th, 1998 + + Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). + + + Pascal tranlastion + Copyright (C) 1998 by Jacques Nomssi Nzali + For conditions of distribution and use, see copyright notice in readme.txt +} + +interface + +{$I imzconf.inc} + +uses + imzutil; + +{ zconf.h -- configuration of the zlib compression library } +{ zutil.c -- target dependent utility functions for the compression library } + +{ The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. } + + + +{ Compile with -DMAXSEG_64K if the alloc function cannot allocate more + than 64k bytes at a time (needed on systems with 16-bit int). } + +{ Maximum value for memLevel in deflateInit2 } +const + MAX_MEM_LEVEL = 9; + DEF_MEM_LEVEL = 8; { if MAX_MEM_LEVEL > 8 } + +{ Maximum value for windowBits in deflateInit2 and inflateInit2 } +const + MAX_WBITS = 15; { 32K LZ77 window } + +{ default windowBits for decompression. MAX_WBITS is for compression only } +const + DEF_WBITS = MAX_WBITS; + +{ The memory requirements for deflate are (in bytes): + 1 shl (windowBits+2) + 1 shl (memLevel+9) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + DMAX_WBITS=14 DMAX_MEM_LEVEL=7 + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 shl windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. } + + +{ Huffman code lookup table entry--this entry is four bytes for machines + that have 16-bit pointers (e.g. PC's in the small or medium model). } + +type + pInflate_huft = ^inflate_huft; + inflate_huft = Record + Exop, { number of extra bits or operation } + bits : Byte; { number of bits in this code or subcode } + {pad : uInt;} { pad structure to a power of 2 (4 bytes for } + { 16-bit, 8 bytes for 32-bit int's) } + base : uInt; { literal, length base, or distance base } + { or table offset } + End; + +type + huft_field = Array[0..(MaxInt div SizeOf(inflate_huft))-1] of inflate_huft; + huft_ptr = ^huft_field; +type + ppInflate_huft = ^pInflate_huft; + +type + inflate_codes_mode = ( { waiting for "i:"=input, "o:"=output, "x:"=nothing } + START, { x: set up for LEN } + LEN, { i: get length/literal/eob next } + LENEXT, { i: getting length extra (have base) } + DIST, { i: get distance next } + DISTEXT, { i: getting distance extra } + COPY, { o: copying bytes in window, waiting for space } + LIT, { o: got literal, waiting for output space } + WASH, { o: got eob, possibly still output waiting } + ZEND, { x: got eob and all data flushed } + BADCODE); { x: got error } + +{ inflate codes private state } +type + pInflate_codes_state = ^inflate_codes_state; + inflate_codes_state = record + + mode : inflate_codes_mode; { current inflate_codes mode } + + { mode dependent information } + len : uInt; + sub : record { submode } + Case Byte of + 0:(code : record { if LEN or DIST, where in tree } + tree : pInflate_huft; { pointer into tree } + need : uInt; { bits needed } + end); + 1:(lit : uInt); { if LIT, literal } + 2:(copy: record { if EXT or COPY, where and how much } + get : uInt; { bits to get for extra } + dist : uInt; { distance back to copy from } + end); + end; + + { mode independent information } + lbits : Byte; { ltree bits decoded per branch } + dbits : Byte; { dtree bits decoder per branch } + ltree : pInflate_huft; { literal/length/eob tree } + dtree : pInflate_huft; { distance tree } + end; + +type + check_func = function(check : uLong; + buf : pBytef; + {const buf : array of byte;} + len : uInt) : uLong; +type + inflate_block_mode = + (ZTYPE, { get type bits (3, including end bit) } + LENS, { get lengths for stored } + STORED, { processing stored block } + TABLE, { get table lengths } + BTREE, { get bit lengths tree for a dynamic block } + DTREE, { get length, distance trees for a dynamic block } + CODES, { processing fixed or dynamic block } + DRY, { output remaining window bytes } + BLKDONE, { finished last block, done } + BLKBAD); { got a data error--stuck here } + +type + pInflate_blocks_state = ^inflate_blocks_state; + +{ inflate blocks semi-private state } + inflate_blocks_state = record + + mode : inflate_block_mode; { current inflate_block mode } + + { mode dependent information } + sub : record { submode } + case Byte of + 0:(left : uInt); { if STORED, bytes left to copy } + 1:(trees : record { if DTREE, decoding info for trees } + table : uInt; { table lengths (14 bits) } + index : uInt; { index into blens (or border) } + blens : PuIntArray; { bit lengths of codes } + bb : uInt; { bit length tree depth } + tb : pInflate_huft; { bit length decoding tree } + end); + 2:(decode : record { if CODES, current state } + tl : pInflate_huft; + td : pInflate_huft; { trees to free } + codes : pInflate_codes_state; + end); + end; + last : boolean; { true if this block is the last block } + + { mode independent information } + bitk : uInt; { bits in bit buffer } + bitb : uLong; { bit buffer } + hufts : huft_ptr; {pInflate_huft;} { single malloc for tree space } + window : pBytef; { sliding window } + zend : pBytef; { one byte after sliding window } + read : pBytef; { window read pointer } + write : pBytef; { window write pointer } + checkfn : check_func; { check function } + check : uLong; { check on output } + end; + +type + inflate_mode = ( + METHOD, { waiting for method byte } + FLAG, { waiting for flag byte } + DICT4, { four dictionary check bytes to go } + DICT3, { three dictionary check bytes to go } + DICT2, { two dictionary check bytes to go } + DICT1, { one dictionary check byte to go } + DICT0, { waiting for inflateSetDictionary } + BLOCKS, { decompressing blocks } + CHECK4, { four check bytes to go } + CHECK3, { three check bytes to go } + CHECK2, { two check bytes to go } + CHECK1, { one check byte to go } + DONE, { finished check, done } + BAD); { got an error--stay here } + +{ inflate private state } +type + pInternal_state = ^internal_state; { or point to a deflate_state record } + internal_state = record + + mode : inflate_mode; { current inflate mode } + + { mode dependent information } + sub : record { submode } + case byte of + 0:(method : uInt); { if FLAGS, method byte } + 1:(check : record { if CHECK, check values to compare } + was : uLong; { computed check value } + need : uLong; { stream check value } + end); + 2:(marker : uInt); { if BAD, inflateSync's marker bytes count } + end; + + { mode independent information } + nowrap : boolean; { flag for no wrapper } + wbits : uInt; { log2(window size) (8..15, defaults to 15) } + blocks : pInflate_blocks_state; { current inflate_blocks state } + end; + +type + alloc_func = function(opaque : voidpf; items : uInt; size : uInt) : voidpf; + free_func = procedure(opaque : voidpf; address : voidpf); + +type + z_streamp = ^z_stream; + z_stream = record + next_in : pBytef; { next input byte } + avail_in : uInt; { number of bytes available at next_in } + total_in : uLong; { total nb of input bytes read so far } + + next_out : pBytef; { next output byte should be put there } + avail_out : uInt; { remaining free space at next_out } + total_out : uLong; { total nb of bytes output so far } + + msg : string[255]; { last error message, '' if no error } + state : pInternal_state; { not visible by applications } + + zalloc : alloc_func; { used to allocate the internal state } + zfree : free_func; { used to free the internal state } + opaque : voidpf; { private data object passed to zalloc and zfree } + + data_type : int; { best guess about the data type: ascii or binary } + adler : uLong; { adler32 value of the uncompressed data } + reserved : uLong; { reserved for future use } + end; + + +{ The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). } + +const { constants } + Z_NO_FLUSH = 0; + Z_PARTIAL_FLUSH = 1; + Z_SYNC_FLUSH = 2; + Z_FULL_FLUSH = 3; + Z_FINISH = 4; +{ Allowed flush values; see deflate() below for details } + + Z_OK = 0; + Z_STREAM_END = 1; + Z_NEED_DICT = 2; + Z_ERRNO = (-1); + Z_STREAM_ERROR = (-2); + Z_DATA_ERROR = (-3); + Z_MEM_ERROR = (-4); + Z_BUF_ERROR = (-5); + Z_VERSION_ERROR = (-6); +{ Return codes for the compression/decompression functions. Negative + values are errors, positive values are used for special but normal events.} + + Z_NO_COMPRESSION = 0; + Z_BEST_SPEED = 1; + Z_BEST_COMPRESSION = 9; + Z_DEFAULT_COMPRESSION = (-1); +{ compression levels } + + Z_FILTERED = 1; + Z_HUFFMAN_ONLY = 2; + Z_DEFAULT_STRATEGY = 0; +{ compression strategy; see deflateInit2() below for details } + + Z_BINARY = 0; + Z_ASCII = 1; + Z_UNKNOWN = 2; +{ Possible values of the data_type field } + + Z_DEFLATED = 8; +{ The deflate compression method (the only one supported in this version) } + + Z_NULL = NIL; { for initializing zalloc, zfree, opaque } + + {$IFDEF GZIO} +var + errno : int; + {$ENDIF} + + { common constants } + + +{ The three kinds of block type } +const + STORED_BLOCK = 0; + STATIC_TREES = 1; + DYN_TREES = 2; +{ The minimum and maximum match lengths } +const + MIN_MATCH = 3; + MAX_MATCH = 258; + +const + PRESET_DICT = $20; { preset dictionary flag in zlib header } + + + {$IFDEF DEBUG} + procedure Assert(cond : boolean; msg : AnsiString); + {$ENDIF} + + procedure Trace(x : AnsiString); + procedure Tracev(x : AnsiString); + procedure Tracevv(x : AnsiString); + procedure Tracevvv(x : AnsiString); + procedure Tracec(c : boolean; x : AnsiString); + procedure Tracecv(c : boolean; x : AnsiString); + +function zlibVersion : AnsiString; +{ The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. } + +function zError(err : int) : AnsiString; +function ZALLOC (var strm : z_stream; items : uInt; size : uInt) : voidpf; +procedure ZFREE (var strm : z_stream; ptr : voidpf); +procedure TRY_FREE (var strm : z_stream; ptr : voidpf); + +const + ZLIB_VERSION : string[10] = '1.1.2'; + +const + z_errbase = Z_NEED_DICT; + z_errmsg : Array[0..9] of string[21] = { indexed by 2-zlib_error } + ('need dictionary', { Z_NEED_DICT 2 } + 'stream end', { Z_STREAM_END 1 } + '', { Z_OK 0 } + 'file error', { Z_ERRNO (-1) } + 'stream error', { Z_STREAM_ERROR (-2) } + 'data error', { Z_DATA_ERROR (-3) } + 'insufficient memory', { Z_MEM_ERROR (-4) } + 'buffer error', { Z_BUF_ERROR (-5) } + 'incompatible version',{ Z_VERSION_ERROR (-6) } + ''); +const + z_verbose : int = 1; + +function deflateInit_(var Stream: z_stream; Level: LongInt; const Version: AnsiString; + Stream_size: LongInt): LongInt; +function inflateInit_(var Stream: z_stream; const Version: AnsiString; + Stream_size: Longint): LongInt; + +{$IFDEF DEBUG} +procedure z_error (m : string); +{$ENDIF} + +implementation + +uses + imzdeflate, imzinflate; + +function deflateInit_(var Stream: z_stream; Level: LongInt; const Version: AnsiString; + Stream_size: LongInt): LongInt; +begin + Result := imzdeflate.deflateInit_(@Stream, Level, Version, Stream_size); +end; + +function inflateInit_(var Stream: z_stream; const Version: AnsiString; + Stream_size: Longint): LongInt; +begin + Result := imzinflate.inflateInit_(@Stream, Version, Stream_size); +end; + +function zError(err : int) : AnsiString; +begin + zError := z_errmsg[Z_NEED_DICT-err]; +end; + +function zlibVersion : AnsiString; +begin + zlibVersion := ZLIB_VERSION; +end; + +procedure z_error (m : AnsiString); +begin + WriteLn(output, m); + Write('Zlib - Halt...'); + ReadLn; + Halt(1); +end; + +procedure Assert(cond : boolean; msg : AnsiString); +begin + if not cond then + z_error(msg); +end; + +procedure Trace(x : AnsiString); +begin + WriteLn(x); +end; + +procedure Tracev(x : AnsiString); +begin + if (z_verbose>0) then + WriteLn(x); +end; + +procedure Tracevv(x : AnsiString); +begin + if (z_verbose>1) then + WriteLn(x); +end; + +procedure Tracevvv(x : AnsiString); +begin + if (z_verbose>2) then + WriteLn(x); +end; + +procedure Tracec(c : boolean; x : AnsiString); +begin + if (z_verbose>0) and (c) then + WriteLn(x); +end; + +procedure Tracecv(c : boolean; x : AnsiString); +begin + if (z_verbose>1) and c then + WriteLn(x); +end; + +function ZALLOC (var strm : z_stream; items : uInt; size : uInt) : voidpf; +begin + ZALLOC := strm.zalloc(strm.opaque, items, size); +end; + +procedure ZFREE (var strm : z_stream; ptr : voidpf); +begin + strm.zfree(strm.opaque, ptr); +end; + +procedure TRY_FREE (var strm : z_stream; ptr : voidpf); +begin + {if @strm <> Z_NULL then} + strm.zfree(strm.opaque, ptr); +end; + +end. diff --git a/resources/libraries/deskew/Imaging/ZLib/imtrees.pas b/resources/libraries/deskew/Imaging/ZLib/imtrees.pas new file mode 100755 index 0000000..04c0ebf --- /dev/null +++ b/resources/libraries/deskew/Imaging/ZLib/imtrees.pas @@ -0,0 +1,2249 @@ +Unit imtrees; + +{$T-} +{$define ORG_DEBUG} +{ + trees.c -- output deflated data using Huffman coding + Copyright (C) 1995-1998 Jean-loup Gailly + + Pascal tranlastion + Copyright (C) 1998 by Jacques Nomssi Nzali + For conditions of distribution and use, see copyright notice in readme.txt +} + +{ + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + } + +interface + +{$I imzconf.inc} + +uses + {$ifdef DEBUG} + SysUtils, strutils, + {$ENDIF} + imzutil, impaszlib; + +{ =========================================================================== + Internal compression state. } + +const + LENGTH_CODES = 29; +{ number of length codes, not counting the special END_BLOCK code } + + LITERALS = 256; +{ number of literal bytes 0..255 } + + L_CODES = (LITERALS+1+LENGTH_CODES); +{ number of Literal or Length codes, including the END_BLOCK code } + + D_CODES = 30; +{ number of distance codes } + + BL_CODES = 19; +{ number of codes used to transfer the bit lengths } + + HEAP_SIZE = (2*L_CODES+1); +{ maximum heap size } + + MAX_BITS = 15; +{ All codes must not exceed MAX_BITS bits } + +const + INIT_STATE = 42; + BUSY_STATE = 113; + FINISH_STATE = 666; +{ Stream status } + + +{ Data structure describing a single value and its code string. } +type + ct_data_ptr = ^ct_data; + ct_data = record + fc : record + case byte of + 0:(freq : ush); { frequency count } + 1:(code : ush); { bit string } + end; + dl : record + case byte of + 0:(dad : ush); { father node in Huffman tree } + 1:(len : ush); { length of bit string } + end; + end; + +{ Freq = fc.freq + Code = fc.code + Dad = dl.dad + Len = dl.len } + +type + ltree_type = array[0..HEAP_SIZE-1] of ct_data; { literal and length tree } + dtree_type = array[0..2*D_CODES+1-1] of ct_data; { distance tree } + htree_type = array[0..2*BL_CODES+1-1] of ct_data; { Huffman tree for bit lengths } + { generic tree type } + tree_type = array[0..(MaxInt div SizeOf(ct_data))-1] of ct_data; + + tree_ptr = ^tree_type; + ltree_ptr = ^ltree_type; + dtree_ptr = ^dtree_type; + htree_ptr = ^htree_type; + + +type + static_tree_desc_ptr = ^static_tree_desc; + static_tree_desc = + record + {const} static_tree : tree_ptr; { static tree or NIL } + {const} extra_bits : pzIntfArray; { extra bits for each code or NIL } + extra_base : int; { base index for extra_bits } + elems : int; { max number of elements in the tree } + max_length : int; { max bit length for the codes } + end; + + tree_desc_ptr = ^tree_desc; + tree_desc = record + dyn_tree : tree_ptr; { the dynamic tree } + max_code : int; { largest code with non zero frequency } + stat_desc : static_tree_desc_ptr; { the corresponding static tree } + end; + +type + Pos = ush; + Posf = Pos; {FAR} + IPos = uInt; + + pPosf = ^Posf; + + zPosfArray = array[0..(MaxInt div SizeOf(Posf))-1] of Posf; + pzPosfArray = ^zPosfArray; + +{ A Pos is an index in the character window. We use short instead of int to + save space in the various tables. IPos is used only for parameter passing.} + +type + deflate_state_ptr = ^deflate_state; + deflate_state = record + strm : z_streamp; { pointer back to this zlib stream } + status : int; { as the name implies } + pending_buf : pzByteArray; { output still pending } + pending_buf_size : ulg; { size of pending_buf } + pending_out : pBytef; { next pending byte to output to the stream } + pending : int; { nb of bytes in the pending buffer } + noheader : int; { suppress zlib header and adler32 } + data_type : Byte; { UNKNOWN, BINARY or ASCII } + method : Byte; { STORED (for zip only) or DEFLATED } + last_flush : int; { value of flush param for previous deflate call } + + { used by deflate.pas: } + + w_size : uInt; { LZ77 window size (32K by default) } + w_bits : uInt; { log2(w_size) (8..16) } + w_mask : uInt; { w_size - 1 } + + window : pzByteArray; + { Sliding window. Input bytes are read into the second half of the window, + and move to the first half later to keep a dictionary of at least wSize + bytes. With this organization, matches are limited to a distance of + wSize-MAX_MATCH bytes, but this ensures that IO is always + performed with a length multiple of the block size. Also, it limits + the window size to 64K, which is quite useful on MSDOS. + To do: use the user input buffer as sliding window. } + + window_size : ulg; + { Actual size of window: 2*wSize, except when the user input buffer + is directly used as sliding window. } + + prev : pzPosfArray; + { Link to older string with same hash index. To limit the size of this + array to 64K, this link is maintained only for the last 32K strings. + An index in this array is thus a window index modulo 32K. } + + head : pzPosfArray; { Heads of the hash chains or NIL. } + + ins_h : uInt; { hash index of string to be inserted } + hash_size : uInt; { number of elements in hash table } + hash_bits : uInt; { log2(hash_size) } + hash_mask : uInt; { hash_size-1 } + + hash_shift : uInt; + { Number of bits by which ins_h must be shifted at each input + step. It must be such that after MIN_MATCH steps, the oldest + byte no longer takes part in the hash key, that is: + hash_shift * MIN_MATCH >= hash_bits } + + block_start : long; + { Window position at the beginning of the current output block. Gets + negative when the window is moved backwards. } + + match_length : uInt; { length of best match } + prev_match : IPos; { previous match } + match_available : boolean; { set if previous match exists } + strstart : uInt; { start of string to insert } + match_start : uInt; { start of matching string } + lookahead : uInt; { number of valid bytes ahead in window } + + prev_length : uInt; + { Length of the best match at previous step. Matches not greater than this + are discarded. This is used in the lazy match evaluation. } + + max_chain_length : uInt; + { To speed up deflation, hash chains are never searched beyond this + length. A higher limit improves compression ratio but degrades the + speed. } + + { moved to the end because Borland Pascal won't accept the following: + max_lazy_match : uInt; + max_insert_length : uInt absolute max_lazy_match; + } + + level : int; { compression level (1..9) } + strategy : int; { favor or force Huffman coding} + + good_match : uInt; + { Use a faster search when the previous match is longer than this } + + nice_match : int; { Stop searching when current match exceeds this } + + { used by trees.pas: } + { Didn't use ct_data typedef below to supress compiler warning } + dyn_ltree : ltree_type; { literal and length tree } + dyn_dtree : dtree_type; { distance tree } + bl_tree : htree_type; { Huffman tree for bit lengths } + + l_desc : tree_desc; { desc. for literal tree } + d_desc : tree_desc; { desc. for distance tree } + bl_desc : tree_desc; { desc. for bit length tree } + + bl_count : array[0..MAX_BITS+1-1] of ush; + { number of codes at each bit length for an optimal tree } + + heap : array[0..2*L_CODES+1-1] of int; { heap used to build the Huffman trees } + heap_len : int; { number of elements in the heap } + heap_max : int; { element of largest frequency } + { The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + The same heap array is used to build all trees. } + + depth : array[0..2*L_CODES+1-1] of uch; + { Depth of each subtree used as tie breaker for trees of equal frequency } + + + l_buf : puchfArray; { buffer for literals or lengths } + + lit_bufsize : uInt; + { Size of match buffer for literals/lengths. There are 4 reasons for + limiting lit_bufsize to 64K: + - frequencies can be kept in 16 bit counters + - if compression is not successful for the first block, all input + data is still in the window so we can still emit a stored block even + when input comes from standard input. (This can also be done for + all blocks if lit_bufsize is not greater than 32K.) + - if compression is not successful for a file smaller than 64K, we can + even emit a stored file instead of a stored block (saving 5 bytes). + This is applicable only for zip (not gzip or zlib). + - creating new Huffman trees less frequently may not provide fast + adaptation to changes in the input data statistics. (Take for + example a binary file with poorly compressible code followed by + a highly compressible string table.) Smaller buffer sizes give + fast adaptation but have of course the overhead of transmitting + trees more frequently. + - I can't count above 4 } + + + last_lit : uInt; { running index in l_buf } + + d_buf : pushfArray; + { Buffer for distances. To simplify the code, d_buf and l_buf have + the same number of elements. To use different lengths, an extra flag + array would be necessary. } + + opt_len : ulg; { bit length of current block with optimal trees } + static_len : ulg; { bit length of current block with static trees } + compressed_len : ulg; { total bit length of compressed file } + matches : uInt; { number of string matches in current block } + last_eob_len : int; { bit length of EOB code for last block } + +{$ifdef DEBUG} + bits_sent : ulg; { bit length of the compressed data } +{$endif} + + bi_buf : ush; + { Output buffer. bits are inserted starting at the bottom (least + significant bits). } + + bi_valid : int; + { Number of valid bits in bi_buf. All bits above the last valid bit + are always zero. } + + case byte of + 0:(max_lazy_match : uInt); + { Attempt to find a better match only when the current match is strictly + smaller than this value. This mechanism is used only for compression + levels >= 4. } + + 1:(max_insert_length : uInt); + { Insert new strings in the hash table only if the match length is not + greater than this length. This saves time but degrades compression. + max_insert_length is used only for compression levels <= 3. } + end; + +procedure _tr_init (var s : deflate_state); + +function _tr_tally (var s : deflate_state; + dist : unsigned; + lc : unsigned) : boolean; + +function _tr_flush_block (var s : deflate_state; + buf : pcharf; + stored_len : ulg; + eof : boolean) : ulg; + +procedure _tr_align(var s : deflate_state); + +procedure _tr_stored_block(var s : deflate_state; + buf : pcharf; + stored_len : ulg; + eof : boolean); + +implementation + +{ #define GEN_TREES_H } + +{$ifndef GEN_TREES_H} +{ header created automatically with -DGEN_TREES_H } + +const + DIST_CODE_LEN = 512; { see definition of array dist_code below } + +{ The static literal tree. Since the bit lengths are imposed, there is no + need for the L_CODES extra codes used during heap construction. However + The codes 286 and 287 are needed to build a canonical tree (see _tr_init + below). } +var + static_ltree : array[0..L_CODES+2-1] of ct_data = ( +{ fc:(freq, code) dl:(dad,len) } +(fc:(freq: 12);dl:(len: 8)), (fc:(freq:140);dl:(len: 8)), (fc:(freq: 76);dl:(len: 8)), +(fc:(freq:204);dl:(len: 8)), (fc:(freq: 44);dl:(len: 8)), (fc:(freq:172);dl:(len: 8)), +(fc:(freq:108);dl:(len: 8)), (fc:(freq:236);dl:(len: 8)), (fc:(freq: 28);dl:(len: 8)), +(fc:(freq:156);dl:(len: 8)), (fc:(freq: 92);dl:(len: 8)), (fc:(freq:220);dl:(len: 8)), +(fc:(freq: 60);dl:(len: 8)), (fc:(freq:188);dl:(len: 8)), (fc:(freq:124);dl:(len: 8)), +(fc:(freq:252);dl:(len: 8)), (fc:(freq: 2);dl:(len: 8)), (fc:(freq:130);dl:(len: 8)), +(fc:(freq: 66);dl:(len: 8)), (fc:(freq:194);dl:(len: 8)), (fc:(freq: 34);dl:(len: 8)), +(fc:(freq:162);dl:(len: 8)), (fc:(freq: 98);dl:(len: 8)), (fc:(freq:226);dl:(len: 8)), +(fc:(freq: 18);dl:(len: 8)), (fc:(freq:146);dl:(len: 8)), (fc:(freq: 82);dl:(len: 8)), +(fc:(freq:210);dl:(len: 8)), (fc:(freq: 50);dl:(len: 8)), (fc:(freq:178);dl:(len: 8)), +(fc:(freq:114);dl:(len: 8)), (fc:(freq:242);dl:(len: 8)), (fc:(freq: 10);dl:(len: 8)), +(fc:(freq:138);dl:(len: 8)), (fc:(freq: 74);dl:(len: 8)), (fc:(freq:202);dl:(len: 8)), +(fc:(freq: 42);dl:(len: 8)), (fc:(freq:170);dl:(len: 8)), (fc:(freq:106);dl:(len: 8)), +(fc:(freq:234);dl:(len: 8)), (fc:(freq: 26);dl:(len: 8)), (fc:(freq:154);dl:(len: 8)), +(fc:(freq: 90);dl:(len: 8)), (fc:(freq:218);dl:(len: 8)), (fc:(freq: 58);dl:(len: 8)), +(fc:(freq:186);dl:(len: 8)), (fc:(freq:122);dl:(len: 8)), (fc:(freq:250);dl:(len: 8)), +(fc:(freq: 6);dl:(len: 8)), (fc:(freq:134);dl:(len: 8)), (fc:(freq: 70);dl:(len: 8)), +(fc:(freq:198);dl:(len: 8)), (fc:(freq: 38);dl:(len: 8)), (fc:(freq:166);dl:(len: 8)), +(fc:(freq:102);dl:(len: 8)), (fc:(freq:230);dl:(len: 8)), (fc:(freq: 22);dl:(len: 8)), +(fc:(freq:150);dl:(len: 8)), (fc:(freq: 86);dl:(len: 8)), (fc:(freq:214);dl:(len: 8)), +(fc:(freq: 54);dl:(len: 8)), (fc:(freq:182);dl:(len: 8)), (fc:(freq:118);dl:(len: 8)), +(fc:(freq:246);dl:(len: 8)), (fc:(freq: 14);dl:(len: 8)), (fc:(freq:142);dl:(len: 8)), +(fc:(freq: 78);dl:(len: 8)), (fc:(freq:206);dl:(len: 8)), (fc:(freq: 46);dl:(len: 8)), +(fc:(freq:174);dl:(len: 8)), (fc:(freq:110);dl:(len: 8)), (fc:(freq:238);dl:(len: 8)), +(fc:(freq: 30);dl:(len: 8)), (fc:(freq:158);dl:(len: 8)), (fc:(freq: 94);dl:(len: 8)), +(fc:(freq:222);dl:(len: 8)), (fc:(freq: 62);dl:(len: 8)), (fc:(freq:190);dl:(len: 8)), +(fc:(freq:126);dl:(len: 8)), (fc:(freq:254);dl:(len: 8)), (fc:(freq: 1);dl:(len: 8)), +(fc:(freq:129);dl:(len: 8)), (fc:(freq: 65);dl:(len: 8)), (fc:(freq:193);dl:(len: 8)), +(fc:(freq: 33);dl:(len: 8)), (fc:(freq:161);dl:(len: 8)), (fc:(freq: 97);dl:(len: 8)), +(fc:(freq:225);dl:(len: 8)), (fc:(freq: 17);dl:(len: 8)), (fc:(freq:145);dl:(len: 8)), +(fc:(freq: 81);dl:(len: 8)), (fc:(freq:209);dl:(len: 8)), (fc:(freq: 49);dl:(len: 8)), +(fc:(freq:177);dl:(len: 8)), (fc:(freq:113);dl:(len: 8)), (fc:(freq:241);dl:(len: 8)), +(fc:(freq: 9);dl:(len: 8)), (fc:(freq:137);dl:(len: 8)), (fc:(freq: 73);dl:(len: 8)), +(fc:(freq:201);dl:(len: 8)), (fc:(freq: 41);dl:(len: 8)), (fc:(freq:169);dl:(len: 8)), +(fc:(freq:105);dl:(len: 8)), (fc:(freq:233);dl:(len: 8)), (fc:(freq: 25);dl:(len: 8)), +(fc:(freq:153);dl:(len: 8)), (fc:(freq: 89);dl:(len: 8)), (fc:(freq:217);dl:(len: 8)), +(fc:(freq: 57);dl:(len: 8)), (fc:(freq:185);dl:(len: 8)), (fc:(freq:121);dl:(len: 8)), +(fc:(freq:249);dl:(len: 8)), (fc:(freq: 5);dl:(len: 8)), (fc:(freq:133);dl:(len: 8)), +(fc:(freq: 69);dl:(len: 8)), (fc:(freq:197);dl:(len: 8)), (fc:(freq: 37);dl:(len: 8)), +(fc:(freq:165);dl:(len: 8)), (fc:(freq:101);dl:(len: 8)), (fc:(freq:229);dl:(len: 8)), +(fc:(freq: 21);dl:(len: 8)), (fc:(freq:149);dl:(len: 8)), (fc:(freq: 85);dl:(len: 8)), +(fc:(freq:213);dl:(len: 8)), (fc:(freq: 53);dl:(len: 8)), (fc:(freq:181);dl:(len: 8)), +(fc:(freq:117);dl:(len: 8)), (fc:(freq:245);dl:(len: 8)), (fc:(freq: 13);dl:(len: 8)), +(fc:(freq:141);dl:(len: 8)), (fc:(freq: 77);dl:(len: 8)), (fc:(freq:205);dl:(len: 8)), +(fc:(freq: 45);dl:(len: 8)), (fc:(freq:173);dl:(len: 8)), (fc:(freq:109);dl:(len: 8)), +(fc:(freq:237);dl:(len: 8)), (fc:(freq: 29);dl:(len: 8)), (fc:(freq:157);dl:(len: 8)), +(fc:(freq: 93);dl:(len: 8)), (fc:(freq:221);dl:(len: 8)), (fc:(freq: 61);dl:(len: 8)), +(fc:(freq:189);dl:(len: 8)), (fc:(freq:125);dl:(len: 8)), (fc:(freq:253);dl:(len: 8)), +(fc:(freq: 19);dl:(len: 9)), (fc:(freq:275);dl:(len: 9)), (fc:(freq:147);dl:(len: 9)), +(fc:(freq:403);dl:(len: 9)), (fc:(freq: 83);dl:(len: 9)), (fc:(freq:339);dl:(len: 9)), +(fc:(freq:211);dl:(len: 9)), (fc:(freq:467);dl:(len: 9)), (fc:(freq: 51);dl:(len: 9)), +(fc:(freq:307);dl:(len: 9)), (fc:(freq:179);dl:(len: 9)), (fc:(freq:435);dl:(len: 9)), +(fc:(freq:115);dl:(len: 9)), (fc:(freq:371);dl:(len: 9)), (fc:(freq:243);dl:(len: 9)), +(fc:(freq:499);dl:(len: 9)), (fc:(freq: 11);dl:(len: 9)), (fc:(freq:267);dl:(len: 9)), +(fc:(freq:139);dl:(len: 9)), (fc:(freq:395);dl:(len: 9)), (fc:(freq: 75);dl:(len: 9)), +(fc:(freq:331);dl:(len: 9)), (fc:(freq:203);dl:(len: 9)), (fc:(freq:459);dl:(len: 9)), +(fc:(freq: 43);dl:(len: 9)), (fc:(freq:299);dl:(len: 9)), (fc:(freq:171);dl:(len: 9)), +(fc:(freq:427);dl:(len: 9)), (fc:(freq:107);dl:(len: 9)), (fc:(freq:363);dl:(len: 9)), +(fc:(freq:235);dl:(len: 9)), (fc:(freq:491);dl:(len: 9)), (fc:(freq: 27);dl:(len: 9)), +(fc:(freq:283);dl:(len: 9)), (fc:(freq:155);dl:(len: 9)), (fc:(freq:411);dl:(len: 9)), +(fc:(freq: 91);dl:(len: 9)), (fc:(freq:347);dl:(len: 9)), (fc:(freq:219);dl:(len: 9)), +(fc:(freq:475);dl:(len: 9)), (fc:(freq: 59);dl:(len: 9)), (fc:(freq:315);dl:(len: 9)), +(fc:(freq:187);dl:(len: 9)), (fc:(freq:443);dl:(len: 9)), (fc:(freq:123);dl:(len: 9)), +(fc:(freq:379);dl:(len: 9)), (fc:(freq:251);dl:(len: 9)), (fc:(freq:507);dl:(len: 9)), +(fc:(freq: 7);dl:(len: 9)), (fc:(freq:263);dl:(len: 9)), (fc:(freq:135);dl:(len: 9)), +(fc:(freq:391);dl:(len: 9)), (fc:(freq: 71);dl:(len: 9)), (fc:(freq:327);dl:(len: 9)), +(fc:(freq:199);dl:(len: 9)), (fc:(freq:455);dl:(len: 9)), (fc:(freq: 39);dl:(len: 9)), +(fc:(freq:295);dl:(len: 9)), (fc:(freq:167);dl:(len: 9)), (fc:(freq:423);dl:(len: 9)), +(fc:(freq:103);dl:(len: 9)), (fc:(freq:359);dl:(len: 9)), (fc:(freq:231);dl:(len: 9)), +(fc:(freq:487);dl:(len: 9)), (fc:(freq: 23);dl:(len: 9)), (fc:(freq:279);dl:(len: 9)), +(fc:(freq:151);dl:(len: 9)), (fc:(freq:407);dl:(len: 9)), (fc:(freq: 87);dl:(len: 9)), +(fc:(freq:343);dl:(len: 9)), (fc:(freq:215);dl:(len: 9)), (fc:(freq:471);dl:(len: 9)), +(fc:(freq: 55);dl:(len: 9)), (fc:(freq:311);dl:(len: 9)), (fc:(freq:183);dl:(len: 9)), +(fc:(freq:439);dl:(len: 9)), (fc:(freq:119);dl:(len: 9)), (fc:(freq:375);dl:(len: 9)), +(fc:(freq:247);dl:(len: 9)), (fc:(freq:503);dl:(len: 9)), (fc:(freq: 15);dl:(len: 9)), +(fc:(freq:271);dl:(len: 9)), (fc:(freq:143);dl:(len: 9)), (fc:(freq:399);dl:(len: 9)), +(fc:(freq: 79);dl:(len: 9)), (fc:(freq:335);dl:(len: 9)), (fc:(freq:207);dl:(len: 9)), +(fc:(freq:463);dl:(len: 9)), (fc:(freq: 47);dl:(len: 9)), (fc:(freq:303);dl:(len: 9)), +(fc:(freq:175);dl:(len: 9)), (fc:(freq:431);dl:(len: 9)), (fc:(freq:111);dl:(len: 9)), +(fc:(freq:367);dl:(len: 9)), (fc:(freq:239);dl:(len: 9)), (fc:(freq:495);dl:(len: 9)), +(fc:(freq: 31);dl:(len: 9)), (fc:(freq:287);dl:(len: 9)), (fc:(freq:159);dl:(len: 9)), +(fc:(freq:415);dl:(len: 9)), (fc:(freq: 95);dl:(len: 9)), (fc:(freq:351);dl:(len: 9)), +(fc:(freq:223);dl:(len: 9)), (fc:(freq:479);dl:(len: 9)), (fc:(freq: 63);dl:(len: 9)), +(fc:(freq:319);dl:(len: 9)), (fc:(freq:191);dl:(len: 9)), (fc:(freq:447);dl:(len: 9)), +(fc:(freq:127);dl:(len: 9)), (fc:(freq:383);dl:(len: 9)), (fc:(freq:255);dl:(len: 9)), +(fc:(freq:511);dl:(len: 9)), (fc:(freq: 0);dl:(len: 7)), (fc:(freq: 64);dl:(len: 7)), +(fc:(freq: 32);dl:(len: 7)), (fc:(freq: 96);dl:(len: 7)), (fc:(freq: 16);dl:(len: 7)), +(fc:(freq: 80);dl:(len: 7)), (fc:(freq: 48);dl:(len: 7)), (fc:(freq:112);dl:(len: 7)), +(fc:(freq: 8);dl:(len: 7)), (fc:(freq: 72);dl:(len: 7)), (fc:(freq: 40);dl:(len: 7)), +(fc:(freq:104);dl:(len: 7)), (fc:(freq: 24);dl:(len: 7)), (fc:(freq: 88);dl:(len: 7)), +(fc:(freq: 56);dl:(len: 7)), (fc:(freq:120);dl:(len: 7)), (fc:(freq: 4);dl:(len: 7)), +(fc:(freq: 68);dl:(len: 7)), (fc:(freq: 36);dl:(len: 7)), (fc:(freq:100);dl:(len: 7)), +(fc:(freq: 20);dl:(len: 7)), (fc:(freq: 84);dl:(len: 7)), (fc:(freq: 52);dl:(len: 7)), +(fc:(freq:116);dl:(len: 7)), (fc:(freq: 3);dl:(len: 8)), (fc:(freq:131);dl:(len: 8)), +(fc:(freq: 67);dl:(len: 8)), (fc:(freq:195);dl:(len: 8)), (fc:(freq: 35);dl:(len: 8)), +(fc:(freq:163);dl:(len: 8)), (fc:(freq: 99);dl:(len: 8)), (fc:(freq:227);dl:(len: 8)) +); + + +{ The static distance tree. (Actually a trivial tree since all lens use + 5 bits.) } + static_dtree : array[0..D_CODES-1] of ct_data = ( +(fc:(freq: 0); dl:(len:5)), (fc:(freq:16); dl:(len:5)), (fc:(freq: 8); dl:(len:5)), +(fc:(freq:24); dl:(len:5)), (fc:(freq: 4); dl:(len:5)), (fc:(freq:20); dl:(len:5)), +(fc:(freq:12); dl:(len:5)), (fc:(freq:28); dl:(len:5)), (fc:(freq: 2); dl:(len:5)), +(fc:(freq:18); dl:(len:5)), (fc:(freq:10); dl:(len:5)), (fc:(freq:26); dl:(len:5)), +(fc:(freq: 6); dl:(len:5)), (fc:(freq:22); dl:(len:5)), (fc:(freq:14); dl:(len:5)), +(fc:(freq:30); dl:(len:5)), (fc:(freq: 1); dl:(len:5)), (fc:(freq:17); dl:(len:5)), +(fc:(freq: 9); dl:(len:5)), (fc:(freq:25); dl:(len:5)), (fc:(freq: 5); dl:(len:5)), +(fc:(freq:21); dl:(len:5)), (fc:(freq:13); dl:(len:5)), (fc:(freq:29); dl:(len:5)), +(fc:(freq: 3); dl:(len:5)), (fc:(freq:19); dl:(len:5)), (fc:(freq:11); dl:(len:5)), +(fc:(freq:27); dl:(len:5)), (fc:(freq: 7); dl:(len:5)), (fc:(freq:23); dl:(len:5)) +); + +{ Distance codes. The first 256 values correspond to the distances + 3 .. 258, the last 256 values correspond to the top 8 bits of + the 15 bit distances. } + _dist_code : array[0..DIST_CODE_LEN-1] of uch = ( + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +); + +{ length code for each normalized match length (0 == MIN_MATCH) } + _length_code : array[0..MAX_MATCH-MIN_MATCH+1-1] of uch = ( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +); + + +{ First normalized length for each code (0 = MIN_MATCH) } + base_length : array[0..LENGTH_CODES-1] of int = ( +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +); + + +{ First normalized distance for each code (0 = distance of 1) } + base_dist : array[0..D_CODES-1] of int = ( + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +); +{$endif} + +{ Output a byte on the stream. + IN assertion: there is enough room in pending_buf. +macro put_byte(s, c) +begin + s^.pending_buf^[s^.pending] := (c); + Inc(s^.pending); +end +} + +const + MIN_LOOKAHEAD = (MAX_MATCH+MIN_MATCH+1); +{ Minimum amount of lookahead, except at the end of the input file. + See deflate.c for comments about the MIN_MATCH+1. } + +{macro d_code(dist) + if (dist) < 256 then + := _dist_code[dist] + else + := _dist_code[256+((dist) shr 7)]); + Mapping from a distance to a distance code. dist is the distance - 1 and + must not have side effects. _dist_code[256] and _dist_code[257] are never + used. } + +{$ifndef ORG_DEBUG} +{ Inline versions of _tr_tally for speed: } + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch _length_code[]; + extern uch _dist_code[]; +#else + extern const uch _length_code[]; + extern const uch _dist_code[]; +#endif + +macro _tr_tally_lit(s, c, flush) +var + cc : uch; +begin + cc := (c); + s^.d_buf[s^.last_lit] := 0; + s^.l_buf[s^.last_lit] := cc; + Inc(s^.last_lit); + Inc(s^.dyn_ltree[cc].fc.Freq); + flush := (s^.last_lit = s^.lit_bufsize-1); +end; + +macro _tr_tally_dist(s, distance, length, flush) \ +var + len : uch; + dist : ush; +begin + len := (length); + dist := (distance); + s^.d_buf[s^.last_lit] := dist; + s^.l_buf[s^.last_lit] = len; + Inc(s^.last_lit); + Dec(dist); + Inc(s^.dyn_ltree[_length_code[len]+LITERALS+1].fc.Freq); + Inc(s^.dyn_dtree[d_code(dist)].Freq); + flush := (s^.last_lit = s^.lit_bufsize-1); +end; + +{$endif} + +{ =========================================================================== + Constants } + +const + MAX_BL_BITS = 7; +{ Bit length codes must not exceed MAX_BL_BITS bits } + +const + END_BLOCK = 256; +{ end of block literal code } + +const + REP_3_6 = 16; +{ repeat previous bit length 3-6 times (2 bits of repeat count) } + +const + REPZ_3_10 = 17; +{ repeat a zero length 3-10 times (3 bits of repeat count) } + +const + REPZ_11_138 = 18; +{ repeat a zero length 11-138 times (7 bits of repeat count) } + +{local} +const + extra_lbits : array[0..LENGTH_CODES-1] of int + { extra bits for each length code } + = (0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0); + +{local} +const + extra_dbits : array[0..D_CODES-1] of int + { extra bits for each distance code } + = (0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13); + +{local} +const + extra_blbits : array[0..BL_CODES-1] of int { extra bits for each bit length code } + = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7); + +{local} +const + bl_order : array[0..BL_CODES-1] of uch + = (16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15); +{ The lengths of the bit length codes are sent in order of decreasing + probability, to avoid transmitting the lengths for unused bit length codes. + } + +const + Buf_size = (8 * 2*sizeof(uch)); +{ Number of bits used within bi_buf. (bi_buf might be implemented on + more than 16 bits on some systems.) } + +{ =========================================================================== + Local data. These are initialized only once. } + + +{$ifdef GEN_TREES_H)} +{ non ANSI compilers may not accept trees.h } + +const + DIST_CODE_LEN = 512; { see definition of array dist_code below } + +{local} +var + static_ltree : array[0..L_CODES+2-1] of ct_data; +{ The static literal tree. Since the bit lengths are imposed, there is no + need for the L_CODES extra codes used during heap construction. However + The codes 286 and 287 are needed to build a canonical tree (see _tr_init + below). } + +{local} + static_dtree : array[0..D_CODES-1] of ct_data; +{ The static distance tree. (Actually a trivial tree since all codes use + 5 bits.) } + + _dist_code : array[0..DIST_CODE_LEN-1] of uch; +{ Distance codes. The first 256 values correspond to the distances + 3 .. 258, the last 256 values correspond to the top 8 bits of + the 15 bit distances. } + + _length_code : array[0..MAX_MATCH-MIN_MATCH+1-1] of uch; +{ length code for each normalized match length (0 == MIN_MATCH) } + +{local} + base_length : array[0..LENGTH_CODES-1] of int; +{ First normalized length for each code (0 = MIN_MATCH) } + +{local} + base_dist : array[0..D_CODES-1] of int; +{ First normalized distance for each code (0 = distance of 1) } + +{$endif} { GEN_TREES_H } + +{local} +const + static_l_desc : static_tree_desc = + (static_tree: {tree_ptr}(@(static_ltree)); { pointer to array of ct_data } + extra_bits: {pzIntfArray}(@(extra_lbits)); { pointer to array of int } + extra_base: LITERALS+1; + elems: L_CODES; + max_length: MAX_BITS); + +{local} +const + static_d_desc : static_tree_desc = + (static_tree: {tree_ptr}(@(static_dtree)); + extra_bits: {pzIntfArray}(@(extra_dbits)); + extra_base : 0; + elems: D_CODES; + max_length: MAX_BITS); + +{local} +const + static_bl_desc : static_tree_desc = + (static_tree: {tree_ptr}(NIL); + extra_bits: {pzIntfArray}@(extra_blbits); + extra_base : 0; + elems: BL_CODES; + max_length: MAX_BL_BITS); + +(* =========================================================================== + Local (static) routines in this file. } + +procedure tr_static_init; +procedure init_block(var deflate_state); +procedure pqdownheap(var s : deflate_state; + var tree : ct_data; + k : int); +procedure gen_bitlen(var s : deflate_state; + var desc : tree_desc); +procedure gen_codes(var tree : ct_data; + max_code : int; + bl_count : pushf); +procedure build_tree(var s : deflate_state; + var desc : tree_desc); +procedure scan_tree(var s : deflate_state; + var tree : ct_data; + max_code : int); +procedure send_tree(var s : deflate_state; + var tree : ct_data; + max_code : int); +function build_bl_tree(var deflate_state) : int; +procedure send_all_trees(var deflate_state; + lcodes : int; + dcodes : int; + blcodes : int); +procedure compress_block(var s : deflate_state; + var ltree : ct_data; + var dtree : ct_data); +procedure set_data_type(var s : deflate_state); +function bi_reverse(value : unsigned; + length : int) : unsigned; +procedure bi_windup(var deflate_state); +procedure bi_flush(var deflate_state); +procedure copy_block(var deflate_state; + buf : pcharf; + len : unsigned; + header : int); +*) + +{$ifdef GEN_TREES_H} +{local} +procedure gen_trees_header; +{$endif} + +(* +{ =========================================================================== + Output a short LSB first on the stream. + IN assertion: there is enough room in pendingBuf. } + +macro put_short(s, w) +begin + {put_byte(s, (uch)((w) & 0xff));} + s.pending_buf^[s.pending] := uch((w) and $ff); + Inc(s.pending); + + {put_byte(s, (uch)((ush)(w) >> 8));} + s.pending_buf^[s.pending] := uch(ush(w) shr 8);; + Inc(s.pending); +end +*) + +{ =========================================================================== + Send a value on a given number of bits. + IN assertion: length <= 16 and value fits in length bits. } + +{$ifdef ORG_DEBUG} + +{local} +procedure send_bits(var s : deflate_state; + value : int; { value to send } + length : int); { number of bits } +begin + {$ifdef DEBUG} + Tracevv(' l '+IntToStr(length)+ ' v '+IntToStr(value)); + Assert((length > 0) and (length <= 15), 'invalid length'); + Inc(s.bits_sent, ulg(length)); + {$ENDIF} + + { If not enough room in bi_buf, use (valid) bits from bi_buf and + (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + unused bits in value. } + {$IFOPT Q+} {$Q-} {$DEFINE NoOverflowCheck} {$ENDIF} + {$IFOPT R+} {$R-} {$DEFINE NoRangeCheck} {$ENDIF} + if (s.bi_valid > int(Buf_size) - length) then + begin + s.bi_buf := s.bi_buf or int(value shl s.bi_valid); + {put_short(s, s.bi_buf);} + s.pending_buf^[s.pending] := uch(s.bi_buf and $ff); + Inc(s.pending); + s.pending_buf^[s.pending] := uch(ush(s.bi_buf) shr 8);; + Inc(s.pending); + + s.bi_buf := ush(value) shr (Buf_size - s.bi_valid); + Inc(s.bi_valid, length - Buf_size); + end + else + begin + s.bi_buf := s.bi_buf or int(value shl s.bi_valid); + Inc(s.bi_valid, length); + end; + {$IFDEF NoOverflowCheck} {$Q+} {$UNDEF NoOverflowCheck} {$ENDIF} + {$IFDEF NoRangeCheck} {$Q+} {$UNDEF NoRangeCheck} {$ENDIF} +end; + +{$else} { !DEBUG } + + +macro send_code(s, c, tree) +begin + send_bits(s, tree[c].Code, tree[c].Len); + { Send a code of the given tree. c and tree must not have side effects } +end + +macro send_bits(s, value, length) \ +begin int len := length;\ + if (s^.bi_valid > (int)Buf_size - len) begin\ + int val := value;\ + s^.bi_buf |= (val << s^.bi_valid);\ + {put_short(s, s.bi_buf);} + s.pending_buf^[s.pending] := uch(s.bi_buf and $ff); + Inc(s.pending); + s.pending_buf^[s.pending] := uch(ush(s.bi_buf) shr 8);; + Inc(s.pending); + + s^.bi_buf := (ush)val >> (Buf_size - s^.bi_valid);\ + s^.bi_valid += len - Buf_size;\ + end else begin\ + s^.bi_buf |= (value) << s^.bi_valid;\ + s^.bi_valid += len;\ + end\ +end; +{$endif} { DEBUG } + +{ =========================================================================== + Reverse the first len bits of a code, using straightforward code (a faster + method would use a table) + IN assertion: 1 <= len <= 15 } + +{local} +function bi_reverse(code : unsigned; { the value to invert } + len : int) : unsigned; { its bit length } + +var + res : unsigned; {register} +begin + res := 0; + repeat + res := res or (code and 1); + code := code shr 1; + res := res shl 1; + Dec(len); + until (len <= 0); + bi_reverse := res shr 1; +end; + +{ =========================================================================== + Generate the codes for a given tree and bit counts (which need not be + optimal). + IN assertion: the array bl_count contains the bit length statistics for + the given tree and the field len is set for all tree elements. + OUT assertion: the field code is set for all tree elements of non + zero code length. } + +{local} +procedure gen_codes(tree : tree_ptr; { the tree to decorate } + max_code : int; { largest code with non zero frequency } + var bl_count : array of ushf); { number of codes at each bit length } + +var + next_code : array[0..MAX_BITS+1-1] of ush; { next code value for each bit length } + code : ush; { running code value } + bits : int; { bit index } + n : int; { code index } +var + len : int; +begin + code := 0; + + { The distribution counts are first used to generate the code values + without bit reversal. } + + for bits := 1 to MAX_BITS do + begin + code := ((code + bl_count[bits-1]) shl 1); + next_code[bits] := code; + end; + { Check that the bit counts in bl_count are consistent. The last code + must be all ones. } + + {$IFDEF DEBUG} + Assert (code + bl_count[MAX_BITS]-1 = (1 shl MAX_BITS)-1, + 'inconsistent bit counts'); + Tracev(#13'gen_codes: max_code '+IntToStr(max_code)); + {$ENDIF} + + for n := 0 to max_code do + begin + len := tree^[n].dl.Len; + if (len = 0) then + continue; + { Now reverse the bits } + tree^[n].fc.Code := bi_reverse(next_code[len], len); + Inc(next_code[len]); + {$ifdef DEBUG} + if (n>31) and (n<128) then + Tracecv(tree <> tree_ptr(@static_ltree), + (^M'n #'+IntToStr(n)+' '+AnsiChar(n)+' l '+IntToStr(len)+' c '+ + IntToStr(tree^[n].fc.Code)+' ('+IntToStr(next_code[len]-1)+')')) + else + Tracecv(tree <> tree_ptr(@static_ltree), + (^M'n #'+IntToStr(n)+' l '+IntToStr(len)+' c '+ + IntToStr(tree^[n].fc.Code)+' ('+IntToStr(next_code[len]-1)+')')); + {$ENDIF} + end; +end; + +{ =========================================================================== + Genererate the file trees.h describing the static trees. } +{$ifdef GEN_TREES_H} + +macro SEPARATOR(i, last, width) + if (i) = (last) then + ( ^M');'^M^M + else \ + if (i) mod (width) = (width)-1 then + ','^M + else + ', ' + +procedure gen_trees_header; +var + header : system.text; + i : int; +begin + system.assign(header, 'trees.inc'); + {$I-} + ReWrite(header); + {$I+} + Assert (IOresult <> 0, 'Can''t open trees.h'); + WriteLn(header, + '{ header created automatically with -DGEN_TREES_H }'^M); + + WriteLn(header, 'local const ct_data static_ltree[L_CODES+2] := ('); + for i := 0 to L_CODES+2-1 do + begin + WriteLn(header, '((%3u),(%3u))%s', static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + end; + + WriteLn(header, 'local const ct_data static_dtree[D_CODES] := ('); + for i := 0 to D_CODES-1 do + begin + WriteLn(header, '((%2u),(%2u))%s', static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + end; + + WriteLn(header, 'const uch _dist_code[DIST_CODE_LEN] := ('); + for i := 0 to DIST_CODE_LEN-1 do + begin + WriteLn(header, '%2u%s', _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + end; + + WriteLn(header, 'const uch _length_code[MAX_MATCH-MIN_MATCH+1]= ('); + for i := 0 to MAX_MATCH-MIN_MATCH+1-1 do + begin + WriteLn(header, '%2u%s', _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + end; + + WriteLn(header, 'local const int base_length[LENGTH_CODES] := ('); + for i := 0 to LENGTH_CODES-1 do + begin + WriteLn(header, '%1u%s', base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + end; + + WriteLn(header, 'local const int base_dist[D_CODES] := ('); + for i := 0 to D_CODES-1 do + begin + WriteLn(header, '%5u%s', base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + end; + + close(header); +end; +{$endif} { GEN_TREES_H } + + +{ =========================================================================== + Initialize the various 'constant' tables. } + +{local} +procedure tr_static_init; + +{$ifdef GEN_TREES_H} +const + static_init_done : boolean = FALSE; +var + n : int; { iterates over tree elements } + bits : int; { bit counter } + length : int; { length value } + code : int; { code value } + dist : int; { distance index } + bl_count : array[0..MAX_BITS+1-1] of ush; + { number of codes at each bit length for an optimal tree } +begin + if (static_init_done) then + exit; + + { Initialize the mapping length (0..255) -> length code (0..28) } + length := 0; + for code := 0 to LENGTH_CODES-1-1 do + begin + base_length[code] := length; + for n := 0 to (1 shl extra_lbits[code])-1 do + begin + _length_code[length] := uch(code); + Inc(length); + end; + end; + Assert (length = 256, 'tr_static_init: length <> 256'); + { Note that the length 255 (match length 258) can be represented + in two different ways: code 284 + 5 bits or code 285, so we + overwrite length_code[255] to use the best encoding: } + + _length_code[length-1] := uch(code); + + { Initialize the mapping dist (0..32K) -> dist code (0..29) } + dist := 0; + for code := 0 to 16-1 do + begin + base_dist[code] := dist; + for n := 0 to (1 shl extra_dbits[code])-1 do + begin + _dist_code[dist] := uch(code); + Inc(dist); + end; + end; + Assert (dist = 256, 'tr_static_init: dist <> 256'); + dist := dist shr 7; { from now on, all distances are divided by 128 } + for code := 16 to D_CODES-1 do + begin + base_dist[code] := dist shl 7; + for n := 0 to (1 shl (extra_dbits[code]-7))-1 do + begin + _dist_code[256 + dist] := uch(code); + Inc(dist); + end; + end; + Assert (dist = 256, 'tr_static_init: 256+dist <> 512'); + + { Construct the codes of the static literal tree } + for bits := 0 to MAX_BITS do + bl_count[bits] := 0; + n := 0; + while (n <= 143) do + begin + static_ltree[n].dl.Len := 8; + Inc(n); + Inc(bl_count[8]); + end; + while (n <= 255) do + begin + static_ltree[n].dl.Len := 9; + Inc(n); + Inc(bl_count[9]); + end; + while (n <= 279) do + begin + static_ltree[n].dl.Len := 7; + Inc(n); + Inc(bl_count[7]); + end; + while (n <= 287) do + begin + static_ltree[n].dl.Len := 8; + Inc(n); + Inc(bl_count[8]); + end; + + { Codes 286 and 287 do not exist, but we must include them in the + tree construction to get a canonical Huffman tree (longest code + all ones) } + + gen_codes(tree_ptr(@static_ltree), L_CODES+1, bl_count); + + { The static distance tree is trivial: } + for n := 0 to D_CODES-1 do + begin + static_dtree[n].dl.Len := 5; + static_dtree[n].fc.Code := bi_reverse(unsigned(n), 5); + end; + static_init_done := TRUE; + + gen_trees_header; { save to include file } +{$else} +begin +{$endif} { GEN_TREES_H) } +end; + +{ =========================================================================== + Initialize a new block. } +{local} + +procedure init_block(var s : deflate_state); +var + n : int; { iterates over tree elements } +begin + { Initialize the trees. } + for n := 0 to L_CODES-1 do + s.dyn_ltree[n].fc.Freq := 0; + for n := 0 to D_CODES-1 do + s.dyn_dtree[n].fc.Freq := 0; + for n := 0 to BL_CODES-1 do + s.bl_tree[n].fc.Freq := 0; + + s.dyn_ltree[END_BLOCK].fc.Freq := 1; + s.static_len := Long(0); + s.opt_len := Long(0); + s.matches := 0; + s.last_lit := 0; +end; + +const + SMALLEST = 1; +{ Index within the heap array of least frequent node in the Huffman tree } + +{ =========================================================================== + Initialize the tree data structures for a new zlib stream. } +procedure _tr_init(var s : deflate_state); +begin + tr_static_init; + + s.compressed_len := Long(0); + + s.l_desc.dyn_tree := tree_ptr(@s.dyn_ltree); + s.l_desc.stat_desc := @static_l_desc; + + s.d_desc.dyn_tree := tree_ptr(@s.dyn_dtree); + s.d_desc.stat_desc := @static_d_desc; + + s.bl_desc.dyn_tree := tree_ptr(@s.bl_tree); + s.bl_desc.stat_desc := @static_bl_desc; + + s.bi_buf := 0; + s.bi_valid := 0; + s.last_eob_len := 8; { enough lookahead for inflate } +{$ifdef DEBUG} + s.bits_sent := Long(0); +{$endif} + + { Initialize the first block of the first file: } + init_block(s); +end; + +{ =========================================================================== + Remove the smallest element from the heap and recreate the heap with + one less element. Updates heap and heap_len. + +macro pqremove(s, tree, top) +begin + top := s.heap[SMALLEST]; + s.heap[SMALLEST] := s.heap[s.heap_len]; + Dec(s.heap_len); + pqdownheap(s, tree, SMALLEST); +end +} + +{ =========================================================================== + Compares to subtrees, using the tree depth as tie breaker when + the subtrees have equal frequency. This minimizes the worst case length. + +macro smaller(tree, n, m, depth) + ( (tree[n].Freq < tree[m].Freq) or + ((tree[n].Freq = tree[m].Freq) and (depth[n] <= depth[m])) ) +} + +{ =========================================================================== + Restore the heap property by moving down the tree starting at node k, + exchanging a node with the smallest of its two sons if necessary, stopping + when the heap property is re-established (each father smaller than its + two sons). } +{local} + +procedure pqdownheap(var s : deflate_state; + var tree : tree_type; { the tree to restore } + k : int); { node to move down } +var + v : int; + j : int; +begin + v := s.heap[k]; + j := k shl 1; { left son of k } + while (j <= s.heap_len) do + begin + { Set j to the smallest of the two sons: } + if (j < s.heap_len) and + {smaller(tree, s.heap[j+1], s.heap[j], s.depth)} + ( (tree[s.heap[j+1]].fc.Freq < tree[s.heap[j]].fc.Freq) or + ((tree[s.heap[j+1]].fc.Freq = tree[s.heap[j]].fc.Freq) and + (s.depth[s.heap[j+1]] <= s.depth[s.heap[j]])) ) then + begin + Inc(j); + end; + { Exit if v is smaller than both sons } + if {(smaller(tree, v, s.heap[j], s.depth))} + ( (tree[v].fc.Freq < tree[s.heap[j]].fc.Freq) or + ((tree[v].fc.Freq = tree[s.heap[j]].fc.Freq) and + (s.depth[v] <= s.depth[s.heap[j]])) ) then + break; + { Exchange v with the smallest son } + s.heap[k] := s.heap[j]; + k := j; + + { And continue down the tree, setting j to the left son of k } + j := j shl 1; + end; + s.heap[k] := v; +end; + +{ =========================================================================== + Compute the optimal bit lengths for a tree and update the total bit length + for the current block. + IN assertion: the fields freq and dad are set, heap[heap_max] and + above are the tree nodes sorted by increasing frequency. + OUT assertions: the field len is set to the optimal bit length, the + array bl_count contains the frequencies for each bit length. + The length opt_len is updated; static_len is also updated if stree is + not null. } + +{local} +procedure gen_bitlen(var s : deflate_state; + var desc : tree_desc); { the tree descriptor } +var + tree : tree_ptr; + max_code : int; + stree : tree_ptr; {const} + extra : pzIntfArray; {const} + base : int; + max_length : int; + h : int; { heap index } + n, m : int; { iterate over the tree elements } + bits : int; { bit length } + xbits : int; { extra bits } + f : ush; { frequency } + overflow : int; { number of elements with bit length too large } +begin + tree := desc.dyn_tree; + max_code := desc.max_code; + stree := desc.stat_desc^.static_tree; + extra := desc.stat_desc^.extra_bits; + base := desc.stat_desc^.extra_base; + max_length := desc.stat_desc^.max_length; + overflow := 0; + + for bits := 0 to MAX_BITS do + s.bl_count[bits] := 0; + + { In a first pass, compute the optimal bit lengths (which may + overflow in the case of the bit length tree). } + + tree^[s.heap[s.heap_max]].dl.Len := 0; { root of the heap } + + for h := s.heap_max+1 to HEAP_SIZE-1 do + begin + n := s.heap[h]; + bits := tree^[tree^[n].dl.Dad].dl.Len + 1; + if (bits > max_length) then + begin + bits := max_length; + Inc(overflow); + end; + tree^[n].dl.Len := ush(bits); + { We overwrite tree[n].dl.Dad which is no longer needed } + + if (n > max_code) then + continue; { not a leaf node } + + Inc(s.bl_count[bits]); + xbits := 0; + if (n >= base) then + xbits := extra^[n-base]; + f := tree^[n].fc.Freq; + Inc(s.opt_len, ulg(f) * (bits + xbits)); + if (stree <> NIL) then + Inc(s.static_len, ulg(f) * (stree^[n].dl.Len + xbits)); + end; + if (overflow = 0) then + exit; + {$ifdef DEBUG} + Tracev(^M'bit length overflow'); + {$endif} + { This happens for example on obj2 and pic of the Calgary corpus } + + { Find the first bit length which could increase: } + repeat + bits := max_length-1; + while (s.bl_count[bits] = 0) do + Dec(bits); + Dec(s.bl_count[bits]); { move one leaf down the tree } + Inc(s.bl_count[bits+1], 2); { move one overflow item as its brother } + Dec(s.bl_count[max_length]); + { The brother of the overflow item also moves one step up, + but this does not affect bl_count[max_length] } + + Dec(overflow, 2); + until (overflow <= 0); + + { Now recompute all bit lengths, scanning in increasing frequency. + h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + lengths instead of fixing only the wrong ones. This idea is taken + from 'ar' written by Haruhiko Okumura.) } + h := HEAP_SIZE; { Delphi3: compiler warning w/o this } + for bits := max_length downto 1 do + begin + n := s.bl_count[bits]; + while (n <> 0) do + begin + Dec(h); + m := s.heap[h]; + if (m > max_code) then + continue; + if (tree^[m].dl.Len <> unsigned(bits)) then + begin + {$ifdef DEBUG} + Trace('code '+IntToStr(m)+' bits '+IntToStr(tree^[m].dl.Len) + +'.'+IntToStr(bits)); + {$ENDIF} + Inc(s.opt_len, (long(bits) - long(tree^[m].dl.Len)) + * long(tree^[m].fc.Freq) ); + tree^[m].dl.Len := ush(bits); + end; + Dec(n); + end; + end; +end; + +{ =========================================================================== + Construct one Huffman tree and assigns the code bit strings and lengths. + Update the total bit length for the current block. + IN assertion: the field freq is set for all tree elements. + OUT assertions: the fields len and code are set to the optimal bit length + and corresponding code. The length opt_len is updated; static_len is + also updated if stree is not null. The field max_code is set. } + +{local} +procedure build_tree(var s : deflate_state; + var desc : tree_desc); { the tree descriptor } + +var + tree : tree_ptr; + stree : tree_ptr; {const} + elems : int; + n, m : int; { iterate over heap elements } + max_code : int; { largest code with non zero frequency } + node : int; { new node being created } +begin + tree := desc.dyn_tree; + stree := desc.stat_desc^.static_tree; + elems := desc.stat_desc^.elems; + max_code := -1; + + { Construct the initial heap, with least frequent element in + heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + heap[0] is not used. } + s.heap_len := 0; + s.heap_max := HEAP_SIZE; + + for n := 0 to elems-1 do + begin + if (tree^[n].fc.Freq <> 0) then + begin + max_code := n; + Inc(s.heap_len); + s.heap[s.heap_len] := n; + s.depth[n] := 0; + end + else + begin + tree^[n].dl.Len := 0; + end; + end; + + { The pkzip format requires that at least one distance code exists, + and that at least one bit should be sent even if there is only one + possible code. So to avoid special checks later on we force at least + two codes of non zero frequency. } + + while (s.heap_len < 2) do + begin + Inc(s.heap_len); + if (max_code < 2) then + begin + Inc(max_code); + s.heap[s.heap_len] := max_code; + node := max_code; + end + else + begin + s.heap[s.heap_len] := 0; + node := 0; + end; + tree^[node].fc.Freq := 1; + s.depth[node] := 0; + Dec(s.opt_len); + if (stree <> NIL) then + Dec(s.static_len, stree^[node].dl.Len); + { node is 0 or 1 so it does not have extra bits } + end; + desc.max_code := max_code; + + { The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + establish sub-heaps of increasing lengths: } + + for n := s.heap_len div 2 downto 1 do + pqdownheap(s, tree^, n); + + { Construct the Huffman tree by repeatedly combining the least two + frequent nodes. } + + node := elems; { next internal node of the tree } + repeat + {pqremove(s, tree, n);} { n := node of least frequency } + n := s.heap[SMALLEST]; + s.heap[SMALLEST] := s.heap[s.heap_len]; + Dec(s.heap_len); + pqdownheap(s, tree^, SMALLEST); + + m := s.heap[SMALLEST]; { m := node of next least frequency } + + Dec(s.heap_max); + s.heap[s.heap_max] := n; { keep the nodes sorted by frequency } + Dec(s.heap_max); + s.heap[s.heap_max] := m; + + { Create a new node father of n and m } + tree^[node].fc.Freq := tree^[n].fc.Freq + tree^[m].fc.Freq; + { maximum } + if (s.depth[n] >= s.depth[m]) then + s.depth[node] := uch (s.depth[n] + 1) + else + s.depth[node] := uch (s.depth[m] + 1); + + tree^[m].dl.Dad := ush(node); + tree^[n].dl.Dad := ush(node); +{$ifdef DUMP_BL_TREE} + if (tree = tree_ptr(@s.bl_tree)) then + begin + WriteLn(#13'node ',node,'(',tree^[node].fc.Freq,') sons ',n, + '(',tree^[n].fc.Freq,') ', m, '(',tree^[m].fc.Freq,')'); + end; +{$endif} + { and insert the new node in the heap } + s.heap[SMALLEST] := node; + Inc(node); + pqdownheap(s, tree^, SMALLEST); + + until (s.heap_len < 2); + + Dec(s.heap_max); + s.heap[s.heap_max] := s.heap[SMALLEST]; + + { At this point, the fields freq and dad are set. We can now + generate the bit lengths. } + + gen_bitlen(s, desc); + + { The field len is now set, we can generate the bit codes } + gen_codes (tree, max_code, s.bl_count); +end; + +{ =========================================================================== + Scan a literal or distance tree to determine the frequencies of the codes + in the bit length tree. } + +{local} +procedure scan_tree(var s : deflate_state; + var tree : array of ct_data; { the tree to be scanned } + max_code : int); { and its largest code of non zero frequency } +var + n : int; { iterates over all tree elements } + prevlen : int; { last emitted length } + curlen : int; { length of current code } + nextlen : int; { length of next code } + count : int; { repeat count of the current code } + max_count : int; { max repeat count } + min_count : int; { min repeat count } +begin + prevlen := -1; + nextlen := tree[0].dl.Len; + count := 0; + max_count := 7; + min_count := 4; + + if (nextlen = 0) then + begin + max_count := 138; + min_count := 3; + end; + tree[max_code+1].dl.Len := ush($ffff); { guard } + + for n := 0 to max_code do + begin + curlen := nextlen; + nextlen := tree[n+1].dl.Len; + Inc(count); + if (count < max_count) and (curlen = nextlen) then + continue + else + if (count < min_count) then + Inc(s.bl_tree[curlen].fc.Freq, count) + else + if (curlen <> 0) then + begin + if (curlen <> prevlen) then + Inc(s.bl_tree[curlen].fc.Freq); + Inc(s.bl_tree[REP_3_6].fc.Freq); + end + else + if (count <= 10) then + Inc(s.bl_tree[REPZ_3_10].fc.Freq) + else + Inc(s.bl_tree[REPZ_11_138].fc.Freq); + + count := 0; + prevlen := curlen; + if (nextlen = 0) then + begin + max_count := 138; + min_count := 3; + end + else + if (curlen = nextlen) then + begin + max_count := 6; + min_count := 3; + end + else + begin + max_count := 7; + min_count := 4; + end; + end; +end; + +{ =========================================================================== + Send a literal or distance tree in compressed form, using the codes in + bl_tree. } + +{local} +procedure send_tree(var s : deflate_state; + var tree : array of ct_data; { the tree to be scanned } + max_code : int); { and its largest code of non zero frequency } + +var + n : int; { iterates over all tree elements } + prevlen : int; { last emitted length } + curlen : int; { length of current code } + nextlen : int; { length of next code } + count : int; { repeat count of the current code } + max_count : int; { max repeat count } + min_count : int; { min repeat count } +begin + prevlen := -1; + nextlen := tree[0].dl.Len; + count := 0; + max_count := 7; + min_count := 4; + + { tree[max_code+1].dl.Len := -1; } { guard already set } + if (nextlen = 0) then + begin + max_count := 138; + min_count := 3; + end; + + for n := 0 to max_code do + begin + curlen := nextlen; + nextlen := tree[n+1].dl.Len; + Inc(count); + if (count < max_count) and (curlen = nextlen) then + continue + else + if (count < min_count) then + begin + repeat + {$ifdef DEBUG} + Tracevvv(#13'cd '+IntToStr(curlen)); + {$ENDIF} + send_bits(s, s.bl_tree[curlen].fc.Code, s.bl_tree[curlen].dl.Len); + Dec(count); + until (count = 0); + end + else + if (curlen <> 0) then + begin + if (curlen <> prevlen) then + begin + {$ifdef DEBUG} + Tracevvv(#13'cd '+IntToStr(curlen)); + {$ENDIF} + send_bits(s, s.bl_tree[curlen].fc.Code, s.bl_tree[curlen].dl.Len); + Dec(count); + end; + {$IFDEF DEBUG} + Assert((count >= 3) and (count <= 6), ' 3_6?'); + {$ENDIF} + {$ifdef DEBUG} + Tracevvv(#13'cd '+IntToStr(REP_3_6)); + {$ENDIF} + send_bits(s, s.bl_tree[REP_3_6].fc.Code, s.bl_tree[REP_3_6].dl.Len); + send_bits(s, count-3, 2); + end + else + if (count <= 10) then + begin + {$ifdef DEBUG} + Tracevvv(#13'cd '+IntToStr(REPZ_3_10)); + {$ENDIF} + send_bits(s, s.bl_tree[REPZ_3_10].fc.Code, s.bl_tree[REPZ_3_10].dl.Len); + send_bits(s, count-3, 3); + end + else + begin + {$ifdef DEBUG} + Tracevvv(#13'cd '+IntToStr(REPZ_11_138)); + {$ENDIF} + send_bits(s, s.bl_tree[REPZ_11_138].fc.Code, s.bl_tree[REPZ_11_138].dl.Len); + send_bits(s, count-11, 7); + end; + count := 0; + prevlen := curlen; + if (nextlen = 0) then + begin + max_count := 138; + min_count := 3; + end + else + if (curlen = nextlen) then + begin + max_count := 6; + min_count := 3; + end + else + begin + max_count := 7; + min_count := 4; + end; + end; +end; + +{ =========================================================================== + Construct the Huffman tree for the bit lengths and return the index in + bl_order of the last bit length code to send. } + +{local} +function build_bl_tree(var s : deflate_state) : int; +var + max_blindex : int; { index of last bit length code of non zero freq } +begin + { Determine the bit length frequencies for literal and distance trees } + scan_tree(s, s.dyn_ltree, s.l_desc.max_code); + scan_tree(s, s.dyn_dtree, s.d_desc.max_code); + + { Build the bit length tree: } + build_tree(s, s.bl_desc); + { opt_len now includes the length of the tree representations, except + the lengths of the bit lengths codes and the 5+5+4 bits for the counts. } + + { Determine the number of bit length codes to send. The pkzip format + requires that at least 4 bit length codes be sent. (appnote.txt says + 3 but the actual value used is 4.) } + + for max_blindex := BL_CODES-1 downto 3 do + begin + if (s.bl_tree[bl_order[max_blindex]].dl.Len <> 0) then + break; + end; + { Update opt_len to include the bit length tree and counts } + Inc(s.opt_len, 3*(max_blindex+1) + 5+5+4); + {$ifdef DEBUG} + Tracev(^M'dyn trees: dyn %ld, stat %ld {s.opt_len, s.static_len}'); + {$ENDIF} + + build_bl_tree := max_blindex; +end; + +{ =========================================================================== + Send the header for a block using dynamic Huffman trees: the counts, the + lengths of the bit length codes, the literal tree and the distance tree. + IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. } + +{local} +procedure send_all_trees(var s : deflate_state; + lcodes : int; + dcodes : int; + blcodes : int); { number of codes for each tree } +var + rank : int; { index in bl_order } +begin + {$IFDEF DEBUG} + Assert ((lcodes >= 257) and (dcodes >= 1) and (blcodes >= 4), + 'not enough codes'); + Assert ((lcodes <= L_CODES) and (dcodes <= D_CODES) + and (blcodes <= BL_CODES), 'too many codes'); + Tracev(^M'bl counts: '); + {$ENDIF} + send_bits(s, lcodes-257, 5); { not +255 as stated in appnote.txt } + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); { not -3 as stated in appnote.txt } + for rank := 0 to blcodes-1 do + begin + {$ifdef DEBUG} + Tracev(^M'bl code '+IntToStr(bl_order[rank])); + {$ENDIF} + send_bits(s, s.bl_tree[bl_order[rank]].dl.Len, 3); + end; + {$ifdef DEBUG} + Tracev(^M'bl tree: sent '+IntToStr(s.bits_sent)); + {$ENDIF} + + send_tree(s, s.dyn_ltree, lcodes-1); { literal tree } + {$ifdef DEBUG} + Tracev(^M'lit tree: sent '+IntToStr(s.bits_sent)); + {$ENDIF} + + send_tree(s, s.dyn_dtree, dcodes-1); { distance tree } + {$ifdef DEBUG} + Tracev(^M'dist tree: sent '+IntToStr(s.bits_sent)); + {$ENDIF} +end; + +{ =========================================================================== + Flush the bit buffer and align the output on a byte boundary } + +{local} +procedure bi_windup(var s : deflate_state); +begin + if (s.bi_valid > 8) then + begin + {put_short(s, s.bi_buf);} + s.pending_buf^[s.pending] := uch(s.bi_buf and $ff); + Inc(s.pending); + s.pending_buf^[s.pending] := uch(ush(s.bi_buf) shr 8);; + Inc(s.pending); + end + else + if (s.bi_valid > 0) then + begin + {put_byte(s, (Byte)s^.bi_buf);} + s.pending_buf^[s.pending] := Byte(s.bi_buf); + Inc(s.pending); + end; + s.bi_buf := 0; + s.bi_valid := 0; +{$ifdef DEBUG} + s.bits_sent := (s.bits_sent+7) and (not 7); +{$endif} +end; + +{ =========================================================================== + Copy a stored block, storing first the length and its + one's complement if requested. } + +{local} +procedure copy_block(var s : deflate_state; + buf : pcharf; { the input data } + len : unsigned; { its length } + header : boolean); { true if block header must be written } +begin + bi_windup(s); { align on byte boundary } + s.last_eob_len := 8; { enough lookahead for inflate } + + if (header) then + begin + {put_short(s, (ush)len);} + s.pending_buf^[s.pending] := uch(ush(len) and $ff); + Inc(s.pending); + s.pending_buf^[s.pending] := uch(ush(len) shr 8);; + Inc(s.pending); + {put_short(s, (ush)~len);} + s.pending_buf^[s.pending] := uch(ush(not len) and $ff); + Inc(s.pending); + s.pending_buf^[s.pending] := uch(ush(not len) shr 8);; + Inc(s.pending); + +{$ifdef DEBUG} + Inc(s.bits_sent, 2*16); +{$endif} + end; +{$ifdef DEBUG} + Inc(s.bits_sent, ulg(len shl 3)); +{$endif} + while (len <> 0) do + begin + Dec(len); + {put_byte(s, *buf++);} + s.pending_buf^[s.pending] := buf^; + Inc(buf); + Inc(s.pending); + end; +end; + + +{ =========================================================================== + Send a stored block } + +procedure _tr_stored_block(var s : deflate_state; + buf : pcharf; { input block } + stored_len : ulg; { length of input block } + eof : boolean); { true if this is the last block for a file } + +begin + send_bits(s, (STORED_BLOCK shl 1)+ord(eof), 3); { send block type } + s.compressed_len := (s.compressed_len + 3 + 7) and ulg(not Long(7)); + Inc(s.compressed_len, (stored_len + 4) shl 3); + + copy_block(s, buf, unsigned(stored_len), TRUE); { with header } +end; + +{ =========================================================================== + Flush the bit buffer, keeping at most 7 bits in it. } + +{local} +procedure bi_flush(var s : deflate_state); +begin + if (s.bi_valid = 16) then + begin + {put_short(s, s.bi_buf);} + s.pending_buf^[s.pending] := uch(s.bi_buf and $ff); + Inc(s.pending); + s.pending_buf^[s.pending] := uch(ush(s.bi_buf) shr 8);; + Inc(s.pending); + + s.bi_buf := 0; + s.bi_valid := 0; + end + else + if (s.bi_valid >= 8) then + begin + {put_byte(s, (Byte)s^.bi_buf);} + s.pending_buf^[s.pending] := Byte(s.bi_buf); + Inc(s.pending); + + s.bi_buf := s.bi_buf shr 8; + Dec(s.bi_valid, 8); + end; +end; + + +{ =========================================================================== + Send one empty static block to give enough lookahead for inflate. + This takes 10 bits, of which 7 may remain in the bit buffer. + The current inflate code requires 9 bits of lookahead. If the + last two codes for the previous block (real code plus EOB) were coded + on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + the last real code. In this case we send two empty static blocks instead + of one. (There are no problems if the previous block is stored or fixed.) + To simplify the code, we assume the worst case of last real code encoded + on one bit only. } + +procedure _tr_align(var s : deflate_state); +begin + send_bits(s, STATIC_TREES shl 1, 3); + {$ifdef DEBUG} + Tracevvv(#13'cd '+IntToStr(END_BLOCK)); + {$ENDIF} + send_bits(s, static_ltree[END_BLOCK].fc.Code, static_ltree[END_BLOCK].dl.Len); + Inc(s.compressed_len, Long(10)); { 3 for block type, 7 for EOB } + bi_flush(s); + { Of the 10 bits for the empty block, we have already sent + (10 - bi_valid) bits. The lookahead for the last real code (before + the EOB of the previous block) was thus at least one plus the length + of the EOB plus what we have just sent of the empty static block. } + if (1 + s.last_eob_len + 10 - s.bi_valid < 9) then + begin + send_bits(s, STATIC_TREES shl 1, 3); + {$ifdef DEBUG} + Tracevvv(#13'cd '+IntToStr(END_BLOCK)); + {$ENDIF} + send_bits(s, static_ltree[END_BLOCK].fc.Code, static_ltree[END_BLOCK].dl.Len); + Inc(s.compressed_len, Long(10)); + bi_flush(s); + end; + s.last_eob_len := 7; +end; + +{ =========================================================================== + Set the data type to ASCII or BINARY, using a crude approximation: + binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + IN assertion: the fields freq of dyn_ltree are set and the total of all + frequencies does not exceed 64K (to fit in an int on 16 bit machines). } + +{local} +procedure set_data_type(var s : deflate_state); +var + n : int; + ascii_freq : unsigned; + bin_freq : unsigned; +begin + n := 0; + ascii_freq := 0; + bin_freq := 0; + + while (n < 7) do + begin + Inc(bin_freq, s.dyn_ltree[n].fc.Freq); + Inc(n); + end; + while (n < 128) do + begin + Inc(ascii_freq, s.dyn_ltree[n].fc.Freq); + Inc(n); + end; + while (n < LITERALS) do + begin + Inc(bin_freq, s.dyn_ltree[n].fc.Freq); + Inc(n); + end; + if (bin_freq > (ascii_freq shr 2)) then + s.data_type := Byte(Z_BINARY) + else + s.data_type := Byte(Z_ASCII); +end; + +{ =========================================================================== + Send the block data compressed using the given Huffman trees } + +{local} +procedure compress_block(var s : deflate_state; + var ltree : array of ct_data; { literal tree } + var dtree : array of ct_data); { distance tree } +var + dist : unsigned; { distance of matched string } + lc : int; { match length or unmatched char (if dist == 0) } + lx : unsigned; { running index in l_buf } + code : unsigned; { the code to send } + extra : int; { number of extra bits to send } +begin + lx := 0; + if (s.last_lit <> 0) then + repeat + dist := s.d_buf^[lx]; + lc := s.l_buf^[lx]; + Inc(lx); + if (dist = 0) then + begin + { send a literal byte } + {$ifdef DEBUG} + Tracevvv(#13'cd '+IntToStr(lc)); + Tracecv((lc > 31) and (lc < 128), ' '+AnsiChar(lc)+' '); + {$ENDIF} + send_bits(s, ltree[lc].fc.Code, ltree[lc].dl.Len); + end + else + begin + { Here, lc is the match length - MIN_MATCH } + code := _length_code[lc]; + { send the length code } + {$ifdef DEBUG} + Tracevvv(#13'cd '+IntToStr(code+LITERALS+1)); + {$ENDIF} + send_bits(s, ltree[code+LITERALS+1].fc.Code, ltree[code+LITERALS+1].dl.Len); + extra := extra_lbits[code]; + if (extra <> 0) then + begin + Dec(lc, base_length[code]); + send_bits(s, lc, extra); { send the extra length bits } + end; + Dec(dist); { dist is now the match distance - 1 } + {code := d_code(dist);} + if (dist < 256) then + code := _dist_code[dist] + else + code := _dist_code[256+(dist shr 7)]; + + {$IFDEF DEBUG} + Assert (code < D_CODES, 'bad d_code'); + {$ENDIF} + + { send the distance code } + {$ifdef DEBUG} + Tracevvv(#13'cd '+IntToStr(code)); + {$ENDIF} + send_bits(s, dtree[code].fc.Code, dtree[code].dl.Len); + extra := extra_dbits[code]; + if (extra <> 0) then + begin + Dec(dist, base_dist[code]); + send_bits(s, dist, extra); { send the extra distance bits } + end; + end; { literal or match pair ? } + + { Check that the overlay between pending_buf and d_buf+l_buf is ok: } + {$IFDEF DEBUG} + Assert(s.pending < s.lit_bufsize + 2*lx, 'pendingBuf overflow'); + {$ENDIF} + until (lx >= s.last_lit); + + {$ifdef DEBUG} + Tracevvv(#13'cd '+IntToStr(END_BLOCK)); + {$ENDIF} + send_bits(s, ltree[END_BLOCK].fc.Code, ltree[END_BLOCK].dl.Len); + s.last_eob_len := ltree[END_BLOCK].dl.Len; +end; + + +{ =========================================================================== + Determine the best encoding for the current block: dynamic trees, static + trees or store, and output the encoded block to the zip file. This function + returns the total compressed length for the file so far. } + +function _tr_flush_block (var s : deflate_state; + buf : pcharf; { input block, or NULL if too old } + stored_len : ulg; { length of input block } + eof : boolean) : ulg; { true if this is the last block for a file } +var + opt_lenb, static_lenb : ulg; { opt_len and static_len in bytes } + max_blindex : int; { index of last bit length code of non zero freq } +begin + max_blindex := 0; + + { Build the Huffman trees unless a stored block is forced } + if (s.level > 0) then + begin + { Check if the file is ascii or binary } + if (s.data_type = Z_UNKNOWN) then + set_data_type(s); + + { Construct the literal and distance trees } + build_tree(s, s.l_desc); + {$ifdef DEBUG} + Tracev(^M'lit data: dyn %ld, stat %ld {s.opt_len, s.static_len}'); + {$ENDIF} + + build_tree(s, s.d_desc); + {$ifdef DEBUG} + Tracev(^M'dist data: dyn %ld, stat %ld {s.opt_len, s.static_len}'); + {$ENDIF} + { At this point, opt_len and static_len are the total bit lengths of + the compressed block data, excluding the tree representations. } + + { Build the bit length tree for the above two trees, and get the index + in bl_order of the last bit length code to send. } + max_blindex := build_bl_tree(s); + + { Determine the best encoding. Compute first the block length in bytes} + opt_lenb := (s.opt_len+3+7) shr 3; + static_lenb := (s.static_len+3+7) shr 3; + + {$ifdef DEBUG} + Tracev(^M'opt %lu(%lu) stat %lu(%lu) stored %lu lit %u '+ + '{opt_lenb, s.opt_len, static_lenb, s.static_len, stored_len,'+ + 's.last_lit}'); + {$ENDIF} + + if (static_lenb <= opt_lenb) then + opt_lenb := static_lenb; + + end + else + begin + {$IFDEF DEBUG} + Assert(buf <> pcharf(NIL), 'lost buf'); + {$ENDIF} + static_lenb := stored_len + 5; + opt_lenb := static_lenb; { force a stored block } + end; + + { If compression failed and this is the first and last block, + and if the .zip file can be seeked (to rewrite the local header), + the whole file is transformed into a stored file: } + +{$ifdef STORED_FILE_OK} +{$ifdef FORCE_STORED_FILE} + if eof and (s.compressed_len = Long(0)) then + begin { force stored file } +{$else} + if (stored_len <= opt_lenb) and eof and (s.compressed_len=Long(0)) + and seekable()) do + begin +{$endif} + { Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: } + if (buf = pcharf(0)) then + error ('block vanished'); + + copy_block(buf, unsigned(stored_len), 0); { without header } + s.compressed_len := stored_len shl 3; + s.method := STORED; + end + else +{$endif} { STORED_FILE_OK } + +{$ifdef FORCE_STORED} + if (buf <> pcharf(0)) then + begin { force stored block } +{$else} + if (stored_len+4 <= opt_lenb) and (buf <> pcharf(0)) then + begin + { 4: two words for the lengths } +{$endif} + { The test buf <> NULL is only necessary if LIT_BUFSIZE > WSIZE. + Otherwise we can't have processed more than WSIZE input bytes since + the last block flush, because compression would have been + successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + transform a block into a stored block. } + + _tr_stored_block(s, buf, stored_len, eof); + +{$ifdef FORCE_STATIC} + end + else + if (static_lenb >= 0) then + begin { force static trees } +{$else} + end + else + if (static_lenb = opt_lenb) then + begin +{$endif} + send_bits(s, (STATIC_TREES shl 1)+ord(eof), 3); + compress_block(s, static_ltree, static_dtree); + Inc(s.compressed_len, 3 + s.static_len); + end + else + begin + send_bits(s, (DYN_TREES shl 1)+ord(eof), 3); + send_all_trees(s, s.l_desc.max_code+1, s.d_desc.max_code+1, + max_blindex+1); + compress_block(s, s.dyn_ltree, s.dyn_dtree); + Inc(s.compressed_len, 3 + s.opt_len); + end; + {$ifdef DEBUG} + Assert (s.compressed_len = s.bits_sent, 'bad compressed size'); + {$ENDIF} + init_block(s); + + if (eof) then + begin + bi_windup(s); + Inc(s.compressed_len, 7); { align on byte boundary } + end; + {$ifdef DEBUG} + Tracev(#13'comprlen %lu(%lu) {s.compressed_len shr 3,'+ + 's.compressed_len-7*ord(eof)}'); + {$ENDIF} + + _tr_flush_block := s.compressed_len shr 3; +end; + + +{ =========================================================================== + Save the match info and tally the frequency counts. Return true if + the current block must be flushed. } + +function _tr_tally (var s : deflate_state; + dist : unsigned; { distance of matched string } + lc : unsigned) : boolean; { match length-MIN_MATCH or unmatched char (if dist=0) } +var + {$IFDEF DEBUG} + MAX_DIST : ush; + {$ENDIF} + code : ush; +{$ifdef TRUNCATE_BLOCK} +var + out_length : ulg; + in_length : ulg; + dcode : int; +{$endif} +begin + s.d_buf^[s.last_lit] := ush(dist); + s.l_buf^[s.last_lit] := uch(lc); + Inc(s.last_lit); + if (dist = 0) then + begin + { lc is the unmatched char } + Inc(s.dyn_ltree[lc].fc.Freq); + end + else + begin + Inc(s.matches); + { Here, lc is the match length - MIN_MATCH } + Dec(dist); { dist := match distance - 1 } + + {macro d_code(dist)} + if (dist) < 256 then + code := _dist_code[dist] + else + code := _dist_code[256+(dist shr 7)]; + {$IFDEF DEBUG} +{macro MAX_DIST(s) <=> ((s)^.w_size-MIN_LOOKAHEAD) + In order to simplify the code, particularly on 16 bit machines, match + distances are limited to MAX_DIST instead of WSIZE. } + MAX_DIST := ush(s.w_size-MIN_LOOKAHEAD); + Assert((dist < ush(MAX_DIST)) and + (ush(lc) <= ush(MAX_MATCH-MIN_MATCH)) and + (ush(code) < ush(D_CODES)), '_tr_tally: bad match'); + {$ENDIF} + Inc(s.dyn_ltree[_length_code[lc]+LITERALS+1].fc.Freq); + {s.dyn_dtree[d_code(dist)].Freq++;} + Inc(s.dyn_dtree[code].fc.Freq); + end; + +{$ifdef TRUNCATE_BLOCK} + { Try to guess if it is profitable to stop the current block here } + if (s.last_lit and $1fff = 0) and (s.level > 2) then + begin + { Compute an upper bound for the compressed length } + out_length := ulg(s.last_lit)*Long(8); + in_length := ulg(long(s.strstart) - s.block_start); + for dcode := 0 to D_CODES-1 do + begin + Inc(out_length, ulg(s.dyn_dtree[dcode].fc.Freq * + (Long(5)+extra_dbits[dcode])) ); + end; + out_length := out_length shr 3; + {$ifdef DEBUG} + Tracev(^M'last_lit %u, in %ld, out ~%ld(%ld%%) '); + { s.last_lit, in_length, out_length, + Long(100) - out_length*Long(100) div in_length)); } + {$ENDIF} + if (s.matches < s.last_lit div 2) and (out_length < in_length div 2) then + begin + _tr_tally := TRUE; + exit; + end; + end; +{$endif} + _tr_tally := (s.last_lit = s.lit_bufsize-1); + { We avoid equality with lit_bufsize because of wraparound at 64K + on 16 bit machines and because stored blocks are restricted to + 64K-1 bytes. } +end; + +end. \ No newline at end of file diff --git a/resources/libraries/deskew/Imaging/ZLib/imzconf.inc b/resources/libraries/deskew/Imaging/ZLib/imzconf.inc new file mode 100755 index 0000000..bd518a5 --- /dev/null +++ b/resources/libraries/deskew/Imaging/ZLib/imzconf.inc @@ -0,0 +1,25 @@ +{ -------------------------------------------------------------------- } + +{$DEFINE MAX_MATCH_IS_258} + +{ Compile with -DMAXSEG_64K if the alloc function cannot allocate more + than 64k bytes at a time (needed on systems with 16-bit int). } + +{$UNDEF MAXSEG_64K} +{$DEFINE UNALIGNED_OK} { requires SizeOf(ush) = 2 ! } +{$UNDEF DYNAMIC_CRC_TABLE} +{$UNDEF FASTEST} +{$DEFINE Use32} +{$DEFINE patch112} { apply patch from the zlib home page } + +{$IFDEF FPC} + {$MODE DELPHI} +{$ENDIF} + +{$UNDEF DEBUG} // for Delphi 2007 in DEBUG mode + +{$RANGECHECKS OFF} +{$OVERFLOWCHECKS OFF} +{ -------------------------------------------------------------------- } + + diff --git a/resources/libraries/deskew/Imaging/ZLib/imzdeflate.pas b/resources/libraries/deskew/Imaging/ZLib/imzdeflate.pas new file mode 100755 index 0000000..dc5e96f --- /dev/null +++ b/resources/libraries/deskew/Imaging/ZLib/imzdeflate.pas @@ -0,0 +1,2129 @@ +Unit imzdeflate; + +{ Orginal: deflate.h -- internal compression state + deflate.c -- compress data using the deflation algorithm + Copyright (C) 1995-1996 Jean-loup Gailly. + + Pascal tranlastion + Copyright (C) 1998 by Jacques Nomssi Nzali + For conditions of distribution and use, see copyright notice in readme.txt +} + + +{ ALGORITHM + + The "deflation" process depends on being able to identify portions + of the input text which are identical to earlier input (within a + sliding window trailing behind the input currently being processed). + + The most straightforward technique turns out to be the fastest for + most input files: try all possible matches and select the longest. + The key feature of this algorithm is that insertions into the string + dictionary are very simple and thus fast, and deletions are avoided + completely. Insertions are performed at each input character, whereas + string matches are performed only when the previous match ends. So it + is preferable to spend more time in matches to allow very fast string + insertions and avoid deletions. The matching algorithm for small + strings is inspired from that of Rabin & Karp. A brute force approach + is used to find longer strings when a small match has been found. + A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + (by Leonid Broukhis). + A previous version of this file used a more sophisticated algorithm + (by Fiala and Greene) which is guaranteed to run in linear amortized + time, but has a larger average cost, uses more memory and is patented. + However the F&G algorithm may be faster for some highly redundant + files if the parameter max_chain_length (described below) is too large. + + ACKNOWLEDGEMENTS + + The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + I found it in 'freeze' written by Leonid Broukhis. + Thanks to many people for bug reports and testing. + + REFERENCES + + Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + + A description of the Rabin and Karp algorithm is given in the book + "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + + Fiala,E.R., and Greene,D.H. + Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595} + +interface + +{$I imzconf.inc} + +uses + imzutil, impaszlib; + + +function deflateInit_(strm : z_streamp; + level : int; + const version : AnsiString; + stream_size : int) : int; + + +function deflateInit (var strm : z_stream; level : int) : int; + +{ Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). } + + +{EXPORT} +function deflate (var strm : z_stream; flush : int) : int; + +{ Performs one or both of the following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + If the parameter flush is set to Z_PARTIAL_FLUSH, the current compression + block is terminated and flushed to the output buffer so that the + decompressor can get all input data available so far. For method 9, a future + variant on method 8, the current block will be flushed but not terminated. + Z_SYNC_FLUSH has the same effect as partial flush except that the compressed + output is byte aligned (the compressor can clear its internal bit buffer) + and the current block is always terminated; this can be useful if the + compressor has to be restarted from scratch after an interruption (in which + case the internal state of the compressor may be lost). + If flush is set to Z_FULL_FLUSH, the compression block is terminated, a + special marker is output and the compression dictionary is discarded; this + is useful to allow the decompressor to synchronize if one compressed block + has been damaged (see inflateSync below). Flushing degrades compression and + so should be used only when necessary. Using Z_FULL_FLUSH too often can + seriously degrade the compression. If deflate returns with avail_out == 0, + this function must be called again with the same value of the flush + parameter and more output space (updated avail_out), until the flush is + complete (deflate returns with non-zero avail_out). + + If the parameter flush is set to Z_FINISH, all pending input is processed, + all pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + 0.1% larger than avail_in plus 12 bytes. If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() may update data_type if it can make a good guess about + the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible. } + + +function deflateEnd (var strm : z_stream) : int; + +{ All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). } + + + + + { Advanced functions } + +{ The following functions are needed only in some special applications. } + + +{EXPORT} +function deflateInit2 (var strm : z_stream; + level : int; + method : int; + windowBits : int; + memLevel : int; + strategy : int) : int; + +{ This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. (Method 9 will allow a 64K history buffer and + partial block flushes.) + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library (the value 16 will be allowed for method 9). Larger + values of this parameter result in better compression at the expense of + memory usage. The default value is 15 if deflateInit is used instead. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match). Filtered data consists mostly of small values with a + somewhat random distribution. In this case, the compression algorithm is + tuned to compress them better. The effect of Z_FILTERED is to force more + Huffman coding and less string matching; it is somewhat intermediate + between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects + the compression ratio but not the correctness of the compressed output even + if it is not set appropriately. + + If next_in is not null, the library will use this buffer to hold also + some history information; the buffer must either hold the entire input + data, or have at least 1<<(windowBits+1) bytes and be writable. If next_in + is null, the library will allocate its own history buffer (and leave next_in + null). next_out need not be provided here but must be provided by the + application for the next call of deflate(). + + If the history buffer is provided by the application, next_in must + must never be changed by the application since the compressor maintains + information inside this buffer from call to call; the application + must provide more input only by increasing avail_in. next_in is always + reset by the library in this case. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was + not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as + an invalid method). msg is set to null if there is no error message. + deflateInit2 does not perform any compression: this will be done by + deflate(). } + + +{EXPORT} +function deflateSetDictionary (var strm : z_stream; + dictionary : pBytef; {const bytes} + dictLength : uint) : int; + +{ Initializes the compression dictionary (history buffer) from the given + byte sequence without producing any compressed output. This function must + be called immediately after deflateInit or deflateInit2, before any call + of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and + can be predicted with good accuracy; the data can then be compressed better + than with the default empty dictionary. In this version of the library, + only the last 32K bytes of the dictionary are used. + Upon return of this function, strm->adler is set to the Adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The Adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state + is inconsistent (for example if deflate has already been called for this + stream). deflateSetDictionary does not perform any compression: this will + be done by deflate(). } + +{EXPORT} +function deflateCopy (dest : z_streamp; + source : z_streamp) : int; + +{ Sets the destination stream as a complete copy of the source stream. If + the source stream is using an application-supplied history buffer, a new + buffer is allocated for the destination stream. The compressed output + buffer is always application-supplied. It's the responsibility of the + application to provide the correct values of next_out and avail_out for the + next call of deflate. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. } + +{EXPORT} +function deflateReset (var strm : z_stream) : int; + +{ This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NIL). } + + +{EXPORT} +function deflateParams (var strm : z_stream; level : int; strategy : int) : int; + +{ Dynamically update the compression level and compression strategy. + This can be used to switch between compression and straight copy of + the input data, or to switch to a different kind of input data requiring + a different strategy. If the compression level is changed, the input + available so far is compressed with the old level (and may be flushed); + the new level will take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. } + + +const + deflate_copyright : string = ' deflate 1.1.2 Copyright 1995-1998 Jean-loup Gailly '; + +{ If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. } + +implementation + +uses + imtrees, imadler; + +{ =========================================================================== + Function prototypes. } + +type + block_state = ( + need_more, { block not completed, need more input or more output } + block_done, { block flush performed } + finish_started, { finish started, need only more output at next deflate } + finish_done); { finish done, accept no more input or output } + +{ Compression function. Returns the block state after the call. } +type + compress_func = function(var s : deflate_state; flush : int) : block_state; + +{local} +procedure fill_window(var s : deflate_state); forward; +{local} +function deflate_stored(var s : deflate_state; flush : int) : block_state; forward; +{local} +function deflate_fast(var s : deflate_state; flush : int) : block_state; forward; +{local} +function deflate_slow(var s : deflate_state; flush : int) : block_state; forward; +{local} +procedure lm_init(var s : deflate_state); forward; + +{local} +procedure putShortMSB(var s : deflate_state; b : uInt); forward; +{local} +procedure flush_pending (var strm : z_stream); forward; +{local} +function read_buf(strm : z_streamp; + buf : pBytef; + size : unsigned) : int; forward; +{$ifdef ASMV} +procedure match_init; { asm code initialization } +function longest_match(var deflate_state; cur_match : IPos) : uInt; forward; +{$else} +{local} +function longest_match(var s : deflate_state; cur_match : IPos) : uInt; + forward; +{$endif} + +{$ifdef DEBUG} +{local} +procedure check_match(var s : deflate_state; + start, match : IPos; + length : int); forward; +{$endif} + +{ ========================================================================== + local data } + +const + ZNIL = 0; +{ Tail of hash chains } + +const + TOO_FAR = 4096; +{ Matches of length 3 are discarded if their distance exceeds TOO_FAR } + +const + MIN_LOOKAHEAD = (MAX_MATCH+MIN_MATCH+1); +{ Minimum amount of lookahead, except at the end of the input file. + See deflate.c for comments about the MIN_MATCH+1. } + +{macro MAX_DIST(var s : deflate_state) : uInt; +begin + MAX_DIST := (s.w_size - MIN_LOOKAHEAD); +end; + In order to simplify the code, particularly on 16 bit machines, match + distances are limited to MAX_DIST instead of WSIZE. } + + +{ Values for max_lazy_match, good_match and max_chain_length, depending on + the desired pack level (0..9). The values given below have been tuned to + exclude worst case performance for pathological files. Better values may be + found for specific files. } + +type + config = record + good_length : ush; { reduce lazy search above this match length } + max_lazy : ush; { do not perform lazy search above this match length } + nice_length : ush; { quit search above this match length } + max_chain : ush; + func : compress_func; + end; + +{local} +const + configuration_table : array[0..10-1] of config = ( +{ good lazy nice chain } +{0} (good_length:0; max_lazy:0; nice_length:0; max_chain:0; func:deflate_stored), { store only } +{1} (good_length:4; max_lazy:4; nice_length:8; max_chain:4; func:deflate_fast), { maximum speed, no lazy matches } +{2} (good_length:4; max_lazy:5; nice_length:16; max_chain:8; func:deflate_fast), +{3} (good_length:4; max_lazy:6; nice_length:32; max_chain:32; func:deflate_fast), + +{4} (good_length:4; max_lazy:4; nice_length:16; max_chain:16; func:deflate_slow), { lazy matches } +{5} (good_length:8; max_lazy:16; nice_length:32; max_chain:32; func:deflate_slow), +{6} (good_length:8; max_lazy:16; nice_length:128; max_chain:128; func:deflate_slow), +{7} (good_length:8; max_lazy:32; nice_length:128; max_chain:256; func:deflate_slow), +{8} (good_length:32; max_lazy:128; nice_length:258; max_chain:1024; func:deflate_slow), +{9} (good_length:32; max_lazy:258; nice_length:258; max_chain:4096; func:deflate_slow)); { maximum compression } + +{ Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + For deflate_fast() (levels <= 3) good is ignored and lazy has a different + meaning. } + +const + EQUAL = 0; +{ result of memcmp for equal strings } + +{ ========================================================================== + Update a hash value with the given input byte + IN assertion: all calls to to UPDATE_HASH are made with consecutive + input characters, so that a running hash key can be computed from the + previous key instead of complete recalculation each time. + +macro UPDATE_HASH(s,h,c) + h := (( (h) shl s^.hash_shift) xor (c)) and s^.hash_mask; +} + +{ =========================================================================== + Insert string str in the dictionary and set match_head to the previous head + of the hash chain (the most recent string with same hash key). Return + the previous length of the hash chain. + If this file is compiled with -DFASTEST, the compression level is forced + to 1, and no hash chains are maintained. + IN assertion: all calls to to INSERT_STRING are made with consecutive + input characters and the first MIN_MATCH bytes of str are valid + (except for the last MIN_MATCH-1 bytes of the input file). } + +procedure INSERT_STRING(var s : deflate_state; + str : uInt; + var match_head : IPos); +begin +{$ifdef FASTEST} + {UPDATE_HASH(s, s.ins_h, s.window[(str) + (MIN_MATCH-1)])} + s.ins_h := ((s.ins_h shl s.hash_shift) xor + (s.window^[(str) + (MIN_MATCH-1)])) and s.hash_mask; + match_head := s.head[s.ins_h] + s.head[s.ins_h] := Pos(str); +{$else} + {UPDATE_HASH(s, s.ins_h, s.window[(str) + (MIN_MATCH-1)])} + s.ins_h := ((s.ins_h shl s.hash_shift) xor + (s.window^[(str) + (MIN_MATCH-1)])) and s.hash_mask; + + match_head := s.head^[s.ins_h]; + s.prev^[(str) and s.w_mask] := match_head; + s.head^[s.ins_h] := Pos(str); +{$endif} +end; + +{ ========================================================================= + Initialize the hash table (avoiding 64K overflow for 16 bit systems). + prev[] will be initialized on the fly. + +macro CLEAR_HASH(s) + s^.head[s^.hash_size-1] := ZNIL; + zmemzero(pBytef(s^.head), unsigned(s^.hash_size-1)*sizeof(s^.head^[0])); +} + +{ ======================================================================== } + +function deflateInit2_(var strm : z_stream; + level : int; + method : int; + windowBits : int; + memLevel : int; + strategy : int; + const version : AnsiString; + stream_size : int) : int; +var + s : deflate_state_ptr; + noheader : int; + + overlay : pushfArray; + { We overlay pending_buf and d_buf+l_buf. This works since the average + output size for (length,distance) codes is <= 24 bits. } +begin + noheader := 0; + if (version = '') or (version[1] <> ZLIB_VERSION[1]) or + (stream_size <> sizeof(z_stream)) then + begin + deflateInit2_ := Z_VERSION_ERROR; + exit; + end; + { + if (strm = Z_NULL) then + begin + deflateInit2_ := Z_STREAM_ERROR; + exit; + end; + } + { SetLength(strm.msg, 255); } + strm.msg := ''; + if not Assigned(strm.zalloc) then + begin + {$IFDEF FPC} strm.zalloc := @zcalloc; {$ELSE} + strm.zalloc := zcalloc; + {$ENDIF} + strm.opaque := voidpf(0); + end; + if not Assigned(strm.zfree) then + {$IFDEF FPC} strm.zfree := @zcfree; {$ELSE} + strm.zfree := zcfree; + {$ENDIF} + + if (level = Z_DEFAULT_COMPRESSION) then + level := 6; +{$ifdef FASTEST} + level := 1; +{$endif} + + if (windowBits < 0) then { undocumented feature: suppress zlib header } + begin + noheader := 1; + windowBits := -windowBits; + end; + if (memLevel < 1) or (memLevel > MAX_MEM_LEVEL) or (method <> Z_DEFLATED) + or (windowBits < 8) or (windowBits > 15) or (level < 0) + or (level > 9) or (strategy < 0) or (strategy > Z_HUFFMAN_ONLY) then + begin + deflateInit2_ := Z_STREAM_ERROR; + exit; + end; + + s := deflate_state_ptr (ZALLOC(strm, 1, sizeof(deflate_state))); + if (s = Z_NULL) then + begin + deflateInit2_ := Z_MEM_ERROR; + exit; + end; + strm.state := pInternal_state(s); + s^.strm := @strm; + + s^.noheader := noheader; + s^.w_bits := windowBits; + s^.w_size := 1 shl s^.w_bits; + s^.w_mask := s^.w_size - 1; + + s^.hash_bits := memLevel + 7; + s^.hash_size := 1 shl s^.hash_bits; + s^.hash_mask := s^.hash_size - 1; + s^.hash_shift := ((s^.hash_bits+MIN_MATCH-1) div MIN_MATCH); + + s^.window := pzByteArray (ZALLOC(strm, s^.w_size, 2*sizeof(Byte))); + s^.prev := pzPosfArray (ZALLOC(strm, s^.w_size, sizeof(Pos))); + s^.head := pzPosfArray (ZALLOC(strm, s^.hash_size, sizeof(Pos))); + + s^.lit_bufsize := 1 shl (memLevel + 6); { 16K elements by default } + + overlay := pushfArray (ZALLOC(strm, s^.lit_bufsize, sizeof(ush)+2)); + s^.pending_buf := pzByteArray (overlay); + s^.pending_buf_size := ulg(s^.lit_bufsize) * (sizeof(ush)+Long(2)); + + if (s^.window = Z_NULL) or (s^.prev = Z_NULL) or (s^.head = Z_NULL) + or (s^.pending_buf = Z_NULL) then + begin + {ERR_MSG(Z_MEM_ERROR);} + strm.msg := z_errmsg[z_errbase-Z_MEM_ERROR]; + deflateEnd (strm); + deflateInit2_ := Z_MEM_ERROR; + exit; + end; + s^.d_buf := pushfArray( @overlay^[s^.lit_bufsize div sizeof(ush)] ); + s^.l_buf := puchfArray( @s^.pending_buf^[(1+sizeof(ush))*s^.lit_bufsize] ); + + s^.level := level; + s^.strategy := strategy; + s^.method := Byte(method); + + deflateInit2_ := deflateReset(strm); +end; + +{ ========================================================================= } + +function deflateInit2(var strm : z_stream; + level : int; + method : int; + windowBits : int; + memLevel : int; + strategy : int) : int; +{ a macro } +begin + deflateInit2 := deflateInit2_(strm, level, method, windowBits, + memLevel, strategy, ZLIB_VERSION, sizeof(z_stream)); +end; + +{ ========================================================================= } + +function deflateInit_(strm : z_streamp; + level : int; + const version : AnsiString; + stream_size : int) : int; +begin + if (strm = Z_NULL) then + deflateInit_ := Z_STREAM_ERROR + else + deflateInit_ := deflateInit2_(strm^, level, Z_DEFLATED, MAX_WBITS, + DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, version, stream_size); + { To do: ignore strm^.next_in if we use it as window } +end; + +{ ========================================================================= } + +function deflateInit(var strm : z_stream; level : int) : int; +{ deflateInit is a macro to allow checking the zlib version + and the compiler's view of z_stream: } +begin + deflateInit := deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, + DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, ZLIB_VERSION, sizeof(z_stream)); +end; + +{ ======================================================================== } +function deflateSetDictionary (var strm : z_stream; + dictionary : pBytef; + dictLength : uInt) : int; +var + s : deflate_state_ptr; + length : uInt; + n : uInt; + hash_head : IPos; +var + MAX_DIST : uInt; {macro} +begin + length := dictLength; + hash_head := 0; + + if {(@strm = Z_NULL) or} + (strm.state = Z_NULL) or (dictionary = Z_NULL) + or (deflate_state_ptr(strm.state)^.status <> INIT_STATE) then + begin + deflateSetDictionary := Z_STREAM_ERROR; + exit; + end; + + s := deflate_state_ptr(strm.state); + strm.adler := adler32(strm.adler, dictionary, dictLength); + + if (length < MIN_MATCH) then + begin + deflateSetDictionary := Z_OK; + exit; + end; + MAX_DIST := (s^.w_size - MIN_LOOKAHEAD); + if (length > MAX_DIST) then + begin + length := MAX_DIST; +{$ifndef USE_DICT_HEAD} + Inc(dictionary, dictLength - length); { use the tail of the dictionary } +{$endif} + end; + + zmemcpy( pBytef(s^.window), dictionary, length); + s^.strstart := length; + s^.block_start := long(length); + + { Insert all strings in the hash table (except for the last two bytes). + s^.lookahead stays null, so s^.ins_h will be recomputed at the next + call of fill_window. } + + s^.ins_h := s^.window^[0]; + {UPDATE_HASH(s, s^.ins_h, s^.window[1]);} + s^.ins_h := ((s^.ins_h shl s^.hash_shift) xor (s^.window^[1])) + and s^.hash_mask; + + for n := 0 to length - MIN_MATCH do + begin + INSERT_STRING(s^, n, hash_head); + end; + {if (hash_head <> 0) then + hash_head := 0; - to make compiler happy } + deflateSetDictionary := Z_OK; +end; + +{ ======================================================================== } +function deflateReset (var strm : z_stream) : int; +var + s : deflate_state_ptr; +begin + if {(@strm = Z_NULL) or} + (strm.state = Z_NULL) + or (not Assigned(strm.zalloc)) or (not Assigned(strm.zfree)) then + begin + deflateReset := Z_STREAM_ERROR; + exit; + end; + + strm.total_out := 0; + strm.total_in := 0; + strm.msg := ''; { use zfree if we ever allocate msg dynamically } + strm.data_type := Z_UNKNOWN; + + s := deflate_state_ptr(strm.state); + s^.pending := 0; + s^.pending_out := pBytef(s^.pending_buf); + + if (s^.noheader < 0) then + begin + s^.noheader := 0; { was set to -1 by deflate(..., Z_FINISH); } + end; + if s^.noheader <> 0 then + s^.status := BUSY_STATE + else + s^.status := INIT_STATE; + strm.adler := 1; + s^.last_flush := Z_NO_FLUSH; + + _tr_init(s^); + lm_init(s^); + + deflateReset := Z_OK; +end; + +{ ======================================================================== } +function deflateParams(var strm : z_stream; + level : int; + strategy : int) : int; +var + s : deflate_state_ptr; + func : compress_func; + err : int; +begin + err := Z_OK; + if {(@strm = Z_NULL) or} (strm.state = Z_NULL) then + begin + deflateParams := Z_STREAM_ERROR; + exit; + end; + + s := deflate_state_ptr(strm.state); + + if (level = Z_DEFAULT_COMPRESSION) then + begin + level := 6; + end; + if (level < 0) or (level > 9) or (strategy < 0) + or (strategy > Z_HUFFMAN_ONLY) then + begin + deflateParams := Z_STREAM_ERROR; + exit; + end; + func := configuration_table[s^.level].func; + + if (@func <> @configuration_table[level].func) + and (strm.total_in <> 0) then + begin + { Flush the last buffer: } + err := deflate(strm, Z_PARTIAL_FLUSH); + end; + if (s^.level <> level) then + begin + s^.level := level; + s^.max_lazy_match := configuration_table[level].max_lazy; + s^.good_match := configuration_table[level].good_length; + s^.nice_match := configuration_table[level].nice_length; + s^.max_chain_length := configuration_table[level].max_chain; + end; + s^.strategy := strategy; + deflateParams := err; +end; + +{ ========================================================================= + Put a short in the pending buffer. The 16-bit value is put in MSB order. + IN assertion: the stream state is correct and there is enough room in + pending_buf. } + +{local} +procedure putShortMSB (var s : deflate_state; b : uInt); +begin + s.pending_buf^[s.pending] := Byte(b shr 8); + Inc(s.pending); + s.pending_buf^[s.pending] := Byte(b and $ff); + Inc(s.pending); +end; + +{ ========================================================================= + Flush as much pending output as possible. All deflate() output goes + through this function so some applications may wish to modify it + to avoid allocating a large strm^.next_out buffer and copying into it. + (See also read_buf()). } + +{local} +procedure flush_pending(var strm : z_stream); +var + len : unsigned; + s : deflate_state_ptr; +begin + s := deflate_state_ptr(strm.state); + len := s^.pending; + + if (len > strm.avail_out) then + len := strm.avail_out; + if (len = 0) then + exit; + + zmemcpy(strm.next_out, s^.pending_out, len); + Inc(strm.next_out, len); + Inc(s^.pending_out, len); + Inc(strm.total_out, len); + Dec(strm.avail_out, len); + Dec(s^.pending, len); + if (s^.pending = 0) then + begin + s^.pending_out := pBytef(s^.pending_buf); + end; +end; + +{ ========================================================================= } +function deflate (var strm : z_stream; flush : int) : int; +var + old_flush : int; { value of flush param for previous deflate call } + s : deflate_state_ptr; +var + header : uInt; + level_flags : uInt; +var + bstate : block_state; +begin + if {(@strm = Z_NULL) or} (strm.state = Z_NULL) + or (flush > Z_FINISH) or (flush < 0) then + begin + deflate := Z_STREAM_ERROR; + exit; + end; + s := deflate_state_ptr(strm.state); + + if (strm.next_out = Z_NULL) or + ((strm.next_in = Z_NULL) and (strm.avail_in <> 0)) or + ((s^.status = FINISH_STATE) and (flush <> Z_FINISH)) then + begin + {ERR_RETURN(strm^, Z_STREAM_ERROR);} + strm.msg := z_errmsg[z_errbase - Z_STREAM_ERROR]; + deflate := Z_STREAM_ERROR; + exit; + end; + if (strm.avail_out = 0) then + begin + {ERR_RETURN(strm^, Z_BUF_ERROR);} + strm.msg := z_errmsg[z_errbase - Z_BUF_ERROR]; + deflate := Z_BUF_ERROR; + exit; + end; + + s^.strm := @strm; { just in case } + old_flush := s^.last_flush; + s^.last_flush := flush; + + { Write the zlib header } + if (s^.status = INIT_STATE) then + begin + + header := (Z_DEFLATED + ((s^.w_bits-8) shl 4)) shl 8; + level_flags := (s^.level-1) shr 1; + + if (level_flags > 3) then + level_flags := 3; + header := header or (level_flags shl 6); + if (s^.strstart <> 0) then + header := header or PRESET_DICT; + Inc(header, 31 - (header mod 31)); + + s^.status := BUSY_STATE; + putShortMSB(s^, header); + + { Save the adler32 of the preset dictionary: } + if (s^.strstart <> 0) then + begin + putShortMSB(s^, uInt(strm.adler shr 16)); + putShortMSB(s^, uInt(strm.adler and $ffff)); + end; + strm.adler := long(1); + end; + + { Flush as much pending output as possible } + if (s^.pending <> 0) then + begin + flush_pending(strm); + if (strm.avail_out = 0) then + begin + { Since avail_out is 0, deflate will be called again with + more output space, but possibly with both pending and + avail_in equal to zero. There won't be anything to do, + but this is not an error situation so make sure we + return OK instead of BUF_ERROR at next call of deflate: } + + s^.last_flush := -1; + deflate := Z_OK; + exit; + end; + + { Make sure there is something to do and avoid duplicate consecutive + flushes. For repeated and useless calls with Z_FINISH, we keep + returning Z_STREAM_END instead of Z_BUFF_ERROR. } + + end + else + if (strm.avail_in = 0) and (flush <= old_flush) + and (flush <> Z_FINISH) then + begin + {ERR_RETURN(strm^, Z_BUF_ERROR);} + strm.msg := z_errmsg[z_errbase - Z_BUF_ERROR]; + deflate := Z_BUF_ERROR; + exit; + end; + + { User must not provide more input after the first FINISH: } + if (s^.status = FINISH_STATE) and (strm.avail_in <> 0) then + begin + {ERR_RETURN(strm^, Z_BUF_ERROR);} + strm.msg := z_errmsg[z_errbase - Z_BUF_ERROR]; + deflate := Z_BUF_ERROR; + exit; + end; + + { Start a new block or continue the current one. } + if (strm.avail_in <> 0) or (s^.lookahead <> 0) + or ((flush <> Z_NO_FLUSH) and (s^.status <> FINISH_STATE)) then + begin + bstate := configuration_table[s^.level].func(s^, flush); + + if (bstate = finish_started) or (bstate = finish_done) then + s^.status := FINISH_STATE; + + if (bstate = need_more) or (bstate = finish_started) then + begin + if (strm.avail_out = 0) then + s^.last_flush := -1; { avoid BUF_ERROR next call, see above } + + deflate := Z_OK; + exit; + { If flush != Z_NO_FLUSH && avail_out == 0, the next call + of deflate should use the same flush parameter to make sure + that the flush is complete. So we don't have to output an + empty block here, this will be done at next call. This also + ensures that for a very small output buffer, we emit at most + one empty block. } + end; + if (bstate = block_done) then + begin + if (flush = Z_PARTIAL_FLUSH) then + _tr_align(s^) + else + begin { FULL_FLUSH or SYNC_FLUSH } + _tr_stored_block(s^, pcharf(NIL), Long(0), FALSE); + { For a full flush, this empty block will be recognized + as a special marker by inflate_sync(). } + + if (flush = Z_FULL_FLUSH) then + begin + {macro CLEAR_HASH(s);} { forget history } + s^.head^[s^.hash_size-1] := ZNIL; + zmemzero(pBytef(s^.head), unsigned(s^.hash_size-1)*sizeof(s^.head^[0])); + end; + end; + + flush_pending(strm); + if (strm.avail_out = 0) then + begin + s^.last_flush := -1; { avoid BUF_ERROR at next call, see above } + deflate := Z_OK; + exit; + end; + + end; + end; + {$IFDEF DEBUG} + Assert(strm.avail_out > 0, 'bug2'); + {$ENDIF} + if (flush <> Z_FINISH) then + begin + deflate := Z_OK; + exit; + end; + + if (s^.noheader <> 0) then + begin + deflate := Z_STREAM_END; + exit; + end; + + { Write the zlib trailer (adler32) } + putShortMSB(s^, uInt(strm.adler shr 16)); + putShortMSB(s^, uInt(strm.adler and $ffff)); + flush_pending(strm); + { If avail_out is zero, the application will call deflate again + to flush the rest. } + + s^.noheader := -1; { write the trailer only once! } + if s^.pending <> 0 then + deflate := Z_OK + else + deflate := Z_STREAM_END; +end; + +{ ========================================================================= } +function deflateEnd (var strm : z_stream) : int; +var + status : int; + s : deflate_state_ptr; +begin + if {(@strm = Z_NULL) or} (strm.state = Z_NULL) then + begin + deflateEnd := Z_STREAM_ERROR; + exit; + end; + + s := deflate_state_ptr(strm.state); + status := s^.status; + if (status <> INIT_STATE) and (status <> BUSY_STATE) and + (status <> FINISH_STATE) then + begin + deflateEnd := Z_STREAM_ERROR; + exit; + end; + + { Deallocate in reverse order of allocations: } + TRY_FREE(strm, s^.pending_buf); + TRY_FREE(strm, s^.head); + TRY_FREE(strm, s^.prev); + TRY_FREE(strm, s^.window); + + ZFREE(strm, s); + strm.state := Z_NULL; + + if status = BUSY_STATE then + deflateEnd := Z_DATA_ERROR + else + deflateEnd := Z_OK; +end; + +{ ========================================================================= + Copy the source state to the destination state. + To simplify the source, this is not supported for 16-bit MSDOS (which + doesn't have enough memory anyway to duplicate compression states). } + + +{ ========================================================================= } +function deflateCopy (dest, source : z_streamp) : int; +{$ifndef MAXSEG_64K} +var + ds : deflate_state_ptr; + ss : deflate_state_ptr; + overlay : pushfArray; +{$endif} +begin +{$ifdef MAXSEG_64K} + deflateCopy := Z_STREAM_ERROR; + exit; +{$else} + + if (source = Z_NULL) or (dest = Z_NULL) or (source^.state = Z_NULL) then + begin + deflateCopy := Z_STREAM_ERROR; + exit; + end; + ss := deflate_state_ptr(source^.state); + dest^ := source^; + + ds := deflate_state_ptr( ZALLOC(dest^, 1, sizeof(deflate_state)) ); + if (ds = Z_NULL) then + begin + deflateCopy := Z_MEM_ERROR; + exit; + end; + dest^.state := pInternal_state(ds); + ds^ := ss^; + ds^.strm := dest; + + ds^.window := pzByteArray ( ZALLOC(dest^, ds^.w_size, 2*sizeof(Byte)) ); + ds^.prev := pzPosfArray ( ZALLOC(dest^, ds^.w_size, sizeof(Pos)) ); + ds^.head := pzPosfArray ( ZALLOC(dest^, ds^.hash_size, sizeof(Pos)) ); + overlay := pushfArray ( ZALLOC(dest^, ds^.lit_bufsize, sizeof(ush)+2) ); + ds^.pending_buf := pzByteArray ( overlay ); + + if (ds^.window = Z_NULL) or (ds^.prev = Z_NULL) or (ds^.head = Z_NULL) + or (ds^.pending_buf = Z_NULL) then + begin + deflateEnd (dest^); + deflateCopy := Z_MEM_ERROR; + exit; + end; + { following zmemcpy do not work for 16-bit MSDOS } + zmemcpy(pBytef(ds^.window), pBytef(ss^.window), ds^.w_size * 2 * sizeof(Byte)); + zmemcpy(pBytef(ds^.prev), pBytef(ss^.prev), ds^.w_size * sizeof(Pos)); + zmemcpy(pBytef(ds^.head), pBytef(ss^.head), ds^.hash_size * sizeof(Pos)); + zmemcpy(pBytef(ds^.pending_buf), pBytef(ss^.pending_buf), uInt(ds^.pending_buf_size)); + + ds^.pending_out := @ds^.pending_buf^[ptr2int(ss^.pending_out) - ptr2int(ss^.pending_buf)]; + ds^.d_buf := pushfArray (@overlay^[ds^.lit_bufsize div sizeof(ush)] ); + ds^.l_buf := puchfArray (@ds^.pending_buf^[(1+sizeof(ush))*ds^.lit_bufsize]); + + ds^.l_desc.dyn_tree := tree_ptr(@ds^.dyn_ltree); + ds^.d_desc.dyn_tree := tree_ptr(@ds^.dyn_dtree); + ds^.bl_desc.dyn_tree := tree_ptr(@ds^.bl_tree); + + deflateCopy := Z_OK; +{$endif} +end; + + +{ =========================================================================== + Read a new buffer from the current input stream, update the adler32 + and total number of bytes read. All deflate() input goes through + this function so some applications may wish to modify it to avoid + allocating a large strm^.next_in buffer and copying from it. + (See also flush_pending()). } + +{local} +function read_buf(strm : z_streamp; buf : pBytef; size : unsigned) : int; +var + len : unsigned; +begin + len := strm^.avail_in; + + if (len > size) then + len := size; + if (len = 0) then + begin + read_buf := 0; + exit; + end; + + Dec(strm^.avail_in, len); + + if deflate_state_ptr(strm^.state)^.noheader = 0 then + begin + strm^.adler := adler32(strm^.adler, strm^.next_in, len); + end; + zmemcpy(buf, strm^.next_in, len); + Inc(strm^.next_in, len); + Inc(strm^.total_in, len); + + read_buf := int(len); +end; + +{ =========================================================================== + Initialize the "longest match" routines for a new zlib stream } + +{local} +procedure lm_init (var s : deflate_state); +begin + s.window_size := ulg( uLong(2)*s.w_size); + + {macro CLEAR_HASH(s);} + s.head^[s.hash_size-1] := ZNIL; + zmemzero(pBytef(s.head), unsigned(s.hash_size-1)*sizeof(s.head^[0])); + + { Set the default configuration parameters: } + + s.max_lazy_match := configuration_table[s.level].max_lazy; + s.good_match := configuration_table[s.level].good_length; + s.nice_match := configuration_table[s.level].nice_length; + s.max_chain_length := configuration_table[s.level].max_chain; + + s.strstart := 0; + s.block_start := long(0); + s.lookahead := 0; + s.prev_length := MIN_MATCH-1; + s.match_length := MIN_MATCH-1; + s.match_available := FALSE; + s.ins_h := 0; +{$ifdef ASMV} + match_init; { initialize the asm code } +{$endif} +end; + +{ =========================================================================== + Set match_start to the longest match starting at the given string and + return its length. Matches shorter or equal to prev_length are discarded, + in which case the result is equal to prev_length and match_start is + garbage. + IN assertions: cur_match is the head of the hash chain for the current + string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + OUT assertion: the match length is not greater than s^.lookahead. } + + +{$ifndef ASMV} +{ For 80x86 and 680x0, an optimized version will be provided in match.asm or + match.S. The code will be functionally equivalent. } + +{$ifndef FASTEST} + +{local} +function longest_match(var s : deflate_state; + cur_match : IPos { current match } + ) : uInt; +label + nextstep; +var + chain_length : unsigned; { max hash chain length } + {register} scan : pBytef; { current string } + {register} match : pBytef; { matched string } + {register} len : int; { length of current match } + best_len : int; { best match length so far } + nice_match : int; { stop if match long enough } + limit : IPos; + + prev : pzPosfArray; + wmask : uInt; +{$ifdef UNALIGNED_OK} + {register} strend : pBytef; + {register} scan_start : ush; + {register} scan_end : ush; +{$else} + {register} strend : pBytef; + {register} scan_end1 : Byte; + {register} scan_end : Byte; +{$endif} +var + MAX_DIST : uInt; +begin + chain_length := s.max_chain_length; { max hash chain length } + scan := @(s.window^[s.strstart]); + best_len := s.prev_length; { best match length so far } + nice_match := s.nice_match; { stop if match long enough } + + + MAX_DIST := s.w_size - MIN_LOOKAHEAD; +{In order to simplify the code, particularly on 16 bit machines, match +distances are limited to MAX_DIST instead of WSIZE. } + + if s.strstart > IPos(MAX_DIST) then + limit := s.strstart - IPos(MAX_DIST) + else + limit := ZNIL; + { Stop when cur_match becomes <= limit. To simplify the code, + we prevent matches with the string of window index 0. } + + prev := s.prev; + wmask := s.w_mask; + +{$ifdef UNALIGNED_OK} + { Compare two bytes at a time. Note: this is not always beneficial. + Try with and without -DUNALIGNED_OK to check. } + + strend := pBytef(@(s.window^[s.strstart + MAX_MATCH - 1])); + scan_start := pushf(scan)^; + scan_end := pushfArray(scan)^[best_len-1]; { fix } +{$else} + strend := pBytef(@(s.window^[s.strstart + MAX_MATCH])); + {$IFOPT R+} {$R-} {$DEFINE NoRangeCheck} {$ENDIF} + scan_end1 := pzByteArray(scan)^[best_len-1]; + {$IFDEF NoRangeCheck} {$R+} {$UNDEF NoRangeCheck} {$ENDIF} + scan_end := pzByteArray(scan)^[best_len]; +{$endif} + + { The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + It is easy to get rid of this optimization if necessary. } + {$IFDEF DEBUG} + Assert((s.hash_bits >= 8) and (MAX_MATCH = 258), 'Code too clever'); + {$ENDIF} + { Do not waste too much time if we already have a good match: } + if (s.prev_length >= s.good_match) then + begin + chain_length := chain_length shr 2; + end; + + { Do not look for matches beyond the end of the input. This is necessary + to make deflate deterministic. } + + if (uInt(nice_match) > s.lookahead) then + nice_match := s.lookahead; + {$IFDEF DEBUG} + Assert(ulg(s.strstart) <= s.window_size-MIN_LOOKAHEAD, 'need lookahead'); + {$ENDIF} + repeat + {$IFDEF DEBUG} + Assert(cur_match < s.strstart, 'no future'); + {$ENDIF} + match := @(s.window^[cur_match]); + + { Skip to next match if the match length cannot increase + or if the match length is less than 2: } + +{$undef DO_UNALIGNED_OK} +{$ifdef UNALIGNED_OK} + {$ifdef MAX_MATCH_IS_258} + {$define DO_UNALIGNED_OK} + {$endif} +{$endif} + +{$ifdef DO_UNALIGNED_OK} + { This code assumes sizeof(unsigned short) = 2. Do not use + UNALIGNED_OK if your compiler uses a different size. } + {$IFOPT R+} {$R-} {$DEFINE NoRangeCheck} {$ENDIF} + if (pushfArray(match)^[best_len-1] <> scan_end) or + (pushf(match)^ <> scan_start) then + goto nextstep; {continue;} + {$IFDEF NoRangeCheck} {$R+} {$UNDEF NoRangeCheck} {$ENDIF} + + { It is not necessary to compare scan[2] and match[2] since they are + always equal when the other bytes match, given that the hash keys + are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + strstart+3, +5, ... up to strstart+257. We check for insufficient + lookahead only every 4th comparison; the 128th check will be made + at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + necessary to put more guard bytes at the end of the window, or + to check more often for insufficient lookahead. } + {$IFDEF DEBUG} + Assert(pzByteArray(scan)^[2] = pzByteArray(match)^[2], 'scan[2]?'); + {$ENDIF} + Inc(scan); + Inc(match); + + repeat + Inc(scan,2); Inc(match,2); if (pushf(scan)^<>pushf(match)^) then break; + Inc(scan,2); Inc(match,2); if (pushf(scan)^<>pushf(match)^) then break; + Inc(scan,2); Inc(match,2); if (pushf(scan)^<>pushf(match)^) then break; + Inc(scan,2); Inc(match,2); if (pushf(scan)^<>pushf(match)^) then break; + until (ptr2int(scan) >= ptr2int(strend)); + { The funny "do while" generates better code on most compilers } + + { Here, scan <= window+strstart+257 } + {$IFDEF DEBUG} + {$ifopt R+} {$define RangeCheck} {$endif} {$R-} + Assert(ptr2int(scan) <= + ptr2int(@(s.window^[unsigned(s.window_size-1)])), + 'wild scan'); + {$ifdef RangeCheck} {$R+} {$undef RangeCheck} {$endif} + {$ENDIF} + if (scan^ = match^) then + Inc(scan); + + len := (MAX_MATCH - 1) - int(ptr2int(strend)) + int(ptr2int(scan)); + scan := strend; + Dec(scan, (MAX_MATCH-1)); + +{$else} { UNALIGNED_OK } + + {$IFOPT R+} {$R-} {$DEFINE NoRangeCheck} {$ENDIF} + if (pzByteArray(match)^[best_len] <> scan_end) or + (pzByteArray(match)^[best_len-1] <> scan_end1) or + (match^ <> scan^) then + goto nextstep; {continue;} + {$IFDEF NoRangeCheck} {$R+} {$UNDEF NoRangeCheck} {$ENDIF} + Inc(match); + if (match^ <> pzByteArray(scan)^[1]) then + goto nextstep; {continue;} + + { The check at best_len-1 can be removed because it will be made + again later. (This heuristic is not always a win.) + It is not necessary to compare scan[2] and match[2] since they + are always equal when the other bytes match, given that + the hash keys are equal and that HASH_BITS >= 8. } + + Inc(scan, 2); + Inc(match); + {$IFDEF DEBUG} + Assert( scan^ = match^, 'match[2]?'); + {$ENDIF} + { We check for insufficient lookahead only every 8th comparison; + the 256th check will be made at strstart+258. } + + repeat + Inc(scan); Inc(match); if (scan^ <> match^) then break; + Inc(scan); Inc(match); if (scan^ <> match^) then break; + Inc(scan); Inc(match); if (scan^ <> match^) then break; + Inc(scan); Inc(match); if (scan^ <> match^) then break; + Inc(scan); Inc(match); if (scan^ <> match^) then break; + Inc(scan); Inc(match); if (scan^ <> match^) then break; + Inc(scan); Inc(match); if (scan^ <> match^) then break; + Inc(scan); Inc(match); if (scan^ <> match^) then break; + until (ptr2int(scan) >= ptr2int(strend)); + + {$IFDEF DEBUG} + Assert(ptr2int(scan) <= + ptr2int(@(s.window^[unsigned(s.window_size-1)])), + 'wild scan'); + {$ENDIF} + + len := MAX_MATCH - int(ptr2int(strend) - ptr2int(scan)); + scan := strend; + Dec(scan, MAX_MATCH); + +{$endif} { UNALIGNED_OK } + + if (len > best_len) then + begin + s.match_start := cur_match; + best_len := len; + if (len >= nice_match) then + break; + {$IFOPT R+} {$R-} {$DEFINE NoRangeCheck} {$ENDIF} +{$ifdef UNALIGNED_OK} + scan_end := pzByteArray(scan)^[best_len-1]; +{$else} + scan_end1 := pzByteArray(scan)^[best_len-1]; + scan_end := pzByteArray(scan)^[best_len]; +{$endif} + {$IFDEF NoRangeCheck} {$R+} {$UNDEF NoRangeCheck} {$ENDIF} + end; + nextstep: + cur_match := prev^[cur_match and wmask]; + Dec(chain_length); + until (cur_match <= limit) or (chain_length = 0); + + if (uInt(best_len) <= s.lookahead) then + longest_match := uInt(best_len) + else + longest_match := s.lookahead; +end; +{$endif} { ASMV } + +{$else} { FASTEST } +{ --------------------------------------------------------------------------- + Optimized version for level = 1 only } + +{local} +function longest_match(var s : deflate_state; + cur_match : IPos { current match } + ) : uInt; +var + {register} scan : pBytef; { current string } + {register} match : pBytef; { matched string } + {register} len : int; { length of current match } + {register} strend : pBytef; +begin + scan := @s.window^[s.strstart]; + strend := @s.window^[s.strstart + MAX_MATCH]; + + + { The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + It is easy to get rid of this optimization if necessary. } + {$IFDEF DEBUG} + Assert((s.hash_bits >= 8) and (MAX_MATCH = 258), 'Code too clever'); + + Assert(ulg(s.strstart) <= s.window_size-MIN_LOOKAHEAD, 'need lookahead'); + + Assert(cur_match < s.strstart, 'no future'); + {$ENDIF} + match := s.window + cur_match; + + { Return failure if the match length is less than 2: } + + if (match[0] <> scan[0]) or (match[1] <> scan[1]) then + begin + longest_match := MIN_MATCH-1; + exit; + end; + + { The check at best_len-1 can be removed because it will be made + again later. (This heuristic is not always a win.) + It is not necessary to compare scan[2] and match[2] since they + are always equal when the other bytes match, given that + the hash keys are equal and that HASH_BITS >= 8. } + + scan += 2, match += 2; + Assert(scan^ = match^, 'match[2]?'); + + { We check for insufficient lookahead only every 8th comparison; + the 256th check will be made at strstart+258. } + + repeat + Inc(scan); Inc(match); if scan^<>match^ then break; + Inc(scan); Inc(match); if scan^<>match^ then break; + Inc(scan); Inc(match); if scan^<>match^ then break; + Inc(scan); Inc(match); if scan^<>match^ then break; + Inc(scan); Inc(match); if scan^<>match^ then break; + Inc(scan); Inc(match); if scan^<>match^ then break; + Inc(scan); Inc(match); if scan^<>match^ then break; + Inc(scan); Inc(match); if scan^<>match^ then break; + until (ptr2int(scan) >= ptr2int(strend)); + + Assert(scan <= s.window+unsigned(s.window_size-1), 'wild scan'); + + len := MAX_MATCH - int(strend - scan); + + if (len < MIN_MATCH) then + begin + return := MIN_MATCH - 1; + exit; + end; + + s.match_start := cur_match; + if len <= s.lookahead then + longest_match := len + else + longest_match := s.lookahead; +end; +{$endif} { FASTEST } + +{$ifdef DEBUG} +{ =========================================================================== + Check that the match at match_start is indeed a match. } + +{local} +procedure check_match(var s : deflate_state; + start, match : IPos; + length : int); +begin + exit; + { check that the match is indeed a match } + if (zmemcmp(pBytef(@s.window^[match]), + pBytef(@s.window^[start]), length) <> EQUAL) then + begin + WriteLn(' start ',start,', match ',match ,' length ', length); + repeat + Write(AnsiChar(s.window^[match]), AnsiChar(s.window^[start])); + Inc(match); + Inc(start); + Dec(length); + Until (length = 0); + z_error('invalid match'); + end; + if (z_verbose > 1) then + begin + Write('\\[',start-match,',',length,']'); + repeat + Write(AnsiChar(s.window^[start])); + Inc(start); + Dec(length); + Until (length = 0); + end; +end; +{$endif} + +{ =========================================================================== + Fill the window when the lookahead becomes insufficient. + Updates strstart and lookahead. + + IN assertion: lookahead < MIN_LOOKAHEAD + OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + At least one byte has been read, or avail_in = 0; reads are + performed for at least two bytes (required for the zip translate_eol + option -- not supported here). } + +{local} +procedure fill_window(var s : deflate_state); +var + {register} n, m : unsigned; + {register} p : pPosf; + more : unsigned; { Amount of free space at the end of the window. } + wsize : uInt; +begin + wsize := s.w_size; + repeat + more := unsigned(s.window_size -ulg(s.lookahead) -ulg(s.strstart)); + + { Deal with !@#$% 64K limit: } + if (more = 0) and (s.strstart = 0) and (s.lookahead = 0) then + more := wsize + else + if (more = unsigned(-1)) then + begin + { Very unlikely, but possible on 16 bit machine if strstart = 0 + and lookahead = 1 (input done one byte at time) } + Dec(more); + + { If the window is almost full and there is insufficient lookahead, + move the upper half to the lower one to make room in the upper half.} + end + else + if (s.strstart >= wsize+ {MAX_DIST}(wsize-MIN_LOOKAHEAD)) then + begin + zmemcpy( pBytef(s.window), pBytef(@(s.window^[wsize])), + unsigned(wsize)); + Dec(s.match_start, wsize); + Dec(s.strstart, wsize); { we now have strstart >= MAX_DIST } + Dec(s.block_start, long(wsize)); + + { Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level = 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) } + + n := s.hash_size; + p := @s.head^[n]; + repeat + Dec(p); + m := p^; + if (m >= wsize) then + p^ := Pos(m-wsize) + else + p^ := Pos(ZNIL); + Dec(n); + Until (n=0); + + n := wsize; +{$ifndef FASTEST} + p := @s.prev^[n]; + repeat + Dec(p); + m := p^; + if (m >= wsize) then + p^ := Pos(m-wsize) + else + p^:= Pos(ZNIL); + { If n is not on any hash chain, prev^[n] is garbage but + its value will never be used. } + Dec(n); + Until (n=0); +{$endif} + Inc(more, wsize); + end; + if (s.strm^.avail_in = 0) then + exit; + + {* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. } + + {$IFDEF DEBUG} + Assert(more >= 2, 'more < 2'); + {$ENDIF} + + n := read_buf(s.strm, pBytef(@(s.window^[s.strstart + s.lookahead])), + more); + Inc(s.lookahead, n); + + { Initialize the hash value now that we have some input: } + if (s.lookahead >= MIN_MATCH) then + begin + s.ins_h := s.window^[s.strstart]; + {UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]);} + s.ins_h := ((s.ins_h shl s.hash_shift) xor s.window^[s.strstart+1]) + and s.hash_mask; +{$ifdef MIN_MATCH <> 3} + Call UPDATE_HASH() MIN_MATCH-3 more times +{$endif} + end; + { If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + but this is not important since only literal bytes will be emitted. } + + until (s.lookahead >= MIN_LOOKAHEAD) or (s.strm^.avail_in = 0); +end; + +{ =========================================================================== + Flush the current block, with given end-of-file flag. + IN assertion: strstart is set to the end of the current match. } + +procedure FLUSH_BLOCK_ONLY(var s : deflate_state; eof : boolean); {macro} +begin + if (s.block_start >= Long(0)) then + _tr_flush_block(s, pcharf(@s.window^[unsigned(s.block_start)]), + ulg(long(s.strstart) - s.block_start), eof) + else + _tr_flush_block(s, pcharf(Z_NULL), + ulg(long(s.strstart) - s.block_start), eof); + + s.block_start := s.strstart; + flush_pending(s.strm^); + {$IFDEF DEBUG} + Tracev('[FLUSH]'); + {$ENDIF} +end; + +{ Same but force premature exit if necessary. +macro FLUSH_BLOCK(var s : deflate_state; eof : boolean) : boolean; +var + result : block_state; +begin + FLUSH_BLOCK_ONLY(s, eof); + if (s.strm^.avail_out = 0) then + begin + if eof then + result := finish_started + else + result := need_more; + exit; + end; +end; +} + +{ =========================================================================== + Copy without compression as much as possible from the input stream, return + the current block state. + This function does not insert new strings in the dictionary since + uncompressible data is probably not useful. This function is used + only for the level=0 compression option. + NOTE: this function should be optimized to avoid extra copying from + window to pending_buf. } + + +{local} +function deflate_stored(var s : deflate_state; flush : int) : block_state; +{ Stored blocks are limited to 0xffff bytes, pending_buf is limited + to pending_buf_size, and each stored block has a 5 byte header: } +var + max_block_size : ulg; + max_start : ulg; +begin + max_block_size := $ffff; + if (max_block_size > s.pending_buf_size - 5) then + max_block_size := s.pending_buf_size - 5; + + { Copy as much as possible from input to output: } + while TRUE do + begin + { Fill the window as much as possible: } + if (s.lookahead <= 1) then + begin + {$IFDEF DEBUG} + Assert( (s.strstart < s.w_size + {MAX_DIST}s.w_size-MIN_LOOKAHEAD) or + (s.block_start >= long(s.w_size)), 'slide too late'); + {$ENDIF} + fill_window(s); + if (s.lookahead = 0) and (flush = Z_NO_FLUSH) then + begin + deflate_stored := need_more; + exit; + end; + + if (s.lookahead = 0) then + break; { flush the current block } + end; + {$IFDEF DEBUG} + Assert(s.block_start >= long(0), 'block gone'); + {$ENDIF} + Inc(s.strstart, s.lookahead); + s.lookahead := 0; + + { Emit a stored block if pending_buf will be full: } + max_start := s.block_start + max_block_size; + if (s.strstart = 0) or (ulg(s.strstart) >= max_start) then + begin + { strstart = 0 is possible when wraparound on 16-bit machine } + s.lookahead := s.strstart - uInt(max_start); + s.strstart := uInt(max_start); + {FLUSH_BLOCK(s, FALSE);} + FLUSH_BLOCK_ONLY(s, FALSE); + if (s.strm^.avail_out = 0) then + begin + deflate_stored := need_more; + exit; + end; + end; + + { Flush if we may have to slide, otherwise block_start may become + negative and the data will be gone: } + + if (s.strstart - uInt(s.block_start) >= {MAX_DIST} + s.w_size-MIN_LOOKAHEAD) then + begin + {FLUSH_BLOCK(s, FALSE);} + FLUSH_BLOCK_ONLY(s, FALSE); + if (s.strm^.avail_out = 0) then + begin + deflate_stored := need_more; + exit; + end; + end; + end; + + {FLUSH_BLOCK(s, flush = Z_FINISH);} + FLUSH_BLOCK_ONLY(s, flush = Z_FINISH); + if (s.strm^.avail_out = 0) then + begin + if flush = Z_FINISH then + deflate_stored := finish_started + else + deflate_stored := need_more; + exit; + end; + + if flush = Z_FINISH then + deflate_stored := finish_done + else + deflate_stored := block_done; +end; + +{ =========================================================================== + Compress as much as possible from the input stream, return the current + block state. + This function does not perform lazy evaluation of matches and inserts + new strings in the dictionary only for unmatched strings or for short + matches. It is used only for the fast compression options. } + +{local} +function deflate_fast(var s : deflate_state; flush : int) : block_state; +var + hash_head : IPos; { head of the hash chain } + bflush : boolean; { set if current block must be flushed } +begin + hash_head := ZNIL; + while TRUE do + begin + { Make sure that we always have enough lookahead, except + at the end of the input file. We need MAX_MATCH bytes + for the next match, plus MIN_MATCH bytes to insert the + string following the next match. } + + if (s.lookahead < MIN_LOOKAHEAD) then + begin + fill_window(s); + if (s.lookahead < MIN_LOOKAHEAD) and (flush = Z_NO_FLUSH) then + begin + deflate_fast := need_more; + exit; + end; + + if (s.lookahead = 0) then + break; { flush the current block } + end; + + + { Insert the string window[strstart .. strstart+2] in the + dictionary, and set hash_head to the head of the hash chain: } + + if (s.lookahead >= MIN_MATCH) then + begin + INSERT_STRING(s, s.strstart, hash_head); + end; + + { Find the longest match, discarding those <= prev_length. + At this point we have always match_length < MIN_MATCH } + if (hash_head <> ZNIL) and + (s.strstart - hash_head <= (s.w_size-MIN_LOOKAHEAD){MAX_DIST}) then + begin + { To simplify the code, we prevent matches with the string + of window index 0 (in particular we have to avoid a match + of the string with itself at the start of the input file). } + if (s.strategy <> Z_HUFFMAN_ONLY) then + begin + s.match_length := longest_match (s, hash_head); + end; + { longest_match() sets match_start } + end; + if (s.match_length >= MIN_MATCH) then + begin + {$IFDEF DEBUG} + check_match(s, s.strstart, s.match_start, s.match_length); + {$ENDIF} + + {_tr_tally_dist(s, s.strstart - s.match_start, + s.match_length - MIN_MATCH, bflush);} + bflush := _tr_tally(s, s.strstart - s.match_start, + s.match_length - MIN_MATCH); + + Dec(s.lookahead, s.match_length); + + { Insert new strings in the hash table only if the match length + is not too large. This saves time but degrades compression. } + +{$ifndef FASTEST} + if (s.match_length <= s.max_insert_length) + and (s.lookahead >= MIN_MATCH) then + begin + Dec(s.match_length); { string at strstart already in hash table } + repeat + Inc(s.strstart); + INSERT_STRING(s, s.strstart, hash_head); + { strstart never exceeds WSIZE-MAX_MATCH, so there are + always MIN_MATCH bytes ahead. } + Dec(s.match_length); + until (s.match_length = 0); + Inc(s.strstart); + end + else +{$endif} + + begin + Inc(s.strstart, s.match_length); + s.match_length := 0; + s.ins_h := s.window^[s.strstart]; + {UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]);} + s.ins_h := (( s.ins_h shl s.hash_shift) xor + s.window^[s.strstart+1]) and s.hash_mask; +if MIN_MATCH <> 3 then { the linker removes this } +begin + {Call UPDATE_HASH() MIN_MATCH-3 more times} +end; + + { If lookahead < MIN_MATCH, ins_h is garbage, but it does not + matter since it will be recomputed at next deflate call. } + + end; + end + else + begin + { No match, output a literal byte } + {$IFDEF DEBUG} + Tracevv(AnsiChar(s.window^[s.strstart])); + {$ENDIF} + {_tr_tally_lit (s, 0, s.window^[s.strstart], bflush);} + bflush := _tr_tally (s, 0, s.window^[s.strstart]); + + Dec(s.lookahead); + Inc(s.strstart); + end; + if bflush then + begin {FLUSH_BLOCK(s, FALSE);} + FLUSH_BLOCK_ONLY(s, FALSE); + if (s.strm^.avail_out = 0) then + begin + deflate_fast := need_more; + exit; + end; + end; + end; + {FLUSH_BLOCK(s, flush = Z_FINISH);} + FLUSH_BLOCK_ONLY(s, flush = Z_FINISH); + if (s.strm^.avail_out = 0) then + begin + if flush = Z_FINISH then + deflate_fast := finish_started + else + deflate_fast := need_more; + exit; + end; + + if flush = Z_FINISH then + deflate_fast := finish_done + else + deflate_fast := block_done; +end; + +{ =========================================================================== + Same as above, but achieves better compression. We use a lazy + evaluation for matches: a match is finally adopted only if there is + no better match at the next window position. } + +{local} +function deflate_slow(var s : deflate_state; flush : int) : block_state; +var + hash_head : IPos; { head of hash chain } + bflush : boolean; { set if current block must be flushed } +var + max_insert : uInt; +begin + hash_head := ZNIL; + + { Process the input block. } + while TRUE do + begin + { Make sure that we always have enough lookahead, except + at the end of the input file. We need MAX_MATCH bytes + for the next match, plus MIN_MATCH bytes to insert the + string following the next match. } + + if (s.lookahead < MIN_LOOKAHEAD) then + begin + fill_window(s); + if (s.lookahead < MIN_LOOKAHEAD) and (flush = Z_NO_FLUSH) then + begin + deflate_slow := need_more; + exit; + end; + + if (s.lookahead = 0) then + break; { flush the current block } + end; + + { Insert the string window[strstart .. strstart+2] in the + dictionary, and set hash_head to the head of the hash chain: } + + if (s.lookahead >= MIN_MATCH) then + begin + INSERT_STRING(s, s.strstart, hash_head); + end; + + { Find the longest match, discarding those <= prev_length. } + + s.prev_length := s.match_length; + s.prev_match := s.match_start; + s.match_length := MIN_MATCH-1; + + if (hash_head <> ZNIL) and (s.prev_length < s.max_lazy_match) and + (s.strstart - hash_head <= {MAX_DIST}(s.w_size-MIN_LOOKAHEAD)) then + begin + { To simplify the code, we prevent matches with the string + of window index 0 (in particular we have to avoid a match + of the string with itself at the start of the input file). } + + if (s.strategy <> Z_HUFFMAN_ONLY) then + begin + s.match_length := longest_match (s, hash_head); + end; + { longest_match() sets match_start } + + if (s.match_length <= 5) and ((s.strategy = Z_FILTERED) or + ((s.match_length = MIN_MATCH) and + (s.strstart - s.match_start > TOO_FAR))) then + begin + { If prev_match is also MIN_MATCH, match_start is garbage + but we will ignore the current match anyway. } + + s.match_length := MIN_MATCH-1; + end; + end; + { If there was a match at the previous step and the current + match is not better, output the previous match: } + + if (s.prev_length >= MIN_MATCH) + and (s.match_length <= s.prev_length) then + begin + max_insert := s.strstart + s.lookahead - MIN_MATCH; + { Do not insert strings in hash table beyond this. } + {$ifdef DEBUG} + check_match(s, s.strstart-1, s.prev_match, s.prev_length); + {$endif} + + {_tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush);} + bflush := _tr_tally(s, s.strstart -1 - s.prev_match, + s.prev_length - MIN_MATCH); + + { Insert in hash table all strings up to the end of the match. + strstart-1 and strstart are already inserted. If there is not + enough lookahead, the last two strings are not inserted in + the hash table. } + + Dec(s.lookahead, s.prev_length-1); + Dec(s.prev_length, 2); + repeat + Inc(s.strstart); + if (s.strstart <= max_insert) then + begin + INSERT_STRING(s, s.strstart, hash_head); + end; + Dec(s.prev_length); + until (s.prev_length = 0); + s.match_available := FALSE; + s.match_length := MIN_MATCH-1; + Inc(s.strstart); + + if (bflush) then {FLUSH_BLOCK(s, FALSE);} + begin + FLUSH_BLOCK_ONLY(s, FALSE); + if (s.strm^.avail_out = 0) then + begin + deflate_slow := need_more; + exit; + end; + end; + end + else + if (s.match_available) then + begin + { If there was no match at the previous position, output a + single literal. If there was a match but the current match + is longer, truncate the previous match to a single literal. } + {$IFDEF DEBUG} + Tracevv(AnsiChar(s.window^[s.strstart-1])); + {$ENDIF} + bflush := _tr_tally (s, 0, s.window^[s.strstart-1]); + + if bflush then + begin + FLUSH_BLOCK_ONLY(s, FALSE); + end; + Inc(s.strstart); + Dec(s.lookahead); + if (s.strm^.avail_out = 0) then + begin + deflate_slow := need_more; + exit; + end; + end + else + begin + { There is no previous match to compare with, wait for + the next step to decide. } + + s.match_available := TRUE; + Inc(s.strstart); + Dec(s.lookahead); + end; + end; + + {$IFDEF DEBUG} + Assert (flush <> Z_NO_FLUSH, 'no flush?'); + {$ENDIF} + if (s.match_available) then + begin + {$IFDEF DEBUG} + Tracevv(AnsiChar(s.window^[s.strstart-1])); + bflush := + {$ENDIF} + _tr_tally (s, 0, s.window^[s.strstart-1]); + s.match_available := FALSE; + end; + {FLUSH_BLOCK(s, flush = Z_FINISH);} + FLUSH_BLOCK_ONLY(s, flush = Z_FINISH); + if (s.strm^.avail_out = 0) then + begin + if flush = Z_FINISH then + deflate_slow := finish_started + else + deflate_slow := need_more; + exit; + end; + if flush = Z_FINISH then + deflate_slow := finish_done + else + deflate_slow := block_done; +end; + +end. diff --git a/resources/libraries/deskew/Imaging/ZLib/imzinflate.pas b/resources/libraries/deskew/Imaging/ZLib/imzinflate.pas new file mode 100755 index 0000000..6984950 --- /dev/null +++ b/resources/libraries/deskew/Imaging/ZLib/imzinflate.pas @@ -0,0 +1,750 @@ +Unit imzinflate; + +{ inflate.c -- zlib interface to inflate modules + Copyright (C) 1995-1998 Mark Adler + + Pascal tranlastion + Copyright (C) 1998 by Jacques Nomssi Nzali + For conditions of distribution and use, see copyright notice in readme.txt +} + +interface + +{$I imzconf.inc} + +uses + imzutil, impaszlib, iminfblock, iminfutil; + +function inflateInit(var z : z_stream) : int; + +{ Initializes the internal stream state for decompression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, inflateInit updates them to use default + allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_VERSION_ERROR if the zlib library version is incompatible + with the version assumed by the caller. msg is set to null if there is no + error message. inflateInit does not perform any decompression: this will be + done by inflate(). } + + + +function inflateInit_(z : z_streamp; + const version : AnsiString; + stream_size : int) : int; + + +function inflateInit2_(var z: z_stream; + w : int; + const version : AnsiString; + stream_size : int) : int; + +function inflateInit2(var z: z_stream; + windowBits : int) : int; + +{ + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. If a compressed stream with a larger window size is given as + input, inflate() will return with the error code Z_DATA_ERROR instead of + trying to allocate a larger window. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative + memLevel). msg is set to null if there is no error message. inflateInit2 + does not perform any decompression apart from reading the zlib header if + present: this will be done by inflate(). (So next_in and avail_in may be + modified, but next_out and avail_out are unchanged.) +} + + + +function inflateEnd(var z : z_stream) : int; + +{ + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +} + +function inflateReset(var z : z_stream) : int; + +{ + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +} + + +function inflate(var z : z_stream; + f : int) : int; +{ + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) + except when forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much + output as possible to the output buffer. The flushing behavior of inflate is + not specified for values of the flush parameter other than Z_SYNC_FLUSH + and Z_FINISH, but the current implementation actually flushes as much output + as possible anyway. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster routine + may be used for the single inflate() call. + + If a preset dictionary is needed at this point (see inflateSetDictionary + below), inflate sets strm-adler to the adler32 checksum of the + dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise + it sets strm->adler to the adler32 checksum of all output produced + so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or + an error code as described below. At the end of the stream, inflate() + checks that its computed adler32 checksum is equal to that saved by the + compressor and returns Z_STREAM_END only if the checksum is correct. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect + adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent + (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if no progress is possible or if there was not + enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR + case, the application may then call inflateSync to look for a good + compression block. +} + + +function inflateSetDictionary(var z : z_stream; + dictionary : pBytef; {const array of byte} + dictLength : uInt) : int; + +{ + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate + if this call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the Adler32 value returned by this call of + inflate. The compressor and decompressor must use exactly the same + dictionary (see deflateSetDictionary). + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect Adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +} + +function inflateSync(var z : z_stream) : int; + +{ + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +} + + +function inflateSyncPoint(var z : z_stream) : int; + + +implementation + +uses + imadler; + +function inflateReset(var z : z_stream) : int; +begin + if (z.state = Z_NULL) then + begin + inflateReset := Z_STREAM_ERROR; + exit; + end; + z.total_out := 0; + z.total_in := 0; + z.msg := ''; + if z.state^.nowrap then + z.state^.mode := BLOCKS + else + z.state^.mode := METHOD; + inflate_blocks_reset(z.state^.blocks^, z, Z_NULL); + {$IFDEF DEBUG} + Tracev('inflate: reset'); + {$ENDIF} + inflateReset := Z_OK; +end; + + +function inflateEnd(var z : z_stream) : int; +begin + if (z.state = Z_NULL) or not Assigned(z.zfree) then + begin + inflateEnd := Z_STREAM_ERROR; + exit; + end; + if (z.state^.blocks <> Z_NULL) then + inflate_blocks_free(z.state^.blocks, z); + ZFREE(z, z.state); + z.state := Z_NULL; + {$IFDEF DEBUG} + Tracev('inflate: end'); + {$ENDIF} + inflateEnd := Z_OK; +end; + + +function inflateInit2_(var z: z_stream; + w : int; + const version : AnsiString; + stream_size : int) : int; +begin + if (version = '') or (version[1] <> ZLIB_VERSION[1]) or + (stream_size <> sizeof(z_stream)) then + begin + inflateInit2_ := Z_VERSION_ERROR; + exit; + end; + { initialize state } + { SetLength(strm.msg, 255); } + z.msg := ''; + if not Assigned(z.zalloc) then + begin + {$IFDEF FPC} z.zalloc := @zcalloc; {$ELSE} + z.zalloc := zcalloc; + {$endif} + z.opaque := voidpf(0); + end; + if not Assigned(z.zfree) then + {$IFDEF FPC} z.zfree := @zcfree; {$ELSE} + z.zfree := zcfree; + {$ENDIF} + + z.state := pInternal_state( ZALLOC(z,1,sizeof(internal_state)) ); + if (z.state = Z_NULL) then + begin + inflateInit2_ := Z_MEM_ERROR; + exit; + end; + + z.state^.blocks := Z_NULL; + + { handle undocumented nowrap option (no zlib header or check) } + z.state^.nowrap := FALSE; + if (w < 0) then + begin + w := - w; + z.state^.nowrap := TRUE; + end; + + { set window size } + if (w < 8) or (w > 15) then + begin + inflateEnd(z); + inflateInit2_ := Z_STREAM_ERROR; + exit; + end; + z.state^.wbits := uInt(w); + + { create inflate_blocks state } + if z.state^.nowrap then + z.state^.blocks := inflate_blocks_new(z, NIL, uInt(1) shl w) + else + {$IFDEF FPC} + z.state^.blocks := inflate_blocks_new(z, @adler32, uInt(1) shl w); + {$ELSE} + z.state^.blocks := inflate_blocks_new(z, adler32, uInt(1) shl w); + {$ENDIF} + if (z.state^.blocks = Z_NULL) then + begin + inflateEnd(z); + inflateInit2_ := Z_MEM_ERROR; + exit; + end; + {$IFDEF DEBUG} + Tracev('inflate: allocated'); + {$ENDIF} + { reset state } + inflateReset(z); + inflateInit2_ := Z_OK; +end; + +function inflateInit2(var z: z_stream; windowBits : int) : int; +begin + inflateInit2 := inflateInit2_(z, windowBits, ZLIB_VERSION, sizeof(z_stream)); +end; + + +function inflateInit(var z : z_stream) : int; +{ inflateInit is a macro to allow checking the zlib version + and the compiler's view of z_stream: } +begin + inflateInit := inflateInit2_(z, DEF_WBITS, ZLIB_VERSION, sizeof(z_stream)); +end; + +function inflateInit_(z : z_streamp; + const version : AnsiString; + stream_size : int) : int; +begin + { initialize state } + if (z = Z_NULL) then + inflateInit_ := Z_STREAM_ERROR + else + inflateInit_ := inflateInit2_(z^, DEF_WBITS, version, stream_size); +end; + +function inflate(var z : z_stream; + f : int) : int; +var + r : int; + b : uInt; +begin + if (z.state = Z_NULL) or (z.next_in = Z_NULL) then + begin + inflate := Z_STREAM_ERROR; + exit; + end; + if f = Z_FINISH then + f := Z_BUF_ERROR + else + f := Z_OK; + r := Z_BUF_ERROR; + while True do + case (z.state^.mode) of + BLOCKS: + begin + r := inflate_blocks(z.state^.blocks^, z, r); + if (r = Z_DATA_ERROR) then + begin + z.state^.mode := BAD; + z.state^.sub.marker := 0; { can try inflateSync } + continue; { break C-switch } + end; + if (r = Z_OK) then + r := f; + if (r <> Z_STREAM_END) then + begin + inflate := r; + exit; + end; + r := f; + inflate_blocks_reset(z.state^.blocks^, z, @z.state^.sub.check.was); + if (z.state^.nowrap) then + begin + z.state^.mode := DONE; + continue; { break C-switch } + end; + z.state^.mode := CHECK4; { falltrough } + end; + CHECK4: + begin + {NEEDBYTE} + if (z.avail_in = 0) then + begin + inflate := r; + exit; + end; + r := f; + + {z.state^.sub.check.need := uLong(NEXTBYTE(z)) shl 24;} + Dec(z.avail_in); + Inc(z.total_in); + z.state^.sub.check.need := uLong(z.next_in^) shl 24; + Inc(z.next_in); + + z.state^.mode := CHECK3; { falltrough } + end; + CHECK3: + begin + {NEEDBYTE} + if (z.avail_in = 0) then + begin + inflate := r; + exit; + end; + r := f; + {Inc( z.state^.sub.check.need, uLong(NEXTBYTE(z)) shl 16);} + Dec(z.avail_in); + Inc(z.total_in); + Inc(z.state^.sub.check.need, uLong(z.next_in^) shl 16); + Inc(z.next_in); + + z.state^.mode := CHECK2; { falltrough } + end; + CHECK2: + begin + {NEEDBYTE} + if (z.avail_in = 0) then + begin + inflate := r; + exit; + end; + r := f; + + {Inc( z.state^.sub.check.need, uLong(NEXTBYTE(z)) shl 8);} + Dec(z.avail_in); + Inc(z.total_in); + Inc(z.state^.sub.check.need, uLong(z.next_in^) shl 8); + Inc(z.next_in); + + z.state^.mode := CHECK1; { falltrough } + end; + CHECK1: + begin + {NEEDBYTE} + if (z.avail_in = 0) then + begin + inflate := r; + exit; + end; + r := f; + {Inc( z.state^.sub.check.need, uLong(NEXTBYTE(z)) );} + Dec(z.avail_in); + Inc(z.total_in); + Inc(z.state^.sub.check.need, uLong(z.next_in^) ); + Inc(z.next_in); + + + if (z.state^.sub.check.was <> z.state^.sub.check.need) then + begin + z.state^.mode := BAD; + z.msg := 'incorrect data check'; + z.state^.sub.marker := 5; { can't try inflateSync } + continue; { break C-switch } + end; + {$IFDEF DEBUG} + Tracev('inflate: zlib check ok'); + {$ENDIF} + z.state^.mode := DONE; { falltrough } + end; + DONE: + begin + inflate := Z_STREAM_END; + exit; + end; + METHOD: + begin + {NEEDBYTE} + if (z.avail_in = 0) then + begin + inflate := r; + exit; + end; + r := f; {} + + {z.state^.sub.method := NEXTBYTE(z);} + Dec(z.avail_in); + Inc(z.total_in); + z.state^.sub.method := z.next_in^; + Inc(z.next_in); + + if ((z.state^.sub.method and $0f) <> Z_DEFLATED) then + begin + z.state^.mode := BAD; + z.msg := 'unknown compression method'; + z.state^.sub.marker := 5; { can't try inflateSync } + continue; { break C-switch } + end; + if ((z.state^.sub.method shr 4) + 8 > z.state^.wbits) then + begin + z.state^.mode := BAD; + z.msg := 'invalid window size'; + z.state^.sub.marker := 5; { can't try inflateSync } + continue; { break C-switch } + end; + z.state^.mode := FLAG; + { fall trough } + end; + FLAG: + begin + {NEEDBYTE} + if (z.avail_in = 0) then + begin + inflate := r; + exit; + end; + r := f; {} + {b := NEXTBYTE(z);} + Dec(z.avail_in); + Inc(z.total_in); + b := z.next_in^; + Inc(z.next_in); + + if (((z.state^.sub.method shl 8) + b) mod 31) <> 0 then {% mod ?} + begin + z.state^.mode := BAD; + z.msg := 'incorrect header check'; + z.state^.sub.marker := 5; { can't try inflateSync } + continue; { break C-switch } + end; + {$IFDEF DEBUG} + Tracev('inflate: zlib header ok'); + {$ENDIF} + if ((b and PRESET_DICT) = 0) then + begin + z.state^.mode := BLOCKS; + continue; { break C-switch } + end; + z.state^.mode := DICT4; + { falltrough } + end; + DICT4: + begin + if (z.avail_in = 0) then + begin + inflate := r; + exit; + end; + r := f; + + {z.state^.sub.check.need := uLong(NEXTBYTE(z)) shl 24;} + Dec(z.avail_in); + Inc(z.total_in); + z.state^.sub.check.need := uLong(z.next_in^) shl 24; + Inc(z.next_in); + + z.state^.mode := DICT3; { falltrough } + end; + DICT3: + begin + if (z.avail_in = 0) then + begin + inflate := r; + exit; + end; + r := f; + {Inc(z.state^.sub.check.need, uLong(NEXTBYTE(z)) shl 16);} + Dec(z.avail_in); + Inc(z.total_in); + Inc(z.state^.sub.check.need, uLong(z.next_in^) shl 16); + Inc(z.next_in); + + z.state^.mode := DICT2; { falltrough } + end; + DICT2: + begin + if (z.avail_in = 0) then + begin + inflate := r; + exit; + end; + r := f; + + {Inc(z.state^.sub.check.need, uLong(NEXTBYTE(z)) shl 8);} + Dec(z.avail_in); + Inc(z.total_in); + Inc(z.state^.sub.check.need, uLong(z.next_in^) shl 8); + Inc(z.next_in); + + z.state^.mode := DICT1; { falltrough } + end; + DICT1: + begin + if (z.avail_in = 0) then + begin + inflate := r; + exit; + end; + { r := f; --- wird niemals benutzt } + {Inc(z.state^.sub.check.need, uLong(NEXTBYTE(z)) );} + Dec(z.avail_in); + Inc(z.total_in); + Inc(z.state^.sub.check.need, uLong(z.next_in^) ); + Inc(z.next_in); + + z.adler := z.state^.sub.check.need; + z.state^.mode := DICT0; + inflate := Z_NEED_DICT; + exit; + end; + DICT0: + begin + z.state^.mode := BAD; + z.msg := 'need dictionary'; + z.state^.sub.marker := 0; { can try inflateSync } + inflate := Z_STREAM_ERROR; + exit; + end; + BAD: + begin + inflate := Z_DATA_ERROR; + exit; + end; + else + begin + inflate := Z_STREAM_ERROR; + exit; + end; + end; +{$ifdef NEED_DUMMY_result} + result := Z_STREAM_ERROR; { Some dumb compilers complain without this } +{$endif} +end; + +function inflateSetDictionary(var z : z_stream; + dictionary : pBytef; {const array of byte} + dictLength : uInt) : int; +var + length : uInt; +begin + length := dictLength; + + if (z.state = Z_NULL) or (z.state^.mode <> DICT0) then + begin + inflateSetDictionary := Z_STREAM_ERROR; + exit; + end; + if (adler32(Long(1), dictionary, dictLength) <> z.adler) then + begin + inflateSetDictionary := Z_DATA_ERROR; + exit; + end; + z.adler := Long(1); + + if (length >= (uInt(1) shl z.state^.wbits)) then + begin + length := (1 shl z.state^.wbits)-1; + Inc( dictionary, dictLength - length); + end; + inflate_set_dictionary(z.state^.blocks^, dictionary^, length); + z.state^.mode := BLOCKS; + inflateSetDictionary := Z_OK; +end; + + +function inflateSync(var z : z_stream) : int; +const + mark : packed array[0..3] of byte = (0, 0, $ff, $ff); +var + n : uInt; { number of bytes to look at } + p : pBytef; { pointer to bytes } + m : uInt; { number of marker bytes found in a row } + r, w : uLong; { temporaries to save total_in and total_out } +begin + { set up } + if (z.state = Z_NULL) then + begin + inflateSync := Z_STREAM_ERROR; + exit; + end; + if (z.state^.mode <> BAD) then + begin + z.state^.mode := BAD; + z.state^.sub.marker := 0; + end; + n := z.avail_in; + if (n = 0) then + begin + inflateSync := Z_BUF_ERROR; + exit; + end; + p := z.next_in; + m := z.state^.sub.marker; + + { search } + while (n <> 0) and (m < 4) do + begin + if (p^ = mark[m]) then + Inc(m) + else + if (p^ <> 0) then + m := 0 + else + m := 4 - m; + Inc(p); + Dec(n); + end; + + { restore } + Inc(z.total_in, ptr2int(p) - ptr2int(z.next_in)); + z.next_in := p; + z.avail_in := n; + z.state^.sub.marker := m; + + + { return no joy or set up to restart on a new block } + if (m <> 4) then + begin + inflateSync := Z_DATA_ERROR; + exit; + end; + r := z.total_in; + w := z.total_out; + inflateReset(z); + z.total_in := r; + z.total_out := w; + z.state^.mode := BLOCKS; + inflateSync := Z_OK; +end; + + +{ + returns true if inflate is currently at the end of a block generated + by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH + but removes the length bytes of the resulting empty stored block. When + decompressing, PPP checks that at the end of input packet, inflate is + waiting for these length bytes. +} + +function inflateSyncPoint(var z : z_stream) : int; +begin + if (z.state = Z_NULL) or (z.state^.blocks = Z_NULL) then + begin + inflateSyncPoint := Z_STREAM_ERROR; + exit; + end; + inflateSyncPoint := inflate_blocks_sync_point(z.state^.blocks^); +end; + +end. diff --git a/resources/libraries/deskew/Imaging/ZLib/imzutil.pas b/resources/libraries/deskew/Imaging/ZLib/imzutil.pas new file mode 100755 index 0000000..420b5fb --- /dev/null +++ b/resources/libraries/deskew/Imaging/ZLib/imzutil.pas @@ -0,0 +1,191 @@ +Unit imzutil; + +{ + Copyright (C) 1998 by Jacques Nomssi Nzali + For conditions of distribution and use, see copyright notice in readme.txt +} + +interface + +{$I imzconf.inc} + +{ Type declarations } + +type + {Byte = usigned char; 8 bits} + Bytef = byte; + charf = byte; + + int = longint; + intf = int; + uInt = cardinal; { 16 bits or more } + uIntf = uInt; + + Long = longint; + uLong = Cardinal; + uLongf = uLong; + + voidp = pointer; + voidpf = voidp; + pBytef = ^Bytef; + pIntf = ^intf; + puIntf = ^uIntf; + puLong = ^uLongf; + + ptr2int = uInt; +{ a pointer to integer casting is used to do pointer arithmetic. + ptr2int must be an integer type and sizeof(ptr2int) must be less + than sizeof(pointer) - Nomssi } + +type + zByteArray = array[0..(MaxInt div SizeOf(Bytef))-1] of Bytef; + pzByteArray = ^zByteArray; +type + zIntfArray = array[0..(MaxInt div SizeOf(Intf))-1] of Intf; + pzIntfArray = ^zIntfArray; +type + zuIntArray = array[0..(MaxInt div SizeOf(uInt))-1] of uInt; + PuIntArray = ^zuIntArray; + +{ Type declarations - only for deflate } + +type + uch = Byte; + uchf = uch; { FAR } + ush = Word; + ushf = ush; + ulg = LongInt; + + unsigned = uInt; + + pcharf = ^charf; + puchf = ^uchf; + pushf = ^ushf; + +type + zuchfArray = zByteArray; + puchfArray = ^zuchfArray; +type + zushfArray = array[0..(MaxInt div SizeOf(ushf))-1] of ushf; + pushfArray = ^zushfArray; + +procedure zmemcpy(destp : pBytef; sourcep : pBytef; len : uInt); +function zmemcmp(s1p, s2p : pBytef; len : uInt) : int; +procedure zmemzero(destp : pBytef; len : uInt); +procedure zcfree(opaque : voidpf; ptr : voidpf); +function zcalloc (opaque : voidpf; items : uInt; size : uInt) : voidpf; + +implementation + +procedure zmemcpy(destp : pBytef; sourcep : pBytef; len : uInt); +begin + Move(sourcep^, destp^, len); +end; + +function zmemcmp(s1p, s2p : pBytef; len : uInt) : int; +var + j : uInt; + source, + dest : pBytef; +begin + source := s1p; + dest := s2p; + for j := 0 to pred(len) do + begin + if (source^ <> dest^) then + begin + zmemcmp := 2*Ord(source^ > dest^)-1; + exit; + end; + Inc(source); + Inc(dest); + end; + zmemcmp := 0; +end; + +procedure zmemzero(destp : pBytef; len : uInt); +begin + FillChar(destp^, len, 0); +end; + +procedure zcfree(opaque : voidpf; ptr : voidpf); +{$ifdef Delphi16} +var + Handle : THandle; +{$endif} +{$IFDEF FPC} +var + memsize : uint; +{$ENDIF} +begin + (* + {$IFDEF DPMI} + {h :=} GlobalFreePtr(ptr); + {$ELSE} + {$IFDEF CALL_DOS} + dosFree(ptr); + {$ELSE} + {$ifdef HugeMem} + FreeMemHuge(ptr); + {$else} + {$ifdef Delphi16} + Handle := GlobalHandle(LH(ptr).H); { HiWord(LongInt(ptr)) } + GlobalUnLock(Handle); + GlobalFree(Handle); + {$else} + {$IFDEF FPC} + Dec(puIntf(ptr)); + memsize := puIntf(ptr)^; + FreeMem(ptr, memsize+SizeOf(uInt)); + {$ELSE} + FreeMem(ptr); { Delphi 2,3,4 } + {$ENDIF} + {$endif} + {$endif} + {$ENDIF} + {$ENDIF} + *) + FreeMem(ptr); +end; + +function zcalloc (opaque : voidpf; items : uInt; size : uInt) : voidpf; +var + p : voidpf; + memsize : uLong; +{$ifdef Delphi16} + handle : THandle; +{$endif} +begin + memsize := uLong(items) * size; + (* + { $IFDEF DPMI} + p := GlobalAllocPtr(gmem_moveable, memsize); + { $ELSE} + { $IFDEF CALLDOS} + p := dosAlloc(memsize); + { $ELSE} + {$ifdef HugeMem} + GetMemHuge(p, memsize); + { $else} + { $ifdef Delphi16} + Handle := GlobalAlloc(HeapAllocFlags, memsize); + p := GlobalLock(Handle); + { $else} + { $IFDEF FPC} + GetMem(p, memsize+SizeOf(uInt)); + puIntf(p)^:= memsize; + Inc(puIntf(p)); + { $ELSE} + GetMem(p, memsize); { Delphi: p := AllocMem(memsize); } + { $ENDIF} + { $endif} + { $endif} + { $ENDIF} + { $ENDIF} + *) + GetMem(p, memsize); + zcalloc := p; +end; + +end. + diff --git a/resources/libraries/deskew/MainUnit.pas b/resources/libraries/deskew/MainUnit.pas new file mode 100755 index 0000000..9c35791 --- /dev/null +++ b/resources/libraries/deskew/MainUnit.pas @@ -0,0 +1,416 @@ +{ + Deskew + by Marek Mauder + http://galfar.vevb.net/deskew + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +unit MainUnit; + +{$I ImagingOptions.inc} + +interface + +procedure RunDeskew; + +implementation + +uses + Types, + SysUtils, + Classes, + ImagingTypes, + Imaging, + ImagingClasses, + ImagingFormats, + ImagingUtility, + ImagingExtras, + // Project units + CmdLineOptions, + ImageUtils, + RotationDetector; + +const + SAppTitle = 'Deskew 1.30 (2019-06-07)' + {$IF Defined(CPUX64)} + ' x64' + {$ELSEIF Defined(CPUX86)} + ' x86' + {$ELSEIF Defined(CPUARM)} + ' ARM' + {$IFEND} + {$IFDEF DEBUG} + ' (DEBUG)'{$ENDIF} + + ' by Marek Mauder'; + SAppHome = 'http://galfar.vevb.net/deskew/'; + +var + // Program options + Options: TCmdLineOptions; + // Input and output image + InputImage, OutputImage: TSingleImage; + +procedure WriteUsage; +var + InFilter, OutFilter: string; + I, Count: Integer; + Fmt: TImageFileFormat; +begin + InFilter := ''; + OutFilter := ''; + + WriteLn('Usage:'); + WriteLn('deskew [-o output] [-a angle] [-b color] [..] input'); + WriteLn(' input: Input image file'); + WriteLn(' Options:'); + WriteLn(' -o output: Output image file (default: out.png)'); + WriteLn(' -a angle: Maximal expected skew angle (both directions) in degrees (default: 10)'); + WriteLn(' -b color: Background color in hex format RRGGBB|LL|AARRGGBB (default: black)'); + WriteLn(' Ext. options:'); + WriteLn(' -q filter: Resampling filter used for rotations (default: linear,'); + WriteLn(' values: nearest|linear|cubic|lanczos)'); + WriteLn(' -t a|treshold: Auto threshold or value in 0..255 (default: a)'); + WriteLn(' -r rect: Skew detection only in content rectangle (pixels):'); + WriteLn(' left,top,right,bottom (default: whole page)'); + WriteLn(' -f format: Force output pixel format (values: b1|g8|rgb24|rgba32)'); + WriteLn(' -l angle: Skip deskewing step if skew angle is smaller (default: 0.01)'); + WriteLn(' -g flags: Operational flags (any combination of):'); + WriteLn(' c - auto crop, d - detect only (no output to file)'); + WriteLn(' -s info: Info dump (any combination of):'); + WriteLn(' s - skew detection stats, p - program parameters, t - timings'); + WriteLn(' -c specs: Output compression specs for some file formats. Several specs'); + WriteLn(' can be defined - delimited by commas. Supported specs:'); + WriteLn(' jXX - JPEG compression quality, XX is in range [1,100(best)]'); + WriteLn(' tSCHEME - TIFF compression scheme: none|lzw|rle|deflate|jpeg|g4'); + + + Count := GetFileFormatCount; + for I := 0 to Count - 1 do + begin + Fmt := GetFileFormatAtIndex(I); + if Fmt.CanLoad then + InFilter := InFilter + Fmt.Extensions[0] + Iff(I < Count - 1, ', ', ''); + if Fmt.CanSave then + OutFilter := OutFilter + Fmt.Extensions[0] + Iff(I < Count - 1, ', ', ''); + end; + + WriteLn; + WriteLn(' Supported file formats'); + WriteLn(' Input: ', UpperCase(InFilter)); + WriteLn(' Output: ', UpperCase(OutFilter)); +end; + +procedure ReportBadInput(const Msg: string; ShowUsage: Boolean = True); +begin + WriteLn; + WriteLn('Error: ' + Msg); + if Options.ErrorMessage <> '' then + WriteLn(Options.ErrorMessage); + WriteLn; + + if ShowUsage then + WriteUsage; + + ExitCode := 1; +end; + +function FormatNiceNumber(const X: Int64; Width : Integer = 16): string; +var + FmtStr: string; +begin + if Width = 0 then + FmtStr := '%.0n' + else + FmtStr := '%' + IntToStr(Width) + '.0n'; + Result := Format(FmtStr, [X * 1.0], GetFormatSettingsForFloats); +end; + +var + Time: Int64; + +procedure WriteTiming(const StepName: string); +begin + if Options.ShowTimings then + WriteLn(StepName + ' - time taken: ' + FormatNiceNumber(GetTimeMicroseconds - Time, 0) + ' us'); +end; + +function DoDeskew: Boolean; +var + SkewAngle: Double; + Threshold: Integer; + ContentRect: TRect; + Stats: TCalcSkewAngleStats; + + procedure WriteStats; + begin + WriteLn('Skew detection stats:'); + WriteLn(' pixel count: ', FormatNiceNumber(Stats.PixelCount)); + WriteLn(' tested pixels: ', FormatNiceNumber(Stats.TestedPixels)); + WriteLn(' accumulator size: ', FormatNiceNumber(Stats.AccumulatorSize)); + WriteLn(' accumulated counts: ', FormatNiceNumber(Stats.AccumulatedCounts)); + WriteLn(' best count: ', FormatNiceNumber(Stats.BestCount)); + end; + +begin + Result := False; + Threshold := 0; + WriteLn('Preparing input image (', ExtractFileName(Options.InputFile), ' [', + InputImage.Width, 'x', InputImage.Height, '/', string(InputImage.FormatInfo.Name), ']) ...'); + + // Clone input image and convert it to 8bit grayscale. This will be our + // working image. + OutputImage.Assign(InputImage); + InputImage.Format := ifGray8; + + // Determine threshold level for black/white pixel classification during skew detection + case Options.ThresholdingMethod of + tmExplicit: + begin + // Use explicit threshold + Threshold := Options.ThresholdLevel; + end; + tmOtsu: + begin + // Determine the threshold automatically + Time := GetTimeMicroseconds; + Threshold := OtsuThresholding(InputImage.ImageDataPointer^); + WriteTiming('Auto thresholding'); + end; + end; + + // Determine the content rect - where exactly to detect rotated text + ContentRect := InputImage.BoundsRect; + if not IsRectEmpty(Options.ContentRect) then + begin + if not IntersectRect(ContentRect, Options.ContentRect, InputImage.BoundsRect) then + ContentRect := InputImage.BoundsRect; + end; + + // Main step - calculate image rotation SkewAngle + WriteLn('Calculating skew angle...'); + Time := GetTimeMicroseconds; + SkewAngle := CalcRotationAngle(Options.MaxAngle, Threshold, + InputImage.Width, InputImage.Height, InputImage.Bits, + @ContentRect, @Stats); + WriteTiming('Skew detection'); + WriteLn('Skew angle found [deg]: ', SkewAngle:4:3); + + if Options.ShowStats then + WriteStats; + + if ofDetectOnly in Options.OperationalFlags then + Exit; + + // Check if detected skew angle is higher than "skip" threshold - may not + // want to do rotation needlessly. + if Abs(SkewAngle) >= Options.SkipAngle then + begin + Result := True; + + // Finally, rotate the image. We rotate the original input image, not the working + // one so the color space is preserved if possible. + WriteLn('Rotating image...'); + + // Rotation is optimized for Gray8, RGB24, and ARGB32 formats at this time + if not (OutputImage.Format in ImageUtils.SupportedRotationFormats) then + begin + if OutputImage.Format = ifIndex8 then + begin + if PaletteHasAlpha(OutputImage.Palette, OutputImage.PaletteEntries) then + OutputImage.Format := ifA8R8G8B8 + else if PaletteIsGrayScale(OutputImage.Palette, OutputImage.PaletteEntries) then + OutputImage.Format := ifGray8 + else + OutputImage.Format := ifR8G8B8; + end + else if OutputImage.FormatInfo.HasAlphaChannel then + OutputImage.Format := ifA8R8G8B8 + else if (OutputImage.Format = ifBinary) or OutputImage.FormatInfo.HasGrayChannel then + OutputImage.Format := ifGray8 + else + OutputImage.Format := ifR8G8B8; + end; + + if (Options.BackgroundColor and $FF000000) <> $FF000000 then + begin + // User explicitly requested some alpha in background color + OutputImage.Format := ifA8R8G8B8; + end + else if (OutputImage.Format = ifGray8) and not ( + (GetRedValue(Options.BackgroundColor) = GetGreenValue(Options.BackgroundColor)) and + (GetBlueValue(Options.BackgroundColor) = GetGreenValue(Options.BackgroundColor))) then + begin + // Some non-grayscale background for gray image was requested + OutputImage.Format := ifR8G8B8; + end; + + Time := GetTimeMicroseconds; + ImageUtils.RotateImage(OutputImage.ImageDataPointer^, SkewAngle, Options.BackgroundColor, + Options.ResamplingFilter, not (ofAutoCrop in Options.OperationalFlags)); + WriteTiming('Rotate image'); + end + else + WriteLn('Skipping deskewing step, skew angle lower than threshold of ', Options.SkipAngle:4:2); + + if (Options.ForcedOutputFormat <> ifUnknown) and (OutputImage.Format <> Options.ForcedOutputFormat) then + begin + // Force output format. For example Deskew won't automatically + // save image as binary if the input was binary since it + // might degrade the output a lot (rotation adds a lot of colors to image). + OutputImage.Format := Options.ForcedOutputFormat; + Result := True; + end; +end; + +procedure RunDeskew; + + procedure EnsureOutputLocation(const FileName: string); + var + Dir, Path: string; + begin + Path := ExpandFileName(FileName); + Dir := GetFileDir(Path); + if Dir <> '' then + ForceDirectories(Dir); + end; + + procedure CopyFile(const SrcPath, DestPath: string); + var + SrcStream, DestStream: TFileStream; + begin + if SameText(SrcPath, DestPath) then + Exit; // No need to copy anything + + SrcStream := TFileStream.Create(SrcPath, fmOpenRead); + DestStream := TFileStream.Create(DestPath, fmCreate); + DestStream.CopyFrom(SrcStream, SrcStream.Size); + DestStream.Free; + SrcStream.Free; + end; + + procedure SetImagingOptions; + begin + if Options.JpegCompressionQuality <> -1 then + begin + Imaging.SetOption(ImagingJpegQuality, Options.JpegCompressionQuality); + Imaging.SetOption(ImagingTiffJpegQuality, Options.JpegCompressionQuality); + Imaging.SetOption(ImagingJNGQuality, Options.JpegCompressionQuality); + end; + if Options.TiffCompressionScheme <> -1 then + Imaging.SetOption(ImagingTiffCompression, Options.TiffCompressionScheme); + end; + +var + Changed: Boolean; +begin +{$IF Defined(FPC) and not Defined(MSWINDOWS)} + // Flush after WriteLn also when output is redirected to file/pipe + if Textrec(Output).FlushFunc = nil then + Textrec(Output).FlushFunc := Textrec(Output).InOutFunc; +{$IFEND} + + WriteLn(SAppTitle); + WriteLn(SAppHome); + + Options := TCmdLineOptions.Create; + InputImage := TSingleImage.Create; + OutputImage := TSingleImage.Create; + + try + try + if Options.ParseCommnadLine and Options.IsValid then + begin + SetImagingOptions; + if Options.ShowParams then + WriteLn(Options.OptionsToString); + + if not IsFileFormatSupported(Options.InputFile) then + begin + ReportBadInput('File format not supported: ' + Options.InputFile); + Exit; + end; + + // Load input image + Time := GetTimeMicroseconds; + InputImage.LoadFromFile(Options.InputFile); + WriteTiming('Load input file'); + + if not InputImage.Valid then + begin + ReportBadInput('Loaded input image is not valid: ' + Options.InputFile, False); + Exit; + end; + + // Do the magic + Changed := DoDeskew(); + + if not (ofDetectOnly in Options.OperationalFlags) then + begin + WriteLn('Saving output (', ExpandFileName(Options.OutputFile), ' [', + OutputImage.Width, 'x', OutputImage.Height, '/', string(OutputImage.FormatInfo.Name), ']) ...'); + + // Make sure output folders are ready + EnsureOutputLocation(Options.OutputFile); + // In case no change to image was done by deskewing we still need to resave if requested file format differs from input + Changed := Changed or not SameText(GetFileExt(Options.InputFile), GetFileExt(Options.OutputFile)); + + Time := GetTimeMicroseconds; + if Changed then + begin + // Make sure recognized metadata stays (like scanning DPI info) + GlobalMetadata.CopyLoadedMetaItemsForSaving; + // Save the output + OutputImage.SaveToFile(Options.OutputFile); + end + else + begin + // No change to image made, just copy it to the desired destination + CopyFile(Options.InputFile, Options.OutputFile); + end; + WriteTiming('Save output file'); + end; + + WriteLn('Done!'); + end + else + begin + // Bad input + ReportBadInput('Invalid parameters!'); + end; + + except + on E: Exception do + begin + WriteLn; + WriteLn(E.ClassName, ': ', E.Message); + ExitCode := 1; + end; + end; + finally + Options.Free; + InputImage.Free; + OutputImage.Free; + +{$IFDEF DEBUG} + ReadLn; +{$ENDIF} + end; +end; + +end. diff --git a/resources/libraries/deskew/Readme.md b/resources/libraries/deskew/Readme.md new file mode 100755 index 0000000..e008d48 --- /dev/null +++ b/resources/libraries/deskew/Readme.md @@ -0,0 +1,165 @@ +Deskew +======================= + +by Marek Mauder \ +<http://galfar.vevb.net/deskew> \ +<https://bitbucket.org/galfar/app-deskew> \ +<https://github.com/galfar/deskew> + +**v1.30 2018-06-07** + + +Overview +------------------------ + +Deskew is a command line tool for deskewing scanned text documents. +It uses Hough transform to detect "text lines" in the image. As an output, you +get an image rotated so that the lines are horizontal. + +There are binaries built for these platforms (located in Bin folder): +Win64 (`deskew.exe`), Win32 (`deskew32.exe`), Linux 64bit (`deskew`), macOS (`deskew-mac`), Linux ARMv7 (`deskew-arm`). + +GUI frontend for this CLI tool is available as well (Windows, Linux, and macOS). + +License: MIT + +### Downloads And Releases + +<https://github.com/galfar/deskew/releases> \ +<https://bitbucket.org/galfar/app-deskew/downloads/> + +Usage +------------------------ + +```console +Usage: +deskew [-o output] [-a angle] [-b color] [..] input + input: Input image file + Options: + -o output: Output image file (default: out.png) + -a angle: Maximal expected skew angle (both directions) in degrees (default: 10) + -b color: Background color in hex format RRGGBB|LL|AARRGGBB (default: black) + Ext. options: + -q filter: Resampling filter used for rotations (default: linear, + values: nearest|linear|cubic|lanczos) + -t a|treshold: Auto threshold or value in 0..255 (default: a) + -r rect: Skew detection only in content rectangle (pixels): + left,top,right,bottom (default: whole page) + -f format: Force output pixel format (values: b1|g8|rgb24|rgba32) + -l angle: Skip deskewing step if skew angle is smaller (default: 0.01) + -g flags: Operational flags (any combination of): + c - auto crop, d - detect only (no output to file) + -s info: Info dump (any combination of): + s - skew detection stats, p - program parameters, t - timings + -c specs: Output compression specs for some file formats. Several specs + can be defined - delimited by commas. Supported specs: + jXX - JPEG compression quality, XX is in range [1,100(best)] + tSCHEME - TIFF compression scheme: none|lzw|rle|deflate|jpeg|g4 + + Supported file formats + Input: BMP, JPG, PNG, JNG, GIF, DDS, TGA, PBM, PGM, PPM, PAM, PFM, TIF, PSD + Output: BMP, JPG, PNG, JNG, GIF, DDS, TGA, PGM, PPM, PAM, PFM, TIF, PSD +``` + +### Notes + +For TIFF support in Linux and macOS you need to have libtiff 4.x installed (package is usually called libtiff5). + +For macOS you can download prebuilt libtiff binaries here: <https://bitbucket.org/galfar/app-deskew/downloads/TiffLibBins-macOS.zip>. Just put the files inside the archive to the same folder as `deskew-mac` executable. + +You can find some test images in TestImages folder and +scripts to run tests (`RunTests.bat` and `runtests.sh`) in Bin. +By default scripts just call `deskew` command but you can pass a different one as a parameter +(e.g. `runtests.sh deskew-arm`). + +### Bugs, Issues, Proposals + +File them here: \ +<https://bitbucket.org/galfar/app-deskew/issues> \ +<https://github.com/galfar/deskew/issues> + + +Version History +------------------------ + +v1.30 2019-06-07: + +- fix #15: Better image quality after rotation - better default and also selectable nearest|linear|cubic|lanczos filtering +- fix #5: Detect skew angle only (no rotation done) - optionally only skew detection +- fix #17: Optional auto-crop after rotation +- fix #3: Command line option to set output compression - now for TIFF and JPEG +- fix #12: Bad behavior when an output is given and no deskewing is needed +- libtiff in macOS is now picked up also when binaries are put directly in the directory with deskew +- text output is flushed after every write (Linux/Unix): it used to be flushed only when writing to device but not file/pipe. + +v1.25 2018-05-19: + +- fix #6: Preserve DPI measurement system (TIFF) +- fix #4: Output image not saved in requested format (when deskewing is skipped) +- dynamic loading of libtiff library - adds TIFF support in macOS when libtiff is installed + +v1.21 2017-11-01: + +- fix #8: Cannot compile in Free Pascal 3.0+ (Windows) - Fails to link precompiled LibTiff library +- fix #7: Windows FPC build fails with *Access violation exception* when loading certain TIFFs (especially those saved by Windows Photo Viewer etc.) + +v1.20 2016-09-01: + +- much faster rotation, especially when background color is set (>2x faster, 2x less memory) +- can skip deskewing step if detected skew angle is lower than parameter +- new option for timing of individual steps +- fix: crash when last row of page is classified as text +- misc: default back color is now opaque black, new forced output format "rgb24", background color can define also alpha channel, nicer formatting of text output + +v1.10 2014-03-04: + +- TIFF support for Win64 and 32/64bit Linux +- forced output formats +- fix: output file names were always lowercase +- fix: preserves resolution metadata (e.g. 300dpi) of input when writing output + +v1.00 2012-06-04: + +- background color +- "area of interest" content rectangle +- 64bit and Mac OSX support +- PSD and TIFF (win32) support +- show skew detection stats and program parameters + +v0.95 2010-12-28: + +- Added auto thresholding + +v0.90 2010-02-12: + +- Initial version + + +Compiling Deskew +------------------------ + +Deskew is written in Object Pascal. You need Free Pascal or Delphi to recompile it. + +### Tested Compilers + +There are project files for these IDEs: + + 1. Lazarus 2.0.2 (deskew.lpi) + 2. Delphi XE + 10.3 (deskew.dproj) + +Additionally, there are compile shell/batch scripts for standalone FPC compiler in `Scripts` folder. + +### Supported/Tested Platforms + +Deskew is precompiled and was tested on these platforms: +Win32, Win64, Linux 64bit, macOS 64bit, Linux ARMv7 + +### Source Code + +Latest source code can be found here: \ +<https://bitbucket.org/galfar/app-deskew> \ +<https://github.com/galfar/deskew> + +### Dependencies + +Vampyre Imaging Library is needed for compilation and it's included in Deskew's repo in Imaging folder. diff --git a/resources/libraries/deskew/RotationDetector.pas b/resources/libraries/deskew/RotationDetector.pas new file mode 100755 index 0000000..e692790 --- /dev/null +++ b/resources/libraries/deskew/RotationDetector.pas @@ -0,0 +1,228 @@ +{ + Deskew + by Marek Mauder + http://galfar.vevb.net/deskew + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} +unit RotationDetector; + +interface + +uses + Types, + SysUtils, + Math, + ImagingUtility; + +type + TCalcSkewAngleStats = record + PixelCount: Integer; + TestedPixels: Integer; + AccumulatorSize: Integer; + AccumulatedCounts: Integer; + BestCount: Integer; + end; + PCalcSkewAngleStats = ^TCalcSkewAngleStats; + +{ Calculates rotation angle for given 8bit grayscale image. + Useful for finding skew of scanned documents etc. + Uses Hough transform internally. + MaxAngle is maximal (abs. value) expected skew angle in degrees (to speed things up) + and Threshold (0..255) is used to classify pixel as black (text) or white (background). + Area of interest rectangle can be defined to restrict the detection to + work only in defined part of image (useful when the document has text only in + smaller area of page and non-text features outside the area confuse the rotation detector). + Various calculations stats can be retrieved by passing Stats parameter.} +function CalcRotationAngle(const MaxAngle: Double; Treshold: Integer; + Width, Height: Integer; Pixels: PByteArray; DetectionArea: PRect = nil; + Stats: PCalcSkewAngleStats = nil): Double; + +implementation + +function CalcRotationAngle(const MaxAngle: Double; Treshold: Integer; + Width, Height: Integer; Pixels: PByteArray; DetectionArea: PRect; Stats: PCalcSkewAngleStats): Double; +const + // Number of "best" lines we take into account when determining + // resulting rotation angle (lines with most votes). + BestLinesCount = 20; + // Angle step used in alpha parameter quantization + AlphaStep = 0.1; +type + TLine = record + Count: Integer; + Index: Integer; + Alpha: Double; + Distance: Double; + end; + TLineArray = array of TLine; +var + AlphaStart, MinDist, SumAngles: Double; + AlphaSteps, DistCount, AccumulatorSize, I, AccumulatedCounts: Integer; + BestLines: TLineArray; + HoughAccumulator: array of Integer; + PageWidth, PageHeight: Integer; + ContentRect: TRect; + + // Classifies pixel at [X, Y] as black or white using threshold. + function IsPixelBlack(X, Y: Integer): Boolean; + begin + Result := Pixels[Y * Width + X] < Treshold; + end; + + // Calculates final angle for given angle step. + function GetFinalAngle(StepIndex: Integer): Double; + begin + Result := AlphaStart + StepIndex * AlphaStep; + end; + + // Calculates angle and distance parameters for all lines + // going through point [X, Y]. + procedure CalcLines(X, Y: Integer); + var + D, Rads: Double; + I, DIndex, Index: Integer; + Sin, Cos: Extended; + begin + for I := 0 to AlphaSteps - 1 do + begin + // Angle for current step in radians + Rads := GetFinalAngle(I) * PI / 180; + SinCos(Rads, Sin, Cos); + // Parameter D(distance from origin) of the line y=tg(alpha)x + d + D := Y * Cos - X * Sin; + // Calc index into accumulator for current line + DIndex := Trunc(D - MinDist); + Index := DIndex * AlphaSteps + I; + // Add one vote for current line + HoughAccumulator[Index] := HoughAccumulator[Index] + 1; + end; + end; + + // Uses Hough transform to calculate all lines that intersect + // interesting points (those classified as beign on base line of the text). + procedure CalcHoughTransform; + var + Y, X: Integer; + begin + for Y := 0 to PageHeight - 1 do + for X := 0 to PageWidth - 1 do + begin + if IsPixelBlack(ContentRect.Left + X, ContentRect.Top + Y) and + not IsPixelBlack(ContentRect.Left + X, ContentRect.Top + Y + 1) then + begin + CalcLines(X, Y); + end; + end; + end; + + // Chooses "best" lines (with the most votes) from the accumulator + function GetBestLines(Count: Integer): TLineArray; + var + I, J, DistIndex, AlphaIndex: Integer; + Temp: TLine; + begin + SetLength(Result, Count); + + for I := 0 to AccumulatorSize - 1 do + begin + if HoughAccumulator[I] > Result[Count - 1].Count then + begin + // Current line has more votes than the last selected one, + // let's put it the pot + Result[Count - 1].Count := HoughAccumulator[I]; + Result[Count - 1].Index := I; + J := Count - 1; + + // Sort the lines based on number of votes + while (J > 0) and (Result[J].Count > Result[J - 1].Count) do + begin + Temp := Result[J]; + Result[J] := Result[J - 1]; + Result[J - 1] := Temp; + J := J - 1; + end; + end; + + AccumulatedCounts := AccumulatedCounts + HoughAccumulator[I]; + end; + + for I := 0 to Count - 1 do + begin + // Caculate line angle and distance according to index in the accumulator + DistIndex := Result[I].Index div AlphaSteps; + AlphaIndex := Result[I].Index - DistIndex * AlphaSteps; + Result[I].Alpha := GetFinalAngle(AlphaIndex); + Result[I].Distance := DistIndex + MinDist; + end; + end; + +begin + AccumulatedCounts := 0; + + // Use supplied page content rect or just the whole image + ContentRect := Rect(0, 0, Width, Height); + if DetectionArea <> nil then + begin + Assert((RectWidth(DetectionArea^) <= Width) and (RectHeight(DetectionArea^) <= Height)); + ContentRect := DetectionArea^; + end; + + PageWidth := ContentRect.Right - ContentRect.Left; + PageHeight := ContentRect.Bottom - ContentRect.Top; + if (ContentRect.Bottom = Height) then + Dec(PageHeight); // Don't check for black pixels outsize of image in CalcHoughTransform() + + AlphaStart := -MaxAngle; + AlphaSteps := Ceil(2 * MaxAngle / AlphaStep); // Number of angle steps = samples from interval <-MaxAngle, MaxAngle> + MinDist := -Max(PageWidth, PageHeight); + DistCount := 2 * (PageWidth + PageHeight); + + // Determine the size of line accumulator + AccumulatorSize := DistCount * AlphaSteps; + SetLength(HoughAccumulator, AccumulatorSize); + + // Calculate Hough transform + CalcHoughTransform; + + // Get the best lines with most votes + BestLines := GetBestLines(BestLinesCount); + + // Average angles of the selected lines to get the rotation angle of the image + SumAngles := 0; + for I := 0 to BestLinesCount - 1 do + SumAngles := SumAngles + BestLines[I].Alpha; + + Result := SumAngles / BestLinesCount; + + if Stats <> nil then + begin + Stats.BestCount := BestLines[0].Count; + Stats.PixelCount := PageWidth * PageHeight; + Stats.AccumulatorSize := AccumulatorSize; + Stats.AccumulatedCounts := AccumulatedCounts; + Stats.TestedPixels := AccumulatedCounts div AlphaSteps; + end; +end; + + +end. diff --git a/resources/libraries/deskew/Scripts/Compile.bat b/resources/libraries/deskew/Scripts/Compile.bat new file mode 100755 index 0000000..e2c9027 --- /dev/null +++ b/resources/libraries/deskew/Scripts/Compile.bat @@ -0,0 +1,4 @@ +pushd .. +mkdir Dcu +fpc -FuImaging -FuImaging/ZLib -FuImaging/JpegLib -FuImaging/LibTiff -FiImaging -FUDcu -FlImaging\LibTiff\Compiled -O3 -B -Xs -XX -Mdelphi -FEBin deskew.lpr +popd \ No newline at end of file diff --git a/resources/libraries/deskew/Scripts/build_gui_release_macos.sh b/resources/libraries/deskew/Scripts/build_gui_release_macos.sh new file mode 100755 index 0000000..6fe373e --- /dev/null +++ b/resources/libraries/deskew/Scripts/build_gui_release_macos.sh @@ -0,0 +1,84 @@ +set -e + +APP_NAME=${1:-DeskewGui} +APP_TITLE=${2:-Deskew GUI} +BUNDLE_ID=${3:-net.galfarslair.deskewgui} +COPYRIGHT=${4:-© 2019 Marek Mauder} +ICON_PATH=${5:-"../Gui/deskewgui.icns"} +ICON_NAME=$(basename $ICON_PATH .icns) + +TIFFLIB_BIN=../_internal/TiffLibBins-macOS +RELEASE_DIR=../_internal/MacRelease +CONTENT_DIR=$RELEASE_DIR/$APP_NAME.app/Contents + +mkdir -p $RELEASE_DIR +rm -rf $RELEASE_DIR/* + +# build executables +rm -f ../Bin/deskew-mac +rm -f ../Gui/deskewgui +lazbuild --build-mode=Release-macOS -q --no-write-project ../deskew.lpi +lazbuild --build-mode=Release-macOS -q --no-write-project ../Gui/deskewgui.lpi + +# app bundle contents +mkdir -p $CONTENT_DIR/MacOS +mkdir $CONTENT_DIR/Resources + +cp ../Bin/deskew-mac $CONTENT_DIR/MacOS/ +cp ../Gui/deskewgui $CONTENT_DIR/MacOS/ +if [ -d $TIFFLIB_BIN ]; then + cp $TIFFLIB_BIN/*.dylib $CONTENT_DIR/MacOS/ +fi +chmod 755 $CONTENT_DIR/MacOS/*.* + +cp $ICON_PATH $CONTENT_DIR/Resources/ +chmod 644 $CONTENT_DIR/Resources/$ICON_NAME.icns + +echo "APPL????" > $CONTENT_DIR/PkgInfo + +# app bundle props +cat <<EOT >> $CONTENT_DIR/Info.plist +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleExecutable</key> + <string>deskewgui</string> + <key>CFBundleDisplayName</key> + <string>$APP_TITLE</string> + <key>CFBundleName</key> + <string>$APP_TITLE</string> + <key>CFBundleIdentifier</key> + <string>$BUNDLE_ID</string> + <key>CFBundleIconFile</key> + <string>$ICON_NAME</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleSignature</key> + <string>desk</string> + <key>CSResourcesFileMapped</key> + <true/> + <key>NSHumanReadableCopyright</key> + <string>$COPYRIGHT</string> + <key>NSHighResolutionCapable</key> + <true/> +</dict> +</plist> +EOT + +# update version from Lazarus project file +source ./get_gui_versions.sh +plutil -insert CFBundleShortVersionString -string $MAJOR_VER.$MINOR_VER $CONTENT_DIR/Info.plist +plutil -insert CFBundleVersion -string $MAJOR_VER.$MINOR_VER $CONTENT_DIR/Info.plist + +# create DMG from folder +DMG_NAME=$APP_NAME-$MAJOR_VER.$MINOR_VER.dmg + +hdiutil create -srcfolder $RELEASE_DIR -volname $APP_NAME -format UDZO -ov $RELEASE_DIR/$DMG_NAME + + +echo "Finished OK!" \ No newline at end of file diff --git a/resources/libraries/deskew/Scripts/compile-arm-linux.sh b/resources/libraries/deskew/Scripts/compile-arm-linux.sh new file mode 100755 index 0000000..6bbda4e --- /dev/null +++ b/resources/libraries/deskew/Scripts/compile-arm-linux.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +pushd .. +mkdir -p Dcu + +# By default FPC ARM cross compiler is built with "-dFPC_ARMHF" option and +# can only produce binaries with this ABI (https://wiki.freepascal.org/ARM_compiler_options). +# If you have ARM softfp target tou have to compile the cross compiler with "-dFPC_ARMEL". + +/mnt/c/Programs/FPCup/fpc/bin/x86_64-win64/ppcrossarm.exe -TLinux \ +-FuImaging -FuImaging/ZLib -FuImaging/JpegLib -FuImaging/LibTiff -FiImaging -FUDcu \ +-O3 -B -Xs -XX -Mdelphi -FEBin -odeskew-arm deskew.lpr + +popd \ No newline at end of file diff --git a/resources/libraries/deskew/Scripts/compile.sh b/resources/libraries/deskew/Scripts/compile.sh new file mode 100755 index 0000000..0b53729 --- /dev/null +++ b/resources/libraries/deskew/Scripts/compile.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +pushd .. +mkdir -p Dcu + +fpc -FuImaging -FuImaging/ZLib -FuImaging/JpegLib -FuImaging/LibTiff -FiImaging -FUDcu -O3 -B -Xs -XX -Mdelphi -FEBin deskew.lpr + +popd \ No newline at end of file diff --git a/resources/libraries/deskew/Scripts/create_cli_release_zip.sh b/resources/libraries/deskew/Scripts/create_cli_release_zip.sh new file mode 100755 index 0000000..cab57cf --- /dev/null +++ b/resources/libraries/deskew/Scripts/create_cli_release_zip.sh @@ -0,0 +1,40 @@ +set -e + +cd .. + +# extract version number from main unit +VERSION_STR=$(grep -oP "(?<=SAppTitle = 'Deskew )([0-9\.]+)" MainUnit.pas) + +ZIP_NAME=Deskew-$VERSION_STR.zip +RELEASE_DIR=_internal/Release-CLI +CONTENTS_DIR=$RELEASE_DIR/Deskew +rm -rf $RELEASE_DIR/* +mkdir -p $CONTENTS_DIR + +# clone sources (without .hg folder) +hg archive -t files $CONTENTS_DIR +rm $CONTENTS_DIR/.hg* + +# copy binaries to release folder (must be prebuilt - no platfrom where we could cross-compile all atm) +cp Bin/deskew $CONTENTS_DIR/Bin +cp Bin/deskew.exe $CONTENTS_DIR/Bin +cp Bin/deskew32.exe $CONTENTS_DIR/Bin +cp Bin/deskew-mac $CONTENTS_DIR/Bin +cp Bin/deskew-arm $CONTENTS_DIR/Bin +chmod 755 $CONTENTS_DIR/Bin/deskew* + +# build the ZIP (in Linux/WSL so the exe permissions are presserved) +cd $RELEASE_DIR +zip -r -9 -q $ZIP_NAME Deskew + +# check expected contents +OUTPUT=$(unzip -Z $ZIP_NAME) + +echo "$OUTPUT" | grep -q "^-r.xr.xr.x.*unx.*deskew$" +echo "$OUTPUT" | grep -q "^-r.xr.xr.x.*unx.*deskew-mac$" +echo "$OUTPUT" | grep -q "^-r.xr.xr.x.*unx.*deskew-arm$" + +echo "Finished OK with $RELEASE_DIR/$ZIP_NAME!" + + + diff --git a/resources/libraries/deskew/Scripts/create_gui_release_zip.sh b/resources/libraries/deskew/Scripts/create_gui_release_zip.sh new file mode 100755 index 0000000..fd9fbc9 --- /dev/null +++ b/resources/libraries/deskew/Scripts/create_gui_release_zip.sh @@ -0,0 +1,29 @@ +set -e + +RELEASE_DIR=../_internal/Release-GUI +rm -f $RELEASE_DIR/*.zip + +# get version from Lazarus project file +source ./get_gui_versions.sh +VERSION_STR=$MAJOR_VER.$MINOR_VER + +echo "Building RELEASE zip for v$VERSION_STR" + +# create the zip in Linux/WSL so the permissions are presserved +chmod 755 $RELEASE_DIR/deskewgui +chmod 644 $RELEASE_DIR/DeskewGui-$VERSION_STR.dmg + +ZIP_CMD="zip -j -9 $RELEASE_DIR/DeskewGui-$VERSION_STR.zip -@" + +echo $RELEASE_DIR/deskewgui | $ZIP_CMD +echo $RELEASE_DIR/deskewgui.exe | $ZIP_CMD +echo $RELEASE_DIR/DeskewGui-$VERSION_STR.dmg | $ZIP_CMD + +# check expected contents +OUTPUT=$(unzip -Z $RELEASE_DIR/DeskewGui-$VERSION_STR.zip) + +echo "$OUTPUT" | grep -q "^-r.xr.xr.x.*unx.*deskewgui$" +echo "$OUTPUT" | grep -q "^-.*unx.*deskewgui.exe$" +echo "$OUTPUT" | grep -q "^-r..r..r...*unx.*DeskewGui-$VERSION_STR.dmg$" + +echo "Finished OK with $RELEASE_DIR/DeskewGui-$VERSION_STR.zip!" \ No newline at end of file diff --git a/resources/libraries/deskew/Scripts/get_gui_versions.sh b/resources/libraries/deskew/Scripts/get_gui_versions.sh new file mode 100755 index 0000000..7493edb --- /dev/null +++ b/resources/libraries/deskew/Scripts/get_gui_versions.sh @@ -0,0 +1,7 @@ +set -e + +# get version from Lazarus project file +MAJOR_VER=$(grep 'MajorVersionNr' ../Gui/deskewgui.lpi | grep -oE '[0-9]+' || true) +MAJOR_VER=${MAJOR_VER:-0} # if major=0 it's not included in project file +MINOR_VER=$(grep 'MinorVersionNr' ../Gui/deskewgui.lpi | grep -oE '[0-9]+') + diff --git a/resources/libraries/deskew/deskew.dpr b/resources/libraries/deskew/deskew.dpr new file mode 100755 index 0000000..7b28e51 --- /dev/null +++ b/resources/libraries/deskew/deskew.dpr @@ -0,0 +1,47 @@ +{ + Deskew + by Marek Mauder + http://galfar.vevb.net/deskew + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +program deskew; + +{$APPTYPE CONSOLE} + +{$IFDEF FPC} + {$ERROR 'Use deskew.lpr as FPC/Lazarus project file'} +{$ENDIF} + +uses + RotationDetector in 'RotationDetector.pas', + CmdLineOptions in 'CmdLineOptions.pas', + ImageUtils in 'ImageUtils.pas', + MainUnit in 'MainUnit.pas'; + +begin +{$IFDEF DEBUG} + ReportMemoryLeaksOnShutdown := True; +{$ENDIF} + RunDeskew; +end. diff --git a/resources/libraries/deskew/deskew.dproj b/resources/libraries/deskew/deskew.dproj new file mode 100755 index 0000000..5a67c98 --- /dev/null +++ b/resources/libraries/deskew/deskew.dproj @@ -0,0 +1,115 @@ + <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <ProjectGuid>{3B82BB3A-34E5-4CB2-9865-C7BF0FB7CF1A}</ProjectGuid> + <ProjectVersion>13.4</ProjectVersion> + <MainSource>deskew.dpr</MainSource> + <Config Condition="'$(Config)'==''">Release</Config> + <DCC_DCCCompiler>DCC32</DCC_DCCCompiler> + <FrameworkType>None</FrameworkType> + <Base>True</Base> + <TargetedPlatforms>7</TargetedPlatforms> + <AppType>Console</AppType> + <Platform>Win32</Platform> + </PropertyGroup> + <PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''"> + <Base>true</Base> + </PropertyGroup> + <PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_1)'!=''"> + <Cfg_1>true</Cfg_1> + <CfgParent>Base</CfgParent> + <Base>true</Base> + </PropertyGroup> + <PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_2)'!=''"> + <Cfg_2>true</Cfg_2> + <CfgParent>Base</CfgParent> + <Base>true</Base> + </PropertyGroup> + <PropertyGroup Condition="'$(Base)'!=''"> + <DCC_UnitSearchPath>Imaging;Imaging\LibTiff;Imaging\JpegLib;Imaging\ZLib;$(DCC_UnitSearchPath)</DCC_UnitSearchPath> + <DCC_Define>DONT_LINK_EXTRAS;$(DCC_Define)</DCC_Define> + <DCC_DcuOutput>Dcu\$(Platform)\$(Config)</DCC_DcuOutput> + <DCC_ExeOutput>Bin</DCC_ExeOutput> + <Manifest_File>None</Manifest_File> + <VerInfo_Locale>1029</VerInfo_Locale> + <VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=</VerInfo_Keys> + <DCC_Namespace>System;Winapi;$(DCC_Namespace)</DCC_Namespace> + <DCC_DependencyCheckOutputName>deskew.exe</DCC_DependencyCheckOutputName> + <DCC_ImageBase>00400000</DCC_ImageBase> + </PropertyGroup> + <PropertyGroup Condition="'$(Cfg_1)'!=''"> + <DCC_Define>RELEASE;$(DCC_Define)</DCC_Define> + </PropertyGroup> + <PropertyGroup Condition="'$(Cfg_2)'!=''"> + <DCC_GenerateStackFrames>true</DCC_GenerateStackFrames> + <DCC_Optimize>false</DCC_Optimize> + <DCC_DebugDCUs>true</DCC_DebugDCUs> + <DCC_Define>DEBUG;$(DCC_Define)</DCC_Define> + </PropertyGroup> + <ItemGroup> + <DelphiCompile Include="deskew.dpr"> + <MainSource>MainSource</MainSource> + </DelphiCompile> + <DCCReference Include="RotationDetector.pas"/> + <DCCReference Include="CmdLineOptions.pas"/> + <DCCReference Include="ImageUtils.pas"/> + <DCCReference Include="MainUnit.pas"/> + <BuildConfiguration Include="Debug"> + <Key>Cfg_2</Key> + <CfgParent>Base</CfgParent> + </BuildConfiguration> + <BuildConfiguration Include="Base"> + <Key>Base</Key> + </BuildConfiguration> + <BuildConfiguration Include="Release"> + <Key>Cfg_1</Key> + <CfgParent>Base</CfgParent> + </BuildConfiguration> + </ItemGroup> + <Import Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')" Project="$(BDS)\Bin\CodeGear.Delphi.Targets"/> + <Import Condition="Exists('$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj')" Project="$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj"/> + <ProjectExtensions> + <Borland.Personality>Delphi.Personality.12</Borland.Personality> + <Borland.ProjectType/> + <BorlandProject> + <Delphi.Personality> + <Source> + <Source Name="MainSource">deskew.dpr</Source> + </Source> + <Parameters> + <Parameters Name="RunParams">-o out.jpg "18 (source-300dpi).jpg"</Parameters> + </Parameters> + <VersionInfo> + <VersionInfo Name="IncludeVerInfo">False</VersionInfo> + <VersionInfo Name="AutoIncBuild">False</VersionInfo> + <VersionInfo Name="MajorVer">1</VersionInfo> + <VersionInfo Name="MinorVer">0</VersionInfo> + <VersionInfo Name="Release">0</VersionInfo> + <VersionInfo Name="Build">0</VersionInfo> + <VersionInfo Name="Debug">False</VersionInfo> + <VersionInfo Name="PreRelease">False</VersionInfo> + <VersionInfo Name="Special">False</VersionInfo> + <VersionInfo Name="Private">False</VersionInfo> + <VersionInfo Name="DLL">False</VersionInfo> + <VersionInfo Name="Locale">1029</VersionInfo> + <VersionInfo Name="CodePage">1250</VersionInfo> + </VersionInfo> + <VersionInfoKeys> + <VersionInfoKeys Name="CompanyName"/> + <VersionInfoKeys Name="FileDescription"/> + <VersionInfoKeys Name="FileVersion">1.0.0.0</VersionInfoKeys> + <VersionInfoKeys Name="InternalName"/> + <VersionInfoKeys Name="LegalCopyright"/> + <VersionInfoKeys Name="LegalTrademarks"/> + <VersionInfoKeys Name="OriginalFilename"/> + <VersionInfoKeys Name="ProductName"/> + <VersionInfoKeys Name="ProductVersion">1.0.0.0</VersionInfoKeys> + <VersionInfoKeys Name="Comments"/> + </VersionInfoKeys> + </Delphi.Personality> + <Platforms> + <Platform value="Win32">True</Platform> + </Platforms> + </BorlandProject> + <ProjectFileVersion>12</ProjectFileVersion> + </ProjectExtensions> + </Project> diff --git a/resources/libraries/deskew/deskew.lpi b/resources/libraries/deskew/deskew.lpi new file mode 100755 index 0000000..58a08d0 --- /dev/null +++ b/resources/libraries/deskew/deskew.lpi @@ -0,0 +1,269 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CONFIG> + <ProjectOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <General> + <Flags> + <MainUnitHasCreateFormStatements Value="False"/> + <MainUnitHasTitleStatement Value="False"/> + <LRSInOutputDirectory Value="False"/> + </Flags> + <SessionStorage Value="InProjectDir"/> + <MainUnit Value="0"/> + <Title Value="Deskew"/> + <UseAppBundle Value="False"/> + <ResourceType Value="res"/> + </General> + <i18n> + <EnableI18N LFM="False"/> + </i18n> + <BuildModes Count="5"> + <Item1 Name="Release" Default="True"/> + <Item2 Name="Debug"> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="Bin\deskew"/> + </Target> + <SearchPaths> + <IncludeFiles Value="Imaging;$(ProjOutDir)"/> + <Libraries Value="Imaging\LibTiff\Compiled"/> + <OtherUnitFiles Value="Imaging;Imaging\ZLib;Imaging\JpegLib;Imaging\LibTiff"/> + <UnitOutputDirectory Value="dcu\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Parsing> + <SyntaxOptions> + <SyntaxMode Value="Delphi"/> + <IncludeAssertionCode Value="True"/> + </SyntaxOptions> + </Parsing> + <CodeGeneration> + <Checks> + <IOChecks Value="True"/> + <StackChecks Value="True"/> + </Checks> + <Optimizations> + <OptimizationLevel Value="0"/> + </Optimizations> + </CodeGeneration> + <Linking> + <Debugging> + <UseHeaptrc Value="True"/> + </Debugging> + </Linking> + <Other> + <Verbosity> + <ShowHints Value="False"/> + <ShowHintsForUnusedUnitsInMainSrc Value="True"/> + </Verbosity> + <CustomOptions Value="-dDEBUG"/> + </Other> + </CompilerOptions> + </Item2> + <Item3 Name="Release-Win64"> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="Bin\deskew"/> + </Target> + <SearchPaths> + <IncludeFiles Value="Imaging;$(ProjOutDir)"/> + <Libraries Value="Imaging\LibTiff\Compiled"/> + <OtherUnitFiles Value="Imaging;Imaging\ZLib;Imaging\JpegLib;Imaging\LibTiff"/> + <UnitOutputDirectory Value="dcu\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Parsing> + <SyntaxOptions> + <SyntaxMode Value="Delphi"/> + </SyntaxOptions> + </Parsing> + <CodeGeneration> + <SmartLinkUnit Value="True"/> + <TargetCPU Value="x86_64"/> + <TargetOS Value="win64"/> + <Optimizations> + <OptimizationLevel Value="3"/> + <VariablesInRegisters Value="True"/> + </Optimizations> + </CodeGeneration> + <Linking> + <Debugging> + <GenerateDebugInfo Value="False"/> + </Debugging> + <LinkSmart Value="True"/> + </Linking> + <Other> + <Verbosity> + <ShowNotes Value="False"/> + <ShowHints Value="False"/> + </Verbosity> + </Other> + </CompilerOptions> + </Item3> + <Item4 Name="Release-Win32"> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="Bin\deskew32"/> + </Target> + <SearchPaths> + <IncludeFiles Value="Imaging;$(ProjOutDir)"/> + <Libraries Value="Imaging\LibTiff\Compiled"/> + <OtherUnitFiles Value="Imaging;Imaging\ZLib;Imaging\JpegLib;Imaging\LibTiff"/> + <UnitOutputDirectory Value="dcu\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Parsing> + <SyntaxOptions> + <SyntaxMode Value="Delphi"/> + </SyntaxOptions> + </Parsing> + <CodeGeneration> + <SmartLinkUnit Value="True"/> + <TargetCPU Value="i386"/> + <TargetOS Value="win32"/> + <Optimizations> + <OptimizationLevel Value="3"/> + <VariablesInRegisters Value="True"/> + </Optimizations> + </CodeGeneration> + <Linking> + <Debugging> + <GenerateDebugInfo Value="False"/> + </Debugging> + <LinkSmart Value="True"/> + </Linking> + <Other> + <Verbosity> + <ShowNotes Value="False"/> + <ShowHints Value="False"/> + </Verbosity> + </Other> + </CompilerOptions> + </Item4> + <Item5 Name="Release-macOS"> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="Bin\deskew-mac"/> + </Target> + <SearchPaths> + <IncludeFiles Value="Imaging;$(ProjOutDir)"/> + <Libraries Value="Imaging\LibTiff\Compiled"/> + <OtherUnitFiles Value="Imaging;Imaging\ZLib;Imaging\JpegLib;Imaging\LibTiff"/> + <UnitOutputDirectory Value="dcu\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Parsing> + <SyntaxOptions> + <SyntaxMode Value="Delphi"/> + </SyntaxOptions> + </Parsing> + <CodeGeneration> + <SmartLinkUnit Value="True"/> + <TargetCPU Value="x86_64"/> + <TargetOS Value="darwin"/> + <Optimizations> + <OptimizationLevel Value="3"/> + <VariablesInRegisters Value="True"/> + </Optimizations> + </CodeGeneration> + <Linking> + <Debugging> + <GenerateDebugInfo Value="False"/> + </Debugging> + <LinkSmart Value="True"/> + </Linking> + <Other> + <Verbosity> + <ShowNotes Value="False"/> + <ShowHints Value="False"/> + </Verbosity> + </Other> + </CompilerOptions> + </Item5> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + <RunParams> + <FormatVersion Value="2"/> + <Modes Count="0"/> + </RunParams> + <Units Count="5"> + <Unit0> + <Filename Value="deskew.lpr"/> + <IsPartOfProject Value="True"/> + </Unit0> + <Unit1> + <Filename Value="RotationDetector.pas"/> + <IsPartOfProject Value="True"/> + </Unit1> + <Unit2> + <Filename Value="CmdLineOptions.pas"/> + <IsPartOfProject Value="True"/> + </Unit2> + <Unit3> + <Filename Value="ImageUtils.pas"/> + <IsPartOfProject Value="True"/> + </Unit3> + <Unit4> + <Filename Value="MainUnit.pas"/> + <IsPartOfProject Value="True"/> + </Unit4> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="Bin\deskew"/> + </Target> + <SearchPaths> + <IncludeFiles Value="Imaging;$(ProjOutDir)"/> + <Libraries Value="Imaging\LibTiff\Compiled"/> + <OtherUnitFiles Value="Imaging;Imaging\ZLib;Imaging\JpegLib;Imaging\LibTiff"/> + <UnitOutputDirectory Value="dcu\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Parsing> + <SyntaxOptions> + <SyntaxMode Value="Delphi"/> + </SyntaxOptions> + </Parsing> + <CodeGeneration> + <SmartLinkUnit Value="True"/> + <Optimizations> + <OptimizationLevel Value="3"/> + <VariablesInRegisters Value="True"/> + </Optimizations> + </CodeGeneration> + <Linking> + <Debugging> + <GenerateDebugInfo Value="False"/> + </Debugging> + <LinkSmart Value="True"/> + </Linking> + <Other> + <Verbosity> + <ShowNotes Value="False"/> + <ShowHints Value="False"/> + </Verbosity> + </Other> + </CompilerOptions> + <Debugging> + <Exceptions Count="3"> + <Item1> + <Name Value="EAbort"/> + </Item1> + <Item2> + <Name Value="ECodetoolError"/> + </Item2> + <Item3> + <Name Value="EFOpenError"/> + </Item3> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/resources/libraries/deskew/deskew.lpr b/resources/libraries/deskew/deskew.lpr new file mode 100755 index 0000000..d9dc30f --- /dev/null +++ b/resources/libraries/deskew/deskew.lpr @@ -0,0 +1,47 @@ +{ + Deskew + by Marek Mauder + http://galfar.vevb.net/deskew + + The contents of this file are used with permission, subject to the Mozilla + Public License Version 1.1 (the "License"); you may not use this file except + in compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/MPL-1.1.html + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + + Alternatively, the contents of this file may be used under the terms of the + GNU Lesser General Public License (the "LGPL License"), in which case the + provisions of the LGPL License are applicable instead of those above. + If you wish to allow use of your version of this file only under the terms + of the LGPL License and not to allow others to use your version of this file + under the MPL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the LGPL + License. If you do not delete the provisions above, a recipient may use + your version of this file under either the MPL or the LGPL License. + + For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html +} + +program deskew; + +{$IFDEF MSWINDOWS} + {$APPTYPE CONSOLE} +{$ENDIF} + +uses + RotationDetector in 'RotationDetector.pas', + CmdLineOptions in 'CmdLineOptions.pas', + ImageUtils in 'ImageUtils.pas', + MainUnit in 'MainUnit.pas'; + +begin +{$IFDEF DEBUG} +{$IFNDEF FPC} + ReportMemoryLeaksOnShutdown := True; +{$ENDIF} +{$ENDIF} + RunDeskew; +end. diff --git a/resources/python/dewarp b/resources/python/dewarp new file mode 160000 index 0000000..c40293a --- /dev/null +++ b/resources/python/dewarp @@ -0,0 +1 @@ +Subproject commit c40293a606194a322fa116ba5a7bec38148e07ff diff --git a/resources/python/unproject b/resources/python/unproject new file mode 160000 index 0000000..734d21a --- /dev/null +++ b/resources/python/unproject @@ -0,0 +1 @@ +Subproject commit 734d21ad596000cff276ccaf118954362b5eb37e