From fc562d11f0574edf098047497af633b76e0974a2 Mon Sep 17 00:00:00 2001 From: DRC Date: Mon, 7 Mar 2022 14:29:37 -0600 Subject: [PATCH 001/162] Bump version to 2.2 alpha1 ... ... to prepare for new features --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1198eced2..a672ebac0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ if(CMAKE_EXECUTABLE_SUFFIX) endif() project(libjpeg-turbo C) -set(VERSION 2.1.3) +set(VERSION 2.1.80) set(COPYRIGHT_YEAR "1991-2022") string(REPLACE "." ";" VERSION_TRIPLET ${VERSION}) list(GET VERSION_TRIPLET 0 VERSION_MAJOR) From 7fec5074f962b20ed00b4f5da4533e1e8d4ed8ac Mon Sep 17 00:00:00 2001 From: DRC Date: Tue, 8 Mar 2022 12:34:11 -0600 Subject: [PATCH 002/162] Support 8-bit & 12-bit JPEGs using the same build Partially implements #199 This commit also implements a request from #178 (the ability to compile the libjpeg example as a standalone program.) --- .github/workflows/build.yml | 2 +- CMakeLists.txt | 991 ++++++++++--------- ChangeLog.md | 15 + README.ijg | 2 +- cdjpeg.h | 4 +- cmakescripts/BuildPackages.cmake | 13 +- cmyk.h | 4 +- example.txt => example.c | 412 +++++++- jcapimin.c | 2 +- jcapistd.c | 6 +- jccoefct.c | 6 +- jccolor.c | 4 +- jcdctmgr.c | 4 +- jchuff.c | 2 +- jcicc.c | 4 +- jcinit.c | 4 +- jcmainct.c | 6 +- jcmarker.c | 4 +- jcmaster.c | 4 +- jcomapi.c | 6 +- jconfig.h.in | 34 +- jconfigint.h.in | 29 + jcparam.c | 4 +- jcphuff.c | 2 +- jcprepct.c | 2 +- jcsample.c | 4 +- jctrans.c | 2 +- jdapimin.c | 2 +- jdatadst.c | 2 +- jdatasrc.c | 2 +- jdcoefct.h | 3 +- jdcolor.c | 4 +- jddctmgr.c | 2 +- jdhuff.c | 4 +- jdicc.c | 4 +- jdinput.c | 2 +- jdmainct.h | 4 +- jdmarker.c | 2 +- jdmaster.c | 2 +- jdmerge.c | 4 +- jdmerge.h | 4 +- jdphuff.c | 2 +- jdpostct.c | 6 +- jdsample.h | 4 +- jdtrans.c | 4 +- jerror.c | 3 +- jfdctflt.c | 6 +- jfdctfst.c | 4 +- jfdctint.c | 4 +- jidctflt.c | 4 +- jidctfst.c | 4 +- jidctint.c | 4 +- jidctred.c | 4 +- jmemmgr.c | 2 +- jmemnobs.c | 4 +- jmorecfg.h | 22 +- jpeg12int.h | 391 ++++++++ jpeg12lib.h | 1153 +++++++++++++++++++++++ jpegint.h | 15 +- jpeglib.h | 10 +- jpeglibint.h | 328 +++++++ jquant1.c | 4 +- jquant2.c | 4 +- jsimd_none.c | 4 +- jutils.c | 2 +- libjpeg.map.in | 2 +- libjpeg.txt | 62 +- release/installer.nsi.in | 54 +- release/libjpeg12.pc.in | 10 + release/rpm.spec.in | 20 + sharedlib/CMakeLists.txt | 98 +- structure.txt | 5 +- transupp.c | 2 +- turbojpeg.c | 6 +- win/gcc/projectTargets-release.cmake.in | 20 + win/jconfig.h.in | 25 - win/jpeg12-62-memsrcdst.def | 108 +++ win/jpeg12-62.def | 106 +++ win/jpeg12-7-memsrcdst.def | 110 +++ win/jpeg12-7.def | 108 +++ win/jpeg12-8.def | 111 +++ win/vc/projectTargets-release.cmake.in | 20 + 82 files changed, 3796 insertions(+), 672 deletions(-) rename example.txt => example.c (60%) create mode 100644 jpeg12int.h create mode 100644 jpeg12lib.h create mode 100644 jpeglibint.h create mode 100644 release/libjpeg12.pc.in delete mode 100644 win/jconfig.h.in create mode 100644 win/jpeg12-62-memsrcdst.def create mode 100644 win/jpeg12-62.def create mode 100644 win/jpeg12-7-memsrcdst.def create mode 100644 win/jpeg12-7.def create mode 100644 win/jpeg12-8.def diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9a5fd710b..c11131a29 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -119,7 +119,7 @@ jobs: run: | mkdir build pushd build - cmake -G"Unix Makefiles" -DWITH_12BIT=1 \ + cmake -G"Unix Makefiles" -DWITH_12BIT=0 \ -DCMAKE_C_FLAGS='--std=gnu90 -Wall -Werror -Wextra -Wpedantic -pedantic-errors -Wdouble-promotion -Wformat-overflow=2 -Wformat-security -Wformat-signedness -Wformat-truncation=2 -Wformat-y2k -Wmissing-include-dirs -Wshift-overflow=2 -Wswitch-bool -Wno-unused-parameter -Wuninitialized -Wstrict-overflow=2 -Wstringop-overflow=4 -Wstringop-truncation -Wduplicated-branches -Wduplicated-cond -Wdeclaration-after-statement -Wshadow -Wunsafe-loop-optimizations -Wundef -Wcast-align -Wno-clobbered -Wjump-misses-init -Wno-sign-compare -Wlogical-op -Waggregate-return -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Wpacked -Wredundant-decls -Wnested-externs -Winline -Wno-long-long -Wdisabled-optimization -Wno-overlength-strings' \ .. export NUMCPUS=`grep -c '^processor' /proc/cpuinfo` diff --git a/CMakeLists.txt b/CMakeLists.txt index a672ebac0..42a35e60c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -187,7 +187,7 @@ option(ENABLE_STATIC "Build static libraries" TRUE) boolean_number(ENABLE_STATIC) option(REQUIRE_SIMD "Generate a fatal error if SIMD extensions are not available for this platform (default is to fall back to a non-SIMD build)" FALSE) boolean_number(REQUIRE_SIMD) -option(WITH_12BIT "Encode/decode JPEG images with 12-bit samples (implies WITH_ARITH_DEC=0 WITH_ARITH_ENC=0 WITH_JAVA=0 WITH_SIMD=0 WITH_TURBOJPEG=0 )" FALSE) +option(WITH_12BIT "Include 12-bit JPEG support" TRUE) boolean_number(WITH_12BIT) option(WITH_ARITH_DEC "Include arithmetic decoding support when emulating the libjpeg v6b API/ABI" TRUE) boolean_number(WITH_ARITH_DEC) @@ -248,40 +248,23 @@ if(WITH_JPEG8) set(WITH_MEM_SRCDST 0) endif() -if(WITH_12BIT) - set(WITH_ARITH_DEC 0) - set(WITH_ARITH_ENC 0) - set(WITH_JAVA 0) - set(WITH_SIMD 0) - set(WITH_TURBOJPEG 0) - set(BITS_IN_JSAMPLE 12) -else() - set(BITS_IN_JSAMPLE 8) -endif() report_option(WITH_12BIT "12-bit JPEG support") if(WITH_ARITH_DEC) set(D_ARITH_CODING_SUPPORTED 1) endif() -if(NOT WITH_12BIT) - report_option(WITH_ARITH_DEC "Arithmetic decoding support") -endif() +report_option(WITH_ARITH_DEC "Arithmetic decoding support") if(WITH_ARITH_ENC) set(C_ARITH_CODING_SUPPORTED 1) endif() -if(NOT WITH_12BIT) - report_option(WITH_ARITH_ENC "Arithmetic encoding support") -endif() +report_option(WITH_ARITH_ENC "Arithmetic encoding support") -if(NOT WITH_12BIT) - report_option(WITH_TURBOJPEG "TurboJPEG API library") - report_option(WITH_JAVA "TurboJPEG Java wrapper") -endif() +report_option(WITH_TURBOJPEG "TurboJPEG API library") +report_option(WITH_JAVA "TurboJPEG Java wrapper") if(WITH_MEM_SRCDST) set(MEM_SRCDST_SUPPORTED 1) - set(MEM_SRCDST_FUNCTIONS "global: jpeg_mem_dest; jpeg_mem_src;") endif() if(NOT WITH_JPEG8) report_option(WITH_MEM_SRCDST "In-memory source/destination managers") @@ -514,15 +497,22 @@ if(UNIX AND NOT APPLE) endif() # Generate files -if(WIN32) - configure_file(win/jconfig.h.in jconfig.h) -else() - configure_file(jconfig.h.in jconfig.h) -endif() +configure_file(jconfig.h.in jconfig.h) configure_file(jconfigint.h.in jconfigint.h) configure_file(jversion.h.in jversion.h) if(UNIX) + if(WITH_MEM_SRCDST) + set(MEM_SRCDST_FUNCTIONS "global: jpeg_mem_dest; jpeg_mem_src;") + endif() + set(SIMD_FUNCTIONS "jsimd_*") configure_file(libjpeg.map.in libjpeg.map) + if(WITH_12BIT) + if(WITH_MEM_SRCDST) + set(MEM_SRCDST_FUNCTIONS "global: jpeg12_mem_dest; jpeg12_mem_src;") + endif() + set(SIMD_FUNCTIONS "jsimd12_*") + configure_file(libjpeg.map.in libjpeg12.map) + endif() endif() # Include directories and compiler definitions @@ -538,13 +528,15 @@ if(CMAKE_EXECUTABLE_SUFFIX_TMP) endif() message(STATUS "CMAKE_EXECUTABLE_SUFFIX = ${CMAKE_EXECUTABLE_SUFFIX}") -set(JPEG_SOURCES jcapimin.c jcapistd.c jccoefct.c jccolor.c jcdctmgr.c jchuff.c - jcicc.c jcinit.c jcmainct.c jcmarker.c jcmaster.c jcomapi.c jcparam.c - jcphuff.c jcprepct.c jcsample.c jctrans.c jdapimin.c jdapistd.c jdatadst.c - jdatasrc.c jdcoefct.c jdcolor.c jddctmgr.c jdhuff.c jdicc.c jdinput.c - jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdphuff.c jdpostct.c jdsample.c - jdtrans.c jerror.c jfdctflt.c jfdctfst.c jfdctint.c jidctflt.c jidctfst.c - jidctint.c jidctred.c jquant1.c jquant2.c jutils.c jmemmgr.c jmemnobs.c) +set(JPEG12_SOURCES jcapimin.c jcapistd.c jccoefct.c jccolor.c jcdctmgr.c + jchuff.c jcicc.c jcinit.c jcmainct.c jcmarker.c jcmaster.c jcomapi.c + jcparam.c jcphuff.c jcprepct.c jcsample.c jctrans.c jdapimin.c jdapistd.c + jdatadst.c jdatasrc.c jdcoefct.c jdcolor.c jddctmgr.c jdhuff.c jdicc.c + jdinput.c jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdphuff.c jdpostct.c + jdsample.c jdtrans.c jerror.c jfdctflt.c jfdctfst.c jfdctint.c jidctflt.c + jidctfst.c jidctint.c jidctred.c jquant1.c jquant2.c jutils.c jmemmgr.c + jmemnobs.c) +set(JPEG_SOURCES ${JPEG12_SOURCES}) if(WITH_ARITH_ENC OR WITH_ARITH_DEC) set(JPEG_SOURCES ${JPEG_SOURCES} jaricom.c) @@ -563,7 +555,7 @@ if(WITH_SIMD) if(NEON_INTRINSICS) add_definitions(-DNEON_INTRINSICS) endif() -elseif(NOT WITH_12BIT) +else() message(STATUS "SIMD extensions: None (WITH_SIMD = ${WITH_SIMD})") endif() if(WITH_SIMD) @@ -592,6 +584,15 @@ if(ENABLE_STATIC) if(NOT MSVC) set_target_properties(jpeg-static PROPERTIES OUTPUT_NAME jpeg) endif() + + if(WITH_12BIT) + add_library(jpeg12-static STATIC ${JPEG12_SOURCES} jsimd_none.c) + set_property(TARGET jpeg12-static PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=12") + if(NOT MSVC) + set_target_properties(jpeg12-static PROPERTIES OUTPUT_NAME jpeg12) + endif() + endif() endif() if(WITH_TURBOJPEG) @@ -672,28 +673,49 @@ endif() if(WIN32) set(USE_SETMODE "-DUSE_SETMODE") endif() -if(WITH_12BIT) - set(COMPILE_FLAGS "-DGIF_SUPPORTED -DPPM_SUPPORTED ${USE_SETMODE}") -else() - set(COMPILE_FLAGS "-DBMP_SUPPORTED -DGIF_SUPPORTED -DPPM_SUPPORTED -DTARGA_SUPPORTED ${USE_SETMODE}") - set(CJPEG_BMP_SOURCES rdbmp.c rdtarga.c) - set(DJPEG_BMP_SOURCES wrbmp.c wrtarga.c) -endif() if(ENABLE_STATIC) - add_executable(cjpeg-static cjpeg.c cdjpeg.c rdgif.c rdppm.c rdswitch.c - ${CJPEG_BMP_SOURCES}) - set_property(TARGET cjpeg-static PROPERTY COMPILE_FLAGS ${COMPILE_FLAGS}) + add_executable(cjpeg-static cjpeg.c cdjpeg.c rdbmp.c rdgif.c rdppm.c + rdswitch.c rdtarga.c) + set_property(TARGET cjpeg-static PROPERTY COMPILE_FLAGS + "-DBMP_SUPPORTED -DGIF_SUPPORTED -DPPM_SUPPORTED -DTARGA_SUPPORTED ${USE_SETMODE}") target_link_libraries(cjpeg-static jpeg-static) - add_executable(djpeg-static djpeg.c cdjpeg.c rdcolmap.c rdswitch.c wrgif.c - wrppm.c ${DJPEG_BMP_SOURCES}) - set_property(TARGET djpeg-static PROPERTY COMPILE_FLAGS ${COMPILE_FLAGS}) + add_executable(djpeg-static djpeg.c cdjpeg.c rdcolmap.c rdswitch.c wrbmp.c + wrgif.c wrppm.c wrtarga.c) + set_property(TARGET djpeg-static PROPERTY COMPILE_FLAGS + "-DBMP_SUPPORTED -DGIF_SUPPORTED -DPPM_SUPPORTED -DTARGA_SUPPORTED ${USE_SETMODE}") target_link_libraries(djpeg-static jpeg-static) add_executable(jpegtran-static jpegtran.c cdjpeg.c rdswitch.c transupp.c) target_link_libraries(jpegtran-static jpeg-static) set_property(TARGET jpegtran-static PROPERTY COMPILE_FLAGS "${USE_SETMODE}") + + if(WITH_12BIT) + add_executable(cjpeg12-static cjpeg.c cdjpeg.c rdgif.c rdppm.c rdswitch.c) + set_property(TARGET cjpeg12-static PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=12 -DGIF_SUPPORTED -DPPM_SUPPORTED ${USE_SETMODE}") + target_link_libraries(cjpeg12-static jpeg12-static) + + add_executable(djpeg12-static djpeg.c cdjpeg.c rdcolmap.c rdswitch.c + wrgif.c wrppm.c) + set_property(TARGET djpeg12-static PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=12 -DGIF_SUPPORTED -DPPM_SUPPORTED ${USE_SETMODE}") + target_link_libraries(djpeg12-static jpeg12-static) + + add_executable(jpeg12tran-static jpegtran.c cdjpeg.c rdswitch.c transupp.c) + target_link_libraries(jpeg12tran-static jpeg12-static) + set_property(TARGET jpeg12tran-static PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=12 ${USE_SETMODE}") + endif() + + add_executable(example-static ../example.c) + if(WITH_12BIT) + target_link_libraries(example-static jpeg-static jpeg12-static) + set_property(TARGET example-static PROPERTY COMPILE_FLAGS "-DWITH_12BIT") + else() + target_link_libraries(example-static jpeg-static) + endif() endif() add_executable(rdjpgcom rdjpgcom.c) @@ -721,124 +743,6 @@ endif() enable_testing() -if(WITH_12BIT) - set(TESTORIG testorig12.jpg) - set(MD5_JPEG_RGB_ISLOW 9d7369207c520d37f2c1cbfcb82b2964) - set(MD5_JPEG_RGB_ISLOW2 a00bd20d8ae49684640ef7177d2e0b64) - set(MD5_PPM_RGB_ISLOW f3301d2219783b8b3d942b7239fa50c0) - set(MD5_JPEG_422_IFAST_OPT 7322e3bd2f127f7de4b40d4480ce60e4) - set(MD5_PPM_422_IFAST 79807fa552899e66a04708f533e16950) - set(MD5_JPEG_440_ISLOW e25c1912e38367be505a89c410c1c2d2) - set(MD5_PPM_440_ISLOW e7d2e26288870cfcb30f3114ad01e380) - set(MD5_PPM_422M_IFAST 07737bfe8a7c1c87aaa393a0098d16b0) - set(MD5_JPEG_420_IFAST_Q100_PROG 9447cef4803d9b0f74bcf333cc710a29) - set(MD5_PPM_420_Q100_IFAST 1b3730122709f53d007255e8dfd3305e) - set(MD5_PPM_420M_Q100_IFAST 980a1a3c5bf9510022869d30b7d26566) - set(MD5_JPEG_GRAY_ISLOW 235c90707b16e2e069f37c888b2636d9) - set(MD5_PPM_GRAY_ISLOW 7213c10af507ad467da5578ca5ee1fca) - set(MD5_PPM_GRAY_ISLOW_RGB e96ee81c30a6ed422d466338bd3de65d) - set(MD5_JPEG_420S_IFAST_OPT 7af8e60be4d9c227ec63ac9b6630855e) - - set(MD5_JPEG_3x2_FLOAT_PROG_SSE a8c17daf77b457725ec929e215b603f8) - set(MD5_PPM_3x2_FLOAT_SSE 42876ab9e5c2f76a87d08db5fbd57956) - set(MD5_JPEG_3x2_FLOAT_PROG_NO_FP_CONTRACT a8c17daf77b457725ec929e215b603f8) - set(MD5_PPM_3x2_FLOAT_NO_FP_CONTRACT ${MD5_PPM_3x2_FLOAT_SSE}) - set(MD5_JPEG_3x2_FLOAT_PROG_FP_CONTRACT - ${MD5_JPEG_3x2_FLOAT_PROG_NO_FP_CONTRACT}) - set(MD5_PPM_3x2_FLOAT_FP_CONTRACT ${MD5_PPM_3x2_FLOAT_SSE}) - set(MD5_JPEG_3x2_FLOAT_PROG_387 bc6dbbefac2872f6b9d6c4a0ae60c3c0) - set(MD5_PPM_3x2_FLOAT_387 bcc5723c61560463ac60f772e742d092) - set(MD5_JPEG_3x2_FLOAT_PROG_MSVC e27840755870fa849872e58aa0cd1400) - set(MD5_PPM_3x2_FLOAT_MSVC 6c2880b83bb1aa41dfe330e7a9768690) - - set(MD5_JPEG_3x2_IFAST_PROG 1396cc2b7185cfe943d408c9d305339e) - set(MD5_PPM_3x2_IFAST 3975985ef6eeb0a2cdc58daa651ccc00) - set(MD5_PPM_420M_ISLOW_2_1 4ca6be2a6f326ff9eaab63e70a8259c0) - set(MD5_PPM_420M_ISLOW_15_8 12aa9f9534c1b3d7ba047322226365eb) - set(MD5_PPM_420M_ISLOW_13_8 f7e22817c7b25e1393e4ec101e9d4e96) - set(MD5_PPM_420M_ISLOW_11_8 800a16f9f4dc9b293197bfe11be10a82) - set(MD5_PPM_420M_ISLOW_9_8 06b7a92a9bc69f4dc36ec40f1937d55c) - set(MD5_PPM_420M_ISLOW_7_8 3ec444a14a4ab4eab88ffc49c48eca43) - set(MD5_PPM_420M_ISLOW_3_4 3e726b7ea872445b19437d1c1d4f0d93) - set(MD5_PPM_420M_ISLOW_5_8 a8a771abdc94301d20ffac119b2caccd) - set(MD5_PPM_420M_ISLOW_1_2 b419124dd5568b085787234866102866) - set(MD5_PPM_420M_ISLOW_3_8 343d19015531b7bbe746124127244fa8) - set(MD5_PPM_420M_ISLOW_1_4 35fd59d866e44659edfa3c18db2a3edb) - set(MD5_PPM_420M_ISLOW_1_8 ccaed48ac0aedefda5d4abe4013f4ad7) - set(MD5_PPM_420_ISLOW_SKIP15_31 86664cd9dc956536409e44e244d20a97) - set(MD5_PPM_420_ISLOW_PROG_CROP62x62_71_71 452a21656115a163029cfba5c04fa76a) - set(MD5_PPM_444_ISLOW_SKIP1_6 ef63901f71ef7a75cd78253fc0914f84) - set(MD5_PPM_444_ISLOW_PROG_CROP98x98_13_13 15b173fb5872d9575572fbcc1b05956f) - set(MD5_JPEG_CROP cdb35ff4b4519392690ea040c56ea99c) -else() - set(TESTORIG testorig.jpg) - set(MD5_JPEG_RGB_ISLOW 1d44a406f61da743b5fd31c0a9abdca3) - set(MD5_JPEG_RGB_ISLOW2 31d121e57b6c2934c890a7fc7763bcd4) - set(MD5_PPM_RGB_ISLOW 00a257f5393fef8821f2b88ac7421291) - set(MD5_BMP_RGB_ISLOW_565 f07d2e75073e4bb10f6c6f4d36e2e3be) - set(MD5_BMP_RGB_ISLOW_565D 4cfa0928ef3e6bb626d7728c924cfda4) - set(MD5_JPEG_422_IFAST_OPT 2540287b79d913f91665e660303ab2c8) - set(MD5_PPM_422_IFAST 35bd6b3f833bad23de82acea847129fa) - set(MD5_JPEG_440_ISLOW 538bc02bd4b4658fd85de6ece6cbeda6) - set(MD5_PPM_440_ISLOW 11e7eab7ef7ef3276934bb7e7b6bb377) - set(MD5_PPM_422M_IFAST 8dbc65323d62cca7c91ba02dd1cfa81d) - set(MD5_BMP_422M_IFAST_565 3294bd4d9a1f2b3d08ea6020d0db7065) - set(MD5_BMP_422M_IFAST_565D da98c9c7b6039511be4a79a878a9abc1) - set(MD5_JPEG_420_IFAST_Q100_PROG 0ba15f9dab81a703505f835f9dbbac6d) - set(MD5_PPM_420_Q100_IFAST 5a732542015c278ff43635e473a8a294) - set(MD5_PPM_420M_Q100_IFAST ff692ee9323a3b424894862557c092f1) - set(MD5_JPEG_GRAY_ISLOW 72b51f894b8f4a10b3ee3066770aa38d) - set(MD5_PPM_GRAY_ISLOW 8d3596c56eace32f205deccc229aa5ed) - set(MD5_PPM_GRAY_ISLOW_RGB 116424ac07b79e5e801f00508eab48ec) - set(MD5_BMP_GRAY_ISLOW_565 12f78118e56a2f48b966f792fedf23cc) - set(MD5_BMP_GRAY_ISLOW_565D bdbbd616441a24354c98553df5dc82db) - set(MD5_JPEG_420S_IFAST_OPT 388708217ac46273ca33086b22827ed8) - - set(MD5_JPEG_3x2_FLOAT_PROG_SSE 343e3f8caf8af5986ebaf0bdc13b5c71) - set(MD5_PPM_3x2_FLOAT_SSE 1a75f36e5904d6fc3a85a43da9ad89bb) - set(MD5_JPEG_3x2_FLOAT_PROG_NO_FP_CONTRACT 9bca803d2042bd1eb03819e2bf92b3e5) - set(MD5_PPM_3x2_FLOAT_NO_FP_CONTRACT f6bfab038438ed8f5522fbd33595dcdc) - set(MD5_JPEG_3x2_FLOAT_PROG_FP_CONTRACT - ${MD5_JPEG_3x2_FLOAT_PROG_NO_FP_CONTRACT}) - set(MD5_PPM_3x2_FLOAT_FP_CONTRACT 0e917a34193ef976b679a6b069b1be26) - set(MD5_JPEG_3x2_FLOAT_PROG_387 1657664a410e0822c924b54f6f65e6e9) - set(MD5_PPM_3x2_FLOAT_387 cb0a1f027f3d2917c902b5640214e025) - set(MD5_JPEG_3x2_FLOAT_PROG_MSVC 7999ce9cd0ee9b6c7043b7351ab7639d) - set(MD5_PPM_3x2_FLOAT_MSVC 28cdc448a6b75e97892f0e0f8d4b21f3) - - set(MD5_JPEG_3x2_IFAST_PROG 1ee5d2c1a77f2da495f993c8c7cceca5) - set(MD5_PPM_3x2_IFAST fd283664b3b49127984af0a7f118fccd) - set(MD5_JPEG_420_ISLOW_ARI e986fb0a637a8d833d96e8a6d6d84ea1) - set(MD5_JPEG_444_ISLOW_PROGARI 0a8f1c8f66e113c3cf635df0a475a617) - set(MD5_PPM_420M_IFAST_ARI 57251da28a35b46eecb7177d82d10e0e) - set(MD5_JPEG_420_ISLOW 9a68f56bc76e466aa7e52f415d0f4a5f) - set(MD5_PPM_420M_ISLOW_2_1 9f9de8c0612f8d06869b960b05abf9c9) - set(MD5_PPM_420M_ISLOW_15_8 b6875bc070720b899566cc06459b63b7) - set(MD5_PPM_420M_ISLOW_13_8 bc3452573c8152f6ae552939ee19f82f) - set(MD5_PPM_420M_ISLOW_11_8 d8cc73c0aaacd4556569b59437ba00a5) - set(MD5_PPM_420M_ISLOW_9_8 d25e61bc7eac0002f5b393aa223747b6) - set(MD5_PPM_420M_ISLOW_7_8 ddb564b7c74a09494016d6cd7502a946) - set(MD5_PPM_420M_ISLOW_3_4 8ed8e68808c3fbc4ea764fc9d2968646) - set(MD5_PPM_420M_ISLOW_5_8 a3363274999da2366a024efae6d16c9b) - set(MD5_PPM_420M_ISLOW_1_2 e692a315cea26b988c8e8b29a5dbcd81) - set(MD5_PPM_420M_ISLOW_3_8 79eca9175652ced755155c90e785a996) - set(MD5_PPM_420M_ISLOW_1_4 79cd778f8bf1a117690052cacdd54eca) - set(MD5_PPM_420M_ISLOW_1_8 391b3d4aca640c8567d6f8745eb2142f) - set(MD5_BMP_420_ISLOW_256 4980185e3776e89bd931736e1cddeee6) - set(MD5_BMP_420_ISLOW_565 bf9d13e16c4923b92e1faa604d7922cb) - set(MD5_BMP_420_ISLOW_565D 6bde71526acc44bcff76f696df8638d2) - set(MD5_BMP_420M_ISLOW_565 8dc0185245353cfa32ad97027342216f) - set(MD5_BMP_420M_ISLOW_565D ce034037d212bc403330df6f915c161b) - set(MD5_PPM_420_ISLOW_SKIP15_31 c4c65c1e43d7275cd50328a61e6534f0) - set(MD5_PPM_420_ISLOW_ARI_SKIP16_139 087c6b123db16ac00cb88c5b590bb74a) - set(MD5_PPM_420_ISLOW_PROG_CROP62x62_71_71 26eb36ccc7d1f0cb80cdabb0ac8b5d99) - set(MD5_PPM_420_ISLOW_ARI_CROP53x53_4_4 886c6775af22370257122f8b16207e6d) - set(MD5_PPM_444_ISLOW_SKIP1_6 5606f86874cf26b8fcee1117a0a436a6) - set(MD5_PPM_444_ISLOW_PROG_CROP98x98_13_13 db87dc7ce26bcdc7a6b56239ce2b9d6c) - set(MD5_PPM_444_ISLOW_ARI_CROP37x37_0_0 cb57b32bd6d03e35432362f7bf184b6d) - set(MD5_JPEG_CROP b4197f377e621c4e9b1d20471432610d) -endif() - if(WITH_JAVA) add_test(TJUnitTest ${Java_JAVA_EXECUTABLE} ${JAVAARGS} -cp java/turbojpeg.jar @@ -874,6 +778,11 @@ if(ENABLE_STATIC) set(TEST_LIBTYPES ${TEST_LIBTYPES} static) endif() +set(TEST_SAMPLE_BITS 8) +if(WITH_12BIT) + set(TEST_SAMPLE_BITS ${TEST_SAMPLE_BITS} 12) +endif() + set(TESTIMAGES ${CMAKE_CURRENT_SOURCE_DIR}/testimages) set(MD5CMP ${CMAKE_CURRENT_BINARY_DIR}/md5/md5cmp) if(CMAKE_CROSSCOMPILING) @@ -1063,296 +972,472 @@ foreach(libtype ${TEST_LIBTYPES}) endif() endmacro() - # CC: null SAMP: fullsize FDCT: islow ENT: huff - add_bittest(cjpeg rgb-islow "-rgb;-dct;int;-icc;${TESTIMAGES}/test1.icc" - testout_rgb_islow.jpg ${TESTIMAGES}/testorig.ppm - ${MD5_JPEG_RGB_ISLOW}) - - # CC: null SAMP: fullsize IDCT: islow ENT: huff - add_bittest(djpeg rgb-islow "-dct;int;-ppm;-icc;testout_rgb_islow.icc" - testout_rgb_islow.ppm testout_rgb_islow.jpg - ${MD5_PPM_RGB_ISLOW} cjpeg-${libtype}-rgb-islow) - - add_test(djpeg-${libtype}-rgb-islow-icc-cmp - ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} - b06a39d730129122e85c1363ed1bbc9e testout_rgb_islow.icc) - set_tests_properties(djpeg-${libtype}-rgb-islow-icc-cmp PROPERTIES - DEPENDS djpeg-${libtype}-rgb-islow) - - add_bittest(jpegtran icc "-copy;all;-icc;${TESTIMAGES}/test2.icc" - testout_rgb_islow2.jpg testout_rgb_islow.jpg - ${MD5_JPEG_RGB_ISLOW2} cjpeg-${libtype}-rgb-islow) - - if(NOT WITH_12BIT) - # CC: RGB->RGB565 SAMP: fullsize IDCT: islow ENT: huff - add_bittest(djpeg rgb-islow-565 "-dct;int;-rgb565;-dither;none;-bmp" - testout_rgb_islow_565.bmp testout_rgb_islow.jpg - ${MD5_BMP_RGB_ISLOW_565} cjpeg-${libtype}-rgb-islow) - - # CC: RGB->RGB565 (dithered) SAMP: fullsize IDCT: islow ENT: huff - add_bittest(djpeg rgb-islow-565D "-dct;int;-rgb565;-bmp" - testout_rgb_islow_565D.bmp testout_rgb_islow.jpg - ${MD5_BMP_RGB_ISLOW_565D} cjpeg-${libtype}-rgb-islow) - endif() - - # CC: RGB->YCC SAMP: fullsize/h2v1 FDCT: ifast ENT: 2-pass huff - add_bittest(cjpeg 422-ifast-opt "-sample;2x1;-dct;fast;-opt" - testout_422_ifast_opt.jpg ${TESTIMAGES}/testorig.ppm - ${MD5_JPEG_422_IFAST_OPT}) - - # CC: YCC->RGB SAMP: fullsize/h2v1 fancy IDCT: ifast ENT: huff - add_bittest(djpeg 422-ifast "-dct;fast" - testout_422_ifast.ppm testout_422_ifast_opt.jpg - ${MD5_PPM_422_IFAST} cjpeg-${libtype}-422-ifast-opt) - - # CC: RGB->YCC SAMP: fullsize/h1v2 FDCT: islow ENT: huff - add_bittest(cjpeg 440-islow "-sample;1x2;-dct;int" - testout_440_islow.jpg ${TESTIMAGES}/testorig.ppm - ${MD5_JPEG_440_ISLOW}) - - # CC: YCC->RGB SAMP: fullsize/h1v2 fancy IDCT: islow ENT: huff - add_bittest(djpeg 440-islow "-dct;int" - testout_440_islow.ppm testout_440_islow.jpg - ${MD5_PPM_440_ISLOW} cjpeg-${libtype}-440-islow) - - # CC: YCC->RGB SAMP: h2v1 merged IDCT: ifast ENT: huff - add_bittest(djpeg 422m-ifast "-dct;fast;-nosmooth" - testout_422m_ifast.ppm testout_422_ifast_opt.jpg - ${MD5_PPM_422M_IFAST} cjpeg-${libtype}-422-ifast-opt) - - if(NOT WITH_12BIT) - # CC: YCC->RGB565 SAMP: h2v1 merged IDCT: ifast ENT: huff - add_bittest(djpeg 422m-ifast-565 - "-dct;int;-nosmooth;-rgb565;-dither;none;-bmp" - testout_422m_ifast_565.bmp testout_422_ifast_opt.jpg - ${MD5_BMP_422M_IFAST_565} cjpeg-${libtype}-422-ifast-opt) - - # CC: YCC->RGB565 (dithered) SAMP: h2v1 merged IDCT: ifast ENT: huff - add_bittest(djpeg 422m-ifast-565D "-dct;int;-nosmooth;-rgb565;-bmp" - testout_422m_ifast_565D.bmp testout_422_ifast_opt.jpg - ${MD5_BMP_422M_IFAST_565D} cjpeg-${libtype}-422-ifast-opt) - endif() + foreach(sample_bits ${TEST_SAMPLE_BITS}) + + if(sample_bits EQUAL 12) + set(cjpeg cjpeg12) + set(djpeg djpeg12) + set(jpegtran jpeg12tran) + set(testout testout12) + + set(TESTORIG testorig12.jpg) + set(MD5_JPEG_RGB_ISLOW 9d7369207c520d37f2c1cbfcb82b2964) + set(MD5_JPEG_RGB_ISLOW2 a00bd20d8ae49684640ef7177d2e0b64) + set(MD5_PPM_RGB_ISLOW f3301d2219783b8b3d942b7239fa50c0) + set(MD5_JPEG_422_IFAST_OPT 7322e3bd2f127f7de4b40d4480ce60e4) + set(MD5_PPM_422_IFAST 79807fa552899e66a04708f533e16950) + set(MD5_JPEG_440_ISLOW e25c1912e38367be505a89c410c1c2d2) + set(MD5_PPM_440_ISLOW e7d2e26288870cfcb30f3114ad01e380) + set(MD5_PPM_422M_IFAST 07737bfe8a7c1c87aaa393a0098d16b0) + set(MD5_JPEG_420_IFAST_Q100_PROG 9447cef4803d9b0f74bcf333cc710a29) + set(MD5_PPM_420_Q100_IFAST 1b3730122709f53d007255e8dfd3305e) + set(MD5_PPM_420M_Q100_IFAST 980a1a3c5bf9510022869d30b7d26566) + set(MD5_JPEG_GRAY_ISLOW 235c90707b16e2e069f37c888b2636d9) + set(MD5_PPM_GRAY_ISLOW 7213c10af507ad467da5578ca5ee1fca) + set(MD5_PPM_GRAY_ISLOW_RGB e96ee81c30a6ed422d466338bd3de65d) + set(MD5_JPEG_420S_IFAST_OPT 7af8e60be4d9c227ec63ac9b6630855e) + + set(MD5_JPEG_3x2_FLOAT_PROG_SSE a8c17daf77b457725ec929e215b603f8) + set(MD5_PPM_3x2_FLOAT_SSE 42876ab9e5c2f76a87d08db5fbd57956) + set(MD5_JPEG_3x2_FLOAT_PROG_NO_FP_CONTRACT + a8c17daf77b457725ec929e215b603f8) + set(MD5_PPM_3x2_FLOAT_NO_FP_CONTRACT ${MD5_PPM_3x2_FLOAT_SSE}) + set(MD5_JPEG_3x2_FLOAT_PROG_FP_CONTRACT + ${MD5_JPEG_3x2_FLOAT_PROG_NO_FP_CONTRACT}) + set(MD5_PPM_3x2_FLOAT_FP_CONTRACT ${MD5_PPM_3x2_FLOAT_SSE}) + set(MD5_JPEG_3x2_FLOAT_PROG_387 bc6dbbefac2872f6b9d6c4a0ae60c3c0) + set(MD5_PPM_3x2_FLOAT_387 bcc5723c61560463ac60f772e742d092) + set(MD5_JPEG_3x2_FLOAT_PROG_MSVC e27840755870fa849872e58aa0cd1400) + set(MD5_PPM_3x2_FLOAT_MSVC 6c2880b83bb1aa41dfe330e7a9768690) + + set(MD5_JPEG_3x2_IFAST_PROG 1396cc2b7185cfe943d408c9d305339e) + set(MD5_PPM_3x2_IFAST 3975985ef6eeb0a2cdc58daa651ccc00) + set(MD5_PPM_420M_ISLOW_2_1 4ca6be2a6f326ff9eaab63e70a8259c0) + set(MD5_PPM_420M_ISLOW_15_8 12aa9f9534c1b3d7ba047322226365eb) + set(MD5_PPM_420M_ISLOW_13_8 f7e22817c7b25e1393e4ec101e9d4e96) + set(MD5_PPM_420M_ISLOW_11_8 800a16f9f4dc9b293197bfe11be10a82) + set(MD5_PPM_420M_ISLOW_9_8 06b7a92a9bc69f4dc36ec40f1937d55c) + set(MD5_PPM_420M_ISLOW_7_8 3ec444a14a4ab4eab88ffc49c48eca43) + set(MD5_PPM_420M_ISLOW_3_4 3e726b7ea872445b19437d1c1d4f0d93) + set(MD5_PPM_420M_ISLOW_5_8 a8a771abdc94301d20ffac119b2caccd) + set(MD5_PPM_420M_ISLOW_1_2 b419124dd5568b085787234866102866) + set(MD5_PPM_420M_ISLOW_3_8 343d19015531b7bbe746124127244fa8) + set(MD5_PPM_420M_ISLOW_1_4 35fd59d866e44659edfa3c18db2a3edb) + set(MD5_PPM_420M_ISLOW_1_8 ccaed48ac0aedefda5d4abe4013f4ad7) + set(MD5_PPM_420_ISLOW_SKIP15_31 86664cd9dc956536409e44e244d20a97) + set(MD5_PPM_420_ISLOW_PROG_CROP62x62_71_71 + 452a21656115a163029cfba5c04fa76a) + set(MD5_PPM_444_ISLOW_SKIP1_6 ef63901f71ef7a75cd78253fc0914f84) + set(MD5_PPM_444_ISLOW_PROG_CROP98x98_13_13 + 15b173fb5872d9575572fbcc1b05956f) + set(MD5_JPEG_CROP cdb35ff4b4519392690ea040c56ea99c) + + set(MD5_JPEG_EXAMPLE_COMPRESS 5e502da0c3c0f957a58c536f31e973dc) + set(MD5_PPM_EXAMPLE_DECOMPRESS 2ff0e8505ee6e0ffaeb24037d5650b57) + else() + set(cjpeg cjpeg) + set(djpeg djpeg) + set(jpegtran jpegtran) + set(testout testout) + + set(TESTORIG testorig.jpg) + set(MD5_JPEG_RGB_ISLOW 1d44a406f61da743b5fd31c0a9abdca3) + set(MD5_JPEG_RGB_ISLOW2 31d121e57b6c2934c890a7fc7763bcd4) + set(MD5_PPM_RGB_ISLOW 00a257f5393fef8821f2b88ac7421291) + set(MD5_BMP_RGB_ISLOW_565 f07d2e75073e4bb10f6c6f4d36e2e3be) + set(MD5_BMP_RGB_ISLOW_565D 4cfa0928ef3e6bb626d7728c924cfda4) + set(MD5_JPEG_422_IFAST_OPT 2540287b79d913f91665e660303ab2c8) + set(MD5_PPM_422_IFAST 35bd6b3f833bad23de82acea847129fa) + set(MD5_JPEG_440_ISLOW 538bc02bd4b4658fd85de6ece6cbeda6) + set(MD5_PPM_440_ISLOW 11e7eab7ef7ef3276934bb7e7b6bb377) + set(MD5_PPM_422M_IFAST 8dbc65323d62cca7c91ba02dd1cfa81d) + set(MD5_BMP_422M_IFAST_565 3294bd4d9a1f2b3d08ea6020d0db7065) + set(MD5_BMP_422M_IFAST_565D da98c9c7b6039511be4a79a878a9abc1) + set(MD5_JPEG_420_IFAST_Q100_PROG 0ba15f9dab81a703505f835f9dbbac6d) + set(MD5_PPM_420_Q100_IFAST 5a732542015c278ff43635e473a8a294) + set(MD5_PPM_420M_Q100_IFAST ff692ee9323a3b424894862557c092f1) + set(MD5_JPEG_GRAY_ISLOW 72b51f894b8f4a10b3ee3066770aa38d) + set(MD5_PPM_GRAY_ISLOW 8d3596c56eace32f205deccc229aa5ed) + set(MD5_PPM_GRAY_ISLOW_RGB 116424ac07b79e5e801f00508eab48ec) + set(MD5_BMP_GRAY_ISLOW_565 12f78118e56a2f48b966f792fedf23cc) + set(MD5_BMP_GRAY_ISLOW_565D bdbbd616441a24354c98553df5dc82db) + set(MD5_JPEG_420S_IFAST_OPT 388708217ac46273ca33086b22827ed8) + + set(MD5_JPEG_3x2_FLOAT_PROG_SSE 343e3f8caf8af5986ebaf0bdc13b5c71) + set(MD5_PPM_3x2_FLOAT_SSE 1a75f36e5904d6fc3a85a43da9ad89bb) + set(MD5_JPEG_3x2_FLOAT_PROG_NO_FP_CONTRACT + 9bca803d2042bd1eb03819e2bf92b3e5) + set(MD5_PPM_3x2_FLOAT_NO_FP_CONTRACT f6bfab038438ed8f5522fbd33595dcdc) + set(MD5_JPEG_3x2_FLOAT_PROG_FP_CONTRACT + ${MD5_JPEG_3x2_FLOAT_PROG_NO_FP_CONTRACT}) + set(MD5_PPM_3x2_FLOAT_FP_CONTRACT 0e917a34193ef976b679a6b069b1be26) + set(MD5_JPEG_3x2_FLOAT_PROG_387 1657664a410e0822c924b54f6f65e6e9) + set(MD5_PPM_3x2_FLOAT_387 cb0a1f027f3d2917c902b5640214e025) + set(MD5_JPEG_3x2_FLOAT_PROG_MSVC 7999ce9cd0ee9b6c7043b7351ab7639d) + set(MD5_PPM_3x2_FLOAT_MSVC 28cdc448a6b75e97892f0e0f8d4b21f3) + + set(MD5_JPEG_3x2_IFAST_PROG 1ee5d2c1a77f2da495f993c8c7cceca5) + set(MD5_PPM_3x2_IFAST fd283664b3b49127984af0a7f118fccd) + set(MD5_JPEG_420_ISLOW_ARI e986fb0a637a8d833d96e8a6d6d84ea1) + set(MD5_JPEG_444_ISLOW_PROGARI 0a8f1c8f66e113c3cf635df0a475a617) + set(MD5_PPM_420M_IFAST_ARI 57251da28a35b46eecb7177d82d10e0e) + set(MD5_JPEG_420_ISLOW 9a68f56bc76e466aa7e52f415d0f4a5f) + set(MD5_PPM_420M_ISLOW_2_1 9f9de8c0612f8d06869b960b05abf9c9) + set(MD5_PPM_420M_ISLOW_15_8 b6875bc070720b899566cc06459b63b7) + set(MD5_PPM_420M_ISLOW_13_8 bc3452573c8152f6ae552939ee19f82f) + set(MD5_PPM_420M_ISLOW_11_8 d8cc73c0aaacd4556569b59437ba00a5) + set(MD5_PPM_420M_ISLOW_9_8 d25e61bc7eac0002f5b393aa223747b6) + set(MD5_PPM_420M_ISLOW_7_8 ddb564b7c74a09494016d6cd7502a946) + set(MD5_PPM_420M_ISLOW_3_4 8ed8e68808c3fbc4ea764fc9d2968646) + set(MD5_PPM_420M_ISLOW_5_8 a3363274999da2366a024efae6d16c9b) + set(MD5_PPM_420M_ISLOW_1_2 e692a315cea26b988c8e8b29a5dbcd81) + set(MD5_PPM_420M_ISLOW_3_8 79eca9175652ced755155c90e785a996) + set(MD5_PPM_420M_ISLOW_1_4 79cd778f8bf1a117690052cacdd54eca) + set(MD5_PPM_420M_ISLOW_1_8 391b3d4aca640c8567d6f8745eb2142f) + set(MD5_BMP_420_ISLOW_256 4980185e3776e89bd931736e1cddeee6) + set(MD5_BMP_420_ISLOW_565 bf9d13e16c4923b92e1faa604d7922cb) + set(MD5_BMP_420_ISLOW_565D 6bde71526acc44bcff76f696df8638d2) + set(MD5_BMP_420M_ISLOW_565 8dc0185245353cfa32ad97027342216f) + set(MD5_BMP_420M_ISLOW_565D ce034037d212bc403330df6f915c161b) + set(MD5_PPM_420_ISLOW_SKIP15_31 c4c65c1e43d7275cd50328a61e6534f0) + set(MD5_PPM_420_ISLOW_ARI_SKIP16_139 087c6b123db16ac00cb88c5b590bb74a) + set(MD5_PPM_420_ISLOW_PROG_CROP62x62_71_71 + 26eb36ccc7d1f0cb80cdabb0ac8b5d99) + set(MD5_PPM_420_ISLOW_ARI_CROP53x53_4_4 886c6775af22370257122f8b16207e6d) + set(MD5_PPM_444_ISLOW_SKIP1_6 5606f86874cf26b8fcee1117a0a436a6) + set(MD5_PPM_444_ISLOW_PROG_CROP98x98_13_13 + db87dc7ce26bcdc7a6b56239ce2b9d6c) + set(MD5_PPM_444_ISLOW_ARI_CROP37x37_0_0 cb57b32bd6d03e35432362f7bf184b6d) + set(MD5_JPEG_CROP b4197f377e621c4e9b1d20471432610d) + + set(MD5_JPEG_EXAMPLE_COMPRESS 95d4d72e2ef127332654c2599afb47bf) + set(MD5_PPM_EXAMPLE_DECOMPRESS 6fdde7301575bfd711e295b969b6b3de) + endif() - # CC: RGB->YCC SAMP: fullsize/h2v2 FDCT: ifast ENT: prog huff - add_bittest(cjpeg 420-q100-ifast-prog - "-sample;2x2;-quality;100;-dct;fast;-scans;${TESTIMAGES}/test.scan" - testout_420_q100_ifast_prog.jpg ${TESTIMAGES}/testorig.ppm - ${MD5_JPEG_420_IFAST_Q100_PROG}) - - # CC: YCC->RGB SAMP: fullsize/h2v2 fancy IDCT: ifast ENT: prog huff - add_bittest(djpeg 420-q100-ifast-prog "-dct;fast" - testout_420_q100_ifast.ppm testout_420_q100_ifast_prog.jpg - ${MD5_PPM_420_Q100_IFAST} cjpeg-${libtype}-420-q100-ifast-prog) - - # CC: YCC->RGB SAMP: h2v2 merged IDCT: ifast ENT: prog huff - add_bittest(djpeg 420m-q100-ifast-prog "-dct;fast;-nosmooth" - testout_420m_q100_ifast.ppm testout_420_q100_ifast_prog.jpg - ${MD5_PPM_420M_Q100_IFAST} cjpeg-${libtype}-420-q100-ifast-prog) - - # CC: RGB->Gray SAMP: fullsize FDCT: islow ENT: huff - add_bittest(cjpeg gray-islow "-gray;-dct;int" - testout_gray_islow.jpg ${TESTIMAGES}/testorig.ppm - ${MD5_JPEG_GRAY_ISLOW}) - - # CC: Gray->Gray SAMP: fullsize IDCT: islow ENT: huff - add_bittest(djpeg gray-islow "-dct;int" - testout_gray_islow.ppm testout_gray_islow.jpg - ${MD5_PPM_GRAY_ISLOW} cjpeg-${libtype}-gray-islow) - - # CC: Gray->RGB SAMP: fullsize IDCT: islow ENT: huff - add_bittest(djpeg gray-islow-rgb "-dct;int;-rgb" - testout_gray_islow_rgb.ppm testout_gray_islow.jpg - ${MD5_PPM_GRAY_ISLOW_RGB} cjpeg-${libtype}-gray-islow) - - if(NOT WITH_12BIT) - # CC: Gray->RGB565 SAMP: fullsize IDCT: islow ENT: huff - add_bittest(djpeg gray-islow-565 "-dct;int;-rgb565;-dither;none;-bmp" - testout_gray_islow_565.bmp testout_gray_islow.jpg - ${MD5_BMP_GRAY_ISLOW_565} cjpeg-${libtype}-gray-islow) - - # CC: Gray->RGB565 (dithered) SAMP: fullsize IDCT: islow ENT: huff - add_bittest(djpeg gray-islow-565D "-dct;int;-rgb565;-bmp" - testout_gray_islow_565D.bmp testout_gray_islow.jpg - ${MD5_BMP_GRAY_ISLOW_565D} cjpeg-${libtype}-gray-islow) - endif() + # CC: null SAMP: fullsize FDCT: islow ENT: huff + add_bittest(${cjpeg} rgb-islow "-rgb;-dct;int;-icc;${TESTIMAGES}/test1.icc" + ${testout}_rgb_islow.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_RGB_ISLOW}) + + # CC: null SAMP: fullsize IDCT: islow ENT: huff + add_bittest(${djpeg} rgb-islow "-dct;int;-ppm;-icc;${testout}_rgb_islow.icc" + ${testout}_rgb_islow.ppm ${testout}_rgb_islow.jpg ${MD5_PPM_RGB_ISLOW} + ${cjpeg}-${libtype}-rgb-islow) + + add_test(${djpeg}-${libtype}-rgb-islow-icc-cmp + ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} + b06a39d730129122e85c1363ed1bbc9e ${testout}_rgb_islow.icc) + set_tests_properties(${djpeg}-${libtype}-rgb-islow-icc-cmp PROPERTIES + DEPENDS ${djpeg}-${libtype}-rgb-islow) + + add_bittest(${jpegtran} icc "-copy;all;-icc;${TESTIMAGES}/test2.icc" + ${testout}_rgb_islow2.jpg ${testout}_rgb_islow.jpg + ${MD5_JPEG_RGB_ISLOW2} ${cjpeg}-${libtype}-rgb-islow) + + if(sample_bits EQUAL 8) + # CC: RGB->RGB565 SAMP: fullsize IDCT: islow ENT: huff + add_bittest(${djpeg} rgb-islow-565 "-dct;int;-rgb565;-dither;none;-bmp" + ${testout}_rgb_islow_565.bmp ${testout}_rgb_islow.jpg + ${MD5_BMP_RGB_ISLOW_565} ${cjpeg}-${libtype}-rgb-islow) + + # CC: RGB->RGB565 (dithered) SAMP: fullsize IDCT: islow ENT: huff + add_bittest(${djpeg} rgb-islow-565D "-dct;int;-rgb565;-bmp" + ${testout}_rgb_islow_565D.bmp ${testout}_rgb_islow.jpg + ${MD5_BMP_RGB_ISLOW_565D} ${cjpeg}-${libtype}-rgb-islow) + endif() - # CC: RGB->YCC SAMP: fullsize smooth/h2v2 smooth FDCT: islow - # ENT: 2-pass huff - add_bittest(cjpeg 420s-ifast-opt "-sample;2x2;-smooth;1;-dct;int;-opt" - testout_420s_ifast_opt.jpg ${TESTIMAGES}/testorig.ppm - ${MD5_JPEG_420S_IFAST_OPT}) - - if(FLOATTEST) - # CC: RGB->YCC SAMP: fullsize/int FDCT: float ENT: prog huff - add_bittest(cjpeg 3x2-float-prog "-sample;3x2;-dct;float;-prog" - testout_3x2_float_prog.jpg ${TESTIMAGES}/testorig.ppm - ${MD5_JPEG_3x2_FLOAT_PROG_${FLOATTEST_UC}}) - - # CC: YCC->RGB SAMP: fullsize/int IDCT: float ENT: prog huff - add_bittest(djpeg 3x2-float-prog "-dct;float" - testout_3x2_float.ppm testout_3x2_float_prog.jpg - ${MD5_PPM_3x2_FLOAT_${FLOATTEST_UC}} cjpeg-${libtype}-3x2-float-prog) - endif() + # CC: RGB->YCC SAMP: fullsize/h2v1 FDCT: ifast ENT: 2-pass huff + add_bittest(${cjpeg} 422-ifast-opt "-sample;2x1;-dct;fast;-opt" + ${testout}_422_ifast_opt.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_422_IFAST_OPT}) + + # CC: YCC->RGB SAMP: fullsize/h2v1 fancy IDCT: ifast ENT: huff + add_bittest(${djpeg} 422-ifast "-dct;fast" + ${testout}_422_ifast.ppm ${testout}_422_ifast_opt.jpg + ${MD5_PPM_422_IFAST} ${cjpeg}-${libtype}-422-ifast-opt) + + # CC: RGB->YCC SAMP: fullsize/h1v2 FDCT: islow ENT: huff + add_bittest(${cjpeg} 440-islow "-sample;1x2;-dct;int" + ${testout}_440_islow.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_440_ISLOW}) + + # CC: YCC->RGB SAMP: fullsize/h1v2 fancy IDCT: islow ENT: huff + add_bittest(${djpeg} 440-islow "-dct;int" + ${testout}_440_islow.ppm ${testout}_440_islow.jpg + ${MD5_PPM_440_ISLOW} ${cjpeg}-${libtype}-440-islow) + + # CC: YCC->RGB SAMP: h2v1 merged IDCT: ifast ENT: huff + add_bittest(${djpeg} 422m-ifast "-dct;fast;-nosmooth" + ${testout}_422m_ifast.ppm ${testout}_422_ifast_opt.jpg + ${MD5_PPM_422M_IFAST} ${cjpeg}-${libtype}-422-ifast-opt) + + if(sample_bits EQUAL 8) + # CC: YCC->RGB565 SAMP: h2v1 merged IDCT: ifast ENT: huff + add_bittest(${djpeg} 422m-ifast-565 + "-dct;int;-nosmooth;-rgb565;-dither;none;-bmp" + ${testout}_422m_ifast_565.bmp ${testout}_422_ifast_opt.jpg + ${MD5_BMP_422M_IFAST_565} ${cjpeg}-${libtype}-422-ifast-opt) + + # CC: YCC->RGB565 (dithered) SAMP: h2v1 merged IDCT: ifast ENT: huff + add_bittest(${djpeg} 422m-ifast-565D "-dct;int;-nosmooth;-rgb565;-bmp" + ${testout}_422m_ifast_565D.bmp ${testout}_422_ifast_opt.jpg + ${MD5_BMP_422M_IFAST_565D} ${cjpeg}-${libtype}-422-ifast-opt) + endif() - # CC: RGB->YCC SAMP: fullsize/int FDCT: ifast ENT: prog huff - add_bittest(cjpeg 3x2-ifast-prog "-sample;3x2;-dct;fast;-prog" - testout_3x2_ifast_prog.jpg ${TESTIMAGES}/testorig.ppm - ${MD5_JPEG_3x2_IFAST_PROG}) - - # CC: YCC->RGB SAMP: fullsize/int IDCT: ifast ENT: prog huff - add_bittest(djpeg 3x2-ifast-prog "-dct;fast" - testout_3x2_ifast.ppm testout_3x2_ifast_prog.jpg - ${MD5_PPM_3x2_IFAST} cjpeg-${libtype}-3x2-ifast-prog) - - if(WITH_ARITH_ENC) - # CC: YCC->RGB SAMP: fullsize/h2v2 FDCT: islow ENT: arith - add_bittest(cjpeg 420-islow-ari "-dct;int;-arithmetic" - testout_420_islow_ari.jpg ${TESTIMAGES}/testorig.ppm - ${MD5_JPEG_420_ISLOW_ARI}) - - add_bittest(jpegtran 420-islow-ari "-arithmetic" - testout_420_islow_ari2.jpg ${TESTIMAGES}/testimgint.jpg - ${MD5_JPEG_420_ISLOW_ARI}) - - # CC: YCC->RGB SAMP: fullsize FDCT: islow ENT: prog arith - add_bittest(cjpeg 444-islow-progari - "-sample;1x1;-dct;int;-prog;-arithmetic" - testout_444_islow_progari.jpg ${TESTIMAGES}/testorig.ppm - ${MD5_JPEG_444_ISLOW_PROGARI}) - endif() + # CC: RGB->YCC SAMP: fullsize/h2v2 FDCT: ifast ENT: prog huff + add_bittest(${cjpeg} 420-q100-ifast-prog + "-sample;2x2;-quality;100;-dct;fast;-scans;${TESTIMAGES}/test.scan" + ${testout}_420_q100_ifast_prog.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_420_IFAST_Q100_PROG}) + + # CC: YCC->RGB SAMP: fullsize/h2v2 fancy IDCT: ifast ENT: prog huff + add_bittest(${djpeg} 420-q100-ifast-prog "-dct;fast" + ${testout}_420_q100_ifast.ppm ${testout}_420_q100_ifast_prog.jpg + ${MD5_PPM_420_Q100_IFAST} ${cjpeg}-${libtype}-420-q100-ifast-prog) + + # CC: YCC->RGB SAMP: h2v2 merged IDCT: ifast ENT: prog huff + add_bittest(${djpeg} 420m-q100-ifast-prog "-dct;fast;-nosmooth" + ${testout}_420m_q100_ifast.ppm ${testout}_420_q100_ifast_prog.jpg + ${MD5_PPM_420M_Q100_IFAST} ${cjpeg}-${libtype}-420-q100-ifast-prog) + + # CC: RGB->Gray SAMP: fullsize FDCT: islow ENT: huff + add_bittest(${cjpeg} gray-islow "-gray;-dct;int" + ${testout}_gray_islow.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_GRAY_ISLOW}) + + # CC: Gray->Gray SAMP: fullsize IDCT: islow ENT: huff + add_bittest(${djpeg} gray-islow "-dct;int" + ${testout}_gray_islow.ppm ${testout}_gray_islow.jpg + ${MD5_PPM_GRAY_ISLOW} ${cjpeg}-${libtype}-gray-islow) + + # CC: Gray->RGB SAMP: fullsize IDCT: islow ENT: huff + add_bittest(${djpeg} gray-islow-rgb "-dct;int;-rgb" + ${testout}_gray_islow_rgb.ppm ${testout}_gray_islow.jpg + ${MD5_PPM_GRAY_ISLOW_RGB} ${cjpeg}-${libtype}-gray-islow) + + if(sample_bits EQUAL 8) + # CC: Gray->RGB565 SAMP: fullsize IDCT: islow ENT: huff + add_bittest(${djpeg} gray-islow-565 "-dct;int;-rgb565;-dither;none;-bmp" + ${testout}_gray_islow_565.bmp ${testout}_gray_islow.jpg + ${MD5_BMP_GRAY_ISLOW_565} ${cjpeg}-${libtype}-gray-islow) + + # CC: Gray->RGB565 (dithered) SAMP: fullsize IDCT: islow ENT: huff + add_bittest(${djpeg} gray-islow-565D "-dct;int;-rgb565;-bmp" + ${testout}_gray_islow_565D.bmp ${testout}_gray_islow.jpg + ${MD5_BMP_GRAY_ISLOW_565D} ${cjpeg}-${libtype}-gray-islow) + endif() - if(WITH_ARITH_DEC) - # CC: RGB->YCC SAMP: h2v2 merged IDCT: ifast ENT: arith - add_bittest(djpeg 420m-ifast-ari "-fast;-skip;1,20;-ppm" - testout_420m_ifast_ari.ppm ${TESTIMAGES}/testimgari.jpg - ${MD5_PPM_420M_IFAST_ARI}) + # CC: RGB->YCC SAMP: fullsize smooth/h2v2 smooth FDCT: islow + # ENT: 2-pass huff + add_bittest(${cjpeg} 420s-ifast-opt "-sample;2x2;-smooth;1;-dct;int;-opt" + ${testout}_420s_ifast_opt.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_420S_IFAST_OPT}) + + if(FLOATTEST) + # CC: RGB->YCC SAMP: fullsize/int FDCT: float ENT: prog huff + add_bittest(${cjpeg} 3x2-float-prog "-sample;3x2;-dct;float;-prog" + ${testout}_3x2_float_prog.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_3x2_FLOAT_PROG_${FLOATTEST_UC}}) + + # CC: YCC->RGB SAMP: fullsize/int IDCT: float ENT: prog huff + add_bittest(${djpeg} 3x2-float-prog "-dct;float" + ${testout}_3x2_float.ppm ${testout}_3x2_float_prog.jpg + ${MD5_PPM_3x2_FLOAT_${FLOATTEST_UC}} + ${cjpeg}-${libtype}-3x2-float-prog) + endif() - add_bittest(jpegtran 420-islow "" - testout_420_islow.jpg ${TESTIMAGES}/testimgari.jpg - ${MD5_JPEG_420_ISLOW}) - endif() + # CC: RGB->YCC SAMP: fullsize/int FDCT: ifast ENT: prog huff + add_bittest(${cjpeg} 3x2-ifast-prog "-sample;3x2;-dct;fast;-prog" + ${testout}_3x2_ifast_prog.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_3x2_IFAST_PROG}) + + # CC: YCC->RGB SAMP: fullsize/int IDCT: ifast ENT: prog huff + add_bittest(${djpeg} 3x2-ifast-prog "-dct;fast" + ${testout}_3x2_ifast.ppm ${testout}_3x2_ifast_prog.jpg + ${MD5_PPM_3x2_IFAST} ${cjpeg}-${libtype}-3x2-ifast-prog) + + if(WITH_ARITH_ENC AND sample_bits EQUAL 8) + # CC: YCC->RGB SAMP: fullsize/h2v2 FDCT: islow ENT: arith + add_bittest(${cjpeg} 420-islow-ari "-dct;int;-arithmetic" + ${testout}_420_islow_ari.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_420_ISLOW_ARI}) + + add_bittest(${jpegtran} 420-islow-ari "-arithmetic" + ${testout}_420_islow_ari2.jpg ${TESTIMAGES}/testimgint.jpg + ${MD5_JPEG_420_ISLOW_ARI}) + + # CC: YCC->RGB SAMP: fullsize FDCT: islow ENT: prog arith + add_bittest(${cjpeg} 444-islow-progari + "-sample;1x1;-dct;int;-prog;-arithmetic" + ${testout}_444_islow_progari.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_444_ISLOW_PROGARI}) + endif() - # 2/1-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 16x16 islow ENT: huff - # 15/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 15x15 islow ENT: huff - # 13/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 13x13 islow ENT: huff - # 11/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 11x11 islow ENT: huff - # 9/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 9x9 islow ENT: huff - # 7/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 7x7 islow/14x14 islow - # ENT: huff - # 3/4-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 6x6 islow/12x12 islow - # ENT: huff - # 5/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 5x5 islow/10x10 islow - # ENT: huff - # 1/2-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 4x4 islow/8x8 islow - # ENT: huff - # 3/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 3x3 islow/6x6 islow - # ENT: huff - # 1/4-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 2x2 islow/4x4 islow - # ENT: huff - # 1/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 1x1 islow/2x2 islow - # ENT: huff - foreach(scale 2_1 15_8 13_8 11_8 9_8 7_8 3_4 5_8 1_2 3_8 1_4 1_8) - string(REGEX REPLACE "_" "/" scalearg ${scale}) - add_bittest(djpeg 420m-islow-${scale} - "-dct;int;-scale;${scalearg};-nosmooth;-ppm" - testout_420m_islow_${scale}.ppm ${TESTIMAGES}/${TESTORIG} - ${MD5_PPM_420M_ISLOW_${scale}}) - endforeach() + if(WITH_ARITH_DEC AND sample_bits EQUAL 8) + # CC: RGB->YCC SAMP: h2v2 merged IDCT: ifast ENT: arith + add_bittest(${djpeg} 420m-ifast-ari "-fast;-skip;1,20;-ppm" + ${testout}_420m_ifast_ari.ppm ${TESTIMAGES}/testimgari.jpg + ${MD5_PPM_420M_IFAST_ARI}) - if(NOT WITH_12BIT) - # CC: YCC->RGB (dithered) SAMP: h2v2 fancy IDCT: islow ENT: huff - add_bittest(djpeg 420-islow-256 "-dct;int;-colors;256;-bmp" - testout_420_islow_256.bmp ${TESTIMAGES}/${TESTORIG} - ${MD5_BMP_420_ISLOW_256}) - - # CC: YCC->RGB565 SAMP: h2v2 fancy IDCT: islow ENT: huff - add_bittest(djpeg 420-islow-565 "-dct;int;-rgb565;-dither;none;-bmp" - testout_420_islow_565.bmp ${TESTIMAGES}/${TESTORIG} - ${MD5_BMP_420_ISLOW_565}) - - # CC: YCC->RGB565 (dithered) SAMP: h2v2 fancy IDCT: islow ENT: huff - add_bittest(djpeg 420-islow-565D "-dct;int;-rgb565;-bmp" - testout_420_islow_565D.bmp ${TESTIMAGES}/${TESTORIG} - ${MD5_BMP_420_ISLOW_565D}) - - # CC: YCC->RGB565 SAMP: h2v2 merged IDCT: islow ENT: huff - add_bittest(djpeg 420m-islow-565 - "-dct;int;-nosmooth;-rgb565;-dither;none;-bmp" - testout_420m_islow_565.bmp ${TESTIMAGES}/${TESTORIG} - ${MD5_BMP_420M_ISLOW_565}) - - # CC: YCC->RGB565 (dithered) SAMP: h2v2 merged IDCT: islow ENT: huff - add_bittest(djpeg 420m-islow-565D "-dct;int;-nosmooth;-rgb565;-bmp" - testout_420m_islow_565D.bmp ${TESTIMAGES}/${TESTORIG} - ${MD5_BMP_420M_ISLOW_565D}) - endif() + add_bittest(${jpegtran} 420-islow "" + ${testout}_420_islow.jpg ${TESTIMAGES}/testimgari.jpg + ${MD5_JPEG_420_ISLOW}) + endif() - # Partial decode tests. These tests are designed to cover all of the - # possible code paths in jpeg_skip_scanlines(). + # 2/1-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 16x16 islow ENT: huff + # 15/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 15x15 islow ENT: huff + # 13/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 13x13 islow ENT: huff + # 11/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 11x11 islow ENT: huff + # 9/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 9x9 islow ENT: huff + # 7/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 7x7 islow/14x14 islow + # ENT: huff + # 3/4-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 6x6 islow/12x12 islow + # ENT: huff + # 5/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 5x5 islow/10x10 islow + # ENT: huff + # 1/2-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 4x4 islow/8x8 islow + # ENT: huff + # 3/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 3x3 islow/6x6 islow + # ENT: huff + # 1/4-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 2x2 islow/4x4 islow + # ENT: huff + # 1/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 1x1 islow/2x2 islow + # ENT: huff + foreach(scale 2_1 15_8 13_8 11_8 9_8 7_8 3_4 5_8 1_2 3_8 1_4 1_8) + string(REGEX REPLACE "_" "/" scalearg ${scale}) + add_bittest(${djpeg} 420m-islow-${scale} + "-dct;int;-scale;${scalearg};-nosmooth;-ppm" + ${testout}_420m_islow_${scale}.ppm ${TESTIMAGES}/${TESTORIG} + ${MD5_PPM_420M_ISLOW_${scale}}) + endforeach() - # Context rows: Yes Intra-iMCU row: Yes iMCU row prefetch: No ENT: huff - add_bittest(djpeg 420-islow-skip15_31 "-dct;int;-skip;15,31;-ppm" - testout_420_islow_skip15,31.ppm ${TESTIMAGES}/${TESTORIG} - ${MD5_PPM_420_ISLOW_SKIP15_31}) + if(sample_bits EQUAL 8) + # CC: YCC->RGB (dithered) SAMP: h2v2 fancy IDCT: islow ENT: huff + add_bittest(${djpeg} 420-islow-256 "-dct;int;-colors;256;-bmp" + ${testout}_420_islow_256.bmp ${TESTIMAGES}/${TESTORIG} + ${MD5_BMP_420_ISLOW_256}) + + # CC: YCC->RGB565 SAMP: h2v2 fancy IDCT: islow ENT: huff + add_bittest(${djpeg} 420-islow-565 "-dct;int;-rgb565;-dither;none;-bmp" + ${testout}_420_islow_565.bmp ${TESTIMAGES}/${TESTORIG} + ${MD5_BMP_420_ISLOW_565}) + + # CC: YCC->RGB565 (dithered) SAMP: h2v2 fancy IDCT: islow ENT: huff + add_bittest(${djpeg} 420-islow-565D "-dct;int;-rgb565;-bmp" + ${testout}_420_islow_565D.bmp ${TESTIMAGES}/${TESTORIG} + ${MD5_BMP_420_ISLOW_565D}) + + # CC: YCC->RGB565 SAMP: h2v2 merged IDCT: islow ENT: huff + add_bittest(${djpeg} 420m-islow-565 + "-dct;int;-nosmooth;-rgb565;-dither;none;-bmp" + ${testout}_420m_islow_565.bmp ${TESTIMAGES}/${TESTORIG} + ${MD5_BMP_420M_ISLOW_565}) + + # CC: YCC->RGB565 (dithered) SAMP: h2v2 merged IDCT: islow ENT: huff + add_bittest(${djpeg} 420m-islow-565D "-dct;int;-nosmooth;-rgb565;-bmp" + ${testout}_420m_islow_565D.bmp ${TESTIMAGES}/${TESTORIG} + ${MD5_BMP_420M_ISLOW_565D}) + endif() - # Context rows: Yes Intra-iMCU row: No iMCU row prefetch: Yes ENT: arith - if(WITH_ARITH_DEC) - add_bittest(djpeg 420-islow-ari-skip16_139 "-dct;int;-skip;16,139;-ppm" - testout_420_islow_ari_skip16,139.ppm ${TESTIMAGES}/testimgari.jpg - ${MD5_PPM_420_ISLOW_ARI_SKIP16_139}) - endif() + # Partial decode tests. These tests are designed to cover all of the + # possible code paths in jpeg_skip_scanlines(). + + # Context rows: Yes Intra-iMCU row: Yes iMCU row prefetch: No + # ENT: huff + add_bittest(${djpeg} 420-islow-skip15_31 "-dct;int;-skip;15,31;-ppm" + ${testout}_420_islow_skip15,31.ppm ${TESTIMAGES}/${TESTORIG} + ${MD5_PPM_420_ISLOW_SKIP15_31}) + + # Context rows: Yes Intra-iMCU row: No iMCU row prefetch: Yes + # ENT: arith + if(WITH_ARITH_DEC AND sample_bits EQUAL 8) + add_bittest(${djpeg} 420-islow-ari-skip16_139 + "-dct;int;-skip;16,139;-ppm" ${testout}_420_islow_ari_skip16,139.ppm + ${TESTIMAGES}/testimgari.jpg ${MD5_PPM_420_ISLOW_ARI_SKIP16_139}) + endif() - # Context rows: Yes Intra-iMCU row: No iMCU row prefetch: No ENT: prog huff - add_test(cjpeg-${libtype}-420-islow-prog - ${CMAKE_CROSSCOMPILING_EMULATOR} cjpeg${suffix} -dct int -prog - -outfile testout_420_islow_prog.jpg ${TESTIMAGES}/testorig.ppm) - add_bittest(djpeg 420-islow-prog-crop62x62_71_71 - "-dct;int;-crop;62x62+71+71;-ppm" - testout_420_islow_prog_crop62x62,71,71.ppm testout_420_islow_prog.jpg - ${MD5_PPM_420_ISLOW_PROG_CROP62x62_71_71} cjpeg-${libtype}-420-islow-prog) - - # Context rows: Yes Intra-iMCU row: No iMCU row prefetch: No ENT: arith - if(WITH_ARITH_DEC) - add_bittest(djpeg 420-islow-ari-crop53x53_4_4 - "-dct;int;-crop;53x53+4+4;-ppm" - testout_420_islow_ari_crop53x53,4,4.ppm ${TESTIMAGES}/testimgari.jpg - ${MD5_PPM_420_ISLOW_ARI_CROP53x53_4_4}) - endif() + # Context rows: Yes Intra-iMCU row: No iMCU row prefetch: No + # ENT: prog huff + add_test(${cjpeg}-${libtype}-420-islow-prog + ${CMAKE_CROSSCOMPILING_EMULATOR} ${cjpeg}${suffix} -dct int -prog + -outfile ${testout}_420_islow_prog.jpg ${TESTIMAGES}/testorig.ppm) + add_bittest(${djpeg} 420-islow-prog-crop62x62_71_71 + "-dct;int;-crop;62x62+71+71;-ppm" + ${testout}_420_islow_prog_crop62x62,71,71.ppm + ${testout}_420_islow_prog.jpg ${MD5_PPM_420_ISLOW_PROG_CROP62x62_71_71} + ${cjpeg}-${libtype}-420-islow-prog) + + # Context rows: Yes Intra-iMCU row: No iMCU row prefetch: No + # ENT: arith + if(WITH_ARITH_DEC AND sample_bits EQUAL 8) + add_bittest(${djpeg} 420-islow-ari-crop53x53_4_4 + "-dct;int;-crop;53x53+4+4;-ppm" + ${testout}_420_islow_ari_crop53x53,4,4.ppm ${TESTIMAGES}/testimgari.jpg + ${MD5_PPM_420_ISLOW_ARI_CROP53x53_4_4}) + endif() - # Context rows: No Intra-iMCU row: Yes ENT: huff - add_test(cjpeg-${libtype}-444-islow - ${CMAKE_CROSSCOMPILING_EMULATOR} cjpeg${suffix} -dct int -sample 1x1 - -outfile testout_444_islow.jpg ${TESTIMAGES}/testorig.ppm) - add_bittest(djpeg 444-islow-skip1_6 "-dct;int;-skip;1,6;-ppm" - testout_444_islow_skip1,6.ppm testout_444_islow.jpg - ${MD5_PPM_444_ISLOW_SKIP1_6} cjpeg-${libtype}-444-islow) - - # Context rows: No Intra-iMCU row: No ENT: prog huff - add_test(cjpeg-${libtype}-444-islow-prog - ${CMAKE_CROSSCOMPILING_EMULATOR} cjpeg${suffix} -dct int -prog -sample 1x1 - -outfile testout_444_islow_prog.jpg ${TESTIMAGES}/testorig.ppm) - add_bittest(djpeg 444-islow-prog-crop98x98_13_13 - "-dct;int;-crop;98x98+13+13;-ppm" - testout_444_islow_prog_crop98x98,13,13.ppm testout_444_islow_prog.jpg - ${MD5_PPM_444_ISLOW_PROG_CROP98x98_13_13} cjpeg-${libtype}-444-islow-prog) - - # Context rows: No Intra-iMCU row: No ENT: arith - if(WITH_ARITH_ENC) - add_test(cjpeg-${libtype}-444-islow-ari - ${CMAKE_CROSSCOMPILING_EMULATOR} cjpeg${suffix} -dct int -arithmetic - -sample 1x1 -outfile testout_444_islow_ari.jpg + # Context rows: No Intra-iMCU row: Yes ENT: huff + add_test(${cjpeg}-${libtype}-444-islow + ${CMAKE_CROSSCOMPILING_EMULATOR} ${cjpeg}${suffix} -dct int -sample 1x1 + -outfile ${testout}_444_islow.jpg ${TESTIMAGES}/testorig.ppm) + add_bittest(${djpeg} 444-islow-skip1_6 "-dct;int;-skip;1,6;-ppm" + ${testout}_444_islow_skip1,6.ppm ${testout}_444_islow.jpg + ${MD5_PPM_444_ISLOW_SKIP1_6} ${cjpeg}-${libtype}-444-islow) + + # Context rows: No Intra-iMCU row: No ENT: prog huff + add_test(${cjpeg}-${libtype}-444-islow-prog + ${CMAKE_CROSSCOMPILING_EMULATOR} ${cjpeg}${suffix} -dct int -prog + -sample 1x1 -outfile ${testout}_444_islow_prog.jpg ${TESTIMAGES}/testorig.ppm) - if(WITH_ARITH_DEC) - add_bittest(djpeg 444-islow-ari-crop37x37_0_0 - "-dct;int;-crop;37x37+0+0;-ppm" - testout_444_islow_ari_crop37x37,0,0.ppm testout_444_islow_ari.jpg - ${MD5_PPM_444_ISLOW_ARI_CROP37x37_0_0} cjpeg-${libtype}-444-islow-ari) + add_bittest(${djpeg} 444-islow-prog-crop98x98_13_13 + "-dct;int;-crop;98x98+13+13;-ppm" + ${testout}_444_islow_prog_crop98x98,13,13.ppm + ${testout}_444_islow_prog.jpg ${MD5_PPM_444_ISLOW_PROG_CROP98x98_13_13} + ${cjpeg}-${libtype}-444-islow-prog) + + # Context rows: No Intra-iMCU row: No ENT: arith + if(WITH_ARITH_ENC AND sample_bits EQUAL 8) + add_test(${cjpeg}-${libtype}-444-islow-ari + ${CMAKE_CROSSCOMPILING_EMULATOR} ${cjpeg}${suffix} -dct int -arithmetic + -sample 1x1 -outfile ${testout}_444_islow_ari.jpg + ${TESTIMAGES}/testorig.ppm) + if(WITH_ARITH_DEC) + add_bittest(${djpeg} 444-islow-ari-crop37x37_0_0 + "-dct;int;-crop;37x37+0+0;-ppm" + ${testout}_444_islow_ari_crop37x37,0,0.ppm + ${testout}_444_islow_ari.jpg ${MD5_PPM_444_ISLOW_ARI_CROP37x37_0_0} + ${cjpeg}-${libtype}-444-islow-ari) + endif() + endif() + + add_bittest(${jpegtran} crop "-crop;120x90+20+50;-transpose;-perfect" + ${testout}_crop.jpg ${TESTIMAGES}/${TESTORIG} + ${MD5_JPEG_CROP}) + + unset(EXAMPLE_12BIT_ARG) + if(sample_bits EQUAL 12) + set(EXAMPLE_12BIT_ARG "-12bit") endif() - endif() - add_bittest(jpegtran crop "-crop;120x90+20+50;-transpose;-perfect" - testout_crop.jpg ${TESTIMAGES}/${TESTORIG} - ${MD5_JPEG_CROP}) + add_test(example-${sample_bits}bit-${libtype}-compress + ${CMAKE_CROSSCOMPILING_EMULATOR} example${suffix} compress -q 95 + ${EXAMPLE_12BIT_ARG} ${testout}-example.jpg) + add_test(example-${sample_bits}bit-${libtype}-compress-cmp + ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} ${MD5_JPEG_EXAMPLE_COMPRESS} + ${testout}-example.jpg) + set_tests_properties(example-${sample_bits}bit-${libtype}-compress-cmp + PROPERTIES DEPENDS example-${sample_bits}bit-${libtype}-compress) + + add_test(example-${sample_bits}bit-${libtype}-decompress + ${CMAKE_CROSSCOMPILING_EMULATOR} example${suffix} decompress + ${EXAMPLE_12BIT_ARG} ${testout}-example.jpg ${testout}-example.ppm) + add_test(example-${sample_bits}bit-${libtype}-decompress-cmp + ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} ${MD5_PPM_EXAMPLE_DECOMPRESS} + ${testout}-example.ppm) + set_tests_properties(example-${sample_bits}bit-${libtype}-decompress-cmp + PROPERTIES DEPENDS example-${sample_bits}bit-${libtype}-decompress) + + endforeach() endforeach() @@ -1479,12 +1564,26 @@ if(ENABLE_STATIC) install(PROGRAMS ${DIR}/jpegtran-static${EXE} DESTINATION ${CMAKE_INSTALL_BINDIR} RENAME jpegtran${EXE}) endif() + + if(WITH_12BIT) + install(TARGETS jpeg12-static EXPORT ${CMAKE_PROJECT_NAME}Targets + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + if(NOT ENABLE_SHARED) + install(PROGRAMS ${DIR}/cjpeg12-static${EXE} + DESTINATION ${CMAKE_INSTALL_BINDIR} RENAME cjpeg12${EXE}) + install(PROGRAMS ${DIR}/djpeg12-static${EXE} + DESTINATION ${CMAKE_INSTALL_BINDIR} RENAME djpeg12${EXE}) + install(PROGRAMS ${DIR}/jpeg12tran-static${EXE} + DESTINATION ${CMAKE_INSTALL_BINDIR} RENAME jpeg12tran${EXE}) + endif() + endif() endif() install(TARGETS rdjpgcom wrjpgcom RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/README.ijg - ${CMAKE_CURRENT_SOURCE_DIR}/README.md ${CMAKE_CURRENT_SOURCE_DIR}/example.txt + ${CMAKE_CURRENT_SOURCE_DIR}/README.md ${CMAKE_CURRENT_SOURCE_DIR}/example.c ${CMAKE_CURRENT_SOURCE_DIR}/tjexample.c ${CMAKE_CURRENT_SOURCE_DIR}/libjpeg.txt ${CMAKE_CURRENT_SOURCE_DIR}/structure.txt @@ -1505,6 +1604,10 @@ endif() install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgscripts/libjpeg.pc ${CMAKE_CURRENT_BINARY_DIR}/pkgscripts/libturbojpeg.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) +if(WITH_12BIT) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgscripts/libjpeg12.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) +endif() install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgscripts/${CMAKE_PROJECT_NAME}Config.cmake ${CMAKE_CURRENT_BINARY_DIR}/pkgscripts/${CMAKE_PROJECT_NAME}ConfigVersion.cmake @@ -1517,6 +1620,10 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/jconfig.h ${CMAKE_CURRENT_SOURCE_DIR}/jerror.h ${CMAKE_CURRENT_SOURCE_DIR}/jmorecfg.h ${CMAKE_CURRENT_SOURCE_DIR}/jpeglib.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +if(WITH_12BIT) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/jpeg12lib.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +endif() include(cmakescripts/BuildPackages.cmake) diff --git a/ChangeLog.md b/ChangeLog.md index e6700c3c2..2855211e6 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,18 @@ +2.2 pre-beta +============ + +### Significant changes relative to 2.1.3 + +1. By default, the build system now builds, packages, and tests a separate +12-bit-per-component flavor of the libjpeg API library, cjpeg, djpeg, and +jpegtran. The 12-bit-per-component libjpeg API library mangles the names of +libjpeg API functions, data types, structures, and macros to enable calling +applications to more easily support both 8-bit-per-component and +12-bit-per-component JPEG images. (Refer to [libjpeg.txt](libjpeg.txt) for +more details.) The `WITH_12BIT` CMake variable can be used to disable +12-bit-per-component JPEG support. + + 2.1.3 ===== diff --git a/README.ijg b/README.ijg index 9453c1950..ab5868124 100644 --- a/README.ijg +++ b/README.ijg @@ -43,7 +43,7 @@ User documentation: change.log Version-to-version change highlights. Programmer and internal documentation: libjpeg.txt How to use the JPEG library in your own programs. - example.txt Sample code for calling the JPEG library. + example.c Sample code for calling the JPEG library. structure.txt Overview of the JPEG library's internal structure. coderules.txt Coding style rules --- please read if you contribute code. diff --git a/cdjpeg.h b/cdjpeg.h index 082687ce0..e340332ef 100644 --- a/cdjpeg.h +++ b/cdjpeg.h @@ -5,7 +5,7 @@ * Copyright (C) 1994-1997, Thomas G. Lane. * Modified 2019 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2017, 2019, 2021, D. R. Commander. + * Copyright (C) 2017, 2019, 2021-2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -16,7 +16,7 @@ #define JPEG_CJPEG_DJPEG /* define proper options in jconfig.h */ #define JPEG_INTERNAL_OPTIONS /* cjpeg.c,djpeg.c need to see xxx_SUPPORTED */ #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jerror.h" /* get library error codes too */ #include "cderror.h" /* get application-specific error codes */ diff --git a/cmakescripts/BuildPackages.cmake b/cmakescripts/BuildPackages.cmake index cfbb2c8ab..429ae66de 100644 --- a/cmakescripts/BuildPackages.cmake +++ b/cmakescripts/BuildPackages.cmake @@ -90,6 +90,10 @@ if(WITH_JAVA) set(INST_DEFS ${INST_DEFS} -DJAVA) endif() +if(WITH_12BIT) + set(INST_DEFS ${INST_DEFS} -D12BIT) +endif() + if(MSVC_IDE) set(INST_DEFS ${INST_DEFS} "-DBUILDDIR=${CMAKE_CFG_INTDIR}\\") else() @@ -109,10 +113,13 @@ configure_file(win/${INST_ID}/projectTargets-release.cmake.in if(WITH_JAVA) set(JAVA_DEPEND turbojpeg-java) endif() +if(WITH_12BIT) + set(12BIT_DEPEND jpeg12 jpeg12-static cjpeg12 djpeg12 jpeg12tran) +endif() add_custom_target(installer makensis -nocd ${INST_DEFS} installer.nsi DEPENDS jpeg jpeg-static turbojpeg turbojpeg-static rdjpgcom wrjpgcom - cjpeg djpeg jpegtran tjbench ${JAVA_DEPEND} + cjpeg djpeg jpegtran tjbench ${JAVA_DEPEND} ${12BIT_DEPEND} SOURCES installer.nsi) endif() # WIN32 @@ -159,6 +166,10 @@ add_custom_target(tarball pkgscripts/maketarball configure_file(release/libjpeg.pc.in pkgscripts/libjpeg.pc @ONLY) +if(WITH_12BIT) + configure_file(release/libjpeg12.pc.in pkgscripts/libjpeg12.pc @ONLY) +endif() + configure_file(release/libturbojpeg.pc.in pkgscripts/libturbojpeg.pc @ONLY) include(CMakePackageConfigHelpers) diff --git a/cmyk.h b/cmyk.h index 48187a8f5..e1a332c58 100644 --- a/cmyk.h +++ b/cmyk.h @@ -1,7 +1,7 @@ /* * cmyk.h * - * Copyright (C) 2017-2018, D. R. Commander. + * Copyright (C) 2017-2018, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -16,7 +16,7 @@ #include #define JPEG_INTERNALS -#include +#include "jpeglibint.h" #include "jconfigint.h" diff --git a/example.txt b/example.c similarity index 60% rename from example.txt rename to example.c index d473aede2..f795aea01 100644 --- a/example.txt +++ b/example.c @@ -1,33 +1,41 @@ /* - * example.txt + * example.c * - * This file illustrates how to use the IJG code as a subroutine library - * to read or write JPEG image files. You should look at this code in - * conjunction with the documentation file libjpeg.txt. + * This file was part of the Independent JPEG Group's software. + * Copyright (C) 1992-1996, Thomas G. Lane. + * libjpeg-turbo Modifications: + * Copyright (C) 2017, 2019, 2022, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * - * This code will not do anything useful as-is, but it may be helpful as a - * skeleton for constructing routines that call the JPEG library. + * This file illustrates how to use the IJG code as a subroutine library + * to read or write JPEG image files with 8-bit or 12-bit data precision. You + * should look at this code in conjunction with the documentation file + * libjpeg.txt. * * We present these routines in the same coding style used in the JPEG code * (ANSI function definitions, etc); but you are of course free to code your * routines in a different style if you prefer. */ -/* This example was part of the original libjpeg documentation and has been - * unchanged since 1994. It is, as described in libjpeg.txt, "heavily - * commented skeleton code for calling the JPEG library." It is not meant to - * be compiled as a standalone program, since it has no main() function and - * does not compress from/decompress to a real image buffer (corollary: - * put_scanline_someplace() is not a real function.) First-time users of - * libjpeg-turbo would be better served by looking at tjexample.c, which uses - * the more straightforward TurboJPEG API, or at cjpeg.c and djpeg.c, which are - * examples of libjpeg API usage that can be (and are) compiled into standalone - * programs. Note that this example, as well as the examples in cjpeg.c and - * djpeg.c, interleave disk I/O with JPEG compression/decompression, so none of - * these examples is suitable for benchmarking purposes. +/* First-time users of libjpeg-turbo might be better served by looking at + * tjexample.c, which uses the more straightforward TurboJPEG API. Note that + * this example, like cjpeg and djpeg, interleaves disk I/O with JPEG + * compression/decompression, so it is not suitable for benchmarking purposes. */ +#ifdef _MSC_VER +#define _CRT_SECURE_NO_DEPRECATE +#endif + #include +#include +#include + +#ifdef _WIN32 +#define strcasecmp stricmp +#define strncasecmp strnicmp +#endif /* * Include file for users of JPEG library. @@ -38,6 +46,7 @@ */ #include "jpeglib.h" +#include "jpeg12lib.h" /* * is used for the optional error recovery mechanism shown in @@ -72,17 +81,16 @@ * RGB color and is described by: */ -extern JSAMPLE *image_buffer; /* Points to large array of R,G,B-order data */ -extern int image_height; /* Number of rows in image */ -extern int image_width; /* Number of columns in image */ +#define WIDTH 640 /* Number of columns in image */ +#define HEIGHT 480 /* Number of rows in image */ /* - * Sample routine for JPEG compression. We assume that the target file name - * and a compression quality factor are passed in. + * Sample routine for JPEG compression with 8-bit data precision. We assume + * that the target file name and a compression quality factor are passed in. */ -GLOBAL(void) +METHODDEF(void) write_JPEG_file(char *filename, int quality) { /* This struct contains the JPEG compression parameters and pointers to @@ -103,8 +111,10 @@ write_JPEG_file(char *filename, int quality) struct jpeg_error_mgr jerr; /* More stuff */ FILE *outfile; /* target file */ + JSAMPARRAY image_buffer; /* Points to large array of R,G,B-order data */ JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ int row_stride; /* physical row width in image buffer */ + int row, col; /* Step 1: allocate and initialize JPEG compression object */ @@ -136,8 +146,8 @@ write_JPEG_file(char *filename, int quality) /* First we supply a description of the input image. * Four fields of the cinfo struct must be filled in: */ - cinfo.image_width = image_width; /* image width and height, in pixels */ - cinfo.image_height = image_height; + cinfo.image_width = WIDTH; /* image width and height, in pixels */ + cinfo.image_height = HEIGHT; cinfo.input_components = 3; /* # of color components per pixel */ cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ /* Now use the library's routine to set default compression parameters. @@ -149,6 +159,8 @@ write_JPEG_file(char *filename, int quality) * Here we just illustrate the use of quality (quantization table) scaling: */ jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); + /* Use 4:4:4 subsampling (default is 4:2:0) */ + cinfo.comp_info[0].h_samp_factor = cinfo.comp_info[0].v_samp_factor = 1; /* Step 4: Start compressor */ @@ -157,7 +169,31 @@ write_JPEG_file(char *filename, int quality) */ jpeg_start_compress(&cinfo, TRUE); - /* Step 5: while (scan lines remain to be written) */ + /* Step 5: allocate and initialize image buffer */ + + row_stride = WIDTH * 3; /* JSAMPLEs per row in image_buffer */ + /* Make a sample array that will go away when done with image. Note that, + * for the purposes of this example, we could also create a one-row-high + * sample array and initialize it for each successive scanline written in the + * scanline loop below. + */ + image_buffer = (*cinfo.mem->alloc_sarray) + ((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, HEIGHT); + + /* Initialize image buffer with a repeating pattern */ + for (row = 0; row < HEIGHT; row++) { + for (col = 0; col < WIDTH; col++) { + image_buffer[row][col * 3] = + (col * (MAXJSAMPLE + 1) / WIDTH) % (MAXJSAMPLE + 1); + image_buffer[row][col * 3 + 1] = + (row * (MAXJSAMPLE + 1) / HEIGHT) % (MAXJSAMPLE + 1); + image_buffer[row][col * 3 + 2] = + (row * (MAXJSAMPLE + 1) / HEIGHT + col * (MAXJSAMPLE + 1) / WIDTH) % + (MAXJSAMPLE + 1); + } + } + + /* Step 6: while (scan lines remain to be written) */ /* jpeg_write_scanlines(...); */ /* Here we use the library's state variable cinfo.next_scanline as the @@ -165,24 +201,22 @@ write_JPEG_file(char *filename, int quality) * To keep things simple, we pass one scanline per call; you can pass * more if you wish, though. */ - row_stride = image_width * 3; /* JSAMPLEs per row in image_buffer */ - while (cinfo.next_scanline < cinfo.image_height) { /* jpeg_write_scanlines expects an array of pointers to scanlines. * Here the array is only one element long, but you could pass * more than one scanline at a time if that's more convenient. */ - row_pointer[0] = &image_buffer[cinfo.next_scanline * row_stride]; + row_pointer[0] = image_buffer[cinfo.next_scanline]; (void)jpeg_write_scanlines(&cinfo, row_pointer, 1); } - /* Step 6: Finish compression */ + /* Step 7: Finish compression */ jpeg_finish_compress(&cinfo); /* After finish_compress, we can close the output file. */ fclose(outfile); - /* Step 7: release JPEG compression object */ + /* Step 8: release JPEG compression object */ /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_compress(&cinfo); @@ -220,6 +254,76 @@ write_JPEG_file(char *filename, int quality) */ +/* + * Sample routine for JPEG compression with 12-bit data precision. We assume + * that the target file name and a compression quality factor are passed in. + */ + +#ifdef WITH_12BIT + +METHODDEF(void) +write_JPEG12_file(char *filename, int quality) +{ + struct jpeg12_compress_struct cinfo; + struct jpeg12_error_mgr jerr; + FILE *outfile; + J12SAMPARRAY image_buffer; + J12SAMPROW row_pointer[1]; + int row_stride; + int row, col; + + cinfo.err = jpeg12_std_error(&jerr); + jpeg12_create_compress(&cinfo); + + if ((outfile = fopen(filename, "wb")) == NULL) { + fprintf(stderr, "can't open %s\n", filename); + exit(1); + } + jpeg12_stdio_dest(&cinfo, outfile); + + cinfo.image_width = WIDTH; + cinfo.image_height = HEIGHT; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + jpeg12_set_defaults(&cinfo); + jpeg12_set_quality(&cinfo, quality, TRUE); + cinfo.comp_info[0].h_samp_factor = cinfo.comp_info[0].v_samp_factor = 1; + + jpeg12_start_compress(&cinfo, TRUE); + + row_stride = WIDTH * 3; + image_buffer = (*cinfo.mem->alloc_sarray) + ((j12_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, HEIGHT); + + /* Initialize image buffer with a repeating pattern */ + for (row = 0; row < HEIGHT; row++) { + for (col = 0; col < WIDTH; col++) { + image_buffer[row][col * 3] = + (col * (MAXJ12SAMPLE + 1) / WIDTH) % (MAXJ12SAMPLE + 1); + image_buffer[row][col * 3 + 1] = + (row * (MAXJ12SAMPLE + 1) / HEIGHT) % (MAXJ12SAMPLE + 1); + image_buffer[row][col * 3 + 2] = + (row * (MAXJ12SAMPLE + 1) / HEIGHT + + col * (MAXJ12SAMPLE + 1) / WIDTH) % (MAXJ12SAMPLE + 1); + } + } + + row_stride = WIDTH * 3; + + while (cinfo.next_scanline < cinfo.image_height) { + row_pointer[0] = image_buffer[cinfo.next_scanline]; + (void)jpeg12_write_scanlines(&cinfo, row_pointer, 1); + } + + jpeg12_finish_compress(&cinfo); + fclose(outfile); + + jpeg12_destroy_compress(&cinfo); +} + +#endif + + /******************** JPEG DECOMPRESSION SAMPLE INTERFACE *******************/ @@ -289,22 +393,23 @@ my_error_exit(j_common_ptr cinfo) METHODDEF(int) do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, - char *filename); + char *infilename, char *outfilename); /* - * Sample routine for JPEG decompression. We assume that the source file name - * is passed in. We want to return 1 on success, 0 on error. + * Sample routine for JPEG decompression with 8-bit data precision. We assume + * that the source file name is passed in. We want to return 1 on success, 0 + * on error. */ -GLOBAL(int) -read_JPEG_file(char *filename) +METHODDEF(int) +read_JPEG_file(char *infilename, char *outfilename) { /* This struct contains the JPEG decompression parameters and pointers to * working space (which is allocated as needed by the JPEG library). */ struct jpeg_decompress_struct cinfo; - return do_read_JPEG_file(&cinfo, filename); + return do_read_JPEG_file(&cinfo, infilename, outfilename); } /* @@ -316,7 +421,8 @@ read_JPEG_file(char *filename) */ METHODDEF(int) -do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, char *filename) +do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, char *infilename, + char *outfilename) { /* We use our private extension JPEG error handler. * Note that this struct must live as long as the main JPEG parameter @@ -325,19 +431,29 @@ do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, char *filename) struct my_error_mgr jerr; /* More stuff */ FILE *infile; /* source file */ + FILE *outfile; /* output file */ JSAMPARRAY buffer; /* Output row buffer */ int row_stride; /* physical row width in output buffer */ - /* In this example we want to open the input file before doing anything else, - * so that the setjmp() error recovery below can assume the file is open. + /* In this example we want to open the input and output files before doing + * anything else, so that the setjmp() error recovery below can assume the + * files are open. + * * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that - * requires it in order to read binary files. + * requires it in order to read/write binary files. */ - if ((infile = fopen(filename, "rb")) == NULL) { - fprintf(stderr, "can't open %s\n", filename); + if ((infile = fopen(infilename, "rb")) == NULL) { + fprintf(stderr, "can't open %s\n", infilename); + return 0; + } + if ((outfile = fopen(outfilename, "wb")) == NULL) { + fprintf(stderr, "can't open %s\n", outfilename); + fclose(infile); return 0; } + /* emit header for raw PPM format */ + fprintf(outfile, "P6\n%d %d\n%d\n", WIDTH, HEIGHT, MAXJSAMPLE); /* Step 1: allocate and initialize JPEG decompression object */ @@ -351,6 +467,7 @@ do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, char *filename) */ jpeg_destroy_decompress(cinfo); fclose(infile); + fclose(outfile); return 0; } /* Now we can initialize the JPEG decompression object. */ @@ -392,7 +509,7 @@ do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, char *filename) row_stride = cinfo->output_width * cinfo->output_components; /* Make a one-row-high sample array that will go away when done with image */ buffer = (*cinfo->mem->alloc_sarray) - ((j_common_ptr)cinfo, JPOOL_IMAGE, row_stride, 1); + ((j_common_ptr)cinfo, JPOOL_IMAGE, row_stride, 1); /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ @@ -406,8 +523,7 @@ do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, char *filename) * more than one scanline at a time if that's more convenient. */ (void)jpeg_read_scanlines(cinfo, buffer, 1); - /* Assume put_scanline_someplace wants a pointer and sample count. */ - put_scanline_someplace(buffer[0], row_stride); + fwrite(buffer[0], 1, row_stride, outfile); } /* Step 7: Finish decompression */ @@ -422,12 +538,13 @@ do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, char *filename) /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_decompress(cinfo); - /* After finish_decompress, we can close the input file. + /* After finish_decompress, we can close the input and output files. * Here we postpone it until after no more JPEG errors are possible, * so as to simplify the setjmp error logic above. (Actually, I don't * think that jpeg_destroy can do an error exit, but why assume anything...) */ fclose(infile); + fclose(outfile); /* At this point you may want to check to see whether any corrupt-data * warnings occurred (test whether jerr.pub.num_warnings is nonzero). @@ -462,3 +579,202 @@ do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, char *filename) * On some systems you may need to set up a signal handler to ensure that * temporary files are deleted if the program is interrupted. See libjpeg.txt. */ + + +#ifdef WITH_12BIT + +struct my12_error_mgr { + struct jpeg12_error_mgr pub; + + jmp_buf setjmp_buffer; +}; + +typedef struct my12_error_mgr *my12_error_ptr; + +METHODDEF(void) +my12_error_exit(j12_common_ptr cinfo) +{ + my12_error_ptr myerr = (my12_error_ptr)cinfo->err; + + (*cinfo->err->output_message) (cinfo); + + longjmp(myerr->setjmp_buffer, 1); +} + +METHODDEF(int) do_read_JPEG12_file(struct jpeg12_decompress_struct *cinfo, + char *infilename, char *outfilename); + +/* + * Sample routine for JPEG decompression with 12-bit data precision. We assume + * that the source file name is passed in. We want to return 1 on success, 0 + * on error. + */ + +METHODDEF(int) +read_JPEG12_file(char *infilename, char *outfilename) +{ + struct jpeg12_decompress_struct cinfo; + + return do_read_JPEG12_file(&cinfo, infilename, outfilename); +} + +METHODDEF(int) +do_read_JPEG12_file(struct jpeg12_decompress_struct *cinfo, char *infilename, + char *outfilename) +{ + struct my12_error_mgr jerr; + FILE *infile; + FILE *outfile; + J12SAMPARRAY buffer; + int row_stride; + int col; + + if ((infile = fopen(infilename, "rb")) == NULL) { + fprintf(stderr, "can't open %s\n", infilename); + return 0; + } + if ((outfile = fopen(outfilename, "wb")) == NULL) { + fprintf(stderr, "can't open %s\n", outfilename); + fclose(infile); + return 0; + } + fprintf(outfile, "P6\n%d %d\n%d\n", WIDTH, HEIGHT, MAXJ12SAMPLE); + + cinfo->err = jpeg12_std_error(&jerr.pub); + jerr.pub.error_exit = my12_error_exit; + if (setjmp(jerr.setjmp_buffer)) { + jpeg12_destroy_decompress(cinfo); + fclose(infile); + fclose(outfile); + return 0; + } + jpeg12_create_decompress(cinfo); + + jpeg12_stdio_src(cinfo, infile); + + (void)jpeg12_read_header(cinfo, TRUE); + + (void)jpeg12_start_decompress(cinfo); + + row_stride = cinfo->output_width * cinfo->output_components; + buffer = (*cinfo->mem->alloc_sarray) + ((j12_common_ptr)cinfo, JPOOL_IMAGE, row_stride, 1); + + while (cinfo->output_scanline < cinfo->output_height) { + (void)jpeg12_read_scanlines(cinfo, buffer, 1); + /* Swap MSB and LSB in each sample */ + for (col = 0; col < row_stride; col++) + buffer[0][col] = ((buffer[0][col] & 0xFF) << 8) | + ((buffer[0][col] >> 8) & 0xFF); + fwrite(buffer[0], 1, row_stride * sizeof(J12SAMPLE), outfile); + } + + (void)jpeg12_finish_decompress(cinfo); + + jpeg12_destroy_decompress(cinfo); + + fclose(infile); + fclose(outfile); + + return 1; +} + +#endif + + + +LOCAL(void) +usage(const char *progname) +{ + fprintf(stderr, "usage: %s compress [switches] outputfile[.jpg]\n", + progname); + fprintf(stderr, " %s decompress inputfile[.jpg] outputfile[.ppm]\n", + progname); + fprintf(stderr, "Switches (names may be abbreviated):\n"); +#ifdef WITH_12BIT + fprintf(stderr, " -12bit Compress/decompress JPEG file with 12-bit data precision\n"); +#endif + fprintf(stderr, " -quality N Compression quality (0..100; 5-95 is most useful range,\n"); + fprintf(stderr, " default is 75)\n"); + + exit(EXIT_FAILURE); +} + + +typedef enum { + COMPRESS, + DECOMPRESS +} EXAMPLE_MODE; + + +int +main(int argc, char **argv) +{ + int argn, quality = 75; +#ifdef WITH_12BIT + int _12bit = 0; +#endif + EXAMPLE_MODE mode; + char *arg, *filename = NULL; + + if (argc < 3) + usage(argv[0]); + + if (!strcasecmp(argv[1], "compress")) + mode = COMPRESS; + else if (!strcasecmp(argv[1], "decompress")) + mode = DECOMPRESS; + else + usage(argv[0]); + + for (argn = 2; argn < argc; argn++) { + arg = argv[argn]; + if (*arg != '-') { + filename = arg; + /* Not a switch, must be a file name argument */ + break; /* done parsing switches */ + } + arg++; /* advance past switch marker character */ + +#ifdef WITH_12BIT + if (!strncasecmp(arg, "1", 1)) { + _12bit = 1; + } + else +#endif + if (!strncasecmp(arg, "q", 1)) { + /* Quality rating (quantization table scaling factor). */ + if (++argn >= argc) /* advance to next argument */ + usage(argv[0]); + if (sscanf(argv[argn], "%d", &quality) < 1 || quality < 0 || + quality > 100) + usage(argv[0]); + if (quality < 1) + quality = 1; + } + } + + if (!filename) + usage(argv[0]); + + if (mode == COMPRESS) { +#ifdef WITH_12BIT + if (_12bit) + write_JPEG12_file(filename, quality); + else +#endif + write_JPEG_file(filename, quality); + } else if (mode == DECOMPRESS) { + if (argc - argn < 2) + usage(argv[0]); + +#ifdef WITH_12BIT + if (_12bit) + read_JPEG12_file(argv[argn], argv[argn + 1]); + else +#endif + read_JPEG_file(argv[argn], argv[argn + 1]); + } + + return 0; +} diff --git a/jcapimin.c b/jcapimin.c index 84e7ecc9a..a57f3d69a 100644 --- a/jcapimin.c +++ b/jcapimin.c @@ -22,7 +22,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" /* diff --git a/jcapistd.c b/jcapistd.c index aa2aad9f6..64bb9f941 100644 --- a/jcapistd.c +++ b/jcapistd.c @@ -1,8 +1,10 @@ /* * jcapistd.c * + * This file was part of the Independent JPEG Group's software. * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -17,7 +19,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" /* diff --git a/jccoefct.c b/jccoefct.c index 068232a52..cd27e5bd7 100644 --- a/jccoefct.c +++ b/jccoefct.c @@ -3,8 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1997, Thomas G. Lane. - * It was modified by The libjpeg-turbo Project to include only code and - * information relevant to libjpeg-turbo. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -15,7 +15,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" /* We use a full-image coefficient buffer when doing Huffman optimization, diff --git a/jccolor.c b/jccolor.c index bdc563c72..cbf3720d5 100644 --- a/jccolor.c +++ b/jccolor.c @@ -5,7 +5,7 @@ * Copyright (C) 1991-1996, Thomas G. Lane. * libjpeg-turbo Modifications: * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2009-2012, 2015, D. R. Commander. + * Copyright (C) 2009-2012, 2015, 2022, D. R. Commander. * Copyright (C) 2014, MIPS Technologies, Inc., California. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -15,7 +15,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jsimd.h" #include "jconfigint.h" diff --git a/jcdctmgr.c b/jcdctmgr.c index 7dae17a6e..2a30d07a1 100644 --- a/jcdctmgr.c +++ b/jcdctmgr.c @@ -6,7 +6,7 @@ * libjpeg-turbo Modifications: * Copyright (C) 1999-2006, MIYASAKA Masaru. * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2011, 2014-2015, D. R. Commander. + * Copyright (C) 2011, 2014-2015, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -18,7 +18,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jdct.h" /* Private declarations for DCT subsystem */ #include "jsimddct.h" diff --git a/jchuff.c b/jchuff.c index f4dfa1cb5..bb9e14ce5 100644 --- a/jchuff.c +++ b/jchuff.c @@ -25,7 +25,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jsimd.h" #include "jconfigint.h" #include diff --git a/jcicc.c b/jcicc.c index 11037ff69..8390ec267 100644 --- a/jcicc.c +++ b/jcicc.c @@ -2,7 +2,7 @@ * jcicc.c * * Copyright (C) 1997-1998, Thomas G. Lane, Todd Newman. - * Copyright (C) 2017, D. R. Commander. + * Copyright (C) 2017, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -15,7 +15,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jerror.h" diff --git a/jcinit.c b/jcinit.c index 157353a22..b99cf6dc2 100644 --- a/jcinit.c +++ b/jcinit.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2020, D. R. Commander. + * Copyright (C) 2020, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -20,7 +20,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jpegcomp.h" diff --git a/jcmainct.c b/jcmainct.c index 3f23028c4..6367ce872 100644 --- a/jcmainct.c +++ b/jcmainct.c @@ -3,8 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. - * It was modified by The libjpeg-turbo Project to include only code relevant - * to libjpeg-turbo. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -15,7 +15,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" /* Private buffer controller object */ diff --git a/jcmarker.c b/jcmarker.c index 801fbab4e..4bdcadfba 100644 --- a/jcmarker.c +++ b/jcmarker.c @@ -5,7 +5,7 @@ * Copyright (C) 1991-1998, Thomas G. Lane. * Modified 2003-2010 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2010, D. R. Commander. + * Copyright (C) 2010, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -14,7 +14,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jpegcomp.h" diff --git a/jcmaster.c b/jcmaster.c index c2b260003..b02c3a287 100644 --- a/jcmaster.c +++ b/jcmaster.c @@ -5,7 +5,7 @@ * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 2003-2010 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2010, 2016, 2018, D. R. Commander. + * Copyright (C) 2010, 2016, 2018, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -17,7 +17,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jpegcomp.h" #include "jconfigint.h" diff --git a/jcomapi.c b/jcomapi.c index efbb8357b..4da13e65d 100644 --- a/jcomapi.c +++ b/jcomapi.c @@ -3,8 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1997, Thomas G. Lane. - * It was modified by The libjpeg-turbo Project to include only code relevant - * to libjpeg-turbo. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -14,7 +14,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" /* diff --git a/jconfig.h.in b/jconfig.h.in index e0180122f..77ce90858 100644 --- a/jconfig.h.in +++ b/jconfig.h.in @@ -9,29 +9,39 @@ /* libjpeg-turbo version in integer form */ #define LIBJPEG_TURBO_VERSION_NUMBER @LIBJPEG_TURBO_VERSION_NUMBER@ -/* Support arithmetic encoding */ +/* Support arithmetic encoding when using 8-bit samples */ #cmakedefine C_ARITH_CODING_SUPPORTED 1 -/* Support arithmetic decoding */ +/* Support arithmetic decoding when using 8-bit samples */ #cmakedefine D_ARITH_CODING_SUPPORTED 1 /* Support in-memory source/destination managers */ #cmakedefine MEM_SRCDST_SUPPORTED 1 -/* Use accelerated SIMD routines. */ +/* Use accelerated SIMD routines when using 8-bit samples */ #cmakedefine WITH_SIMD 1 -/* - * 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 _WIN32 + +#undef RIGHT_SHIFT_IS_UNSIGNED + +/* Define "boolean" as unsigned char, not int, per Windows custom */ +#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */ +typedef unsigned char boolean; +#endif +#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */ -#define BITS_IN_JSAMPLE @BITS_IN_JSAMPLE@ /* use 8 or 12 */ +/* Define "INT32" as int, not long, per Windows custom */ +#if !(defined(_BASETSD_H_) || defined(_BASETSD_H)) /* don't conflict if basetsd.h already read */ +typedef short INT16; +typedef signed int INT32; +#endif +#define XMD_H /* prevent jmorecfg.h from redefining it */ + +#else /* Define if your (broken) compiler shifts signed values as if they were unsigned. */ #cmakedefine RIGHT_SHIFT_IS_UNSIGNED 1 + +#endif diff --git a/jconfigint.h.in b/jconfigint.h.in index d087d7b55..e7e66e74c 100644 --- a/jconfigint.h.in +++ b/jconfigint.h.in @@ -42,3 +42,32 @@ #else #define FALLTHROUGH #endif + +/* + * 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! + */ + +#ifndef BITS_IN_JSAMPLE +#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */ +#endif + +#undef C_ARITH_CODING_SUPPORTED +#undef D_ARITH_CODING_SUPPORTED +#undef WITH_SIMD + +#if BITS_IN_JSAMPLE == 8 + +/* Support arithmetic encoding */ +#cmakedefine C_ARITH_CODING_SUPPORTED 1 + +/* Support arithmetic decoding */ +#cmakedefine D_ARITH_CODING_SUPPORTED 1 + +/* Use accelerated SIMD routines. */ +#cmakedefine WITH_SIMD 1 + +#endif diff --git a/jcparam.c b/jcparam.c index 5bc7174dc..067022587 100644 --- a/jcparam.c +++ b/jcparam.c @@ -5,7 +5,7 @@ * Copyright (C) 1991-1998, Thomas G. Lane. * Modified 2003-2008 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2009-2011, 2018, D. R. Commander. + * Copyright (C) 2009-2011, 2018, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -16,7 +16,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jstdhuff.c" diff --git a/jcphuff.c b/jcphuff.c index 872e570bf..9a6d3bfc2 100644 --- a/jcphuff.c +++ b/jcphuff.c @@ -20,7 +20,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jsimd.h" #include "jconfigint.h" #include diff --git a/jcprepct.c b/jcprepct.c index f27cc3450..fe7519192 100644 --- a/jcprepct.c +++ b/jcprepct.c @@ -19,7 +19,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" /* At present, jcsample.c can request context rows only for smoothing. diff --git a/jcsample.c b/jcsample.c index e8515ebf0..93557ed31 100644 --- a/jcsample.c +++ b/jcsample.c @@ -6,7 +6,7 @@ * libjpeg-turbo Modifications: * Copyright 2009 Pierre Ossman for Cendio AB * Copyright (C) 2014, MIPS Technologies, Inc., California. - * Copyright (C) 2015, 2019, D. R. Commander. + * Copyright (C) 2015, 2019, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -52,7 +52,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jsimd.h" diff --git a/jctrans.c b/jctrans.c index e121028ec..af2e79868 100644 --- a/jctrans.c +++ b/jctrans.c @@ -16,7 +16,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jpegcomp.h" diff --git a/jdapimin.c b/jdapimin.c index f50c27edc..a0d07931a 100644 --- a/jdapimin.c +++ b/jdapimin.c @@ -21,7 +21,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jdmaster.h" #include "jconfigint.h" diff --git a/jdatadst.c b/jdatadst.c index 6b4fed233..2df66d626 100644 --- a/jdatadst.c +++ b/jdatadst.c @@ -20,7 +20,7 @@ /* this is not a core library module, so it doesn't define JPEG_INTERNALS */ #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jerror.h" diff --git a/jdatasrc.c b/jdatasrc.c index e36a30d89..4d6f04157 100644 --- a/jdatasrc.c +++ b/jdatasrc.c @@ -20,7 +20,7 @@ /* this is not a core library module, so it doesn't define JPEG_INTERNALS */ #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jerror.h" diff --git a/jdcoefct.h b/jdcoefct.h index 9a0e78066..f6cb52feb 100644 --- a/jdcoefct.h +++ b/jdcoefct.h @@ -6,12 +6,13 @@ * libjpeg-turbo Modifications: * Copyright 2009 Pierre Ossman for Cendio AB * Copyright (C) 2020, Google, Inc. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. */ #define JPEG_INTERNALS -#include "jpeglib.h" +#include "jpeglibint.h" /* Block smoothing is only applicable for progressive JPEG, so: */ diff --git a/jdcolor.c b/jdcolor.c index 8da2b4eaf..586ed19de 100644 --- a/jdcolor.c +++ b/jdcolor.c @@ -6,7 +6,7 @@ * Modified 2011 by Guido Vollbeding. * libjpeg-turbo Modifications: * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2009, 2011-2012, 2014-2015, D. R. Commander. + * Copyright (C) 2009, 2011-2012, 2014-2015, 2022, D. R. Commander. * Copyright (C) 2013, Linaro Limited. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -16,7 +16,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jsimd.h" #include "jconfigint.h" diff --git a/jddctmgr.c b/jddctmgr.c index e78d7bebe..2bef52800 100644 --- a/jddctmgr.c +++ b/jddctmgr.c @@ -23,7 +23,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jdct.h" /* Private declarations for DCT subsystem */ #include "jsimddct.h" #include "jpegcomp.h" diff --git a/jdhuff.c b/jdhuff.c index 679d22168..8b691edbc 100644 --- a/jdhuff.c +++ b/jdhuff.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2009-2011, 2016, 2018-2019, D. R. Commander. + * Copyright (C) 2009-2011, 2016, 2018-2019, 2022, D. R. Commander. * Copyright (C) 2018, Matthias Räncker. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -23,7 +23,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jdhuff.h" /* Declarations shared with jdphuff.c */ #include "jpegcomp.h" #include "jstdhuff.c" diff --git a/jdicc.c b/jdicc.c index 50aa9a967..266a90ef6 100644 --- a/jdicc.c +++ b/jdicc.c @@ -2,7 +2,7 @@ * jdicc.c * * Copyright (C) 1997-1998, Thomas G. Lane, Todd Newman. - * Copyright (C) 2017, D. R. Commander. + * Copyright (C) 2017, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -15,7 +15,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jerror.h" diff --git a/jdinput.c b/jdinput.c index 1bc5aff1a..20d3e9079 100644 --- a/jdinput.c +++ b/jdinput.c @@ -17,7 +17,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jpegcomp.h" diff --git a/jdmainct.h b/jdmainct.h index 37b201ca8..19c88a448 100644 --- a/jdmainct.h +++ b/jdmainct.h @@ -3,12 +3,14 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. */ #define JPEG_INTERNALS -#include "jpeglib.h" +#include "jpeglibint.h" #include "jpegcomp.h" diff --git a/jdmarker.c b/jdmarker.c index f7eba615f..3e84c900e 100644 --- a/jdmarker.c +++ b/jdmarker.c @@ -17,7 +17,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" typedef enum { /* JPEG marker codes */ diff --git a/jdmaster.c b/jdmaster.c index a3690bf56..c7963ff52 100644 --- a/jdmaster.c +++ b/jdmaster.c @@ -19,7 +19,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jpegcomp.h" #include "jdmaster.h" diff --git a/jdmerge.c b/jdmerge.c index 3a456d658..59f34966c 100644 --- a/jdmerge.c +++ b/jdmerge.c @@ -5,7 +5,7 @@ * Copyright (C) 1994-1996, Thomas G. Lane. * libjpeg-turbo Modifications: * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2009, 2011, 2014-2015, 2020, D. R. Commander. + * Copyright (C) 2009, 2011, 2014-2015, 2020, 2022, D. R. Commander. * Copyright (C) 2013, Linaro Limited. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -39,7 +39,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jdmerge.h" #include "jsimd.h" #include "jconfigint.h" diff --git a/jdmerge.h b/jdmerge.h index b583396b1..86c273f9a 100644 --- a/jdmerge.h +++ b/jdmerge.h @@ -4,13 +4,13 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2020, D. R. Commander. + * Copyright (C) 2020, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. */ #define JPEG_INTERNALS -#include "jpeglib.h" +#include "jpeglibint.h" #ifdef UPSAMPLE_MERGING_SUPPORTED diff --git a/jdphuff.c b/jdphuff.c index 9680ebcbd..9ba65584b 100644 --- a/jdphuff.c +++ b/jdphuff.c @@ -22,7 +22,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jdhuff.h" /* Declarations shared with jdhuff.c */ #include diff --git a/jdpostct.c b/jdpostct.c index 6a2cf5c1b..7722e6aba 100644 --- a/jdpostct.c +++ b/jdpostct.c @@ -3,8 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. - * It was modified by The libjpeg-turbo Project to include only code relevant - * to libjpeg-turbo. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -21,7 +21,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" /* Private buffer controller object */ diff --git a/jdsample.h b/jdsample.h index a6bf08a03..4dbe30f58 100644 --- a/jdsample.h +++ b/jdsample.h @@ -3,12 +3,14 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1996, Thomas G. Lane. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. */ #define JPEG_INTERNALS -#include "jpeglib.h" +#include "jpeglibint.h" /* Pointer to routine to upsample a single component */ diff --git a/jdtrans.c b/jdtrans.c index d7ec4b83b..bae31f2ae 100644 --- a/jdtrans.c +++ b/jdtrans.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1995-1997, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2020, D. R. Commander. + * Copyright (C) 2020, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -15,7 +15,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jpegcomp.h" diff --git a/jerror.c b/jerror.c index d54470293..19cc03cc2 100644 --- a/jerror.c +++ b/jerror.c @@ -22,8 +22,9 @@ */ /* this is not a core library module, so it doesn't define JPEG_INTERNALS */ +#define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jversion.h" #include "jerror.h" diff --git a/jfdctflt.c b/jfdctflt.c index ab6f6d082..b3b3c3b4c 100644 --- a/jfdctflt.c +++ b/jfdctflt.c @@ -1,8 +1,10 @@ /* * jfdctflt.c * + * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -37,7 +39,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jdct.h" /* Private declarations for DCT subsystem */ #ifdef DCT_FLOAT_SUPPORTED diff --git a/jfdctfst.c b/jfdctfst.c index 4c9ce0de8..569ffa87f 100644 --- a/jfdctfst.c +++ b/jfdctfst.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2015, D. R. Commander. + * Copyright (C) 2015, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -35,7 +35,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jdct.h" /* Private declarations for DCT subsystem */ #ifdef DCT_IFAST_SUPPORTED diff --git a/jfdctint.c b/jfdctint.c index c95a3a7fb..5e67e04f2 100644 --- a/jfdctint.c +++ b/jfdctint.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2015, 2020, D. R. Commander. + * Copyright (C) 2015, 2020, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -28,7 +28,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jdct.h" /* Private declarations for DCT subsystem */ #ifdef DCT_ISLOW_SUPPORTED diff --git a/jidctflt.c b/jidctflt.c index 5aee74e23..d738228d3 100644 --- a/jidctflt.c +++ b/jidctflt.c @@ -5,7 +5,7 @@ * Copyright (C) 1994-1998, Thomas G. Lane. * Modified 2010 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2014, D. R. Commander. + * Copyright (C) 2014, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -42,7 +42,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jdct.h" /* Private declarations for DCT subsystem */ #ifdef DCT_FLOAT_SUPPORTED diff --git a/jidctfst.c b/jidctfst.c index 89a20c937..23b354bfa 100644 --- a/jidctfst.c +++ b/jidctfst.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1998, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2015, D. R. Commander. + * Copyright (C) 2015, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -37,7 +37,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jdct.h" /* Private declarations for DCT subsystem */ #ifdef DCT_IFAST_SUPPORTED diff --git a/jidctint.c b/jidctint.c index bb0874801..6bea037fb 100644 --- a/jidctint.c +++ b/jidctint.c @@ -5,7 +5,7 @@ * Copyright (C) 1991-1998, Thomas G. Lane. * Modification developed 2002-2018 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2015, 2020, D. R. Commander. + * Copyright (C) 2015, 2020, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -52,7 +52,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jdct.h" /* Private declarations for DCT subsystem */ #ifdef DCT_ISLOW_SUPPORTED diff --git a/jidctred.c b/jidctred.c index 1dd65a94d..492cc90dc 100644 --- a/jidctred.c +++ b/jidctred.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1998, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2015, D. R. Commander. + * Copyright (C) 2015, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -25,7 +25,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jdct.h" /* Private declarations for DCT subsystem */ #ifdef IDCT_SCALING_SUPPORTED diff --git a/jmemmgr.c b/jmemmgr.c index 8f5a4ab1c..c9ffd597a 100644 --- a/jmemmgr.c +++ b/jmemmgr.c @@ -30,7 +30,7 @@ #define JPEG_INTERNALS #define AM_MEMORY_MANAGER /* we define jvirt_Xarray_control structs */ #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jmemsys.h" /* import the system-dependent declarations */ #if !defined(_MSC_VER) || _MSC_VER > 1600 #include diff --git a/jmemnobs.c b/jmemnobs.c index cd6571ba1..a220cdc5b 100644 --- a/jmemnobs.c +++ b/jmemnobs.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1992-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2017-2018, D. R. Commander. + * Copyright (C) 2017-2018, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -19,7 +19,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jmemsys.h" /* import the system-dependent declarations */ diff --git a/jmorecfg.h b/jmorecfg.h index b33a99191..16b091e3c 100644 --- a/jmorecfg.h +++ b/jmorecfg.h @@ -5,7 +5,7 @@ * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 1997-2009 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2009, 2011, 2014-2015, 2018, 2020, D. R. Commander. + * Copyright (C) 2009, 2011, 2014-2015, 2018, 2020, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -14,6 +14,9 @@ * optimizations. Most users will not need to touch this file. */ +#ifndef JMORECFG_H +#define JMORECFG_H + /* * Maximum number of components (color channels) allowed in JPEG image. @@ -41,7 +44,6 @@ * arrays is very slow on your hardware, you might want to change these. */ -#if BITS_IN_JSAMPLE == 8 /* JSAMPLE should be the smallest type that will hold the values 0..255. */ @@ -51,21 +53,15 @@ typedef unsigned char JSAMPLE; #define MAXJSAMPLE 255 #define CENTERJSAMPLE 128 -#endif /* BITS_IN_JSAMPLE == 8 */ - -#if BITS_IN_JSAMPLE == 12 -/* JSAMPLE should be the smallest type that will hold the values 0..4095. +/* J12SAMPLE should be the smallest type that will hold the values 0..4095. * On nearly all machines "short" will do nicely. */ -typedef short JSAMPLE; -#define GETJSAMPLE(value) ((int)(value)) +typedef short J12SAMPLE; -#define MAXJSAMPLE 4095 -#define CENTERJSAMPLE 2048 - -#endif /* BITS_IN_JSAMPLE == 12 */ +#define MAXJ12SAMPLE 4095 +#define CENTERJ12SAMPLE 2048 /* Representation of a DCT frequency coefficient. @@ -380,3 +376,5 @@ static const int rgb_pixelsize[JPEG_NUMCS] = { #endif #endif /* JPEG_INTERNAL_OPTIONS */ + +#endif /* JMORECFG_H */ diff --git a/jpeg12int.h b/jpeg12int.h new file mode 100644 index 000000000..bc9448262 --- /dev/null +++ b/jpeg12int.h @@ -0,0 +1,391 @@ +/* + * jpeg12int.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 1997-2009 by Guido Vollbeding. + * libjpeg-turbo Modifications: + * Copyright (C) 2015-2016, 2019, 2021-2022, D. R. Commander. + * Copyright (C) 2015, Google, Inc. + * Copyright (C) 2021, Alex Richardson. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file provides common declarations for the various JPEG modules. + * These declarations are considered internal to the JPEG library; most + * applications using the library shouldn't need to include this file. + */ + +#ifndef JPEG12INT_H +#define JPEG12INT_H + + +#ifndef JPEGINT_H + +/* Declarations for both compression & decompression */ + +typedef enum { /* Operating modes for buffer controllers */ + JBUF_PASS_THRU, /* Plain stripwise operation */ + /* Remaining modes require a full-image buffer to have been created */ + JBUF_SAVE_SOURCE, /* Run source subobject only, save output */ + JBUF_CRANK_DEST, /* Run dest subobject only, using saved data */ + JBUF_SAVE_AND_PASS /* Run both subobjects, save output */ +} J_BUF_MODE; + +/* Values of global_state field (jdapi.c has some dependencies on ordering!) */ +#define CSTATE_START 100 /* after create_compress */ +#define CSTATE_SCANNING 101 /* start_compress done, write_scanlines OK */ +#define CSTATE_RAW_OK 102 /* start_compress done, write_raw_data OK */ +#define CSTATE_WRCOEFS 103 /* jpeg12_write_coefficients done */ +#define DSTATE_START 200 /* after create_decompress */ +#define DSTATE_INHEADER 201 /* reading header markers, no SOS yet */ +#define DSTATE_READY 202 /* found SOS, ready for start_decompress */ +#define DSTATE_PRELOAD 203 /* reading multiscan file in start_decompress*/ +#define DSTATE_PRESCAN 204 /* performing dummy pass for 2-pass quant */ +#define DSTATE_SCANNING 205 /* start_decompress done, read_scanlines OK */ +#define DSTATE_RAW_OK 206 /* start_decompress done, read_raw_data OK */ +#define DSTATE_BUFIMAGE 207 /* expecting jpeg12_start_output */ +#define DSTATE_BUFPOST 208 /* looking for SOS/EOI in + jpeg12_finish_output */ +#define DSTATE_RDCOEFS 209 /* reading file in jpeg12_read_coefficients */ +#define DSTATE_STOPPING 210 /* looking for EOI in + jpeg12_finish_decompress */ + + +/* JLONG must hold at least signed 32-bit values. */ +typedef long JLONG; + +/* JUINTPTR must hold pointer values. */ +#ifdef __UINTPTR_TYPE__ +/* + * __UINTPTR_TYPE__ is GNU-specific and available in GCC 4.6+ and Clang 3.0+. + * Fortunately, that is sufficient to support the few architectures for which + * sizeof(void *) != sizeof(size_t). The only other options would require C99 + * or Clang-specific builtins. + */ +typedef __UINTPTR_TYPE__ JUINTPTR; +#else +typedef size_t JUINTPTR; +#endif + +/* + * Left shift macro that handles a negative operand without causing any + * sanitizer warnings + */ + +#define LEFT_SHIFT(a, b) ((JLONG)((unsigned long)(a) << (b))) + +#endif /* JPEGINT_H */ + + +/* Declarations for compression modules */ + +/* Master control module */ +struct jpeg12_comp_master { + void (*prepare_for_pass) (j12_compress_ptr cinfo); + void (*pass_startup) (j12_compress_ptr cinfo); + void (*finish_pass) (j12_compress_ptr cinfo); + + /* State variables made visible to other modules */ + boolean call_pass_startup; /* True if pass_startup must be called */ + boolean is_last_pass; /* True during last pass */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg12_c_main_controller { + void (*start_pass) (j12_compress_ptr cinfo, J_BUF_MODE pass_mode); + void (*process_data) (j12_compress_ptr cinfo, J12SAMPARRAY input_buf, + JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail); +}; + +/* Compression preprocessing (downsampling input buffer control) */ +struct jpeg12_c_prep_controller { + void (*start_pass) (j12_compress_ptr cinfo, J_BUF_MODE pass_mode); + void (*pre_process_data) (j12_compress_ptr cinfo, J12SAMPARRAY input_buf, + JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail, + J12SAMPIMAGE output_buf, + JDIMENSION *out_row_group_ctr, + JDIMENSION out_row_groups_avail); +}; + +/* Coefficient buffer control */ +struct jpeg12_c_coef_controller { + void (*start_pass) (j12_compress_ptr cinfo, J_BUF_MODE pass_mode); + boolean (*compress_data) (j12_compress_ptr cinfo, J12SAMPIMAGE input_buf); +}; + +/* Colorspace conversion */ +struct jpeg12_color_converter { + void (*start_pass) (j12_compress_ptr cinfo); + void (*color_convert) (j12_compress_ptr cinfo, J12SAMPARRAY input_buf, + J12SAMPIMAGE output_buf, JDIMENSION output_row, + int num_rows); +}; + +/* Downsampling */ +struct jpeg12_downsampler { + void (*start_pass) (j12_compress_ptr cinfo); + void (*downsample) (j12_compress_ptr cinfo, J12SAMPIMAGE input_buf, + JDIMENSION in_row_index, J12SAMPIMAGE output_buf, + JDIMENSION out_row_group_index); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Forward DCT (also controls coefficient quantization) */ +struct jpeg12_forward_dct { + void (*start_pass) (j12_compress_ptr cinfo); + /* perhaps this should be an array??? */ + void (*forward_DCT) (j12_compress_ptr cinfo, jpeg_component_info *compptr, + J12SAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks); +}; + +/* Entropy encoding */ +struct jpeg12_entropy_encoder { + void (*start_pass) (j12_compress_ptr cinfo, boolean gather_statistics); + boolean (*encode_mcu) (j12_compress_ptr cinfo, JBLOCKROW *MCU_data); + void (*finish_pass) (j12_compress_ptr cinfo); +}; + +/* Marker writing */ +struct jpeg12_marker_writer { + void (*write_file_header) (j12_compress_ptr cinfo); + void (*write_frame_header) (j12_compress_ptr cinfo); + void (*write_scan_header) (j12_compress_ptr cinfo); + void (*write_file_trailer) (j12_compress_ptr cinfo); + void (*write_tables_only) (j12_compress_ptr cinfo); + /* These routines are exported to allow insertion of extra markers */ + /* Probably only COM and APPn markers should be written this way */ + void (*write_marker_header) (j12_compress_ptr cinfo, int marker, + unsigned int datalen); + void (*write_marker_byte) (j12_compress_ptr cinfo, int val); +}; + + +/* Declarations for decompression modules */ + +/* Master control module */ +struct jpeg12_decomp_master { + void (*prepare_for_output_pass) (j12_decompress_ptr cinfo); + void (*finish_output_pass) (j12_decompress_ptr cinfo); + + /* State variables made visible to other modules */ + boolean is_dummy_pass; /* True during 1st pass for 2-pass quant */ + + /* Partial decompression variables */ + JDIMENSION first_iMCU_col; + JDIMENSION last_iMCU_col; + JDIMENSION first_MCU_col[MAX_COMPONENTS]; + JDIMENSION last_MCU_col[MAX_COMPONENTS]; + boolean jinit_upsampler_no_alloc; + + /* Last iMCU row that was successfully decoded */ + JDIMENSION last_good_iMCU_row; +}; + +/* Input control module */ +struct jpeg12_input_controller { + int (*consume_input) (j12_decompress_ptr cinfo); + void (*reset_input_controller) (j12_decompress_ptr cinfo); + void (*start_input_pass) (j12_decompress_ptr cinfo); + void (*finish_input_pass) (j12_decompress_ptr cinfo); + + /* State variables made visible to other modules */ + boolean has_multiple_scans; /* True if file has multiple scans */ + boolean eoi_reached; /* True when EOI has been consumed */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg12_d_main_controller { + void (*start_pass) (j12_decompress_ptr cinfo, J_BUF_MODE pass_mode); + void (*process_data) (j12_decompress_ptr cinfo, J12SAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail); +}; + +/* Coefficient buffer control */ +struct jpeg12_d_coef_controller { + void (*start_input_pass) (j12_decompress_ptr cinfo); + int (*consume_data) (j12_decompress_ptr cinfo); + void (*start_output_pass) (j12_decompress_ptr cinfo); + int (*decompress_data) (j12_decompress_ptr cinfo, J12SAMPIMAGE output_buf); + /* Pointer to array of coefficient virtual arrays, or NULL if none */ + jvirt_barray_ptr *coef_arrays; +}; + +/* Decompression postprocessing (color quantization buffer control) */ +struct jpeg12_d_post_controller { + void (*start_pass) (j12_decompress_ptr cinfo, J_BUF_MODE pass_mode); + void (*post_process_data) (j12_decompress_ptr cinfo, J12SAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + J12SAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail); +}; + +/* Marker reading & parsing */ +struct jpeg12_marker_reader { + void (*reset_marker_reader) (j12_decompress_ptr cinfo); + /* Read markers until SOS or EOI. + * Returns same codes as are defined for jpeg12_consume_input: + * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + */ + int (*read_markers) (j12_decompress_ptr cinfo); + /* Read a restart marker --- exported for use by entropy decoder only */ + jpeg12_marker_parser_method read_restart_marker; + + /* State of marker reader --- nominally internal, but applications + * supplying COM or APPn handlers might like to know the state. + */ + boolean saw_SOI; /* found SOI? */ + boolean saw_SOF; /* found SOF? */ + int next_restart_num; /* next restart number expected (0-7) */ + unsigned int discarded_bytes; /* # of bytes skipped looking for a marker */ +}; + +/* Entropy decoding */ +struct jpeg12_entropy_decoder { + void (*start_pass) (j12_decompress_ptr cinfo); + boolean (*decode_mcu) (j12_decompress_ptr cinfo, JBLOCKROW *MCU_data); + + /* This is here to share code between baseline and progressive decoders; */ + /* other modules probably should not use it */ + boolean insufficient_data; /* set TRUE after emitting warning */ +}; + +/* Inverse DCT (also performs dequantization) */ +typedef void (*inverse_DCT_12_method_ptr) (j12_decompress_ptr cinfo, + jpeg_component_info *compptr, + JCOEFPTR coef_block, + J12SAMPARRAY output_buf, + JDIMENSION output_col); + +struct jpeg12_inverse_dct { + void (*start_pass) (j12_decompress_ptr cinfo); + /* It is useful to allow each component to have a separate IDCT method. */ + inverse_DCT_12_method_ptr inverse_DCT[MAX_COMPONENTS]; +}; + +/* Upsampling (note that upsampler must also call color converter) */ +struct jpeg12_upsampler { + void (*start_pass) (j12_decompress_ptr cinfo); + void (*upsample) (j12_decompress_ptr cinfo, J12SAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, J12SAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Colorspace conversion */ +struct jpeg12_color_deconverter { + void (*start_pass) (j12_decompress_ptr cinfo); + void (*color_convert) (j12_decompress_ptr cinfo, J12SAMPIMAGE input_buf, + JDIMENSION input_row, J12SAMPARRAY output_buf, + int num_rows); +}; + +/* Color quantization or color precision reduction */ +struct jpeg12_color_quantizer { + void (*start_pass) (j12_decompress_ptr cinfo, boolean is_pre_scan); + void (*color_quantize) (j12_decompress_ptr cinfo, J12SAMPARRAY input_buf, + J12SAMPARRAY output_buf, int num_rows); + void (*finish_pass) (j12_decompress_ptr cinfo); + void (*new_color_map) (j12_decompress_ptr cinfo); +}; + + +/* Miscellaneous useful macros */ + +#undef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#undef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + + +/* We assume that right shift corresponds to signed division by 2 with + * rounding towards minus infinity. This is correct for typical "arithmetic + * shift" instructions that shift in copies of the sign bit. But some + * C compilers implement >> with an unsigned shift. For these machines you + * must define RIGHT_SHIFT_IS_UNSIGNED. + * RIGHT_SHIFT provides a proper signed right shift of a JLONG quantity. + * It is only applied with constant shift counts. SHIFT_TEMPS must be + * included in the variables of any routine using RIGHT_SHIFT. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define SHIFT_TEMPS JLONG shift_temp; +#define RIGHT_SHIFT(x, shft) \ + ((shift_temp = (x)) < 0 ? \ + (shift_temp >> (shft)) | ((~((JLONG)0)) << (32 - (shft))) : \ + (shift_temp >> (shft))) +#else +#define SHIFT_TEMPS +#define RIGHT_SHIFT(x, shft) ((x) >> (shft)) +#endif + + +/* Compression module initialization routines */ +EXTERN(void) j12init_compress_master(j12_compress_ptr cinfo); +EXTERN(void) j12init_c_master_control(j12_compress_ptr cinfo, + boolean transcode_only); +EXTERN(void) j12init_c_main_controller(j12_compress_ptr cinfo, + boolean need_full_buffer); +EXTERN(void) j12init_c_prep_controller(j12_compress_ptr cinfo, + boolean need_full_buffer); +EXTERN(void) j12init_c_coef_controller(j12_compress_ptr cinfo, + boolean need_full_buffer); +EXTERN(void) j12init_color_converter(j12_compress_ptr cinfo); +EXTERN(void) j12init_downsampler(j12_compress_ptr cinfo); +EXTERN(void) j12init_forward_dct(j12_compress_ptr cinfo); +EXTERN(void) j12init_huff_encoder(j12_compress_ptr cinfo); +EXTERN(void) j12init_phuff_encoder(j12_compress_ptr cinfo); +EXTERN(void) j12init_arith_encoder(j12_compress_ptr cinfo); +EXTERN(void) j12init_marker_writer(j12_compress_ptr cinfo); +/* Decompression module initialization routines */ +EXTERN(void) j12init_master_decompress(j12_decompress_ptr cinfo); +EXTERN(void) j12init_d_main_controller(j12_decompress_ptr cinfo, + boolean need_full_buffer); +EXTERN(void) j12init_d_coef_controller(j12_decompress_ptr cinfo, + boolean need_full_buffer); +EXTERN(void) j12init_d_post_controller(j12_decompress_ptr cinfo, + boolean need_full_buffer); +EXTERN(void) j12init_input_controller(j12_decompress_ptr cinfo); +EXTERN(void) j12init_marker_reader(j12_decompress_ptr cinfo); +EXTERN(void) j12init_huff_decoder(j12_decompress_ptr cinfo); +EXTERN(void) j12init_phuff_decoder(j12_decompress_ptr cinfo); +EXTERN(void) j12init_arith_decoder(j12_decompress_ptr cinfo); +EXTERN(void) j12init_inverse_dct(j12_decompress_ptr cinfo); +EXTERN(void) j12init_upsampler(j12_decompress_ptr cinfo); +EXTERN(void) j12init_color_deconverter(j12_decompress_ptr cinfo); +EXTERN(void) j12init_1pass_quantizer(j12_decompress_ptr cinfo); +EXTERN(void) j12init_2pass_quantizer(j12_decompress_ptr cinfo); +EXTERN(void) j12init_merged_upsampler(j12_decompress_ptr cinfo); +/* Memory manager initialization */ +EXTERN(void) j12init_memory_mgr(j12_common_ptr cinfo); + +#ifndef JPEGINT_H +/* Utility routines in jutils.c */ +EXTERN(long) j12div_round_up(long a, long b); +EXTERN(long) j12round_up(long a, long b); +#endif +EXTERN(void) j12copy_sample_rows(J12SAMPARRAY input_array, int source_row, + J12SAMPARRAY output_array, int dest_row, + int num_rows, JDIMENSION num_cols); +#ifndef JPEGINT_H +EXTERN(void) j12copy_block_row(JBLOCKROW input_row, JBLOCKROW output_row, + JDIMENSION num_blocks); +EXTERN(void) j12zero_far(void *target, size_t bytestozero); +/* Constant tables in jutils.c */ +#if 0 /* This table is not actually needed in v6a */ +extern const int jpeg_zigzag_order[]; /* natural coef order to zigzag order */ +#endif +extern const int jpeg12_natural_order[]; /* zigzag coef order to natural + order */ + +/* Arithmetic coding probability estimation tables in jaricom.c */ +extern const JLONG jpeg_aritab[]; +#endif + +#endif /* JPEG12INT_H */ diff --git a/jpeg12lib.h b/jpeg12lib.h new file mode 100644 index 000000000..c3a55def7 --- /dev/null +++ b/jpeg12lib.h @@ -0,0 +1,1153 @@ +/* + * jpeg12lib.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1991-1998, Thomas G. Lane. + * Modified 2002-2009 by Guido Vollbeding. + * libjpeg-turbo Modifications: + * Copyright (C) 2009-2011, 2013-2014, 2016-2017, 2020, 2022, D. R. Commander. + * Copyright (C) 2015, Google, Inc. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file defines the application interface for the JPEG library. + * Most applications using the library need only include this file, + * and perhaps jerror.h if they want to know the exact error codes. + */ + +#ifndef JPEG12LIB_H +#define JPEG12LIB_H + +/* + * First we include the configuration files that record how this + * installation of the JPEG library is set up. jconfig.h can be + * generated automatically for many systems. jmorecfg.h contains + * manual configuration options that most people need not worry about. + */ + +#ifndef JCONFIG_INCLUDED /* in case jinclude.h already did */ +#include "jconfig.h" /* widely used configuration options */ +#endif +#include "jmorecfg.h" /* seldom changed options */ + + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +extern "C" { +#endif +#endif + + +/* Various constants determining the sizes of things. + * All of these are specified by the JPEG standard, so don't change them + * if you want to be compatible. + */ + +#define DCTSIZE 8 /* The basic DCT block is 8x8 samples */ +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ +#define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ +#define NUM_HUFF_TBLS 4 /* Huffman tables are numbered 0..3 */ +#define NUM_ARITH_TBLS 16 /* Arith-coding tables are numbered 0..15 */ +#define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ +#define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ +/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; + * the PostScript DCT filter can emit files with many more than 10 blocks/MCU. + * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU + * to handle it. We even let you do this from the jconfig.h file. However, + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe + * sometimes emits noncompliant files doesn't mean you should too. + */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#endif + + +/* Data structures for images (arrays of samples and of DCT coefficients). + */ + +typedef J12SAMPLE *J12SAMPROW; /* ptr to one image row of pixel + samples. */ +typedef J12SAMPROW *J12SAMPARRAY; /* ptr to some rows (a 2-D sample array) */ +typedef J12SAMPARRAY *J12SAMPIMAGE; /* a 3-D sample array: top index is + color */ + +#ifndef JPEGLIB_H + +typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ +typedef JBLOCK *JBLOCKROW; /* pointer to one row of coefficient blocks */ +typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ +typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ + +typedef JCOEF *JCOEFPTR; /* useful in a couple of places */ + + +/* Types for JPEG compression parameters and working tables. */ + + +/* DCT coefficient quantization tables. */ + +typedef struct { + /* This array gives the coefficient quantizers in natural array order + * (not the zigzag order in which they are stored in a JPEG DQT marker). + * CAUTION: IJG versions prior to v6a kept this array in zigzag order. + */ + UINT16 quantval[DCTSIZE2]; /* quantization step for each coefficient */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg12_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JQUANT_TBL; + + +/* Huffman coding tables. */ + +typedef struct { + /* These two fields directly represent the contents of a JPEG DHT marker */ + UINT8 bits[17]; /* bits[k] = # of symbols with codes of */ + /* length k bits; bits[0] is unused */ + UINT8 huffval[256]; /* The symbols, in order of incr code length */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg12_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JHUFF_TBL; + + +/* Basic info about one component (color channel). */ + +typedef struct { + /* These values are fixed over the whole image. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOF marker. */ + int component_id; /* identifier for this component (0..255) */ + int component_index; /* its index in SOF or cinfo->comp_info[] */ + int h_samp_factor; /* horizontal sampling factor (1..4) */ + int v_samp_factor; /* vertical sampling factor (1..4) */ + int quant_tbl_no; /* quantization table selector (0..3) */ + /* These values may vary between scans. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOS marker. */ + /* The decompressor output side may not use these variables. */ + int dc_tbl_no; /* DC entropy table selector (0..3) */ + int ac_tbl_no; /* AC entropy table selector (0..3) */ + + /* Remaining fields should be treated as private by applications. */ + + /* These values are computed during compression or decompression startup: */ + /* Component's size in DCT blocks. + * Any dummy blocks added to complete an MCU are not counted; therefore + * these values do not depend on whether a scan is interleaved or not. + */ + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a DCT block in samples. Always DCTSIZE for compression. + * For decompression this is the size of the output from one DCT block, + * reflecting any scaling we choose to apply during the IDCT step. + * Values from 1 to 16 are supported. + * Note that different components may receive different IDCT scalings. + */ +#if JPEG_LIB_VERSION >= 70 + int DCT_h_scaled_size; + int DCT_v_scaled_size; +#else + int DCT_scaled_size; +#endif + /* 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_[h_]scaled_size/DCTSIZE) + */ + JDIMENSION downsampled_width; /* actual width in samples */ + JDIMENSION downsampled_height; /* actual height in samples */ + /* This flag is used only for decompression. In cases where some of the + * components will be ignored (eg grayscale output from YCbCr image), + * we can skip most computations for the unused components. + */ + boolean component_needed; /* do we need the value of this component? */ + + /* These values are computed before starting a scan of the component. */ + /* The decompressor output side may not use these variables. */ + int MCU_width; /* number of blocks per MCU, horizontally */ + int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples, MCU_width*DCT_[h_]scaled_size */ + int last_col_width; /* # of non-dummy blocks across in last MCU */ + int last_row_height; /* # of non-dummy blocks down in last MCU */ + + /* Saved quantization table for component; NULL if none yet saved. + * See jdinput.c comments about the need for this information. + * This field is currently used only for decompression. + */ + JQUANT_TBL *quant_table; + + /* Private per-component storage for DCT or IDCT subsystem. */ + void *dct_table; +} jpeg_component_info; + + +/* The script for encoding a multiple-scan file is an array of these: */ + +typedef struct { + int comps_in_scan; /* number of components encoded in this scan */ + int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ + int Ss, Se; /* progressive JPEG spectral selection parms */ + int Ah, Al; /* progressive JPEG successive approx. parms */ +} jpeg_scan_info; + +/* The decompressor can save APPn and COM markers in a list of these: */ + +typedef struct jpeg_marker_struct *jpeg_saved_marker_ptr; + +struct jpeg_marker_struct { + jpeg_saved_marker_ptr next; /* next in list, or NULL */ + UINT8 marker; /* marker code: JPEG_COM, or JPEG_APP0+n */ + unsigned int original_length; /* # bytes of data in the file */ + unsigned int data_length; /* # bytes of data saved at data[] */ + JOCTET *data; /* the data contained in the marker */ + /* the marker length word is not counted in data_length or original_length */ +}; + +/* Known color spaces. */ + +#define JCS_EXTENSIONS 1 +#define JCS_ALPHA_EXTENSIONS 1 + +typedef enum { + JCS_UNKNOWN, /* error/unspecified */ + JCS_GRAYSCALE, /* monochrome */ + JCS_RGB, /* red/green/blue as specified by the RGB_RED, + RGB_GREEN, RGB_BLUE, and RGB_PIXELSIZE macros */ + JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */ + JCS_CMYK, /* C/M/Y/K */ + JCS_YCCK, /* Y/Cb/Cr/K */ + JCS_EXT_RGB, /* red/green/blue */ + JCS_EXT_RGBX, /* red/green/blue/x */ + JCS_EXT_BGR, /* blue/green/red */ + JCS_EXT_BGRX, /* blue/green/red/x */ + JCS_EXT_XBGR, /* x/blue/green/red */ + JCS_EXT_XRGB, /* x/red/green/blue */ + /* When out_color_space it set to JCS_EXT_RGBX, JCS_EXT_BGRX, JCS_EXT_XBGR, + or JCS_EXT_XRGB during decompression, the X byte is undefined, and in + order to ensure the best performance, libjpeg-turbo can set that byte to + whatever value it wishes. Use the following colorspace constants to + ensure that the X byte is set to 0xFF, so that it can be interpreted as an + opaque alpha channel. */ + JCS_EXT_RGBA, /* red/green/blue/alpha */ + JCS_EXT_BGRA, /* blue/green/red/alpha */ + JCS_EXT_ABGR, /* alpha/blue/green/red */ + JCS_EXT_ARGB, /* alpha/red/green/blue */ + JCS_RGB565 /* 5-bit red/6-bit green/5-bit blue */ +} J_COLOR_SPACE; + +/* DCT/IDCT algorithm options. */ + +typedef enum { + JDCT_ISLOW, /* accurate integer method */ + JDCT_IFAST, /* less accurate integer method [legacy feature] */ + JDCT_FLOAT /* floating-point method [legacy feature] */ +} J_DCT_METHOD; + +#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */ +#define JDCT_DEFAULT JDCT_ISLOW +#endif +#ifndef JDCT_FASTEST /* may be overridden in jconfig.h */ +#define JDCT_FASTEST JDCT_IFAST +#endif + +/* Dithering options for decompression. */ + +typedef enum { + JDITHER_NONE, /* no dithering */ + JDITHER_ORDERED, /* simple ordered dither */ + JDITHER_FS /* Floyd-Steinberg error diffusion dither */ +} J_DITHER_MODE; + +#endif /* JPEGLIB_H */ + +/* Common fields between JPEG compression and decompression master structs. */ + +#define jpeg12_common_fields \ + struct jpeg12_error_mgr *err; /* Error handler module */ \ + struct jpeg12_memory_mgr *mem; /* Memory manager module */ \ + struct jpeg12_progress_mgr *progress; /* Progress monitor, or NULL if \ + none */ \ + void *client_data; /* Available for use by application */ \ + boolean is_decompressor; /* So common code can tell which is which */ \ + int global_state /* For checking call sequence validity */ + +/* Routines that are to be used by both halves of the library are declared + * to receive a pointer to this structure. There are no actual instances of + * jpeg12_common_struct, only of jpeg12_compress_struct and + * jpeg12_decompress_struct. + */ +struct jpeg12_common_struct { + jpeg12_common_fields; /* Fields common to both master struct types */ + /* Additional fields follow in an actual jpeg12_compress_struct or + * jpeg12_decompress_struct. All three structs must agree on these + * initial fields! (This would be a lot cleaner in C++.) + */ +}; + +typedef struct jpeg12_common_struct *j12_common_ptr; +typedef struct jpeg12_compress_struct *j12_compress_ptr; +typedef struct jpeg12_decompress_struct *j12_decompress_ptr; + + +/* Master record for a compression instance */ + +struct jpeg12_compress_struct { + jpeg12_common_fields; /* Fields shared with jpeg12_decompress_struct */ + + /* Destination for compressed data */ + struct jpeg12_destination_mgr *dest; + + /* Description of source image --- these fields must be filled in by + * outer application before starting compression. in_color_space must + * be correct before you can even call jpeg12_set_defaults(). + */ + + JDIMENSION image_width; /* input image width */ + JDIMENSION image_height; /* input image height */ + int input_components; /* # of color components in input image */ + J_COLOR_SPACE in_color_space; /* colorspace of input image */ + + double input_gamma; /* image gamma of input image */ + + /* Compression parameters --- these fields must be set before calling + * jpeg12_start_compress(). We recommend calling jpeg12_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. + */ + +#if JPEG_LIB_VERSION >= 70 + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + JDIMENSION jpeg_width; /* scaled JPEG image width */ + JDIMENSION jpeg_height; /* scaled JPEG image height */ + /* Dimensions of actual JPEG image that will be written to file, + * derived from input dimensions by scaling factors above. + * These fields are computed by jpeg12_start_compress(). + * You can also use jpeg12_calc_jpeg_dimensions() to determine these values + * in advance of calling jpeg12_start_compress(). + */ +#endif + + int data_precision; /* bits of precision in image data */ + + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + jpeg_component_info *comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + JQUANT_TBL *quant_tbl_ptrs[NUM_QUANT_TBLS]; +#if JPEG_LIB_VERSION >= 70 + int q_scale_factor[NUM_QUANT_TBLS]; +#endif + /* ptrs to coefficient quantization tables, or NULL if not defined, + * and corresponding scale factors (percentage, initialized 100). + */ + + JHUFF_TBL *dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL *ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + int num_scans; /* # of entries in scan_info array */ + const jpeg_scan_info *scan_info; /* script for multi-scan file, or NULL */ + /* The default value of scan_info is NULL, which causes a single-scan + * sequential JPEG file to be emitted. To create a multi-scan file, + * set num_scans and scan_info to point to an array of scan definitions. + */ + + boolean raw_data_in; /* TRUE=caller supplies downsampled data */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + boolean optimize_coding; /* TRUE=optimize entropy encoding parms */ + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ +#if JPEG_LIB_VERSION >= 70 + boolean do_fancy_downsampling; /* TRUE=apply fancy downsampling */ +#endif + int smoothing_factor; /* 1..100, or 0 for no input smoothing */ + J_DCT_METHOD dct_method; /* DCT algorithm selector */ + + /* The restart interval can be specified in absolute MCUs by setting + * restart_interval, or in MCU rows by setting restart_in_rows + * (in which case the correct restart_interval will be figured + * for each scan). + */ + unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */ + int restart_in_rows; /* if > 0, MCU rows per restart interval */ + + /* Parameters controlling emission of special markers. */ + + boolean write_JFIF_header; /* should a JFIF marker be written? */ + UINT8 JFIF_major_version; /* What to write for the JFIF version number */ + UINT8 JFIF_minor_version; + /* These three values are not used by the JPEG code, merely copied */ + /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ + /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ + /* ratio is defined by X_density/Y_density even when density_unit=0. */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean write_Adobe_marker; /* should an Adobe marker be written? */ + + /* State variable: index of next scanline to be written to + * jpeg12_write_scanlines(). Application may use this to control its + * processing loop, e.g., "while (next_scanline < image_height)". + */ + + JDIMENSION next_scanline; /* 0 .. image_height-1 */ + + /* Remaining fields are known throughout compressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during compression startup + */ + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + +#if JPEG_LIB_VERSION >= 70 + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ +#endif + + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ + /* The coefficient controller receives data in units of MCU rows as defined + * for fully interleaved scans (whether the JPEG file is interleaved or not). + * There are v_samp_factor * DCTSIZE sample rows of each component in an + * "iMCU" (interleaved MCU) row. + */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info *cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + +#if JPEG_LIB_VERSION >= 80 + int block_size; /* the basic DCT block size: 1..16 */ + const int *natural_order; /* natural-order position array */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) */ +#endif + + /* + * Links to compression subobjects (methods and private variables of modules) + */ + struct jpeg12_comp_master *master; + struct jpeg12_c_main_controller *main; + struct jpeg12_c_prep_controller *prep; + struct jpeg12_c_coef_controller *coef; + struct jpeg12_marker_writer *marker; + struct jpeg12_color_converter *cconvert; + struct jpeg12_downsampler *downsample; + struct jpeg12_forward_dct *fdct; + struct jpeg12_entropy_encoder *entropy; + jpeg_scan_info *script_space; /* workspace for jpeg12_simple_progression */ + int script_space_size; +}; + + +/* Master record for a decompression instance */ + +struct jpeg12_decompress_struct { + jpeg12_common_fields; /* Fields shared with jpeg12_compress_struct */ + + /* Source of compressed data */ + struct jpeg12_source_mgr *src; + + /* Basic description of image --- filled in by jpeg12_read_header(). */ + /* Application may inspect these values to decide how to process image. */ + + JDIMENSION image_width; /* nominal image width (from SOF marker) */ + JDIMENSION image_height; /* nominal image height */ + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + /* Decompression processing parameters --- these fields must be set before + * calling jpeg12_start_decompress(). Note that jpeg12_read_header() + * initializes them to default values. + */ + + J_COLOR_SPACE out_color_space; /* colorspace for output */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + double output_gamma; /* image gamma wanted in output */ + + boolean buffered_image; /* TRUE=multiple output passes */ + boolean raw_data_out; /* TRUE=downsampled data wanted */ + + J_DCT_METHOD dct_method; /* IDCT algorithm selector */ + boolean do_fancy_upsampling; /* TRUE=apply fancy upsampling */ + boolean do_block_smoothing; /* TRUE=apply interblock smoothing */ + + boolean quantize_colors; /* TRUE=colormapped output wanted */ + /* the following are ignored if not quantize_colors: */ + J_DITHER_MODE dither_mode; /* type of color dithering to use */ + boolean two_pass_quantize; /* TRUE=use two-pass color quantization */ + int desired_number_of_colors; /* max # colors to use in created colormap */ + /* these are significant only in buffered-image mode: */ + boolean enable_1pass_quant; /* enable future use of 1-pass quantizer */ + boolean enable_external_quant;/* enable future use of external colormap */ + boolean enable_2pass_quant; /* enable future use of 2-pass quantizer */ + + /* Description of actual output image that will be returned to application. + * These fields are computed by jpeg12_start_decompress(). + * You can also use jpeg12_calc_output_dimensions() to determine these values + * in advance of calling jpeg12_start_decompress(). + */ + + JDIMENSION output_width; /* scaled image width */ + JDIMENSION output_height; /* scaled image height */ + int out_color_components; /* # of color components in out_color_space */ + int output_components; /* # of color components returned */ + /* output_components is 1 (a colormap index) when quantizing colors; + * otherwise it equals out_color_components. + */ + int rec_outbuf_height; /* min recommended height of scanline buffer */ + /* If the buffer passed to jpeg12_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 jpeg12_start_decompress; otherwise a colormap is created during + * jpeg12_start_decompress or jpeg12_start_output. + * The map has out_color_components rows and actual_number_of_colors columns. + */ + int actual_number_of_colors; /* number of entries in use */ + J12SAMPARRAY colormap; /* The color map as a 2-D pixel array */ + + /* State variables: these variables indicate the progress of decompression. + * The application may examine these but must not modify them. + */ + + /* Row index of next scanline to be read from jpeg12_read_scanlines(). + * Application may use this to control its processing loop, e.g., + * "while (output_scanline < output_height)". + */ + JDIMENSION output_scanline; /* 0 .. output_height-1 */ + + /* Current input scan number and number of iMCU rows completed in scan. + * These indicate the progress of the decompressor input side. + */ + int input_scan_number; /* Number of SOS markers seen so far */ + JDIMENSION input_iMCU_row; /* Number of iMCU rows completed */ + + /* The "output scan number" is the notional scan being displayed by the + * output side. The decompressor will not allow output scan/row number + * to get ahead of input scan/row, but it can fall arbitrarily far behind. + */ + int output_scan_number; /* Nominal scan number being displayed */ + JDIMENSION output_iMCU_row; /* Number of iMCU rows read */ + + /* Current progression status. coef_bits[c][i] indicates the precision + * with which component c's DCT coefficient i (in zigzag order) is known. + * It is -1 when no data has yet been received, otherwise it is the point + * transform (shift) value for the most recent scan of the coefficient + * (thus, 0 at completion of the progression). + * This pointer is NULL when reading a non-progressive file. + */ + int (*coef_bits)[DCTSIZE2]; /* -1 or current Al value for each coef */ + + /* Internal JPEG parameters --- the application usually need not look at + * these fields. Note that the decompressor output side may not use + * any parameters that can change between scans. + */ + + /* Quantization and Huffman tables are carried forward across input + * datastreams when processing abbreviated JPEG datastreams. + */ + + JQUANT_TBL *quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL *dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL *ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + /* These parameters are never carried across datastreams, since they + * are given in SOF/SOS markers or defined to be reset by SOI. + */ + + int data_precision; /* bits of precision in image data */ + + jpeg_component_info *comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + +#if JPEG_LIB_VERSION >= 80 + boolean is_baseline; /* TRUE if Baseline SOF0 encountered */ +#endif + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */ + + /* These fields record data obtained from optional markers recognized by + * the JPEG library. + */ + boolean saw_JFIF_marker; /* TRUE iff a JFIF APP0 marker was found */ + /* Data copied from JFIF marker; only valid if saw_JFIF_marker is TRUE: */ + UINT8 JFIF_major_version; /* JFIF version number */ + UINT8 JFIF_minor_version; + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean saw_Adobe_marker; /* TRUE iff an Adobe APP14 marker was found */ + UINT8 Adobe_transform; /* Color transform code from Adobe marker */ + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + /* Aside from the specific data retained from APPn markers known to the + * library, the uninterpreted contents of any or all APPn and COM markers + * can be saved in a list for examination by the application. + */ + jpeg_saved_marker_ptr marker_list; /* Head of list of saved markers */ + + /* Remaining fields are known throughout decompressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during decompression startup + */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + +#if JPEG_LIB_VERSION >= 70 + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ +#else + int min_DCT_scaled_size; /* smallest DCT_scaled_size of any component */ +#endif + + JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ + /* The coefficient controller's input and output progress is measured in + * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows + * in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. We define an iMCU row as v_samp_factor DCT block + * rows of each component. Therefore, the IDCT output contains + * v_samp_factor*DCT_[v_]scaled_size sample rows of a component per iMCU row. + */ + + J12SAMPLE *sample_range_limit; /* table for fast range-limiting */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + * Note that the decompressor output side must not use these fields. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info *cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + +#if JPEG_LIB_VERSION >= 80 + /* These fields are derived from Se of first SOS marker. + */ + int block_size; /* the basic DCT block size: 1..16 */ + const int *natural_order; /* natural-order position array for entropy decode */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) for entropy decode */ +#endif + + /* This field is shared between entropy decoder and marker parser. + * It is either zero or the code of a JPEG marker that has been + * read from the data source, but has not yet been processed. + */ + int unread_marker; + + /* + * Links to decompression subobjects (methods, private variables of modules) + */ + struct jpeg12_decomp_master *master; + struct jpeg12_d_main_controller *main; + struct jpeg12_d_coef_controller *coef; + struct jpeg12_d_post_controller *post; + struct jpeg12_input_controller *inputctl; + struct jpeg12_marker_reader *marker; + struct jpeg12_entropy_decoder *entropy; + struct jpeg12_inverse_dct *idct; + struct jpeg12_upsampler *upsample; + struct jpeg12_color_deconverter *cconvert; + struct jpeg12_color_quantizer *cquantize; +}; + + +/* "Object" declarations for JPEG modules that may be supplied or called + * directly by the surrounding application. + * As with all objects in the JPEG library, these structs only define the + * publicly visible methods and state variables of a module. Additional + * private fields may exist after the public ones. + */ + + +/* Error handler object */ + +struct jpeg12_error_mgr { + /* Error exit handler: does not return to caller */ + void (*error_exit) (j12_common_ptr cinfo); + /* Conditionally emit a trace or warning message */ + void (*emit_message) (j12_common_ptr cinfo, int msg_level); + /* Routine that actually outputs a trace or error message */ + void (*output_message) (j12_common_ptr cinfo); + /* Format a message string for the most recent JPEG error or message */ + void (*format_message) (j12_common_ptr cinfo, char *buffer); +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ + /* Reset error state variables at start of a new image */ + void (*reset_error_mgr) (j12_common_ptr cinfo); + + /* The message ID code and any parameters are saved here. + * A message can have one string parameter or up to 8 int parameters. + */ + int msg_code; +#define JMSG_STR_PARM_MAX 80 + union { + int i[8]; + char s[JMSG_STR_PARM_MAX]; + } msg_parm; + + /* Standard state variables for error facility */ + + int trace_level; /* max msg_level that will be displayed */ + + /* For recoverable corrupt-data errors, we emit a warning message, + * but keep going unless emit_message chooses to abort. emit_message + * should count warnings in num_warnings. The surrounding application + * can check for bad data by seeing if num_warnings is nonzero at the + * end of processing. + */ + long num_warnings; /* number of corrupt-data warnings */ + + /* These fields point to the table(s) of error message strings. + * An application can change the table pointer to switch to a different + * message list (typically, to change the language in which errors are + * reported). Some applications may wish to add additional error codes + * that will be handled by the JPEG library error mechanism; the second + * table pointer is used for this purpose. + * + * First table includes all errors generated by JPEG library itself. + * Error code 0 is reserved for a "no such error string" message. + */ + const char * const *jpeg_message_table; /* Library errors */ + int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ + /* Second table can be added by application (see cjpeg/djpeg for example). + * It contains strings numbered first_addon_message..last_addon_message. + */ + const char * const *addon_message_table; /* Non-library errors */ + int first_addon_message; /* code for first string in addon table */ + int last_addon_message; /* code for last string in addon table */ +}; + + +/* Progress monitor object */ + +struct jpeg12_progress_mgr { + void (*progress_monitor) (j12_common_ptr cinfo); + + long pass_counter; /* work units completed in this pass */ + long pass_limit; /* total number of work units in this pass */ + int completed_passes; /* passes completed so far */ + int total_passes; /* total number of passes expected */ +}; + + +/* Data destination object for compression */ + +struct jpeg12_destination_mgr { + JOCTET *next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + void (*init_destination) (j12_compress_ptr cinfo); + boolean (*empty_output_buffer) (j12_compress_ptr cinfo); + void (*term_destination) (j12_compress_ptr cinfo); +}; + + +/* Data source object for decompression */ + +struct jpeg12_source_mgr { + const JOCTET *next_input_byte; /* => next byte to read from buffer */ + size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + + void (*init_source) (j12_decompress_ptr cinfo); + boolean (*fill_input_buffer) (j12_decompress_ptr cinfo); + void (*skip_input_data) (j12_decompress_ptr cinfo, long num_bytes); + boolean (*resync_to_restart) (j12_decompress_ptr cinfo, int desired); + void (*term_source) (j12_decompress_ptr cinfo); +}; + + +/* Memory manager object. + * Allocates "small" objects (a few K total), "large" objects (tens of K), + * and "really big" objects (virtual arrays with backing store if needed). + * The memory manager does not allow individual objects to be freed; rather, + * each created object is assigned to a pool, and whole pools can be freed + * at once. This is faster and more convenient than remembering exactly what + * to free, especially where malloc()/free() are not too speedy. + * NB: alloc routines never return NULL. They exit to error_exit if not + * successful. + */ + +#ifndef JPEGLIB_H + +#define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ +#define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ +#define JPOOL_NUMPOOLS 2 + +typedef struct jvirt_sarray_control *jvirt_sarray_ptr; +typedef struct jvirt_barray_control *jvirt_barray_ptr; + +#endif + + +struct jpeg12_memory_mgr { + /* Method pointers */ + void *(*alloc_small) (j12_common_ptr cinfo, int pool_id, + size_t sizeofobject); + void *(*alloc_large) (j12_common_ptr cinfo, int pool_id, + size_t sizeofobject); + J12SAMPARRAY (*alloc_sarray) (j12_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, JDIMENSION numrows); + JBLOCKARRAY (*alloc_barray) (j12_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, JDIMENSION numrows); + jvirt_sarray_ptr (*request_virt_sarray) (j12_common_ptr cinfo, int pool_id, + boolean pre_zero, + JDIMENSION samplesperrow, + JDIMENSION numrows, + JDIMENSION maxaccess); + jvirt_barray_ptr (*request_virt_barray) (j12_common_ptr cinfo, int pool_id, + boolean pre_zero, + JDIMENSION blocksperrow, + JDIMENSION numrows, + JDIMENSION maxaccess); + void (*realize_virt_arrays) (j12_common_ptr cinfo); + J12SAMPARRAY (*access_virt_sarray) (j12_common_ptr cinfo, + jvirt_sarray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, boolean writable); + JBLOCKARRAY (*access_virt_barray) (j12_common_ptr cinfo, + jvirt_barray_ptr ptr, + JDIMENSION start_row, JDIMENSION num_rows, + boolean writable); + void (*free_pool) (j12_common_ptr cinfo, int pool_id); + void (*self_destruct) (j12_common_ptr cinfo); + + /* Limit on memory allocation for this JPEG object. (Note that this is + * merely advisory, not a guaranteed maximum; it only affects the space + * used for virtual-array buffers.) May be changed by outer application + * after creating the JPEG object. + */ + long max_memory_to_use; + + /* Maximum allocation request accepted by alloc_large. */ + long max_alloc_chunk; +}; + + +/* Routine signature for application-supplied marker processing methods. + * Need not pass marker code since it is stored in cinfo->unread_marker. + */ +typedef boolean (*jpeg12_marker_parser_method) (j12_decompress_ptr cinfo); + + +/* Originally, this macro was used as a way of defining function prototypes + * for both modern compilers as well as older compilers that did not support + * prototype parameters. libjpeg-turbo has never supported these older, + * non-ANSI compilers, but the macro is still included because there is some + * software out there that uses it. + */ + +#define JPP(arglist) arglist + + +/* Default error-management setup */ +EXTERN(struct jpeg12_error_mgr *) + jpeg12_std_error(struct jpeg12_error_mgr *err); + +/* Initialization of JPEG compression objects. + * jpeg12_create_compress() and jpeg12_create_decompress() are the exported + * names that applications should call. These expand to calls on + * jpeg12_CreateCompress and jpeg12_CreateDecompress with additional + * information passed for version mismatch checking. + * NB: you must set up the error-manager BEFORE calling jpeg12_create_xxx. + */ +#define jpeg12_create_compress(cinfo) \ + jpeg12_CreateCompress((cinfo), JPEG_LIB_VERSION, \ + (size_t)sizeof(struct jpeg12_compress_struct)) +#define jpeg12_create_decompress(cinfo) \ + jpeg12_CreateDecompress((cinfo), JPEG_LIB_VERSION, \ + (size_t)sizeof(struct jpeg12_decompress_struct)) +EXTERN(void) jpeg12_CreateCompress(j12_compress_ptr cinfo, int version, + size_t structsize); +EXTERN(void) jpeg12_CreateDecompress(j12_decompress_ptr cinfo, int version, + size_t structsize); +/* Destruction of JPEG compression objects */ +EXTERN(void) jpeg12_destroy_compress(j12_compress_ptr cinfo); +EXTERN(void) jpeg12_destroy_decompress(j12_decompress_ptr cinfo); + +/* Standard data source and destination managers: stdio streams. */ +/* Caller is responsible for opening the file before and closing after. */ +EXTERN(void) jpeg12_stdio_dest(j12_compress_ptr cinfo, FILE *outfile); +EXTERN(void) jpeg12_stdio_src(j12_decompress_ptr cinfo, FILE *infile); + +#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) +/* Data source and destination managers: memory buffers. */ +EXTERN(void) jpeg12_mem_dest(j12_compress_ptr cinfo, unsigned char **outbuffer, + unsigned long *outsize); +EXTERN(void) jpeg12_mem_src(j12_decompress_ptr cinfo, + const unsigned char *inbuffer, + unsigned long insize); +#endif + +/* Default parameter setup for compression */ +EXTERN(void) jpeg12_set_defaults(j12_compress_ptr cinfo); +/* Compression parameter setup aids */ +EXTERN(void) jpeg12_set_colorspace(j12_compress_ptr cinfo, + J_COLOR_SPACE colorspace); +EXTERN(void) jpeg12_default_colorspace(j12_compress_ptr cinfo); +EXTERN(void) jpeg12_set_quality(j12_compress_ptr cinfo, int quality, + boolean force_baseline); +EXTERN(void) jpeg12_set_linear_quality(j12_compress_ptr cinfo, + int scale_factor, + boolean force_baseline); +#if JPEG_LIB_VERSION >= 70 +EXTERN(void) jpeg12_default_qtables(j12_compress_ptr cinfo, + boolean force_baseline); +#endif +EXTERN(void) jpeg12_add_quant_table(j12_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, boolean force_baseline); +EXTERN(int) jpeg12_quality_scaling(int quality); +EXTERN(void) jpeg12_simple_progression(j12_compress_ptr cinfo); +EXTERN(void) jpeg12_suppress_tables(j12_compress_ptr cinfo, boolean suppress); +EXTERN(JQUANT_TBL *) jpeg12_alloc_quant_table(j12_common_ptr cinfo); +EXTERN(JHUFF_TBL *) jpeg12_alloc_huff_table(j12_common_ptr cinfo); + +/* Main entry points for compression */ +EXTERN(void) jpeg12_start_compress(j12_compress_ptr cinfo, + boolean write_all_tables); +EXTERN(JDIMENSION) jpeg12_write_scanlines(j12_compress_ptr cinfo, + J12SAMPARRAY scanlines, + JDIMENSION num_lines); +EXTERN(void) jpeg12_finish_compress(j12_compress_ptr cinfo); + +#if JPEG_LIB_VERSION >= 70 +/* Precalculate JPEG dimensions for current compression parameters. */ +EXTERN(void) jpeg12_calc_jpeg_dimensions(j12_compress_ptr cinfo); +#endif + +/* Replaces jpeg12_write_scanlines when writing raw downsampled data. */ +EXTERN(JDIMENSION) jpeg12_write_raw_data(j12_compress_ptr cinfo, + J12SAMPIMAGE data, + JDIMENSION num_lines); + +/* Write a special marker. See libjpeg.txt concerning safe usage. */ +EXTERN(void) jpeg12_write_marker(j12_compress_ptr cinfo, int marker, + const JOCTET *dataptr, unsigned int datalen); +/* Same, but piecemeal. */ +EXTERN(void) jpeg12_write_m_header(j12_compress_ptr cinfo, int marker, + unsigned int datalen); +EXTERN(void) jpeg12_write_m_byte(j12_compress_ptr cinfo, int val); + +/* Alternate compression function: just write an abbreviated table file */ +EXTERN(void) jpeg12_write_tables(j12_compress_ptr cinfo); + +/* Write ICC profile. See libjpeg.txt for usage information. */ +EXTERN(void) jpeg12_write_icc_profile(j12_compress_ptr cinfo, + const JOCTET *icc_data_ptr, + unsigned int icc_data_len); + + +/* Decompression startup: read start of JPEG datastream to see what's there */ +EXTERN(int) jpeg12_read_header(j12_decompress_ptr cinfo, + boolean require_image); +/* Return value is one of: */ +#define JPEG_SUSPENDED 0 /* Suspended due to lack of input data */ +#define JPEG_HEADER_OK 1 /* Found valid image datastream */ +#define JPEG_HEADER_TABLES_ONLY 2 /* Found valid table-specs-only datastream */ +/* If you pass require_image = TRUE (normal case), you need not check for + * a TABLES_ONLY return code; an abbreviated file will cause an error exit. + * JPEG_SUSPENDED is only possible if you use a data source module that can + * give a suspension return (the stdio source module doesn't). + */ + +/* Main entry points for decompression */ +EXTERN(boolean) jpeg12_start_decompress(j12_decompress_ptr cinfo); +EXTERN(JDIMENSION) jpeg12_read_scanlines(j12_decompress_ptr cinfo, + J12SAMPARRAY scanlines, + JDIMENSION max_lines); +EXTERN(JDIMENSION) jpeg12_skip_scanlines(j12_decompress_ptr cinfo, + JDIMENSION num_lines); +EXTERN(void) jpeg12_crop_scanline(j12_decompress_ptr cinfo, + JDIMENSION *xoffset, JDIMENSION *width); +EXTERN(boolean) jpeg12_finish_decompress(j12_decompress_ptr cinfo); + +/* Replaces jpeg12_read_scanlines when reading raw downsampled data. */ +EXTERN(JDIMENSION) jpeg12_read_raw_data(j12_decompress_ptr cinfo, + J12SAMPIMAGE data, + JDIMENSION max_lines); + +/* Additional entry points for buffered-image mode. */ +EXTERN(boolean) jpeg12_has_multiple_scans(j12_decompress_ptr cinfo); +EXTERN(boolean) jpeg12_start_output(j12_decompress_ptr cinfo, int scan_number); +EXTERN(boolean) jpeg12_finish_output(j12_decompress_ptr cinfo); +EXTERN(boolean) jpeg12_input_complete(j12_decompress_ptr cinfo); +EXTERN(void) jpeg12_new_colormap(j12_decompress_ptr cinfo); +EXTERN(int) jpeg12_consume_input(j12_decompress_ptr cinfo); +/* Return value is one of: */ +/* #define JPEG_SUSPENDED 0 Suspended due to lack of input data */ +#define JPEG_REACHED_SOS 1 /* Reached start of new scan */ +#define JPEG_REACHED_EOI 2 /* Reached end of image */ +#define JPEG_ROW_COMPLETED 3 /* Completed one iMCU row */ +#define JPEG_SCAN_COMPLETED 4 /* Completed last iMCU row of a scan */ + +/* Precalculate output dimensions for current decompression parameters. */ +#if JPEG_LIB_VERSION >= 80 +EXTERN(void) jpeg12_core_output_dimensions(j12_decompress_ptr cinfo); +#endif +EXTERN(void) jpeg12_calc_output_dimensions(j12_decompress_ptr cinfo); + +/* Control saving of COM and APPn markers into marker_list. */ +EXTERN(void) jpeg12_save_markers(j12_decompress_ptr cinfo, int marker_code, + unsigned int length_limit); + +/* Install a special processing method for COM or APPn markers. */ +EXTERN(void) jpeg12_set_marker_processor(j12_decompress_ptr cinfo, + int marker_code, + jpeg12_marker_parser_method routine); + +/* Read or write raw DCT coefficients --- useful for lossless transcoding. */ +EXTERN(jvirt_barray_ptr *) jpeg12_read_coefficients(j12_decompress_ptr cinfo); +EXTERN(void) jpeg12_write_coefficients(j12_compress_ptr cinfo, + jvirt_barray_ptr *coef_arrays); +EXTERN(void) jpeg12_copy_critical_parameters(j12_decompress_ptr srcinfo, + j12_compress_ptr dstinfo); + +/* If you choose to abort compression or decompression before completing + * jpeg12_finish_(de)compress, then you need to clean up to release memory, + * temporary files, etc. You can just call jpeg12_destroy_(de)compress + * if you're done with the JPEG object, but if you want to clean it up and + * reuse it, call this: + */ +EXTERN(void) jpeg12_abort_compress(j12_compress_ptr cinfo); +EXTERN(void) jpeg12_abort_decompress(j12_decompress_ptr cinfo); + +/* Generic versions of jpeg12_abort and jpeg12_destroy that work on either + * flavor of JPEG object. These may be more convenient in some places. + */ +EXTERN(void) jpeg12_abort(j12_common_ptr cinfo); +EXTERN(void) jpeg12_destroy(j12_common_ptr cinfo); + +/* Default restart-marker-resync procedure for use by data source modules */ +EXTERN(boolean) jpeg12_resync_to_restart(j12_decompress_ptr cinfo, + int desired); + +/* Read ICC profile. See libjpeg.txt for usage information. */ +EXTERN(boolean) jpeg12_read_icc_profile(j12_decompress_ptr cinfo, + JOCTET **icc_data_ptr, + unsigned int *icc_data_len); + + +/* These marker codes are exported since applications and data source modules + * are likely to want to use them. + */ + +#define JPEG_RST0 0xD0 /* RST0 marker code */ +#define JPEG_EOI 0xD9 /* EOI marker code */ +#define JPEG_APP0 0xE0 /* APP0 marker code */ +#define JPEG_COM 0xFE /* COM marker code */ + + +/* If we have a brain-damaged compiler that emits warnings (or worse, errors) + * for structure definitions that are never filled in, keep it quiet by + * supplying dummy definitions for the various substructures. + */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef JPEG_INTERNALS /* will be defined in jpeg12int.h */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +struct jpeg12_comp_master { long dummy; }; +struct jpeg12_c_main_controller { long dummy; }; +struct jpeg12_c_prep_controller { long dummy; }; +struct jpeg12_c_coef_controller { long dummy; }; +struct jpeg12_marker_writer { long dummy; }; +struct jpeg12_color_converter { long dummy; }; +struct jpeg12_downsampler { long dummy; }; +struct jpeg12_forward_dct { long dummy; }; +struct jpeg12_entropy_encoder { long dummy; }; +struct jpeg12_decomp_master { long dummy; }; +struct jpeg12_d_main_controller { long dummy; }; +struct jpeg12_d_coef_controller { long dummy; }; +struct jpeg12_d_post_controller { long dummy; }; +struct jpeg12_input_controller { long dummy; }; +struct jpeg12_marker_reader { long dummy; }; +struct jpeg12_entropy_decoder { long dummy; }; +struct jpeg12_inverse_dct { long dummy; }; +struct jpeg12_upsampler { long dummy; }; +struct jpeg12_color_deconverter { long dummy; }; +struct jpeg12_color_quantizer { long dummy; }; +#endif /* JPEG_INTERNALS */ +#endif /* INCOMPLETE_TYPES_BROKEN */ + + +/* + * The JPEG library modules define JPEG_INTERNALS before including this file. + * The internal structure declarations are read only when that is true. + * Applications using the library should not include jpeg12int.h, but may wish + * to include jerror.h. + */ + +#ifdef JPEG_INTERNALS +#include "jpeg12int.h" /* fetch private declarations */ +#include "jerror.h" /* fetch error codes too */ +#endif + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +} +#endif +#endif + +#endif /* JPEG12LIB_H */ diff --git a/jpegint.h b/jpegint.h index 6af9e2a17..f781cbed7 100644 --- a/jpegint.h +++ b/jpegint.h @@ -5,7 +5,7 @@ * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 1997-2009 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2015-2016, 2019, 2021, D. R. Commander. + * Copyright (C) 2015-2016, 2019, 2021-2022, D. R. Commander. * Copyright (C) 2015, Google, Inc. * Copyright (C) 2021, Alex Richardson. * For conditions of distribution and use, see the accompanying README.ijg @@ -16,6 +16,11 @@ * applications using the library shouldn't need to include this file. */ +#ifndef JPEGINT_H +#define JPEGINT_H + + +#ifndef JPEG12INT_H /* Declarations for both compression & decompression */ @@ -68,6 +73,8 @@ typedef size_t JUINTPTR; #define LEFT_SHIFT(a, b) ((JLONG)((unsigned long)(a) << (b))) +#endif /* JPEG12INT_H */ + /* Declarations for compression modules */ @@ -356,12 +363,15 @@ EXTERN(void) jinit_merged_upsampler(j_decompress_ptr cinfo); /* Memory manager initialization */ EXTERN(void) jinit_memory_mgr(j_common_ptr cinfo); +#ifndef JPEG12INT_H /* Utility routines in jutils.c */ EXTERN(long) jdiv_round_up(long a, long b); EXTERN(long) jround_up(long a, long b); +#endif EXTERN(void) jcopy_sample_rows(JSAMPARRAY input_array, int source_row, JSAMPARRAY output_array, int dest_row, int num_rows, JDIMENSION num_cols); +#ifndef JPEG12INT_H EXTERN(void) jcopy_block_row(JBLOCKROW input_row, JBLOCKROW output_row, JDIMENSION num_blocks); EXTERN(void) jzero_far(void *target, size_t bytestozero); @@ -373,3 +383,6 @@ extern const int jpeg_natural_order[]; /* zigzag coef order to natural order */ /* Arithmetic coding probability estimation tables in jaricom.c */ extern const JLONG jpeg_aritab[]; +#endif + +#endif /* JPEGINT_H */ diff --git a/jpeglib.h b/jpeglib.h index d7664f063..1b8ce09d2 100644 --- a/jpeglib.h +++ b/jpeglib.h @@ -5,7 +5,7 @@ * Copyright (C) 1991-1998, Thomas G. Lane. * Modified 2002-2009 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2009-2011, 2013-2014, 2016-2017, 2020, D. R. Commander. + * Copyright (C) 2009-2011, 2013-2014, 2016-2017, 2020, 2022, D. R. Commander. * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -70,6 +70,8 @@ typedef JSAMPLE *JSAMPROW; /* ptr to one image row of pixel samples. */ typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ +#ifndef JPEG12LIB_H + typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ typedef JBLOCK *JBLOCKROW; /* pointer to one row of coefficient blocks */ typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ @@ -264,6 +266,8 @@ typedef enum { JDITHER_FS /* Floyd-Steinberg error diffusion dither */ } J_DITHER_MODE; +#endif /* JPEG12LIB_H */ + /* Common fields between JPEG compression and decompression master structs. */ @@ -822,6 +826,8 @@ struct jpeg_source_mgr { * successful. */ +#ifndef JPEG12LIB_H + #define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ #define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ #define JPOOL_NUMPOOLS 2 @@ -829,6 +835,8 @@ struct jpeg_source_mgr { typedef struct jvirt_sarray_control *jvirt_sarray_ptr; typedef struct jvirt_barray_control *jvirt_barray_ptr; +#endif + struct jpeg_memory_mgr { /* Method pointers */ diff --git a/jpeglibint.h b/jpeglibint.h new file mode 100644 index 000000000..6aac021f6 --- /dev/null +++ b/jpeglibint.h @@ -0,0 +1,328 @@ +/* + * jpeglibint.h + * + * Copyright (C) 2022, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + */ + +#ifndef JPEGLIBINT_H +#define JPEGLIBINT_H + + +#if BITS_IN_JSAMPLE == 12 + +#include "jpeg12lib.h" + + +/* Rename all external types and functions that are affected by JSAMPLE. */ + +#define JSAMPLE J12SAMPLE + +#undef MAXJSAMPLE +#define MAXJSAMPLE MAXJ12SAMPLE +#undef CENTERJSAMPLE +#define CENTERJSAMPLE CENTERJ12SAMPLE + +#define JSAMPROW J12SAMPROW +#define JSAMPARRAY J12SAMPARRAY +#define JSAMPIMAGE J12SAMPIMAGE + +#define jpeg_common_struct jpeg12_common_struct + +#define j_common_ptr j12_common_ptr +#define j_compress_ptr j12_compress_ptr +#define j_decompress_ptr j12_decompress_ptr + +#define jpeg_compress_struct jpeg12_compress_struct + +#define jpeg_decompress_struct jpeg12_decompress_struct + +#define jpeg_error_mgr jpeg12_error_mgr + +#define jpeg_progress_mgr jpeg12_progress_mgr + +#define jpeg_destination_mgr jpeg12_destination_mgr + +#define jpeg_source_mgr jpeg12_source_mgr + +#define jpeg_memory_mgr jpeg12_memory_mgr + +#define jpeg_marker_parser_method jpeg12_marker_parser_method + +#define jpeg_std_error jpeg12_std_error + +#define jpeg_create_compress jpeg12_create_compress +#define jpeg_create_decompress jpeg12_create_decompress +#define jpeg_CreateCompress jpeg12_CreateCompress +#define jpeg_CreateDecompress jpeg12_CreateDecompress +#define jpeg_destroy_compress jpeg12_destroy_compress +#define jpeg_destroy_decompress jpeg12_destroy_decompress + +#define jpeg_stdio_dest jpeg12_stdio_dest +#define jpeg_stdio_src jpeg12_stdio_src + +#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) +#define jpeg_mem_dest jpeg12_mem_dest +#define jpeg_mem_src jpeg12_mem_src +#endif + +#define jpeg_set_defaults jpeg12_set_defaults +#define jpeg_set_colorspace jpeg12_set_colorspace +#define jpeg_default_colorspace jpeg12_default_colorspace +#define jpeg_set_quality jpeg12_set_quality +#define jpeg_set_linear_quality jpeg12_set_linear_quality +#if JPEG_LIB_VERSION >= 70 +#define jpeg_default_qtables jpeg12_default_qtables +#endif +#define jpeg_add_quant_table jpeg12_add_quant_table +#define jpeg_quality_scaling jpeg12_quality_scaling +#define jpeg_simple_progression jpeg12_simple_progression +#define jpeg_suppress_tables jpeg12_suppress_tables +#define jpeg_alloc_quant_table jpeg12_alloc_quant_table +#define jpeg_alloc_huff_table jpeg12_alloc_huff_table + +#define jpeg_start_compress jpeg12_start_compress +#define jpeg_write_scanlines jpeg12_write_scanlines +#define jpeg_finish_compress jpeg12_finish_compress + +#if JPEG_LIB_VERSION >= 70 +#define jpeg_calc_jpeg_dimensions jpeg12_calc_jpeg_dimensions +#endif + +#define jpeg_write_raw_data jpeg12_write_raw_data + +#define jpeg_write_marker jpeg12_write_marker +#define jpeg_write_m_header jpeg12_write_m_header +#define jpeg_write_m_byte jpeg12_write_m_byte + +#define jpeg_write_tables jpeg12_write_tables + +#define jpeg_write_icc_profile jpeg12_write_icc_profile + +#define jpeg_read_header jpeg12_read_header + +#define jpeg_start_decompress jpeg12_start_decompress +#define jpeg_read_scanlines jpeg12_read_scanlines +#define jpeg_skip_scanlines jpeg12_skip_scanlines +#define jpeg_crop_scanline jpeg12_crop_scanline +#define jpeg_finish_decompress jpeg12_finish_decompress + +#define jpeg_read_raw_data jpeg12_read_raw_data + +#define jpeg_has_multiple_scans jpeg12_has_multiple_scans +#define jpeg_start_output jpeg12_start_output +#define jpeg_finish_output jpeg12_finish_output +#define jpeg_input_complete jpeg12_input_complete +#define jpeg_new_colormap jpeg12_new_colormap +#define jpeg_consume_input jpeg12_consume_input + +#if JPEG_LIB_VERSION >= 80 +#define jpeg_core_output_dimensions jpeg12_core_output_dimensions +#endif +#define jpeg_calc_output_dimensions jpeg12_calc_output_dimensions + +#define jpeg_save_markers jpeg12_save_markers + +#define jpeg_set_marker_processor jpeg12_set_marker_processor + +#define jpeg_read_coefficients jpeg12_read_coefficients +#define jpeg_write_coefficients jpeg12_write_coefficients +#define jpeg_copy_critical_parameters jpeg12_copy_critical_parameters + +#define jpeg_abort_compress jpeg12_abort_compress +#define jpeg_abort_decompress jpeg12_abort_decompress + +#define jpeg_abort jpeg12_abort +#define jpeg_destroy jpeg12_destroy + +#define jpeg_resync_to_restart jpeg12_resync_to_restart + +#define jpeg_read_icc_profile jpeg12_read_icc_profile + + +/* Rename all internal types and functions that are affected by JSAMPLE. */ + +#ifdef JPEG_INTERNALS + +#define jpeg_comp_master jpeg12_comp_master +#define jpeg_c_main_controller jpeg12_c_main_controller +#define jpeg_c_prep_controller jpeg12_c_prep_controller +#define jpeg_c_coef_controller jpeg12_c_coef_controller +#define jpeg_color_converter jpeg12_color_converter +#define jpeg_downsampler jpeg12_downsampler +#define jpeg_forward_dct jpeg12_forward_dct +#define jpeg_entropy_encoder jpeg12_entropy_encoder +#define jpeg_marker_writer jpeg12_marker_writer +#define jpeg_decomp_master jpeg12_decomp_master +#define jpeg_input_controller jpeg12_input_controller +#define jpeg_d_main_controller jpeg12_d_main_controller +#define jpeg_d_coef_controller jpeg12_d_coef_controller +#define jpeg_d_post_controller jpeg12_d_post_controller +#define jpeg_marker_reader jpeg12_marker_reader +#define jpeg_entropy_decoder jpeg12_entropy_decoder +#define inverse_DCT_method_ptr inverse_DCT_12_method_ptr +#define jpeg_inverse_dct jpeg12_inverse_dct +#define jpeg_upsampler jpeg12_upsampler +#define jpeg_color_deconverter jpeg12_color_deconverter +#define jpeg_color_quantizer jpeg12_color_quantizer + +#define jinit_compress_master j12init_compress_master +#define jinit_c_master_control j12init_c_master_control +#define jinit_c_main_controller j12init_c_main_controller +#define jinit_c_prep_controller j12init_c_prep_controller +#define jinit_c_coef_controller j12init_c_coef_controller +#define jinit_color_converter j12init_color_converter +#define jinit_downsampler j12init_downsampler +#define jinit_forward_dct j12init_forward_dct +#define jinit_huff_encoder j12init_huff_encoder +#define jinit_phuff_encoder j12init_phuff_encoder +#define jinit_arith_encoder j12init_arith_encoder +#define jinit_marker_writer j12init_marker_writer +#define jinit_master_decompress j12init_master_decompress +#define jinit_d_main_controller j12init_d_main_controller +#define jinit_d_coef_controller j12init_d_coef_controller +#define jinit_d_post_controller j12init_d_post_controller +#define jinit_input_controller j12init_input_controller +#define jinit_marker_reader j12init_marker_reader +#define jinit_huff_decoder j12init_huff_decoder +#define jinit_phuff_decoder j12init_phuff_decoder +#define jinit_arith_decoder j12init_arith_decoder +#define jinit_inverse_dct j12init_inverse_dct +#define jinit_upsampler j12init_upsampler +#define jinit_color_deconverter j12init_color_deconverter +#define jinit_1pass_quantizer j12init_1pass_quantizer +#define jinit_2pass_quantizer j12init_2pass_quantizer +#define jinit_merged_upsampler j12init_merged_upsampler +#define jinit_memory_mgr j12init_memory_mgr +#define jdiv_round_up j12div_round_up +#define jround_up j12round_up +#define jcopy_sample_rows j12copy_sample_rows +#define jcopy_block_row j12copy_block_row +#define jzero_far j12zero_far + +#define jpeg_natural_order jpeg12_natural_order + +#define jpeg_make_c_derived_tbl jpeg12_make_c_derived_tbl +#define jpeg_gen_optimal_table jpeg12_gen_optimal_table + +#define jpeg_fdct_islow jpeg12_fdct_islow +#define jpeg_fdct_ifast jpeg12_fdct_ifast +#define jpeg_fdct_float jpeg12_fdct_float +#define jpeg_idct_islow jpeg12_idct_islow +#define jpeg_idct_ifast jpeg12_idct_ifast +#define jpeg_idct_float jpeg12_idct_float +#define jpeg_idct_7x7 jpeg12_idct_7x7 +#define jpeg_idct_6x6 jpeg12_idct_6x6 +#define jpeg_idct_5x5 jpeg12_idct_5x5 +#define jpeg_idct_4x4 jpeg12_idct_4x4 +#define jpeg_idct_3x3 jpeg12_idct_3x3 +#define jpeg_idct_2x2 jpeg12_idct_2x2 +#define jpeg_idct_1x1 jpeg12_idct_1x1 +#define jpeg_idct_9x9 jpeg12_idct_9x9 +#define jpeg_idct_10x10 jpeg12_idct_10x10 +#define jpeg_idct_11x11 jpeg12_idct_11x11 +#define jpeg_idct_12x12 jpeg12_idct_12x12 +#define jpeg_idct_13x13 jpeg12_idct_13x13 +#define jpeg_idct_14x14 jpeg12_idct_14x14 +#define jpeg_idct_15x15 jpeg12_idct_15x15 +#define jpeg_idct_16x16 jpeg12_idct_16x16 + +#define jpeg_make_d_derived_tbl jpeg12_make_d_derived_tbl +#define jpeg_fill_bit_buffer jpeg12_fill_bit_buffer +#define jpeg_huff_decode jpeg12_huff_decode + +#define jpeg_std_message_table jpeg12_std_message_table + +#define jpeg_get_small jpeg12_get_small +#define jpeg_free_small jpeg12_free_small +#define jpeg_get_large jpeg12_get_large +#define jpeg_free_large jpeg12_free_large +#define jpeg_mem_available jpeg12_mem_available +#define jpeg_open_backing_store jpeg12_open_backing_store +#define jpeg_mem_init jpeg12_mem_init +#define jpeg_mem_term jpeg12_mem_term + +#define jsimd_can_rgb_ycc j12simd_can_rgb_ycc +#define jsimd_can_rgb_gray j12simd_can_rgb_gray +#define jsimd_can_ycc_rgb j12simd_can_ycc_rgb +#define jsimd_can_ycc_rgb565 j12simd_can_ycc_rgb565 +#define jsimd_c_can_null_convert j12simd_c_can_null_convert +#define jsimd_rgb_ycc_convert j12simd_rgb_ycc_convert +#define jsimd_rgb_gray_convert j12simd_rgb_gray_convert +#define jsimd_ycc_rgb_convert j12simd_ycc_rgb_convert +#define jsimd_ycc_rgb565_convert j12simd_ycc_rgb565_convert +#define jsimd_c_null_convert j12simd_c_null_convert +#define jsimd_can_h2v2_downsample j12simd_can_h2v2_downsample +#define jsimd_can_h2v1_downsample j12simd_can_h2v1_downsample +#define jsimd_h2v2_downsample j12simd_h2v2_downsample +#define jsimd_can_h2v2_smooth_downsample j12simd_can_h2v2_smooth_downsample +#define jsimd_h2v2_smooth_downsample j12simd_h2v2_smooth_downsample +#define jsimd_h2v1_downsample j12simd_h2v1_downsample +#define jsimd_can_h2v2_upsample j12simd_can_h2v2_upsample +#define jsimd_can_h2v1_upsample j12simd_can_h2v1_upsample +#define jsimd_can_int_upsample j12simd_can_int_upsample +#define jsimd_h2v2_upsample j12simd_h2v2_upsample +#define jsimd_h2v1_upsample j12simd_h2v1_upsample +#define jsimd_int_upsample j12simd_int_upsample +#define jsimd_can_h2v2_fancy_upsample j12simd_can_h2v2_fancy_upsample +#define jsimd_can_h2v1_fancy_upsample j12simd_can_h2v1_fancy_upsample +#define jsimd_can_h1v2_fancy_upsample j12simd_can_h1v2_fancy_upsample +#define jsimd_h2v2_fancy_upsample j12simd_h2v2_fancy_upsample +#define jsimd_h2v1_fancy_upsample j12simd_h2v1_fancy_upsample +#define jsimd_h1v2_fancy_upsample j12simd_h1v2_fancy_upsample +#define jsimd_can_h2v2_merged_upsample j12simd_can_h2v2_merged_upsample +#define jsimd_can_h2v1_merged_upsample j12simd_can_h2v1_merged_upsample +#define jsimd_h2v2_merged_upsample j12simd_h2v2_merged_upsample +#define jsimd_h2v1_merged_upsample j12simd_h2v1_merged_upsample +#define jsimd_can_huff_encode_one_block j12simd_can_huff_encode_one_block +#define jsimd_huff_encode_one_block j12simd_huff_encode_one_block +#define jsimd_can_encode_mcu_AC_first_prepare \ + j12simd_can_encode_mcu_AC_first_prepare +#define jsimd_encode_mcu_AC_first_prepare \ + j12simd_encode_mcu_AC_first_prepare +#define jsimd_can_encode_mcu_AC_refine_prepare \ + j12simd_can_encode_mcu_AC_refine_prepare +#define jsimd_encode_mcu_AC_refine_prepare \ + j12simd_encode_mcu_AC_refine_prepare + +#define jsimd_can_convsamp j12simd_can_convsamp +#define jsimd_can_convsamp_float j12simd_can_convsamp_float +#define jsimd_convsamp j12simd_convsamp +#define jsimd_convsamp_float j12simd_convsamp_float +#define jsimd_can_fdct_islow j12simd_can_fdct_islow +#define jsimd_can_fdct_ifast j12simd_can_fdct_ifast +#define jsimd_can_fdct_float j12simd_can_fdct_float +#define jsimd_fdct_islow j12simd_fdct_islow +#define jsimd_fdct_ifast j12simd_fdct_ifast +#define jsimd_fdct_float j12simd_fdct_float +#define jsimd_can_quantize j12simd_can_quantize +#define jsimd_can_quantize_float j12simd_can_quantize_float +#define jsimd_quantize j12simd_quantize +#define jsimd_quantize_float j12simd_quantize_float +#define jsimd_can_idct_2x2 j12simd_can_idct_2x2 +#define jsimd_can_idct_4x4 j12simd_can_idct_4x4 +#define jsimd_can_idct_6x6 j12simd_can_idct_6x6 +#define jsimd_can_idct_12x12 j12simd_can_idct_12x12 +#define jsimd_idct_2x2 j12simd_idct_2x2 +#define jsimd_idct_4x4 j12simd_idct_4x4 +#define jsimd_idct_6x6 j12simd_idct_6x6 +#define jsimd_idct_12x12 j12simd_idct_12x12 +#define jsimd_can_idct_islow j12simd_can_idct_islow +#define jsimd_can_idct_ifast j12simd_can_idct_ifast +#define jsimd_can_idct_float j12simd_can_idct_float +#define jsimd_idct_islow j12simd_idct_islow +#define jsimd_idct_ifast j12simd_idct_ifast +#define jsimd_idct_float j12simd_idct_float + +#endif /* JPEG_INTERNALS */ + + +#else /* BITS_IN_JSAMPLE == 12 */ + +#include "jpeglib.h" + +#endif + + +#endif /* JPEGLIBINT_H */ diff --git a/jquant1.c b/jquant1.c index 73b83e16e..0ea5573b7 100644 --- a/jquant1.c +++ b/jquant1.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2009, 2015, D. R. Commander. + * Copyright (C) 2009, 2015, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -15,7 +15,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #ifdef QUANT_1PASS_SUPPORTED diff --git a/jquant2.c b/jquant2.c index 44efb18ca..a49e554de 100644 --- a/jquant2.c +++ b/jquant2.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2009, 2014-2015, 2020, D. R. Commander. + * Copyright (C) 2009, 2014-2015, 2020, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -22,7 +22,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #ifdef QUANT_2PASS_SUPPORTED diff --git a/jsimd_none.c b/jsimd_none.c index 5b38a9fb5..8e33d390b 100644 --- a/jsimd_none.c +++ b/jsimd_none.c @@ -2,7 +2,7 @@ * jsimd_none.c * * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2009-2011, 2014, D. R. Commander. + * Copyright (C) 2009-2011, 2014, 2022, D. R. Commander. * Copyright (C) 2015-2016, 2018, Matthieu Darbois. * Copyright (C) 2020, Arm Limited. * @@ -15,7 +15,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "jsimd.h" #include "jdct.h" #include "jsimddct.h" diff --git a/jutils.c b/jutils.c index d86271624..26f4dc22c 100644 --- a/jutils.c +++ b/jutils.c @@ -16,7 +16,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" /* diff --git a/libjpeg.map.in b/libjpeg.map.in index b4480d834..9db61c03f 100644 --- a/libjpeg.map.in +++ b/libjpeg.map.in @@ -1,7 +1,7 @@ LIBJPEGTURBO_@JPEG_LIB_VERSION_DECIMAL@ { @MEM_SRCDST_FUNCTIONS@ local: - jsimd_*; + @SIMD_FUNCTIONS@; jconst_*; }; diff --git a/libjpeg.txt b/libjpeg.txt index 309f9d3b4..72492c954 100644 --- a/libjpeg.txt +++ b/libjpeg.txt @@ -11,10 +11,11 @@ For conditions of distribution and use, see the accompanying README.ijg file. This file describes how to use the IJG JPEG library within an application program. Read it if you want to write a program that uses the library. -The file example.txt provides heavily commented skeleton code for calling the -JPEG library. Also see jpeglib.h (the include file to be used by application -programs) for full details about data structures and function parameter lists. -The library source code, of course, is the ultimate reference. +The file example.c provides heavily commented code for calling the JPEG +library. Also see jpeglib.h and jpeg12lib.h (the include files to be used by +application programs) for full details about data structures and function +parameter lists. The library source code, of course, is the ultimate +reference. Note that there have been *major* changes from the application interface presented by IJG version 4 and earlier versions. The old design had several @@ -29,6 +30,7 @@ TABLE OF CONTENTS Overview: Functions provided by the library + 12-bit Data Precision Outline of typical usage Basic library usage: Data formats @@ -99,9 +101,7 @@ use.) Unsupported ISO options include: * Lossless JPEG * DNL marker * Nonintegral subsampling ratios -We support both 8- and 12-bit data precision, but this is a compile-time -choice rather than a run-time choice; hence it is difficult to use both -precisions in a single application. +We support both 8- and 12-bit data precision. By itself, the library handles only interchange JPEG datastreams --- in particular the widely used JFIF file format. The library can be used by @@ -110,6 +110,38 @@ are embedded in more complex file formats. (For example, this library is used by the free LIBTIFF library to support JPEG compression in TIFF.) +12-bit Data Precision +--------------------- + +Support for JPEG images with 12-bit, rather than 8-bit, samples is provided +through a separate library, API, and header file (jpeg12lib.h instead of +jpeglib.h). Functions supporting 12-bit samples have a prefix of "jpeg12_" +instead of "jpeg_" and use the following data types, structures, and macros: + + * J12SAMPLE instead of JSAMPLE + * J12SAMPROW instead of JSAMPROW + * J12SAMPARRAY instead of JSAMPARRAY + * J12SAMPIMAGE instead of JSAMPIMAGE + * MAXJ12SAMPLE instead of MAXJSAMPLE + * CENTERJ12SAMPLE instead of CENTERJSAMPLE + * jpeg12_common_struct instead of jpeg_common_struct + * j12_common_ptr instead of j_common_ptr + * jpeg12_compress_struct instead of jpeg_compress_struct + * j12_compress_ptr instead of j_compress_ptr + * jpeg12_decompress_struct instead of jpeg_decompress_struct + * j12_decompress_ptr instead of j_decompress_ptr + * jpeg12_error_mgr instead of jpeg_error_mgr + * jpeg12_progress_mgr instead of jpeg_progress_mgr + * jpeg12_destination_mgr instead of jpeg_destination_mgr + * jpeg12_source_mgr instead of jpeg_source_mgr + * jpeg12_memory_mgr instead of jpeg_memory_mgr + * jpeg12_marker_parser_method instead of jpeg_marker_parser_method + +This allows both 8-bit and 12-bit precision to be used in a single application. +(Refer to example.c). Arithmetic coding and SIMD acceleration are not +currently implemented for 12-bit samples. + + Outline of typical usage ------------------------ @@ -402,16 +434,13 @@ this variable as the loop counter, so that the loop test looks like "while (cinfo.next_scanline < cinfo.image_height)". Code for this step depends heavily on the way that you store the source data. -example.txt shows the following code for the case of a full-size 2-D source +example.c shows the following code for the case of a full-size 2-D source array containing 3-byte RGB pixels: JSAMPROW row_pointer[1]; /* pointer to a single row */ - int row_stride; /* physical row width in buffer */ - - row_stride = image_width * 3; /* JSAMPLEs per row in image_buffer */ while (cinfo.next_scanline < cinfo.image_height) { - row_pointer[0] = &image_buffer[cinfo.next_scanline * row_stride]; + row_pointer[0] = image_buffer[cinfo.next_scanline]; jpeg_write_scanlines(&cinfo, row_pointer, 1); } @@ -1445,7 +1474,7 @@ When the default error handler is used, any error detected inside the JPEG routines will cause a message to be printed on stderr, followed by exit(). You can supply your own error handling routines to override this behavior and to control the treatment of nonfatal warnings and trace/debug messages. -The file example.txt illustrates the most common case, which is to have the +The file example.c illustrates the most common case, which is to have the application regain control after an error rather than exiting. The JPEG library never writes any message directly; it always goes through @@ -1462,7 +1491,7 @@ You may, if you wish, simply replace the entire JPEG error handling module only replacing some of the routines depending on the behavior you need. This is accomplished by calling jpeg_std_error() as usual, but then overriding some of the method pointers in the jpeg_error_mgr struct, as illustrated by -example.txt. +example.c. All of the error handling routines will receive a pointer to the JPEG object (a j_common_ptr which points to either a jpeg_compress_struct or a @@ -1473,7 +1502,7 @@ additional data which is not known to the JPEG library or the standard error handler. The most convenient way to do this is to embed either the JPEG object or the jpeg_error_mgr struct in a larger structure that contains additional fields; then casting the passed pointer provides access to the -additional fields. Again, see example.txt for one way to do it. (Beginning +additional fields. Again, see example.c for one way to do it. (Beginning with IJG version 6b, there is also a void pointer "client_data" in each JPEG object, which the application can also use to find related data. The library does not touch client_data at all.) @@ -3064,8 +3093,7 @@ BITS_IN_JSAMPLE as 12 rather than 8. Note that this causes JSAMPLE to be larger than a char, so it affects the surrounding application's image data. The sample applications cjpeg and djpeg can support 12-bit mode only for PPM and GIF file formats; you must disable the other file formats to compile a -12-bit cjpeg or djpeg. At present, a 12-bit library can handle *only* 12-bit -images, not both precisions. +12-bit cjpeg or djpeg. Note that a 12-bit library always compresses in Huffman optimization mode, in order to generate valid Huffman tables. This is necessary because our diff --git a/release/installer.nsi.in b/release/installer.nsi.in index 65db63da3..132e422d8 100644 --- a/release/installer.nsi.in +++ b/release/installer.nsi.in @@ -47,12 +47,23 @@ Section "@CMAKE_PROJECT_NAME@ SDK for @INST_PLATFORM@ (required)" !endif !ifdef GCC File "@CMAKE_CURRENT_BINARY_DIR@\libjpeg-@SO_MAJOR_VERSION@.dll" +!ifdef 12BIT + File "@CMAKE_CURRENT_BINARY_DIR@\libjpeg12-@SO_MAJOR_VERSION@.dll" +!endif !else File "@CMAKE_CURRENT_BINARY_DIR@\${BUILDDIR}jpeg@SO_MAJOR_VERSION@.dll" +!ifdef 12BIT + File "@CMAKE_CURRENT_BINARY_DIR@\${BUILDDIR}jpeg12-@SO_MAJOR_VERSION@.dll" +!endif !endif File "@CMAKE_CURRENT_BINARY_DIR@\${BUILDDIR}cjpeg.exe" File "@CMAKE_CURRENT_BINARY_DIR@\${BUILDDIR}djpeg.exe" File "@CMAKE_CURRENT_BINARY_DIR@\${BUILDDIR}jpegtran.exe" +!ifdef 12BIT + File "@CMAKE_CURRENT_BINARY_DIR@\${BUILDDIR}cjpeg12.exe" + File "@CMAKE_CURRENT_BINARY_DIR@\${BUILDDIR}djpeg12.exe" + File "@CMAKE_CURRENT_BINARY_DIR@\${BUILDDIR}jpeg12tran.exe" +!endif File "@CMAKE_CURRENT_BINARY_DIR@\${BUILDDIR}tjbench.exe" File "@CMAKE_CURRENT_BINARY_DIR@\${BUILDDIR}rdjpgcom.exe" File "@CMAKE_CURRENT_BINARY_DIR@\${BUILDDIR}wrjpgcom.exe" @@ -62,14 +73,25 @@ Section "@CMAKE_PROJECT_NAME@ SDK for @INST_PLATFORM@ (required)" File "@CMAKE_CURRENT_BINARY_DIR@\libturbojpeg.a" File "@CMAKE_CURRENT_BINARY_DIR@\libjpeg.dll.a" File "@CMAKE_CURRENT_BINARY_DIR@\libjpeg.a" +!ifdef 12BIT + File "@CMAKE_CURRENT_BINARY_DIR@\libjpeg12.dll.a" + File "@CMAKE_CURRENT_BINARY_DIR@\libjpeg12.a" +!endif !else File "@CMAKE_CURRENT_BINARY_DIR@\${BUILDDIR}turbojpeg.lib" File "@CMAKE_CURRENT_BINARY_DIR@\${BUILDDIR}turbojpeg-static.lib" File "@CMAKE_CURRENT_BINARY_DIR@\${BUILDDIR}jpeg.lib" File "@CMAKE_CURRENT_BINARY_DIR@\${BUILDDIR}jpeg-static.lib" +!ifdef 12BIT + File "@CMAKE_CURRENT_BINARY_DIR@\${BUILDDIR}jpeg12.lib" + File "@CMAKE_CURRENT_BINARY_DIR@\${BUILDDIR}jpeg12-static.lib" +!endif !endif SetOutPath $INSTDIR\lib\pkgconfig File "@CMAKE_CURRENT_BINARY_DIR@\pkgscripts\libjpeg.pc" +!ifdef 12BIT + File "@CMAKE_CURRENT_BINARY_DIR@\pkgscripts\libjpeg12.pc" +!endif File "@CMAKE_CURRENT_BINARY_DIR@\pkgscripts\libturbojpeg.pc" SetOutPath $INSTDIR\lib\cmake\@CMAKE_PROJECT_NAME@ File "@CMAKE_CURRENT_BINARY_DIR@\pkgscripts\@CMAKE_PROJECT_NAME@Config.cmake" @@ -85,12 +107,15 @@ Section "@CMAKE_PROJECT_NAME@ SDK for @INST_PLATFORM@ (required)" File "@CMAKE_CURRENT_SOURCE_DIR@\jerror.h" File "@CMAKE_CURRENT_SOURCE_DIR@\jmorecfg.h" File "@CMAKE_CURRENT_SOURCE_DIR@\jpeglib.h" +!ifdef 12BIT + File "@CMAKE_CURRENT_SOURCE_DIR@\jpeg12lib.h" +!endif File "@CMAKE_CURRENT_SOURCE_DIR@\turbojpeg.h" SetOutPath $INSTDIR\doc File "@CMAKE_CURRENT_SOURCE_DIR@\README.ijg" File "@CMAKE_CURRENT_SOURCE_DIR@\README.md" File "@CMAKE_CURRENT_SOURCE_DIR@\LICENSE.md" - File "@CMAKE_CURRENT_SOURCE_DIR@\example.txt" + File "@CMAKE_CURRENT_SOURCE_DIR@\example.c" File "@CMAKE_CURRENT_SOURCE_DIR@\libjpeg.txt" File "@CMAKE_CURRENT_SOURCE_DIR@\structure.txt" File "@CMAKE_CURRENT_SOURCE_DIR@\usage.txt" @@ -129,22 +154,39 @@ Section "Uninstall" !ifdef GCC Delete $INSTDIR\bin\libjpeg-@SO_MAJOR_VERSION@.dll +!ifdef 12BIT + Delete $INSTDIR\bin\libjpeg12-@SO_MAJOR_VERSION@.dll +!endif Delete $INSTDIR\bin\libturbojpeg.dll Delete $SYSDIR\libturbojpeg.dll Delete $INSTDIR\lib\libturbojpeg.dll.a Delete $INSTDIR\lib\libturbojpeg.a Delete $INSTDIR\lib\libjpeg.dll.a Delete $INSTDIR\lib\libjpeg.a +!ifdef 12BIT + Delete $INSTDIR\lib\libjpeg12.dll.a + Delete $INSTDIR\lib\libjpeg12.a +!endif !else Delete $INSTDIR\bin\jpeg@SO_MAJOR_VERSION@.dll +!ifdef 12BIT + Delete $INSTDIR\bin\jpeg12-@SO_MAJOR_VERSION@.dll +!endif Delete $INSTDIR\bin\turbojpeg.dll Delete $SYSDIR\turbojpeg.dll Delete $INSTDIR\lib\jpeg.lib Delete $INSTDIR\lib\jpeg-static.lib +!ifdef 12BIT + Delete $INSTDIR\lib\jpeg12.lib + Delete $INSTDIR\lib\jpeg12-static.lib +!endif Delete $INSTDIR\lib\turbojpeg.lib Delete $INSTDIR\lib\turbojpeg-static.lib !endif Delete $INSTDIR\lib\pkgconfig\libjpeg.pc +!ifdef 12BIT + Delete $INSTDIR\lib\pkgconfig\libjpeg12.pc +!endif Delete $INSTDIR\lib\pkgconfig\libturbojpeg.pc Delete $INSTDIR\lib\cmake\@CMAKE_PROJECT_NAME@\@CMAKE_PROJECT_NAME@Config.cmake Delete $INSTDIR\lib\cmake\@CMAKE_PROJECT_NAME@\@CMAKE_PROJECT_NAME@ConfigVersion.cmake @@ -156,6 +198,11 @@ Section "Uninstall" Delete $INSTDIR\bin\cjpeg.exe Delete $INSTDIR\bin\djpeg.exe Delete $INSTDIR\bin\jpegtran.exe +!ifdef 12BIT + Delete $INSTDIR\bin\cjpeg12.exe + Delete $INSTDIR\bin\djpeg12.exe + Delete $INSTDIR\bin\jpeg12tran.exe +!endif Delete $INSTDIR\bin\tjbench.exe Delete $INSTDIR\bin\rdjpgcom.exe Delete $INSTDIR\bin\wrjpgcom.exe @@ -163,12 +210,15 @@ Section "Uninstall" Delete $INSTDIR\include\jerror.h Delete $INSTDIR\include\jmorecfg.h Delete $INSTDIR\include\jpeglib.h +!ifdef 12BIT + Delete $INSTDIR\include\jpeg12lib.h +!endif Delete $INSTDIR\include\turbojpeg.h Delete $INSTDIR\uninstall_@VERSION@.exe Delete $INSTDIR\doc\README.ijg Delete $INSTDIR\doc\README.md Delete $INSTDIR\doc\LICENSE.md - Delete $INSTDIR\doc\example.txt + Delete $INSTDIR\doc\example.c Delete $INSTDIR\doc\libjpeg.txt Delete $INSTDIR\doc\structure.txt Delete $INSTDIR\doc\usage.txt diff --git a/release/libjpeg12.pc.in b/release/libjpeg12.pc.in new file mode 100644 index 000000000..54a056311 --- /dev/null +++ b/release/libjpeg12.pc.in @@ -0,0 +1,10 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=@CMAKE_INSTALL_PREFIX@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ + +Name: libjpeg12 +Description: A 12-bit JPEG codec that provides the libjpeg API +Version: @VERSION@ +Libs: -L${libdir} -ljpeg12 +Cflags: -I${includedir} diff --git a/release/rpm.spec.in b/release/rpm.spec.in index 420702428..74ef0dd74 100644 --- a/release/rpm.spec.in +++ b/release/rpm.spec.in @@ -9,6 +9,7 @@ %define _enable_shared @ENABLE_SHARED@ %define _with_turbojpeg @WITH_TURBOJPEG@ %define _with_java @WITH_JAVA@ +%define _with_12bit @WITH_12BIT@ %if "%{?__isa_bits:1}" == "1" %define _bits %{__isa_bits} @@ -174,6 +175,11 @@ rm -rf $RPM_BUILD_ROOT %{_bindir}/cjpeg %{_bindir}/djpeg %{_bindir}/jpegtran +%if "%{_with_12bit}" == "1" + %{_bindir}/cjpeg12 + %{_bindir}/djpeg12 + %{_bindir}/jpeg12tran +%endif %if "%{_with_turbojpeg}" == "1" %{_bindir}/tjbench %endif @@ -186,12 +192,23 @@ rm -rf $RPM_BUILD_ROOT %{_libdir}/libjpeg.so.@SO_MAJOR_VERSION@.@SO_AGE@.@SO_MINOR_VERSION@ %{_libdir}/libjpeg.so.@SO_MAJOR_VERSION@ %{_libdir}/libjpeg.so + %if "%{_with_12bit}" == "1" + %{_libdir}/libjpeg12.so.@SO_MAJOR_VERSION@.@SO_AGE@.@SO_MINOR_VERSION@ + %{_libdir}/libjpeg12.so.@SO_MAJOR_VERSION@ + %{_libdir}/libjpeg12.so + %endif %endif %if "%{_enable_static}" == "1" %{_libdir}/libjpeg.a + %if "%{_with_12bit}" == "1" + %{_libdir}/libjpeg12.a + %endif %endif %dir %{_libdir}/pkgconfig %{_libdir}/pkgconfig/libjpeg.pc + %if "%{_with_12bit}" == "1" + %{_libdir}/pkgconfig/libjpeg12.pc + %endif %dir %{_libdir}/cmake %dir %{_libdir}/cmake/@CMAKE_PROJECT_NAME@ %{_libdir}/cmake/@CMAKE_PROJECT_NAME@ @@ -211,6 +228,9 @@ rm -rf $RPM_BUILD_ROOT %{_includedir}/jerror.h %{_includedir}/jmorecfg.h %{_includedir}/jpeglib.h +%if "%{_with_12bit}" == "1" + %{_includedir}/jpeg12lib.h +%endif %if "%{_with_turbojpeg}" == "1" %{_includedir}/turbojpeg.h %endif diff --git a/sharedlib/CMakeLists.txt b/sharedlib/CMakeLists.txt index aea0b9d7c..197a8900e 100644 --- a/sharedlib/CMakeLists.txt +++ b/sharedlib/CMakeLists.txt @@ -23,6 +23,10 @@ foreach(src ${JPEG_SOURCES}) set(JPEG_SRCS ${JPEG_SRCS} ../${src}) endforeach() +foreach(src ${JPEG12_SOURCES}) + set(JPEG12_SRCS ${JPEG12_SRCS} ../${src}) +endforeach() + if(WITH_SIMD AND (MSVC_IDE OR XCODE)) # This tells CMake that the "source" files haven't been generated yet set_source_files_properties(${SIMD_OBJS} PROPERTIES GENERATED 1) @@ -60,36 +64,89 @@ if(MSVC) set_target_properties(jpeg PROPERTIES RUNTIME_OUTPUT_NAME jpeg${SO_MAJOR_VERSION}) # The jsimd_*.c file is built using /MT, so this prevents a linker warning. - set_target_properties(jpeg PROPERTIES LINK_FLAGS "/NODEFAULTLIB:LIBCMT /NODEFAULTLIB:LIBCMTD") + set_target_properties(jpeg PROPERTIES LINK_FLAGS + "/NODEFAULTLIB:LIBCMT /NODEFAULTLIB:LIBCMTD") elseif(MINGW) set_target_properties(jpeg PROPERTIES SUFFIX -${SO_MAJOR_VERSION}.dll) endif() +if(WITH_12BIT) + if(WIN32) + if(WITH_MEM_SRCDST) + set(DEFFILE ../win/jpeg12-${SO_MAJOR_VERSION}-memsrcdst.def) + else() + set(DEFFILE ../win/jpeg12-${SO_MAJOR_VERSION}.def) + endif() + endif() + add_library(jpeg12 SHARED ${JPEG12_SRCS} ${DEFFILE} ../jsimd_none.c) + set_property(TARGET jpeg12 PROPERTY COMPILE_FLAGS "-DBITS_IN_JSAMPLE=12") + + set_target_properties(jpeg12 PROPERTIES SOVERSION ${SO_MAJOR_VERSION} + VERSION ${SO_MAJOR_VERSION}.${SO_AGE}.${SO_MINOR_VERSION}) + if(APPLE AND (NOT CMAKE_OSX_DEPLOYMENT_TARGET OR + CMAKE_OSX_DEPLOYMENT_TARGET VERSION_GREATER 10.4)) + set_target_properties(jpeg12 PROPERTIES MACOSX_RPATH 1) + endif() + if(MAPFLAG) + set_target_properties(jpeg12 PROPERTIES + LINK_FLAGS "${MAPFLAG}${CMAKE_CURRENT_BINARY_DIR}/../libjpeg12.map") + endif() + if(MSVC) + set_target_properties(jpeg12 PROPERTIES + RUNTIME_OUTPUT_NAME jpeg12-${SO_MAJOR_VERSION}) + elseif(MINGW) + set_target_properties(jpeg12 PROPERTIES SUFFIX -${SO_MAJOR_VERSION}.dll) + endif() +endif() + if(WIN32) set(USE_SETMODE "-DUSE_SETMODE") endif() -if(WITH_12BIT) - set(COMPILE_FLAGS "-DGIF_SUPPORTED -DPPM_SUPPORTED ${USE_SETMODE}") -else() - set(COMPILE_FLAGS "-DBMP_SUPPORTED -DGIF_SUPPORTED -DPPM_SUPPORTED -DTARGA_SUPPORTED ${USE_SETMODE}") - set(CJPEG_BMP_SOURCES ../rdbmp.c ../rdtarga.c) - set(DJPEG_BMP_SOURCES ../wrbmp.c ../wrtarga.c) -endif() -add_executable(cjpeg ../cjpeg.c ../cdjpeg.c ../rdgif.c ../rdppm.c - ../rdswitch.c ${CJPEG_BMP_SOURCES}) -set_property(TARGET cjpeg PROPERTY COMPILE_FLAGS ${COMPILE_FLAGS}) +add_executable(cjpeg ../cjpeg.c ../cdjpeg.c ../rdbmp.c ../rdgif.c ../rdppm.c + ../rdswitch.c ../rdtarga.c) +set_property(TARGET cjpeg PROPERTY COMPILE_FLAGS + "-DBMP_SUPPORTED -DGIF_SUPPORTED -DPPM_SUPPORTED -DTARGA_SUPPORTED ${USE_SETMODE}") target_link_libraries(cjpeg jpeg) add_executable(djpeg ../djpeg.c ../cdjpeg.c ../rdcolmap.c ../rdswitch.c - ../wrgif.c ../wrppm.c ${DJPEG_BMP_SOURCES}) -set_property(TARGET djpeg PROPERTY COMPILE_FLAGS ${COMPILE_FLAGS}) + ../wrbmp.c ../wrgif.c ../wrppm.c ../wrtarga.c) +set_property(TARGET djpeg PROPERTY COMPILE_FLAGS + "-DBMP_SUPPORTED -DGIF_SUPPORTED -DPPM_SUPPORTED -DTARGA_SUPPORTED ${USE_SETMODE}") target_link_libraries(djpeg jpeg) add_executable(jpegtran ../jpegtran.c ../cdjpeg.c ../rdswitch.c ../transupp.c) target_link_libraries(jpegtran jpeg) set_property(TARGET jpegtran PROPERTY COMPILE_FLAGS "${USE_SETMODE}") +if(WITH_12BIT) + add_executable(cjpeg12 ../cjpeg.c ../cdjpeg.c ../rdgif.c ../rdppm.c + ../rdswitch.c) + set_property(TARGET cjpeg12 PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=12 -DGIF_SUPPORTED -DPPM_SUPPORTED ${USE_SETMODE}") + target_link_libraries(cjpeg12 jpeg12) + + add_executable(djpeg12 ../djpeg.c ../cdjpeg.c ../rdcolmap.c ../rdswitch.c + ../wrgif.c ../wrppm.c) + set_property(TARGET djpeg12 PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=12 -DGIF_SUPPORTED -DPPM_SUPPORTED ${USE_SETMODE}") + target_link_libraries(djpeg12 jpeg12) + + add_executable(jpeg12tran ../jpegtran.c ../cdjpeg.c ../rdswitch.c + ../transupp.c) + target_link_libraries(jpeg12tran jpeg12) + set_property(TARGET jpeg12tran PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=12 ${USE_SETMODE}") +endif() + +add_executable(example ../example.c) +if(WITH_12BIT) + target_link_libraries(example jpeg jpeg12) + set_property(TARGET example PROPERTY COMPILE_FLAGS "-DWITH_12BIT") +else() + target_link_libraries(example jpeg) +endif() + add_executable(jcstest ../jcstest.c) target_link_libraries(jcstest jpeg) @@ -105,3 +162,18 @@ if(NOT CMAKE_VERSION VERSION_LESS "3.1" AND MSVC AND install(FILES "$" DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL) endif() + +if(WITH_12BIT) + install(TARGETS jpeg12 EXPORT ${CMAKE_PROJECT_NAME}Targets + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + install(TARGETS cjpeg12 djpeg12 jpeg12tran + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + if(NOT CMAKE_VERSION VERSION_LESS "3.1" AND MSVC AND + CMAKE_C_LINKER_SUPPORTS_PDB) + install(FILES "$" + DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL) + endif() +endif() diff --git a/structure.txt b/structure.txt index 15b8d3785..59b7253f0 100644 --- a/structure.txt +++ b/structure.txt @@ -57,10 +57,7 @@ Within these limits, any set of compression parameters allowed by the JPEG spec should be readable for decompression. (We can be more restrictive about what formats we can generate.) Although the system design allows for all parameter values, some uncommon settings are not yet implemented and may -never be; nonintegral sampling ratios are the prime example. Furthermore, -we treat 8-bit vs. 12-bit data precision as a compile-time switch, not a -run-time option, because most machines can store 8-bit pixels much more -compactly than 12-bit. +never be; nonintegral sampling ratios are the prime example. By itself, the library handles only interchange JPEG datastreams --- in particular the widely used JFIF file format. The library can be used by diff --git a/transupp.c b/transupp.c index a3d878cc7..e713f26c2 100644 --- a/transupp.c +++ b/transupp.c @@ -21,7 +21,7 @@ #define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jpeglibint.h" #include "transupp.h" /* My own external interface */ #include "jpegcomp.h" #include /* to declare isdigit() */ diff --git a/turbojpeg.c b/turbojpeg.c index 1a37e8cf1..53e2e540a 100644 --- a/turbojpeg.c +++ b/turbojpeg.c @@ -53,7 +53,7 @@ extern void jpeg_mem_src_tj(j_decompress_ptr, const unsigned char *, #define IS_POW2(x) (((x) & (x - 1)) == 0) -/* Error handling (based on example in example.txt) */ +/* Error handling (based on example in example.c) */ static THREAD_LOCAL char errStr[JMSG_LENGTH_MAX] = "No error"; @@ -490,7 +490,7 @@ static tjhandle _tjInitCompress(tjinstance *this) unsigned char *buf = buffer; unsigned long size = 1; - /* This is also straight out of example.txt */ + /* This is also straight out of example.c */ this->cinfo.err = jpeg_std_error(&this->jerr.pub); this->jerr.pub.error_exit = my_error_exit; this->jerr.pub.output_message = my_output_message; @@ -1164,7 +1164,7 @@ static tjhandle _tjInitDecompress(tjinstance *this) { static unsigned char buffer[1]; - /* This is also straight out of example.txt */ + /* This is also straight out of example.c */ this->dinfo.err = jpeg_std_error(&this->jerr.pub); this->jerr.pub.error_exit = my_error_exit; this->jerr.pub.output_message = my_output_message; diff --git a/win/gcc/projectTargets-release.cmake.in b/win/gcc/projectTargets-release.cmake.in index 1e1a8a34a..091e404de 100644 --- a/win/gcc/projectTargets-release.cmake.in +++ b/win/gcc/projectTargets-release.cmake.in @@ -15,6 +15,16 @@ set_target_properties(@CMAKE_PROJECT_NAME@::jpeg PROPERTIES list(APPEND _IMPORT_CHECK_TARGETS @CMAKE_PROJECT_NAME@::jpeg ) list(APPEND _IMPORT_CHECK_FILES_FOR_@CMAKE_PROJECT_NAME@::jpeg "${_IMPORT_PREFIX}/lib/libjpeg.dll.a" "${_IMPORT_PREFIX}/bin/libjpeg-62.dll" ) +# Import target "@CMAKE_PROJECT_NAME@::jpeg12" for configuration "Release" +set_property(TARGET @CMAKE_PROJECT_NAME@::jpeg12 APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) +set_target_properties(@CMAKE_PROJECT_NAME@::jpeg12 PROPERTIES + IMPORTED_IMPLIB_RELEASE "${_IMPORT_PREFIX}/lib/libjpeg12.dll.a" + IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/libjpeg12-62.dll" + ) + +list(APPEND _IMPORT_CHECK_TARGETS @CMAKE_PROJECT_NAME@::jpeg12 ) +list(APPEND _IMPORT_CHECK_FILES_FOR_@CMAKE_PROJECT_NAME@::jpeg12 "${_IMPORT_PREFIX}/lib/libjpeg12.dll.a" "${_IMPORT_PREFIX}/bin/libjpeg12-62.dll" ) + # Import target "@CMAKE_PROJECT_NAME@::turbojpeg" for configuration "Release" set_property(TARGET @CMAKE_PROJECT_NAME@::turbojpeg APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) set_target_properties(@CMAKE_PROJECT_NAME@::turbojpeg PROPERTIES @@ -45,5 +55,15 @@ set_target_properties(@CMAKE_PROJECT_NAME@::jpeg-static PROPERTIES list(APPEND _IMPORT_CHECK_TARGETS @CMAKE_PROJECT_NAME@::jpeg-static ) list(APPEND _IMPORT_CHECK_FILES_FOR_@CMAKE_PROJECT_NAME@::jpeg-static "${_IMPORT_PREFIX}/lib/libjpeg.a" ) +# Import target "@CMAKE_PROJECT_NAME@::jpeg12-static" for configuration "Release" +set_property(TARGET @CMAKE_PROJECT_NAME@::jpeg12-static APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) +set_target_properties(@CMAKE_PROJECT_NAME@::jpeg12-static PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C" + IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/libjpeg12.a" + ) + +list(APPEND _IMPORT_CHECK_TARGETS @CMAKE_PROJECT_NAME@::jpeg12-static ) +list(APPEND _IMPORT_CHECK_FILES_FOR_@CMAKE_PROJECT_NAME@::jpeg12-static "${_IMPORT_PREFIX}/lib/libjpeg12.a" ) + # Commands beyond this point should not need to know the version. set(CMAKE_IMPORT_FILE_VERSION) diff --git a/win/jconfig.h.in b/win/jconfig.h.in deleted file mode 100644 index 0fca77b23..000000000 --- a/win/jconfig.h.in +++ /dev/null @@ -1,25 +0,0 @@ -#define JPEG_LIB_VERSION @JPEG_LIB_VERSION@ -#define LIBJPEG_TURBO_VERSION @VERSION@ -#define LIBJPEG_TURBO_VERSION_NUMBER @LIBJPEG_TURBO_VERSION_NUMBER@ - -#cmakedefine C_ARITH_CODING_SUPPORTED -#cmakedefine D_ARITH_CODING_SUPPORTED -#cmakedefine MEM_SRCDST_SUPPORTED -#cmakedefine WITH_SIMD - -#define BITS_IN_JSAMPLE @BITS_IN_JSAMPLE@ /* use 8 or 12 */ - -#undef RIGHT_SHIFT_IS_UNSIGNED - -/* Define "boolean" as unsigned char, not int, per Windows custom */ -#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */ -typedef unsigned char boolean; -#endif -#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */ - -/* Define "INT32" as int, not long, per Windows custom */ -#if !(defined(_BASETSD_H_) || defined(_BASETSD_H)) /* don't conflict if basetsd.h already read */ -typedef short INT16; -typedef signed int INT32; -#endif -#define XMD_H /* prevent jmorecfg.h from redefining it */ diff --git a/win/jpeg12-62-memsrcdst.def b/win/jpeg12-62-memsrcdst.def new file mode 100644 index 000000000..aaaa536ec --- /dev/null +++ b/win/jpeg12-62-memsrcdst.def @@ -0,0 +1,108 @@ +EXPORTS + j12copy_block_row @ 1 ; + j12copy_sample_rows @ 2 ; + j12div_round_up @ 3 ; + j12init_1pass_quantizer @ 4 ; + j12init_2pass_quantizer @ 5 ; + j12init_c_coef_controller @ 6 ; + j12init_c_main_controller @ 7 ; + j12init_c_master_control @ 8 ; + j12init_c_prep_controller @ 9 ; + j12init_color_converter @ 10 ; + j12init_color_deconverter @ 11 ; + j12init_compress_master @ 12 ; + j12init_d_coef_controller @ 13 ; + j12init_d_main_controller @ 14 ; + j12init_d_post_controller @ 15 ; + j12init_downsampler @ 16 ; + j12init_forward_dct @ 17 ; + j12init_huff_decoder @ 18 ; + j12init_huff_encoder @ 19 ; + j12init_input_controller @ 20 ; + j12init_inverse_dct @ 21 ; + j12init_marker_reader @ 22 ; + j12init_marker_writer @ 23 ; + j12init_master_decompress @ 24 ; + j12init_memory_mgr @ 25 ; + j12init_merged_upsampler @ 26 ; + j12init_phuff_decoder @ 27 ; + j12init_phuff_encoder @ 28 ; + j12init_upsampler @ 29 ; + jpeg12_CreateCompress @ 30 ; + jpeg12_CreateDecompress @ 31 ; + jpeg12_abort @ 32 ; + jpeg12_abort_compress @ 33 ; + jpeg12_abort_decompress @ 34 ; + jpeg12_add_quant_table @ 35 ; + jpeg12_alloc_huff_table @ 36 ; + jpeg12_alloc_quant_table @ 37 ; + jpeg12_calc_output_dimensions @ 38 ; + jpeg12_consume_input @ 39 ; + jpeg12_copy_critical_parameters @ 40 ; + jpeg12_default_colorspace @ 41 ; + jpeg12_destroy @ 42 ; + jpeg12_destroy_compress @ 43 ; + jpeg12_destroy_decompress @ 44 ; + jpeg12_fdct_float @ 45 ; + jpeg12_fdct_ifast @ 46 ; + jpeg12_fdct_islow @ 47 ; + jpeg12_fill_bit_buffer @ 48 ; + jpeg12_finish_compress @ 49 ; + jpeg12_finish_decompress @ 50 ; + jpeg12_finish_output @ 51 ; + jpeg12_free_large @ 52 ; + jpeg12_free_small @ 53 ; + jpeg12_gen_optimal_table @ 54 ; + jpeg12_get_large @ 55 ; + jpeg12_get_small @ 56 ; + jpeg12_has_multiple_scans @ 57 ; + jpeg12_huff_decode @ 58 ; + jpeg12_idct_1x1 @ 59 ; + jpeg12_idct_2x2 @ 60 ; + jpeg12_idct_4x4 @ 61 ; + jpeg12_idct_float @ 62 ; + jpeg12_idct_ifast @ 63 ; + jpeg12_idct_islow @ 64 ; + jpeg12_input_complete @ 65 ; + jpeg12_make_c_derived_tbl @ 66 ; + jpeg12_make_d_derived_tbl @ 67 ; + jpeg12_mem_available @ 68 ; + jpeg12_mem_init @ 69 ; + jpeg12_mem_term @ 70 ; + jpeg12_new_colormap @ 71 ; + jpeg12_open_backing_store @ 72 ; + jpeg12_quality_scaling @ 73 ; + jpeg12_read_coefficients @ 74 ; + jpeg12_read_header @ 75 ; + jpeg12_read_raw_data @ 76 ; + jpeg12_read_scanlines @ 77 ; + jpeg12_resync_to_restart @ 78 ; + jpeg12_save_markers @ 79 ; + jpeg12_set_colorspace @ 80 ; + jpeg12_set_defaults @ 81 ; + jpeg12_set_linear_quality @ 82 ; + jpeg12_set_marker_processor @ 83 ; + jpeg12_set_quality @ 84 ; + jpeg12_simple_progression @ 85 ; + jpeg12_start_compress @ 86 ; + jpeg12_start_decompress @ 87 ; + jpeg12_start_output @ 88 ; + jpeg12_std_error @ 89 ; + jpeg12_stdio_dest @ 90 ; + jpeg12_stdio_src @ 91 ; + jpeg12_suppress_tables @ 92 ; + jpeg12_write_coefficients @ 93 ; + jpeg12_write_m_byte @ 94 ; + jpeg12_write_m_header @ 95 ; + jpeg12_write_marker @ 96 ; + jpeg12_write_raw_data @ 97 ; + jpeg12_write_scanlines @ 98 ; + jpeg12_write_tables @ 99 ; + j12round_up @ 100 ; + j12zero_far @ 101 ; + jpeg12_mem_dest @ 102 ; + jpeg12_mem_src @ 103 ; + jpeg12_skip_scanlines @ 104 ; + jpeg12_crop_scanline @ 105 ; + jpeg12_read_icc_profile @ 106 ; + jpeg12_write_icc_profile @ 107 ; diff --git a/win/jpeg12-62.def b/win/jpeg12-62.def new file mode 100644 index 000000000..cc33eea6d --- /dev/null +++ b/win/jpeg12-62.def @@ -0,0 +1,106 @@ +EXPORTS + j12copy_block_row @ 1 ; + j12copy_sample_rows @ 2 ; + j12div_round_up @ 3 ; + j12init_1pass_quantizer @ 4 ; + j12init_2pass_quantizer @ 5 ; + j12init_c_coef_controller @ 6 ; + j12init_c_main_controller @ 7 ; + j12init_c_master_control @ 8 ; + j12init_c_prep_controller @ 9 ; + j12init_color_converter @ 10 ; + j12init_color_deconverter @ 11 ; + j12init_compress_master @ 12 ; + j12init_d_coef_controller @ 13 ; + j12init_d_main_controller @ 14 ; + j12init_d_post_controller @ 15 ; + j12init_downsampler @ 16 ; + j12init_forward_dct @ 17 ; + j12init_huff_decoder @ 18 ; + j12init_huff_encoder @ 19 ; + j12init_input_controller @ 20 ; + j12init_inverse_dct @ 21 ; + j12init_marker_reader @ 22 ; + j12init_marker_writer @ 23 ; + j12init_master_decompress @ 24 ; + j12init_memory_mgr @ 25 ; + j12init_merged_upsampler @ 26 ; + j12init_phuff_decoder @ 27 ; + j12init_phuff_encoder @ 28 ; + j12init_upsampler @ 29 ; + jpeg12_CreateCompress @ 30 ; + jpeg12_CreateDecompress @ 31 ; + jpeg12_abort @ 32 ; + jpeg12_abort_compress @ 33 ; + jpeg12_abort_decompress @ 34 ; + jpeg12_add_quant_table @ 35 ; + jpeg12_alloc_huff_table @ 36 ; + jpeg12_alloc_quant_table @ 37 ; + jpeg12_calc_output_dimensions @ 38 ; + jpeg12_consume_input @ 39 ; + jpeg12_copy_critical_parameters @ 40 ; + jpeg12_default_colorspace @ 41 ; + jpeg12_destroy @ 42 ; + jpeg12_destroy_compress @ 43 ; + jpeg12_destroy_decompress @ 44 ; + jpeg12_fdct_float @ 45 ; + jpeg12_fdct_ifast @ 46 ; + jpeg12_fdct_islow @ 47 ; + jpeg12_fill_bit_buffer @ 48 ; + jpeg12_finish_compress @ 49 ; + jpeg12_finish_decompress @ 50 ; + jpeg12_finish_output @ 51 ; + jpeg12_free_large @ 52 ; + jpeg12_free_small @ 53 ; + jpeg12_gen_optimal_table @ 54 ; + jpeg12_get_large @ 55 ; + jpeg12_get_small @ 56 ; + jpeg12_has_multiple_scans @ 57 ; + jpeg12_huff_decode @ 58 ; + jpeg12_idct_1x1 @ 59 ; + jpeg12_idct_2x2 @ 60 ; + jpeg12_idct_4x4 @ 61 ; + jpeg12_idct_float @ 62 ; + jpeg12_idct_ifast @ 63 ; + jpeg12_idct_islow @ 64 ; + jpeg12_input_complete @ 65 ; + jpeg12_make_c_derived_tbl @ 66 ; + jpeg12_make_d_derived_tbl @ 67 ; + jpeg12_mem_available @ 68 ; + jpeg12_mem_init @ 69 ; + jpeg12_mem_term @ 70 ; + jpeg12_new_colormap @ 71 ; + jpeg12_open_backing_store @ 72 ; + jpeg12_quality_scaling @ 73 ; + jpeg12_read_coefficients @ 74 ; + jpeg12_read_header @ 75 ; + jpeg12_read_raw_data @ 76 ; + jpeg12_read_scanlines @ 77 ; + jpeg12_resync_to_restart @ 78 ; + jpeg12_save_markers @ 79 ; + jpeg12_set_colorspace @ 80 ; + jpeg12_set_defaults @ 81 ; + jpeg12_set_linear_quality @ 82 ; + jpeg12_set_marker_processor @ 83 ; + jpeg12_set_quality @ 84 ; + jpeg12_simple_progression @ 85 ; + jpeg12_start_compress @ 86 ; + jpeg12_start_decompress @ 87 ; + jpeg12_start_output @ 88 ; + jpeg12_std_error @ 89 ; + jpeg12_stdio_dest @ 90 ; + jpeg12_stdio_src @ 91 ; + jpeg12_suppress_tables @ 92 ; + jpeg12_write_coefficients @ 93 ; + jpeg12_write_m_byte @ 94 ; + jpeg12_write_m_header @ 95 ; + jpeg12_write_marker @ 96 ; + jpeg12_write_raw_data @ 97 ; + jpeg12_write_scanlines @ 98 ; + jpeg12_write_tables @ 99 ; + j12round_up @ 100 ; + j12zero_far @ 101 ; + jpeg12_skip_scanlines @ 102 ; + jpeg12_crop_scanline @ 103 ; + jpeg12_read_icc_profile @ 104 ; + jpeg12_write_icc_profile @ 105 ; diff --git a/win/jpeg12-7-memsrcdst.def b/win/jpeg12-7-memsrcdst.def new file mode 100644 index 000000000..a05861b4b --- /dev/null +++ b/win/jpeg12-7-memsrcdst.def @@ -0,0 +1,110 @@ +EXPORTS + j12copy_block_row @ 1 ; + j12copy_sample_rows @ 2 ; + j12div_round_up @ 3 ; + j12init_1pass_quantizer @ 4 ; + j12init_2pass_quantizer @ 5 ; + j12init_c_coef_controller @ 6 ; + j12init_c_main_controller @ 7 ; + j12init_c_master_control @ 8 ; + j12init_c_prep_controller @ 9 ; + j12init_color_converter @ 10 ; + j12init_color_deconverter @ 11 ; + j12init_compress_master @ 12 ; + j12init_d_coef_controller @ 13 ; + j12init_d_main_controller @ 14 ; + j12init_d_post_controller @ 15 ; + j12init_downsampler @ 16 ; + j12init_forward_dct @ 17 ; + j12init_huff_decoder @ 18 ; + j12init_huff_encoder @ 19 ; + j12init_input_controller @ 20 ; + j12init_inverse_dct @ 21 ; + j12init_marker_reader @ 22 ; + j12init_marker_writer @ 23 ; + j12init_master_decompress @ 24 ; + j12init_memory_mgr @ 25 ; + j12init_merged_upsampler @ 26 ; + j12init_phuff_decoder @ 27 ; + j12init_phuff_encoder @ 28 ; + j12init_upsampler @ 29 ; + jpeg12_CreateCompress @ 30 ; + jpeg12_CreateDecompress @ 31 ; + jpeg12_abort @ 32 ; + jpeg12_abort_compress @ 33 ; + jpeg12_abort_decompress @ 34 ; + jpeg12_add_quant_table @ 35 ; + jpeg12_alloc_huff_table @ 36 ; + jpeg12_alloc_quant_table @ 37 ; + jpeg12_calc_jpeg_dimensions @ 38 ; + jpeg12_calc_output_dimensions @ 39 ; + jpeg12_consume_input @ 40 ; + jpeg12_copy_critical_parameters @ 41 ; + jpeg12_default_colorspace @ 42 ; + jpeg12_default_qtables @ 43 ; + jpeg12_destroy @ 44 ; + jpeg12_destroy_compress @ 45 ; + jpeg12_destroy_decompress @ 46 ; + jpeg12_fdct_float @ 47 ; + jpeg12_fdct_ifast @ 48 ; + jpeg12_fdct_islow @ 49 ; + jpeg12_fill_bit_buffer @ 50 ; + jpeg12_finish_compress @ 51 ; + jpeg12_finish_decompress @ 52 ; + jpeg12_finish_output @ 53 ; + jpeg12_free_large @ 54 ; + jpeg12_free_small @ 55 ; + jpeg12_gen_optimal_table @ 56 ; + jpeg12_get_large @ 57 ; + jpeg12_get_small @ 58 ; + jpeg12_has_multiple_scans @ 59 ; + jpeg12_huff_decode @ 60 ; + jpeg12_idct_1x1 @ 61 ; + jpeg12_idct_2x2 @ 62 ; + jpeg12_idct_4x4 @ 63 ; + jpeg12_idct_float @ 64 ; + jpeg12_idct_ifast @ 65 ; + jpeg12_idct_islow @ 66 ; + jpeg12_input_complete @ 67 ; + jpeg12_make_c_derived_tbl @ 68 ; + jpeg12_make_d_derived_tbl @ 69 ; + jpeg12_mem_available @ 70 ; + jpeg12_mem_init @ 71 ; + jpeg12_mem_term @ 72 ; + jpeg12_new_colormap @ 73 ; + jpeg12_open_backing_store @ 74 ; + jpeg12_quality_scaling @ 75 ; + jpeg12_read_coefficients @ 76 ; + jpeg12_read_header @ 77 ; + jpeg12_read_raw_data @ 78 ; + jpeg12_read_scanlines @ 79 ; + jpeg12_resync_to_restart @ 80 ; + jpeg12_save_markers @ 81 ; + jpeg12_set_colorspace @ 82 ; + jpeg12_set_defaults @ 83 ; + jpeg12_set_linear_quality @ 84 ; + jpeg12_set_marker_processor @ 85 ; + jpeg12_set_quality @ 86 ; + jpeg12_simple_progression @ 87 ; + jpeg12_start_compress @ 88 ; + jpeg12_start_decompress @ 89 ; + jpeg12_start_output @ 90 ; + jpeg12_std_error @ 91 ; + jpeg12_stdio_dest @ 92 ; + jpeg12_stdio_src @ 93 ; + jpeg12_suppress_tables @ 94 ; + jpeg12_write_coefficients @ 95 ; + jpeg12_write_m_byte @ 96 ; + jpeg12_write_m_header @ 97 ; + jpeg12_write_marker @ 98 ; + jpeg12_write_raw_data @ 99 ; + jpeg12_write_scanlines @ 100 ; + jpeg12_write_tables @ 101 ; + j12round_up @ 102 ; + j12zero_far @ 103 ; + jpeg12_mem_dest @ 104 ; + jpeg12_mem_src @ 105 ; + jpeg12_skip_scanlines @ 106 ; + jpeg12_crop_scanline @ 107 ; + jpeg12_read_icc_profile @ 108 ; + jpeg12_write_icc_profile @ 109 ; diff --git a/win/jpeg12-7.def b/win/jpeg12-7.def new file mode 100644 index 000000000..ac4e64ba0 --- /dev/null +++ b/win/jpeg12-7.def @@ -0,0 +1,108 @@ +EXPORTS + j12copy_block_row @ 1 ; + j12copy_sample_rows @ 2 ; + j12div_round_up @ 3 ; + j12init_1pass_quantizer @ 4 ; + j12init_2pass_quantizer @ 5 ; + j12init_c_coef_controller @ 6 ; + j12init_c_main_controller @ 7 ; + j12init_c_master_control @ 8 ; + j12init_c_prep_controller @ 9 ; + j12init_color_converter @ 10 ; + j12init_color_deconverter @ 11 ; + j12init_compress_master @ 12 ; + j12init_d_coef_controller @ 13 ; + j12init_d_main_controller @ 14 ; + j12init_d_post_controller @ 15 ; + j12init_downsampler @ 16 ; + j12init_forward_dct @ 17 ; + j12init_huff_decoder @ 18 ; + j12init_huff_encoder @ 19 ; + j12init_input_controller @ 20 ; + j12init_inverse_dct @ 21 ; + j12init_marker_reader @ 22 ; + j12init_marker_writer @ 23 ; + j12init_master_decompress @ 24 ; + j12init_memory_mgr @ 25 ; + j12init_merged_upsampler @ 26 ; + j12init_phuff_decoder @ 27 ; + j12init_phuff_encoder @ 28 ; + j12init_upsampler @ 29 ; + jpeg12_CreateCompress @ 30 ; + jpeg12_CreateDecompress @ 31 ; + jpeg12_abort @ 32 ; + jpeg12_abort_compress @ 33 ; + jpeg12_abort_decompress @ 34 ; + jpeg12_add_quant_table @ 35 ; + jpeg12_alloc_huff_table @ 36 ; + jpeg12_alloc_quant_table @ 37 ; + jpeg12_calc_jpeg_dimensions @ 38 ; + jpeg12_calc_output_dimensions @ 39 ; + jpeg12_consume_input @ 40 ; + jpeg12_copy_critical_parameters @ 41 ; + jpeg12_default_colorspace @ 42 ; + jpeg12_default_qtables @ 43 ; + jpeg12_destroy @ 44 ; + jpeg12_destroy_compress @ 45 ; + jpeg12_destroy_decompress @ 46 ; + jpeg12_fdct_float @ 47 ; + jpeg12_fdct_ifast @ 48 ; + jpeg12_fdct_islow @ 49 ; + jpeg12_fill_bit_buffer @ 50 ; + jpeg12_finish_compress @ 51 ; + jpeg12_finish_decompress @ 52 ; + jpeg12_finish_output @ 53 ; + jpeg12_free_large @ 54 ; + jpeg12_free_small @ 55 ; + jpeg12_gen_optimal_table @ 56 ; + jpeg12_get_large @ 57 ; + jpeg12_get_small @ 58 ; + jpeg12_has_multiple_scans @ 59 ; + jpeg12_huff_decode @ 60 ; + jpeg12_idct_1x1 @ 61 ; + jpeg12_idct_2x2 @ 62 ; + jpeg12_idct_4x4 @ 63 ; + jpeg12_idct_float @ 64 ; + jpeg12_idct_ifast @ 65 ; + jpeg12_idct_islow @ 66 ; + jpeg12_input_complete @ 67 ; + jpeg12_make_c_derived_tbl @ 68 ; + jpeg12_make_d_derived_tbl @ 69 ; + jpeg12_mem_available @ 70 ; + jpeg12_mem_init @ 71 ; + jpeg12_mem_term @ 72 ; + jpeg12_new_colormap @ 73 ; + jpeg12_open_backing_store @ 74 ; + jpeg12_quality_scaling @ 75 ; + jpeg12_read_coefficients @ 76 ; + jpeg12_read_header @ 77 ; + jpeg12_read_raw_data @ 78 ; + jpeg12_read_scanlines @ 79 ; + jpeg12_resync_to_restart @ 80 ; + jpeg12_save_markers @ 81 ; + jpeg12_set_colorspace @ 82 ; + jpeg12_set_defaults @ 83 ; + jpeg12_set_linear_quality @ 84 ; + jpeg12_set_marker_processor @ 85 ; + jpeg12_set_quality @ 86 ; + jpeg12_simple_progression @ 87 ; + jpeg12_start_compress @ 88 ; + jpeg12_start_decompress @ 89 ; + jpeg12_start_output @ 90 ; + jpeg12_std_error @ 91 ; + jpeg12_stdio_dest @ 92 ; + jpeg12_stdio_src @ 93 ; + jpeg12_suppress_tables @ 94 ; + jpeg12_write_coefficients @ 95 ; + jpeg12_write_m_byte @ 96 ; + jpeg12_write_m_header @ 97 ; + jpeg12_write_marker @ 98 ; + jpeg12_write_raw_data @ 99 ; + jpeg12_write_scanlines @ 100 ; + jpeg12_write_tables @ 101 ; + j12round_up @ 102 ; + j12zero_far @ 103 ; + jpeg12_skip_scanlines @ 104 ; + jpeg12_crop_scanline @ 105 ; + jpeg12_read_icc_profile @ 106 ; + jpeg12_write_icc_profile @ 107 ; diff --git a/win/jpeg12-8.def b/win/jpeg12-8.def new file mode 100644 index 000000000..71fc4f81c --- /dev/null +++ b/win/jpeg12-8.def @@ -0,0 +1,111 @@ +EXPORTS + j12copy_block_row @ 1 ; + j12copy_sample_rows @ 2 ; + j12div_round_up @ 3 ; + j12init_1pass_quantizer @ 4 ; + j12init_2pass_quantizer @ 5 ; + j12init_c_coef_controller @ 6 ; + j12init_c_main_controller @ 7 ; + j12init_c_master_control @ 8 ; + j12init_c_prep_controller @ 9 ; + j12init_color_converter @ 10 ; + j12init_color_deconverter @ 11 ; + j12init_compress_master @ 12 ; + j12init_d_coef_controller @ 13 ; + j12init_d_main_controller @ 14 ; + j12init_d_post_controller @ 15 ; + j12init_downsampler @ 16 ; + j12init_forward_dct @ 17 ; + j12init_huff_decoder @ 18 ; + j12init_huff_encoder @ 19 ; + j12init_input_controller @ 20 ; + j12init_inverse_dct @ 21 ; + j12init_marker_reader @ 22 ; + j12init_marker_writer @ 23 ; + j12init_master_decompress @ 24 ; + j12init_memory_mgr @ 25 ; + j12init_merged_upsampler @ 26 ; + j12init_phuff_decoder @ 27 ; + j12init_phuff_encoder @ 28 ; + j12init_upsampler @ 29 ; + jpeg12_CreateCompress @ 30 ; + jpeg12_CreateDecompress @ 31 ; + jpeg12_abort @ 32 ; + jpeg12_abort_compress @ 33 ; + jpeg12_abort_decompress @ 34 ; + jpeg12_add_quant_table @ 35 ; + jpeg12_alloc_huff_table @ 36 ; + jpeg12_alloc_quant_table @ 37 ; + jpeg12_calc_jpeg_dimensions @ 38 ; + jpeg12_calc_output_dimensions @ 39 ; + jpeg12_consume_input @ 40 ; + jpeg12_copy_critical_parameters @ 41 ; + jpeg12_core_output_dimensions @ 42 ; + jpeg12_default_colorspace @ 43 ; + jpeg12_default_qtables @ 44 ; + jpeg12_destroy @ 45 ; + jpeg12_destroy_compress @ 46 ; + jpeg12_destroy_decompress @ 47 ; + jpeg12_fdct_float @ 48 ; + jpeg12_fdct_ifast @ 49 ; + jpeg12_fdct_islow @ 50 ; + jpeg12_fill_bit_buffer @ 51 ; + jpeg12_finish_compress @ 52 ; + jpeg12_finish_decompress @ 53 ; + jpeg12_finish_output @ 54 ; + jpeg12_free_large @ 55 ; + jpeg12_free_small @ 56 ; + jpeg12_gen_optimal_table @ 57 ; + jpeg12_get_large @ 58 ; + jpeg12_get_small @ 59 ; + jpeg12_has_multiple_scans @ 60 ; + jpeg12_huff_decode @ 61 ; + jpeg12_idct_1x1 @ 62 ; + jpeg12_idct_2x2 @ 63 ; + jpeg12_idct_4x4 @ 64 ; + jpeg12_idct_float @ 65 ; + jpeg12_idct_ifast @ 66 ; + jpeg12_idct_islow @ 67 ; + jpeg12_input_complete @ 68 ; + jpeg12_make_c_derived_tbl @ 69 ; + jpeg12_make_d_derived_tbl @ 70 ; + jpeg12_mem_available @ 71 ; + jpeg12_mem_dest @ 72 ; + jpeg12_mem_init @ 73 ; + jpeg12_mem_src @ 74 ; + jpeg12_mem_term @ 75 ; + jpeg12_new_colormap @ 76 ; + jpeg12_open_backing_store @ 77 ; + jpeg12_quality_scaling @ 78 ; + jpeg12_read_coefficients @ 79 ; + jpeg12_read_header @ 80 ; + jpeg12_read_raw_data @ 81 ; + jpeg12_read_scanlines @ 82 ; + jpeg12_resync_to_restart @ 83 ; + jpeg12_save_markers @ 84 ; + jpeg12_set_colorspace @ 85 ; + jpeg12_set_defaults @ 86 ; + jpeg12_set_linear_quality @ 87 ; + jpeg12_set_marker_processor @ 88 ; + jpeg12_set_quality @ 89 ; + jpeg12_simple_progression @ 90 ; + jpeg12_start_compress @ 91 ; + jpeg12_start_decompress @ 92 ; + jpeg12_start_output @ 93 ; + jpeg12_std_error @ 94 ; + jpeg12_stdio_dest @ 95 ; + jpeg12_stdio_src @ 96 ; + jpeg12_suppress_tables @ 97 ; + jpeg12_write_coefficients @ 98 ; + jpeg12_write_m_byte @ 99 ; + jpeg12_write_m_header @ 100 ; + jpeg12_write_marker @ 101 ; + jpeg12_write_raw_data @ 102 ; + jpeg12_write_scanlines @ 103 ; + jpeg12_write_tables @ 104 ; + j12round_up @ 105 ; + j12zero_far @ 106 ; + jpeg12_skip_scanlines @ 107 ; + jpeg12_crop_scanline @ 108 ; + jpeg12_read_icc_profile @ 109 ; + jpeg12_write_icc_profile @ 110 ; diff --git a/win/vc/projectTargets-release.cmake.in b/win/vc/projectTargets-release.cmake.in index 7abb281b7..80f159642 100644 --- a/win/vc/projectTargets-release.cmake.in +++ b/win/vc/projectTargets-release.cmake.in @@ -15,6 +15,16 @@ set_target_properties(@CMAKE_PROJECT_NAME@::jpeg PROPERTIES list(APPEND _IMPORT_CHECK_TARGETS @CMAKE_PROJECT_NAME@::jpeg ) list(APPEND _IMPORT_CHECK_FILES_FOR_@CMAKE_PROJECT_NAME@::jpeg "${_IMPORT_PREFIX}/lib/jpeg.lib" "${_IMPORT_PREFIX}/bin/jpeg62.dll" ) +# Import target "@CMAKE_PROJECT_NAME@::jpeg12" for configuration "Release" +set_property(TARGET @CMAKE_PROJECT_NAME@::jpeg12 APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) +set_target_properties(@CMAKE_PROJECT_NAME@::jpeg12 PROPERTIES + IMPORTED_IMPLIB_RELEASE "${_IMPORT_PREFIX}/lib/jpeg12.lib" + IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/jpeg12-62.dll" + ) + +list(APPEND _IMPORT_CHECK_TARGETS @CMAKE_PROJECT_NAME@::jpeg12 ) +list(APPEND _IMPORT_CHECK_FILES_FOR_@CMAKE_PROJECT_NAME@::jpeg12 "${_IMPORT_PREFIX}/lib/jpeg12.lib" "${_IMPORT_PREFIX}/bin/jpeg12-62.dll" ) + # Import target "@CMAKE_PROJECT_NAME@::turbojpeg" for configuration "Release" set_property(TARGET @CMAKE_PROJECT_NAME@::turbojpeg APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) set_target_properties(@CMAKE_PROJECT_NAME@::turbojpeg PROPERTIES @@ -45,5 +55,15 @@ set_target_properties(@CMAKE_PROJECT_NAME@::jpeg-static PROPERTIES list(APPEND _IMPORT_CHECK_TARGETS @CMAKE_PROJECT_NAME@::jpeg-static ) list(APPEND _IMPORT_CHECK_FILES_FOR_@CMAKE_PROJECT_NAME@::jpeg-static "${_IMPORT_PREFIX}/lib/jpeg-static.lib" ) +# Import target "@CMAKE_PROJECT_NAME@::jpeg12-static" for configuration "Release" +set_property(TARGET @CMAKE_PROJECT_NAME@::jpeg12-static APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) +set_target_properties(@CMAKE_PROJECT_NAME@::jpeg12-static PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C" + IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/jpeg12-static.lib" + ) + +list(APPEND _IMPORT_CHECK_TARGETS @CMAKE_PROJECT_NAME@::jpeg12-static ) +list(APPEND _IMPORT_CHECK_FILES_FOR_@CMAKE_PROJECT_NAME@::jpeg12-static "${_IMPORT_PREFIX}/lib/jpeg12-static.lib" ) + # Commands beyond this point should not need to know the version. set(CMAKE_IMPORT_FILE_VERSION) From a32038e3b38bf21ee413dda0eee54d263983f852 Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 10 Mar 2022 23:10:12 -0600 Subject: [PATCH 003/162] Fix formatting issues detected by checkstyle --- example.c | 3 +-- jmorecfg.h | 8 ++++---- jpeglibint.h | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/example.c b/example.c index f795aea01..c0bdc39d0 100644 --- a/example.c +++ b/example.c @@ -737,9 +737,8 @@ main(int argc, char **argv) arg++; /* advance past switch marker character */ #ifdef WITH_12BIT - if (!strncasecmp(arg, "1", 1)) { + if (!strncasecmp(arg, "1", 1)) _12bit = 1; - } else #endif if (!strncasecmp(arg, "q", 1)) { diff --git a/jmorecfg.h b/jmorecfg.h index 16b091e3c..8681af423 100644 --- a/jmorecfg.h +++ b/jmorecfg.h @@ -50,8 +50,8 @@ typedef unsigned char JSAMPLE; #define GETJSAMPLE(value) ((int)(value)) -#define MAXJSAMPLE 255 -#define CENTERJSAMPLE 128 +#define MAXJSAMPLE 255 +#define CENTERJSAMPLE 128 /* J12SAMPLE should be the smallest type that will hold the values 0..4095. @@ -60,8 +60,8 @@ typedef unsigned char JSAMPLE; typedef short J12SAMPLE; -#define MAXJ12SAMPLE 4095 -#define CENTERJ12SAMPLE 2048 +#define MAXJ12SAMPLE 4095 +#define CENTERJ12SAMPLE 2048 /* Representation of a DCT frequency coefficient. diff --git a/jpeglibint.h b/jpeglibint.h index 6aac021f6..4f11f6a77 100644 --- a/jpeglibint.h +++ b/jpeglibint.h @@ -197,7 +197,7 @@ #define jinit_memory_mgr j12init_memory_mgr #define jdiv_round_up j12div_round_up #define jround_up j12round_up -#define jcopy_sample_rows j12copy_sample_rows +#define jcopy_sample_rows j12copy_sample_rows #define jcopy_block_row j12copy_block_row #define jzero_far j12zero_far From b3ae7779647591f1b82635eddfa519b23d7d9c51 Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 10 Mar 2022 23:13:43 -0600 Subject: [PATCH 004/162] Fix in-tree builds (oops) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 42a35e60c..c10209cbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -709,7 +709,7 @@ if(ENABLE_STATIC) "-DBITS_IN_JSAMPLE=12 ${USE_SETMODE}") endif() - add_executable(example-static ../example.c) + add_executable(example-static example.c) if(WITH_12BIT) target_link_libraries(example-static jpeg-static jpeg12-static) set_property(TARGET example-static PROPERTY COMPILE_FLAGS "-DWITH_12BIT") From ebd1930207615f7ae900ac108b84e1ae6683ca81 Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 10 Mar 2022 23:21:55 -0600 Subject: [PATCH 005/162] GitHub Actions: "linux-12bit" --> "linux-no12bit" This job tests the non-default value of WITH_12BIT, which is now 0 instead of 1. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c11131a29..e0336ad95 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -108,7 +108,7 @@ jobs: cmake -DFLOATTEST=no-fp-contract .. JSIMD_FORCENONE=1 make test popd - linux-12bit: + linux-no12bit: runs-on: ubuntu-latest steps: - name: Check out code From cb715fe1ae4a9f8f9fbf69340b574d4af8d94851 Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 11 Mar 2022 17:12:37 -0600 Subject: [PATCH 006/162] example.c: Fix compiler warning with GCC 4.4.7 --- example.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example.c b/example.c index c0bdc39d0..08f08154b 100644 --- a/example.c +++ b/example.c @@ -714,7 +714,7 @@ main(int argc, char **argv) #ifdef WITH_12BIT int _12bit = 0; #endif - EXAMPLE_MODE mode; + EXAMPLE_MODE mode = -1; char *arg, *filename = NULL; if (argc < 3) From 1b9edb5cafe87ab62cf171b41981d13e39fa8d5c Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 10 Mar 2022 23:57:11 -0600 Subject: [PATCH 007/162] Build: Fix 12-bit FP tests w/ 32-bit builds With x86-64 builds, the default value of FLOATTEST works with both the 8-bit-per-sample and 12-bit-per-sample flavors of the libjpeg API library. However, that is not the case with x86 builds. Thus, we need separate 8-bit-per-sample and 12-bit-per-sample FLOATTEST variables. --- .github/workflows/build.yml | 6 +-- CMakeLists.txt | 82 +++++++++++++++++++++++++------------ 2 files changed, 59 insertions(+), 29 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e0336ad95..433e1b0e4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -105,7 +105,7 @@ jobs: make -j$NUMCPUS --load-average=$NUMCPUS make test JSIMD_FORCESSE2=1 make test - cmake -DFLOATTEST=no-fp-contract .. + cmake -DFLOATTEST8=no-fp-contract .. JSIMD_FORCENONE=1 make test popd linux-no12bit: @@ -148,7 +148,7 @@ jobs: make -j$NUMCPUS --load-average=$NUMCPUS make test JSIMD_FORCESSE2=1 make test - cmake -DFLOATTEST=no-fp-contract .. + cmake -DFLOATTEST8=no-fp-contract .. JSIMD_FORCENONE=1 make test popd linux-jpeg8: @@ -172,7 +172,7 @@ jobs: make -j$NUMCPUS --load-average=$NUMCPUS make test JSIMD_FORCESSE2=1 make test - cmake -DFLOATTEST=no-fp-contract .. + cmake -DFLOATTEST8=no-fp-contract .. JSIMD_FORCENONE=1 make test popd linux-msan: diff --git a/CMakeLists.txt b/CMakeLists.txt index ce1cbfb39..bf3d641f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -791,9 +791,9 @@ if(CMAKE_CROSSCOMPILING) endif() # The output of the floating point DCT/IDCT algorithms differs depending on the -# type of floating point math used, so the FLOATTEST CMake variable must be -# set in order to tell the testing system which floating point results it -# should expect: +# type of floating point math used, so the FLOATTEST8 and FLOATTEST12 CMake +# variables must be set in order to tell the testing system which floating +# point results it should expect: # # sse = validate against the expected results from the libjpeg-turbo SSE SIMD # extensions @@ -815,9 +815,9 @@ endif() if(CPU_TYPE STREQUAL "x86_64" OR CPU_TYPE STREQUAL "i386") if(WITH_SIMD) - set(DEFAULT_FLOATTEST sse) + set(DEFAULT_FLOATTEST8 sse) elseif(CPU_TYPE STREQUAL "x86_64") - set(DEFAULT_FLOATTEST no-fp-contract) + set(DEFAULT_FLOATTEST8 no-fp-contract) # else we can't really set an intelligent default for i386. The appropriate # value could be no-fp-contract, fp-contract, 387, or msvc, depending on the # compiler and compiler options. We leave it to the user to set FLOATTEST @@ -826,32 +826,62 @@ if(CPU_TYPE STREQUAL "x86_64" OR CPU_TYPE STREQUAL "i386") else() if((CPU_TYPE STREQUAL "powerpc" OR CPU_TYPE STREQUAL "arm64") AND NOT CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT MSVC) - set(DEFAULT_FLOATTEST fp-contract) + set(DEFAULT_FLOATTEST8 fp-contract) else() - set(DEFAULT_FLOATTEST no-fp-contract) + set(DEFAULT_FLOATTEST8 no-fp-contract) endif() endif() -# This causes FLOATTEST to reset to the default value if WITH_SIMD has +# This causes FLOATTEST8 to reset to the default value if WITH_SIMD has # changed. if(DEFINED WITH_SIMD_INT AND NOT WITH_SIMD EQUAL WITH_SIMD_INT) - set(FORCE_FLOATTEST "FORCE") + set(FORCE_FLOATTEST8 "FORCE") endif() set(WITH_SIMD_INT ${WITH_SIMD} CACHE INTERNAL "") -set(FLOATTEST ${DEFAULT_FLOATTEST} CACHE STRING - "The type of floating point math used by the floating point DCT/IDCT algorithms. This tells the testing system which numerical results it should expect from those tests. [sse = libjpeg-turbo x86/x86-64 SIMD extensions, no-fp-contract = generic FPU with floating point expression contraction disabled, fp-contract = generic FPU with floating point expression contraction enabled, 387 = 387 FPU, msvc = 32-bit Visual Studio] (default = ${DEFAULT_FLOATTEST})" - ${FORCE_FLOATTEST}) -message(STATUS "FLOATTEST = ${FLOATTEST}") - -if(FLOATTEST) - string(TOUPPER ${FLOATTEST} FLOATTEST_UC) - string(REGEX REPLACE "-" "_" FLOATTEST_UC ${FLOATTEST_UC}) - string(TOLOWER ${FLOATTEST} FLOATTEST) - if(NOT FLOATTEST STREQUAL "sse" AND - NOT FLOATTEST STREQUAL "no-fp-contract" AND - NOT FLOATTEST STREQUAL "fp-contract" AND NOT FLOATTEST STREQUAL "387" AND - NOT FLOATTEST STREQUAL "msvc") - message(FATAL_ERROR "\"${FLOATTEST}\" is not a valid value for FLOATTEST.") +set(FLOATTEST8 ${DEFAULT_FLOATTEST8} CACHE STRING + "The type of floating point math used by the 8-bit-per-sample floating point DCT/IDCT algorithms. This tells the testing system which numerical results it should expect from those tests. [sse = libjpeg-turbo x86/x86-64 SIMD extensions, no-fp-contract = generic FPU with floating point expression contraction disabled, fp-contract = generic FPU with floating point expression contraction enabled, 387 = 387 FPU, msvc = 32-bit Visual Studio] (default = ${DEFAULT_FLOATTEST8})" + ${FORCE_FLOATTEST8}) +message(STATUS "FLOATTEST8 = ${FLOATTEST8}") + +if(FLOATTEST8) + string(TOUPPER ${FLOATTEST8} FLOATTEST8_UC) + string(REGEX REPLACE "-" "_" FLOATTEST8_UC ${FLOATTEST8_UC}) + string(TOLOWER ${FLOATTEST8} FLOATTEST8) + if(NOT FLOATTEST8 STREQUAL "sse" AND + NOT FLOATTEST8 STREQUAL "no-fp-contract" AND + NOT FLOATTEST8 STREQUAL "fp-contract" AND NOT FLOATTEST8 STREQUAL "387" AND + NOT FLOATTEST8 STREQUAL "msvc") + message(FATAL_ERROR "\"${FLOATTEST8}\" is not a valid value for FLOATTEST8.") + endif() +endif() + +if(WITH_12BIT) + if(CPU_TYPE STREQUAL "x86_64") + set(DEFAULT_FLOATTEST12 no-fp-contract) + elseif(NOT CPU_TYPE STREQUAL "i386") + if((CPU_TYPE STREQUAL "powerpc" OR CPU_TYPE STREQUAL "arm64") AND + NOT CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT MSVC) + set(DEFAULT_FLOATTEST12 fp-contract) + else() + set(DEFAULT_FLOATTEST12 no-fp-contract) + endif() + endif() + + set(FLOATTEST12 ${DEFAULT_FLOATTEST12} CACHE STRING + "The type of floating point math used by the 12-bit-per-sample floating point DCT/IDCT algorithms. This tells the testing system which numerical results it should expect from those tests. [sse = libjpeg-turbo x86/x86-64 SIMD extensions, no-fp-contract = generic FPU with floating point expression contraction disabled, fp-contract = generic FPU with floating point expression contraction enabled, 387 = 387 FPU, msvc = 32-bit Visual Studio] (default = ${DEFAULT_FLOATTEST12})") + message(STATUS "FLOATTEST12 = ${FLOATTEST12}") + + if(FLOATTEST12) + string(TOUPPER ${FLOATTEST12} FLOATTEST12_UC) + string(REGEX REPLACE "-" "_" FLOATTEST12_UC ${FLOATTEST12_UC}) + string(TOLOWER ${FLOATTEST12} FLOATTEST12) + if(NOT FLOATTEST12 STREQUAL "sse" AND + NOT FLOATTEST12 STREQUAL "no-fp-contract" AND + NOT FLOATTEST12 STREQUAL "fp-contract" AND + NOT FLOATTEST12 STREQUAL "387" AND + NOT FLOATTEST12 STREQUAL "msvc") + message(FATAL_ERROR "\"${FLOATTEST12}\" is not a valid value for FLOATTEST12.") + endif() endif() endif() @@ -1231,16 +1261,16 @@ foreach(libtype ${TEST_LIBTYPES}) ${testout}_420s_ifast_opt.jpg ${TESTIMAGES}/testorig.ppm ${MD5_JPEG_420S_IFAST_OPT}) - if(FLOATTEST) + if(FLOATTEST${sample_bits}) # CC: RGB->YCC SAMP: fullsize/int FDCT: float ENT: prog huff add_bittest(${cjpeg} 3x2-float-prog "-sample;3x2;-dct;float;-prog" ${testout}_3x2_float_prog.jpg ${TESTIMAGES}/testorig.ppm - ${MD5_JPEG_3x2_FLOAT_PROG_${FLOATTEST_UC}}) + ${MD5_JPEG_3x2_FLOAT_PROG_${FLOATTEST${sample_bits}_UC}}) # CC: YCC->RGB SAMP: fullsize/int IDCT: float ENT: prog huff add_bittest(${djpeg} 3x2-float-prog "-dct;float" ${testout}_3x2_float.ppm ${testout}_3x2_float_prog.jpg - ${MD5_PPM_3x2_FLOAT_${FLOATTEST_UC}} + ${MD5_PPM_3x2_FLOAT_${FLOATTEST${sample_bits}_UC}} ${cjpeg}-${libtype}-3x2-float-prog) endif() From aadd60ae4965e409073efba5ea127718f0140e0b Mon Sep 17 00:00:00 2001 From: Felix Hanau Date: Wed, 25 May 2022 19:51:52 -0400 Subject: [PATCH 008/162] Speed up computation of optimal Huffman tables Closes #602 --- ChangeLog.md | 5 ++++ jchuff.c | 83 +++++++++++++++++++++++++++++++--------------------- 2 files changed, 54 insertions(+), 34 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index a3060d8f7..9af838b88 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -12,6 +12,11 @@ applications to more easily support both 8-bit-per-component and more details.) The `WITH_12BIT` CMake variable can be used to disable 12-bit-per-component JPEG support. +2. Significantly sped up the computation of optimal Huffman tables. This +speeds up the compression of tiny images by as much as 2x and provides a +noticeable speedup for images as large as 256x256 when using optimal Huffman +tables. + 2.1.4 ===== diff --git a/jchuff.c b/jchuff.c index bb9e14ce5..f47030468 100644 --- a/jchuff.c +++ b/jchuff.c @@ -8,6 +8,7 @@ * Copyright (C) 2015, Matthieu Darbois. * Copyright (C) 2018, Matthias Räncker. * Copyright (C) 2020, Arm Limited. + * Copyright (C) 2022, Felix Hanau. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -933,11 +934,13 @@ jpeg_gen_optimal_table(j_compress_ptr cinfo, JHUFF_TBL *htbl, long freq[]) { #define MAX_CLEN 32 /* assumed maximum initial code length */ UINT8 bits[MAX_CLEN + 1]; /* bits[k] = # of symbols with code length k */ + int bit_pos[MAX_CLEN + 1]; /* # of symbols with smaller code length */ int codesize[257]; /* codesize[k] = code length of symbol k */ int others[257]; /* next symbol in current branch of tree */ int c1, c2; int p, i, j; - long v; + int indices[257], num_symbols = 0; + long v, v2; /* This algorithm is explained in section K.2 of the JPEG standard */ @@ -947,6 +950,14 @@ jpeg_gen_optimal_table(j_compress_ptr cinfo, JHUFF_TBL *htbl, long freq[]) others[i] = -1; /* init links to empty */ freq[256] = 1; /* make sure 256 has a nonzero count */ + + for (i = 0; i < 257; i++) { + if (freq[i]) { + indices[num_symbols] = i; + freq[num_symbols] = freq[i]; + num_symbols++; + } + } /* 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. @@ -955,25 +966,23 @@ jpeg_gen_optimal_table(j_compress_ptr cinfo, JHUFF_TBL *htbl, long freq[]) /* Huffman's basic algorithm to assign optimal code lengths to symbols */ for (;;) { - /* Find the smallest nonzero frequency, set c1 = its symbol */ + /* Find the two smallest nonzero frequencies; set c1, c2 = their symbols */ /* In case of ties, take the larger symbol number */ c1 = -1; - v = 1000000000L; - for (i = 0; i <= 256; i++) { - if (freq[i] && freq[i] <= v) { - v = freq[i]; - c1 = i; - } - } - - /* Find the next smallest nonzero frequency, set c2 = its symbol */ - /* In case of ties, take the larger symbol number */ c2 = -1; v = 1000000000L; - for (i = 0; i <= 256; i++) { - if (freq[i] && freq[i] <= v && i != c1) { - v = freq[i]; - c2 = i; + v2 = 1000000000L; + for (i = 0; i < num_symbols; i++) { + if (freq[i] <= v2) { + if (freq[i] <= v) { + c2 = c1; + v2 = v; + v = freq[i]; + c1 = i; + } else { + v2 = freq[i]; + c2 = i; + } } } @@ -983,7 +992,9 @@ jpeg_gen_optimal_table(j_compress_ptr cinfo, JHUFF_TBL *htbl, long freq[]) /* Else merge the two counts/trees */ freq[c1] += freq[c2]; - freq[c2] = 0; + freq[c2] = 1000000001L; + c1 = indices[c1]; + c2 = indices[c2]; /* Increment the codesize of everything in c1's tree branch */ codesize[c1]++; @@ -1003,15 +1014,24 @@ jpeg_gen_optimal_table(j_compress_ptr cinfo, JHUFF_TBL *htbl, long freq[]) } /* Now count the number of symbols of each code length */ - for (i = 0; i <= 256; i++) { - if (codesize[i]) { - /* The JPEG standard seems to think that this can't happen, */ - /* but I'm paranoid... */ - if (codesize[i] > MAX_CLEN) - ERREXIT(cinfo, JERR_HUFF_CLEN_OVERFLOW); - - bits[codesize[i]]++; - } + for (i = 0; i < num_symbols; i++) { + /* The JPEG standard seems to think that this can't happen, */ + /* but I'm paranoid... */ + if (codesize[indices[i]] > MAX_CLEN) + ERREXIT(cinfo, JERR_HUFF_CLEN_OVERFLOW); + + bits[codesize[indices[i]]]++; + } + + /* Count the number of symbols with a length smaller than i bits, so we can + * construct the symbol table more efficiently. Note that this includes the + * pseudo-symbol 256, but since it is the last symbol, it will not affect the + * table. + */ + p = 0; + for (i = 1; i <= MAX_CLEN; i++) { + bit_pos[i] = p; + p += bits[i]; } /* JPEG doesn't allow symbols with code lengths over 16 bits, so if the pure @@ -1051,14 +1071,9 @@ jpeg_gen_optimal_table(j_compress_ptr cinfo, JHUFF_TBL *htbl, long freq[]) * changes made above, but Rec. ITU-T T.81 | ISO/IEC 10918-1 seems to think * this works. */ - p = 0; - for (i = 1; i <= MAX_CLEN; i++) { - for (j = 0; j <= 255; j++) { - if (codesize[j] == i) { - htbl->huffval[p] = (UINT8)j; - p++; - } - } + for (i = 0; i < num_symbols - 1; i++) { + htbl->huffval[bit_pos[codesize[indices[i]]]] = (UINT8)indices[i]; + bit_pos[codesize[indices[i]]]++; } /* Set sent_table FALSE so updated table will be written to JPEG file. */ From faa7c74aef69079bf88dfa43e22cdb0e792c16c9 Mon Sep 17 00:00:00 2001 From: DRC Date: Tue, 31 May 2022 13:07:00 -0500 Subject: [PATCH 009/162] ChangeLog.md: Acknowledge 2.1.4 release --- ChangeLog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index 9af838b88..9d4705efc 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,7 +1,7 @@ 2.2 pre-beta ============ -### Significant changes relative to 2.1.3 +### Significant changes relative to 2.1.4 1. By default, the build system now builds, packages, and tests a separate 12-bit-per-component flavor of the libjpeg API library, cjpeg, djpeg, and From 225781aff9045533f89e335b635999a89d7bcc42 Mon Sep 17 00:00:00 2001 From: DRC Date: Tue, 14 Jun 2022 14:25:11 -0500 Subject: [PATCH 010/162] jchuff.c: Clean up aadd60ae + add code comments Based on: https://github.com/fhanau/libjpeg-turbo/commit/4d73f52032a608262f8b286dd4584836ddc73180 https://github.com/fhanau/libjpeg-turbo/commit/184f10703eeb214177b02f88939af237267a8d66 Refer to #602 --- jchuff.c | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/jchuff.c b/jchuff.c index f47030468..f62912bd7 100644 --- a/jchuff.c +++ b/jchuff.c @@ -936,10 +936,12 @@ jpeg_gen_optimal_table(j_compress_ptr cinfo, JHUFF_TBL *htbl, long freq[]) UINT8 bits[MAX_CLEN + 1]; /* bits[k] = # of symbols with code length k */ int bit_pos[MAX_CLEN + 1]; /* # of symbols with smaller code length */ int codesize[257]; /* codesize[k] = code length of symbol k */ + int nz_index[257]; /* index of nonzero symbol in the original freq + array */ int others[257]; /* next symbol in current branch of tree */ int c1, c2; int p, i, j; - int indices[257], num_symbols = 0; + int num_nz_symbols; long v, v2; /* This algorithm is explained in section K.2 of the JPEG standard */ @@ -950,29 +952,36 @@ jpeg_gen_optimal_table(j_compress_ptr cinfo, JHUFF_TBL *htbl, long freq[]) 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. + */ + /* Group nonzero frequencies together so we can more easily find the + * smallest. + */ + num_nz_symbols = 0; for (i = 0; i < 257; i++) { if (freq[i]) { - indices[num_symbols] = i; - freq[num_symbols] = freq[i]; - num_symbols++; + nz_index[num_nz_symbols] = i; + freq[num_nz_symbols] = freq[i]; + num_nz_symbols++; } } - /* Including the pseudo-symbol 256 in the Huffman procedure guarantees - * that no real symbol is given code-value of all ones, because 256 - * will be placed last in the largest codeword category. - */ /* Huffman's basic algorithm to assign optimal code lengths to symbols */ for (;;) { /* Find the two smallest nonzero frequencies; set c1, c2 = their symbols */ - /* In case of ties, take the larger symbol number */ + /* In case of ties, take the larger symbol number. Since we have grouped + * the nonzero symbols together, checking for zero symbols is not + * necessary. + */ c1 = -1; c2 = -1; v = 1000000000L; v2 = 1000000000L; - for (i = 0; i < num_symbols; i++) { + for (i = 0; i < num_nz_symbols; i++) { if (freq[i] <= v2) { if (freq[i] <= v) { c2 = c1; @@ -992,9 +1001,10 @@ jpeg_gen_optimal_table(j_compress_ptr cinfo, JHUFF_TBL *htbl, long freq[]) /* Else merge the two counts/trees */ freq[c1] += freq[c2]; + /* Set the frequency to a very high value instead of zero, so we don't have + * to check for zero values. + */ freq[c2] = 1000000001L; - c1 = indices[c1]; - c2 = indices[c2]; /* Increment the codesize of everything in c1's tree branch */ codesize[c1]++; @@ -1014,13 +1024,13 @@ jpeg_gen_optimal_table(j_compress_ptr cinfo, JHUFF_TBL *htbl, long freq[]) } /* Now count the number of symbols of each code length */ - for (i = 0; i < num_symbols; i++) { + for (i = 0; i < num_nz_symbols; i++) { /* The JPEG standard seems to think that this can't happen, */ /* but I'm paranoid... */ - if (codesize[indices[i]] > MAX_CLEN) + if (codesize[i] > MAX_CLEN) ERREXIT(cinfo, JERR_HUFF_CLEN_OVERFLOW); - bits[codesize[indices[i]]]++; + bits[codesize[i]]++; } /* Count the number of symbols with a length smaller than i bits, so we can @@ -1071,9 +1081,9 @@ jpeg_gen_optimal_table(j_compress_ptr cinfo, JHUFF_TBL *htbl, long freq[]) * changes made above, but Rec. ITU-T T.81 | ISO/IEC 10918-1 seems to think * this works. */ - for (i = 0; i < num_symbols - 1; i++) { - htbl->huffval[bit_pos[codesize[indices[i]]]] = (UINT8)indices[i]; - bit_pos[codesize[indices[i]]]++; + for (i = 0; i < num_nz_symbols - 1; i++) { + htbl->huffval[bit_pos[codesize[i]]] = (UINT8)nz_index[i]; + bit_pos[codesize[i]]++; } /* Set sent_table FALSE so updated table will be written to JPEG file. */ From 8a3b0f70d29cb585c82537dcc8b65e4dda840c5e Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 24 Jun 2022 15:21:51 -0500 Subject: [PATCH 011/162] Implement 12-bit-specific error/warn/trace macros The macros in jerror.h refer to j_common_ptr, so it is unfortunately necessary to introduce a 12-bit-specific version of that header file (j12error.h) with 12-bit specific ERREXIT*(), WARNMS*(), and TRACEMS*() macros. (The message table is still shared between 8-bit and 12-bit implementations.) Fixes #607 --- CMakeLists.txt | 1 + cdjpeg.h | 2 +- example.c | 14 +- j12error.h | 331 +++++++++++++++++++++++++++++++++++++++ jcicc.c | 2 +- jdatadst.c | 2 +- jdatasrc.c | 2 +- jdicc.c | 2 +- jerror.c | 4 +- jerror.h | 2 +- jerrorint.h | 13 ++ jpeg12lib.h | 6 +- jpeglibint.h | 22 ++- libjpeg.txt | 8 +- release/installer.nsi.in | 1 + release/rpm.spec.in | 1 + 16 files changed, 391 insertions(+), 22 deletions(-) create mode 100644 j12error.h create mode 100644 jerrorint.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 00a3e1b4b..4e4b83b03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1653,6 +1653,7 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/jconfig.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) if(WITH_12BIT) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/jpeg12lib.h + ${CMAKE_CURRENT_SOURCE_DIR}/j12error.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) endif() diff --git a/cdjpeg.h b/cdjpeg.h index e340332ef..49c57e578 100644 --- a/cdjpeg.h +++ b/cdjpeg.h @@ -17,7 +17,7 @@ #define JPEG_INTERNAL_OPTIONS /* cjpeg.c,djpeg.c need to see xxx_SUPPORTED */ #include "jinclude.h" #include "jpeglibint.h" -#include "jerror.h" /* get library error codes too */ +#include "jerrorint.h" /* get library error codes too */ #include "cderror.h" /* get application-specific error codes */ diff --git a/example.c b/example.c index 08f08154b..aab9b2613 100644 --- a/example.c +++ b/example.c @@ -46,7 +46,9 @@ */ #include "jpeglib.h" +#include "jerror.h" #include "jpeg12lib.h" +#include "j12error.h" /* * is used for the optional error recovery mechanism shown in @@ -135,10 +137,8 @@ write_JPEG_file(char *filename, int quality) * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that * requires it in order to write binary files. */ - if ((outfile = fopen(filename, "wb")) == NULL) { - fprintf(stderr, "can't open %s\n", filename); - exit(1); - } + if ((outfile = fopen(filename, "wb")) == NULL) + ERREXIT(&cinfo, JERR_FILE_WRITE); jpeg_stdio_dest(&cinfo, outfile); /* Step 3: set parameters for compression */ @@ -275,10 +275,8 @@ write_JPEG12_file(char *filename, int quality) cinfo.err = jpeg12_std_error(&jerr); jpeg12_create_compress(&cinfo); - if ((outfile = fopen(filename, "wb")) == NULL) { - fprintf(stderr, "can't open %s\n", filename); - exit(1); - } + if ((outfile = fopen(filename, "wb")) == NULL) + J12ERREXIT(&cinfo, JERR_FILE_WRITE); jpeg12_stdio_dest(&cinfo, outfile); cinfo.image_width = WIDTH; diff --git a/j12error.h b/j12error.h new file mode 100644 index 000000000..e202830fb --- /dev/null +++ b/j12error.h @@ -0,0 +1,331 @@ +/* + * j12error.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1994-1997, Thomas G. Lane. + * Modified 1997-2009 by Guido Vollbeding. + * libjpeg-turbo Modifications: + * Copyright (C) 2014, 2017, 2021-2022, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file defines the error and message codes for the JPEG library. + * Edit this file to add new codes, or to translate the message strings to + * some other language. + * A set of error-reporting macros are defined too. Some applications using + * the JPEG library may wish to include this file to get the error codes + * and/or the macros. + */ + +/* + * To define the enum list of message codes, include this file without + * defining macro JMESSAGE. To create a message string table, include it + * again with a suitable JMESSAGE definition (see jerror.c for an example). + */ +#ifndef JMESSAGE +#if !defined(JERROR_H) && !defined(J12ERROR_H) +/* First time through, define the enum list */ +#define JMAKE_ENUM_LIST +#else +/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ +#define JMESSAGE(code, string) +#endif /* JERROR_H */ +#endif /* JMESSAGE */ + +#ifdef JMAKE_ENUM_LIST + +typedef enum { + +#define JMESSAGE(code, string) code, + +#endif /* JMAKE_ENUM_LIST */ + +JMESSAGE(JMSG_NOMESSAGE, "Bogus message code %d") /* Must be first entry! */ + +/* For maintenance convenience, list is alphabetical by message code name */ +#if JPEG_LIB_VERSION < 70 +JMESSAGE(JERR_ARITH_NOTIMPL, "Sorry, arithmetic coding is not implemented") +#endif +JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix") +JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix") +JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode") +JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") +#if JPEG_LIB_VERSION >= 70 +JMESSAGE(JERR_BAD_CROP_SPEC, "Invalid crop request") +#endif +JMESSAGE(JERR_BAD_DCT_COEF, "DCT coefficient out of range") +JMESSAGE(JERR_BAD_DCTSIZE, "IDCT output block size %d not supported") +#if JPEG_LIB_VERSION >= 70 +JMESSAGE(JERR_BAD_DROP_SAMPLING, + "Component index %d: mismatching sampling ratio %d:%d, %d:%d, %c") +#endif +JMESSAGE(JERR_BAD_HUFF_TABLE, "Bogus Huffman table definition") +JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace") +JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace") +JMESSAGE(JERR_BAD_LENGTH, "Bogus marker length") +JMESSAGE(JERR_BAD_LIB_VERSION, + "Wrong JPEG library version: library is %d, caller expects %d") +JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan") +JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d") +JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d") +JMESSAGE(JERR_BAD_PROGRESSION, + "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d") +JMESSAGE(JERR_BAD_PROG_SCRIPT, + "Invalid progressive parameters at scan script entry %d") +JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors") +JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d") +JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d") +JMESSAGE(JERR_BAD_STRUCT_SIZE, + "JPEG parameter struct mismatch: library thinks size is %u, caller expects %u") +JMESSAGE(JERR_BAD_VIRTUAL_ACCESS, "Bogus virtual array access") +JMESSAGE(JERR_BUFFER_SIZE, "Buffer passed to JPEG library is too small") +JMESSAGE(JERR_CANT_SUSPEND, "Suspension not allowed here") +JMESSAGE(JERR_CCIR601_NOTIMPL, "CCIR601 sampling not implemented yet") +JMESSAGE(JERR_COMPONENT_COUNT, "Too many color components: %d, max %d") +JMESSAGE(JERR_CONVERSION_NOTIMPL, "Unsupported color conversion request") +JMESSAGE(JERR_DAC_INDEX, "Bogus DAC index %d") +JMESSAGE(JERR_DAC_VALUE, "Bogus DAC value 0x%x") +JMESSAGE(JERR_DHT_INDEX, "Bogus DHT index %d") +JMESSAGE(JERR_DQT_INDEX, "Bogus DQT index %d") +JMESSAGE(JERR_EMPTY_IMAGE, "Empty JPEG image (DNL not supported)") +JMESSAGE(JERR_EMS_READ, "Read from EMS failed") +JMESSAGE(JERR_EMS_WRITE, "Write to EMS failed") +JMESSAGE(JERR_EOI_EXPECTED, "Didn't expect more than one scan") +JMESSAGE(JERR_FILE_READ, "Input file read error") +JMESSAGE(JERR_FILE_WRITE, "Output file write error --- out of disk space?") +JMESSAGE(JERR_FRACT_SAMPLE_NOTIMPL, "Fractional sampling not implemented yet") +JMESSAGE(JERR_HUFF_CLEN_OVERFLOW, "Huffman code size table overflow") +JMESSAGE(JERR_HUFF_MISSING_CODE, "Missing Huffman code table entry") +JMESSAGE(JERR_IMAGE_TOO_BIG, "Maximum supported image dimension is %u pixels") +JMESSAGE(JERR_INPUT_EMPTY, "Empty input file") +JMESSAGE(JERR_INPUT_EOF, "Premature end of input file") +JMESSAGE(JERR_MISMATCHED_QUANT_TABLE, + "Cannot transcode due to multiple use of quantization table %d") +JMESSAGE(JERR_MISSING_DATA, "Scan script does not transmit all data") +JMESSAGE(JERR_MODE_CHANGE, "Invalid color quantization mode change") +JMESSAGE(JERR_NOTIMPL, "Requested features are incompatible") +JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time") +#if JPEG_LIB_VERSION >= 70 +JMESSAGE(JERR_NO_ARITH_TABLE, "Arithmetic table 0x%02x was not defined") +#endif +JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported") +JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined") +JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image") +JMESSAGE(JERR_NO_QUANT_TABLE, "Quantization table 0x%02x was not defined") +JMESSAGE(JERR_NO_SOI, "Not a JPEG file: starts with 0x%02x 0x%02x") +JMESSAGE(JERR_OUT_OF_MEMORY, "Insufficient memory (case %d)") +JMESSAGE(JERR_QUANT_COMPONENTS, + "Cannot quantize more than %d color components") +JMESSAGE(JERR_QUANT_FEW_COLORS, "Cannot quantize to fewer than %d colors") +JMESSAGE(JERR_QUANT_MANY_COLORS, "Cannot quantize to more than %d colors") +JMESSAGE(JERR_SOF_DUPLICATE, "Invalid JPEG file structure: two SOF markers") +JMESSAGE(JERR_SOF_NO_SOS, "Invalid JPEG file structure: missing SOS marker") +JMESSAGE(JERR_SOF_UNSUPPORTED, "Unsupported JPEG process: SOF type 0x%02x") +JMESSAGE(JERR_SOI_DUPLICATE, "Invalid JPEG file structure: two SOI markers") +JMESSAGE(JERR_SOS_NO_SOF, "Invalid JPEG file structure: SOS before SOF") +JMESSAGE(JERR_TFILE_CREATE, "Failed to create temporary file %s") +JMESSAGE(JERR_TFILE_READ, "Read failed on temporary file") +JMESSAGE(JERR_TFILE_SEEK, "Seek failed on temporary file") +JMESSAGE(JERR_TFILE_WRITE, + "Write failed on temporary file --- out of disk space?") +JMESSAGE(JERR_TOO_LITTLE_DATA, "Application transferred too few scanlines") +JMESSAGE(JERR_UNKNOWN_MARKER, "Unsupported marker type 0x%02x") +JMESSAGE(JERR_VIRTUAL_BUG, "Virtual array controller messed up") +JMESSAGE(JERR_WIDTH_OVERFLOW, "Image too wide for this implementation") +JMESSAGE(JERR_XMS_READ, "Read from XMS failed") +JMESSAGE(JERR_XMS_WRITE, "Write to XMS failed") +JMESSAGE(JMSG_COPYRIGHT, JCOPYRIGHT_SHORT) +JMESSAGE(JMSG_VERSION, JVERSION) +JMESSAGE(JTRC_16BIT_TABLES, + "Caution: quantization tables are too coarse for baseline JPEG") +JMESSAGE(JTRC_ADOBE, + "Adobe APP14 marker: version %d, flags 0x%04x 0x%04x, transform %d") +JMESSAGE(JTRC_APP0, "Unknown APP0 marker (not JFIF), length %u") +JMESSAGE(JTRC_APP14, "Unknown APP14 marker (not Adobe), length %u") +JMESSAGE(JTRC_DAC, "Define Arithmetic Table 0x%02x: 0x%02x") +JMESSAGE(JTRC_DHT, "Define Huffman Table 0x%02x") +JMESSAGE(JTRC_DQT, "Define Quantization Table %d precision %d") +JMESSAGE(JTRC_DRI, "Define Restart Interval %u") +JMESSAGE(JTRC_EMS_CLOSE, "Freed EMS handle %u") +JMESSAGE(JTRC_EMS_OPEN, "Obtained EMS handle %u") +JMESSAGE(JTRC_EOI, "End Of Image") +JMESSAGE(JTRC_HUFFBITS, " %3d %3d %3d %3d %3d %3d %3d %3d") +JMESSAGE(JTRC_JFIF, "JFIF APP0 marker: version %d.%02d, density %dx%d %d") +JMESSAGE(JTRC_JFIF_BADTHUMBNAILSIZE, + "Warning: thumbnail image size does not match data length %u") +JMESSAGE(JTRC_JFIF_EXTENSION, "JFIF extension marker: type 0x%02x, length %u") +JMESSAGE(JTRC_JFIF_THUMBNAIL, " with %d x %d thumbnail image") +JMESSAGE(JTRC_MISC_MARKER, "Miscellaneous marker 0x%02x, length %u") +JMESSAGE(JTRC_PARMLESS_MARKER, "Unexpected marker 0x%02x") +JMESSAGE(JTRC_QUANTVALS, " %4u %4u %4u %4u %4u %4u %4u %4u") +JMESSAGE(JTRC_QUANT_3_NCOLORS, "Quantizing to %d = %d*%d*%d colors") +JMESSAGE(JTRC_QUANT_NCOLORS, "Quantizing to %d colors") +JMESSAGE(JTRC_QUANT_SELECTED, "Selected %d colors for quantization") +JMESSAGE(JTRC_RECOVERY_ACTION, "At marker 0x%02x, recovery action %d") +JMESSAGE(JTRC_RST, "RST%d") +JMESSAGE(JTRC_SMOOTH_NOTIMPL, + "Smoothing not supported with nonstandard sampling ratios") +JMESSAGE(JTRC_SOF, "Start Of Frame 0x%02x: width=%u, height=%u, components=%d") +JMESSAGE(JTRC_SOF_COMPONENT, " Component %d: %dhx%dv q=%d") +JMESSAGE(JTRC_SOI, "Start of Image") +JMESSAGE(JTRC_SOS, "Start Of Scan: %d components") +JMESSAGE(JTRC_SOS_COMPONENT, " Component %d: dc=%d ac=%d") +JMESSAGE(JTRC_SOS_PARAMS, " Ss=%d, Se=%d, Ah=%d, Al=%d") +JMESSAGE(JTRC_TFILE_CLOSE, "Closed temporary file %s") +JMESSAGE(JTRC_TFILE_OPEN, "Opened temporary file %s") +JMESSAGE(JTRC_THUMB_JPEG, + "JFIF extension marker: JPEG-compressed thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_PALETTE, + "JFIF extension marker: palette thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_RGB, + "JFIF extension marker: RGB thumbnail image, length %u") +JMESSAGE(JTRC_UNKNOWN_IDS, + "Unrecognized component IDs %d %d %d, assuming YCbCr") +JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") +JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") +JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") +#if JPEG_LIB_VERSION >= 70 +JMESSAGE(JWRN_ARITH_BAD_CODE, "Corrupt JPEG data: bad arithmetic code") +#endif +JMESSAGE(JWRN_BOGUS_PROGRESSION, + "Inconsistent progression sequence for component %d coefficient %d") +JMESSAGE(JWRN_EXTRANEOUS_DATA, + "Corrupt JPEG data: %u extraneous bytes before marker 0x%02x") +JMESSAGE(JWRN_HIT_MARKER, "Corrupt JPEG data: premature end of data segment") +JMESSAGE(JWRN_HUFF_BAD_CODE, "Corrupt JPEG data: bad Huffman code") +JMESSAGE(JWRN_JFIF_MAJOR, "Warning: unknown JFIF revision number %d.%02d") +JMESSAGE(JWRN_JPEG_EOF, "Premature end of JPEG file") +JMESSAGE(JWRN_MUST_RESYNC, + "Corrupt JPEG data: found marker 0x%02x instead of RST%d") +JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG") +JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") +#if JPEG_LIB_VERSION < 70 +JMESSAGE(JERR_BAD_CROP_SPEC, "Invalid crop request") +#if defined(C_ARITH_CODING_SUPPORTED) || defined(D_ARITH_CODING_SUPPORTED) +JMESSAGE(JERR_NO_ARITH_TABLE, "Arithmetic table 0x%02x was not defined") +JMESSAGE(JWRN_ARITH_BAD_CODE, "Corrupt JPEG data: bad arithmetic code") +#endif +#endif +JMESSAGE(JWRN_BOGUS_ICC, "Corrupt JPEG data: bad ICC marker") +#if JPEG_LIB_VERSION < 70 +JMESSAGE(JERR_BAD_DROP_SAMPLING, + "Component index %d: mismatching sampling ratio %d:%d, %d:%d, %c") +#endif + +#ifdef JMAKE_ENUM_LIST + + JMSG_LASTMSGCODE +} J_MESSAGE_CODE; + +#undef JMAKE_ENUM_LIST +#endif /* JMAKE_ENUM_LIST */ + +/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ +#undef JMESSAGE + + +#ifndef J12ERROR_H +#define J12ERROR_H + +/* Macros to simplify using the error and trace message stuff */ +/* The first parameter is either type of cinfo pointer */ + +/* Fatal errors (print message and exit) */ +#define J12ERREXIT(cinfo, code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->error_exit) ((j12_common_ptr)(cinfo))) +#define J12ERREXIT1(cinfo, code, p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->error_exit) ((j12_common_ptr)(cinfo))) +#define J12ERREXIT2(cinfo, code, p1, p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->error_exit) ((j12_common_ptr)(cinfo))) +#define J12ERREXIT3(cinfo, code, p1, p2, p3) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (*(cinfo)->err->error_exit) ((j12_common_ptr)(cinfo))) +#define J12ERREXIT4(cinfo, code, p1, p2, p3, p4) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (*(cinfo)->err->error_exit) ((j12_common_ptr)(cinfo))) +#define J12ERREXIT6(cinfo, code, p1, p2, p3, p4, p5, p6) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (cinfo)->err->msg_parm.i[4] = (p5), \ + (cinfo)->err->msg_parm.i[5] = (p6), \ + (*(cinfo)->err->error_exit) ((j12_common_ptr)(cinfo))) +#define J12ERREXITS(cinfo, code, str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (cinfo)->err->msg_parm.s[JMSG_STR_PARM_MAX - 1] = '\0', \ + (*(cinfo)->err->error_exit) ((j12_common_ptr)(cinfo))) + +#define MAKESTMT(stuff) do { stuff } while (0) + +/* Nonfatal errors (we can keep going, but the data is probably corrupt) */ +#define J12WARNMS(cinfo, code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j12_common_ptr)(cinfo), -1)) +#define J12WARNMS1(cinfo, code, p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j12_common_ptr)(cinfo), -1)) +#define J12WARNMS2(cinfo, code, p1, p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j12_common_ptr)(cinfo), -1)) + +/* Informational/debugging messages */ +#define J12TRACEMS(cinfo, lvl, code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j12_common_ptr)(cinfo), (lvl))) +#define J12TRACEMS1(cinfo, lvl, code, p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j12_common_ptr)(cinfo), (lvl))) +#define J12TRACEMS2(cinfo, lvl, code, p1, p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j12_common_ptr)(cinfo), (lvl))) +#define J12TRACEMS3(cinfo, lvl, code, p1, p2, p3) \ + MAKESTMT(int *_mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j12_common_ptr)(cinfo), (lvl)); ) +#define J12TRACEMS4(cinfo, lvl, code, p1, p2, p3, p4) \ + MAKESTMT(int *_mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j12_common_ptr)(cinfo), (lvl)); ) +#define J12TRACEMS5(cinfo, lvl, code, p1, p2, p3, p4, p5) \ + MAKESTMT(int *_mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j12_common_ptr)(cinfo), (lvl)); ) +#define J12TRACEMS8(cinfo, lvl, code, p1, p2, p3, p4, p5, p6, p7, p8) \ + MAKESTMT(int *_mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); _mp[5] = (p6); _mp[6] = (p7); _mp[7] = (p8); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j12_common_ptr)(cinfo), (lvl)); ) +#define J12TRACEMSS(cinfo, lvl, code, str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (cinfo)->err->msg_parm.s[JMSG_STR_PARM_MAX - 1] = '\0', \ + (*(cinfo)->err->emit_message) ((j12_common_ptr)(cinfo), (lvl))) + +#endif /* JERROR_H */ diff --git a/jcicc.c b/jcicc.c index 8390ec267..f812eb262 100644 --- a/jcicc.c +++ b/jcicc.c @@ -16,7 +16,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglibint.h" -#include "jerror.h" +#include "jerrorint.h" /* diff --git a/jdatadst.c b/jdatadst.c index 2df66d626..f1abd5a04 100644 --- a/jdatadst.c +++ b/jdatadst.c @@ -21,7 +21,7 @@ /* this is not a core library module, so it doesn't define JPEG_INTERNALS */ #include "jinclude.h" #include "jpeglibint.h" -#include "jerror.h" +#include "jerrorint.h" /* Expanded data destination object for stdio output */ diff --git a/jdatasrc.c b/jdatasrc.c index 4d6f04157..bb01bcdb3 100644 --- a/jdatasrc.c +++ b/jdatasrc.c @@ -21,7 +21,7 @@ /* this is not a core library module, so it doesn't define JPEG_INTERNALS */ #include "jinclude.h" #include "jpeglibint.h" -#include "jerror.h" +#include "jerrorint.h" /* Expanded data source object for stdio input */ diff --git a/jdicc.c b/jdicc.c index 266a90ef6..eb6edb8a3 100644 --- a/jdicc.c +++ b/jdicc.c @@ -16,7 +16,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglibint.h" -#include "jerror.h" +#include "jerrorint.h" #define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */ diff --git a/jerror.c b/jerror.c index b70e6063d..851e0d25b 100644 --- a/jerror.c +++ b/jerror.c @@ -26,7 +26,7 @@ #include "jinclude.h" #include "jpeglibint.h" #include "jversion.h" -#include "jerror.h" +#include "jerrorint.h" #ifdef USE_WINDOWS_MESSAGEBOX #include @@ -48,7 +48,7 @@ #define JMESSAGE(code, string) string, const char * const jpeg_std_message_table[] = { -#include "jerror.h" +#include "jerrorint.h" NULL }; diff --git a/jerror.h b/jerror.h index eb44a1140..494742551 100644 --- a/jerror.h +++ b/jerror.h @@ -23,7 +23,7 @@ * again with a suitable JMESSAGE definition (see jerror.c for an example). */ #ifndef JMESSAGE -#ifndef JERROR_H +#if !defined(JERROR_H) && !defined(J12ERROR_H) /* First time through, define the enum list */ #define JMAKE_ENUM_LIST #else diff --git a/jerrorint.h b/jerrorint.h new file mode 100644 index 000000000..1f95b4636 --- /dev/null +++ b/jerrorint.h @@ -0,0 +1,13 @@ +/* + * jerrorint.h + * + * Copyright (C) 2022, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + */ + +#if BITS_IN_JSAMPLE == 12 +#include "j12error.h" +#else +#include "jerror.h" +#endif diff --git a/jpeg12lib.h b/jpeg12lib.h index c3a55def7..cd4ca4ea4 100644 --- a/jpeg12lib.h +++ b/jpeg12lib.h @@ -12,7 +12,7 @@ * * 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. + * and perhaps j12error.h if they want to know the exact error codes. */ #ifndef JPEG12LIB_H @@ -1136,12 +1136,12 @@ struct jpeg12_color_quantizer { long dummy; }; * The JPEG library modules define JPEG_INTERNALS before including this file. * The internal structure declarations are read only when that is true. * Applications using the library should not include jpeg12int.h, but may wish - * to include jerror.h. + * to include j12error.h. */ #ifdef JPEG_INTERNALS #include "jpeg12int.h" /* fetch private declarations */ -#include "jerror.h" /* fetch error codes too */ +#include "j12error.h" /* fetch error codes too */ #endif #ifdef __cplusplus diff --git a/jpeglibint.h b/jpeglibint.h index 4f11f6a77..461d57c44 100644 --- a/jpeglibint.h +++ b/jpeglibint.h @@ -15,7 +15,8 @@ #include "jpeg12lib.h" -/* Rename all external types and functions that are affected by JSAMPLE. */ +/* Rename all external types, functions, and macros that are affected by + JSAMPLE. */ #define JSAMPLE J12SAMPLE @@ -28,6 +29,25 @@ #define JSAMPARRAY J12SAMPARRAY #define JSAMPIMAGE J12SAMPIMAGE +#define ERREXIT J12ERREXIT +#define ERREXIT1 J12ERREXIT1 +#define ERREXIT2 J12ERREXIT2 +#define ERREXIT3 J12ERREXIT3 +#define ERREXIT4 J12ERREXIT4 +#define ERREXIT6 J12ERREXIT6 +#define ERREXITS J12ERREXITS +#define WARNMS J12WARNMS +#define WARNMS1 J12WARNMS1 +#define WARNMS2 J12WARNMS2 +#define TRACEMS J12TRACEMS +#define TRACEMS1 J12TRACEMS1 +#define TRACEMS2 J12TRACEMS2 +#define TRACEMS3 J12TRACEMS3 +#define TRACEMS4 J12TRACEMS4 +#define TRACEMS5 J12TRACEMS5 +#define TRACEMS8 J12TRACEMS8 +#define TRACEMSS J12TRACEMSS + #define jpeg_common_struct jpeg12_common_struct #define j_common_ptr j12_common_ptr diff --git a/libjpeg.txt b/libjpeg.txt index 72492c954..b4c72089b 100644 --- a/libjpeg.txt +++ b/libjpeg.txt @@ -115,8 +115,9 @@ used by the free LIBTIFF library to support JPEG compression in TIFF.) Support for JPEG images with 12-bit, rather than 8-bit, samples is provided through a separate library, API, and header file (jpeg12lib.h instead of -jpeglib.h). Functions supporting 12-bit samples have a prefix of "jpeg12_" -instead of "jpeg_" and use the following data types, structures, and macros: +jpeglib.h and j12error.h instead of jerror.h). Functions supporting 12-bit +samples have a prefix of "jpeg12_" instead of "jpeg_" and use the following +data types, structures, and macros: * J12SAMPLE instead of JSAMPLE * J12SAMPROW instead of JSAMPROW @@ -124,6 +125,9 @@ instead of "jpeg_" and use the following data types, structures, and macros: * J12SAMPIMAGE instead of JSAMPIMAGE * MAXJ12SAMPLE instead of MAXJSAMPLE * CENTERJ12SAMPLE instead of CENTERJSAMPLE + * J12ERREXIT*() instead of ERREXIT*() + * J12WARNMS*() instead of WARNMS*() + * J12TRACEMS*() instead of TRACEMS*() * jpeg12_common_struct instead of jpeg_common_struct * j12_common_ptr instead of j_common_ptr * jpeg12_compress_struct instead of jpeg_compress_struct diff --git a/release/installer.nsi.in b/release/installer.nsi.in index 132e422d8..87c186b93 100644 --- a/release/installer.nsi.in +++ b/release/installer.nsi.in @@ -109,6 +109,7 @@ Section "@CMAKE_PROJECT_NAME@ SDK for @INST_PLATFORM@ (required)" File "@CMAKE_CURRENT_SOURCE_DIR@\jpeglib.h" !ifdef 12BIT File "@CMAKE_CURRENT_SOURCE_DIR@\jpeg12lib.h" + File "@CMAKE_CURRENT_SOURCE_DIR@\j12error.h" !endif File "@CMAKE_CURRENT_SOURCE_DIR@\turbojpeg.h" SetOutPath $INSTDIR\doc diff --git a/release/rpm.spec.in b/release/rpm.spec.in index 74ef0dd74..d54e28519 100644 --- a/release/rpm.spec.in +++ b/release/rpm.spec.in @@ -230,6 +230,7 @@ rm -rf $RPM_BUILD_ROOT %{_includedir}/jpeglib.h %if "%{_with_12bit}" == "1" %{_includedir}/jpeg12lib.h + %{_includedir}/j12error.h %endif %if "%{_with_turbojpeg}" == "1" %{_includedir}/turbojpeg.h From 931884e78dc8777e6477451e984af8b263e450b1 Mon Sep 17 00:00:00 2001 From: DRC Date: Mon, 8 Aug 2022 15:41:01 -0500 Subject: [PATCH 012/162] Java: Remove deprecated fields, ctors, and methods Most of these have been deprecated since libjpeg-turbo 1.4.x. It's time. --- ChangeLog.md | 3 + java/doc/constant-values.html | 28 --- java/doc/deprecated-list.html | 139 ----------- java/doc/index-all.html | 105 -------- java/doc/org/libjpegturbo/turbojpeg/TJ.html | 117 +-------- .../libjpegturbo/turbojpeg/TJCompressor.html | 229 +----------------- .../turbojpeg/TJDecompressor.html | 132 ++-------- .../libjpegturbo/turbojpeg/TJTransformer.html | 2 +- java/org/libjpegturbo/turbojpeg/TJ.java | 20 -- .../libjpegturbo/turbojpeg/TJCompressor.java | 154 +----------- .../turbojpeg/TJDecompressor.java | 73 +----- 11 files changed, 44 insertions(+), 958 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index c08e89a6b..01baa94dd 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -17,6 +17,9 @@ speeds up the compression of tiny images by as much as 2x and provides a noticeable speedup for images as large as 256x256 when using optimal Huffman tables. +3. All deprecated fields, constructors, and methods in the TurboJPEG Java API +have been removed. + 2.1.4 ===== diff --git a/java/doc/constant-values.html b/java/doc/constant-values.html index 07ba05270..853f5843b 100644 --- a/java/doc/constant-values.html +++ b/java/doc/constant-values.html @@ -161,34 +161,6 @@

org.libjpegturbo.*

256 - - -public static final int -FLAG_FORCEMMX -8 - - - - -public static final int -FLAG_FORCESSE -16 - - - - -public static final int -FLAG_FORCESSE2 -32 - - - - -public static final int -FLAG_FORCESSE3 -128 - - public static final int diff --git a/java/doc/deprecated-list.html b/java/doc/deprecated-list.html index 31d4e643a..b8726bcb0 100644 --- a/java/doc/deprecated-list.html +++ b/java/doc/deprecated-list.html @@ -65,145 +65,6 @@ -
- - - - - - - - - - -
-
decompress(byte[], int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
-
- -
decompress(int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
Decompress the JPEG source image associated with this decompressor @@ -201,12 +172,6 @@

D

instance into a YUV planar image and store it in the given YUVImage instance.
-
decompressToYUV(byte[], int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
-
- -
decompressToYUV(int, int[], int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
Decompress the JPEG source image associated with this decompressor @@ -219,12 +184,6 @@

D

instance into a unified YUV planar image buffer and return a YUVImage instance containing the decompressed image.
-
decompressToYUV(int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
-
- -
@@ -237,12 +196,6 @@

E

instance into a YUV planar image and store it in the given YUVImage instance. -
encodeYUV(byte[], int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
-
-
Deprecated. - -
-
encodeYUV(int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
Encode the uncompressed source image associated with this compressor @@ -255,28 +208,6 @@

E

instance into separate Y, U (Cb), and V (Cr) image planes and return a YUVImage instance containing the encoded image planes.
-
encodeYUV(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
-
-
Deprecated. - -
-
-
encodeYUV(BufferedImage, byte[], int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
-
- -
-
encodeYUV(BufferedImage, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
-
- -
equals(TJScalingFactor) - Method in class org.libjpegturbo.turbojpeg.TJScalingFactor
Returns true or false, depending on whether this instance and @@ -321,22 +252,6 @@

F

subsampling, use the fastest chrominance upsampling algorithm available in the underlying codec.
-
FLAG_FORCEMMX - Static variable in class org.libjpegturbo.turbojpeg.TJ
-
-
Deprecated.
-
-
FLAG_FORCESSE - Static variable in class org.libjpegturbo.turbojpeg.TJ
-
-
Deprecated.
-
-
FLAG_FORCESSE2 - Static variable in class org.libjpegturbo.turbojpeg.TJ
-
-
Deprecated.
-
-
FLAG_FORCESSE3 - Static variable in class org.libjpegturbo.turbojpeg.TJ
-
-
Deprecated.
-
FLAG_LIMITSCANS - Static variable in class org.libjpegturbo.turbojpeg.TJ
Limit the number of progressive JPEG scans that the decompression and @@ -758,12 +673,6 @@

S

Assign a unified image buffer to this YUVImage instance.
-
setJPEGImage(byte[], int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
-
-
Deprecated. - -
-
setJPEGQuality(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
Set the JPEG image quality level for subsequent compress operations.
@@ -773,13 +682,6 @@

S

Associate an uncompressed RGB, grayscale, or CMYK source image with this compressor instance.
-
setSourceImage(byte[], int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
-
- -
setSourceImage(BufferedImage, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
Associate an uncompressed RGB or grayscale source image with this @@ -830,13 +732,6 @@

T

source image stored in srcImage with the newly created instance.
-
TJCompressor(byte[], int, int, int, int) - Constructor for class org.libjpegturbo.turbojpeg.TJCompressor
-
- -
TJCompressor(BufferedImage, int, int, int, int) - Constructor for class org.libjpegturbo.turbojpeg.TJCompressor
Create a TurboJPEG compressor instance and associate the uncompressed diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJ.html b/java/doc/org/libjpegturbo/turbojpeg/TJ.html index 2a3de371c..4ce45ce6b 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJ.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJ.html @@ -190,30 +190,6 @@

Field Summary

static int -FLAG_FORCEMMX -
Deprecated. 
- - - -static int -FLAG_FORCESSE -
Deprecated. 
- - - -static int -FLAG_FORCESSE2 -
Deprecated. 
- - - -static int -FLAG_FORCESSE3 -
Deprecated. 
- - - -static int FLAG_LIMITSCANS
Limit the number of progressive JPEG scans that the decompression and transform operations will process.
@@ -391,16 +367,6 @@

Method Summary

static int -bufSizeYUV(int width, - int height, - int subsamp) -
Deprecated.  - -
- - - -static int bufSizeYUV(int width, int pad, int height, @@ -409,62 +375,62 @@

Method Summary

image with the given width, height, and level of chrominance subsampling.
- + static int getAlphaOffset(int pixelFormat)
For the given pixel format, returns the number of bytes that the alpha component is offset from the start of the pixel.
- + static int getBlueOffset(int pixelFormat)
For the given pixel format, returns the number of bytes that the blue component is offset from the start of the pixel.
- + static int getGreenOffset(int pixelFormat)
For the given pixel format, returns the number of bytes that the green component is offset from the start of the pixel.
- + static int getMCUHeight(int subsamp)
Returns the MCU block height for the given level of chrominance subsampling.
- + static int getMCUWidth(int subsamp)
Returns the MCU block width for the given level of chrominance subsampling.
- + static int getPixelSize(int pixelFormat)
Returns the pixel size (in bytes) for the given pixel format.
- + static int getRedOffset(int pixelFormat)
For the given pixel format, returns the number of bytes that the red component is offset from the start of the pixel.
- + static TJScalingFactor[] getScalingFactors()
Returns a list of fractional scaling factors that the JPEG decompressor in this implementation of TurboJPEG supports.
- + static int planeHeight(int componentID, int height, @@ -472,7 +438,7 @@

Method Summary

Returns the plane height of a YUV image plane with the given parameters.
- + static int planeSizeYUV(int componentID, int width, @@ -483,7 +449,7 @@

Method Summary

plane with the given parameters. - + static int planeWidth(int componentID, int width, @@ -891,54 +857,6 @@

FLAG_BOTTOMUP

See Also:
Constant Field Values
- - - -
    -
  • -

    FLAG_FORCEMMX

    -
    @Deprecated
    -public static final int FLAG_FORCEMMX
    -
    Deprecated. 
    -
    See Also:
    Constant Field Values
    -
  • -
- - - -
    -
  • -

    FLAG_FORCESSE

    -
    @Deprecated
    -public static final int FLAG_FORCESSE
    -
    Deprecated. 
    -
    See Also:
    Constant Field Values
    -
  • -
- - - -
    -
  • -

    FLAG_FORCESSE2

    -
    @Deprecated
    -public static final int FLAG_FORCESSE2
    -
    Deprecated. 
    -
    See Also:
    Constant Field Values
    -
  • -
- - - -
    -
  • -

    FLAG_FORCESSE3

    -
    @Deprecated
    -public static final int FLAG_FORCESSE3
    -
    Deprecated. 
    -
    See Also:
    Constant Field Values
    -
  • -
@@ -1230,19 +1148,6 @@

bufSizeYUV

image with the given width, height, and level of chrominance subsampling.
- - - -
    -
  • -

    bufSizeYUV

    -
    @Deprecated
    -public static int bufSizeYUV(int width,
    -                        int height,
    -                        int subsamp)
    -
    Deprecated. Use bufSizeYUV(int, int, int, int) instead.
    -
  • -
diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html b/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html index a53f87973..e8d734271 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html @@ -138,18 +138,6 @@

Constructor Summary

-TJCompressor(byte[] srcImage, - int width, - int pitch, - int height, - int pixelFormat) - - - - TJCompressor(byte[] srcImage, int x, int y, @@ -185,29 +173,6 @@

Method Summary

void -compress(java.awt.image.BufferedImage srcImage, - byte[] dstBuf, - int flags) - - - - -byte[] -compress(java.awt.image.BufferedImage srcImage, - int flags) - - - - -void compress(byte[] dstBuf, int flags)
Compress the uncompressed source image associated with this compressor @@ -222,46 +187,6 @@

Method Summary

-void -encodeYUV(java.awt.image.BufferedImage srcImage, - byte[] dstBuf, - int flags) - - - - -byte[] -encodeYUV(java.awt.image.BufferedImage srcImage, - int flags) - - - - -void -encodeYUV(byte[] dstBuf, - int flags) -
Deprecated.  - -
- - - -byte[] -encodeYUV(int flags) -
Deprecated.  -
Use encodeYUV(int, int) instead.
-
- - - YUVImage encodeYUV(int[] strides, int flags) @@ -318,19 +243,6 @@

Method Summary

void -setSourceImage(byte[] srcImage, - int width, - int pitch, - int height, - int pixelFormat) - - - - -void setSourceImage(byte[] srcImage, int x, int y, @@ -342,14 +254,14 @@

Method Summary

compressor instance.
- + void setSourceImage(YUVImage srcImage)
Associate an uncompressed YUV planar source image with this compressor instance.
- + void setSubsamp(int newSubsamp)
Set the level of chrominance subsampling for subsequent compress/encode @@ -414,25 +326,6 @@

TJCompressor

TJException
- - - - @@ -500,25 +393,6 @@

setSourceImage

TJException
- - - - @@ -638,41 +512,6 @@

compress

TJException
- - - - - - - - @@ -695,21 +534,6 @@

encodeYUV

TJException
- - - - @@ -762,55 +586,6 @@

encodeYUV

TJException
- - - - - - - - - - - - diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html b/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html index 6666e4e17..0a0d100c4 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html @@ -230,20 +230,6 @@

Method Summary

void -decompress(byte[] dstBuf, - int desiredWidth, - int pitch, - int desiredHeight, - int pixelFormat, - int flags) - - - - -void decompress(byte[] dstBuf, int x, int y, @@ -257,7 +243,7 @@

Method Summary

to the given destination buffer.
- + void decompress(int[] dstBuf, int x, @@ -272,7 +258,7 @@

Method Summary

to the given destination buffer. - + java.awt.image.BufferedImage decompress(int desiredWidth, int desiredHeight, @@ -283,7 +269,7 @@

Method Summary

instance containing the decompressed/decoded image. - + byte[] decompress(int desiredWidth, int pitch, @@ -294,24 +280,7 @@

Method Summary

instance and return a buffer containing the decompressed image. - -void -decompressToYUV(byte[] dstBuf, - int flags) -
Deprecated.  - -
- - -byte[] -decompressToYUV(int flags) -
Deprecated.  - -
- - - YUVImage decompressToYUV(int desiredWidth, int[] strides, @@ -322,7 +291,7 @@

Method Summary

YUVImage instance containing the decompressed image planes. - + YUVImage decompressToYUV(int desiredWidth, int pad, @@ -333,7 +302,7 @@

Method Summary

YUVImage instance containing the decompressed image. - + void decompressToYUV(YUVImage dstImage, int flags) @@ -342,38 +311,38 @@

Method Summary

YUVImage instance. - + protected void finalize()  - + int getColorspace()
Returns the colorspace used in the source image (JPEG or YUV) associated with this decompressor instance.
- + int getHeight()
Returns the height of the source image (JPEG or YUV) associated with this decompressor instance.
- + byte[] getJPEGBuf()
Returns the JPEG image buffer associated with this decompressor instance.
- + int getJPEGSize()
Returns the size of the JPEG image (in bytes) associated with this decompressor instance.
- + int getScaledHeight(int desiredWidth, int desiredHeight) @@ -382,7 +351,7 @@

Method Summary

height. - + int getScaledWidth(int desiredWidth, int desiredHeight) @@ -391,29 +360,20 @@

Method Summary

height. - + int getSubsamp()
Returns the level of chrominance subsampling used in the source image (JPEG or YUV) associated with this decompressor instance.
- + int getWidth()
Returns the width of the source image (JPEG or YUV) associated with this decompressor instance.
- -void -setJPEGImage(byte[] jpegImage, - int imageSize) -
Deprecated.  - -
- - void setSourceImage(byte[] jpegImage, @@ -629,21 +589,6 @@

setSourceImage

TJException
- - - - @@ -851,26 +796,6 @@

decompress

TJException
- - - - @@ -929,21 +854,6 @@

decompressToYUV

TJException
- - - - @@ -1026,20 +936,6 @@

decompressToYUV

TJException
- - - - diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJTransformer.html b/java/doc/org/libjpegturbo/turbojpeg/TJTransformer.html index a30fe30ca..8bad93f58 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJTransformer.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJTransformer.html @@ -206,7 +206,7 @@

Method Summary

Methods inherited from class org.libjpegturbo.turbojpeg.TJDecompressor

-close, decompress, decompress, decompress, decompress, decompress, decompress, decompressToYUV, decompressToYUV, decompressToYUV, decompressToYUV, decompressToYUV, finalize, getColorspace, getHeight, getJPEGBuf, getJPEGSize, getScaledHeight, getScaledWidth, getSubsamp, getWidth, setJPEGImage, setSourceImage, setSourceImage +close, decompress, decompress, decompress, decompress, decompress, decompressToYUV, decompressToYUV, decompressToYUV, finalize, getColorspace, getHeight, getJPEGBuf, getJPEGSize, getScaledHeight, getScaledWidth, getSubsamp, getWidth, setSourceImage, setSourceImage @@ -955,6 +963,21 @@

FLAG_LIMITSCANS

See Also:
Constant Field Values
+ + + +
    +
  • +

    FLAG_ARITHMETIC

    +
    public static final int FLAG_ARITHMETIC
    +
    Use arithmetic entropy coding in JPEG images generated by compression and + transform operations. Arithmetic entropy coding will generally improve + compression relative to Huffman entropy coding (the default), but it will + reduce compression and decompression performance considerably. Can be + combined with FLAG_PROGRESSIVE.
    +
    See Also:
    Constant Field Values
    +
  • +
diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJTransform.html b/java/doc/org/libjpegturbo/turbojpeg/TJTransform.html index 5f22691ef..65c2c1a7e 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJTransform.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJTransform.html @@ -221,53 +221,60 @@

Field Summary

static int +OPT_ARITHMETIC +
This option will enable arithmetic entropy coding in the output image + generated by this particular transform.
+ + + +static int OPT_COPYNONE
This option will prevent TJTransformer.transform() from copying any extra markers (including EXIF and ICC profile data) from the source image to the output image.
- + static int OPT_CROP
This option will enable lossless cropping.
- + static int OPT_GRAY
This option will discard the color data in the input image and produce a grayscale output image.
- + static int OPT_NOOUTPUT
This option will prevent TJTransformer.transform() from outputting a JPEG image for this particular transform.
- + static int OPT_PERFECT
This option will cause TJTransformer.transform() to throw an exception if the transform is not perfect.
- + static int OPT_PROGRESSIVE
This option will enable progressive entropy coding in the output image generated by this particular transform.
- + static int OPT_TRIM
This option will discard any partial MCU blocks that cannot be transformed.
- + int options
Transform options (bitwise OR of one or more of OPT_*)
@@ -577,7 +584,7 @@

OPT_PROGRESSIVE

generated by this particular transform. Progressive entropy coding will generally improve compression relative to baseline entropy coding (the default), but it will reduce compression and decompression performance - considerably. + considerably. Can be combined with OPT_ARITHMETIC.
See Also:
Constant Field Values
@@ -593,6 +600,21 @@

OPT_COPYNONE

See Also:
Constant Field Values
+ + + +
    +
  • +

    OPT_ARITHMETIC

    +
    public static final int OPT_ARITHMETIC
    +
    This option will enable arithmetic entropy coding in the output image + generated by this particular transform. Arithmetic entropy coding will + generally improve compression relative to Huffman entropy coding (the + default), but it will reduce compression and decompression performance + considerably. Can be combined with OPT_PROGRESSIVE.
    +
    See Also:
    Constant Field Values
    +
  • +
diff --git a/java/org/libjpegturbo/turbojpeg/TJ.java b/java/org/libjpegturbo/turbojpeg/TJ.java index 37a47e757..92d28566d 100644 --- a/java/org/libjpegturbo/turbojpeg/TJ.java +++ b/java/org/libjpegturbo/turbojpeg/TJ.java @@ -1,5 +1,5 @@ /* - * Copyright (C)2011-2013, 2017-2018, 2020-2021 D. R. Commander. + * Copyright (C)2011-2013, 2017-2018, 2020-2022 D. R. Commander. * All Rights Reserved. * Copyright (C)2015 Viktor Szathmáry. All Rights Reserved. * @@ -422,7 +422,8 @@ public static int getAlphaOffset(int pixelFormat) { * Use progressive entropy coding in JPEG images generated by compression and * transform operations. Progressive entropy coding will generally improve * compression relative to baseline entropy coding (the default), but it will - * reduce compression and decompression performance considerably. + * reduce compression and decompression performance considerably. Can be + * combined with {@link #FLAG_ARITHMETIC}. */ public static final int FLAG_PROGRESSIVE = 16384; /** @@ -435,7 +436,14 @@ public static int getAlphaOffset(int pixelFormat) { * this report. */ public static final int FLAG_LIMITSCANS = 32768; - + /** + * Use arithmetic entropy coding in JPEG images generated by compression and + * transform operations. Arithmetic entropy coding will generally improve + * compression relative to Huffman entropy coding (the default), but it will + * reduce compression and decompression performance considerably. Can be + * combined with {@link #FLAG_PROGRESSIVE}. + */ + public static final int FLAG_ARITHMETIC = 65536; /** * The number of error codes diff --git a/java/org/libjpegturbo/turbojpeg/TJTransform.java b/java/org/libjpegturbo/turbojpeg/TJTransform.java index 41c4b45ed..c655bf19e 100644 --- a/java/org/libjpegturbo/turbojpeg/TJTransform.java +++ b/java/org/libjpegturbo/turbojpeg/TJTransform.java @@ -1,5 +1,5 @@ /* - * Copyright (C)2011, 2013, 2018 D. R. Commander. All Rights Reserved. + * Copyright (C)2011, 2013, 2018, 2022 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -131,7 +131,7 @@ public class TJTransform extends Rectangle { * generated by this particular transform. Progressive entropy coding will * generally improve compression relative to baseline entropy coding (the * default), but it will reduce compression and decompression performance - * considerably. + * considerably. Can be combined with {@link #OPT_ARITHMETIC}. */ public static final int OPT_PROGRESSIVE = 32; /** @@ -140,6 +140,14 @@ public class TJTransform extends Rectangle { * and ICC profile data) from the source image to the output image. */ public static final int OPT_COPYNONE = 64; + /** + * This option will enable arithmetic entropy coding in the output image + * generated by this particular transform. Arithmetic entropy coding will + * generally improve compression relative to Huffman entropy coding (the + * default), but it will reduce compression and decompression performance + * considerably. Can be combined with {@link #OPT_PROGRESSIVE}. + */ + public static final int OPT_ARITHMETIC = 128; /** diff --git a/tjbench.c b/tjbench.c index 90786cf0b..b6848c433 100644 --- a/tjbench.c +++ b/tjbench.c @@ -771,7 +771,10 @@ static void usage(char *progName) printf("-accuratedct = Use the most accurate DCT/IDCT algorithms available in the\n"); printf(" underlying codec\n"); printf("-progressive = Use progressive entropy coding in JPEG images generated by\n"); - printf(" compression and transform operations.\n"); + printf(" compression and transform operations. (Can be combined with -arithmetic.)\n"); + printf("-arithmetic = Use arithmetic entropy coding in JPEG images generated by\n"); + printf(" compression and transform operations. (Can be combined with\n"); + printf(" -progressive.)\n"); printf("-subsamp = When testing JPEG compression, this option specifies the level\n"); printf(" of chrominance subsampling to use ( = 444, 422, 440, 420, 411, or\n"); printf(" GRAY). The default is to test Grayscale, 4:2:0, 4:2:2, and 4:4:4 in\n"); @@ -868,6 +871,9 @@ int main(int argc, char *argv[]) } else if (!strcasecmp(argv[i], "-progressive")) { printf("Using progressive entropy coding\n\n"); flags |= TJFLAG_PROGRESSIVE; + } else if (!strcasecmp(argv[i], "-arithmetic")) { + printf("Using arithmetic entropy coding\n\n"); + flags |= TJFLAG_ARITHMETIC; } else if (!strcasecmp(argv[i], "-rgb")) pf = TJPF_RGB; else if (!strcasecmp(argv[i], "-rgbx")) diff --git a/tjbenchtest.in b/tjbenchtest.in index 1c08b374c..d8aa208aa 100755 --- a/tjbenchtest.in +++ b/tjbenchtest.in @@ -30,6 +30,7 @@ YUVARG= ALLOC=0 ALLOCARG= PROGARG= +ARIARG= if [ "$EXT" = "bmp" ]; then BMPARG=-bmp; fi if [ -d $OUTDIR ]; then @@ -68,25 +69,28 @@ while [ $# -gt 0 ]; do -progressive) PROGARG=-progressive ;; + -arithmetic) + ARIARG=-arithmetic + ;; esac shift done -exec >$EXEDIR/tjbenchtest$YUVARG$ALLOCARG$PROGARG.log +exec >$EXEDIR/tjbenchtest$YUVARG$ALLOCARG$PROGARG$ARIARG.log # Standard tests for image in $IMAGES; do cp $IMGDIR/$image $OUTDIR basename=`basename $image .${EXT}` - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG -grayscale -outfile $OUTDIR/${basename}_GRAY_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG -sample 2x2 -outfile $OUTDIR/${basename}_420_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG -sample 2x1 -outfile $OUTDIR/${basename}_422_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG -sample 1x1 -outfile $OUTDIR/${basename}_444_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG -grayscale -outfile $OUTDIR/${basename}_GRAY_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG -sample 2x2 -outfile $OUTDIR/${basename}_420_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG -sample 2x1 -outfile $OUTDIR/${basename}_422_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG -sample 1x1 -outfile $OUTDIR/${basename}_444_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG $ARIARG -grayscale -outfile $OUTDIR/${basename}_GRAY_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG $ARIARG -sample 2x2 -outfile $OUTDIR/${basename}_420_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG $ARIARG -sample 2x1 -outfile $OUTDIR/${basename}_422_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG $ARIARG -sample 1x1 -outfile $OUTDIR/${basename}_444_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG $ARIARG -grayscale -outfile $OUTDIR/${basename}_GRAY_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG $ARIARG -sample 2x2 -outfile $OUTDIR/${basename}_420_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG $ARIARG -sample 2x1 -outfile $OUTDIR/${basename}_422_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG $ARIARG -sample 1x1 -outfile $OUTDIR/${basename}_444_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} for samp in GRAY 420 422 444; do runme $EXEDIR/djpeg -rgb $NSARG $BMPARG -outfile $OUTDIR/${basename}_${samp}_default_djpeg.${EXT} $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg runme $EXEDIR/djpeg -dct fast -rgb $NSARG $BMPARG -outfile $OUTDIR/${basename}_${samp}_fast_djpeg.${EXT} $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg @@ -100,7 +104,7 @@ for image in $IMAGES; do # Compression for dct in accurate fast; do - runme $EXEDIR/tjbench $OUTDIR/$image 95 -rgb -quiet -benchtime 0.01 -warmup 0 -${dct}dct $YUVARG $ALLOCARG $PROGARG + runme $EXEDIR/tjbench $OUTDIR/$image 95 -rgb -quiet -benchtime 0.01 -warmup 0 -${dct}dct $YUVARG $ALLOCARG $PROGARG $ARIARG for samp in GRAY 420 422 444; do runme cmp $OUTDIR/${basename}_${samp}_Q95.jpg $OUTDIR/${basename}_${samp}_${dct}_cjpeg.jpg done @@ -113,7 +117,7 @@ for image in $IMAGES; do fi # Tiled compression & decompression - runme $EXEDIR/tjbench $OUTDIR/$image 95 -rgb -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $PROGARG + runme $EXEDIR/tjbench $OUTDIR/$image 95 -rgb -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $PROGARG $ARIARG for samp in GRAY 444; do if [ $ALLOC = 1 ]; then runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${dct}_djpeg.${EXT} @@ -126,7 +130,7 @@ for image in $IMAGES; do done fi done - runme $EXEDIR/tjbench $OUTDIR/$image 95 -rgb -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $ALLOCARG $PROGARG + runme $EXEDIR/tjbench $OUTDIR/$image 95 -rgb -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $ALLOCARG $PROGARG $ARIARG for samp in 420 422; do if [ $ALLOC = 1 ]; then runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.${EXT} @@ -142,7 +146,7 @@ for image in $IMAGES; do # Tiled decompression for samp in GRAY 444; do - runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $PROGARG + runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $PROGARG $ARIARG if [ $ALLOC = 1 ]; then runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${dct}_djpeg.${EXT} rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} @@ -155,7 +159,7 @@ for image in $IMAGES; do fi done for samp in 420 422; do - runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $ALLOCARG $PROGARG + runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $ALLOCARG $PROGARG $ARIARG if [ $ALLOC = 1 ]; then runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.${EXT} rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} @@ -174,7 +178,7 @@ for image in $IMAGES; do scalearg=`echo $scale | sed 's/\_/\//g'` for samp in GRAY 420 422 444; do runme $EXEDIR/djpeg -rgb -scale ${scalearg} $NSARG $BMPARG -outfile $OUTDIR/${basename}_${samp}_${scale}_djpeg.${EXT} $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg - runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -scale ${scalearg} -quiet -benchtime 0.01 -warmup 0 $YUVARG $ALLOCARG $PROGARG + runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -scale ${scalearg} -quiet -benchtime 0.01 -warmup 0 $YUVARG $ALLOCARG $PROGARG $ARIARG runme cmp $OUTDIR/${basename}_${samp}_Q95_${scale}.${EXT} $OUTDIR/${basename}_${samp}_${scale}_djpeg.${EXT} rm $OUTDIR/${basename}_${samp}_Q95_${scale}.${EXT} done @@ -193,7 +197,7 @@ for image in $IMAGES; do for xform in hflip vflip transpose transverse rot90 rot180 rot270; do for samp in GRAY 444; do runme $EXEDIR/djpeg -rgb $BMPARG -outfile $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg - runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -tile -quiet -benchtime 0.01 -warmup 0 $YUVARG $ALLOCARG $PROGARG + runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -tile -quiet -benchtime 0.01 -warmup 0 $YUVARG $ALLOCARG $PROGARG $ARIARG if [ $ALLOC = 1 ]; then runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} @@ -207,7 +211,7 @@ for image in $IMAGES; do done for samp in 420 422; do runme $EXEDIR/djpeg -nosmooth -rgb $BMPARG -outfile $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg - runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample $YUVARG $ALLOCARG $PROGARG + runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample $YUVARG $ALLOCARG $PROGARG $ARIARG if [ $ALLOC = 1 ]; then runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} @@ -224,7 +228,7 @@ for image in $IMAGES; do # Grayscale transform for xform in hflip vflip transpose transverse rot90 rot180 rot270; do for samp in GRAY 444 422 420; do - runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -tile -quiet -benchtime 0.01 -warmup 0 -grayscale $YUVARG $ALLOCARG $PROGARG + runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -tile -quiet -benchtime 0.01 -warmup 0 -grayscale $YUVARG $ALLOCARG $PROGARG $ARIARG if [ $ALLOC = 1 ]; then runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_GRAY_${xform}_jpegtran.${EXT} rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} @@ -244,7 +248,7 @@ for image in $IMAGES; do for scale in 2_1 15_8 7_4 13_8 3_2 11_8 5_4 9_8 7_8 3_4 5_8 1_2 3_8 1_4 1_8; do scalearg=`echo $scale | sed 's/\_/\//g'` runme $EXEDIR/djpeg -rgb -scale ${scalearg} $NSARG $BMPARG -outfile $OUTDIR/${basename}_${samp}_${xform}_${scale}_jpegtran.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg - runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -scale ${scalearg} -quiet -benchtime 0.01 -warmup 0 $YUVARG $ALLOCARG $PROGARG + runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -scale ${scalearg} -quiet -benchtime 0.01 -warmup 0 $YUVARG $ALLOCARG $PROGARG $ARIARG runme cmp $OUTDIR/${basename}_${samp}_Q95_${scale}.${EXT} $OUTDIR/${basename}_${samp}_${xform}_${scale}_jpegtran.${EXT} rm $OUTDIR/${basename}_${samp}_Q95_${scale}.${EXT} done diff --git a/tjbenchtest.java.in b/tjbenchtest.java.in index 689561d26..a392b373f 100755 --- a/tjbenchtest.java.in +++ b/tjbenchtest.java.in @@ -29,6 +29,7 @@ BMPARG= NSARG= YUVARG= PROGARG= +ARIARG= if [ -d $OUTDIR ]; then rm -rf $OUTDIR @@ -62,25 +63,28 @@ while [ $# -gt 0 ]; do -progressive) PROGARG=-progressive ;; + -arithmetic) + ARIARG=-arithmetic + ;; esac shift done -exec >$EXEDIR/tjbenchtest-java$YUVARG$PROGARG.log +exec >$EXEDIR/tjbenchtest-java$YUVARG$PROGARG$ARIARG.log # Standard tests for image in $IMAGES; do cp $IMGDIR/$image $OUTDIR basename=`basename $image .bmp` - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG -grayscale -outfile $OUTDIR/${basename}_GRAY_fast_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG -sample 2x2 -outfile $OUTDIR/${basename}_420_fast_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG -sample 2x1 -outfile $OUTDIR/${basename}_422_fast_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG -sample 1x1 -outfile $OUTDIR/${basename}_444_fast_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG -grayscale -outfile $OUTDIR/${basename}_GRAY_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG -sample 2x2 -outfile $OUTDIR/${basename}_420_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG -sample 2x1 -outfile $OUTDIR/${basename}_422_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG -sample 1x1 -outfile $OUTDIR/${basename}_444_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp + runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG $ARIARG -grayscale -outfile $OUTDIR/${basename}_GRAY_fast_cjpeg.jpg $IMGDIR/${basename}.bmp + runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG $ARIARG -sample 2x2 -outfile $OUTDIR/${basename}_420_fast_cjpeg.jpg $IMGDIR/${basename}.bmp + runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG $ARIARG -sample 2x1 -outfile $OUTDIR/${basename}_422_fast_cjpeg.jpg $IMGDIR/${basename}.bmp + runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG $ARIARG -sample 1x1 -outfile $OUTDIR/${basename}_444_fast_cjpeg.jpg $IMGDIR/${basename}.bmp + runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG $ARIARG -grayscale -outfile $OUTDIR/${basename}_GRAY_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp + runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG $ARIARG -sample 2x2 -outfile $OUTDIR/${basename}_420_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp + runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG $ARIARG -sample 2x1 -outfile $OUTDIR/${basename}_422_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp + runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG $ARIARG -sample 1x1 -outfile $OUTDIR/${basename}_444_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp for samp in GRAY 420 422 444; do runme $EXEDIR/djpeg -rgb -bmp -outfile $OUTDIR/${basename}_${samp}_default_djpeg.bmp $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg runme $EXEDIR/djpeg -dct fast -rgb -bmp -outfile $OUTDIR/${basename}_${samp}_fast_djpeg.bmp $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg @@ -94,7 +98,7 @@ for image in $IMAGES; do # Compression for dct in accurate fast; do - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/$image 95 -rgb -quiet -benchtime 0.01 -warmup 0 -${dct}dct $YUVARG $PROGARG + runme "$JAVA" $JAVAARGS TJBench $OUTDIR/$image 95 -rgb -quiet -benchtime 0.01 -warmup 0 -${dct}dct $YUVARG $PROGARG $ARIARG for samp in GRAY 420 422 444; do runme cmp $OUTDIR/${basename}_${samp}_Q95.jpg $OUTDIR/${basename}_${samp}_${dct}_cjpeg.jpg done @@ -107,7 +111,7 @@ for image in $IMAGES; do fi # Tiled compression & decompression - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/$image 95 -rgb -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $PROGARG + runme "$JAVA" $JAVAARGS TJBench $OUTDIR/$image 95 -rgb -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $PROGARG $ARIARG for samp in GRAY 444; do for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].bmp \ $OUTDIR/${basename}_${samp}_Q95_full.bmp; do @@ -115,7 +119,7 @@ for image in $IMAGES; do rm $i done done - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/$image 95 -rgb -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $PROGARG + runme "$JAVA" $JAVAARGS TJBench $OUTDIR/$image 95 -rgb -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $PROGARG $ARIARG for samp in 420 422; do for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].bmp \ $OUTDIR/${basename}_${samp}_Q95_full.bmp; do @@ -126,7 +130,7 @@ for image in $IMAGES; do # Tiled decompression for samp in GRAY 444; do - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $PROGARG + runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $PROGARG $ARIARG for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].bmp \ $OUTDIR/${basename}_${samp}_Q95_full.bmp; do runme cmp -i 54:54 $i $OUTDIR/${basename}_${samp}_${dct}_djpeg.bmp @@ -134,7 +138,7 @@ for image in $IMAGES; do done done for samp in 420 422; do - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $PROGARG + runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $PROGARG $ARIARG for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].bmp \ $OUTDIR/${basename}_${samp}_Q95_full.bmp; do runme cmp $i -i 54:54 $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.bmp @@ -148,7 +152,7 @@ for image in $IMAGES; do scalearg=`echo $scale | sed 's/\_/\//g'` for samp in GRAY 420 422 444; do runme $EXEDIR/djpeg -rgb -scale ${scalearg} $NSARG -bmp -outfile $OUTDIR/${basename}_${samp}_${scale}_djpeg.bmp $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -scale ${scalearg} -quiet -benchtime 0.01 -warmup 0 $YUVARG $PROGARG + runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -scale ${scalearg} -quiet -benchtime 0.01 -warmup 0 $YUVARG $PROGARG $ARIARG runme cmp -i 54:54 $OUTDIR/${basename}_${samp}_Q95_${scale}.bmp $OUTDIR/${basename}_${samp}_${scale}_djpeg.bmp rm $OUTDIR/${basename}_${samp}_Q95_${scale}.bmp done @@ -167,7 +171,7 @@ for image in $IMAGES; do for xform in hflip vflip transpose transverse rot90 rot180 rot270; do for samp in GRAY 444; do runme $EXEDIR/djpeg -rgb -bmp -outfile $OUTDIR/${basename}_${samp}_${xform}_jpegtran.bmp $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -$xform -tile -quiet -benchtime 0.01 -warmup 0 $YUVARG $PROGARG + runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -$xform -tile -quiet -benchtime 0.01 -warmup 0 $YUVARG $PROGARG $ARIARG for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].bmp \ $OUTDIR/${basename}_${samp}_Q95_full.bmp; do runme cmp -i 54:54 $i $OUTDIR/${basename}_${samp}_${xform}_jpegtran.bmp @@ -176,7 +180,7 @@ for image in $IMAGES; do done for samp in 420 422; do runme $EXEDIR/djpeg -nosmooth -rgb -bmp -outfile $OUTDIR/${basename}_${samp}_${xform}_jpegtran.bmp $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -$xform -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample $YUVARG $PROGARG + runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -$xform -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample $YUVARG $PROGARG $ARIARG for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].bmp \ $OUTDIR/${basename}_${samp}_Q95_full.bmp; do runme cmp -i 54:54 $i $OUTDIR/${basename}_${samp}_${xform}_jpegtran.bmp @@ -188,7 +192,7 @@ for image in $IMAGES; do # Grayscale transform for xform in hflip vflip transpose transverse rot90 rot180 rot270; do for samp in GRAY 444 422 420; do - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -$xform -tile -quiet -benchtime 0.01 -warmup 0 -grayscale $YUVARG $PROGARG + runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -$xform -tile -quiet -benchtime 0.01 -warmup 0 -grayscale $YUVARG $PROGARG $ARIARG for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].bmp \ $OUTDIR/${basename}_${samp}_Q95_full.bmp; do runme cmp -i 54:54 $i $OUTDIR/${basename}_GRAY_${xform}_jpegtran.bmp @@ -203,7 +207,7 @@ for image in $IMAGES; do for scale in 2_1 15_8 7_4 13_8 3_2 11_8 5_4 9_8 7_8 3_4 5_8 1_2 3_8 1_4 1_8; do scalearg=`echo $scale | sed 's/\_/\//g'` runme $EXEDIR/djpeg -rgb -scale ${scalearg} $NSARG -bmp -outfile $OUTDIR/${basename}_${samp}_${xform}_${scale}_jpegtran.bmp $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -$xform -scale ${scalearg} -quiet -benchtime 0.01 -warmup 0 $YUVARG $PROGARG + runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -$xform -scale ${scalearg} -quiet -benchtime 0.01 -warmup 0 $YUVARG $PROGARG $ARIARG runme cmp -i 54:54 $OUTDIR/${basename}_${samp}_Q95_${scale}.bmp $OUTDIR/${basename}_${samp}_${xform}_${scale}_jpegtran.bmp rm $OUTDIR/${basename}_${samp}_Q95_${scale}.bmp done diff --git a/turbojpeg.c b/turbojpeg.c index ff8d6223f..dc9d6d8cf 100644 --- a/turbojpeg.c +++ b/turbojpeg.c @@ -287,8 +287,6 @@ static void setCompDefaults(struct jpeg_compress_struct *cinfo, #ifndef NO_GETENV if (!GETENV_S(env, 7, "TJ_OPTIMIZE") && !strcmp(env, "1")) cinfo->optimize_coding = TRUE; - if (!GETENV_S(env, 7, "TJ_ARITHMETIC") && !strcmp(env, "1")) - cinfo->arith_code = TRUE; if (!GETENV_S(env, 7, "TJ_RESTART") && strlen(env) > 0) { int temp = -1; char tempc = 0; @@ -329,6 +327,12 @@ static void setCompDefaults(struct jpeg_compress_struct *cinfo, else if (!GETENV_S(env, 7, "TJ_PROGRESSIVE") && !strcmp(env, "1")) jpeg_simple_progression(cinfo); #endif + if (flags & TJFLAG_ARITHMETIC) + cinfo->arith_code = TRUE; +#ifndef NO_GETENV + else if (!GETENV_S(env, 7, "TJ_ARITHMETIC") && !strcmp(env, "1")) + cinfo->arith_code = TRUE; +#endif cinfo->comp_info[0].h_samp_factor = tjMCUWidth[subsamp] / 8; cinfo->comp_info[1].h_samp_factor = 1; @@ -2010,6 +2014,8 @@ DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf, dstcoefs = jtransform_adjust_parameters(dinfo, cinfo, srccoefs, &xinfo[i]); if (flags & TJFLAG_PROGRESSIVE || t[i].options & TJXOPT_PROGRESSIVE) jpeg_simple_progression(cinfo); + if (flags & TJFLAG_ARITHMETIC || t[i].options & TJXOPT_ARITHMETIC) + cinfo->arith_code = TRUE; if (!(t[i].options & TJXOPT_NOOUTPUT)) { jpeg_write_coefficients(cinfo, dstcoefs); jcopy_markers_execute(dinfo, cinfo, t[i].options & TJXOPT_COPYNONE ? diff --git a/turbojpeg.h b/turbojpeg.h index 02b54ca99..570c7c6a9 100644 --- a/turbojpeg.h +++ b/turbojpeg.h @@ -1,5 +1,5 @@ /* - * Copyright (C)2009-2015, 2017, 2020-2021 D. R. Commander. + * Copyright (C)2009-2015, 2017, 2020-2022 D. R. Commander. * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without @@ -416,7 +416,8 @@ enum TJCS { * Use progressive entropy coding in JPEG images generated by the compression * and transform functions. Progressive entropy coding will generally improve * compression relative to baseline entropy coding (the default), but it will - * reduce compression and decompression performance considerably. + * reduce compression and decompression performance considerably. Can be + * combined with #TJFLAG_ARITHMETIC. */ #define TJFLAG_PROGRESSIVE 16384 /** @@ -429,6 +430,14 @@ enum TJCS { * this report. */ #define TJFLAG_LIMITSCANS 32768 +/** + * Use arithmetic entropy coding in JPEG images generated by the compression + * and transform functions. Arithmetic entropy coding will generally improve + * compression relative to Huffman entropy coding (the default), but it will + * reduce compression and decompression performance considerably. Can be + * combined with #TJFLAG_PROGRESSIVE. + */ +#define TJFLAG_ARITHMETIC 65536 /** @@ -546,7 +555,7 @@ enum TJXOP { * generated by this particular transform. Progressive entropy coding will * generally improve compression relative to baseline entropy coding (the * default), but it will reduce compression and decompression performance - * considerably. + * considerably. Can be combined with #TJXOPT_ARITHMETIC. */ #define TJXOPT_PROGRESSIVE 32 /** @@ -555,6 +564,14 @@ enum TJXOP { * image. */ #define TJXOPT_COPYNONE 64 +/** + * This option will enable arithmetic entropy coding in the output image + * generated by this particular transform. Arithmetic entropy coding will + * generally improve compression relative to Huffman entropy coding (the + * default), but it will reduce compression and decompression performance + * considerably. Can be combined with #TJXOPT_PROGRESSIVE. + */ +#define TJXOPT_ARITHMETIC 128 /** From b56e8b28ba544c439265da17d955ec6ab5aa9277 Mon Sep 17 00:00:00 2001 From: DRC Date: Tue, 8 Nov 2022 15:01:18 -0600 Subject: [PATCH 040/162] Clean up the lossless JPEG feature - Rename jpeg_simple_lossless() to jpeg_enable_lossless() and modify the function so that it stores the lossless parameters directly in the Ss and Al fields of jpeg_compress_struct rather than using a scan script. - Move the cjpeg -lossless switch into "Switches for advanced users". - Document the libjpeg API and run-time features that are unavailable in lossless mode, and ensure that all parameters, functions, and switches related to unavailable features are ignored or generate errors in lossless mode. - Defer any action that depends on whether lossless mode is enabled until jpeg_start_compress()/jpeg_start_decompress() is called. - Document the purpose of the point transform value. - "Codec" stands for coder/decoder, so it is a bit awkward to say "lossless compression codec" and "lossless decompression codec". Use "lossless compressor" and "lossless decompressor" instead. - Restore backward API/ABI compatibility with libjpeg v6b: * Move the new 'lossless' field from the exposed jpeg_compress_struct and jpeg_decompress_struct structures into the opaque jpeg_comp_master and jpeg_decomp_master structures, and allocate the master structures in the body of jpeg_create_compress() and jpeg_create_decompress(). * Remove the new 'process' field from jpeg_compress_struct and jpeg_decompress_struct and replace it with the old 'progressive_mode' field and the new 'lossless' field. * Remove the new 'data_unit' field from jpeg_compress_struct and jpeg_decompress_struct and replace it with a locally-computed data unit variable. * Restore the names of macros and fields that refer to DCT blocks, and document that they have a different meaning in lossless mode. (Most of them aren't very meaningful in lossless mode anyhow.) * Remove the new alloc_darray() method from jpeg_memory_mgr and replace it with an internal macro that wraps the alloc_sarray() method. * Move the JDIFF* data types from jpeglib.h and jmorecfg.h into jpegint.h. * Remove the new 'codec' field from jpeg_compress_struct and jpeg_decompress_struct and instead reuse the existing internal coefficient control, forward/inverse DCT, and entropy encoding/decoding structures for lossless compression/decompression. * Repurpose existing error codes rather than introducing new ones. (The new JERR_BAD_RESTART and JWRN_MUST_DOWNSCALE codes remain, although JWRN_MUST_DOWNSCALE will probably be removed in libjpeg-turbo, since we have a different way of handling multiple data precisions.) - Automatically enable lossless mode when a scan script with parameters that are only valid for lossless mode is detected, and document the use of scan scripts to generate lossless JPEG images. - Move the sequential and shared Huffman routines back into jchuff.c and jdhuff.c, and document that those routines are shared with jclhuff.c and jdlhuff.c as well as with jcphuff.c and jdphuff.c. - Move MAX_DIFF_BITS from jchuff.h into jclhuff.c, the only place where it is used. - Move the predictor and scaler code into jclossls.c and jdlossls.c. - Streamline register usage in the [un]differencers (inspired by similar optimizations in the color [de]converters.) - Restructure the logic in a few places to reduce duplicated code. - Ensure that all lossless-specific code is guarded by C_LOSSLESS_SUPPORTED or D_LOSSLESS_SUPPORTED and that the library can be built successfully if either or both of those macros is undefined. - Remove all short forms of external names introduced by the lossless JPEG patch. (These will not be needed by libjpeg-turbo, so there is no use cleaning them up.) - Various wordsmithing, formatting, and punctuation tweaks - Eliminate various compiler warnings. --- README | 8 +- TODO | 5 - cdjpeg.h | 5 +- cjpeg.1 | 50 +++- cjpeg.c | 31 ++- filelist.doc | 26 +- jcapimin.c | 13 +- jcapistd.c | 9 +- jccoefct.c | 81 +++--- jccolor.c | 13 +- jcdctmgr.c | 39 ++- jcdiffct.c | 158 ++++++------ jchuff.c | 653 ++++++++++++++++++++++++++++++++++++++++++++++++- jchuff.h | 11 +- jcinit.c | 43 +++- jclhuff.c | 92 ++++--- jclossls.c | 313 +++++++++++++++++++++--- jclossy.c | 78 ------ jcmainct.c | 19 +- jcmarker.c | 20 +- jcmaster.c | 200 +++++++-------- jcmaster.h | 30 +++ jcodec.c | 55 ----- jcparam.c | 173 ++++++------- jcphuff.c | 55 ++--- jcpred.c | 299 ----------------------- jcprepct.c | 12 +- jcsample.c | 22 +- jcscale.c | 64 ----- jcshuff.c | 663 -------------------------------------------------- jctrans.c | 112 ++++----- jdapimin.c | 19 +- jdapistd.c | 11 +- jdcoefct.c | 115 ++++----- jdcolor.c | 13 +- jddctmgr.c | 33 ++- jddiffct.c | 144 +++++------ jdhuff.c | 371 ++++++++++++++++++++++++++-- jdhuff.h | 35 +-- jdinput.c | 134 ++++++---- jdlhuff.c | 67 ++--- jdlossls.c | 317 ++++++++++++++++++++---- jdlossy.c | 230 ----------------- jdmainct.c | 95 ++++---- jdmarker.c | 21 +- jdmaster.c | 179 +++++++++++--- jdmaster.h | 27 ++ jdphuff.c | 53 ++-- jdpred.c | 249 ------------------- jdsample.c | 20 +- jdscale.c | 120 --------- jdshuff.c | 362 --------------------------- jdtrans.c | 36 +-- jerror.h | 30 +-- jlossls.h | 111 +++------ jlossy.h | 122 ---------- jmemmgr.c | 72 +----- jmorecfg.h | 11 +- jpegint.h | 148 ++++++++--- jpeglib.h | 158 ++++++------ libjpeg.doc | 45 ++-- makefile.cfg | 72 +++--- rdswitch.c | 30 +-- structure.doc | 122 ++++------ transupp.c | 44 ++-- usage.doc | 29 ++- wizard.doc | 40 +-- 67 files changed, 3166 insertions(+), 3871 deletions(-) delete mode 100644 jclossy.c create mode 100644 jcmaster.h delete mode 100644 jcodec.c delete mode 100644 jcpred.c delete mode 100644 jcscale.c delete mode 100644 jcshuff.c delete mode 100644 jdlossy.c create mode 100644 jdmaster.h delete mode 100644 jdpred.c delete mode 100644 jdscale.c delete mode 100644 jdshuff.c delete mode 100644 jlossy.h diff --git a/README b/README index 242359479..369f1a221 100644 --- a/README +++ b/README @@ -74,10 +74,10 @@ remarkably high compression levels are possible if you can tolerate a low-quality image. For more details, see the references, or just experiment with various compression settings. -This software implements JPEG baseline, extended-sequential, progressive -and lossless compression processes. Provision is made for supporting all -variants of these processes, although some uncommon parameter settings aren't -implemented yet. For legal reasons, we are not distributing code for the +This software implements JPEG baseline, extended-sequential, progressive, and +lossless compression processes. Provision is made for supporting all variants +of these processes, although some uncommon parameter settings aren't +implemented yet. For legal reasons, we are not distributing code for the arithmetic-coding variants of JPEG; see LEGAL ISSUES. We have made no provision for supporting the hierarchical processes defined in the standard. diff --git a/TODO b/TODO index 9f21066fb..17f6d748c 100644 --- a/TODO +++ b/TODO @@ -6,8 +6,3 @@ List of things to complete for lossless codec: * How to check BITS_PER_JSAMPLE for lossy mode (ie, 16-bit data)? - see jdinput.c. - -* Check comment blocks for errors/changes. - -* Review new filenames. Try to avoid filename conflicts with possible JPEG-LS - codec. diff --git a/cdjpeg.h b/cdjpeg.h index 84db6a061..2b387b6e5 100644 --- a/cdjpeg.h +++ b/cdjpeg.h @@ -1,10 +1,8 @@ /* * cdjpeg.h * - * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1997, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * 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 common declarations for the sample applications @@ -137,7 +135,6 @@ EXTERN(boolean) read_quant_tables JPP((j_compress_ptr cinfo, char * filename, EXTERN(boolean) read_scan_script JPP((j_compress_ptr cinfo, char * filename)); EXTERN(boolean) set_quant_slots JPP((j_compress_ptr cinfo, char *arg)); EXTERN(boolean) set_sample_factors JPP((j_compress_ptr cinfo, char *arg)); -EXTERN(boolean) set_simple_lossless JPP((j_compress_ptr cinfo, char *arg)); /* djpeg support routines (in rdcolmap.c) */ diff --git a/cjpeg.1 b/cjpeg.1 index b6903dea8..38477a010 100644 --- a/cjpeg.1 +++ b/cjpeg.1 @@ -1,4 +1,4 @@ -.TH CJPEG 1 "27 April 1999" +.TH CJPEG 1 "11 November 2022" .SH NAME cjpeg \- compress an image file to a JPEG file .SH SYNOPSIS @@ -62,13 +62,6 @@ decompression are unaffected by .B \-progressive Create progressive JPEG file (see below). .TP -.BI \-lossless " psv[,Pt]" -Create a lossless JPEG file using the specified predictor selection value (1-7) -and optional point transform. -.B Caution: -lossless JPEG is not widely implemented, so many decoders will be -unable to view a lossless JPEG file at all. -.TP .B \-targa Input file is Targa format. Targa files that contain an "identification" field will not be automatically recognized by @@ -130,6 +123,43 @@ unable to view a progressive JPEG file at all. .PP Switches for advanced users: .TP +.BI \-lossless " psv[,Pt]" +Create a lossless JPEG file using the specified predictor selection value +(1 through 7) and optional point transform (0 through +.nh +.I precision +.hy +- 1, where +.nh +.I precision +.hy +is the JPEG data precision in bits). A point transform value of 0 (the +default) is necessary in order to create a fully lossless JPEG file. (A +non-zero point transform value right-shifts the input samples by the specified +number of bits, which is effectively a form of lossy color quantization.) +.B Caution: +lossless JPEG is not yet widely implemented, so many decoders will be unable to +view a lossless JPEG file at all. Note that the following features will be +unavailable when compressing or decompressing a lossless JPEG file: +.IP +- Quality/quantization table selection +.IP +- Color conversion (the JPEG image will use the same color space as the input +image) +.IP +- DCT/IDCT algorithm selection +.IP +- Smoothing +.IP +- Downsampling/upsampling +.IP +- IDCT scaling +.IP +- Transformations using +.B jpegtran +.IP +Any switches used to enable or configure those features will be ignored. +.TP .B \-dct int Use integer DCT method (default). .TP @@ -145,8 +175,8 @@ machines, while the integer methods should give the same results everywhere. The fast integer method is much less accurate than the other two. .TP .BI \-restart " N" -Emit a JPEG restart marker every N MCU rows, or every N MCU blocks if "B" is -attached to the number. +Emit a JPEG restart marker every N MCU rows, or every N MCU blocks (samples in +lossless mode) if "B" is attached to the number. .B \-restart 0 (the default) means no restart markers. .TP diff --git a/cjpeg.c b/cjpeg.c index 3fc9150df..b012567b7 100644 --- a/cjpeg.c +++ b/cjpeg.c @@ -5,6 +5,7 @@ * Copyright (C) 1991-1998, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains a command-line user interface for the JPEG compressor. @@ -159,13 +160,13 @@ usage (void) #ifdef C_PROGRESSIVE_SUPPORTED fprintf(stderr, " -progressive Create progressive JPEG file\n"); #endif -#ifdef C_LOSSLESS_SUPPORTED - fprintf(stderr, " -lossless psv[,Pt] Create lossless JPEG file\n"); -#endif #ifdef TARGA_SUPPORTED fprintf(stderr, " -targa Input file is Targa format (usually not needed)\n"); #endif fprintf(stderr, "Switches for advanced users:\n"); +#ifdef C_LOSSLESS_SUPPORTED + fprintf(stderr, " -lossless psv[,Pt] Create lossless JPEG file\n"); +#endif #ifdef DCT_ISLOW_SUPPORTED fprintf(stderr, " -dct int Use integer DCT method%s\n", (JDCT_DEFAULT == JDCT_ISLOW ? " (default)" : "")); @@ -214,6 +215,9 @@ parse_switches (j_compress_ptr cinfo, int argc, char **argv, { int argn; char * arg; +#ifdef C_LOSSLESS_SUPPORTED + int psv, pt = 0; +#endif int quality; /* -quality parameter */ int q_scale_factor; /* scaling percentage for -qtables */ boolean force_baseline; @@ -222,7 +226,6 @@ parse_switches (j_compress_ptr cinfo, int argc, char **argv, char * qslotsarg = NULL; /* saves -qslots parm if any */ char * samplearg = NULL; /* saves -sample parm if any */ char * scansarg = NULL; /* saves -scans parm if any */ - char * losslsarg = NULL; /* saves -lossless parm if any */ /* Set up default JPEG parameters. */ /* Note that default -quality level need not, and does not, @@ -294,12 +297,20 @@ parse_switches (j_compress_ptr cinfo, int argc, char **argv, jpeg_set_colorspace(cinfo, JCS_GRAYSCALE); } else if (keymatch(arg, "lossless", 1)) { -/* Select simple lossless mode. */ + /* Enable lossless mode. */ #ifdef C_LOSSLESS_SUPPORTED + char ch = ',', *ptr; + if (++argn >= argc) /* advance to next argument */ usage(); - losslsarg = argv[argn]; - /* We must postpone execution until num_components is known. */ + if (sscanf(argv[argn], "%d%c", &psv, &ch) < 1 || ch != ',') + usage(); + ptr = argv[argn]; + while (*ptr && *ptr++ != ',') /* advance to next segment of arg string */ + ; + if (*ptr) + sscanf(ptr, "%d", &pt); + jpeg_enable_lossless(cinfo, psv, pt); #else fprintf(stderr, "%s: sorry, lossless output was not compiled\n", progname); @@ -461,12 +472,6 @@ parse_switches (j_compress_ptr cinfo, int argc, char **argv, jpeg_simple_progression(cinfo); #endif -#ifdef C_LOSSLESS_SUPPORTED - if (losslsarg != NULL) /* process -lossless if it was present */ - if (! set_simple_lossless(cinfo, losslsarg)) - usage(); -#endif - #ifdef C_MULTISCAN_FILES_SUPPORTED if (scansarg != NULL) /* process -scans if it was present */ if (! read_scan_script(cinfo, scansarg)) diff --git a/filelist.doc b/filelist.doc index 322c87598..4dfcca3f5 100644 --- a/filelist.doc +++ b/filelist.doc @@ -1,9 +1,10 @@ IJG JPEG LIBRARY: FILE LIST This file was part of the Independent JPEG Group's software: -Copyright (C) 1994-1997, Thomas G. Lane. +Copyright (C) 1994-1998, Thomas G. Lane. Lossless JPEG Modifications: Copyright (C) 1999, Ken Murchison. +Copyright (C) 2022, D. R. Commander. For conditions of distribution and use, see the accompanying README file. @@ -31,7 +32,6 @@ jinclude.h Central include file used by all IJG .c files to reference system include files. jpegint.h JPEG library's internal data structures. jlossls.h JPEG library's lossless codec data structures. -jlossy.h JPEG library's lossy codec structures. jchuff.h Private declarations for Huffman encoder modules. jdhuff.h Private declarations for Huffman decoder modules. jdct.h Private declarations for forward & reverse DCT subsystems. @@ -68,30 +68,27 @@ Compression side of the library: jcinit.c Initialization: determines which other modules to use. jcmaster.c Master control: setup and inter-pass sequencing logic. jcmainct.c Main buffer controller (preprocessor => JPEG compressor). -jchuff.c Codec-independent Huffman entropy encoding routines. jcprepct.c Preprocessor buffer controller. jccolor.c Color space conversion. jcsample.c Downsampling. +jchuff.c Shared Huffman entropy encoding routines. jcmarker.c JPEG marker writing. jdatadst.c Data destination manager for stdio output. Lossy (DCT) codec: -jlossy.c Lossy compressor proper. jccoefct.c Buffer controller for DCT coefficient buffer. jcdctmgr.c DCT manager (DCT implementation selection & control). jfdctint.c Forward DCT using slow-but-accurate integer method. jfdctfst.c Forward DCT using faster, less accurate integer method. jfdctflt.c Forward DCT using floating-point arithmetic. -jcshuff.c Huffman entropy coding for sequential JPEG. +jchuff.c Huffman entropy coding for sequential JPEG. jcphuff.c Huffman entropy coding for progressive JPEG. Lossless (spatial) codec: -jclossls.c Lossless compressor proper. jcdiffct.c Buffer controller for difference buffer. -jcscale.c Point transformation. -jcpred.c Sample predictor and differencer. +jclossls.c Prediction, sample differencing, and point transform jclhuff.c Huffman entropy encoding for lossless JPEG. Decompression side of the library: @@ -99,9 +96,9 @@ Decompression side of the library: jdmaster.c Master control: determines which other modules to use. jdinput.c Input controller: controls input processing modules. jdmainct.c Main buffer controller (JPEG decompressor => postprocessor). -jdhuff.c Codec-independent Huffman entropy decoding routines. jdpostct.c Postprocessor buffer controller. jdmarker.c JPEG marker reading. +jdhuff.c Shared Huffman entropy decoding routines. jdsample.c Upsampling. jdcolor.c Color space conversion. jdmerge.c Merged upsampling/color conversion (faster, lower quality). @@ -112,9 +109,8 @@ jdatasrc.c Data source manager for stdio input. Lossy (DCT) codec: -jdlossy.c Lossy decompressor proper. jdcoefct.c Buffer controller for DCT coefficient buffer. -jdshuff.c Huffman entropy decoding for sequential JPEG. +jdhuff.c Huffman entropy decoding for sequential JPEG. jdphuff.c Huffman entropy decoding for progressive JPEG. jddctmgr.c IDCT manager (IDCT implementation selection & control). jidctint.c Inverse DCT using slow-but-accurate integer method. @@ -124,17 +120,15 @@ jidctred.c Inverse DCTs with reduced-size outputs. Lossless (spatial) codec: -jdlossls.c Lossless decompressor proper. -jddiffct.c Buffer controller for difference buffers. +jddiffct.c Buffer controller for difference buffer. +jdlossls.c Prediction, sample undifferencing, point transform, and sample + scaling jdlhuff.c Huffman entropy decoding for lossless JPEG. -jdpred.c Sample predictor and undifferencer. -jdscale.c Point transformation, sample size scaling. Support files for both compression and decompression: jerror.c Standard error handling routines (application replaceable). jmemmgr.c System-independent (more or less) memory management code. -jcodec.c Codec-independent utility routines. jutils.c Miscellaneous utility routines. jmemmgr.c relies on a system-dependent memory management module. The IJG diff --git a/jcapimin.c b/jcapimin.c index f30f01417..21147447d 100644 --- a/jcapimin.c +++ b/jcapimin.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1998, Thomas G. Lane. * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains application interface code for the compression half @@ -21,6 +21,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" +#include "jcmaster.h" /* @@ -79,6 +80,14 @@ jpeg_CreateCompress (j_compress_ptr cinfo, int version, size_t structsize) /* OK, I'm ready */ cinfo->global_state = CSTATE_START; + + /* The master struct is used to store extension parameters, so we allocate it + * here. + */ + cinfo->master = (struct jpeg_comp_master *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_comp_master)); + MEMZERO(cinfo->master, SIZEOF(my_comp_master)); } @@ -170,7 +179,7 @@ jpeg_finish_compress (j_compress_ptr cinfo) /* We bypass the main controller and invoke coef controller directly; * all work is being done from the coefficient buffer. */ - if (! (*cinfo->codec->compress_data) (cinfo, (JSAMPIMAGE) NULL)) + if (! (*cinfo->coef->compress_data) (cinfo, (JSAMPIMAGE) NULL)) ERREXIT(cinfo, JERR_CANT_SUSPEND); } (*cinfo->master->finish_pass) (cinfo); diff --git a/jcapistd.c b/jcapistd.c index f258cb237..45b0f12cd 100644 --- a/jcapistd.c +++ b/jcapistd.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains application interface code for the compression half @@ -124,6 +124,9 @@ jpeg_write_raw_data (j_compress_ptr cinfo, JSAMPIMAGE data, { JDIMENSION lines_per_iMCU_row; + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); + if (cinfo->global_state != CSTATE_RAW_OK) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); if (cinfo->next_scanline >= cinfo->image_height) { @@ -147,12 +150,12 @@ jpeg_write_raw_data (j_compress_ptr cinfo, JSAMPIMAGE data, (*cinfo->master->pass_startup) (cinfo); /* Verify that at least one iMCU row has been passed. */ - lines_per_iMCU_row = cinfo->max_v_samp_factor * cinfo->data_unit; + lines_per_iMCU_row = cinfo->max_v_samp_factor * DCTSIZE; if (num_lines < lines_per_iMCU_row) ERREXIT(cinfo, JERR_BUFFER_SIZE); /* Directly compress the row. */ - if (! (*cinfo->codec->compress_data) (cinfo, data)) { + if (! (*cinfo->coef->compress_data) (cinfo, data)) { /* If compressor did not consume the whole row, suspend processing. */ return 0; } diff --git a/jccoefct.c b/jccoefct.c index 65f709ae6..fc6983c08 100644 --- a/jccoefct.c +++ b/jccoefct.c @@ -2,20 +2,19 @@ * jccoefct.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. + * Copyright (C) 1994-1997, Thomas G. Lane. * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains the coefficient buffer controller for compression. - * This controller is the top level of the JPEG compressor proper. + * This controller is the top level of the lossy JPEG compressor proper. * The coefficient buffer lies between forward-DCT and entropy encoding steps. */ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossy.h" /* Private declarations for lossy codec */ /* We use a full-image coefficient buffer when doing Huffman optimization, @@ -35,6 +34,8 @@ /* Private buffer controller object */ typedef struct { + struct jpeg_c_coef_controller pub; /* public fields */ + JDIMENSION iMCU_row_num; /* iMCU row # within image */ JDIMENSION mcu_ctr; /* counts MCUs processed in current row */ int MCU_vert_offset; /* counts MCU rows within iMCU row */ @@ -42,20 +43,20 @@ typedef struct { /* 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_DATA_UNITS_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.) + * workspace of C_MAX_BLOCKS_IN_MCU coefficient blocks, and reuse it for each + * MCU constructed and sent. (On 80x86, the workspace is FAR even though + * it's not really very big; this is to keep the module interfaces unchanged + * when a large coefficient buffer is necessary.) * In multi-pass modes, this array points to the current MCU's blocks * within the virtual arrays. */ - JBLOCKROW MCU_buffer[C_MAX_DATA_UNITS_IN_MCU]; + JBLOCKROW MCU_buffer[C_MAX_BLOCKS_IN_MCU]; /* In multi-pass modes, we need a virtual block array for each component. */ jvirt_barray_ptr whole_image[MAX_COMPONENTS]; -} c_coef_controller; +} my_coef_controller; -typedef c_coef_controller * c_coef_ptr; +typedef my_coef_controller * my_coef_ptr; /* Forward declarations */ @@ -73,8 +74,7 @@ LOCAL(void) start_iMCU_row (j_compress_ptr cinfo) /* Reset within-iMCU-row counters for a new row */ { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; /* In an interleaved scan, an MCU row is the same as an iMCU row. * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. @@ -101,8 +101,7 @@ start_iMCU_row (j_compress_ptr cinfo) METHODDEF(void) start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; coef->iMCU_row_num = 0; start_iMCU_row(cinfo); @@ -111,18 +110,18 @@ start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) case JBUF_PASS_THRU: if (coef->whole_image[0] != NULL) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - lossyc->pub.compress_data = compress_data; + coef->pub.compress_data = compress_data; break; #ifdef FULL_COEF_BUFFER_SUPPORTED case JBUF_SAVE_AND_PASS: if (coef->whole_image[0] == NULL) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - lossyc->pub.compress_data = compress_first_pass; + coef->pub.compress_data = compress_first_pass; break; case JBUF_CRANK_DEST: if (coef->whole_image[0] == NULL) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - lossyc->pub.compress_data = compress_output; + coef->pub.compress_data = compress_output; break; #endif default: @@ -145,8 +144,7 @@ start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) METHODDEF(boolean) compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; JDIMENSION MCU_col_num; /* index of current MCU within row */ JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; @@ -178,10 +176,10 @@ compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) for (yindex = 0; yindex < compptr->MCU_height; yindex++) { if (coef->iMCU_row_num < last_iMCU_row || yoffset+yindex < compptr->last_row_height) { - (*lossyc->fdct_forward_DCT) (cinfo, compptr, - input_buf[compptr->component_index], - coef->MCU_buffer[blkn], - ypos, xpos, (JDIMENSION) blockcnt); + (*cinfo->fdct->forward_DCT) (cinfo, compptr, + input_buf[compptr->component_index], + coef->MCU_buffer[blkn], + ypos, xpos, (JDIMENSION) blockcnt); if (blockcnt < compptr->MCU_width) { /* Create some dummy blocks at the right edge of the image. */ jzero_far((void FAR *) coef->MCU_buffer[blkn + blockcnt], @@ -205,7 +203,7 @@ compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) /* 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 (! (*lossyc->entropy_encode_mcu) (cinfo, coef->MCU_buffer)) { + if (! (*cinfo->entropy->encode_mcu) (cinfo, coef->MCU_buffer)) { /* Suspension forced; update state counters and exit */ coef->MCU_vert_offset = yoffset; coef->mcu_ctr = MCU_col_num; @@ -248,8 +246,7 @@ compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) METHODDEF(boolean) compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; JDIMENSION blocks_across, MCUs_across, MCUindex; int bi, ci, h_samp_factor, block_row, block_rows, ndummy; @@ -270,10 +267,10 @@ compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) block_rows = compptr->v_samp_factor; else { /* NB: can't use last_row_height here, since may not be set! */ - block_rows = (int) (compptr->height_in_data_units % compptr->v_samp_factor); + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); if (block_rows == 0) block_rows = compptr->v_samp_factor; } - blocks_across = compptr->width_in_data_units; + blocks_across = compptr->width_in_blocks; h_samp_factor = compptr->h_samp_factor; /* Count number of dummy blocks to be added at the right margin. */ ndummy = (int) (blocks_across % h_samp_factor); @@ -284,7 +281,7 @@ compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) */ for (block_row = 0; block_row < block_rows; block_row++) { thisblockrow = buffer[block_row]; - (*lossyc->fdct_forward_DCT) (cinfo, compptr, + (*cinfo->fdct->forward_DCT) (cinfo, compptr, input_buf[ci], thisblockrow, (JDIMENSION) (block_row * DCTSIZE), (JDIMENSION) 0, blocks_across); @@ -345,8 +342,7 @@ compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) METHODDEF(boolean) compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; JDIMENSION MCU_col_num; /* index of current MCU within row */ int blkn, ci, xindex, yindex, yoffset; JDIMENSION start_col; @@ -384,7 +380,7 @@ compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) } } /* Try to write the MCU. */ - if (! (*lossyc->entropy_encode_mcu) (cinfo, coef->MCU_buffer)) { + if (! (*cinfo->entropy->encode_mcu) (cinfo, coef->MCU_buffer)) { /* Suspension forced; update state counters and exit */ coef->MCU_vert_offset = yoffset; coef->mcu_ctr = MCU_col_num; @@ -410,14 +406,13 @@ compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) GLOBAL(void) jinit_c_coef_controller (j_compress_ptr cinfo, boolean need_full_buffer) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - c_coef_ptr coef; + my_coef_ptr coef; - coef = (c_coef_ptr) + coef = (my_coef_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(c_coef_controller)); - lossyc->coef_private = (struct jpeg_c_coef_controller *) coef; - lossyc->coef_start_pass = start_pass_coef; + SIZEOF(my_coef_controller)); + cinfo->coef = (struct jpeg_c_coef_controller *) coef; + coef->pub.start_pass = start_pass_coef; /* Create the coefficient buffer. */ if (need_full_buffer) { @@ -431,9 +426,9 @@ jinit_c_coef_controller (j_compress_ptr cinfo, boolean need_full_buffer) ci++, compptr++) { coef->whole_image[ci] = (*cinfo->mem->request_virt_barray) ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, - (JDIMENSION) jround_up((long) compptr->width_in_data_units, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, (long) compptr->h_samp_factor), - (JDIMENSION) jround_up((long) compptr->height_in_data_units, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, (long) compptr->v_samp_factor), (JDIMENSION) compptr->v_samp_factor); } @@ -447,8 +442,8 @@ jinit_c_coef_controller (j_compress_ptr cinfo, boolean need_full_buffer) buffer = (JBLOCKROW) (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, - C_MAX_DATA_UNITS_IN_MCU * SIZEOF(JBLOCK)); - for (i = 0; i < C_MAX_DATA_UNITS_IN_MCU; i++) { + C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + for (i = 0; i < C_MAX_BLOCKS_IN_MCU; i++) { coef->MCU_buffer[i] = buffer + i; } coef->whole_image[0] = NULL; /* flag for no virtual arrays */ diff --git a/jccolor.c b/jccolor.c index 0a8a4b5d1..a65a9a155 100644 --- a/jccolor.c +++ b/jccolor.c @@ -1,8 +1,10 @@ /* * jccolor.c * + * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. + * Lossless JPEG Modifications: + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains input colorspace conversion routines. @@ -456,4 +458,13 @@ jinit_color_converter (j_compress_ptr cinfo) cconvert->pub.color_convert = null_convert; break; } + + /* Prevent lossy color conversion in lossless mode */ + if (cinfo->master->lossless) { + if ((cinfo->jpeg_color_space == JCS_GRAYSCALE && + cinfo->in_color_space != JCS_GRAYSCALE) || + (cinfo->jpeg_color_space != JCS_GRAYSCALE && + cconvert->pub.color_convert != null_convert)) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + } } diff --git a/jcdctmgr.c b/jcdctmgr.c index de4dafda2..61fa79b9e 100644 --- a/jcdctmgr.c +++ b/jcdctmgr.c @@ -1,10 +1,8 @@ /* * jcdctmgr.c * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * 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. @@ -16,13 +14,14 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossy.h" /* Private declarations for lossy codec */ #include "jdct.h" /* Private declarations for DCT subsystem */ /* Private subobject for this module */ typedef struct { + struct jpeg_forward_dct pub; /* public fields */ + /* Pointer to the DCT routine actually in use */ forward_DCT_method_ptr do_dct; @@ -37,9 +36,9 @@ typedef struct { float_DCT_method_ptr do_float_dct; FAST_FLOAT * float_divisors[NUM_QUANT_TBLS]; #endif -} fdct_controller; +} my_fdct_controller; -typedef fdct_controller * fdct_ptr; +typedef my_fdct_controller * my_fdct_ptr; /* @@ -54,8 +53,7 @@ typedef fdct_controller * fdct_ptr; METHODDEF(void) start_pass_fdctmgr (j_compress_ptr cinfo) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - fdct_ptr fdct = (fdct_ptr) lossyc->fdct_private; + my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct; int ci, qtblno, i; jpeg_component_info *compptr; JQUANT_TBL * qtbl; @@ -186,8 +184,7 @@ forward_DCT (j_compress_ptr cinfo, jpeg_component_info * compptr, /* This version is used for integer DCT implementations. */ { /* This routine is heavily used, so it's worth coding it tightly. */ - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - fdct_ptr fdct = (fdct_ptr) lossyc->fdct_private; + my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct; forward_DCT_method_ptr do_dct = fdct->do_dct; DCTELEM * divisors = fdct->divisors[compptr->quant_tbl_no]; DCTELEM workspace[DCTSIZE2]; /* work area for FDCT subroutine */ @@ -277,8 +274,7 @@ forward_DCT_float (j_compress_ptr cinfo, jpeg_component_info * compptr, /* This version is used for floating-point DCT implementations. */ { /* This routine is heavily used, so it's worth coding it tightly. */ - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - fdct_ptr fdct = (fdct_ptr) lossyc->fdct_private; + my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct; float_DCT_method_ptr do_dct = fdct->do_float_dct; FAST_FLOAT * divisors = fdct->float_divisors[compptr->quant_tbl_no]; FAST_FLOAT workspace[DCTSIZE2]; /* work area for FDCT subroutine */ @@ -348,32 +344,31 @@ forward_DCT_float (j_compress_ptr cinfo, jpeg_component_info * compptr, GLOBAL(void) jinit_forward_dct (j_compress_ptr cinfo) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - fdct_ptr fdct; + my_fdct_ptr fdct; int i; - fdct = (fdct_ptr) + fdct = (my_fdct_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(fdct_controller)); - lossyc->fdct_private = (struct jpeg_forward_dct *) fdct; - lossyc->fdct_start_pass = start_pass_fdctmgr; + SIZEOF(my_fdct_controller)); + cinfo->fdct = (struct jpeg_forward_dct *) fdct; + fdct->pub.start_pass = start_pass_fdctmgr; switch (cinfo->dct_method) { #ifdef DCT_ISLOW_SUPPORTED case JDCT_ISLOW: - lossyc->fdct_forward_DCT = forward_DCT; + fdct->pub.forward_DCT = forward_DCT; fdct->do_dct = jpeg_fdct_islow; break; #endif #ifdef DCT_IFAST_SUPPORTED case JDCT_IFAST: - lossyc->fdct_forward_DCT = forward_DCT; + fdct->pub.forward_DCT = forward_DCT; fdct->do_dct = jpeg_fdct_ifast; break; #endif #ifdef DCT_FLOAT_SUPPORTED case JDCT_FLOAT: - lossyc->fdct_forward_DCT = forward_DCT_float; + fdct->pub.forward_DCT = forward_DCT_float; fdct->do_float_dct = jpeg_fdct_float; break; #endif diff --git a/jcdiffct.c b/jcdiffct.c index 6ad2e8dd3..a5c951ebc 100644 --- a/jcdiffct.c +++ b/jcdiffct.c @@ -2,15 +2,16 @@ * jcdiffct.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. + * Copyright (C) 1994-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains the difference buffer controller for compression. * This controller is the top level of the lossless JPEG compressor proper. - * The difference buffer lies between prediction/differencing and entropy - * encoding. + * The difference buffer lies between the prediction/differencing and entropy + * encoding steps. */ #define JPEG_INTERNALS @@ -38,20 +39,22 @@ /* Private buffer controller object */ typedef struct { + struct jpeg_c_coef_controller pub; /* public fields */ + JDIMENSION iMCU_row_num; /* iMCU row # within image */ JDIMENSION mcu_ctr; /* counts MCUs processed in current row */ int MCU_vert_offset; /* counts MCU rows within iMCU row */ int MCU_rows_per_iMCU_row; /* number of such rows needed */ - JSAMPROW cur_row[MAX_COMPONENTS]; /* row of point transformed samples */ + JSAMPROW cur_row[MAX_COMPONENTS]; /* row of point-transformed samples */ JSAMPROW prev_row[MAX_COMPONENTS]; /* previous row of Pt'd samples */ JDIFFARRAY diff_buf[MAX_COMPONENTS]; /* iMCU row of differences */ /* In multi-pass modes, we need a virtual sample array for each component. */ jvirt_sarray_ptr whole_image[MAX_COMPONENTS]; -} c_diff_controller; +} my_diff_controller; -typedef c_diff_controller * c_diff_ptr; +typedef my_diff_controller * my_diff_ptr; /* Forward declarations */ @@ -69,8 +72,7 @@ LOCAL(void) start_iMCU_row (j_compress_ptr cinfo) /* Reset within-iMCU-row counters for a new row */ { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - c_diff_ptr diff = (c_diff_ptr) losslsc->diff_private; + my_diff_ptr diff = (my_diff_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. @@ -97,8 +99,15 @@ start_iMCU_row (j_compress_ptr cinfo) METHODDEF(void) start_pass_diff (j_compress_ptr cinfo, J_BUF_MODE pass_mode) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - c_diff_ptr diff = (c_diff_ptr) losslsc->diff_private; + my_diff_ptr diff = (my_diff_ptr) cinfo->coef; + + /* Because it is hitching a ride on the jpeg_forward_dct struct, + * start_pass_lossless() will be called at the start of the initial pass. + * This ensures that it will be called at the start of the Huffman + * optimization and output passes as well. + */ + if (pass_mode == JBUF_CRANK_DEST) + (*cinfo->fdct->start_pass) (cinfo); diff->iMCU_row_num = 0; start_iMCU_row(cinfo); @@ -107,18 +116,18 @@ start_pass_diff (j_compress_ptr cinfo, J_BUF_MODE pass_mode) case JBUF_PASS_THRU: if (diff->whole_image[0] != NULL) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - losslsc->pub.compress_data = compress_data; + diff->pub.compress_data = compress_data; break; #ifdef FULL_SAMP_BUFFER_SUPPORTED case JBUF_SAVE_AND_PASS: if (diff->whole_image[0] == NULL) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - losslsc->pub.compress_data = compress_first_pass; + diff->pub.compress_data = compress_first_pass; break; case JBUF_CRANK_DEST: if (diff->whole_image[0] == NULL) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - losslsc->pub.compress_data = compress_output; + diff->pub.compress_data = compress_output; break; #endif default: @@ -143,13 +152,12 @@ start_pass_diff (j_compress_ptr cinfo, J_BUF_MODE pass_mode) METHODDEF(boolean) compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - c_diff_ptr diff = (c_diff_ptr) losslsc->diff_private; + my_diff_ptr diff = (my_diff_ptr) cinfo->coef; + lossless_comp_ptr losslessc = (lossless_comp_ptr) cinfo->fdct; JDIMENSION MCU_col_num; /* index of current MCU within row */ JDIMENSION MCU_count; /* number of MCUs encoded */ - JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; - int comp, ci, yoffset, samp_row, samp_rows, samps_across; + int ci, compi, yoffset, samp_row, samp_rows, samps_across; jpeg_component_info *compptr; /* Loop to write as much as one whole iMCU row */ @@ -158,20 +166,20 @@ compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) MCU_col_num = diff->mcu_ctr; - /* Scale and predict each scanline of the MCU-row separately. + /* Scale and predict each scanline of the MCU row separately. * - * Note: We only do this if we are at the start of a MCU-row, ie, + * Note: We only do this if we are at the start of an MCU row, ie, * we don't want to reprocess a row suspended by the output. */ if (MCU_col_num == 0) { - for (comp = 0; comp < cinfo->comps_in_scan; comp++) { - compptr = cinfo->cur_comp_info[comp]; - ci = compptr->component_index; + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + compi = compptr->component_index; if (diff->iMCU_row_num < last_iMCU_row) samp_rows = compptr->v_samp_factor; else { /* NB: can't use last_row_height here, since may not be set! */ - samp_rows = (int) (compptr->height_in_data_units % compptr->v_samp_factor); + samp_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); if (samp_rows == 0) samp_rows = compptr->v_samp_factor; else { /* Fill dummy difference rows at the bottom edge with zeros, which @@ -179,43 +187,39 @@ compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) */ for (samp_row = samp_rows; samp_row < compptr->v_samp_factor; samp_row++) - MEMZERO(diff->diff_buf[ci][samp_row], - jround_up((long) compptr->width_in_data_units, + MEMZERO(diff->diff_buf[compi][samp_row], + jround_up((long) compptr->width_in_blocks, (long) compptr->h_samp_factor) * SIZEOF(JDIFF)); } } - samps_across = compptr->width_in_data_units; + samps_across = compptr->width_in_blocks; for (samp_row = 0; samp_row < samp_rows; samp_row++) { - (*losslsc->scaler_scale) (cinfo, - input_buf[ci][samp_row], - diff->cur_row[ci], samps_across); - (*losslsc->predict_difference[ci]) (cinfo, ci, - diff->cur_row[ci], - diff->prev_row[ci], - diff->diff_buf[ci][samp_row], - samps_across); - SWAP_ROWS(diff->cur_row[ci], diff->prev_row[ci]); + (*losslessc->scaler_scale) (cinfo, + input_buf[compi][samp_row], + diff->cur_row[compi], + samps_across); + (*losslessc->predict_difference[compi]) + (cinfo, compi, diff->cur_row[compi], diff->prev_row[compi], + diff->diff_buf[compi][samp_row], samps_across); + SWAP_ROWS(diff->cur_row[compi], diff->prev_row[compi]); } } } - - /* Try to write the MCU-row (or remaining portion of suspended MCU-row). */ + /* Try to write the MCU row (or remaining portion of suspended MCU row). */ MCU_count = - (*losslsc->entropy_encode_mcus) (cinfo, - diff->diff_buf, yoffset, MCU_col_num, - cinfo->MCUs_per_row - MCU_col_num); + (*cinfo->entropy->encode_mcus) (cinfo, + diff->diff_buf, yoffset, MCU_col_num, + cinfo->MCUs_per_row - MCU_col_num); if (MCU_count != cinfo->MCUs_per_row - MCU_col_num) { /* Suspension forced; update state counters and exit */ diff->MCU_vert_offset = yoffset; diff->mcu_ctr += MCU_col_num; return FALSE; } - /* Completed an MCU row, but perhaps not an iMCU row */ diff->mcu_ctr = 0; } - /* Completed the iMCU row, advance counters for next one */ diff->iMCU_row_num++; start_iMCU_row(cinfo); @@ -246,18 +250,17 @@ compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) METHODDEF(boolean) compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - c_diff_ptr diff = (c_diff_ptr) losslsc->diff_private; + my_diff_ptr diff = (my_diff_ptr) cinfo->coef; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; JDIMENSION samps_across; int ci, samp_row, samp_rows; - JSAMPARRAY buffer[MAX_COMPONENTS]; + JSAMPARRAY buffer; jpeg_component_info *compptr; for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - /* Align the virtual buffers for this component. */ - buffer[ci] = (*cinfo->mem->access_virt_sarray) + /* Align the virtual buffer for this component. */ + buffer = (*cinfo->mem->access_virt_sarray) ((j_common_ptr) cinfo, diff->whole_image[ci], diff->iMCU_row_num * compptr->v_samp_factor, (JDIMENSION) compptr->v_samp_factor, TRUE); @@ -267,21 +270,20 @@ compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) samp_rows = compptr->v_samp_factor; else { /* NB: can't use last_row_height here, since may not be set! */ - samp_rows = (int) (compptr->height_in_data_units % compptr->v_samp_factor); + samp_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); if (samp_rows == 0) samp_rows = compptr->v_samp_factor; } - samps_across = compptr->width_in_data_units; + samps_across = compptr->width_in_blocks; /* Perform point transform scaling and prediction/differencing for all * non-dummy rows in this iMCU row. Each call on these functions - * process a complete row of samples. + * processes a complete row of samples. */ for (samp_row = 0; samp_row < samp_rows; samp_row++) { - MEMCOPY(buffer[ci][samp_row], input_buf[ci][samp_row], + MEMCOPY(buffer[samp_row], input_buf[ci][samp_row], samps_across * SIZEOF(JSAMPLE)); } } - /* NB: compress_output will increment iMCU_row_num if successful. * A suspension return will result in redoing all the work above next time. */ @@ -304,23 +306,19 @@ compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) METHODDEF(boolean) compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - c_diff_ptr diff = (c_diff_ptr) losslsc->diff_private; - JDIMENSION MCU_col_num; /* index of current MCU within row */ - JDIMENSION MCU_count; /* number of MCUs encoded */ - int comp, ci, yoffset; - JSAMPARRAY buffer[MAX_COMPONENTS]; + my_diff_ptr diff = (my_diff_ptr) cinfo->coef; + int ci; + JSAMPARRAY buffer[MAX_COMPS_IN_SCAN]; jpeg_component_info *compptr; /* Align the virtual buffers for the components used in this scan. * NB: during first pass, this is safe only because the buffers will * already be aligned properly, so jmemmgr.c won't need to do any I/O. */ - for (comp = 0; comp < cinfo->comps_in_scan; comp++) { - compptr = cinfo->cur_comp_info[comp]; - ci = compptr->component_index; - buffer[ci] = (*cinfo->mem->access_virt_sarray) - ((j_common_ptr) cinfo, diff->whole_image[ci], + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + buffer[compptr->component_index] = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, diff->whole_image[compptr->component_index], diff->iMCU_row_num * compptr->v_samp_factor, (JDIMENSION) compptr->v_samp_factor, FALSE); } @@ -338,28 +336,27 @@ compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) GLOBAL(void) jinit_c_diff_controller (j_compress_ptr cinfo, boolean need_full_buffer) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - c_diff_ptr diff; + my_diff_ptr diff; int ci, row; jpeg_component_info *compptr; - diff = (c_diff_ptr) + diff = (my_diff_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(c_diff_controller)); - losslsc->diff_private = (void *) diff; - losslsc->diff_start_pass = start_pass_diff; + SIZEOF(my_diff_controller)); + cinfo->coef = (struct jpeg_c_coef_controller *) diff; + diff->pub.start_pass = start_pass_diff; /* Create the prediction row buffers. */ for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { diff->cur_row[ci] = *(*cinfo->mem->alloc_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, - (JDIMENSION) jround_up((long) compptr->width_in_data_units, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, (long) compptr->h_samp_factor), (JDIMENSION) 1); diff->prev_row[ci] = *(*cinfo->mem->alloc_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, - (JDIMENSION) jround_up((long) compptr->width_in_data_units, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, (long) compptr->h_samp_factor), (JDIMENSION) 1); } @@ -367,11 +364,11 @@ jinit_c_diff_controller (j_compress_ptr cinfo, boolean need_full_buffer) /* Create the difference buffer. */ for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - diff->diff_buf[ci] = (*cinfo->mem->alloc_darray) - ((j_common_ptr) cinfo, JPOOL_IMAGE, - (JDIMENSION) jround_up((long) compptr->width_in_data_units, - (long) compptr->h_samp_factor), - (JDIMENSION) compptr->v_samp_factor); + diff->diff_buf[ci] = + ALLOC_DARRAY(JPOOL_IMAGE, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) compptr->v_samp_factor); /* Prefill difference rows with zeros. We do this because only actual * data is placed in the buffers during prediction/differencing, leaving * any dummy differences at the right edge as zeros, which will encode @@ -379,7 +376,7 @@ jinit_c_diff_controller (j_compress_ptr cinfo, boolean need_full_buffer) */ for (row = 0; row < compptr->v_samp_factor; row++) MEMZERO(diff->diff_buf[ci][row], - jround_up((long) compptr->width_in_data_units, + jround_up((long) compptr->width_in_blocks, (long) compptr->h_samp_factor) * SIZEOF(JDIFF)); } @@ -388,16 +385,13 @@ jinit_c_diff_controller (j_compress_ptr cinfo, boolean need_full_buffer) #ifdef FULL_SAMP_BUFFER_SUPPORTED /* Allocate a full-image virtual array for each component, */ /* padded to a multiple of samp_factor differences in each direction. */ - int ci; - jpeg_component_info *compptr; - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { diff->whole_image[ci] = (*cinfo->mem->request_virt_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, - (JDIMENSION) jround_up((long) compptr->width_in_data_units, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, (long) compptr->h_samp_factor), - (JDIMENSION) jround_up((long) compptr->height_in_data_units, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, (long) compptr->v_samp_factor), (JDIMENSION) compptr->v_samp_factor); } diff --git a/jchuff.c b/jchuff.c index 26e1442b2..27e544aa4 100644 --- a/jchuff.c +++ b/jchuff.c @@ -2,13 +2,19 @@ * jchuff.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * - * This file contains Huffman entropy decoding routines which are shared - * by the sequential, progressive and lossless decoders. + * This file contains Huffman entropy encoding routines. + * + * Much of the complexity here has to do with supporting output suspension. + * If the data destination module demands suspension, we want to be able to + * back up to the start of the current MCU. To do this, we copy state + * variables into local working storage, and update them back to the + * permanent JPEG objects only upon successful completion of an MCU. */ #define JPEG_INTERNALS @@ -17,9 +23,159 @@ #include "jchuff.h" /* Declarations shared with jc*huff.c */ +/* Expanded entropy encoder object for Huffman encoding. + * + * The savable_state subrecord contains fields that change within an MCU, + * but must not be updated permanently until we complete the MCU. + */ + +typedef struct { + INT32 put_buffer; /* current bit-accumulation buffer */ + int put_bits; /* # of bits now in it */ + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ +} savable_state; + +/* This macro is to work around compilers with missing or broken + * structure assignment. You'll need to fix this code if you have + * such a compiler and you change MAX_COMPS_IN_SCAN. + */ + +#ifndef NO_STRUCT_ASSIGN +#define ASSIGN_STATE(dest,src) ((dest) = (src)) +#else +#if MAX_COMPS_IN_SCAN == 4 +#define ASSIGN_STATE(dest,src) \ + ((dest).put_buffer = (src).put_buffer, \ + (dest).put_bits = (src).put_bits, \ + (dest).last_dc_val[0] = (src).last_dc_val[0], \ + (dest).last_dc_val[1] = (src).last_dc_val[1], \ + (dest).last_dc_val[2] = (src).last_dc_val[2], \ + (dest).last_dc_val[3] = (src).last_dc_val[3]) +#endif +#endif + + +typedef struct { + struct jpeg_entropy_encoder pub; /* public fields */ + + savable_state saved; /* Bit buffer & DC state at start of MCU */ + + /* These fields are NOT loaded into local working state. */ + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + int next_restart_num; /* next restart number to write (0-7) */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + c_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; + c_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; + +#ifdef ENTROPY_OPT_SUPPORTED /* Statistics tables for optimization */ + long * dc_count_ptrs[NUM_HUFF_TBLS]; + long * ac_count_ptrs[NUM_HUFF_TBLS]; +#endif +} huff_entropy_encoder; + +typedef huff_entropy_encoder * huff_entropy_ptr; + +/* Working state while writing an MCU. + * This struct contains all the fields that are needed by subroutines. + */ + +typedef struct { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + savable_state cur; /* Current bit buffer & DC state */ + j_compress_ptr cinfo; /* dump_buffer needs access to this */ +} working_state; + + +/* Forward declarations */ +METHODDEF(boolean) encode_mcu_huff JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(void) finish_pass_huff JPP((j_compress_ptr cinfo)); +#ifdef ENTROPY_OPT_SUPPORTED +METHODDEF(boolean) encode_mcu_gather JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(void) finish_pass_gather JPP((j_compress_ptr cinfo)); +#endif + + +/* + * Initialize for a Huffman-compressed scan. + * If gather_statistics is TRUE, we do not output anything during the scan, + * just count the Huffman symbols used and generate Huffman code tables. + */ + +METHODDEF(void) +start_pass_huff (j_compress_ptr cinfo, boolean gather_statistics) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci, dctbl, actbl; + jpeg_component_info * compptr; + + if (gather_statistics) { +#ifdef ENTROPY_OPT_SUPPORTED + entropy->pub.encode_mcu = encode_mcu_gather; + entropy->pub.finish_pass = finish_pass_gather; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + entropy->pub.encode_mcu = encode_mcu_huff; + entropy->pub.finish_pass = finish_pass_huff; + } + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + actbl = compptr->ac_tbl_no; + if (gather_statistics) { +#ifdef ENTROPY_OPT_SUPPORTED + /* Check for invalid table indexes */ + /* (make_c_derived_tbl does this in the other path) */ + if (dctbl < 0 || dctbl >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, dctbl); + if (actbl < 0 || actbl >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, actbl); + /* Allocate and zero the statistics tables */ + /* Note that jpeg_gen_optimal_table expects 257 entries in each table! */ + if (entropy->dc_count_ptrs[dctbl] == NULL) + entropy->dc_count_ptrs[dctbl] = (long *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + 257 * SIZEOF(long)); + MEMZERO(entropy->dc_count_ptrs[dctbl], 257 * SIZEOF(long)); + if (entropy->ac_count_ptrs[actbl] == NULL) + entropy->ac_count_ptrs[actbl] = (long *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + 257 * SIZEOF(long)); + MEMZERO(entropy->ac_count_ptrs[actbl], 257 * SIZEOF(long)); +#endif + } else { + /* Compute derived values for Huffman tables */ + /* We may do this more than once for a table, but it's not expensive */ + jpeg_make_c_derived_tbl(cinfo, TRUE, dctbl, + & entropy->dc_derived_tbls[dctbl]); + jpeg_make_c_derived_tbl(cinfo, FALSE, actbl, + & entropy->ac_derived_tbls[actbl]); + } + /* Initialize DC predictions to 0 */ + entropy->saved.last_dc_val[ci] = 0; + } + + /* Initialize bit buffer to empty */ + entropy->saved.put_buffer = 0; + entropy->saved.put_bits = 0; + + /* Initialize restart stuff */ + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num = 0; +} + + /* * Compute the derived values for a Huffman table. * This routine also performs some validation checks on the table. + * + * Note this is also used by jcphuff.c and jclhuff.c. */ GLOBAL(void) @@ -94,12 +250,12 @@ jpeg_make_c_derived_tbl (j_compress_ptr cinfo, boolean isDC, int tblno, */ 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..16 for DC. (We could constrain them further - * based on data depth and mode, but this seems enough.) + /* 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 in + * lossy mode and 0..16 for DC in lossless mode. (We could constrain them + * further based on data depth and mode, but this seems enough.) */ - maxsymbol = isDC ? 16 : 255; + maxsymbol = isDC ? (cinfo->master->lossless ? 16 : 15) : 255; for (p = 0; p < lastp; p++) { i = htbl->huffval[p]; @@ -111,8 +267,418 @@ jpeg_make_c_derived_tbl (j_compress_ptr cinfo, boolean isDC, int tblno, } +/* Outputting bytes to the file */ + +/* Emit a byte, taking 'action' if must suspend. */ +#define emit_byte(state,val,action) \ + { *(state)->next_output_byte++ = (JOCTET) (val); \ + if (--(state)->free_in_buffer == 0) \ + if (! dump_buffer(state)) \ + { action; } } + + +LOCAL(boolean) +dump_buffer (working_state * state) +/* Empty the output buffer; return TRUE if successful, FALSE if must suspend */ +{ + struct jpeg_destination_mgr * dest = state->cinfo->dest; + + if (! (*dest->empty_output_buffer) (state->cinfo)) + return FALSE; + /* After a successful buffer dump, must reset buffer pointers */ + state->next_output_byte = dest->next_output_byte; + state->free_in_buffer = dest->free_in_buffer; + return TRUE; +} + + +/* Outputting bits to the file */ + +/* Only the right 24 bits of put_buffer are used; the valid bits are + * left-justified in this part. At most 16 bits can be passed to emit_bits + * in one call, and we never retain more than 7 bits in put_buffer + * between calls, so 24 bits are sufficient. + */ + +INLINE +LOCAL(boolean) +emit_bits (working_state * state, unsigned int code, int size) +/* Emit some bits; return TRUE if successful, FALSE if must suspend */ +{ + /* This routine is heavily used, so it's worth coding tightly. */ + register INT32 put_buffer = (INT32) code; + register int put_bits = state->cur.put_bits; + + /* if size is 0, caller used an invalid Huffman table entry */ + if (size == 0) + ERREXIT(state->cinfo, JERR_HUFF_MISSING_CODE); + + put_buffer &= (((INT32) 1)<cur.put_buffer; /* and merge with old buffer contents */ + + while (put_bits >= 8) { + int c = (int) ((put_buffer >> 16) & 0xFF); + + emit_byte(state, c, return FALSE); + if (c == 0xFF) { /* need to stuff a zero byte? */ + emit_byte(state, 0, return FALSE); + } + put_buffer <<= 8; + put_bits -= 8; + } + + state->cur.put_buffer = put_buffer; /* update state variables */ + state->cur.put_bits = put_bits; + + return TRUE; +} + + +LOCAL(boolean) +flush_bits (working_state * state) +{ + if (! emit_bits(state, 0x7F, 7)) /* fill any partial byte with ones */ + return FALSE; + state->cur.put_buffer = 0; /* and reset bit-buffer to empty */ + state->cur.put_bits = 0; + return TRUE; +} + + +/* Encode a single block's worth of coefficients */ + +LOCAL(boolean) +encode_one_block (working_state * state, JCOEFPTR block, int last_dc_val, + c_derived_tbl *dctbl, c_derived_tbl *actbl) +{ + register int temp, temp2; + register int nbits; + register int k, r, i; + + /* Encode the DC coefficient difference per section F.1.2.1 */ + + temp = temp2 = block[0] - last_dc_val; + + if (temp < 0) { + temp = -temp; /* temp is abs value of input */ + /* For a negative input, want temp2 = bitwise complement of abs(input) */ + /* This code assumes we are on a two's complement machine */ + temp2--; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 0; + while (temp) { + nbits++; + temp >>= 1; + } + /* Check for out-of-range coefficient values. + * Since we're encoding a difference, the range limit is twice as much. + */ + if (nbits > MAX_COEF_BITS+1) + ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); + + /* Emit the Huffman-coded symbol for the number of bits */ + if (! emit_bits(state, dctbl->ehufco[nbits], dctbl->ehufsi[nbits])) + return FALSE; + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + if (nbits) /* emit_bits rejects calls with size 0 */ + if (! emit_bits(state, (unsigned int) temp2, nbits)) + return FALSE; + + /* Encode the AC coefficients per section F.1.2.2 */ + + r = 0; /* r = run length of zeros */ + + for (k = 1; k < DCTSIZE2; k++) { + if ((temp = block[jpeg_natural_order[k]]) == 0) { + r++; + } else { + /* if run length > 15, must emit special run-length-16 codes (0xF0) */ + while (r > 15) { + if (! emit_bits(state, actbl->ehufco[0xF0], actbl->ehufsi[0xF0])) + return FALSE; + r -= 16; + } + + temp2 = temp; + if (temp < 0) { + temp = -temp; /* temp is abs value of input */ + /* This code assumes we are on a two's complement machine */ + temp2--; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 1; /* there must be at least one 1 bit */ + while ((temp >>= 1)) + nbits++; + /* Check for out-of-range coefficient values */ + if (nbits > MAX_COEF_BITS) + ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); + + /* Emit Huffman symbol for run length / number of bits */ + i = (r << 4) + nbits; + if (! emit_bits(state, actbl->ehufco[i], actbl->ehufsi[i])) + return FALSE; + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + if (! emit_bits(state, (unsigned int) temp2, nbits)) + return FALSE; + + r = 0; + } + } + + /* If the last coef(s) were zero, emit an end-of-block code */ + if (r > 0) + if (! emit_bits(state, actbl->ehufco[0], actbl->ehufsi[0])) + return FALSE; + + return TRUE; +} + + +/* + * Emit a restart marker & resynchronize predictions. + */ + +LOCAL(boolean) +emit_restart (working_state * state, int restart_num) +{ + int ci; + + if (! flush_bits(state)) + return FALSE; + + emit_byte(state, 0xFF, return FALSE); + emit_byte(state, JPEG_RST0 + restart_num, return FALSE); + + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < state->cinfo->comps_in_scan; ci++) + state->cur.last_dc_val[ci] = 0; + + /* The restart counter is not updated until we successfully write the MCU. */ + + return TRUE; +} + + +/* + * Encode and output one MCU's worth of Huffman-compressed coefficients. + */ + +METHODDEF(boolean) +encode_mcu_huff (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + working_state state; + int blkn, ci; + jpeg_component_info * compptr; + + /* Load up working state */ + state.next_output_byte = cinfo->dest->next_output_byte; + state.free_in_buffer = cinfo->dest->free_in_buffer; + ASSIGN_STATE(state.cur, entropy->saved); + state.cinfo = cinfo; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! emit_restart(&state, entropy->next_restart_num)) + return FALSE; + } + + /* Encode the MCU data blocks */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + if (! encode_one_block(&state, + MCU_data[blkn][0], state.cur.last_dc_val[ci], + entropy->dc_derived_tbls[compptr->dc_tbl_no], + entropy->ac_derived_tbls[compptr->ac_tbl_no])) + return FALSE; + /* Update last_dc_val */ + state.cur.last_dc_val[ci] = MCU_data[blkn][0][0]; + } + + /* Completed MCU, so update state */ + cinfo->dest->next_output_byte = state.next_output_byte; + cinfo->dest->free_in_buffer = state.free_in_buffer; + ASSIGN_STATE(entropy->saved, state.cur); + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + + +/* + * Finish up at the end of a Huffman-compressed scan. + */ + +METHODDEF(void) +finish_pass_huff (j_compress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + working_state state; + + /* Load up working state ... flush_bits needs it */ + state.next_output_byte = cinfo->dest->next_output_byte; + state.free_in_buffer = cinfo->dest->free_in_buffer; + ASSIGN_STATE(state.cur, entropy->saved); + state.cinfo = cinfo; + + /* Flush out the last data */ + if (! flush_bits(&state)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + + /* Update state */ + cinfo->dest->next_output_byte = state.next_output_byte; + cinfo->dest->free_in_buffer = state.free_in_buffer; + ASSIGN_STATE(entropy->saved, state.cur); +} + + +/* + * Huffman coding optimization. + * + * We first scan the supplied data and count the number of uses of each symbol + * that is to be Huffman-coded. (This process MUST agree with the code above.) + * Then we build a Huffman coding tree for the observed counts. + * Symbols which are not needed at all for the particular image are not + * assigned any code, which saves space in the DHT marker as well as in + * the compressed data. + */ + +#ifdef ENTROPY_OPT_SUPPORTED + + +/* Process a single block's worth of coefficients */ + +LOCAL(void) +htest_one_block (j_compress_ptr cinfo, JCOEFPTR block, int last_dc_val, + long dc_counts[], long ac_counts[]) +{ + register int temp; + register int nbits; + register int k, r; + + /* Encode the DC coefficient difference per section F.1.2.1 */ + + temp = block[0] - last_dc_val; + if (temp < 0) + temp = -temp; + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 0; + while (temp) { + nbits++; + temp >>= 1; + } + /* Check for out-of-range coefficient values. + * Since we're encoding a difference, the range limit is twice as much. + */ + if (nbits > MAX_COEF_BITS+1) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count the Huffman symbol for the number of bits */ + dc_counts[nbits]++; + + /* Encode the AC coefficients per section F.1.2.2 */ + + r = 0; /* r = run length of zeros */ + + for (k = 1; k < DCTSIZE2; k++) { + if ((temp = block[jpeg_natural_order[k]]) == 0) { + r++; + } else { + /* if run length > 15, must emit special run-length-16 codes (0xF0) */ + while (r > 15) { + ac_counts[0xF0]++; + r -= 16; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + if (temp < 0) + temp = -temp; + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 1; /* there must be at least one 1 bit */ + while ((temp >>= 1)) + nbits++; + /* Check for out-of-range coefficient values */ + if (nbits > MAX_COEF_BITS) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count Huffman symbol for run length / number of bits */ + ac_counts[(r << 4) + nbits]++; + + r = 0; + } + } + + /* If the last coef(s) were zero, emit an end-of-block code */ + if (r > 0) + ac_counts[0]++; +} + + +/* + * Trial-encode one MCU's worth of Huffman-compressed coefficients. + * No data is actually output, so no suspension return is possible. + */ + +METHODDEF(boolean) +encode_mcu_gather (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int blkn, ci; + jpeg_component_info * compptr; + + /* Take care of restart intervals if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) + entropy->saved.last_dc_val[ci] = 0; + /* Update restart state */ + entropy->restarts_to_go = cinfo->restart_interval; + } + entropy->restarts_to_go--; + } + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + htest_one_block(cinfo, MCU_data[blkn][0], entropy->saved.last_dc_val[ci], + entropy->dc_count_ptrs[compptr->dc_tbl_no], + entropy->ac_count_ptrs[compptr->ac_tbl_no]); + entropy->saved.last_dc_val[ci] = MCU_data[blkn][0][0]; + } + + return TRUE; +} + + /* * Generate the best Huffman code table for the given counts, fill htbl. + * Note this is also used by jcphuff.c and jclhuff.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 @@ -273,3 +839,74 @@ jpeg_gen_optimal_table (j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[]) /* Set sent_table FALSE so updated table will be written to JPEG file. */ htbl->sent_table = FALSE; } + + +/* + * Finish up a statistics-gathering pass and create the new Huffman tables. + */ + +METHODDEF(void) +finish_pass_gather (j_compress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci, dctbl, actbl; + jpeg_component_info * compptr; + JHUFF_TBL **htblptr; + boolean did_dc[NUM_HUFF_TBLS]; + boolean did_ac[NUM_HUFF_TBLS]; + + /* It's important not to apply jpeg_gen_optimal_table more than once + * per table, because it clobbers the input frequency counts! + */ + MEMZERO(did_dc, SIZEOF(did_dc)); + MEMZERO(did_ac, SIZEOF(did_ac)); + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + actbl = compptr->ac_tbl_no; + if (! did_dc[dctbl]) { + htblptr = & cinfo->dc_huff_tbl_ptrs[dctbl]; + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + jpeg_gen_optimal_table(cinfo, *htblptr, entropy->dc_count_ptrs[dctbl]); + did_dc[dctbl] = TRUE; + } + if (! did_ac[actbl]) { + htblptr = & cinfo->ac_huff_tbl_ptrs[actbl]; + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + jpeg_gen_optimal_table(cinfo, *htblptr, entropy->ac_count_ptrs[actbl]); + did_ac[actbl] = TRUE; + } + } +} + + +#endif /* ENTROPY_OPT_SUPPORTED */ + + +/* + * Module initialization routine for Huffman entropy encoding. + */ + +GLOBAL(void) +jinit_huff_encoder (j_compress_ptr cinfo) +{ + huff_entropy_ptr entropy; + int i; + + entropy = (huff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(huff_entropy_encoder)); + cinfo->entropy = (struct jpeg_entropy_encoder *) entropy; + entropy->pub.start_pass = start_pass_huff; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL; +#ifdef ENTROPY_OPT_SUPPORTED + entropy->dc_count_ptrs[i] = entropy->ac_count_ptrs[i] = NULL; +#endif + } +} diff --git a/jchuff.h b/jchuff.h index b175ca349..a9599fc1e 100644 --- a/jchuff.h +++ b/jchuff.h @@ -1,10 +1,8 @@ /* * jchuff.h * - * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * * This file contains declarations for Huffman entropy encoding routines @@ -24,13 +22,6 @@ #define MAX_COEF_BITS 14 #endif -/* The legal range of a spatial difference is - * -32767 .. +32768. - * Hence the magnitude should always fit in 16 bits. - */ - -#define MAX_DIFF_BITS 16 - /* Derived data constructed for each Huffman table */ typedef struct { diff --git a/jcinit.c b/jcinit.c index e46c03dab..008512d21 100644 --- a/jcinit.c +++ b/jcinit.c @@ -5,6 +5,7 @@ * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains initialization logic for the JPEG compressor. @@ -34,9 +35,6 @@ jinit_compress_master (j_compress_ptr cinfo) /* Initialize master control (includes parameter checking/processing) */ jinit_c_master_control(cinfo, FALSE /* full compression */); - /* Initialize compression codec */ - jinit_c_codec(cinfo); - /* Preprocessing */ if (! cinfo->raw_data_in) { jinit_color_converter(cinfo); @@ -44,6 +42,45 @@ jinit_compress_master (j_compress_ptr cinfo) jinit_c_prep_controller(cinfo, FALSE /* never need full buffer here */); } + if (cinfo->master->lossless) { +#ifdef C_LOSSLESS_SUPPORTED + /* Prediction, sample differencing, and point transform */ + jinit_lossless_compressor(cinfo); + /* Entropy encoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + jinit_lhuff_encoder(cinfo); + } + + /* Need a full-image difference buffer in any multi-pass mode. */ + jinit_c_diff_controller(cinfo, + (boolean) (cinfo->num_scans > 1 || cinfo->optimize_coding)); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + /* Forward DCT */ + jinit_forward_dct(cinfo); + /* Entropy encoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef C_PROGRESSIVE_SUPPORTED + jinit_phuff_encoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_encoder(cinfo); + } + + /* Need a full-image coefficient buffer in any multi-pass mode. */ + jinit_c_coef_controller(cinfo, + (boolean) (cinfo->num_scans > 1 || cinfo->optimize_coding)); + } + jinit_c_main_controller(cinfo, FALSE /* never need full buffer here */); jinit_marker_writer(cinfo); diff --git a/jclhuff.c b/jclhuff.c index 7764f5b75..a83943c17 100644 --- a/jclhuff.c +++ b/jclhuff.c @@ -2,9 +2,10 @@ * jclhuff.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains Huffman entropy encoding routines for lossless JPEG. @@ -23,7 +24,17 @@ #include "jchuff.h" /* Declarations shared with jc*huff.c */ -/* Expanded entropy encoder object for Huffman encoding. +#ifdef C_LOSSLESS_SUPPORTED + +/* The legal range of a spatial difference is + * -32767 .. +32768. + * Hence the magnitude should always fit in 16 bits. + */ + +#define MAX_DIFF_BITS 16 + + +/* Expanded entropy encoder object for Huffman encoding in lossless mode. * * The savable_state subrecord contains fields that change within an MCU, * but must not be updated permanently until we complete the MCU. @@ -54,6 +65,8 @@ typedef struct { typedef struct { + struct jpeg_entropy_encoder pub; /* public fields */ + savable_state saved; /* Bit buffer at start of MCU */ /* These fields are NOT loaded into local working state. */ @@ -64,19 +77,19 @@ typedef struct { c_derived_tbl * derived_tbls[NUM_HUFF_TBLS]; /* Pointers to derived tables to be used for each data unit within an MCU */ - c_derived_tbl * cur_tbls[C_MAX_DATA_UNITS_IN_MCU]; + c_derived_tbl * cur_tbls[C_MAX_BLOCKS_IN_MCU]; #ifdef ENTROPY_OPT_SUPPORTED /* Statistics tables for optimization */ long * count_ptrs[NUM_HUFF_TBLS]; /* Pointers to stats tables to be used for each data unit within an MCU */ - long * cur_counts[C_MAX_DATA_UNITS_IN_MCU]; + long * cur_counts[C_MAX_BLOCKS_IN_MCU]; #endif /* Pointers to the proper input difference row for each group of data units * within an MCU. For each component, there are Vi groups of Hi data units. */ - JDIFFROW input_ptr[C_MAX_DATA_UNITS_IN_MCU]; + JDIFFROW input_ptr[C_MAX_BLOCKS_IN_MCU]; /* Number of input pointers in use for the current MCU. This is the sum * of all Vi in the MCU. @@ -86,10 +99,10 @@ typedef struct { /* Information used for positioning the input pointers within the input * difference rows. */ - lhe_input_ptr_info input_ptr_info[C_MAX_DATA_UNITS_IN_MCU]; + lhe_input_ptr_info input_ptr_info[C_MAX_BLOCKS_IN_MCU]; /* Index of the proper input pointer for each data unit within an MCU */ - int input_ptr_index[C_MAX_DATA_UNITS_IN_MCU]; + int input_ptr_index[C_MAX_BLOCKS_IN_MCU]; } lhuff_entropy_encoder; @@ -131,23 +144,22 @@ METHODDEF(void) finish_pass_gather JPP((j_compress_ptr cinfo)); */ METHODDEF(void) -start_pass_huff (j_compress_ptr cinfo, boolean gather_statistics) +start_pass_lhuff (j_compress_ptr cinfo, boolean gather_statistics) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) losslsc->entropy_private; + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) cinfo->entropy; int ci, dctbl, sampn, ptrn, yoffset, xoffset; jpeg_component_info * compptr; if (gather_statistics) { #ifdef ENTROPY_OPT_SUPPORTED - losslsc->entropy_encode_mcus = encode_mcus_gather; - losslsc->pub.entropy_finish_pass = finish_pass_gather; + entropy->pub.encode_mcus = encode_mcus_gather; + entropy->pub.finish_pass = finish_pass_gather; #else ERREXIT(cinfo, JERR_NOT_COMPILED); #endif } else { - losslsc->entropy_encode_mcus = encode_mcus_huff; - losslsc->pub.entropy_finish_pass = finish_pass_huff; + entropy->pub.encode_mcus = encode_mcus_huff; + entropy->pub.finish_pass = finish_pass_huff; } for (ci = 0; ci < cinfo->comps_in_scan; ci++) { @@ -176,11 +188,9 @@ start_pass_huff (j_compress_ptr cinfo, boolean gather_statistics) } /* Precalculate encoding info for each sample in an MCU of this scan */ - for (sampn = 0, ptrn = 0; sampn < cinfo->data_units_in_MCU;) { + for (sampn = 0, ptrn = 0; sampn < cinfo->blocks_in_MCU;) { compptr = cinfo->cur_comp_info[cinfo->MCU_membership[sampn]]; ci = compptr->component_index; - /* ci = cinfo->MCU_membership[sampn]; - compptr = cinfo->cur_comp_info[ci];*/ for (yoffset = 0; yoffset < compptr->MCU_height; yoffset++, ptrn++) { /* Precalculate the setup info for each input pointer */ entropy->input_ptr_info[ptrn].ci = ci; @@ -297,8 +307,6 @@ flush_bits (working_state * state) LOCAL(boolean) emit_restart (working_state * state, int restart_num) { - int ci; - if (! flush_bits(state)) return FALSE; @@ -312,7 +320,7 @@ emit_restart (working_state * state, int restart_num) /* - * Encode and output one nMCU's worth of Huffman-compressed differences. + * Encode and output nMCU MCUs' worth of Huffman-compressed differences. */ METHODDEF(JDIMENSION) @@ -320,11 +328,9 @@ encode_mcus_huff (j_compress_ptr cinfo, JDIFFIMAGE diff_buf, JDIMENSION MCU_row_num, JDIMENSION MCU_col_num, JDIMENSION nMCU) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) losslsc->entropy_private; + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) cinfo->entropy; working_state state; int mcu_num, sampn, ci, yoffset, MCU_width, ptrn; - jpeg_component_info * compptr; /* Load up working state */ state.next_output_byte = cinfo->dest->next_output_byte; @@ -351,8 +357,8 @@ encode_mcus_huff (j_compress_ptr cinfo, JDIFFIMAGE diff_buf, for (mcu_num = 0; mcu_num < nMCU; mcu_num++) { /* Inner loop handles the samples in the MCU */ - for (sampn = 0; sampn < cinfo->data_units_in_MCU; sampn++) { - register int temp, temp2, temp3; + for (sampn = 0; sampn < cinfo->blocks_in_MCU; sampn++) { + register int temp, temp2; register int nbits; c_derived_tbl *dctbl = entropy->cur_tbls[sampn]; @@ -380,7 +386,7 @@ encode_mcus_huff (j_compress_ptr cinfo, JDIFFIMAGE diff_buf, /* Check for out-of-range difference values. */ if (nbits > MAX_DIFF_BITS) - ERREXIT(cinfo, JERR_BAD_DIFF); + ERREXIT(cinfo, JERR_BAD_DCT_COEF); /* Emit the Huffman-coded symbol for the number of bits */ if (! emit_bits(&state, dctbl->ehufco[nbits], dctbl->ehufsi[nbits])) @@ -422,8 +428,7 @@ encode_mcus_huff (j_compress_ptr cinfo, JDIFFIMAGE diff_buf, METHODDEF(void) finish_pass_huff (j_compress_ptr cinfo) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) losslsc->entropy_private; + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) cinfo->entropy; working_state state; /* Load up working state ... flush_bits needs it */ @@ -457,7 +462,7 @@ finish_pass_huff (j_compress_ptr cinfo) #ifdef ENTROPY_OPT_SUPPORTED /* - * Trial-encode one nMCU's worth of Huffman-compressed differences. + * Trial-encode nMCU MCUs' worth of Huffman-compressed differences. * No data is actually output, so no suspension return is possible. */ @@ -466,10 +471,8 @@ encode_mcus_gather (j_compress_ptr cinfo, JDIFFIMAGE diff_buf, JDIMENSION MCU_row_num, JDIMENSION MCU_col_num, JDIMENSION nMCU) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) losslsc->entropy_private; + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) cinfo->entropy; int mcu_num, sampn, ci, yoffset, MCU_width, ptrn; - jpeg_component_info * compptr; /* Take care of restart intervals if needed */ if (cinfo->restart_interval) { @@ -492,10 +495,9 @@ encode_mcus_gather (j_compress_ptr cinfo, JDIFFIMAGE diff_buf, for (mcu_num = 0; mcu_num < nMCU; mcu_num++) { /* Inner loop handles the samples in the MCU */ - for (sampn = 0; sampn < cinfo->data_units_in_MCU; sampn++) { + for (sampn = 0; sampn < cinfo->blocks_in_MCU; sampn++) { register int temp; register int nbits; - c_derived_tbl *dctbl = entropy->cur_tbls[sampn]; long * counts = entropy->cur_counts[sampn]; /* Encode the difference per section H.1.2.2 */ @@ -519,7 +521,7 @@ encode_mcus_gather (j_compress_ptr cinfo, JDIFFIMAGE diff_buf, /* Check for out-of-range difference values. */ if (nbits > MAX_DIFF_BITS) - ERREXIT(cinfo, JERR_BAD_DIFF); + ERREXIT(cinfo, JERR_BAD_DCT_COEF); /* Count the Huffman symbol for the number of bits */ counts[nbits]++; @@ -537,8 +539,7 @@ encode_mcus_gather (j_compress_ptr cinfo, JDIFFIMAGE diff_buf, METHODDEF(void) finish_pass_gather (j_compress_ptr cinfo) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) losslsc->entropy_private; + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) cinfo->entropy; int ci, dctbl; jpeg_component_info * compptr; JHUFF_TBL **htblptr; @@ -566,30 +567,21 @@ finish_pass_gather (j_compress_ptr cinfo) #endif /* ENTROPY_OPT_SUPPORTED */ -METHODDEF(boolean) -need_optimization_pass (j_compress_ptr cinfo) -{ - return TRUE; -} - - /* - * Module initialization routine for Huffman entropy encoding. + * Module initialization routine for lossless mode Huffman entropy encoding. */ GLOBAL(void) jinit_lhuff_encoder (j_compress_ptr cinfo) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; lhuff_entropy_ptr entropy; int i; entropy = (lhuff_entropy_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, SIZEOF(lhuff_entropy_encoder)); - losslsc->entropy_private = (struct jpeg_entropy_encoder *) entropy; - losslsc->pub.entropy_start_pass = start_pass_huff; - losslsc->pub.need_optimization_pass = need_optimization_pass; + cinfo->entropy = (struct jpeg_entropy_encoder *) entropy; + entropy->pub.start_pass = start_pass_lhuff; /* Mark tables unallocated */ for (i = 0; i < NUM_HUFF_TBLS; i++) { @@ -599,3 +591,5 @@ jinit_lhuff_encoder (j_compress_ptr cinfo) #endif } } + +#endif /* C_LOSSLESS_SUPPORTED */ diff --git a/jclossls.c b/jclossls.c index 1cd2b3d7e..6e4a4a804 100644 --- a/jclossls.c +++ b/jclossls.c @@ -5,9 +5,11 @@ * Copyright (C) 1998, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * - * This file contains the control logic for the lossless JPEG compressor. + * This file contains prediction, sample differencing, and point transform + * routines for the lossless JPEG compressor. */ #define JPEG_INTERNALS @@ -15,66 +17,301 @@ #include "jpeglib.h" #include "jlossls.h" - #ifdef C_LOSSLESS_SUPPORTED + +/************************** Sample differencing **************************/ + /* - * Initialize for a processing pass. + * In order to avoid a performance penalty for checking which predictor is + * being used and which row is being processed for each call of the + * undifferencer, and to promote optimization, we have separate differencing + * functions for each predictor selection value. + * + * We are able to avoid duplicating source code by implementing the predictors + * and differencers as macros. Each of the differencing functions is simply a + * wrapper around a DIFFERENCE macro with the appropriate PREDICTOR macro + * passed as an argument. + */ + +/* Forward declarations */ +LOCAL(void) reset_predictor JPP((j_compress_ptr cinfo, int ci)); + + +/* Predictor for the first column of the first row: 2^(P-Pt-1) */ +#define INITIAL_PREDICTORx (1 << (cinfo->data_precision - cinfo->Al - 1)) + +/* Predictor for the first column of the remaining rows: Rb */ +#define INITIAL_PREDICTOR2 GETJSAMPLE(prev_row[0]) + + +/* + * 1-Dimensional differencer routine. + * + * This macro implements the 1-D horizontal predictor (1). INITIAL_PREDICTOR + * is used as the special case predictor for the first column, which must be + * either INITIAL_PREDICTOR2 or INITIAL_PREDICTORx. The remaining samples + * use PREDICTOR1. + */ + +#define DIFFERENCE_1D(INITIAL_PREDICTOR) \ + lossless_comp_ptr losslessc = (lossless_comp_ptr) cinfo->fdct; \ + boolean restart = FALSE; \ + int samp, Ra; \ + \ + samp = GETJSAMPLE(*input_buf++); \ + *diff_buf++ = samp - INITIAL_PREDICTOR; \ + \ + while (--width) { \ + Ra = samp; \ + samp = GETJSAMPLE(*input_buf++); \ + *diff_buf++ = samp - PREDICTOR1; \ + } \ + \ + /* Account for restart interval (no-op if not using restarts) */ \ + if (cinfo->restart_interval) { \ + if (--(losslessc->restart_rows_to_go[ci]) == 0) { \ + reset_predictor(cinfo, ci); \ + restart = TRUE; \ + } \ + } + + +/* + * 2-Dimensional differencer routine. + * + * This macro implements the 2-D horizontal predictors (#2-7). PREDICTOR2 is + * used as the special case predictor for the first column. The remaining + * samples use PREDICTOR, which is a function of Ra, Rb, and Rc. + * + * Because prev_row and output_buf may point to the same storage area (in an + * interleaved image with Vi=1, for example), we must take care to buffer Rb/Rc + * before writing the current reconstructed sample value into output_buf. + */ + +#define DIFFERENCE_2D(PREDICTOR) \ + lossless_comp_ptr losslessc = (lossless_comp_ptr) cinfo->fdct; \ + int samp, Ra, Rb, Rc; \ + \ + Rb = GETJSAMPLE(*prev_row++); \ + samp = GETJSAMPLE(*input_buf++); \ + *diff_buf++ = samp - PREDICTOR2; \ + \ + while (--width) { \ + Rc = Rb; \ + Rb = GETJSAMPLE(*prev_row++); \ + Ra = samp; \ + samp = GETJSAMPLE(*input_buf++); \ + *diff_buf++ = samp - PREDICTOR; \ + } \ + \ + /* Account for restart interval (no-op if not using restarts) */ \ + if (cinfo->restart_interval) { \ + if (--losslessc->restart_rows_to_go[ci] == 0) \ + reset_predictor(cinfo, ci); \ + } + + +/* + * Differencers for the second and subsequent rows in a scan or restart + * interval. The first sample in the row is differenced using the vertical + * predictor (2). The rest of the samples are differenced using the predictor + * specified in the scan header. */ METHODDEF(void) -start_pass (j_compress_ptr cinfo, J_BUF_MODE pass_mode) +jpeg_difference1(j_compress_ptr cinfo, int ci, + JSAMPROW input_buf, JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_1D(INITIAL_PREDICTOR2); + (void)(restart); +} + +METHODDEF(void) +jpeg_difference2(j_compress_ptr cinfo, int ci, + JSAMPROW input_buf, JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR2); + (void)(Ra); + (void)(Rc); +} + +METHODDEF(void) +jpeg_difference3(j_compress_ptr cinfo, int ci, + JSAMPROW input_buf, JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR3); + (void)(Ra); +} + +METHODDEF(void) +jpeg_difference4(j_compress_ptr cinfo, int ci, + JSAMPROW input_buf, JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR4); +} + +METHODDEF(void) +jpeg_difference5(j_compress_ptr cinfo, int ci, + JSAMPROW input_buf, JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR5); +} + +METHODDEF(void) +jpeg_difference6(j_compress_ptr cinfo, int ci, + JSAMPROW input_buf, JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; + DIFFERENCE_2D(PREDICTOR6); +} - (*losslsc->scaler_start_pass) (cinfo); - (*losslsc->predict_start_pass) (cinfo); - (*losslsc->diff_start_pass) (cinfo, pass_mode); +METHODDEF(void) +jpeg_difference7(j_compress_ptr cinfo, int ci, + JSAMPROW input_buf, JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR7); + (void)(Rc); } /* - * Initialize the lossless compression codec. - * This is called only once, during master selection. + * Differencer for the first row in a scan or restart interval. The first + * sample in the row is differenced using the special predictor constant + * x=2^(P-Pt-1). The rest of the samples are differenced using the + * 1-D horizontal predictor (1). */ -GLOBAL(void) -jinit_lossless_c_codec(j_compress_ptr cinfo) +METHODDEF(void) +jpeg_difference_first_row(j_compress_ptr cinfo, int ci, + JSAMPROW input_buf, JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) { - j_lossless_c_ptr losslsc; + DIFFERENCE_1D(INITIAL_PREDICTORx); - /* Create subobject in permanent pool */ - losslsc = (j_lossless_c_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, - SIZEOF(jpeg_lossless_c_codec)); - cinfo->codec = (struct jpeg_c_codec *) losslsc; + /* + * Now that we have differenced the first row, we want to use the + * differencer that corresponds to the predictor specified in the + * scan header. + * + * Note that we don't do this if we have just reset the predictor + * for a new restart interval. + */ + if (! restart) { + switch (cinfo->Ss) { + case 1: + losslessc->predict_difference[ci] = jpeg_difference1; + break; + case 2: + losslessc->predict_difference[ci] = jpeg_difference2; + break; + case 3: + losslessc->predict_difference[ci] = jpeg_difference3; + break; + case 4: + losslessc->predict_difference[ci] = jpeg_difference4; + break; + case 5: + losslessc->predict_difference[ci] = jpeg_difference5; + break; + case 6: + losslessc->predict_difference[ci] = jpeg_difference6; + break; + case 7: + losslessc->predict_difference[ci] = jpeg_difference7; + break; + } + } +} + +/* + * Reset predictor at the start of a pass or restart interval. + */ - /* Initialize sub-modules */ +LOCAL(void) +reset_predictor (j_compress_ptr cinfo, int ci) +{ + lossless_comp_ptr losslessc = (lossless_comp_ptr) cinfo->fdct; - /* Scaler */ - jinit_c_scaler(cinfo); + /* Initialize restart counter */ + losslessc->restart_rows_to_go[ci] = + cinfo->restart_interval / cinfo->MCUs_per_row; - /* Differencer */ - jinit_differencer(cinfo); + /* Set difference function to first row function */ + losslessc->predict_difference[ci] = jpeg_difference_first_row; +} - /* Entropy encoding: either Huffman or arithmetic coding. */ - if (cinfo->arith_code) { - ERREXIT(cinfo, JERR_ARITH_NOTIMPL); - } else { - jinit_lhuff_encoder(cinfo); - } - /* Need a full-image difference buffer in any multi-pass mode. */ - jinit_c_diff_controller(cinfo, - (boolean) (cinfo->num_scans > 1 || - cinfo->optimize_coding)); +/********************** Sample downscaling by 2^Pt ***********************/ - /* Initialize method pointers. - * - * Note: entropy_start_pass and entropy_finish_pass are assigned in - * jclhuff.c and compress_data is assigned in jcdiffct.c. +METHODDEF(void) +simple_downscale(j_compress_ptr cinfo, + JSAMPROW input_buf, JSAMPROW output_buf, JDIMENSION width) +{ + while (width--) + *output_buf++ = (JSAMPLE) RIGHT_SHIFT(GETJSAMPLE(*input_buf++), cinfo->Al); +} + + +METHODDEF(void) +noscale(j_compress_ptr cinfo, + JSAMPROW input_buf, JSAMPROW output_buf, JDIMENSION width) +{ + MEMCOPY(output_buf, input_buf, width * SIZEOF(JSAMPLE)); + return; +} + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_lossless (j_compress_ptr cinfo) +{ + lossless_comp_ptr losslessc = (lossless_comp_ptr) cinfo->fdct; + int ci; + + /* Set scaler function based on Pt */ + if (cinfo->Al) + losslessc->scaler_scale = simple_downscale; + else + losslessc->scaler_scale = noscale; + + /* Check that the restart interval is an integer multiple of the number + * of MCUs in an MCU row. */ - losslsc->pub.start_pass = start_pass; + if (cinfo->restart_interval % cinfo->MCUs_per_row != 0) + ERREXIT2(cinfo, JERR_BAD_RESTART, + cinfo->restart_interval, cinfo->MCUs_per_row); + + /* Set predictors for start of pass */ + for (ci = 0; ci < cinfo->num_components; ci++) + reset_predictor(cinfo, ci); +} + + +/* + * Initialize the lossless compressor. + */ + +GLOBAL(void) +jinit_lossless_compressor(j_compress_ptr cinfo) +{ + lossless_comp_ptr losslessc; + + /* Create subobject in permanent pool */ + losslessc = (lossless_comp_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(jpeg_lossless_compressor)); + cinfo->fdct = (struct jpeg_forward_dct *) losslessc; + losslessc->pub.start_pass = start_pass_lossless; } #endif /* C_LOSSLESS_SUPPORTED */ diff --git a/jclossy.c b/jclossy.c deleted file mode 100644 index 07f508c88..000000000 --- a/jclossy.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * jclossy.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains the control logic for the lossy JPEG compressor. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jlossy.h" - - -/* - * Initialize for a processing pass. - */ - -METHODDEF(void) -start_pass (j_compress_ptr cinfo, J_BUF_MODE pass_mode) -{ - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - - (*lossyc->fdct_start_pass) (cinfo); - (*lossyc->coef_start_pass) (cinfo, pass_mode); -} - - -/* - * Initialize the lossy compression codec. - * This is called only once, during master selection. - */ - -GLOBAL(void) -jinit_lossy_c_codec (j_compress_ptr cinfo) -{ - j_lossy_c_ptr lossyc; - - /* Create subobject in permanent pool */ - lossyc = (j_lossy_c_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, - SIZEOF(jpeg_lossy_c_codec)); - cinfo->codec = (struct jpeg_c_codec *) lossyc; - - /* Initialize sub-modules */ - - /* Forward DCT */ - jinit_forward_dct(cinfo); - /* Entropy encoding: either Huffman or arithmetic coding. */ - if (cinfo->arith_code) { - ERREXIT(cinfo, JERR_ARITH_NOTIMPL); - } else { - if (cinfo->process == JPROC_PROGRESSIVE) { -#ifdef C_PROGRESSIVE_SUPPORTED - jinit_phuff_encoder(cinfo); -#else - ERREXIT(cinfo, JERR_NOT_COMPILED); -#endif - } else - jinit_shuff_encoder(cinfo); - } - - /* Need a full-image coefficient buffer in any multi-pass mode. */ - jinit_c_coef_controller(cinfo, - (boolean) (cinfo->num_scans > 1 || - cinfo->optimize_coding)); - - /* Initialize method pointers. - * - * Note: entropy_start_pass and entropy_finish_pass are assigned in - * jcshuff.c or jcphuff.c and compress_data is assigned in jccoefct.c. - */ - lossyc->pub.start_pass = start_pass; -} diff --git a/jcmainct.c b/jcmainct.c index 5e8686fca..7623bf0bb 100644 --- a/jcmainct.c +++ b/jcmainct.c @@ -2,9 +2,10 @@ * jcmainct.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. + * Copyright (C) 1994-1996, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains the main buffer controller for compression. @@ -117,7 +118,7 @@ process_data_simple_main (j_compress_ptr cinfo, JDIMENSION in_rows_avail) { my_main_ptr main = (my_main_ptr) cinfo->main; - int data_unit = cinfo->data_unit; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; while (main->cur_iMCU_row < cinfo->total_iMCU_rows) { /* Read input data if we haven't filled the main buffer yet */ @@ -135,7 +136,7 @@ process_data_simple_main (j_compress_ptr cinfo, return; /* Send the completed row to the compressor */ - if (! (*cinfo->codec->compress_data) (cinfo, main->buffer)) { + if (! (*cinfo->coef->compress_data) (cinfo, main->buffer)) { /* If compressor did not consume the whole row, then we must need to * suspend processing and return to the application. In this situation * we pretend we didn't yet consume the last input row; otherwise, if @@ -177,7 +178,7 @@ process_data_buffer_main (j_compress_ptr cinfo, int ci; jpeg_component_info *compptr; boolean writing = (main->pass_mode != JBUF_CRANK_DEST); - int data_unit = cinfo->data_unit; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; while (main->cur_iMCU_row < cinfo->total_iMCU_rows) { /* Realign the virtual buffers if at the start of an iMCU row. */ @@ -210,7 +211,7 @@ process_data_buffer_main (j_compress_ptr cinfo, /* Emit data, unless this is a sink-only pass. */ if (main->pass_mode != JBUF_SAVE_SOURCE) { - if (! (*cinfo->codec->compress_data) (cinfo, main->buffer)) { + if (! (*cinfo->coef->compress_data) (cinfo, main->buffer)) { /* If compressor did not consume the whole row, then we must need to * suspend processing and return to the application. In this situation * we pretend we didn't yet consume the last input row; otherwise, if @@ -251,7 +252,7 @@ jinit_c_main_controller (j_compress_ptr cinfo, boolean need_full_buffer) my_main_ptr main; int ci; jpeg_component_info *compptr; - int data_unit = cinfo->data_unit; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; main = (my_main_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, @@ -274,8 +275,8 @@ jinit_c_main_controller (j_compress_ptr cinfo, boolean need_full_buffer) ci++, compptr++) { main->whole_image[ci] = (*cinfo->mem->request_virt_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, - compptr->width_in_data_units * data_unit, - (JDIMENSION) jround_up((long) compptr->height_in_data_units, + compptr->width_in_blocks * data_unit, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, (long) compptr->v_samp_factor) * data_unit, (JDIMENSION) (compptr->v_samp_factor * data_unit)); } @@ -291,7 +292,7 @@ jinit_c_main_controller (j_compress_ptr cinfo, boolean need_full_buffer) ci++, compptr++) { main->buffer[ci] = (*cinfo->mem->alloc_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, - compptr->width_in_data_units * data_unit, + compptr->width_in_blocks * data_unit, (JDIMENSION) (compptr->v_samp_factor * data_unit)); } } diff --git a/jcmarker.c b/jcmarker.c index c174a517a..24e1e9f1c 100644 --- a/jcmarker.c +++ b/jcmarker.c @@ -5,6 +5,7 @@ * Copyright (C) 1991-1998, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains routines to write JPEG datastream markers. @@ -324,7 +325,7 @@ emit_sos (j_compress_ptr cinfo) emit_byte(cinfo, compptr->component_id); td = compptr->dc_tbl_no; ta = compptr->ac_tbl_no; - if (cinfo->process == JPROC_PROGRESSIVE) { + if (cinfo->progressive_mode) { /* Progressive mode: only DC or only AC tables are used in one scan; * furthermore, Huffman coding of DC refinement uses no table at all. * We emit 0 for unused field(s); this is recommended by the P&M text @@ -495,15 +496,14 @@ write_file_header (j_compress_ptr cinfo) METHODDEF(void) write_frame_header (j_compress_ptr cinfo) { - int ci, prec; + int ci, prec = 0; boolean is_baseline; jpeg_component_info *compptr; - if (cinfo->process != JPROC_LOSSLESS) { + if (! cinfo->master->lossless) { /* Emit DQT for each quantization table. * Note that emit_dqt() suppresses any duplicate tables. */ - prec = 0; for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { prec += emit_dqt(cinfo, compptr->quant_tbl_no); @@ -514,8 +514,8 @@ write_frame_header (j_compress_ptr cinfo) /* Check for a non-baseline specification. * Note we assume that Huffman table numbers won't be changed later. */ - if (cinfo->arith_code || cinfo->process != JPROC_SEQUENTIAL || - cinfo->data_precision != 8) { + if (cinfo->arith_code || cinfo->progressive_mode || + cinfo->master->lossless || cinfo->data_precision != 8) { is_baseline = FALSE; } else { is_baseline = TRUE; @@ -535,9 +535,9 @@ write_frame_header (j_compress_ptr cinfo) if (cinfo->arith_code) { emit_sof(cinfo, M_SOF9); /* SOF code for arithmetic coding */ } else { - if (cinfo->process == JPROC_PROGRESSIVE) + if (cinfo->progressive_mode) emit_sof(cinfo, M_SOF2); /* SOF code for progressive Huffman */ - else if (cinfo->process == JPROC_LOSSLESS) + else if (cinfo->master->lossless) emit_sof(cinfo, M_SOF3); /* SOF code for lossless Huffman */ else if (is_baseline) emit_sof(cinfo, M_SOF0); /* SOF code for baseline implementation */ @@ -572,7 +572,7 @@ write_scan_header (j_compress_ptr cinfo) */ for (i = 0; i < cinfo->comps_in_scan; i++) { compptr = cinfo->cur_comp_info[i]; - if (cinfo->process == JPROC_PROGRESSIVE) { + if (cinfo->progressive_mode) { /* Progressive mode: only DC or only AC tables are used in one scan */ if (cinfo->Ss == 0) { if (cinfo->Ah == 0) /* DC needs no table for refinement scan */ @@ -580,7 +580,7 @@ write_scan_header (j_compress_ptr cinfo) } else { emit_dht(cinfo, compptr->ac_tbl_no, TRUE); } - } else if (cinfo->process == JPROC_LOSSLESS) { + } else if (cinfo->master->lossless) { /* Lossless mode: only DC tables are used */ emit_dht(cinfo, compptr->dc_tbl_no, FALSE); } else { diff --git a/jcmaster.c b/jcmaster.c index ba16ced9c..02138868a 100644 --- a/jcmaster.c +++ b/jcmaster.c @@ -2,9 +2,10 @@ * jcmaster.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains master control logic for the JPEG compressor. @@ -16,29 +17,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossy.h" /* Private declarations for lossy codec */ - - -/* Private state */ - -typedef enum { - main_pass, /* input data, also do first output step */ - huff_opt_pass, /* Huffman code optimization pass */ - output_pass /* data output pass */ -} c_pass_type; - -typedef struct { - struct jpeg_comp_master pub; /* public fields */ - - c_pass_type pass_type; /* the type of the current pass */ - - int pass_number; /* # of passes completed */ - int total_passes; /* total # of passes needed */ - - int scan_number; /* current index in scan_info[] */ -} my_comp_master; - -typedef my_comp_master * my_master_ptr; +#include "jcmaster.h" /* @@ -53,7 +32,7 @@ initial_setup (j_compress_ptr cinfo) jpeg_component_info *compptr; long samplesperrow; JDIMENSION jd_samplesperrow; - int data_unit = cinfo->data_unit; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; /* Sanity check on image dimensions */ if (cinfo->image_height <= 0 || cinfo->image_width <= 0 @@ -99,13 +78,13 @@ initial_setup (j_compress_ptr cinfo) ci++, compptr++) { /* Fill in the correct component_index value; don't rely on application */ compptr->component_index = ci; - /* For compression, we never do any codec-based processing. */ - compptr->codec_data_unit = data_unit; + /* For compression, we never do DCT scaling. */ + compptr->DCT_scaled_size = data_unit; /* Size in data units */ - compptr->width_in_data_units = (JDIMENSION) + compptr->width_in_blocks = (JDIMENSION) jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, (long) (cinfo->max_h_samp_factor * data_unit)); - compptr->height_in_data_units = (JDIMENSION) + compptr->height_in_blocks = (JDIMENSION) jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, (long) (cinfo->max_v_samp_factor * data_unit)); /* Size in samples */ @@ -120,27 +99,24 @@ initial_setup (j_compress_ptr cinfo) } /* Compute number of fully interleaved MCU rows (number of times that - * main controller will call coefficient controller). + * main controller will call coefficient or difference controller). */ cinfo->total_iMCU_rows = (JDIMENSION) jdiv_round_up((long) cinfo->image_height, (long) (cinfo->max_v_samp_factor*data_unit)); } -#ifdef C_MULTISCAN_FILES_SUPPORTED -#define NEED_SCAN_SCRIPT -#else -#ifdef C_LOSSLESS_SUPPORTED + +#if defined(C_MULTISCAN_FILES_SUPPORTED) || defined(C_LOSSLESS_SUPPORTED) #define NEED_SCAN_SCRIPT #endif -#endif #ifdef NEED_SCAN_SCRIPT LOCAL(void) validate_script (j_compress_ptr cinfo) /* Verify that the scan script in cinfo->scan_info[] is valid; also - * determine whether it uses progressive JPEG, and set cinfo->process. + * determine whether it uses progressive JPEG, and set cinfo->progressive_mode. */ { const jpeg_scan_info * scanptr; @@ -162,9 +138,10 @@ validate_script (j_compress_ptr cinfo) #endif scanptr = cinfo->scan_info; - if (cinfo->lossless) { + if (scanptr->Ss != 0 && scanptr->Se == 0) { #ifdef C_LOSSLESS_SUPPORTED - cinfo->process = JPROC_LOSSLESS; + cinfo->master->lossless = TRUE; + cinfo->progressive_mode = FALSE; for (ci = 0; ci < cinfo->num_components; ci++) component_sent[ci] = FALSE; #else @@ -176,7 +153,8 @@ validate_script (j_compress_ptr cinfo) */ else if (scanptr->Ss != 0 || scanptr->Se != DCTSIZE2-1) { #ifdef C_PROGRESSIVE_SUPPORTED - cinfo->process = JPROC_PROGRESSIVE; + cinfo->progressive_mode = TRUE; + cinfo->master->lossless = FALSE; last_bitpos_ptr = & last_bitpos[0][0]; for (ci = 0; ci < cinfo->num_components; ci++) for (coefi = 0; coefi < DCTSIZE2; coefi++) @@ -185,7 +163,7 @@ validate_script (j_compress_ptr cinfo) ERREXIT(cinfo, JERR_NOT_COMPILED); #endif } else { - cinfo->process = JPROC_SEQUENTIAL; + cinfo->progressive_mode = cinfo->master->lossless = FALSE; for (ci = 0; ci < cinfo->num_components; ci++) component_sent[ci] = FALSE; } @@ -208,26 +186,7 @@ validate_script (j_compress_ptr cinfo) Se = scanptr->Se; Ah = scanptr->Ah; Al = scanptr->Al; - if (cinfo->process == JPROC_LOSSLESS) { -#ifdef C_LOSSLESS_SUPPORTED - /* The JPEG spec simply gives the range 0..15 for Al (Pt), but that - * seems wrong: the upper bound ought to depend on data precision. - * Perhaps they really meant 0..N-1 for N-bit precision, which is what - * we allow here. - */ - if (Ss < 1 || Ss > 7 || /* predictor selector */ - Se != 0 || Ah != 0 || - Al < 0 || Al >= cinfo->data_precision) /* point transform */ - ERREXIT1(cinfo, JERR_BAD_LOSSLESS_SCRIPT, scanno); - /* Make sure components are not sent twice */ - for (ci = 0; ci < ncomps; ci++) { - thisi = scanptr->component_index[ci]; - if (component_sent[thisi]) - ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno); - component_sent[thisi] = TRUE; - } -#endif - } else if (cinfo->process == JPROC_PROGRESSIVE) { + if (cinfo->progressive_mode) { #ifdef C_PROGRESSIVE_SUPPORTED /* The JPEG spec simply gives the ranges 0..13 for Ah and Al, but that * seems wrong: the upper bound ought to depend on data precision. @@ -270,9 +229,25 @@ validate_script (j_compress_ptr cinfo) } #endif } else { - /* For sequential JPEG, all progression parameters must be these: */ - if (Ss != 0 || Se != DCTSIZE2-1 || Ah != 0 || Al != 0) - ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); +#ifdef C_LOSSLESS_SUPPORTED + if (cinfo->master->lossless) { + /* The JPEG spec simply gives the range 0..15 for Al (Pt), but that + * seems wrong: the upper bound ought to depend on data precision. + * Perhaps they really meant 0..N-1 for N-bit precision, which is what + * we allow here. Values greater than or equal to the data precision + * will result in a blank image. + */ + if (Ss < 1 || Ss > 7 || /* predictor selection value */ + Se != 0 || Ah != 0 || + Al < 0 || Al >= cinfo->data_precision) /* point transform */ + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } else +#endif + { + /* For sequential JPEG, all progression parameters must be these: */ + if (Ss != 0 || Se != DCTSIZE2-1 || Ah != 0 || Al != 0) + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } /* Make sure components are not sent twice */ for (ci = 0; ci < ncomps; ci++) { thisi = scanptr->component_index[ci]; @@ -284,7 +259,7 @@ validate_script (j_compress_ptr cinfo) } /* Now verify that everything got sent. */ - if (cinfo->process == JPROC_PROGRESSIVE) { + if (cinfo->progressive_mode) { #ifdef C_PROGRESSIVE_SUPPORTED /* For progressive mode, we only check that at least some DC data * got sent for each component; the spec does not require that all bits @@ -339,15 +314,7 @@ select_scan_parameters (j_compress_ptr cinfo) for (ci = 0; ci < cinfo->num_components; ci++) { cinfo->cur_comp_info[ci] = &cinfo->comp_info[ci]; } - if (cinfo->lossless) { -#ifdef C_LOSSLESS_SUPPORTED - /* If we fall through to here, the user specified lossless, but did not - * provide a scan script. - */ - ERREXIT(cinfo, JERR_NO_LOSSLESS_SCRIPT); -#endif - } else { - cinfo->process = JPROC_SEQUENTIAL; + if (! cinfo->master->lossless) { cinfo->Ss = 0; cinfo->Se = DCTSIZE2-1; cinfo->Ah = 0; @@ -364,7 +331,7 @@ per_scan_setup (j_compress_ptr cinfo) { int ci, mcublks, tmp; jpeg_component_info *compptr; - int data_unit = cinfo->data_unit; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; if (cinfo->comps_in_scan == 1) { @@ -372,24 +339,24 @@ per_scan_setup (j_compress_ptr cinfo) compptr = cinfo->cur_comp_info[0]; /* Overall image size in MCUs */ - cinfo->MCUs_per_row = compptr->width_in_data_units; - cinfo->MCU_rows_in_scan = compptr->height_in_data_units; + 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_data_units = 1; + compptr->MCU_blocks = 1; compptr->MCU_sample_width = data_unit; 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_data_units % compptr->v_samp_factor); + tmp = (int) (compptr->height_in_blocks % compptr->v_samp_factor); if (tmp == 0) tmp = compptr->v_samp_factor; compptr->last_row_height = tmp; /* Prepare array describing MCU composition */ - cinfo->data_units_in_MCU = 1; + cinfo->blocks_in_MCU = 1; cinfo->MCU_membership[0] = 0; } else { @@ -407,28 +374,28 @@ per_scan_setup (j_compress_ptr cinfo) jdiv_round_up((long) cinfo->image_height, (long) (cinfo->max_v_samp_factor*data_unit)); - cinfo->data_units_in_MCU = 0; + cinfo->blocks_in_MCU = 0; for (ci = 0; ci < cinfo->comps_in_scan; ci++) { compptr = cinfo->cur_comp_info[ci]; /* Sampling factors give # of blocks of component in each MCU */ compptr->MCU_width = compptr->h_samp_factor; compptr->MCU_height = compptr->v_samp_factor; - compptr->MCU_data_units = compptr->MCU_width * compptr->MCU_height; + compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height; compptr->MCU_sample_width = compptr->MCU_width * data_unit; /* Figure number of non-dummy blocks in last MCU column & row */ - tmp = (int) (compptr->width_in_data_units % compptr->MCU_width); + tmp = (int) (compptr->width_in_blocks % compptr->MCU_width); if (tmp == 0) tmp = compptr->MCU_width; compptr->last_col_width = tmp; - tmp = (int) (compptr->height_in_data_units % compptr->MCU_height); + tmp = (int) (compptr->height_in_blocks % compptr->MCU_height); if (tmp == 0) tmp = compptr->MCU_height; compptr->last_row_height = tmp; /* Prepare array describing MCU composition */ - mcublks = compptr->MCU_data_units; - if (cinfo->data_units_in_MCU + mcublks > C_MAX_DATA_UNITS_IN_MCU) + mcublks = compptr->MCU_blocks; + if (cinfo->blocks_in_MCU + mcublks > C_MAX_BLOCKS_IN_MCU) ERREXIT(cinfo, JERR_BAD_MCU_SIZE); while (mcublks-- > 0) { - cinfo->MCU_membership[cinfo->data_units_in_MCU++] = ci; + cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci; } } @@ -454,7 +421,6 @@ per_scan_setup (j_compress_ptr cinfo) METHODDEF(void) prepare_for_pass (j_compress_ptr cinfo) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; my_master_ptr master = (my_master_ptr) cinfo->master; switch (master->pass_type) { @@ -469,10 +435,11 @@ prepare_for_pass (j_compress_ptr cinfo) (*cinfo->downsample->start_pass) (cinfo); (*cinfo->prep->start_pass) (cinfo, JBUF_PASS_THRU); } - (*cinfo->codec->entropy_start_pass) (cinfo, cinfo->optimize_coding); - (*cinfo->codec->start_pass) (cinfo, - (master->total_passes > 1 ? - JBUF_SAVE_AND_PASS : JBUF_PASS_THRU)); + (*cinfo->fdct->start_pass) (cinfo); + (*cinfo->entropy->start_pass) (cinfo, cinfo->optimize_coding); + (*cinfo->coef->start_pass) (cinfo, + (master->total_passes > 1 ? + JBUF_SAVE_AND_PASS : JBUF_PASS_THRU)); (*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU); if (cinfo->optimize_coding) { /* No immediate data output; postpone writing frame/scan headers */ @@ -487,9 +454,10 @@ prepare_for_pass (j_compress_ptr cinfo) /* Do Huffman optimization for a scan after the first one. */ select_scan_parameters(cinfo); per_scan_setup(cinfo); - if ((*cinfo->codec->need_optimization_pass) (cinfo) || cinfo->arith_code) { - (*cinfo->codec->entropy_start_pass) (cinfo, TRUE); - (*cinfo->codec->start_pass) (cinfo, JBUF_CRANK_DEST); + if (cinfo->Ss != 0 || cinfo->Ah == 0 || cinfo->arith_code || + cinfo->master->lossless) { + (*cinfo->entropy->start_pass) (cinfo, TRUE); + (*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST); master->pub.call_pass_startup = FALSE; break; } @@ -507,8 +475,8 @@ prepare_for_pass (j_compress_ptr cinfo) select_scan_parameters(cinfo); per_scan_setup(cinfo); } - (*cinfo->codec->entropy_start_pass) (cinfo, FALSE); - (*cinfo->codec->start_pass) (cinfo, JBUF_CRANK_DEST); + (*cinfo->entropy->start_pass) (cinfo, FALSE); + (*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST); /* We emit frame/scan headers now */ if (master->scan_number == 0) (*cinfo->marker->write_frame_header) (cinfo); @@ -556,13 +524,12 @@ pass_startup (j_compress_ptr cinfo) METHODDEF(void) finish_pass_master (j_compress_ptr cinfo) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; my_master_ptr master = (my_master_ptr) cinfo->master; /* The entropy coder always needs an end-of-pass call, * either to analyze statistics or to flush its output buffer. */ - (*lossyc->pub.entropy_finish_pass) (cinfo); + (*cinfo->entropy->finish_pass) (cinfo); /* Update state for next pass */ switch (master->pass_type) { @@ -597,22 +564,13 @@ finish_pass_master (j_compress_ptr cinfo) GLOBAL(void) jinit_c_master_control (j_compress_ptr cinfo, boolean transcode_only) { - my_master_ptr master; + my_master_ptr master = (my_master_ptr) cinfo->master; - master = (my_master_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(my_comp_master)); - cinfo->master = (struct jpeg_comp_master *) master; master->pub.prepare_for_pass = prepare_for_pass; master->pub.pass_startup = pass_startup; master->pub.finish_pass = finish_pass_master; master->pub.is_last_pass = FALSE; - cinfo->data_unit = cinfo->lossless ? 1 : DCTSIZE; - - /* Validate parameters, determine derived values */ - initial_setup(cinfo); - if (cinfo->scan_info != NULL) { #ifdef NEED_SCAN_SCRIPT validate_script(cinfo); @@ -620,12 +578,32 @@ jinit_c_master_control (j_compress_ptr cinfo, boolean transcode_only) ERREXIT(cinfo, JERR_NOT_COMPILED); #endif } else { - cinfo->process = JPROC_SEQUENTIAL; + cinfo->progressive_mode = FALSE; cinfo->num_scans = 1; } - if (cinfo->process == JPROC_PROGRESSIVE || /* TEMPORARY HACK ??? */ - cinfo->process == JPROC_LOSSLESS) + /* Disable smoothing and subsampling in lossless mode, since those are lossy + * algorithms. Set the JPEG colorspace to the input colorspace. Disable raw + * (downsampled) data input, because it isn't particularly useful without + * subsampling and has not been tested in lossless mode. + */ + if (cinfo->master->lossless) { + int ci; + jpeg_component_info * compptr; + + cinfo->raw_data_in = FALSE; + cinfo->smoothing_factor = 0; + jpeg_default_colorspace(cinfo); + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) + compptr->h_samp_factor = compptr->v_samp_factor = 1; + } + + /* Validate parameters, determine derived values */ + initial_setup(cinfo); + + if (cinfo->progressive_mode || /* TEMPORARY HACK ??? */ + cinfo->master->lossless) cinfo->optimize_coding = TRUE; /* assume default tables no good for * progressive mode or lossless mode */ diff --git a/jcmaster.h b/jcmaster.h new file mode 100644 index 000000000..aead386b2 --- /dev/null +++ b/jcmaster.h @@ -0,0 +1,30 @@ +/* + * jcmaster.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1991-1995, Thomas G. Lane. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains master control structure for the JPEG compressor. + */ + +/* Private state */ + +typedef enum { + main_pass, /* input data, also do first output step */ + huff_opt_pass, /* Huffman code optimization pass */ + output_pass /* data output pass */ +} c_pass_type; + +typedef struct { + struct jpeg_comp_master pub; /* public fields */ + + c_pass_type pass_type; /* the type of the current pass */ + + int pass_number; /* # of passes completed */ + int total_passes; /* total # of passes needed */ + + int scan_number; /* current index in scan_info[] */ +} my_comp_master; + +typedef my_comp_master * my_master_ptr; diff --git a/jcodec.c b/jcodec.c deleted file mode 100644 index 5eae931e7..000000000 --- a/jcodec.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * jcodec.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains utility functions for the JPEG codec(s). - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jlossy.h" -#include "jlossls.h" - - -/* - * Initialize the compression codec. - * This is called only once, during master selection. - */ - -GLOBAL(void) -jinit_c_codec (j_compress_ptr cinfo) -{ - if (cinfo->process == JPROC_LOSSLESS) { -#ifdef C_LOSSLESS_SUPPORTED - jinit_lossless_c_codec(cinfo); -#else - ERREXIT(cinfo, JERR_NOT_COMPILED); -#endif - } else - jinit_lossy_c_codec(cinfo); -} - - -/* - * Initialize the decompression codec. - * This is called only once, during master selection. - */ - -GLOBAL(void) -jinit_d_codec (j_decompress_ptr cinfo) -{ - if (cinfo->process == JPROC_LOSSLESS) { -#ifdef D_LOSSLESS_SUPPORTED - jinit_lossless_d_codec(cinfo); -#else - ERREXIT(cinfo, JERR_NOT_COMPILED); -#endif - } else - jinit_lossy_d_codec(cinfo); -} diff --git a/jcparam.c b/jcparam.c index 3c14bd937..690666ee4 100644 --- a/jcparam.c +++ b/jcparam.c @@ -5,6 +5,7 @@ * Copyright (C) 1991-1998, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains optional default-setting code for the JPEG compressor. @@ -286,7 +287,6 @@ jpeg_set_defaults (j_compress_ptr cinfo) /* Initialize everything not dependent on the color space */ - cinfo->lossless = FALSE; cinfo->data_precision = BITS_IN_JSAMPLE; /* Set up two quantization tables using default quality of 75 */ jpeg_set_quality(cinfo, 75, TRUE); @@ -361,31 +361,30 @@ jpeg_set_defaults (j_compress_ptr cinfo) GLOBAL(void) jpeg_default_colorspace (j_compress_ptr cinfo) { - if (cinfo->lossless) - jpeg_set_colorspace(cinfo, cinfo->in_color_space); - else { /* lossy */ - switch (cinfo->in_color_space) { - case JCS_GRAYSCALE: - jpeg_set_colorspace(cinfo, JCS_GRAYSCALE); - break; - case JCS_RGB: - jpeg_set_colorspace(cinfo, JCS_YCbCr); - break; - case JCS_YCbCr: + switch (cinfo->in_color_space) { + case JCS_GRAYSCALE: + jpeg_set_colorspace(cinfo, JCS_GRAYSCALE); + break; + case JCS_RGB: + if (cinfo->master->lossless) + jpeg_set_colorspace(cinfo, JCS_RGB); + else jpeg_set_colorspace(cinfo, JCS_YCbCr); - break; - case JCS_CMYK: - jpeg_set_colorspace(cinfo, JCS_CMYK); /* By default, no translation */ - break; - case JCS_YCCK: - jpeg_set_colorspace(cinfo, JCS_YCCK); - break; - case JCS_UNKNOWN: - jpeg_set_colorspace(cinfo, JCS_UNKNOWN); - break; - default: - ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); - } + break; + case JCS_YCbCr: + jpeg_set_colorspace(cinfo, JCS_YCbCr); + break; + case JCS_CMYK: + jpeg_set_colorspace(cinfo, JCS_CMYK); /* By default, no translation */ + break; + case JCS_YCCK: + jpeg_set_colorspace(cinfo, JCS_YCCK); + break; + case JCS_UNKNOWN: + jpeg_set_colorspace(cinfo, JCS_UNKNOWN); + break; + default: + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); } } @@ -440,16 +439,10 @@ jpeg_set_colorspace (j_compress_ptr cinfo, J_COLOR_SPACE colorspace) cinfo->write_JFIF_header = TRUE; /* Write a JFIF marker */ cinfo->num_components = 3; /* JFIF specifies component IDs 1,2,3 */ - if (cinfo->lossless) { - SET_COMP(0, 1, 1,1, 0, 0,0); - SET_COMP(1, 2, 1,1, 1, 1,1); - SET_COMP(2, 3, 1,1, 1, 1,1); - } else { /* lossy */ - /* 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); - } + /* We default to 2x2 subsamples of chrominance */ + SET_COMP(0, 1, 2,2, 0, 0,0); + SET_COMP(1, 2, 1,1, 1, 1,1); + SET_COMP(2, 3, 1,1, 1, 1,1); break; case JCS_CMYK: cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag CMYK */ @@ -462,17 +455,10 @@ jpeg_set_colorspace (j_compress_ptr cinfo, J_COLOR_SPACE colorspace) case JCS_YCCK: cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag YCCK */ cinfo->num_components = 4; - if (cinfo->lossless) { - SET_COMP(0, 1, 1,1, 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, 1,1, 0, 0,0); - } else { /* lossy */ - 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); - } + SET_COMP(0, 1, 2,2, 0, 0,0); + SET_COMP(1, 2, 1,1, 1, 1,1); + SET_COMP(2, 3, 1,1, 1, 1,1); + SET_COMP(3, 4, 2,2, 0, 0,0); break; case JCS_UNKNOWN: cinfo->num_components = cinfo->input_components; @@ -491,6 +477,21 @@ jpeg_set_colorspace (j_compress_ptr cinfo, J_COLOR_SPACE colorspace) #ifdef C_PROGRESSIVE_SUPPORTED +LOCAL(jpeg_scan_info *) +fill_a_scan (jpeg_scan_info * scanptr, int ci, + int Ss, int Se, int Ah, int Al) +/* Support routine: generate one scan for specified component */ +{ + scanptr->comps_in_scan = 1; + scanptr->component_index[0] = ci; + scanptr->Ss = Ss; + scanptr->Se = Se; + scanptr->Ah = Ah; + scanptr->Al = Al; + scanptr++; + return scanptr; +} + LOCAL(jpeg_scan_info *) fill_scans (jpeg_scan_info * scanptr, int ncomps, int Ss, int Se, int Ah, int Al) @@ -510,22 +511,6 @@ fill_scans (jpeg_scan_info * scanptr, int ncomps, return scanptr; } - -LOCAL(jpeg_scan_info *) -fill_a_scan (jpeg_scan_info * scanptr, int ci, - int Ss, int Se, int Ah, int Al) -/* Support routine: generate one scan for specified component */ -{ - scanptr->comps_in_scan = 1; - scanptr->component_index[0] = ci; - scanptr->Ss = Ss; - scanptr->Se = Se; - scanptr->Ah = Ah; - scanptr->Al = Al; - scanptr++; - return scanptr; -} - LOCAL(jpeg_scan_info *) fill_dc_scans (jpeg_scan_info * scanptr, int ncomps, int Ah, int Al) /* Support routine: generate interleaved DC scan if possible, else N scans */ @@ -565,6 +550,11 @@ jpeg_simple_progression (j_compress_ptr cinfo) if (cinfo->global_state != CSTATE_START) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->master->lossless) { + cinfo->master->lossless = FALSE; + jpeg_default_colorspace(cinfo); + } + /* Figure space needed for script. Calculation must match code below! */ if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) { /* Custom script for YCbCr color images. */ @@ -634,56 +624,33 @@ jpeg_simple_progression (j_compress_ptr cinfo) #ifdef C_LOSSLESS_SUPPORTED /* - * Create a single-entry lossless-JPEG script containing all components. - * cinfo->num_components must be correct. + * Enable lossless mode. */ GLOBAL(void) -jpeg_simple_lossless (j_compress_ptr cinfo, int predictor, int point_transform) +jpeg_enable_lossless (j_compress_ptr cinfo, int predictor_selection_value, + int point_transform) { - int ncomps = cinfo->num_components; - int nscans = 1; - int ci; - jpeg_scan_info * scanptr; - /* Safety check to ensure start_compress not called yet. */ if (cinfo->global_state != CSTATE_START) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); - cinfo->lossless = TRUE; - - /* Set jpeg_color_space. */ - jpeg_default_colorspace(cinfo); - - /* Check to ensure that all components will fit in one scan. */ - if (cinfo->num_components > MAX_COMPS_IN_SCAN) - ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, - MAX_COMPS_IN_SCAN); - - /* 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_lossless is called repeatedly for the same JPEG - * object, we try to re-use previously allocated space. + cinfo->master->lossless = TRUE; + cinfo->Ss = predictor_selection_value; + cinfo->Se = 0; + cinfo->Ah = 0; + cinfo->Al = point_transform; + + /* The JPEG spec simply gives the range 0..15 for Al (Pt), but that seems + * wrong: the upper bound ought to depend on data precision. Perhaps they + * really meant 0..N-1 for N-bit precision, which is what we allow here. + * Values greater than or equal to the data precision will result in a blank + * image. */ - if (cinfo->script_space == NULL || cinfo->script_space_size < nscans) { - cinfo->script_space_size = nscans; - cinfo->script_space = (jpeg_scan_info *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, - cinfo->script_space_size * SIZEOF(jpeg_scan_info)); - } - scanptr = cinfo->script_space; - cinfo->scan_info = scanptr; - cinfo->num_scans = nscans; - - /* Fill the script. */ - scanptr->comps_in_scan = ncomps; - for (ci = 0; ci < ncomps; ci++) - scanptr->component_index[ci] = ci; - scanptr->Ss = predictor; - scanptr->Se = 0; - scanptr->Ah = 0; - scanptr->Al = point_transform; + if (cinfo->Ss < 1 || cinfo->Ss > 7 || + cinfo->Al < 0 || cinfo->Al >= cinfo->data_precision) + ERREXIT4(cinfo, JERR_BAD_PROGRESSION, + cinfo->Ss, cinfo->Se, cinfo->Ah, cinfo->Al); } #endif /* C_LOSSLESS_SUPPORTED */ diff --git a/jcphuff.c b/jcphuff.c index 6ecef0898..49f93c556 100644 --- a/jcphuff.c +++ b/jcphuff.c @@ -2,7 +2,7 @@ * jcphuff.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1995-1998, Thomas G. Lane. + * Copyright (C) 1995-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. * For conditions of distribution and use, see the accompanying README file. @@ -17,7 +17,6 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossy.h" /* Private declarations for lossy codec */ #include "jchuff.h" /* Declarations shared with jc*huff.c */ #ifdef C_PROGRESSIVE_SUPPORTED @@ -25,6 +24,8 @@ /* Expanded entropy encoder object for progressive Huffman encoding. */ typedef struct { + struct jpeg_entropy_encoder pub; /* public fields */ + /* Mode flag: TRUE for optimization, FALSE for actual data output */ boolean gather_statistics; @@ -106,8 +107,7 @@ METHODDEF(void) finish_pass_gather_phuff JPP((j_compress_ptr cinfo)); METHODDEF(void) start_pass_phuff (j_compress_ptr cinfo, boolean gather_statistics) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyc->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; boolean is_DC_band; int ci, tbl; jpeg_component_info * compptr; @@ -122,14 +122,14 @@ start_pass_phuff (j_compress_ptr cinfo, boolean gather_statistics) /* Select execution routines */ if (cinfo->Ah == 0) { if (is_DC_band) - lossyc->entropy_encode_mcu = encode_mcu_DC_first; + entropy->pub.encode_mcu = encode_mcu_DC_first; else - lossyc->entropy_encode_mcu = encode_mcu_AC_first; + entropy->pub.encode_mcu = encode_mcu_AC_first; } else { if (is_DC_band) - lossyc->entropy_encode_mcu = encode_mcu_DC_refine; + entropy->pub.encode_mcu = encode_mcu_DC_refine; else { - lossyc->entropy_encode_mcu = encode_mcu_AC_refine; + entropy->pub.encode_mcu = encode_mcu_AC_refine; /* AC refinement needs a correction bit buffer */ if (entropy->bit_buffer == NULL) entropy->bit_buffer = (char *) @@ -138,9 +138,9 @@ start_pass_phuff (j_compress_ptr cinfo, boolean gather_statistics) } } if (gather_statistics) - lossyc->pub.entropy_finish_pass = finish_pass_gather_phuff; + entropy->pub.finish_pass = finish_pass_gather_phuff; else - lossyc->pub.entropy_finish_pass = finish_pass_phuff; + entropy->pub.finish_pass = finish_pass_phuff; /* Only DC coefficients may be interleaved, so cinfo->comps_in_scan = 1 * for AC coefficients. @@ -378,8 +378,7 @@ emit_restart (phuff_entropy_ptr entropy, int restart_num) METHODDEF(boolean) encode_mcu_DC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyc->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; register int temp, temp2; register int nbits; int blkn, ci; @@ -397,7 +396,7 @@ encode_mcu_DC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) emit_restart(entropy, entropy->next_restart_num); /* Encode the MCU data blocks */ - for (blkn = 0; blkn < cinfo->data_units_in_MCU; blkn++) { + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { block = MCU_data[blkn]; ci = cinfo->MCU_membership[blkn]; compptr = cinfo->cur_comp_info[ci]; @@ -466,8 +465,7 @@ encode_mcu_DC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) METHODDEF(boolean) encode_mcu_AC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyc->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; register int temp, temp2; register int nbits; register int r, k; @@ -574,8 +572,7 @@ encode_mcu_AC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) METHODDEF(boolean) encode_mcu_DC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyc->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; register int temp; int blkn; int Al = cinfo->Al; @@ -590,7 +587,7 @@ encode_mcu_DC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) emit_restart(entropy, entropy->next_restart_num); /* Encode the MCU data blocks */ - for (blkn = 0; blkn < cinfo->data_units_in_MCU; blkn++) { + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { block = MCU_data[blkn]; /* We simply emit the Al'th bit of the DC coefficient value. */ @@ -622,8 +619,7 @@ encode_mcu_DC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) METHODDEF(boolean) encode_mcu_AC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyc->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; register int temp; register int r, k; int EOB; @@ -751,8 +747,7 @@ encode_mcu_AC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) METHODDEF(void) finish_pass_phuff (j_compress_ptr cinfo) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyc->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; entropy->next_output_byte = cinfo->dest->next_output_byte; entropy->free_in_buffer = cinfo->dest->free_in_buffer; @@ -773,8 +768,7 @@ finish_pass_phuff (j_compress_ptr cinfo) METHODDEF(void) finish_pass_gather_phuff (j_compress_ptr cinfo) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyc->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; boolean is_DC_band; int ci, tbl; jpeg_component_info * compptr; @@ -814,13 +808,6 @@ finish_pass_gather_phuff (j_compress_ptr cinfo) } -METHODDEF(boolean) -need_optimization_pass (j_compress_ptr cinfo) -{ - return (cinfo->Ss != 0 || cinfo->Ah == 0); -} - - /* * Module initialization routine for progressive Huffman entropy encoding. */ @@ -828,16 +815,14 @@ need_optimization_pass (j_compress_ptr cinfo) GLOBAL(void) jinit_phuff_encoder (j_compress_ptr cinfo) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; phuff_entropy_ptr entropy; int i; entropy = (phuff_entropy_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, SIZEOF(phuff_entropy_encoder)); - lossyc->entropy_private = (struct jpeg_entropy_encoder *) entropy; - lossyc->pub.entropy_start_pass = start_pass_phuff; - lossyc->pub.need_optimization_pass = need_optimization_pass; + cinfo->entropy = (struct jpeg_entropy_encoder *) entropy; + entropy->pub.start_pass = start_pass_phuff; /* Mark tables unallocated */ for (i = 0; i < NUM_HUFF_TBLS; i++) { diff --git a/jcpred.c b/jcpred.c deleted file mode 100644 index 22d3f3c4e..000000000 --- a/jcpred.c +++ /dev/null @@ -1,299 +0,0 @@ -/* - * jcpred.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains sample differencing for lossless JPEG. - * - * In order to avoid paying the performance penalty of having to check the - * predictor being used and the row being processed for each call of the - * undifferencer, and to promote optimization, we have separate differencing - * functions for each case. - * - * We are able to avoid duplicating source code by implementing the predictors - * and differencers as macros. Each of the differencing functions are - * simply wrappers around a DIFFERENCE macro with the appropriate PREDICTOR - * macro passed as an argument. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jlossls.h" /* Private declarations for lossless codec */ - - -#ifdef C_LOSSLESS_SUPPORTED - -/* Private predictor object */ - -typedef struct { - /* MCU-rows left in the restart interval for each component */ - unsigned int restart_rows_to_go[MAX_COMPONENTS]; -} c_predictor; - -typedef c_predictor * c_pred_ptr; - -/* Forward declarations */ -LOCAL(void) reset_predictor - JPP((j_compress_ptr cinfo, int ci)); -METHODDEF(void) start_pass - JPP((j_compress_ptr cinfo)); - - -/* Predictor for the first column of the first row: 2^(P-Pt-1) */ -#define INITIAL_PREDICTORx (1 << (cinfo->data_precision - cinfo->Al - 1)) - -/* Predictor for the first column of the remaining rows: Rb */ -#define INITIAL_PREDICTOR2 GETJSAMPLE(prev_row[0]) - - -/* - * 1-Dimensional differencer routine. - * - * This macro implements the 1-D horizontal predictor (1). INITIAL_PREDICTOR - * is used as the special case predictor for the first column, which must be - * either INITIAL_PREDICTOR2 or INITIAL_PREDICTORx. The remaining samples - * use PREDICTOR1. - */ - -#define DIFFERENCE_1D(INITIAL_PREDICTOR) \ - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; \ - c_pred_ptr pred = (c_pred_ptr) losslsc->pred_private; \ - boolean restart = FALSE; \ - int xindex; \ - int samp, Ra; \ - \ - samp = GETJSAMPLE(input_buf[0]); \ - diff_buf[0] = samp - INITIAL_PREDICTOR; \ - \ - for (xindex = 1; xindex < width; xindex++) { \ - Ra = samp; \ - samp = GETJSAMPLE(input_buf[xindex]); \ - diff_buf[xindex] = samp - PREDICTOR1; \ - } \ - \ - /* Account for restart interval (no-op if not using restarts) */ \ - if (cinfo->restart_interval) { \ - if (--(pred->restart_rows_to_go[ci]) == 0) { \ - reset_predictor(cinfo, ci); \ - restart = TRUE; \ - } \ - } - - -/* - * 2-Dimensional differencer routine. - * - * This macro implements the 2-D horizontal predictors (#2-7). PREDICTOR2 is - * used as the special case predictor for the first column. The remaining - * samples use PREDICTOR, which is a function of Ra, Rb, Rc. - * - * Because prev_row and output_buf may point to the same storage area (in an - * interleaved image with Vi=1, for example), we must take care to buffer Rb/Rc - * before writing the current reconstructed sample value into output_buf. - */ - -#define DIFFERENCE_2D(PREDICTOR) \ - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; \ - c_pred_ptr pred = (c_pred_ptr) losslsc->pred_private; \ - int xindex; \ - int samp, Ra, Rb, Rc; \ - \ - Rb = GETJSAMPLE(prev_row[0]); \ - samp = GETJSAMPLE(input_buf[0]); \ - diff_buf[0] = samp - PREDICTOR2; \ - \ - for (xindex = 1; xindex < width; xindex++) { \ - Rc = Rb; \ - Rb = GETJSAMPLE(prev_row[xindex]); \ - Ra = samp; \ - samp = GETJSAMPLE(input_buf[xindex]); \ - diff_buf[xindex] = samp - PREDICTOR; \ - } \ - \ - /* Account for restart interval (no-op if not using restarts) */ \ - if (cinfo->restart_interval) { \ - if (--pred->restart_rows_to_go[ci] == 0) \ - reset_predictor(cinfo, ci); \ - } - - -/* - * Differencers for the all rows but the first in a scan or restart interval. - * The first sample in the row is differenced using the vertical - * predictor (2). The rest of the samples are differenced using the - * predictor specified in the scan header. - */ - -METHODDEF(void) -jpeg_difference1(j_compress_ptr cinfo, int ci, - JSAMPROW input_buf, JSAMPROW prev_row, - JDIFFROW diff_buf, JDIMENSION width) -{ - DIFFERENCE_1D(INITIAL_PREDICTOR2); -} - -METHODDEF(void) -jpeg_difference2(j_compress_ptr cinfo, int ci, - JSAMPROW input_buf, JSAMPROW prev_row, - JDIFFROW diff_buf, JDIMENSION width) -{ - DIFFERENCE_2D(PREDICTOR2); -} - -METHODDEF(void) -jpeg_difference3(j_compress_ptr cinfo, int ci, - JSAMPROW input_buf, JSAMPROW prev_row, - JDIFFROW diff_buf, JDIMENSION width) -{ - DIFFERENCE_2D(PREDICTOR3); -} - -METHODDEF(void) -jpeg_difference4(j_compress_ptr cinfo, int ci, - JSAMPROW input_buf, JSAMPROW prev_row, - JDIFFROW diff_buf, JDIMENSION width) -{ - DIFFERENCE_2D(PREDICTOR4); -} - -METHODDEF(void) -jpeg_difference5(j_compress_ptr cinfo, int ci, - JSAMPROW input_buf, JSAMPROW prev_row, - JDIFFROW diff_buf, JDIMENSION width) -{ - DIFFERENCE_2D(PREDICTOR5); -} - -METHODDEF(void) -jpeg_difference6(j_compress_ptr cinfo, int ci, - JSAMPROW input_buf, JSAMPROW prev_row, - JDIFFROW diff_buf, JDIMENSION width) -{ - DIFFERENCE_2D(PREDICTOR6); -} - -METHODDEF(void) -jpeg_difference7(j_compress_ptr cinfo, int ci, - JSAMPROW input_buf, JSAMPROW prev_row, - JDIFFROW diff_buf, JDIMENSION width) -{ - DIFFERENCE_2D(PREDICTOR7); -} - - -/* - * Differencer for the first row in a scan or restart interval. The first - * sample in the row is differenced using the special predictor constant - * x=2^(P-Pt-1). The rest of the samples are differenced using the - * 1-D horizontal predictor (1). - */ - -METHODDEF(void) -jpeg_difference_first_row(j_compress_ptr cinfo, int ci, - JSAMPROW input_buf, JSAMPROW prev_row, - JDIFFROW diff_buf, JDIMENSION width) -{ - DIFFERENCE_1D(INITIAL_PREDICTORx); - - /* - * Now that we have differenced the first row, we want to use the - * differencer which corresponds to the predictor specified in the - * scan header. - * - * Note that we don't to do this if we have just reset the predictor - * for a new restart interval. - */ - if (!restart) { - switch (cinfo->Ss) { - case 1: - losslsc->predict_difference[ci] = jpeg_difference1; - break; - case 2: - losslsc->predict_difference[ci] = jpeg_difference2; - break; - case 3: - losslsc->predict_difference[ci] = jpeg_difference3; - break; - case 4: - losslsc->predict_difference[ci] = jpeg_difference4; - break; - case 5: - losslsc->predict_difference[ci] = jpeg_difference5; - break; - case 6: - losslsc->predict_difference[ci] = jpeg_difference6; - break; - case 7: - losslsc->predict_difference[ci] = jpeg_difference7; - break; - } - } -} - -/* - * Reset predictor at the start of a pass or restart interval. - */ - -LOCAL(void) -reset_predictor (j_compress_ptr cinfo, int ci) -{ - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - c_pred_ptr pred = (c_pred_ptr) losslsc->pred_private; - - /* Initialize restart counter */ - pred->restart_rows_to_go[ci] = - cinfo->restart_interval / cinfo->MCUs_per_row; - - /* Set difference function to first row function */ - losslsc->predict_difference[ci] = jpeg_difference_first_row; -} - - -/* - * Initialize for an input processing pass. - */ - -METHODDEF(void) -start_pass (j_compress_ptr cinfo) -{ - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - c_pred_ptr pred = (c_pred_ptr) losslsc->pred_private; - int ci; - - /* Check that the restart interval is an integer multiple of the number - * of MCU in an MCU-row. - */ - if (cinfo->restart_interval % cinfo->MCUs_per_row != 0) - ERREXIT2(cinfo, JERR_BAD_RESTART, - cinfo->restart_interval, cinfo->MCUs_per_row); - - /* Set predictors for start of pass */ - for (ci = 0; ci < cinfo->num_components; ci++) - reset_predictor(cinfo, ci); -} - - -/* - * Module initialization routine for the differencer. - */ - -GLOBAL(void) -jinit_differencer (j_compress_ptr cinfo) -{ - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - c_pred_ptr pred; - - pred = (c_pred_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(c_predictor)); - losslsc->pred_private = (void *) pred; - losslsc->predict_start_pass = start_pass; -} - -#endif /* C_LOSSLESS_SUPPORTED */ - diff --git a/jcprepct.c b/jcprepct.c index f7bf939a2..62eaab52d 100644 --- a/jcprepct.c +++ b/jcprepct.c @@ -2,9 +2,10 @@ * jcprepct.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. + * Copyright (C) 1994-1996, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains the compression preprocessing controller. @@ -137,6 +138,7 @@ pre_process_data (j_compress_ptr cinfo, int numrows, ci; JDIMENSION inrows; jpeg_component_info * compptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; while (*in_row_ctr < in_rows_avail && *out_row_group_ctr < out_row_groups_avail) { @@ -176,7 +178,7 @@ pre_process_data (j_compress_ptr cinfo, for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { expand_bottom_edge(output_buf[ci], - compptr->width_in_data_units * cinfo->data_unit, + compptr->width_in_blocks * data_unit, (int) (*out_row_group_ctr * compptr->v_samp_factor), (int) (out_row_groups_avail * compptr->v_samp_factor)); } @@ -273,6 +275,7 @@ create_context_buffer (j_compress_ptr cinfo) int ci, i; jpeg_component_info * compptr; JSAMPARRAY true_buffer, fake_buffer; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; /* Grab enough space for fake row pointers for all the components; * we need five row groups' worth of pointers for each component. @@ -290,7 +293,7 @@ create_context_buffer (j_compress_ptr cinfo) */ true_buffer = (*cinfo->mem->alloc_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, - (JDIMENSION) (((long) compptr->width_in_data_units * cinfo->data_unit * + (JDIMENSION) (((long) compptr->width_in_blocks * data_unit * cinfo->max_h_samp_factor) / compptr->h_samp_factor), (JDIMENSION) (3 * rgroup_height)); /* Copy true buffer row pointers into the middle of the fake row array */ @@ -319,6 +322,7 @@ jinit_c_prep_controller (j_compress_ptr cinfo, boolean need_full_buffer) my_prep_ptr prep; int ci; jpeg_component_info * compptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; if (need_full_buffer) /* safety check */ ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); @@ -348,7 +352,7 @@ jinit_c_prep_controller (j_compress_ptr cinfo, boolean need_full_buffer) ci++, compptr++) { prep->color_buf[ci] = (*cinfo->mem->alloc_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, - (JDIMENSION) (((long) compptr->width_in_data_units * cinfo->data_unit * + (JDIMENSION) (((long) compptr->width_in_blocks * data_unit * cinfo->max_h_samp_factor) / compptr->h_samp_factor), (JDIMENSION) cinfo->max_v_samp_factor); } diff --git a/jcsample.c b/jcsample.c index d97c8b85e..70a8c43bb 100644 --- a/jcsample.c +++ b/jcsample.c @@ -2,9 +2,10 @@ * jcsample.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1996, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains downsampling routines. @@ -144,7 +145,8 @@ int_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, { int inrow, outrow, h_expand, v_expand, numpix, numpix2, h, v; JDIMENSION outcol, outcol_h; /* outcol_h == outcol*h_expand */ - JDIMENSION output_cols = compptr->width_in_data_units * cinfo->data_unit; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + JDIMENSION output_cols = compptr->width_in_blocks * data_unit; JSAMPROW inptr, outptr; INT32 outvalue; @@ -189,12 +191,14 @@ METHODDEF(void) fullsize_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, JSAMPARRAY input_data, JSAMPARRAY output_data) { + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + /* 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_data_units * cinfo->data_unit); + cinfo->image_width, compptr->width_in_blocks * data_unit); } @@ -216,7 +220,8 @@ h2v1_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, { int outrow; JDIMENSION outcol; - JDIMENSION output_cols = compptr->width_in_data_units * cinfo->data_unit; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + JDIMENSION output_cols = compptr->width_in_blocks * data_unit; register JSAMPROW inptr, outptr; register int bias; @@ -253,7 +258,8 @@ h2v2_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, { int inrow, outrow; JDIMENSION outcol; - JDIMENSION output_cols = compptr->width_in_data_units * cinfo->data_unit; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + JDIMENSION output_cols = compptr->width_in_blocks * data_unit; register JSAMPROW inptr0, inptr1, outptr; register int bias; @@ -296,7 +302,8 @@ h2v2_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, { int inrow, outrow; JDIMENSION colctr; - JDIMENSION output_cols = compptr->width_in_data_units * cinfo->data_unit; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + JDIMENSION output_cols = compptr->width_in_blocks * data_unit; register JSAMPROW inptr0, inptr1, above_ptr, below_ptr, outptr; INT32 membersum, neighsum, memberscale, neighscale; @@ -396,7 +403,8 @@ fullsize_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info *compptr, { int outrow; JDIMENSION colctr; - JDIMENSION output_cols = compptr->width_in_data_units * cinfo->data_unit; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + JDIMENSION output_cols = compptr->width_in_blocks * data_unit; register JSAMPROW inptr, above_ptr, below_ptr, outptr; INT32 membersum, neighsum, memberscale, neighscale; int colsum, lastcolsum, nextcolsum; diff --git a/jcscale.c b/jcscale.c deleted file mode 100644 index ce945bfdc..000000000 --- a/jcscale.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * jcscale.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains sample downscaling by 2^Pt for lossless JPEG. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jlossls.h" /* Private declarations for lossless codec */ - - -#ifdef C_LOSSLESS_SUPPORTED - -METHODDEF(void) -simple_downscale(j_compress_ptr cinfo, - JSAMPROW input_buf, JSAMPROW output_buf, JDIMENSION width) -{ - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - int xindex; - - for (xindex = 0; xindex < width; xindex++) - output_buf[xindex] = (JSAMPLE) RIGHT_SHIFT(GETJSAMPLE(input_buf[xindex]), - cinfo->Al); -} - - -METHODDEF(void) -noscale(j_compress_ptr cinfo, - JSAMPROW input_buf, JSAMPROW output_buf, JDIMENSION width) -{ - MEMCOPY(output_buf, input_buf, width * SIZEOF(JSAMPLE)); - return; -} - - -METHODDEF(void) -scaler_start_pass (j_compress_ptr cinfo) -{ - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - - /* Set scaler function based on Pt */ - if (cinfo->Al) - losslsc->scaler_scale = simple_downscale; - else - losslsc->scaler_scale = noscale; -} - - -GLOBAL(void) -jinit_c_scaler (j_compress_ptr cinfo) -{ - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - - losslsc->scaler_start_pass = scaler_start_pass; -} - -#endif /* C_LOSSLESS_SUPPORTED */ diff --git a/jcshuff.c b/jcshuff.c deleted file mode 100644 index 829f26e2c..000000000 --- a/jcshuff.c +++ /dev/null @@ -1,663 +0,0 @@ -/* - * jcshuff.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains Huffman entropy encoding routines for sequential JPEG. - * - * Much of the complexity here has to do with supporting output suspension. - * If the data destination module demands suspension, we want to be able to - * back up to the start of the current MCU. To do this, we copy state - * variables into local working storage, and update them back to the - * permanent JPEG objects only upon successful completion of an MCU. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jlossy.h" /* Private declarations for lossy codec */ -#include "jchuff.h" /* Declarations shared with jc*huff.c */ - - -/* Expanded entropy encoder object for Huffman encoding. - * - * The savable_state subrecord contains fields that change within an MCU, - * but must not be updated permanently until we complete the MCU. - */ - -typedef struct { - INT32 put_buffer; /* current bit-accumulation buffer */ - int put_bits; /* # of bits now in it */ - int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ -} savable_state; - -/* This macro is to work around compilers with missing or broken - * structure assignment. You'll need to fix this code if you have - * such a compiler and you change MAX_COMPS_IN_SCAN. - */ - -#ifndef NO_STRUCT_ASSIGN -#define ASSIGN_STATE(dest,src) ((dest) = (src)) -#else -#if MAX_COMPS_IN_SCAN == 4 -#define ASSIGN_STATE(dest,src) \ - ((dest).put_buffer = (src).put_buffer, \ - (dest).put_bits = (src).put_bits, \ - (dest).last_dc_val[0] = (src).last_dc_val[0], \ - (dest).last_dc_val[1] = (src).last_dc_val[1], \ - (dest).last_dc_val[2] = (src).last_dc_val[2], \ - (dest).last_dc_val[3] = (src).last_dc_val[3]) -#endif -#endif - - -typedef struct { - savable_state saved; /* Bit buffer & DC state at start of MCU */ - - /* These fields are NOT loaded into local working state. */ - unsigned int restarts_to_go; /* MCUs left in this restart interval */ - int next_restart_num; /* next restart number to write (0-7) */ - - /* Pointers to derived tables (these workspaces have image lifespan) */ - c_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; - c_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; - -#ifdef ENTROPY_OPT_SUPPORTED /* Statistics tables for optimization */ - long * dc_count_ptrs[NUM_HUFF_TBLS]; - long * ac_count_ptrs[NUM_HUFF_TBLS]; -#endif -} shuff_entropy_encoder; - -typedef shuff_entropy_encoder * shuff_entropy_ptr; - -/* Working state while writing an MCU. - * This struct contains all the fields that are needed by subroutines. - */ - -typedef struct { - JOCTET * next_output_byte; /* => next byte to write in buffer */ - size_t free_in_buffer; /* # of byte spaces remaining in buffer */ - savable_state cur; /* Current bit buffer & DC state */ - j_compress_ptr cinfo; /* dump_buffer needs access to this */ -} working_state; - - -/* Forward declarations */ -METHODDEF(boolean) encode_mcu_huff JPP((j_compress_ptr cinfo, - JBLOCKROW *MCU_data)); -METHODDEF(void) finish_pass_huff JPP((j_compress_ptr cinfo)); -#ifdef ENTROPY_OPT_SUPPORTED -METHODDEF(boolean) encode_mcu_gather JPP((j_compress_ptr cinfo, - JBLOCKROW *MCU_data)); -METHODDEF(void) finish_pass_gather JPP((j_compress_ptr cinfo)); -#endif - - -/* - * Initialize for a Huffman-compressed scan. - * If gather_statistics is TRUE, we do not output anything during the scan, - * just count the Huffman symbols used and generate Huffman code tables. - */ - -METHODDEF(void) -start_pass_huff (j_compress_ptr cinfo, boolean gather_statistics) -{ - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - shuff_entropy_ptr entropy = (shuff_entropy_ptr) lossyc->entropy_private; - int ci, dctbl, actbl; - jpeg_component_info * compptr; - - if (gather_statistics) { -#ifdef ENTROPY_OPT_SUPPORTED - lossyc->entropy_encode_mcu = encode_mcu_gather; - lossyc->pub.entropy_finish_pass = finish_pass_gather; -#else - ERREXIT(cinfo, JERR_NOT_COMPILED); -#endif - } else { - lossyc->entropy_encode_mcu = encode_mcu_huff; - lossyc->pub.entropy_finish_pass = finish_pass_huff; - } - - for (ci = 0; ci < cinfo->comps_in_scan; ci++) { - compptr = cinfo->cur_comp_info[ci]; - dctbl = compptr->dc_tbl_no; - actbl = compptr->ac_tbl_no; - if (gather_statistics) { -#ifdef ENTROPY_OPT_SUPPORTED - /* Check for invalid table indexes */ - /* (make_c_derived_tbl does this in the other path) */ - if (dctbl < 0 || dctbl >= NUM_HUFF_TBLS) - ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, dctbl); - if (actbl < 0 || actbl >= NUM_HUFF_TBLS) - ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, actbl); - /* Allocate and zero the statistics tables */ - /* Note that jpeg_gen_optimal_table expects 257 entries in each table! */ - if (entropy->dc_count_ptrs[dctbl] == NULL) - entropy->dc_count_ptrs[dctbl] = (long *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - 257 * SIZEOF(long)); - MEMZERO(entropy->dc_count_ptrs[dctbl], 257 * SIZEOF(long)); - if (entropy->ac_count_ptrs[actbl] == NULL) - entropy->ac_count_ptrs[actbl] = (long *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - 257 * SIZEOF(long)); - MEMZERO(entropy->ac_count_ptrs[actbl], 257 * SIZEOF(long)); -#endif - } else { - /* Compute derived values for Huffman tables */ - /* We may do this more than once for a table, but it's not expensive */ - jpeg_make_c_derived_tbl(cinfo, TRUE, dctbl, - & entropy->dc_derived_tbls[dctbl]); - jpeg_make_c_derived_tbl(cinfo, FALSE, actbl, - & entropy->ac_derived_tbls[actbl]); - } - /* Initialize DC predictions to 0 */ - entropy->saved.last_dc_val[ci] = 0; - } - - /* Initialize bit buffer to empty */ - entropy->saved.put_buffer = 0; - entropy->saved.put_bits = 0; - - /* Initialize restart stuff */ - entropy->restarts_to_go = cinfo->restart_interval; - entropy->next_restart_num = 0; -} - - -/* Outputting bytes to the file */ - -/* Emit a byte, taking 'action' if must suspend. */ -#define emit_byte(state,val,action) \ - { *(state)->next_output_byte++ = (JOCTET) (val); \ - if (--(state)->free_in_buffer == 0) \ - if (! dump_buffer(state)) \ - { action; } } - - -LOCAL(boolean) -dump_buffer (working_state * state) -/* Empty the output buffer; return TRUE if successful, FALSE if must suspend */ -{ - struct jpeg_destination_mgr * dest = state->cinfo->dest; - - if (! (*dest->empty_output_buffer) (state->cinfo)) - return FALSE; - /* After a successful buffer dump, must reset buffer pointers */ - state->next_output_byte = dest->next_output_byte; - state->free_in_buffer = dest->free_in_buffer; - return TRUE; -} - - -/* Outputting bits to the file */ - -/* Only the right 24 bits of put_buffer are used; the valid bits are - * left-justified in this part. At most 16 bits can be passed to emit_bits - * in one call, and we never retain more than 7 bits in put_buffer - * between calls, so 24 bits are sufficient. - */ - -INLINE -LOCAL(boolean) -emit_bits (working_state * state, unsigned int code, int size) -/* Emit some bits; return TRUE if successful, FALSE if must suspend */ -{ - /* This routine is heavily used, so it's worth coding tightly. */ - register INT32 put_buffer = (INT32) code; - register int put_bits = state->cur.put_bits; - - /* if size is 0, caller used an invalid Huffman table entry */ - if (size == 0) - ERREXIT(state->cinfo, JERR_HUFF_MISSING_CODE); - - put_buffer &= (((INT32) 1)<cur.put_buffer; /* and merge with old buffer contents */ - - while (put_bits >= 8) { - int c = (int) ((put_buffer >> 16) & 0xFF); - - emit_byte(state, c, return FALSE); - if (c == 0xFF) { /* need to stuff a zero byte? */ - emit_byte(state, 0, return FALSE); - } - put_buffer <<= 8; - put_bits -= 8; - } - - state->cur.put_buffer = put_buffer; /* update state variables */ - state->cur.put_bits = put_bits; - - return TRUE; -} - - -LOCAL(boolean) -flush_bits (working_state * state) -{ - if (! emit_bits(state, 0x7F, 7)) /* fill any partial byte with ones */ - return FALSE; - state->cur.put_buffer = 0; /* and reset bit-buffer to empty */ - state->cur.put_bits = 0; - return TRUE; -} - - -/* Encode a single block's worth of coefficients */ - -LOCAL(boolean) -encode_one_block (working_state * state, JCOEFPTR block, int last_dc_val, - c_derived_tbl *dctbl, c_derived_tbl *actbl) -{ - register int temp, temp2; - register int nbits; - register int k, r, i; - - /* Encode the DC coefficient difference per section F.1.2.1 */ - - temp = temp2 = block[0] - last_dc_val; - - if (temp < 0) { - temp = -temp; /* temp is abs value of input */ - /* For a negative input, want temp2 = bitwise complement of abs(input) */ - /* This code assumes we are on a two's complement machine */ - temp2--; - } - - /* Find the number of bits needed for the magnitude of the coefficient */ - nbits = 0; - while (temp) { - nbits++; - temp >>= 1; - } - /* Check for out-of-range coefficient values. - * Since we're encoding a difference, the range limit is twice as much. - */ - if (nbits > MAX_COEF_BITS+1) - ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); - - /* Emit the Huffman-coded symbol for the number of bits */ - if (! emit_bits(state, dctbl->ehufco[nbits], dctbl->ehufsi[nbits])) - return FALSE; - - /* Emit that number of bits of the value, if positive, */ - /* or the complement of its magnitude, if negative. */ - if (nbits) /* emit_bits rejects calls with size 0 */ - if (! emit_bits(state, (unsigned int) temp2, nbits)) - return FALSE; - - /* Encode the AC coefficients per section F.1.2.2 */ - - r = 0; /* r = run length of zeros */ - - for (k = 1; k < DCTSIZE2; k++) { - if ((temp = block[jpeg_natural_order[k]]) == 0) { - r++; - } else { - /* if run length > 15, must emit special run-length-16 codes (0xF0) */ - while (r > 15) { - if (! emit_bits(state, actbl->ehufco[0xF0], actbl->ehufsi[0xF0])) - return FALSE; - r -= 16; - } - - temp2 = temp; - if (temp < 0) { - temp = -temp; /* temp is abs value of input */ - /* This code assumes we are on a two's complement machine */ - temp2--; - } - - /* Find the number of bits needed for the magnitude of the coefficient */ - nbits = 1; /* there must be at least one 1 bit */ - while ((temp >>= 1)) - nbits++; - /* Check for out-of-range coefficient values */ - if (nbits > MAX_COEF_BITS) - ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); - - /* Emit Huffman symbol for run length / number of bits */ - i = (r << 4) + nbits; - if (! emit_bits(state, actbl->ehufco[i], actbl->ehufsi[i])) - return FALSE; - - /* Emit that number of bits of the value, if positive, */ - /* or the complement of its magnitude, if negative. */ - if (! emit_bits(state, (unsigned int) temp2, nbits)) - return FALSE; - - r = 0; - } - } - - /* If the last coef(s) were zero, emit an end-of-block code */ - if (r > 0) - if (! emit_bits(state, actbl->ehufco[0], actbl->ehufsi[0])) - return FALSE; - - return TRUE; -} - - -/* - * Emit a restart marker & resynchronize predictions. - */ - -LOCAL(boolean) -emit_restart (working_state * state, int restart_num) -{ - int ci; - - if (! flush_bits(state)) - return FALSE; - - emit_byte(state, 0xFF, return FALSE); - emit_byte(state, JPEG_RST0 + restart_num, return FALSE); - - /* Re-initialize DC predictions to 0 */ - for (ci = 0; ci < state->cinfo->comps_in_scan; ci++) - state->cur.last_dc_val[ci] = 0; - - /* The restart counter is not updated until we successfully write the MCU. */ - - return TRUE; -} - - -/* - * Encode and output one MCU's worth of Huffman-compressed coefficients. - */ - -METHODDEF(boolean) -encode_mcu_huff (j_compress_ptr cinfo, JBLOCKROW *MCU_data) -{ - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - shuff_entropy_ptr entropy = (shuff_entropy_ptr) lossyc->entropy_private; - working_state state; - int blkn, ci; - jpeg_component_info * compptr; - - /* Load up working state */ - state.next_output_byte = cinfo->dest->next_output_byte; - state.free_in_buffer = cinfo->dest->free_in_buffer; - ASSIGN_STATE(state.cur, entropy->saved); - state.cinfo = cinfo; - - /* Emit restart marker if needed */ - if (cinfo->restart_interval) { - if (entropy->restarts_to_go == 0) - if (! emit_restart(&state, entropy->next_restart_num)) - return FALSE; - } - - /* Encode the MCU data blocks */ - for (blkn = 0; blkn < cinfo->data_units_in_MCU; blkn++) { - ci = cinfo->MCU_membership[blkn]; - compptr = cinfo->cur_comp_info[ci]; - if (! encode_one_block(&state, - MCU_data[blkn][0], state.cur.last_dc_val[ci], - entropy->dc_derived_tbls[compptr->dc_tbl_no], - entropy->ac_derived_tbls[compptr->ac_tbl_no])) - return FALSE; - /* Update last_dc_val */ - state.cur.last_dc_val[ci] = MCU_data[blkn][0][0]; - } - - /* Completed MCU, so update state */ - cinfo->dest->next_output_byte = state.next_output_byte; - cinfo->dest->free_in_buffer = state.free_in_buffer; - ASSIGN_STATE(entropy->saved, state.cur); - - /* Update restart-interval state too */ - if (cinfo->restart_interval) { - if (entropy->restarts_to_go == 0) { - entropy->restarts_to_go = cinfo->restart_interval; - entropy->next_restart_num++; - entropy->next_restart_num &= 7; - } - entropy->restarts_to_go--; - } - - return TRUE; -} - - -/* - * Finish up at the end of a Huffman-compressed scan. - */ - -METHODDEF(void) -finish_pass_huff (j_compress_ptr cinfo) -{ - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - shuff_entropy_ptr entropy = (shuff_entropy_ptr) lossyc->entropy_private; - working_state state; - - /* Load up working state ... flush_bits needs it */ - state.next_output_byte = cinfo->dest->next_output_byte; - state.free_in_buffer = cinfo->dest->free_in_buffer; - ASSIGN_STATE(state.cur, entropy->saved); - state.cinfo = cinfo; - - /* Flush out the last data */ - if (! flush_bits(&state)) - ERREXIT(cinfo, JERR_CANT_SUSPEND); - - /* Update state */ - cinfo->dest->next_output_byte = state.next_output_byte; - cinfo->dest->free_in_buffer = state.free_in_buffer; - ASSIGN_STATE(entropy->saved, state.cur); -} - - -/* - * Huffman coding optimization. - * - * We first scan the supplied data and count the number of uses of each symbol - * that is to be Huffman-coded. (This process MUST agree with the code above.) - * Then we build a Huffman coding tree for the observed counts. - * Symbols which are not needed at all for the particular image are not - * assigned any code, which saves space in the DHT marker as well as in - * the compressed data. - */ - -#ifdef ENTROPY_OPT_SUPPORTED - - -/* Process a single block's worth of coefficients */ - -LOCAL(void) -htest_one_block (j_compress_ptr cinfo, JCOEFPTR block, int last_dc_val, - long dc_counts[], long ac_counts[]) -{ - register int temp; - register int nbits; - register int k, r; - - /* Encode the DC coefficient difference per section F.1.2.1 */ - - temp = block[0] - last_dc_val; - if (temp < 0) - temp = -temp; - - /* Find the number of bits needed for the magnitude of the coefficient */ - nbits = 0; - while (temp) { - nbits++; - temp >>= 1; - } - /* Check for out-of-range coefficient values. - * Since we're encoding a difference, the range limit is twice as much. - */ - if (nbits > MAX_COEF_BITS+1) - ERREXIT(cinfo, JERR_BAD_DCT_COEF); - - /* Count the Huffman symbol for the number of bits */ - dc_counts[nbits]++; - - /* Encode the AC coefficients per section F.1.2.2 */ - - r = 0; /* r = run length of zeros */ - - for (k = 1; k < DCTSIZE2; k++) { - if ((temp = block[jpeg_natural_order[k]]) == 0) { - r++; - } else { - /* if run length > 15, must emit special run-length-16 codes (0xF0) */ - while (r > 15) { - ac_counts[0xF0]++; - r -= 16; - } - - /* Find the number of bits needed for the magnitude of the coefficient */ - if (temp < 0) - temp = -temp; - - /* Find the number of bits needed for the magnitude of the coefficient */ - nbits = 1; /* there must be at least one 1 bit */ - while ((temp >>= 1)) - nbits++; - /* Check for out-of-range coefficient values */ - if (nbits > MAX_COEF_BITS) - ERREXIT(cinfo, JERR_BAD_DCT_COEF); - - /* Count Huffman symbol for run length / number of bits */ - ac_counts[(r << 4) + nbits]++; - - r = 0; - } - } - - /* If the last coef(s) were zero, emit an end-of-block code */ - if (r > 0) - ac_counts[0]++; -} - - -/* - * Trial-encode one MCU's worth of Huffman-compressed coefficients. - * No data is actually output, so no suspension return is possible. - */ - -METHODDEF(boolean) -encode_mcu_gather (j_compress_ptr cinfo, JBLOCKROW *MCU_data) -{ - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - shuff_entropy_ptr entropy = (shuff_entropy_ptr) lossyc->entropy_private; - int blkn, ci; - jpeg_component_info * compptr; - - /* Take care of restart intervals if needed */ - if (cinfo->restart_interval) { - if (entropy->restarts_to_go == 0) { - /* Re-initialize DC predictions to 0 */ - for (ci = 0; ci < cinfo->comps_in_scan; ci++) - entropy->saved.last_dc_val[ci] = 0; - /* Update restart state */ - entropy->restarts_to_go = cinfo->restart_interval; - } - entropy->restarts_to_go--; - } - - for (blkn = 0; blkn < cinfo->data_units_in_MCU; blkn++) { - ci = cinfo->MCU_membership[blkn]; - compptr = cinfo->cur_comp_info[ci]; - htest_one_block(cinfo, MCU_data[blkn][0], entropy->saved.last_dc_val[ci], - entropy->dc_count_ptrs[compptr->dc_tbl_no], - entropy->ac_count_ptrs[compptr->ac_tbl_no]); - entropy->saved.last_dc_val[ci] = MCU_data[blkn][0][0]; - } - - return TRUE; -} - - -/* - * Finish up a statistics-gathering pass and create the new Huffman tables. - */ - -METHODDEF(void) -finish_pass_gather (j_compress_ptr cinfo) -{ - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - shuff_entropy_ptr entropy = (shuff_entropy_ptr) lossyc->entropy_private; - int ci, dctbl, actbl; - jpeg_component_info * compptr; - JHUFF_TBL **htblptr; - boolean did_dc[NUM_HUFF_TBLS]; - boolean did_ac[NUM_HUFF_TBLS]; - - /* It's important not to apply jpeg_gen_optimal_table more than once - * per table, because it clobbers the input frequency counts! - */ - MEMZERO(did_dc, SIZEOF(did_dc)); - MEMZERO(did_ac, SIZEOF(did_ac)); - - for (ci = 0; ci < cinfo->comps_in_scan; ci++) { - compptr = cinfo->cur_comp_info[ci]; - dctbl = compptr->dc_tbl_no; - actbl = compptr->ac_tbl_no; - if (! did_dc[dctbl]) { - htblptr = & cinfo->dc_huff_tbl_ptrs[dctbl]; - if (*htblptr == NULL) - *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); - jpeg_gen_optimal_table(cinfo, *htblptr, entropy->dc_count_ptrs[dctbl]); - did_dc[dctbl] = TRUE; - } - if (! did_ac[actbl]) { - htblptr = & cinfo->ac_huff_tbl_ptrs[actbl]; - if (*htblptr == NULL) - *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); - jpeg_gen_optimal_table(cinfo, *htblptr, entropy->ac_count_ptrs[actbl]); - did_ac[actbl] = TRUE; - } - } -} - - -#endif /* ENTROPY_OPT_SUPPORTED */ - - -METHODDEF(boolean) -need_optimization_pass (j_compress_ptr cinfo) -{ - return TRUE; -} - - -/* - * Module initialization routine for Huffman entropy encoding. - */ - -GLOBAL(void) -jinit_shuff_encoder (j_compress_ptr cinfo) -{ - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - shuff_entropy_ptr entropy; - int i; - - entropy = (shuff_entropy_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(shuff_entropy_encoder)); - lossyc->entropy_private = (struct jpeg_entropy_encoder *) entropy; - lossyc->pub.entropy_start_pass = start_pass_huff; - lossyc->pub.need_optimization_pass = need_optimization_pass; - - /* Mark tables unallocated */ - for (i = 0; i < NUM_HUFF_TBLS; i++) { - entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL; -#ifdef ENTROPY_OPT_SUPPORTED - entropy->dc_count_ptrs[i] = entropy->ac_count_ptrs[i] = NULL; -#endif - } -} diff --git a/jctrans.c b/jctrans.c index ee57e330e..8cac98392 100644 --- a/jctrans.c +++ b/jctrans.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1995-1998, Thomas G. Lane. * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains library routines for transcoding compression, @@ -15,14 +15,11 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossy.h" /* Private declarations for lossy codec */ /* Forward declarations */ LOCAL(void) transencode_master_selection JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays)); -LOCAL(void) transencode_codec - JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays)); LOCAL(void) transencode_coef_controller JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays)); @@ -42,6 +39,9 @@ LOCAL(void) transencode_coef_controller GLOBAL(void) jpeg_write_coefficients (j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays) { + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); + if (cinfo->global_state != CSTATE_START) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); /* Mark all tables to be written */ @@ -73,6 +73,9 @@ jpeg_copy_critical_parameters (j_decompress_ptr srcinfo, JQUANT_TBL *c_quant, *slot_quant; int tblno, ci, coefi; + if (srcinfo->master->lossless) + ERREXIT(dstinfo, JERR_NOTIMPL); + /* Safety check to ensure start_compress not called yet. */ if (dstinfo->global_state != CSTATE_START) ERREXIT1(dstinfo, JERR_BAD_STATE, dstinfo->global_state); @@ -163,7 +166,6 @@ LOCAL(void) transencode_master_selection (j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays) { - cinfo->data_unit = DCTSIZE; /* Although we don't actually use input_components for transcoding, * jcmaster.c's initial_setup will complain if input_components is 0. */ @@ -171,8 +173,22 @@ transencode_master_selection (j_compress_ptr cinfo, /* Initialize master control (includes parameter checking/processing) */ jinit_c_master_control(cinfo, TRUE /* transcode only */); - /* We need a special compression codec. */ - transencode_codec(cinfo, coef_arrays); + /* Entropy encoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef C_PROGRESSIVE_SUPPORTED + jinit_phuff_encoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_encoder(cinfo); + } + + /* We need a special coefficient buffer controller. */ + transencode_coef_controller(cinfo, coef_arrays); jinit_marker_writer(cinfo); @@ -198,6 +214,8 @@ transencode_master_selection (j_compress_ptr cinfo, /* Private buffer controller object */ typedef struct { + struct jpeg_c_coef_controller pub; /* public fields */ + JDIMENSION iMCU_row_num; /* iMCU row # within image */ JDIMENSION mcu_ctr; /* counts MCUs processed in current row */ int MCU_vert_offset; /* counts MCU rows within iMCU row */ @@ -207,18 +225,17 @@ typedef struct { jvirt_barray_ptr * whole_image; /* Workspace for constructing dummy blocks at right/bottom edges. */ - JBLOCKROW dummy_buffer[C_MAX_DATA_UNITS_IN_MCU]; -} c_coef_controller; + JBLOCKROW dummy_buffer[C_MAX_BLOCKS_IN_MCU]; +} my_coef_controller; -typedef c_coef_controller * c_coef_ptr; +typedef my_coef_controller * my_coef_ptr; LOCAL(void) start_iMCU_row (j_compress_ptr cinfo) /* Reset within-iMCU-row counters for a new row */ { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; /* In an interleaved scan, an MCU row is the same as an iMCU row. * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. @@ -245,8 +262,7 @@ start_iMCU_row (j_compress_ptr cinfo) METHODDEF(void) start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; if (pass_mode != JBUF_CRANK_DEST) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); @@ -269,15 +285,14 @@ start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) METHODDEF(boolean) compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; JDIMENSION MCU_col_num; /* index of current MCU within row */ JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; int blkn, ci, xindex, yindex, yoffset, blockcnt; JDIMENSION start_col; JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN]; - JBLOCKROW MCU_buffer[C_MAX_DATA_UNITS_IN_MCU]; + JBLOCKROW MCU_buffer[C_MAX_BLOCKS_IN_MCU]; JBLOCKROW buffer_ptr; jpeg_component_info *compptr; @@ -327,7 +342,7 @@ compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) } } /* Try to write the MCU. */ - if (! (*lossyc->entropy_encode_mcu) (cinfo, MCU_buffer)) { + if (! (*cinfo->entropy->encode_mcu) (cinfo, MCU_buffer)) { /* Suspension forced; update state counters and exit */ coef->MCU_vert_offset = yoffset; coef->mcu_ctr = MCU_col_num; @@ -348,7 +363,7 @@ compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) * Initialize coefficient buffer controller. * * Each passed coefficient array must be the right size for that - * coefficient: width_in_data_units wide and height_in_data_units high, + * coefficient: width_in_blocks wide and height_in_blocks high, * with unitheight at least v_samp_factor. */ @@ -356,15 +371,16 @@ LOCAL(void) transencode_coef_controller (j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - c_coef_ptr coef; + my_coef_ptr coef; JBLOCKROW buffer; int i; - coef = (c_coef_ptr) + coef = (my_coef_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(c_coef_controller)); - lossyc->coef_private = (struct jpeg_c_coef_controller *) coef; + SIZEOF(my_coef_controller)); + cinfo->coef = (struct jpeg_c_coef_controller *) coef; + coef->pub.start_pass = start_pass_coef; + coef->pub.compress_data = compress_output; /* Save pointer to virtual arrays */ coef->whole_image = coef_arrays; @@ -372,51 +388,9 @@ transencode_coef_controller (j_compress_ptr cinfo, /* Allocate and pre-zero space for dummy DCT blocks. */ buffer = (JBLOCKROW) (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, - C_MAX_DATA_UNITS_IN_MCU * SIZEOF(JBLOCK)); - jzero_far((void FAR *) buffer, C_MAX_DATA_UNITS_IN_MCU * SIZEOF(JBLOCK)); - for (i = 0; i < C_MAX_DATA_UNITS_IN_MCU; i++) { + C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + jzero_far((void FAR *) buffer, C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + for (i = 0; i < C_MAX_BLOCKS_IN_MCU; i++) { coef->dummy_buffer[i] = buffer + i; } } - - -/* - * Initialize the transencoer codec. - * This is called only once, during master selection. - */ - -LOCAL(void) -transencode_codec (j_compress_ptr cinfo, - jvirt_barray_ptr * coef_arrays) -{ - j_lossy_c_ptr lossyc; - - /* Create subobject in permanent pool */ - lossyc = (j_lossy_c_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, - SIZEOF(jpeg_lossy_c_codec)); - cinfo->codec = (struct jpeg_c_codec *) lossyc; - - /* Initialize sub-modules */ - - /* Entropy encoding: either Huffman or arithmetic coding. */ - if (cinfo->arith_code) { - ERREXIT(cinfo, JERR_ARITH_NOTIMPL); - } else { - if (cinfo->process == JPROC_PROGRESSIVE) { -#ifdef C_PROGRESSIVE_SUPPORTED - jinit_phuff_encoder(cinfo); -#else - ERREXIT(cinfo, JERR_NOT_COMPILED); -#endif - } else - jinit_shuff_encoder(cinfo); - } - - /* We need a special coefficient buffer controller. */ - transencode_coef_controller(cinfo, coef_arrays); - - /* Initialize method pointers */ - lossyc->pub.start_pass = start_pass_coef; - lossyc->pub.compress_data = compress_output; -} diff --git a/jdapimin.c b/jdapimin.c index d7e2edfde..37eefffd7 100644 --- a/jdapimin.c +++ b/jdapimin.c @@ -5,6 +5,7 @@ * Copyright (C) 1994-1998, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains application interface code for the decompression half @@ -21,6 +22,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" +#include "jdmaster.h" /* @@ -82,6 +84,14 @@ jpeg_CreateDecompress (j_decompress_ptr cinfo, int version, size_t structsize) /* OK, I'm ready */ cinfo->global_state = DSTATE_START; + + /* The master struct is used to store extension parameters, so we allocate it + * here. + */ + cinfo->master = (struct jpeg_decomp_master *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_decomp_master)); + MEMZERO(cinfo->master, SIZEOF(my_decomp_master)); } @@ -151,14 +161,11 @@ default_decompress_parms (j_decompress_ptr cinfo) else if (cid0 == 82 && cid1 == 71 && cid2 == 66) cinfo->jpeg_color_space = JCS_RGB; /* ASCII 'R', 'G', 'B' */ else { - if (cinfo->process == JPROC_LOSSLESS) { - TRACEMS3(cinfo, 1, JTRC_UNKNOWN_LOSSLESS_IDS, cid0, cid1, cid2); + TRACEMS3(cinfo, 1, JTRC_UNKNOWN_IDS, cid0, cid1, cid2); + if (cinfo->master->lossless) cinfo->jpeg_color_space = JCS_RGB; /* assume it's RGB */ - } - else { /* Lossy processes */ - TRACEMS3(cinfo, 1, JTRC_UNKNOWN_LOSSY_IDS, cid0, cid1, cid2); + else cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ - } } } /* Always guess RGB is proper output colorspace. */ diff --git a/jdapistd.c b/jdapistd.c index 6df95e52a..1ae681518 100644 --- a/jdapistd.c +++ b/jdapistd.c @@ -2,9 +2,9 @@ * jdapistd.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. + * Copyright (C) 1994-1996, Thomas G. Lane. * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains application interface code for the decompression half @@ -189,6 +189,9 @@ jpeg_read_raw_data (j_decompress_ptr cinfo, JSAMPIMAGE data, { JDIMENSION lines_per_iMCU_row; + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); + if (cinfo->global_state != DSTATE_RAW_OK) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); if (cinfo->output_scanline >= cinfo->output_height) { @@ -204,12 +207,12 @@ jpeg_read_raw_data (j_decompress_ptr cinfo, JSAMPIMAGE data, } /* Verify that at least one iMCU row can be returned. */ - lines_per_iMCU_row = cinfo->max_v_samp_factor * cinfo->min_codec_data_unit; + lines_per_iMCU_row = cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size; if (max_lines < lines_per_iMCU_row) ERREXIT(cinfo, JERR_BUFFER_SIZE); /* Decompress directly into user's buffer. */ - if (! (*cinfo->codec->decompress_data) (cinfo, data)) + if (! (*cinfo->coef->decompress_data) (cinfo, data)) return 0; /* suspension forced, can do nothing more */ /* OK, we processed one iMCU row. */ diff --git a/jdcoefct.c b/jdcoefct.c index 390d844d1..bc4b63681 100644 --- a/jdcoefct.c +++ b/jdcoefct.c @@ -2,9 +2,9 @@ * jdcoefct.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. + * Copyright (C) 1994-1997, Thomas G. Lane. * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains the coefficient buffer controller for decompression. @@ -19,7 +19,6 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossy.h" /* Block smoothing is only applicable for progressive JPEG, so: */ #ifndef D_PROGRESSIVE_SUPPORTED @@ -29,6 +28,8 @@ /* Private buffer controller object */ typedef struct { + struct jpeg_d_coef_controller pub; /* public fields */ + /* These variables keep track of the current location of the input side. */ /* cinfo->input_iMCU_row is also used for this. */ JDIMENSION MCU_ctr; /* counts MCUs processed in current row */ @@ -38,7 +39,7 @@ typedef struct { /* 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_DATA_UNITS_IN_MCU coefficient blocks, + * 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 @@ -46,7 +47,7 @@ typedef struct { * In multi-pass modes, this array points to the current MCU's blocks * within the virtual arrays; it is used only by the input side. */ - JBLOCKROW MCU_buffer[D_MAX_DATA_UNITS_IN_MCU]; + JBLOCKROW MCU_buffer[D_MAX_BLOCKS_IN_MCU]; #ifdef D_MULTISCAN_FILES_SUPPORTED /* In multi-pass modes, we need a virtual block array for each component. */ @@ -58,9 +59,9 @@ typedef struct { int * coef_bits_latch; #define SAVED_COEFS 6 /* we save coef_bits[0..5] */ #endif -} d_coef_controller; +} my_coef_controller; -typedef d_coef_controller * d_coef_ptr; +typedef my_coef_controller * my_coef_ptr; /* Forward declarations */ METHODDEF(int) decompress_onepass @@ -80,8 +81,7 @@ LOCAL(void) start_iMCU_row (j_decompress_ptr cinfo) /* Reset within-iMCU-row counters for a new row (input side) */ { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - d_coef_ptr coef = (d_coef_ptr) lossyd->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; /* In an interleaved scan, an MCU row is the same as an iMCU row. * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. @@ -121,15 +121,14 @@ METHODDEF(void) start_output_pass (j_decompress_ptr cinfo) { #ifdef BLOCK_SMOOTHING_SUPPORTED - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - d_coef_ptr coef = (d_coef_ptr) lossyd->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; /* If multipass, check to see whether to use block smoothing on this pass */ - if (lossyd->coef_arrays != NULL) { + if (coef->pub.coef_arrays != NULL) { if (cinfo->do_block_smoothing && smoothing_ok(cinfo)) - lossyd->pub.decompress_data = decompress_smooth_data; + coef->pub.decompress_data = decompress_smooth_data; else - lossyd->pub.decompress_data = decompress_data; + coef->pub.decompress_data = decompress_data; } #endif cinfo->output_iMCU_row = 0; @@ -149,8 +148,7 @@ start_output_pass (j_decompress_ptr cinfo) METHODDEF(int) decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - d_coef_ptr coef = (d_coef_ptr) lossyd->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; JDIMENSION MCU_col_num; /* index of current MCU within row */ JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; @@ -167,8 +165,8 @@ decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) MCU_col_num++) { /* Try to fetch an MCU. Entropy decoder expects buffer to be zeroed. */ jzero_far((void FAR *) coef->MCU_buffer[0], - (size_t) (cinfo->data_units_in_MCU * SIZEOF(JBLOCK))); - if (! (*lossyd->entropy_decode_mcu) (cinfo, coef->MCU_buffer)) { + (size_t) (cinfo->blocks_in_MCU * SIZEOF(JBLOCK))); + if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) { /* Suspension forced; update state counters and exit */ coef->MCU_vert_offset = yoffset; coef->MCU_ctr = MCU_col_num; @@ -184,14 +182,14 @@ decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) compptr = cinfo->cur_comp_info[ci]; /* Don't bother to IDCT an uninteresting component. */ if (! compptr->component_needed) { - blkn += compptr->MCU_data_units; + blkn += compptr->MCU_blocks; continue; } - inverse_DCT = lossyd->inverse_DCT[compptr->component_index]; + inverse_DCT = cinfo->idct->inverse_DCT[compptr->component_index]; useful_width = (MCU_col_num < last_MCU_col) ? compptr->MCU_width : compptr->last_col_width; output_ptr = output_buf[compptr->component_index] + - yoffset * compptr->codec_data_unit; + yoffset * compptr->DCT_scaled_size; start_col = MCU_col_num * compptr->MCU_sample_width; for (yindex = 0; yindex < compptr->MCU_height; yindex++) { if (cinfo->input_iMCU_row < last_iMCU_row || @@ -201,11 +199,11 @@ decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) (*inverse_DCT) (cinfo, compptr, (JCOEFPTR) coef->MCU_buffer[blkn+xindex], output_ptr, output_col); - output_col += compptr->codec_data_unit; + output_col += compptr->DCT_scaled_size; } } blkn += compptr->MCU_width; - output_ptr += compptr->codec_data_unit; + output_ptr += compptr->DCT_scaled_size; } } } @@ -247,8 +245,7 @@ dummy_consume_data (j_decompress_ptr cinfo) METHODDEF(int) consume_data (j_decompress_ptr cinfo) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - d_coef_ptr coef = (d_coef_ptr) lossyd->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; JDIMENSION MCU_col_num; /* index of current MCU within row */ int blkn, ci, xindex, yindex, yoffset; JDIMENSION start_col; @@ -287,7 +284,7 @@ consume_data (j_decompress_ptr cinfo) } } /* Try to fetch the MCU. */ - if (! (*lossyd->entropy_decode_mcu) (cinfo, coef->MCU_buffer)) { + if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) { /* Suspension forced; update state counters and exit */ coef->MCU_vert_offset = yoffset; coef->MCU_ctr = MCU_col_num; @@ -319,8 +316,7 @@ consume_data (j_decompress_ptr cinfo) METHODDEF(int) decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - d_coef_ptr coef = (d_coef_ptr) lossyd->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; JDIMENSION block_num; int ci, block_row, block_rows; @@ -355,22 +351,22 @@ decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) block_rows = compptr->v_samp_factor; else { /* NB: can't use last_row_height here; it is input-side-dependent! */ - block_rows = (int) (compptr->height_in_data_units % compptr->v_samp_factor); + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); if (block_rows == 0) block_rows = compptr->v_samp_factor; } - inverse_DCT = lossyd->inverse_DCT[ci]; + inverse_DCT = cinfo->idct->inverse_DCT[ci]; output_ptr = output_buf[ci]; /* Loop over all DCT blocks to be processed. */ for (block_row = 0; block_row < block_rows; block_row++) { buffer_ptr = buffer[block_row]; output_col = 0; - for (block_num = 0; block_num < compptr->width_in_data_units; block_num++) { + for (block_num = 0; block_num < compptr->width_in_blocks; block_num++) { (*inverse_DCT) (cinfo, compptr, (JCOEFPTR) buffer_ptr, output_ptr, output_col); buffer_ptr++; - output_col += compptr->codec_data_unit; + output_col += compptr->DCT_scaled_size; } - output_ptr += compptr->codec_data_unit; + output_ptr += compptr->DCT_scaled_size; } } @@ -410,8 +406,7 @@ decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) LOCAL(boolean) smoothing_ok (j_decompress_ptr cinfo) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - d_coef_ptr coef = (d_coef_ptr) lossyd->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; boolean smoothing_useful = FALSE; int ci, coefi; jpeg_component_info *compptr; @@ -419,7 +414,7 @@ smoothing_ok (j_decompress_ptr cinfo) int * coef_bits; int * coef_bits_latch; - if (! cinfo->process == JPROC_PROGRESSIVE || cinfo->coef_bits == NULL) + if (! cinfo->progressive_mode || cinfo->coef_bits == NULL) return FALSE; /* Allocate latch area if not already done */ @@ -467,8 +462,7 @@ smoothing_ok (j_decompress_ptr cinfo) METHODDEF(int) decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - d_coef_ptr coef = (d_coef_ptr) lossyd->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; JDIMENSION block_num, last_block_column; int ci, block_row, block_rows, access_rows; @@ -516,7 +510,7 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) last_row = FALSE; } else { /* NB: can't use last_row_height here; it is input-side-dependent! */ - block_rows = (int) (compptr->height_in_data_units % compptr->v_samp_factor); + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); if (block_rows == 0) block_rows = compptr->v_samp_factor; access_rows = block_rows; /* this iMCU row only */ last_row = TRUE; @@ -545,7 +539,7 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) Q20 = quanttbl->quantval[Q20_POS]; Q11 = quanttbl->quantval[Q11_POS]; Q02 = quanttbl->quantval[Q02_POS]; - inverse_DCT = lossyd->inverse_DCT[ci]; + inverse_DCT = cinfo->idct->inverse_DCT[ci]; output_ptr = output_buf[ci]; /* Loop over all DCT blocks to be processed. */ for (block_row = 0; block_row < block_rows; block_row++) { @@ -565,7 +559,7 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) DC4 = DC5 = DC6 = (int) buffer_ptr[0][0]; DC7 = DC8 = DC9 = (int) next_block_row[0][0]; output_col = 0; - last_block_column = compptr->width_in_data_units - 1; + last_block_column = compptr->width_in_blocks - 1; for (block_num = 0; block_num <= last_block_column; block_num++) { /* Fetch current DCT block into workspace so we can modify it. */ jcopy_block_row(buffer_ptr, (JBLOCKROW) workspace, (JDIMENSION) 1); @@ -662,9 +656,9 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) DC4 = DC5; DC5 = DC6; DC7 = DC8; DC8 = DC9; buffer_ptr++, prev_block_row++, next_block_row++; - output_col += compptr->codec_data_unit; + output_col += compptr->DCT_scaled_size; } - output_ptr += compptr->codec_data_unit; + output_ptr += compptr->DCT_scaled_size; } } @@ -683,15 +677,14 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) GLOBAL(void) jinit_d_coef_controller (j_decompress_ptr cinfo, boolean need_full_buffer) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - d_coef_ptr coef; + my_coef_ptr coef; - coef = (d_coef_ptr) + coef = (my_coef_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(d_coef_controller)); - lossyd->coef_private = (void *) coef; - lossyd->coef_start_input_pass = start_input_pass; - lossyd->coef_start_output_pass = start_output_pass; + SIZEOF(my_coef_controller)); + cinfo->coef = (struct jpeg_d_coef_controller *) coef; + coef->pub.start_input_pass = start_input_pass; + coef->pub.start_output_pass = start_output_pass; #ifdef BLOCK_SMOOTHING_SUPPORTED coef->coef_bits_latch = NULL; #endif @@ -710,20 +703,20 @@ jinit_d_coef_controller (j_decompress_ptr cinfo, boolean need_full_buffer) access_rows = compptr->v_samp_factor; #ifdef BLOCK_SMOOTHING_SUPPORTED /* If block smoothing could be used, need a bigger window */ - if (cinfo->process == JPROC_PROGRESSIVE) + if (cinfo->progressive_mode) access_rows *= 3; #endif coef->whole_image[ci] = (*cinfo->mem->request_virt_barray) ((j_common_ptr) cinfo, JPOOL_IMAGE, TRUE, - (JDIMENSION) jround_up((long) compptr->width_in_data_units, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, (long) compptr->h_samp_factor), - (JDIMENSION) jround_up((long) compptr->height_in_data_units, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, (long) compptr->v_samp_factor), (JDIMENSION) access_rows); } - lossyd->pub.consume_data = consume_data; - lossyd->pub.decompress_data = decompress_data; - lossyd->coef_arrays = coef->whole_image; /* link to virtual arrays */ + coef->pub.consume_data = consume_data; + coef->pub.decompress_data = decompress_data; + coef->pub.coef_arrays = coef->whole_image; /* link to virtual arrays */ #else ERREXIT(cinfo, JERR_NOT_COMPILED); #endif @@ -734,12 +727,12 @@ jinit_d_coef_controller (j_decompress_ptr cinfo, boolean need_full_buffer) buffer = (JBLOCKROW) (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, - D_MAX_DATA_UNITS_IN_MCU * SIZEOF(JBLOCK)); - for (i = 0; i < D_MAX_DATA_UNITS_IN_MCU; i++) { + D_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + for (i = 0; i < D_MAX_BLOCKS_IN_MCU; i++) { coef->MCU_buffer[i] = buffer + i; } - lossyd->pub.consume_data = dummy_consume_data; - lossyd->pub.decompress_data = decompress_onepass; - lossyd->coef_arrays = NULL; /* flag for no virtual arrays */ + coef->pub.consume_data = dummy_consume_data; + coef->pub.decompress_data = decompress_onepass; + coef->pub.coef_arrays = NULL; /* flag for no virtual arrays */ } } diff --git a/jdcolor.c b/jdcolor.c index 6c04dfe8a..e8b2b95bd 100644 --- a/jdcolor.c +++ b/jdcolor.c @@ -1,8 +1,10 @@ /* * jdcolor.c * + * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. + * Lossless JPEG Modifications: + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains output colorspace conversion routines. @@ -389,6 +391,15 @@ jinit_color_deconverter (j_decompress_ptr cinfo) break; } + /* Prevent lossy color conversion in lossless mode */ + if (cinfo->master->lossless) { + if ((cinfo->out_color_space == JCS_GRAYSCALE && + cinfo->jpeg_color_space != JCS_GRAYSCALE) || + (cinfo->out_color_space != JCS_GRAYSCALE && + cconvert->pub.color_convert != null_convert)) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + } + if (cinfo->quantize_colors) cinfo->output_components = 1; /* single colormapped output component */ else diff --git a/jddctmgr.c b/jddctmgr.c index 3b9bcea16..bbf8d0e92 100644 --- a/jddctmgr.c +++ b/jddctmgr.c @@ -1,10 +1,8 @@ /* * jddctmgr.c * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * * This file contains the inverse-DCT management logic. @@ -20,7 +18,6 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossy.h" /* Private declarations for lossy subsystem */ #include "jdct.h" /* Private declarations for DCT subsystem */ @@ -44,15 +41,17 @@ /* Private subobject for this module */ typedef struct { + struct jpeg_inverse_dct pub; /* public fields */ + /* This array contains the IDCT method code that each multiplier table * is currently set up for, or -1 if it's not yet set up. * The actual multiplier tables are pointed to by dct_table in the * per-component comp_info structures. */ int cur_method[MAX_COMPONENTS]; -} idct_controller; +} my_idct_controller; -typedef idct_controller * idct_ptr; +typedef my_idct_controller * my_idct_ptr; /* Allocated multiplier tables: big enough for any supported variant */ @@ -89,8 +88,7 @@ typedef union { METHODDEF(void) start_pass (j_decompress_ptr cinfo) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - idct_ptr idct = (idct_ptr) lossyd->idct_private; + my_idct_ptr idct = (my_idct_ptr) cinfo->idct; int ci, i; jpeg_component_info *compptr; int method = 0; @@ -100,7 +98,7 @@ start_pass (j_decompress_ptr cinfo) for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { /* Select the proper IDCT routine for this component's scaling */ - switch (compptr->codec_data_unit) { + switch (compptr->DCT_scaled_size) { #ifdef IDCT_SCALING_SUPPORTED case 1: method_ptr = jpeg_idct_1x1; @@ -141,10 +139,10 @@ start_pass (j_decompress_ptr cinfo) } break; default: - ERREXIT1(cinfo, JERR_BAD_DCTSIZE, compptr->codec_data_unit); + ERREXIT1(cinfo, JERR_BAD_DCTSIZE, compptr->DCT_scaled_size); break; } - lossyd->inverse_DCT[ci] = method_ptr; + 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 @@ -248,16 +246,15 @@ start_pass (j_decompress_ptr cinfo) GLOBAL(void) jinit_inverse_dct (j_decompress_ptr cinfo) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - idct_ptr idct; + my_idct_ptr idct; int ci; jpeg_component_info *compptr; - idct = (idct_ptr) + idct = (my_idct_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(idct_controller)); - lossyd->idct_private = (void *) idct; - lossyd->idct_start_pass = start_pass; + SIZEOF(my_idct_controller)); + cinfo->idct = (struct jpeg_inverse_dct *) idct; + idct->pub.start_pass = start_pass; for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { diff --git a/jddiffct.c b/jddiffct.c index ba7fde26f..7f8fbb66b 100644 --- a/jddiffct.c +++ b/jddiffct.c @@ -2,9 +2,10 @@ * jddiffct.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. + * Copyright (C) 1994-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains the [un]difference buffer controller for decompression. @@ -20,7 +21,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossls.h" +#include "jlossls.h" /* Private declarations for lossless codec */ #ifdef D_LOSSLESS_SUPPORTED @@ -28,10 +29,13 @@ /* Private buffer controller object */ typedef struct { + struct jpeg_d_coef_controller pub; /* public fields */ + /* These variables keep track of the current location of the input side. */ /* cinfo->input_iMCU_row is also used for this. */ JDIMENSION MCU_ctr; /* counts MCUs processed in current row */ - unsigned int restart_rows_to_go; /* MCU-rows left in this restart interval */ + unsigned int restart_rows_to_go; /* MCU rows left in this restart + interval */ unsigned int MCU_vert_offset; /* counts MCU rows within iMCU row */ unsigned int MCU_rows_per_iMCU_row; /* number of such rows needed */ @@ -44,9 +48,9 @@ typedef struct { /* In multi-pass modes, we need a virtual sample array for each component. */ jvirt_sarray_ptr whole_image[MAX_COMPONENTS]; #endif -} d_diff_controller; +} my_diff_controller; -typedef d_diff_controller * d_diff_ptr; +typedef my_diff_controller * my_diff_ptr; /* Forward declarations */ METHODDEF(int) decompress_data @@ -61,8 +65,7 @@ LOCAL(void) start_iMCU_row (j_decompress_ptr cinfo) /* Reset within-iMCU-row counters for a new row (input side) */ { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - d_diff_ptr diff = (d_diff_ptr) losslsd->diff_private; + my_diff_ptr diff = (my_diff_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. @@ -89,11 +92,17 @@ start_iMCU_row (j_decompress_ptr cinfo) METHODDEF(void) start_input_pass (j_decompress_ptr cinfo) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - d_diff_ptr diff = (d_diff_ptr) losslsd->diff_private; + my_diff_ptr diff = (my_diff_ptr) cinfo->coef; + + /* Because it is hitching a ride on the jpeg_inverse_dct struct, + * start_pass_lossless() will be called at the start of the output pass. + * This ensures that it will be called at the start of the input pass as + * well. + */ + (*cinfo->idct->start_pass) (cinfo); /* Check that the restart interval is an integer multiple of the number - * of MCU in an MCU-row. + * of MCUs in an MCU row. */ if (cinfo->restart_interval % cinfo->MCUs_per_row != 0) ERREXIT2(cinfo, JERR_BAD_RESTART, @@ -115,13 +124,12 @@ start_input_pass (j_decompress_ptr cinfo) METHODDEF(boolean) process_restart (j_decompress_ptr cinfo) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - d_diff_ptr diff = (d_diff_ptr) losslsd->diff_private; + my_diff_ptr diff = (my_diff_ptr) cinfo->coef; - if (! (*losslsd->entropy_process_restart) (cinfo)) + if (! (*cinfo->entropy->process_restart) (cinfo)) return FALSE; - (*losslsd->predict_process_restart) (cinfo); + (*cinfo->idct->start_pass) (cinfo); /* Reset restart counter */ diff->restart_rows_to_go = cinfo->restart_interval / cinfo->MCUs_per_row; @@ -154,12 +162,12 @@ start_output_pass (j_decompress_ptr cinfo) METHODDEF(int) decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - d_diff_ptr diff = (d_diff_ptr) losslsd->diff_private; + my_diff_ptr diff = (my_diff_ptr) cinfo->coef; + lossless_decomp_ptr losslessd = (lossless_decomp_ptr) cinfo->idct; JDIMENSION MCU_col_num; /* index of current MCU within row */ JDIMENSION MCU_count; /* number of MCUs decoded */ JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; - int comp, ci, yoffset, row, prev_row; + int ci, compi, yoffset, row, prev_row; jpeg_component_info *compptr; /* Loop to process as much as one whole iMCU row */ @@ -174,11 +182,11 @@ decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) } MCU_col_num = diff->MCU_ctr; - /* Try to fetch an MCU-row (or remaining portion of suspended MCU-row). */ + /* Try to fetch an MCU row (or remaining portion of suspended MCU row). */ MCU_count = - (*losslsd->entropy_decode_mcus) (cinfo, - diff->diff_buf, yoffset, MCU_col_num, - cinfo->MCUs_per_row - MCU_col_num); + (*cinfo->entropy->decode_mcus) (cinfo, + diff->diff_buf, yoffset, MCU_col_num, + cinfo->MCUs_per_row - MCU_col_num); if (MCU_count != cinfo->MCUs_per_row - MCU_col_num) { /* Suspension forced; update state counters and exit */ diff->MCU_vert_offset = yoffset; @@ -194,25 +202,24 @@ decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) } /* - * Undifference and scale each scanline of the disassembled MCU-row + * Undifference and scale each scanline of the disassembled MCU row * separately. We do not process dummy samples at the end of a scanline * or dummy rows at the end of the image. */ - for (comp = 0; comp < cinfo->comps_in_scan; comp++) { - compptr = cinfo->cur_comp_info[comp]; - ci = compptr->component_index; + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + compi = compptr->component_index; for (row = 0, prev_row = compptr->v_samp_factor - 1; row < (cinfo->input_iMCU_row == last_iMCU_row ? compptr->last_row_height : compptr->v_samp_factor); prev_row = row, row++) { - (*losslsd->predict_undifference[ci]) (cinfo, ci, - diff->diff_buf[ci][row], - diff->undiff_buf[ci][prev_row], - diff->undiff_buf[ci][row], - compptr->width_in_data_units); - (*losslsd->scaler_scale) (cinfo, diff->undiff_buf[ci][row], - output_buf[ci][row], - compptr->width_in_data_units); + (*losslessd->predict_undifference[compi]) + (cinfo, compi, diff->diff_buf[compi][row], + diff->undiff_buf[compi][prev_row], diff->undiff_buf[compi][row], + compptr->width_in_blocks); + (*losslessd->scaler_scale) (cinfo, diff->undiff_buf[compi][row], + output_buf[compi][row], + compptr->width_in_blocks); } } @@ -255,21 +262,16 @@ dummy_consume_data (j_decompress_ptr cinfo) METHODDEF(int) consume_data (j_decompress_ptr cinfo) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - d_diff_ptr diff = (d_diff_ptr) losslsd->diff_private; - JDIMENSION MCU_col_num; /* index of current MCU within row */ - JDIMENSION MCU_count; /* number of MCUs decoded */ - JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; - int comp, ci, yoffset, row, prev_row; + my_diff_ptr diff = (my_diff_ptr) cinfo->coef; + int ci; JSAMPARRAY buffer[MAX_COMPS_IN_SCAN]; jpeg_component_info *compptr; /* Align the virtual buffers for the components used in this scan. */ - for (comp = 0; comp < cinfo->comps_in_scan; comp++) { - compptr = cinfo->cur_comp_info[comp]; - ci = compptr->component_index; - buffer[ci] = (*cinfo->mem->access_virt_sarray) - ((j_common_ptr) cinfo, diff->whole_image[ci], + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + buffer[compptr->component_index] = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, diff->whole_image[compptr->component_index], cinfo->input_iMCU_row * compptr->v_samp_factor, (JDIMENSION) compptr->v_samp_factor, TRUE); } @@ -279,7 +281,7 @@ consume_data (j_decompress_ptr cinfo) /* - * Output some data from the full-image buffer sample in the multi-pass case. + * Output some data from the full-image sample buffer 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. * @@ -289,8 +291,7 @@ consume_data (j_decompress_ptr cinfo) METHODDEF(int) output_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - d_diff_ptr diff = (d_diff_ptr) losslsd->diff_private; + my_diff_ptr diff = (my_diff_ptr) cinfo->coef; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; int ci, samp_rows, row; JSAMPARRAY buffer; @@ -317,13 +318,13 @@ output_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) samp_rows = compptr->v_samp_factor; else { /* NB: can't use last_row_height here; it is input-side-dependent! */ - samp_rows = (int) (compptr->height_in_data_units % compptr->v_samp_factor); + samp_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); if (samp_rows == 0) samp_rows = compptr->v_samp_factor; } for (row = 0; row < samp_rows; row++) { MEMCOPY(output_buf[ci][row], buffer[row], - compptr->width_in_data_units * SIZEOF(JSAMPLE)); + compptr->width_in_blocks * SIZEOF(JSAMPLE)); } } @@ -342,31 +343,30 @@ output_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) GLOBAL(void) jinit_d_diff_controller (j_decompress_ptr cinfo, boolean need_full_buffer) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - d_diff_ptr diff; + my_diff_ptr diff; int ci; jpeg_component_info *compptr; - diff = (d_diff_ptr) + diff = (my_diff_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(d_diff_controller)); - losslsd->diff_private = (void *) diff; - losslsd->diff_start_input_pass = start_input_pass; - losslsd->pub.start_output_pass = start_output_pass; + SIZEOF(my_diff_controller)); + cinfo->coef = (struct jpeg_d_coef_controller *) diff; + diff->pub.start_input_pass = start_input_pass; + diff->pub.start_output_pass = start_output_pass; /* Create the [un]difference buffers. */ for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - diff->diff_buf[ci] = (*cinfo->mem->alloc_darray) - ((j_common_ptr) cinfo, JPOOL_IMAGE, - (JDIMENSION) jround_up((long) compptr->width_in_data_units, - (long) compptr->h_samp_factor), - (JDIMENSION) compptr->v_samp_factor); - diff->undiff_buf[ci] = (*cinfo->mem->alloc_darray) - ((j_common_ptr) cinfo, JPOOL_IMAGE, - (JDIMENSION) jround_up((long) compptr->width_in_data_units, - (long) compptr->h_samp_factor), - (JDIMENSION) compptr->v_samp_factor); + diff->diff_buf[ci] = + ALLOC_DARRAY(JPOOL_IMAGE, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) compptr->v_samp_factor); + diff->undiff_buf[ci] = + ALLOC_DARRAY(JPOOL_IMAGE, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) compptr->v_samp_factor); } if (need_full_buffer) { @@ -379,20 +379,20 @@ jinit_d_diff_controller (j_decompress_ptr cinfo, boolean need_full_buffer) access_rows = compptr->v_samp_factor; diff->whole_image[ci] = (*cinfo->mem->request_virt_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, - (JDIMENSION) jround_up((long) compptr->width_in_data_units, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, (long) compptr->h_samp_factor), - (JDIMENSION) jround_up((long) compptr->height_in_data_units, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, (long) compptr->v_samp_factor), (JDIMENSION) access_rows); } - losslsd->pub.consume_data = consume_data; - losslsd->pub.decompress_data = output_data; + diff->pub.consume_data = consume_data; + diff->pub.decompress_data = output_data; #else ERREXIT(cinfo, JERR_NOT_COMPILED); #endif } else { - losslsd->pub.consume_data = dummy_consume_data; - losslsd->pub.decompress_data = decompress_data; + diff->pub.consume_data = dummy_consume_data; + diff->pub.decompress_data = decompress_data; diff->whole_image[0] = NULL; /* flag for no virtual arrays */ } } diff --git a/jdhuff.c b/jdhuff.c index 807b7fe36..83c76707c 100644 --- a/jdhuff.c +++ b/jdhuff.c @@ -2,26 +2,150 @@ * jdhuff.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * - * This file contains Huffman entropy decoding routines which are shared - * by the sequential, progressive and lossless decoders. + * This file contains Huffman entropy decoding routines. + * + * Much of the complexity here has to do with supporting input suspension. + * If the data source module demands suspension, we want to be able to back + * up to the start of the current MCU. To do this, we copy state variables + * into local working storage, and update them back to the permanent + * storage only upon successful completion of an MCU. */ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossy.h" /* Private declarations for lossy codec */ -#include "jlossls.h" /* Private declarations for lossless codec */ #include "jdhuff.h" /* Declarations shared with jd*huff.c */ +/* + * Expanded entropy decoder object for Huffman decoding. + * + * The savable_state subrecord contains fields that change within an MCU, + * but must not be updated permanently until we complete the MCU. + */ + +typedef struct { + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ +} savable_state; + +/* This macro is to work around compilers with missing or broken + * structure assignment. You'll need to fix this code if you have + * such a compiler and you change MAX_COMPS_IN_SCAN. + */ + +#ifndef NO_STRUCT_ASSIGN +#define ASSIGN_STATE(dest,src) ((dest) = (src)) +#else +#if MAX_COMPS_IN_SCAN == 4 +#define ASSIGN_STATE(dest,src) \ + ((dest).last_dc_val[0] = (src).last_dc_val[0], \ + (dest).last_dc_val[1] = (src).last_dc_val[1], \ + (dest).last_dc_val[2] = (src).last_dc_val[2], \ + (dest).last_dc_val[3] = (src).last_dc_val[3]) +#endif +#endif + + +typedef struct { + struct jpeg_entropy_decoder pub; /* public fields */ + + /* These fields are loaded into local variables at start of each MCU. + * In case of suspension, we exit WITHOUT updating them. + */ + bitread_perm_state bitstate; /* Bit buffer at start of MCU */ + savable_state saved; /* Other state at start of MCU */ + + /* These fields are NOT loaded into local working state. */ + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + d_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; + d_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; + + /* Precalculated info set up by start_pass for use in decode_mcu: */ + + /* Pointers to derived tables to be used for each block within an MCU */ + d_derived_tbl * dc_cur_tbls[D_MAX_BLOCKS_IN_MCU]; + d_derived_tbl * ac_cur_tbls[D_MAX_BLOCKS_IN_MCU]; + /* Whether we care about the DC and AC coefficient values for each block */ + boolean dc_needed[D_MAX_BLOCKS_IN_MCU]; + boolean ac_needed[D_MAX_BLOCKS_IN_MCU]; +} huff_entropy_decoder; + +typedef huff_entropy_decoder * huff_entropy_ptr; + + +/* + * Initialize for a Huffman-compressed scan. + */ + +METHODDEF(void) +start_pass_huff_decoder (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci, blkn, dctbl, actbl; + jpeg_component_info * compptr; + + /* Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG. + * This ought to be an error condition, but we make it a warning because + * there are some baseline files out there with all zeroes in these bytes. + */ + if (cinfo->Ss != 0 || cinfo->Se != DCTSIZE2-1 || + cinfo->Ah != 0 || cinfo->Al != 0) + WARNMS(cinfo, JWRN_NOT_SEQUENTIAL); + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + actbl = compptr->ac_tbl_no; + /* Compute derived values for Huffman tables */ + /* We may do this more than once for a table, but it's not expensive */ + jpeg_make_d_derived_tbl(cinfo, TRUE, dctbl, + & entropy->dc_derived_tbls[dctbl]); + jpeg_make_d_derived_tbl(cinfo, FALSE, actbl, + & entropy->ac_derived_tbls[actbl]); + /* Initialize DC predictions to 0 */ + entropy->saved.last_dc_val[ci] = 0; + } + + /* Precalculate decoding info for each block in an MCU of this scan */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + /* Precalculate which table to use for each block */ + entropy->dc_cur_tbls[blkn] = entropy->dc_derived_tbls[compptr->dc_tbl_no]; + entropy->ac_cur_tbls[blkn] = entropy->ac_derived_tbls[compptr->ac_tbl_no]; + /* Decide whether we really care about the coefficient values */ + if (compptr->component_needed) { + entropy->dc_needed[blkn] = TRUE; + /* we don't need the ACs if producing a 1/8th-size image */ + entropy->ac_needed[blkn] = (compptr->DCT_scaled_size > 1); + } else { + entropy->dc_needed[blkn] = entropy->ac_needed[blkn] = FALSE; + } + } + + /* Initialize bitread state variables */ + entropy->bitstate.bits_left = 0; + entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ + entropy->pub.insufficient_data = FALSE; + + /* Initialize restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; +} + + /* * Compute the derived values for a Huffman table. * This routine also performs some validation checks on the table. + * + * Note this is also used by jdphuff.c and jdlhuff.c. */ GLOBAL(void) @@ -131,14 +255,14 @@ jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, boolean isDC, int tblno, /* 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..16. - * (Tighter bounds could be applied depending on the data depth and mode, - * but this is sufficient to ensure safe decoding.) + * For DC tables, we require the symbols to be in range 0..15 in lossy mode + * and 0..16 in lossless mode. (Tighter bounds could be applied depending on + * the data depth and mode, but this is sufficient to ensure safe decoding.) */ if (isDC) { for (i = 0; i < numsymbols; i++) { int sym = htbl->huffval[i]; - if (sym < 0 || sym > 16) + if (sym < 0 || sym > (cinfo->master->lossless ? 16 : 15)) ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); } } @@ -146,7 +270,7 @@ jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, boolean isDC, int tblno, /* - * Out-of-line code for bit fetching. + * Out-of-line code for bit fetching (shared with jdphuff.c and jdlhuff.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. @@ -248,14 +372,9 @@ jpeg_fill_bit_buffer (bitread_working_state * state, * We use a nonvolatile flag to ensure that only one warning message * appears per data segment. */ - huffd_common_ptr huffd; - if (cinfo->process == JPROC_LOSSLESS) - huffd = (huffd_common_ptr) ((j_lossless_d_ptr) cinfo->codec)->entropy_private; - else - huffd = (huffd_common_ptr) ((j_lossy_d_ptr) cinfo->codec)->entropy_private; - if (! huffd->insufficient_data) { + if (! cinfo->entropy->insufficient_data) { WARNMS(cinfo, JWRN_HIT_MARKER); - huffd->insufficient_data = TRUE; + cinfo->entropy->insufficient_data = TRUE; } /* Fill the buffer with zero bits */ get_buffer <<= MIN_GET_BITS - bits_left; @@ -315,3 +434,221 @@ jpeg_huff_decode (bitread_working_state * state, return htbl->pub->huffval[ (int) (code + htbl->valoffset[l]) ]; } + + +/* + * Figure F.12: extend sign bit. + * On some machines, a shift and add will be faster than a table lookup. + */ + +#ifdef AVOID_TABLES + +#define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x)) + +#else + +#define HUFF_EXTEND(x,s) ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)) + +static const int extend_test[16] = /* entry n is 2**(n-1) */ + { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, + 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; + +static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ + { 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, + ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, + ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, + ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 }; + +#endif /* AVOID_TABLES */ + + +/* + * Check for a restart marker & resynchronize decoder. + * Returns FALSE if must suspend. + */ + +LOCAL(boolean) +process_restart (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci; + + /* Throw away any unused bits remaining in bit buffer; */ + /* include any full bytes in next_marker's count of discarded bytes */ + cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8; + entropy->bitstate.bits_left = 0; + + /* Advance past the RSTn marker */ + if (! (*cinfo->marker->read_restart_marker) (cinfo)) + return FALSE; + + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) + entropy->saved.last_dc_val[ci] = 0; + + /* Reset restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; + + /* Reset out-of-data flag, unless read_restart_marker left us smack up + * against a marker. In that case we will end up treating the next data + * segment as empty, and we can avoid producing bogus output pixels by + * leaving the flag set. + */ + if (cinfo->unread_marker == 0) + entropy->pub.insufficient_data = FALSE; + + return TRUE; +} + + +/* + * Decode and return one MCU's worth of Huffman-compressed coefficients. + * The coefficients are reordered from zigzag order into natural array order, + * but are not dequantized. + * + * The i'th block of the MCU is stored into the block pointed to by + * MCU_data[i]. WE ASSUME THIS AREA HAS BEEN ZEROED BY THE CALLER. + * (Wholesale zeroing is usually a little faster than retail...) + * + * Returns FALSE if data source requested suspension. In that case no + * changes have been made to permanent state. (Exception: some output + * coefficients may already have been assigned. This is harmless for + * this module, since we'll just re-assign them on the next call.) + */ + +METHODDEF(boolean) +decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int blkn; + BITREAD_STATE_VARS; + savable_state state; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* If we've run out of data, just leave the MCU set to zeroes. + * This way, we return uniform gray for the remainder of the segment. + */ + if (! entropy->pub.insufficient_data) { + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(state, entropy->saved); + + /* Outer loop handles each block in the MCU */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + JBLOCKROW block = MCU_data[blkn]; + d_derived_tbl * dctbl = entropy->dc_cur_tbls[blkn]; + d_derived_tbl * actbl = entropy->ac_cur_tbls[blkn]; + register int s, k, r; + + /* Decode a single block's worth of coefficients */ + + /* Section F.2.2.1: decode the DC coefficient difference */ + HUFF_DECODE(s, br_state, dctbl, return FALSE, label1); + if (s) { + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + } + + if (entropy->dc_needed[blkn]) { + /* Convert DC difference to actual value, update last_dc_val */ + int ci = cinfo->MCU_membership[blkn]; + s += state.last_dc_val[ci]; + state.last_dc_val[ci] = s; + /* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */ + (*block)[0] = (JCOEF) s; + } + + if (entropy->ac_needed[blkn]) { + + /* Section F.2.2.2: decode the AC coefficients */ + /* Since zeroes are skipped, output area must be cleared beforehand */ + for (k = 1; k < DCTSIZE2; k++) { + HUFF_DECODE(s, br_state, actbl, return FALSE, label2); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + /* Output coefficient in natural (dezigzagged) order. + * Note: the extra entries in jpeg_natural_order[] will save us + * if k >= DCTSIZE2, which could happen if the data is corrupted. + */ + (*block)[jpeg_natural_order[k]] = (JCOEF) s; + } else { + if (r != 15) + break; + k += 15; + } + } + + } else { + + /* Section F.2.2.2: decode the AC coefficients */ + /* In this path we just discard the values */ + for (k = 1; k < DCTSIZE2; k++) { + HUFF_DECODE(s, br_state, actbl, return FALSE, label3); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + DROP_BITS(s); + } else { + if (r != 15) + break; + k += 15; + } + } + + } + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(entropy->saved, state); + } + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + + +/* + * Module initialization routine for Huffman entropy decoding. + */ + +GLOBAL(void) +jinit_huff_decoder (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy; + int i; + + entropy = (huff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(huff_entropy_decoder)); + cinfo->entropy = (struct jpeg_entropy_decoder *) entropy; + entropy->pub.start_pass = start_pass_huff_decoder; + entropy->pub.decode_mcu = decode_mcu; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL; + } +} diff --git a/jdhuff.h b/jdhuff.h index 77c5296a5..cc8c30f87 100644 --- a/jdhuff.h +++ b/jdhuff.h @@ -2,15 +2,15 @@ * jdhuff.h * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. * For conditions of distribution and use, see the accompanying README file. * * This file contains declarations for Huffman entropy decoding routines - * that are shared between the sequential decoder (jdhuff.c), the - * progressive decoder (jdphuff.c) and the lossless decoder (jdlhuff.c). - * No other modules need to see these. + * that are shared between the sequential decoder (jdhuff.c), the progressive + * decoder (jdphuff.c), and the lossless decoder (jdlhuff.c). No other modules + * need to see these. */ /* Short forms of external names for systems with brain-damaged linkers. */ @@ -202,30 +202,3 @@ slowlabel: \ EXTERN(int) jpeg_huff_decode JPP((bitread_working_state * state, register bit_buf_type get_buffer, register int bits_left, d_derived_tbl * htbl, int min_bits)); - - -/* Common fields between sequential, progressive and lossless Huffman entropy - * decoder master structs. - */ - -#define huffd_common_fields \ - boolean insufficient_data; /* set TRUE after emmitting warning */ \ - /* These fields are loaded into local variables at start of each MCU. \ - * In case of suspension, we exit WITHOUT updating them. \ - */ \ - bitread_perm_state bitstate /* Bit buffer at start of MCU */ - -/* Routines that are to be used by any or all of the entropy decoders are - * declared to receive a pointer to this structure. There are no actual - * instances of huffd_common_struct, only of shuff_entropy_decoder, - * phuff_entropy_decoder and lhuff_entropy_decoder. - */ -struct huffd_common_struct { - huffd_common_fields; /* Fields common to all decoder struct types */ - /* Additional fields follow in an actual shuff_entropy_decoder, - * phuff_entropy_decoder or lhuff_entropy_decoder struct. All four structs - * must agree on these initial fields! (This would be a lot cleaner in C++.) - */ -}; - -typedef struct huffd_common_struct * huffd_common_ptr; diff --git a/jdinput.c b/jdinput.c index 471ab3333..a4df92041 100644 --- a/jdinput.c +++ b/jdinput.c @@ -2,9 +2,10 @@ * jdinput.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains input control logic for the JPEG decompressor. @@ -44,19 +45,19 @@ initial_setup (j_decompress_ptr cinfo) { int ci; jpeg_component_info *compptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; /* Make sure image isn't bigger than I can handle */ if ((long) cinfo->image_height > (long) JPEG_MAX_DIMENSION || (long) cinfo->image_width > (long) JPEG_MAX_DIMENSION) ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) JPEG_MAX_DIMENSION); - if (cinfo->process == JPROC_LOSSLESS) { + if (cinfo->master->lossless) { /* If precision > compiled-in value, we must downscale */ if (cinfo->data_precision > BITS_IN_JSAMPLE) WARNMS2(cinfo, JWRN_MUST_DOWNSCALE, cinfo->data_precision, BITS_IN_JSAMPLE); - } - else { /* Lossy processes */ + } else { /* For now, precision must match compiled-in value... */ if (cinfo->data_precision != BITS_IN_JSAMPLE) ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); @@ -81,23 +82,23 @@ initial_setup (j_decompress_ptr cinfo) compptr->v_samp_factor); } - /* We initialize codec_data_unit and min_codec_data_unit to data_unit. - * In the full decompressor, this will be overridden by jdmaster.c; + /* We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE in lossy + * mode. 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_codec_data_unit = cinfo->data_unit; + cinfo->min_DCT_scaled_size = data_unit; /* Compute dimensions of components */ for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - compptr->codec_data_unit = cinfo->data_unit; + compptr->DCT_scaled_size = data_unit; /* Size in data units */ - compptr->width_in_data_units = (JDIMENSION) + compptr->width_in_blocks = (JDIMENSION) jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, - (long) (cinfo->max_h_samp_factor * cinfo->data_unit)); - compptr->height_in_data_units = (JDIMENSION) + (long) (cinfo->max_h_samp_factor * data_unit)); + compptr->height_in_blocks = (JDIMENSION) jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, - (long) (cinfo->max_v_samp_factor * cinfo->data_unit)); + (long) (cinfo->max_v_samp_factor * data_unit)); /* 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. @@ -118,11 +119,10 @@ initial_setup (j_decompress_ptr cinfo) /* 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*cinfo->data_unit)); + (long) (cinfo->max_v_samp_factor*data_unit)); /* Decide whether file contains multiple scans */ - if (cinfo->comps_in_scan < cinfo->num_components || - cinfo->process == JPROC_PROGRESSIVE) + if (cinfo->comps_in_scan < cinfo->num_components || cinfo->progressive_mode) cinfo->inputctl->has_multiple_scans = TRUE; else cinfo->inputctl->has_multiple_scans = FALSE; @@ -136,31 +136,32 @@ per_scan_setup (j_decompress_ptr cinfo) { int ci, mcublks, tmp; jpeg_component_info *compptr; - + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + if (cinfo->comps_in_scan == 1) { /* Noninterleaved (single-component) scan */ compptr = cinfo->cur_comp_info[0]; /* Overall image size in MCUs */ - cinfo->MCUs_per_row = compptr->width_in_data_units; - cinfo->MCU_rows_in_scan = compptr->height_in_data_units; + cinfo->MCUs_per_row = compptr->width_in_blocks; + cinfo->MCU_rows_in_scan = compptr->height_in_blocks; /* For noninterleaved scan, always one data unit per MCU */ compptr->MCU_width = 1; compptr->MCU_height = 1; - compptr->MCU_data_units = 1; - compptr->MCU_sample_width = compptr->codec_data_unit; + 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 data unit rows present in the last iMCU row. */ - tmp = (int) (compptr->height_in_data_units % compptr->v_samp_factor); + tmp = (int) (compptr->height_in_blocks % compptr->v_samp_factor); if (tmp == 0) tmp = compptr->v_samp_factor; compptr->last_row_height = tmp; /* Prepare array describing MCU composition */ - cinfo->data_units_in_MCU = 1; + cinfo->blocks_in_MCU = 1; cinfo->MCU_membership[0] = 0; } else { @@ -173,33 +174,33 @@ per_scan_setup (j_decompress_ptr cinfo) /* Overall image size in MCUs */ cinfo->MCUs_per_row = (JDIMENSION) jdiv_round_up((long) cinfo->image_width, - (long) (cinfo->max_h_samp_factor*cinfo->data_unit)); + (long) (cinfo->max_h_samp_factor*data_unit)); cinfo->MCU_rows_in_scan = (JDIMENSION) jdiv_round_up((long) cinfo->image_height, - (long) (cinfo->max_v_samp_factor*cinfo->data_unit)); + (long) (cinfo->max_v_samp_factor*data_unit)); - cinfo->data_units_in_MCU = 0; + cinfo->blocks_in_MCU = 0; for (ci = 0; ci < cinfo->comps_in_scan; ci++) { compptr = cinfo->cur_comp_info[ci]; /* Sampling factors give # of data units of component in each MCU */ compptr->MCU_width = compptr->h_samp_factor; compptr->MCU_height = compptr->v_samp_factor; - compptr->MCU_data_units = compptr->MCU_width * compptr->MCU_height; - compptr->MCU_sample_width = compptr->MCU_width * compptr->codec_data_unit; + 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 data units in last MCU column & row */ - tmp = (int) (compptr->width_in_data_units % compptr->MCU_width); + tmp = (int) (compptr->width_in_blocks % compptr->MCU_width); if (tmp == 0) tmp = compptr->MCU_width; compptr->last_col_width = tmp; - tmp = (int) (compptr->height_in_data_units % compptr->MCU_height); + tmp = (int) (compptr->height_in_blocks % compptr->MCU_height); if (tmp == 0) tmp = compptr->MCU_height; compptr->last_row_height = tmp; /* Prepare array describing MCU composition */ - mcublks = compptr->MCU_data_units; - if (cinfo->data_units_in_MCU + mcublks > D_MAX_DATA_UNITS_IN_MCU) + mcublks = compptr->MCU_blocks; + if (cinfo->blocks_in_MCU + mcublks > D_MAX_BLOCKS_IN_MCU) ERREXIT(cinfo, JERR_BAD_MCU_SIZE); while (mcublks-- > 0) { - cinfo->MCU_membership[cinfo->data_units_in_MCU++] = ci; + cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci; } } @@ -207,6 +208,54 @@ per_scan_setup (j_decompress_ptr cinfo) } +/* + * Save away a copy of the Q-table referenced by each component present + * in the current scan, unless already saved during a prior scan. + * + * In a multiple-scan JPEG file, the encoder could assign different components + * the same Q-table slot number, but change table definitions between scans + * so that each component uses a different Q-table. (The IJG encoder is not + * currently capable of doing this, but other encoders might.) Since we want + * to be able to dequantize all the components at the end of the file, this + * means that we have to save away the table actually used for each component. + * We do this by copying the table at the start of the first scan containing + * the component. + * The JPEG spec prohibits the encoder from changing the contents of a Q-table + * slot between scans of a component using that slot. If the encoder does so + * anyway, this decoder will simply use the Q-table values that were current + * at the start of the first scan for the component. + * + * The decompressor output side looks only at the saved quant tables, + * not at the current Q-table slots. + */ + +LOCAL(void) +latch_quant_tables (j_decompress_ptr cinfo) +{ + int ci, qtblno; + jpeg_component_info *compptr; + JQUANT_TBL * qtbl; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* No work if we already saved Q-table for this component */ + if (compptr->quant_table != NULL) + continue; + /* Make sure specified quantization table is present */ + qtblno = compptr->quant_tbl_no; + if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS || + cinfo->quant_tbl_ptrs[qtblno] == NULL) + ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno); + /* OK, save away the quantization table */ + qtbl = (JQUANT_TBL *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(JQUANT_TBL)); + MEMCOPY(qtbl, cinfo->quant_tbl_ptrs[qtblno], SIZEOF(JQUANT_TBL)); + compptr->quant_table = qtbl; + } +} + + /* * Initialize the input modules to read a scan of compressed data. * The first call to this is done by jdmaster.c after initializing @@ -218,15 +267,18 @@ METHODDEF(void) start_input_pass (j_decompress_ptr cinfo) { per_scan_setup(cinfo); - (*cinfo->codec->start_input_pass) (cinfo); - cinfo->inputctl->consume_input = cinfo->codec->consume_data; + if (! cinfo->master->lossless) + latch_quant_tables(cinfo); + (*cinfo->entropy->start_pass) (cinfo); + (*cinfo->coef->start_input_pass) (cinfo); + cinfo->inputctl->consume_input = cinfo->coef->consume_data; } /* * Finish up after inputting a compressed-data scan. - * This is called by the coefficient controller after it's read all - * the expected data of the scan. + * This is called by the coefficient or difference controller after it's read + * all the expected data of the scan. */ METHODDEF(void) @@ -242,8 +294,8 @@ finish_input_pass (j_decompress_ptr cinfo) * 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. + * coefficient or difference controller's consume_data routine, depending on + * whether we are reading a compressed data segment or inter-segment markers. */ METHODDEF(int) @@ -261,12 +313,6 @@ consume_markers (j_decompress_ptr cinfo) case JPEG_REACHED_SOS: /* Found SOS */ if (inputctl->inheaders) { /* 1st SOS */ initial_setup(cinfo); - /* - * Initialize the decompression codec. We need to do this here so that - * any codec-specific fields and function pointers are available to - * the rest of the library. - */ - jinit_d_codec(cinfo); inputctl->inheaders = FALSE; /* Note: start_input_pass must be called by jdmaster.c * before any more input can be consumed. jdapimin.c is diff --git a/jdlhuff.c b/jdlhuff.c index 6f69a0114..7cb5885fc 100644 --- a/jdlhuff.c +++ b/jdlhuff.c @@ -2,9 +2,10 @@ * jdlhuff.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains Huffman entropy decoding routines for lossless JPEG. @@ -30,11 +31,16 @@ typedef struct { } lhd_output_ptr_info; /* - * Private entropy decoder object for lossless Huffman decoding. + * Expanded entropy decoder object for Huffman decoding in lossless mode. */ typedef struct { - huffd_common_fields; /* Fields shared with other entropy decoders */ + struct jpeg_entropy_decoder pub; /* public fields */ + + /* These fields are loaded into local variables at start of each MCU. + * In case of suspension, we exit WITHOUT updating them. + */ + bitread_perm_state bitstate; /* Bit buffer at start of MCU */ /* Pointers to derived tables (these workspaces have image lifespan) */ d_derived_tbl * derived_tbls[NUM_HUFF_TBLS]; @@ -42,12 +48,12 @@ typedef struct { /* Precalculated info set up by start_pass for use in decode_mcus: */ /* Pointers to derived tables to be used for each data unit within an MCU */ - d_derived_tbl * cur_tbls[D_MAX_DATA_UNITS_IN_MCU]; + d_derived_tbl * cur_tbls[D_MAX_BLOCKS_IN_MCU]; /* Pointers to the proper output difference row for each group of data units * within an MCU. For each component, there are Vi groups of Hi data units. */ - JDIFFROW output_ptr[D_MAX_DATA_UNITS_IN_MCU]; + JDIFFROW output_ptr[D_MAX_BLOCKS_IN_MCU]; /* Number of output pointers in use for the current MCU. This is the sum * of all Vi in the MCU. @@ -57,10 +63,10 @@ typedef struct { /* Information used for positioning the output pointers within the output * difference rows. */ - lhd_output_ptr_info output_ptr_info[D_MAX_DATA_UNITS_IN_MCU]; + lhd_output_ptr_info output_ptr_info[D_MAX_BLOCKS_IN_MCU]; /* Index of the proper output pointer for each data unit within an MCU */ - int output_ptr_index[D_MAX_DATA_UNITS_IN_MCU]; + int output_ptr_index[D_MAX_BLOCKS_IN_MCU]; } lhuff_entropy_decoder; @@ -74,8 +80,7 @@ typedef lhuff_entropy_decoder * lhuff_entropy_ptr; METHODDEF(void) start_pass_lhuff_decoder (j_decompress_ptr cinfo) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) losslsd->entropy_private; + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) cinfo->entropy; int ci, dctbl, sampn, ptrn, yoffset, xoffset; jpeg_component_info * compptr; @@ -93,7 +98,7 @@ start_pass_lhuff_decoder (j_decompress_ptr cinfo) } /* Precalculate decoding info for each sample in an MCU of this scan */ - for (sampn = 0, ptrn = 0; sampn < cinfo->data_units_in_MCU;) { + for (sampn = 0, ptrn = 0; sampn < cinfo->blocks_in_MCU;) { compptr = cinfo->cur_comp_info[cinfo->MCU_membership[sampn]]; ci = compptr->component_index; for (yoffset = 0; yoffset < compptr->MCU_height; yoffset++, ptrn++) { @@ -114,7 +119,7 @@ start_pass_lhuff_decoder (j_decompress_ptr cinfo) /* Initialize bitread state variables */ entropy->bitstate.bits_left = 0; entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ - entropy->insufficient_data = FALSE; + entropy->pub.insufficient_data = FALSE; } @@ -149,12 +154,10 @@ static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ * Returns FALSE if must suspend. */ -METHODDEF(boolean) +LOCAL(boolean) process_restart (j_decompress_ptr cinfo) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) losslsd->entropy_private; - int ci; + lhuff_entropy_ptr entropy = (lhuff_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 */ @@ -171,23 +174,23 @@ process_restart (j_decompress_ptr cinfo) * leaving the flag set. */ if (cinfo->unread_marker == 0) - entropy->insufficient_data = FALSE; + entropy->pub.insufficient_data = FALSE; return TRUE; } /* - * Decode and return nMCU's worth of Huffman-compressed differences. + * Decode and return nMCU MCUs' worth of Huffman-compressed differences. * Each MCU is also disassembled and placed accordingly in diff_buf. * * MCU_col_num specifies the column of the first MCU being requested within - * the MCU-row. This tells us where to position the output row pointers in + * the MCU row. This tells us where to position the output row pointers in * diff_buf. * - * Returns the number of MCUs decoded. This may be less than nMCU if data - * source requested suspension. In that case no changes have been made to - * permanent state. (Exception: some output differences may already have + * Returns the number of MCUs decoded. This may be less than nMCU MCUs if + * data source requested suspension. In that case no changes have been made + * to permanent state. (Exception: some output differences may already have * been assigned. This is harmless for this module, since we'll just * re-assign them on the next call.) */ @@ -196,8 +199,7 @@ METHODDEF(JDIMENSION) decode_mcus (j_decompress_ptr cinfo, JDIFFIMAGE diff_buf, JDIMENSION MCU_row_num, JDIMENSION MCU_col_num, JDIMENSION nMCU) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) losslsd->entropy_private; + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) cinfo->entropy; int mcu_num, sampn, ci, yoffset, MCU_width, ptrn; BITREAD_STATE_VARS; @@ -217,12 +219,12 @@ decode_mcus (j_decompress_ptr cinfo, JDIFFIMAGE diff_buf, * NB: We should find a way to do this without interacting with the * undifferencer module directly. */ - if (entropy->insufficient_data) { + if (entropy->pub.insufficient_data) { for (ptrn = 0; ptrn < entropy->num_output_ptrs; ptrn++) jzero_far((void FAR *) entropy->output_ptr[ptrn], nMCU * entropy->output_ptr_info[ptrn].MCU_width * SIZEOF(JDIFF)); - (*losslsd->predict_process_restart) (cinfo); + (*cinfo->idct->start_pass) (cinfo); } else { @@ -230,12 +232,12 @@ decode_mcus (j_decompress_ptr cinfo, JDIFFIMAGE diff_buf, /* Load up working state */ BITREAD_LOAD_STATE(cinfo,entropy->bitstate); - /* Outer loop handles the number of MCU requested */ + /* Outer loop handles the number of MCUs requested */ for (mcu_num = 0; mcu_num < nMCU; mcu_num++) { /* Inner loop handles the samples in the MCU */ - for (sampn = 0; sampn < cinfo->data_units_in_MCU; sampn++) { + for (sampn = 0; sampn < cinfo->blocks_in_MCU; sampn++) { d_derived_tbl * dctbl = entropy->cur_tbls[sampn]; register int s, r; @@ -265,23 +267,22 @@ decode_mcus (j_decompress_ptr cinfo, JDIFFIMAGE diff_buf, /* - * Module initialization routine for lossless Huffman entropy decoding. + * Module initialization routine for lossless mode Huffman entropy decoding. */ GLOBAL(void) jinit_lhuff_decoder (j_decompress_ptr cinfo) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; lhuff_entropy_ptr entropy; int i; entropy = (lhuff_entropy_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, SIZEOF(lhuff_entropy_decoder)); - losslsd->entropy_private = (void *) entropy; - losslsd->entropy_start_pass = start_pass_lhuff_decoder; - losslsd->entropy_process_restart = process_restart; - losslsd->entropy_decode_mcus = decode_mcus; + cinfo->entropy = (struct jpeg_entropy_decoder *) entropy; + entropy->pub.start_pass = start_pass_lhuff_decoder; + entropy->pub.decode_mcus = decode_mcus; + entropy->pub.process_restart = process_restart; /* Mark tables unallocated */ for (i = 0; i < NUM_HUFF_TBLS; i++) { diff --git a/jdlossls.c b/jdlossls.c index 3ef163c73..7a61facc6 100644 --- a/jdlossls.c +++ b/jdlossls.c @@ -5,9 +5,11 @@ * Copyright (C) 1998, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * - * This file contains the control logic for the lossless JPEG decompressor. + * This file contains prediction, sample undifferencing, point transform, and + * sample scaling routines for the lossless JPEG decompressor. */ #define JPEG_INTERNALS @@ -15,82 +17,303 @@ #include "jpeglib.h" #include "jlossls.h" - #ifdef D_LOSSLESS_SUPPORTED + +/**************** Sample undifferencing (reconstruction) *****************/ + +/* + * In order to avoid a performance penalty for checking which predictor is + * being used and which row is being processed for each call of the + * undifferencer, and to promote optimization, we have separate undifferencing + * functions for each predictor selection value. + * + * We are able to avoid duplicating source code by implementing the predictors + * and undifferencers as macros. Each of the undifferencing functions is + * simply a wrapper around an UNDIFFERENCE macro with the appropriate PREDICTOR + * macro passed as an argument. + */ + +/* Predictor for the first column of the first row: 2^(P-Pt-1) */ +#define INITIAL_PREDICTORx (1 << (cinfo->data_precision - cinfo->Al - 1)) + +/* Predictor for the first column of the remaining rows: Rb */ +#define INITIAL_PREDICTOR2 GETJSAMPLE(prev_row[0]) + + +/* + * 1-Dimensional undifferencer routine. + * + * This macro implements the 1-D horizontal predictor (1). INITIAL_PREDICTOR + * is used as the special case predictor for the first column, which must be + * either INITIAL_PREDICTOR2 or INITIAL_PREDICTORx. The remaining samples + * use PREDICTOR1. + * + * The reconstructed sample is supposed to be calculated modulo 2^16, so we + * logically AND the result with 0xFFFF. +*/ + +#define UNDIFFERENCE_1D(INITIAL_PREDICTOR) \ + int Ra; \ + \ + Ra = (*diff_buf++ + INITIAL_PREDICTOR) & 0xFFFF; \ + *undiff_buf++ = Ra; \ + \ + while (--width) { \ + Ra = (*diff_buf++ + PREDICTOR1) & 0xFFFF; \ + *undiff_buf++ = Ra; \ + } + + +/* + * 2-Dimensional undifferencer routine. + * + * This macro implements the 2-D horizontal predictors (#2-7). PREDICTOR2 is + * used as the special case predictor for the first column. The remaining + * samples use PREDICTOR, which is a function of Ra, Rb, and Rc. + * + * Because prev_row and output_buf may point to the same storage area (in an + * interleaved image with Vi=1, for example), we must take care to buffer Rb/Rc + * before writing the current reconstructed sample value into output_buf. + * + * The reconstructed sample is supposed to be calculated modulo 2^16, so we + * logically AND the result with 0xFFFF. + */ + +#define UNDIFFERENCE_2D(PREDICTOR) \ + int Ra, Rb, Rc; \ + \ + Rb = GETJSAMPLE(*prev_row++); \ + Ra = (*diff_buf++ + PREDICTOR2) & 0xFFFF; \ + *undiff_buf++ = Ra; \ + \ + while (--width) { \ + Rc = Rb; \ + Rb = GETJSAMPLE(*prev_row++); \ + Ra = (*diff_buf++ + PREDICTOR) & 0xFFFF; \ + *undiff_buf++ = Ra; \ + } + + /* - * Compute output image dimensions and related values. + * Undifferencers for the second and subsequent rows in a scan or restart + * interval. The first sample in the row is undifferenced using the vertical + * predictor (2). The rest of the samples are undifferenced using the + * predictor specified in the scan header. */ METHODDEF(void) -calc_output_dimensions (j_decompress_ptr cinfo) +jpeg_undifference1(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) { - /* Hardwire it to "no scaling" */ - cinfo->output_width = cinfo->image_width; - cinfo->output_height = cinfo->image_height; - /* jdinput.c has already initialized codec_data_unit to 1, - * and has computed unscaled downsampled_width and downsampled_height. - */ + UNDIFFERENCE_1D(INITIAL_PREDICTOR2); +} + +METHODDEF(void) +jpeg_undifference2(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR2); + (void)(Rc); +} + +METHODDEF(void) +jpeg_undifference3(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR3); +} + +METHODDEF(void) +jpeg_undifference4(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR4); +} + +METHODDEF(void) +jpeg_undifference5(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR5); +} + +METHODDEF(void) +jpeg_undifference6(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR6); +} + +METHODDEF(void) +jpeg_undifference7(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR7); + (void)(Rc); } /* - * Initialize for an input processing pass. + * Undifferencer for the first row in a scan or restart interval. The first + * sample in the row is undifferenced using the special predictor constant + * x=2^(P-Pt-1). The rest of the samples are undifferenced using the + * 1-D horizontal predictor (1). */ METHODDEF(void) -start_input_pass (j_decompress_ptr cinfo) +jpeg_undifference_first_row(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; + lossless_decomp_ptr losslessd = (lossless_decomp_ptr) cinfo->idct; + + UNDIFFERENCE_1D(INITIAL_PREDICTORx); - (*losslsd->entropy_start_pass) (cinfo); - (*losslsd->predict_start_pass) (cinfo); - (*losslsd->scaler_start_pass) (cinfo); - (*losslsd->diff_start_input_pass) (cinfo); + /* + * Now that we have undifferenced the first row, we want to use the + * undifferencer that corresponds to the predictor specified in the + * scan header. + */ + switch (cinfo->Ss) { + case 1: + losslessd->predict_undifference[comp_index] = jpeg_undifference1; + break; + case 2: + losslessd->predict_undifference[comp_index] = jpeg_undifference2; + break; + case 3: + losslessd->predict_undifference[comp_index] = jpeg_undifference3; + break; + case 4: + losslessd->predict_undifference[comp_index] = jpeg_undifference4; + break; + case 5: + losslessd->predict_undifference[comp_index] = jpeg_undifference5; + break; + case 6: + losslessd->predict_undifference[comp_index] = jpeg_undifference6; + break; + case 7: + losslessd->predict_undifference[comp_index] = jpeg_undifference7; + break; + } } +/**************************** Sample scaling *****************************/ + /* - * Initialize the lossless decompression codec. - * This is called only once, during master selection. + * This is a combination of upscaling the undifferenced sample by 2^Pt and + * downscaling the sample to fit into JSAMPLE. */ -GLOBAL(void) -jinit_lossless_d_codec(j_decompress_ptr cinfo) +METHODDEF(void) +simple_upscale(j_decompress_ptr cinfo, + JDIFFROW diff_buf, JSAMPROW output_buf, + JDIMENSION width) { - j_lossless_d_ptr losslsd; - boolean use_c_buffer; + lossless_decomp_ptr losslessd = (lossless_decomp_ptr) cinfo->idct; - /* Create subobject in permanent pool */ - losslsd = (j_lossless_d_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, - SIZEOF(jpeg_lossless_d_codec)); - cinfo->codec = (struct jpeg_d_codec *) losslsd; - - /* Initialize sub-modules */ - /* Entropy decoding: either Huffman or arithmetic coding. */ - if (cinfo->arith_code) { - ERREXIT(cinfo, JERR_ARITH_NOTIMPL); - } else { - jinit_lhuff_decoder(cinfo); - } + while (width--) + *output_buf++ = (JSAMPLE) (*diff_buf++ << losslessd->scale_factor); +} + +METHODDEF(void) +simple_downscale(j_decompress_ptr cinfo, + JDIFFROW diff_buf, JSAMPROW output_buf, + JDIMENSION width) +{ + lossless_decomp_ptr losslessd = (lossless_decomp_ptr) cinfo->idct; - /* Undifferencer */ - jinit_undifferencer(cinfo); + while (width--) + *output_buf++ = (JSAMPLE) RIGHT_SHIFT(*diff_buf++, + losslessd->scale_factor); +} - /* Scaler */ - jinit_d_scaler(cinfo); +METHODDEF(void) +noscale(j_decompress_ptr cinfo, + JDIFFROW diff_buf, JSAMPROW output_buf, + JDIMENSION width) +{ + while (width--) + *output_buf++ = (JSAMPLE) *diff_buf++; +} - use_c_buffer = cinfo->inputctl->has_multiple_scans || cinfo->buffered_image; - jinit_d_diff_controller(cinfo, use_c_buffer); - /* Initialize method pointers. +/* + * Initialize for an input processing pass. + */ + +METHODDEF(void) +start_pass_lossless (j_decompress_ptr cinfo) +{ + lossless_decomp_ptr losslessd = (lossless_decomp_ptr) cinfo->idct; + int ci, downscale; + + /* Check that the scan parameters Ss, Se, Ah, Al are OK for lossless JPEG. + * + * Ss is the predictor selection value (psv). Legal values for sequential + * lossless JPEG are: 1 <= psv <= 7. + * + * Se and Ah are not used and should be zero. * - * Note: consume_data, start_output_pass and decompress_data are - * assigned in jddiffct.c. + * Al specifies the point transform (Pt). + * Legal values are: 0 <= Pt <= (data precision - 1). */ - losslsd->pub.calc_output_dimensions = calc_output_dimensions; - losslsd->pub.start_input_pass = start_input_pass; + if (cinfo->Ss < 1 || cinfo->Ss > 7 || + cinfo->Se != 0 || cinfo->Ah != 0 || + cinfo->Al < 0 || cinfo->Al >= cinfo->data_precision) + ERREXIT4(cinfo, JERR_BAD_PROGRESSION, + cinfo->Ss, cinfo->Se, cinfo->Ah, cinfo->Al); + + /* Set undifference functions to first row function */ + for (ci = 0; ci < cinfo->num_components; ci++) + losslessd->predict_undifference[ci] = jpeg_undifference_first_row; + + /* + * Downscale by the difference in the input vs. output precision. If the + * output precision >= input precision, then do not downscale. + */ + downscale = BITS_IN_JSAMPLE < cinfo->data_precision ? + cinfo->data_precision - BITS_IN_JSAMPLE : 0; + + losslessd->scale_factor = cinfo->Al - downscale; + + /* Set scaler functions based on scale_factor (positive = left shift) */ + if (losslessd->scale_factor > 0) + losslessd->scaler_scale = simple_upscale; + else if (losslessd->scale_factor < 0) { + losslessd->scale_factor = -losslessd->scale_factor; + losslessd->scaler_scale = simple_downscale; + } + else + losslessd->scaler_scale = noscale; +} + + +/* + * Initialize the lossless decompressor. + */ + +GLOBAL(void) +jinit_lossless_decompressor(j_decompress_ptr cinfo) +{ + lossless_decomp_ptr losslessd; + + /* Create subobject in permanent pool */ + losslessd = (lossless_decomp_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(jpeg_lossless_decompressor)); + cinfo->idct = (struct jpeg_inverse_dct *) losslessd; + losslessd->pub.start_pass = start_pass_lossless; } #endif /* D_LOSSLESS_SUPPORTED */ diff --git a/jdlossy.c b/jdlossy.c deleted file mode 100644 index fb4ebe6df..000000000 --- a/jdlossy.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * jdlossy.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains the control logic for the lossy JPEG decompressor. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jlossy.h" - - -/* - * Compute output image dimensions and related values. - */ - -METHODDEF(void) -calc_output_dimensions (j_decompress_ptr cinfo) -{ -#ifdef IDCT_SCALING_SUPPORTED - int ci; - jpeg_component_info *compptr; - - /* Compute actual output image dimensions and DCT scaling choices. */ - if (cinfo->scale_num * 8 <= cinfo->scale_denom) { - /* Provide 1/8 scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width, 8L); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height, 8L); - cinfo->min_codec_data_unit = 1; - } else if (cinfo->scale_num * 4 <= cinfo->scale_denom) { - /* Provide 1/4 scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width, 4L); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height, 4L); - cinfo->min_codec_data_unit = 2; - } else if (cinfo->scale_num * 2 <= cinfo->scale_denom) { - /* Provide 1/2 scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width, 2L); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height, 2L); - cinfo->min_codec_data_unit = 4; - } else { - /* Provide 1/1 scaling */ - cinfo->output_width = cinfo->image_width; - cinfo->output_height = cinfo->image_height; - cinfo->min_codec_data_unit = DCTSIZE; - } - /* In selecting the actual DCT scaling for each component, we try to - * scale up the chroma components via IDCT scaling rather than upsampling. - * This saves time if the upsampler gets to use 1:1 scaling. - * Note this code assumes that the supported DCT scalings are powers of 2. - */ - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - int ssize = cinfo->min_codec_data_unit; - while (ssize < DCTSIZE && - (compptr->h_samp_factor * ssize * 2 <= - cinfo->max_h_samp_factor * cinfo->min_codec_data_unit) && - (compptr->v_samp_factor * ssize * 2 <= - cinfo->max_v_samp_factor * cinfo->min_codec_data_unit)) { - ssize = ssize * 2; - } - compptr->codec_data_unit = ssize; - } - - /* Recompute downsampled dimensions of components; - * application needs to know these if using raw downsampled data. - */ - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - /* Size in samples, after IDCT scaling */ - compptr->downsampled_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width * - (long) (compptr->h_samp_factor * compptr->codec_data_unit), - (long) (cinfo->max_h_samp_factor * DCTSIZE)); - compptr->downsampled_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height * - (long) (compptr->v_samp_factor * compptr->codec_data_unit), - (long) (cinfo->max_v_samp_factor * DCTSIZE)); - } - -#else /* !IDCT_SCALING_SUPPORTED */ - - /* Hardwire it to "no scaling" */ - cinfo->output_width = cinfo->image_width; - cinfo->output_height = cinfo->image_height; - /* jdinput.c has already initialized codec_data_unit to DCTSIZE, - * and has computed unscaled downsampled_width and downsampled_height. - */ - -#endif /* IDCT_SCALING_SUPPORTED */ -} - - -/* - * Save away a copy of the Q-table referenced by each component present - * in the current scan, unless already saved during a prior scan. - * - * In a multiple-scan JPEG file, the encoder could assign different components - * the same Q-table slot number, but change table definitions between scans - * so that each component uses a different Q-table. (The IJG encoder is not - * currently capable of doing this, but other encoders might.) Since we want - * to be able to dequantize all the components at the end of the file, this - * means that we have to save away the table actually used for each component. - * We do this by copying the table at the start of the first scan containing - * the component. - * The JPEG spec prohibits the encoder from changing the contents of a Q-table - * slot between scans of a component using that slot. If the encoder does so - * anyway, this decoder will simply use the Q-table values that were current - * at the start of the first scan for the component. - * - * The decompressor output side looks only at the saved quant tables, - * not at the current Q-table slots. - */ - -LOCAL(void) -latch_quant_tables (j_decompress_ptr cinfo) -{ - int ci, qtblno; - jpeg_component_info *compptr; - JQUANT_TBL * qtbl; - - for (ci = 0; ci < cinfo->comps_in_scan; ci++) { - compptr = cinfo->cur_comp_info[ci]; - /* No work if we already saved Q-table for this component */ - if (compptr->quant_table != NULL) - continue; - /* Make sure specified quantization table is present */ - qtblno = compptr->quant_tbl_no; - if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS || - cinfo->quant_tbl_ptrs[qtblno] == NULL) - ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno); - /* OK, save away the quantization table */ - qtbl = (JQUANT_TBL *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(JQUANT_TBL)); - MEMCOPY(qtbl, cinfo->quant_tbl_ptrs[qtblno], SIZEOF(JQUANT_TBL)); - compptr->quant_table = qtbl; - } -} - - -/* - * Initialize for an input processing pass. - */ - -METHODDEF(void) -start_input_pass (j_decompress_ptr cinfo) -{ - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - - latch_quant_tables(cinfo); - (*lossyd->entropy_start_pass) (cinfo); - (*lossyd->coef_start_input_pass) (cinfo); -} - - -/* - * Initialize for an output processing pass. - */ - -METHODDEF(void) -start_output_pass (j_decompress_ptr cinfo) -{ - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - - (*lossyd->idct_start_pass) (cinfo); - (*lossyd->coef_start_output_pass) (cinfo); -} - -/* - * Initialize the lossy decompression codec. - * This is called only once, during master selection. - */ - -GLOBAL(void) -jinit_lossy_d_codec (j_decompress_ptr cinfo) -{ - j_lossy_d_ptr lossyd; - boolean use_c_buffer; - - /* Create subobject in permanent pool */ - lossyd = (j_lossy_d_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, - SIZEOF(jpeg_lossy_d_codec)); - cinfo->codec = (struct jpeg_d_codec *) lossyd; - - /* Initialize sub-modules */ - - /* Inverse DCT */ - jinit_inverse_dct(cinfo); - /* Entropy decoding: either Huffman or arithmetic coding. */ - if (cinfo->arith_code) { - ERREXIT(cinfo, JERR_ARITH_NOTIMPL); - } else { - if (cinfo->process == JPROC_PROGRESSIVE) { -#ifdef D_PROGRESSIVE_SUPPORTED - jinit_phuff_decoder(cinfo); -#else - ERREXIT(cinfo, JERR_NOT_COMPILED); -#endif - } else - jinit_shuff_decoder(cinfo); - } - - use_c_buffer = cinfo->inputctl->has_multiple_scans || cinfo->buffered_image; - jinit_d_coef_controller(cinfo, use_c_buffer); - - /* Initialize method pointers. - * - * Note: consume_data and decompress_data are assigned in jdcoefct.c. - */ - lossyd->pub.calc_output_dimensions = calc_output_dimensions; - lossyd->pub.start_input_pass = start_input_pass; - lossyd->pub.start_output_pass = start_output_pass; -} - - - - diff --git a/jdmainct.c b/jdmainct.c index 40ab10d31..d30064ae8 100644 --- a/jdmainct.c +++ b/jdmainct.c @@ -2,9 +2,9 @@ * jdmainct.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. + * Copyright (C) 1994-1996, Thomas G. Lane. * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains the main buffer controller for decompression. @@ -22,35 +22,36 @@ /* * 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. + * buffer; any full-height buffers will be found inside the coefficient, + * difference, 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 * codec_data_unit / min_codec_data_unit) - * sample rows of each component. (We require codec_data_unit values to be - * chosen such that these numbers are integers. In practice codec_data_unit + * 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 codec_data_unit / min_codec_data_unit is an integer.) + * 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 decompression codec will deliver data to us one iMCU row at a time; - * each iMCU row contains v_samp_factor * codec_data_unit sample rows, or - * exactly min_codec_data_unit 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. + * The coefficient or difference 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. + * obtain one iMCU row at a time from the coefficient or difference 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 @@ -66,7 +67,7 @@ * 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 codec_data_unit values, so that the actual scale factor at + * 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 @@ -76,7 +77,7 @@ * 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_codec_data_unit is the number + * 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: @@ -101,11 +102,11 @@ * 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_codec_data_unit is 1. In that + * 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_codec_data_unit is 1. That combination seems unlikely to + * 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. */ @@ -163,7 +164,7 @@ alloc_funny_pointers (j_decompress_ptr cinfo) { my_main_ptr main = (my_main_ptr) cinfo->main; int ci, rgroup; - int M = cinfo->min_codec_data_unit; + int M = cinfo->min_DCT_scaled_size; jpeg_component_info *compptr; JSAMPARRAY xbuf; @@ -177,8 +178,8 @@ alloc_funny_pointers (j_decompress_ptr cinfo) for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - rgroup = (compptr->v_samp_factor * compptr->codec_data_unit) / - cinfo->min_codec_data_unit; /* height of a row group of component */ + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ /* Get space for pointer lists --- M+4 row groups in each list. * We alloc both pointer lists with one call to save a few cycles. */ @@ -204,14 +205,14 @@ make_funny_pointers (j_decompress_ptr cinfo) { my_main_ptr main = (my_main_ptr) cinfo->main; int ci, i, rgroup; - int M = cinfo->min_codec_data_unit; + int M = cinfo->min_DCT_scaled_size; jpeg_component_info *compptr; JSAMPARRAY buf, xbuf0, xbuf1; for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - rgroup = (compptr->v_samp_factor * compptr->codec_data_unit) / - cinfo->min_codec_data_unit; /* height of a row group of component */ + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ xbuf0 = main->xbuffer[0][ci]; xbuf1 = main->xbuffer[1][ci]; /* First copy the workspace pointers as-is */ @@ -244,14 +245,14 @@ set_wraparound_pointers (j_decompress_ptr cinfo) { my_main_ptr main = (my_main_ptr) cinfo->main; int ci, i, rgroup; - int M = cinfo->min_codec_data_unit; + int M = cinfo->min_DCT_scaled_size; jpeg_component_info *compptr; JSAMPARRAY xbuf0, xbuf1; for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - rgroup = (compptr->v_samp_factor * compptr->codec_data_unit) / - cinfo->min_codec_data_unit; /* height of a row group of component */ + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ xbuf0 = main->xbuffer[0][ci]; xbuf1 = main->xbuffer[1][ci]; for (i = 0; i < rgroup; i++) { @@ -279,8 +280,8 @@ set_bottom_pointers (j_decompress_ptr cinfo) for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { /* Count sample rows in one iMCU row and in one row group */ - iMCUheight = compptr->v_samp_factor * compptr->codec_data_unit; - rgroup = iMCUheight / cinfo->min_codec_data_unit; + iMCUheight = compptr->v_samp_factor * compptr->DCT_scaled_size; + rgroup = iMCUheight / cinfo->min_DCT_scaled_size; /* Count nondummy sample rows remaining for this component */ rows_left = (int) (compptr->downsampled_height % (JDIMENSION) iMCUheight); if (rows_left == 0) rows_left = iMCUheight; @@ -353,13 +354,13 @@ process_data_simple_main (j_decompress_ptr cinfo, /* Read input data if we haven't filled the main buffer yet */ if (! main->buffer_full) { - if (! (*cinfo->codec->decompress_data) (cinfo, main->buffer)) + if (! (*cinfo->coef->decompress_data) (cinfo, main->buffer)) return; /* suspension forced, can do nothing more */ main->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ } - /* There are always min_codec_data_unit row groups in an iMCU row. */ - rowgroups_avail = (JDIMENSION) cinfo->min_codec_data_unit; + /* 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. @@ -392,7 +393,7 @@ process_data_context_main (j_decompress_ptr cinfo, /* Read input data if we haven't filled the main buffer yet */ if (! main->buffer_full) { - if (! (*cinfo->codec->decompress_data) (cinfo, + if (! (*cinfo->coef->decompress_data) (cinfo, main->xbuffer[main->whichptr])) return; /* suspension forced, can do nothing more */ main->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ @@ -419,7 +420,7 @@ process_data_context_main (j_decompress_ptr cinfo, case CTX_PREPARE_FOR_IMCU: /* Prepare to process first M-1 row groups of this iMCU row */ main->rowgroup_ctr = 0; - main->rowgroups_avail = (JDIMENSION) (cinfo->min_codec_data_unit - 1); + 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. */ @@ -442,8 +443,8 @@ process_data_context_main (j_decompress_ptr cinfo, 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_codec_data_unit + 1); - main->rowgroups_avail = (JDIMENSION) (cinfo->min_codec_data_unit + 2); + 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; } } @@ -494,21 +495,21 @@ jinit_d_main_controller (j_decompress_ptr cinfo, boolean need_full_buffer) * ngroups is the number of row groups we need. */ if (cinfo->upsample->need_context_rows) { - if (cinfo->min_codec_data_unit < 2) /* unsupported, see comments above */ + if (cinfo->min_DCT_scaled_size < 2) /* unsupported, see comments above */ ERREXIT(cinfo, JERR_NOTIMPL); alloc_funny_pointers(cinfo); /* Alloc space for xbuffer[] lists */ - ngroups = cinfo->min_codec_data_unit + 2; + ngroups = cinfo->min_DCT_scaled_size + 2; } else { - ngroups = cinfo->min_codec_data_unit; + ngroups = cinfo->min_DCT_scaled_size; } for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - rgroup = (compptr->v_samp_factor * compptr->codec_data_unit) / - cinfo->min_codec_data_unit; /* height of a row group of component */ + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ main->buffer[ci] = (*cinfo->mem->alloc_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, - compptr->width_in_data_units * compptr->codec_data_unit, + compptr->width_in_blocks * compptr->DCT_scaled_size, (JDIMENSION) (rgroup * ngroups)); } } diff --git a/jdmarker.c b/jdmarker.c index 62f7762da..5670817a7 100644 --- a/jdmarker.c +++ b/jdmarker.c @@ -5,6 +5,7 @@ * Copyright (C) 1991-1998, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains routines to decode JPEG datastream markers. @@ -236,8 +237,8 @@ get_soi (j_decompress_ptr cinfo) LOCAL(boolean) -get_sof (j_decompress_ptr cinfo, J_CODEC_PROCESS process, boolean is_arith, - int data_unit) +get_sof (j_decompress_ptr cinfo, boolean is_prog, boolean is_lossless, + boolean is_arith) /* Process a SOFn marker */ { INT32 length; @@ -245,8 +246,8 @@ get_sof (j_decompress_ptr cinfo, J_CODEC_PROCESS process, boolean is_arith, jpeg_component_info * compptr; INPUT_VARS(cinfo); - cinfo->data_unit = data_unit; - cinfo->process = process; + cinfo->progressive_mode = is_prog; + cinfo->master->lossless = is_lossless; cinfo->arith_code = is_arith; INPUT_2BYTES(cinfo, length, return FALSE); @@ -980,32 +981,32 @@ read_markers (j_decompress_ptr cinfo) case M_SOF0: /* Baseline */ case M_SOF1: /* Extended sequential, Huffman */ - if (! get_sof(cinfo, JPROC_SEQUENTIAL, FALSE, DCTSIZE)) + if (! get_sof(cinfo, FALSE, FALSE, FALSE)) return JPEG_SUSPENDED; break; case M_SOF2: /* Progressive, Huffman */ - if (! get_sof(cinfo, JPROC_PROGRESSIVE, FALSE, DCTSIZE)) + if (! get_sof(cinfo, TRUE, FALSE, FALSE)) return JPEG_SUSPENDED; break; case M_SOF3: /* Lossless, Huffman */ - if (! get_sof(cinfo, JPROC_LOSSLESS, FALSE, 1)) + if (! get_sof(cinfo, FALSE, TRUE, FALSE)) return JPEG_SUSPENDED; break; case M_SOF9: /* Extended sequential, arithmetic */ - if (! get_sof(cinfo, JPROC_SEQUENTIAL, TRUE, DCTSIZE)) + if (! get_sof(cinfo, FALSE, FALSE, TRUE)) return JPEG_SUSPENDED; break; case M_SOF10: /* Progressive, arithmetic */ - if (! get_sof(cinfo, JPROC_PROGRESSIVE, TRUE, DCTSIZE)) + if (! get_sof(cinfo, TRUE, FALSE, TRUE)) return JPEG_SUSPENDED; break; case M_SOF11: /* Lossless, arithmetic */ - if (! get_sof(cinfo, JPROC_LOSSLESS, TRUE, 1)) + if (! get_sof(cinfo, FALSE, TRUE, TRUE)) return JPEG_SUSPENDED; break; diff --git a/jdmaster.c b/jdmaster.c index 9c9842034..450d0d045 100644 --- a/jdmaster.c +++ b/jdmaster.c @@ -2,9 +2,10 @@ * jdmaster.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains master control logic for the JPEG decompressor. @@ -16,25 +17,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" - - -/* Private state */ - -typedef struct { - struct jpeg_decomp_master pub; /* public fields */ - - int pass_number; /* # of passes completed */ - - boolean using_merged_upsample; /* TRUE if using merged upsample/cconvert */ - - /* Saved references to initialized quantizer modules, - * in case we need to switch modes. - */ - struct jpeg_color_quantizer * quantizer_1pass; - struct jpeg_color_quantizer * quantizer_2pass; -} my_decomp_master; - -typedef my_decomp_master * my_master_ptr; +#include "jdmaster.h" /* @@ -62,11 +45,10 @@ use_merged_upsample (j_decompress_ptr cinfo) cinfo->comp_info[1].v_samp_factor != 1 || cinfo->comp_info[2].v_samp_factor != 1) return FALSE; - /* furthermore, it doesn't work if each component has been - processed differently */ - if (cinfo->comp_info[0].codec_data_unit != cinfo->min_codec_data_unit || - cinfo->comp_info[1].codec_data_unit != cinfo->min_codec_data_unit || - cinfo->comp_info[2].codec_data_unit != cinfo->min_codec_data_unit) + /* furthermore, it doesn't work if we've scaled the IDCTs differently */ + if (cinfo->comp_info[0].DCT_scaled_size != cinfo->min_DCT_scaled_size || + cinfo->comp_info[1].DCT_scaled_size != cinfo->min_DCT_scaled_size || + cinfo->comp_info[2].DCT_scaled_size != cinfo->min_DCT_scaled_size) return FALSE; /* ??? also need to test for upsample-time rescaling, when & if supported */ return TRUE; /* by golly, it'll work... */ @@ -87,11 +69,89 @@ GLOBAL(void) jpeg_calc_output_dimensions (j_decompress_ptr cinfo) /* Do computations that are needed before master selection phase */ { +#ifdef IDCT_SCALING_SUPPORTED + int ci; + jpeg_component_info *compptr; +#endif + /* Prevent application from calling me at wrong times */ if (cinfo->global_state != DSTATE_READY) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); - (*cinfo->codec->calc_output_dimensions) (cinfo); +#ifdef IDCT_SCALING_SUPPORTED + + if (! cinfo->master->lossless) { + /* Compute actual output image dimensions and DCT scaling choices. */ + if (cinfo->scale_num * 8 <= cinfo->scale_denom) { + /* Provide 1/8 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 8L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 8L); + cinfo->min_DCT_scaled_size = 1; + } else if (cinfo->scale_num * 4 <= cinfo->scale_denom) { + /* Provide 1/4 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 4L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 4L); + cinfo->min_DCT_scaled_size = 2; + } else if (cinfo->scale_num * 2 <= cinfo->scale_denom) { + /* Provide 1/2 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 2L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 2L); + cinfo->min_DCT_scaled_size = 4; + } else { + /* Provide 1/1 scaling */ + cinfo->output_width = cinfo->image_width; + cinfo->output_height = cinfo->image_height; + cinfo->min_DCT_scaled_size = DCTSIZE; + } + /* In selecting the actual DCT scaling for each component, we try to + * scale up the chroma components via IDCT scaling rather than upsampling. + * This saves time if the upsampler gets to use 1:1 scaling. + * Note this code assumes that the supported DCT scalings are powers of 2. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + int ssize = cinfo->min_DCT_scaled_size; + while (ssize < DCTSIZE && + (compptr->h_samp_factor * ssize * 2 <= + cinfo->max_h_samp_factor * cinfo->min_DCT_scaled_size) && + (compptr->v_samp_factor * ssize * 2 <= + cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size)) { + ssize = ssize * 2; + } + compptr->DCT_scaled_size = ssize; + } + + /* Recompute downsampled dimensions of components; + * application needs to know these if using raw downsampled data. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Size in samples, after IDCT scaling */ + compptr->downsampled_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * + (long) (compptr->h_samp_factor * compptr->DCT_scaled_size), + (long) (cinfo->max_h_samp_factor * DCTSIZE)); + compptr->downsampled_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * + (long) (compptr->v_samp_factor * compptr->DCT_scaled_size), + (long) (cinfo->max_v_samp_factor * DCTSIZE)); + } + } else +#endif /* !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. + */ + } /* Report number of components in selected colorspace. */ /* Probably this should be in the color conversion module... */ @@ -213,9 +273,21 @@ LOCAL(void) master_selection (j_decompress_ptr cinfo) { my_master_ptr master = (my_master_ptr) cinfo->master; + boolean use_c_buffer; long samplesperrow; JDIMENSION jd_samplesperrow; + /* Disable IDCT scaling and raw (downsampled) data output in lossless mode. + * IDCT scaling is not useful in lossless mode, and it must be disabled in + * order to properly calculate the output dimensions. Raw data output isn't + * particularly useful without subsampling and has not been tested in + * lossless mode. + */ + if (cinfo->master->lossless) { + cinfo->raw_data_out = FALSE; + cinfo->scale_num = cinfo->scale_denom = 1; + } + /* Initialize dimensions and other stuff */ jpeg_calc_output_dimensions(cinfo); prepare_range_limit_table(cinfo); @@ -294,7 +366,49 @@ master_selection (j_decompress_ptr cinfo) jinit_d_post_controller(cinfo, cinfo->enable_2pass_quant); } - /* Initialize principal buffer controllers. */ + if (cinfo->master->lossless) { +#ifdef D_LOSSLESS_SUPPORTED + /* Prediction, sample undifferencing, point transform, and sample size + * scaling + */ + jinit_lossless_decompressor(cinfo); + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + jinit_lhuff_decoder(cinfo); + } + + /* Initialize principal buffer controllers. */ + use_c_buffer = cinfo->inputctl->has_multiple_scans || + cinfo->buffered_image; + jinit_d_diff_controller(cinfo, use_c_buffer); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + /* Inverse DCT */ + jinit_inverse_dct(cinfo); + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef D_PROGRESSIVE_SUPPORTED + jinit_phuff_decoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_decoder(cinfo); + } + + /* Initialize principal buffer controllers. */ + use_c_buffer = cinfo->inputctl->has_multiple_scans || + cinfo->buffered_image; + jinit_d_coef_controller(cinfo, use_c_buffer); + } + if (! cinfo->raw_data_out) jinit_d_main_controller(cinfo, FALSE /* never need full buffer here */); @@ -313,7 +427,7 @@ master_selection (j_decompress_ptr cinfo) cinfo->inputctl->has_multiple_scans) { int nscans; /* Estimate number of scans to set pass_limit. */ - if (cinfo->process == JPROC_PROGRESSIVE) { + if (cinfo->progressive_mode) { /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ nscans = 2 + 3 * cinfo->num_components; } else { @@ -367,7 +481,8 @@ prepare_for_output_pass (j_decompress_ptr cinfo) ERREXIT(cinfo, JERR_MODE_CHANGE); } } - (*cinfo->codec->start_output_pass) (cinfo); + (*cinfo->idct->start_pass) (cinfo); + (*cinfo->coef->start_output_pass) (cinfo); if (! cinfo->raw_data_out) { if (! master->using_merged_upsample) (*cinfo->cconvert->start_pass) (cinfo); @@ -447,12 +562,8 @@ jpeg_new_colormap (j_decompress_ptr cinfo) GLOBAL(void) jinit_master_decompress (j_decompress_ptr cinfo) { - my_master_ptr master; + my_master_ptr master = (my_master_ptr) cinfo->master; - master = (my_master_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(my_decomp_master)); - cinfo->master = (struct jpeg_decomp_master *) master; master->pub.prepare_for_output_pass = prepare_for_output_pass; master->pub.finish_output_pass = finish_output_pass; diff --git a/jdmaster.h b/jdmaster.h new file mode 100644 index 000000000..cab0932c8 --- /dev/null +++ b/jdmaster.h @@ -0,0 +1,27 @@ +/* + * jdmaster.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1991-1995, Thomas G. Lane. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the master control structure for the JPEG decompressor. + */ + +/* Private state */ + +typedef struct { + struct jpeg_decomp_master pub; /* public fields */ + + int pass_number; /* # of passes completed */ + + boolean using_merged_upsample; /* TRUE if using merged upsample/cconvert */ + + /* Saved references to initialized quantizer modules, + * in case we need to switch modes. + */ + struct jpeg_color_quantizer * quantizer_1pass; + struct jpeg_color_quantizer * quantizer_2pass; +} my_decomp_master; + +typedef my_decomp_master * my_master_ptr; diff --git a/jdphuff.c b/jdphuff.c index 528313f57..b4d87f355 100644 --- a/jdphuff.c +++ b/jdphuff.c @@ -2,7 +2,7 @@ * jdphuff.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1995-1998, Thomas G. Lane. + * Copyright (C) 1995-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. * For conditions of distribution and use, see the accompanying README file. @@ -19,14 +19,13 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossy.h" /* Private declarations for lossy subsystem */ #include "jdhuff.h" /* Declarations shared with jd*huff.c */ #ifdef D_PROGRESSIVE_SUPPORTED /* - * Private entropy decoder object for progressive Huffman decoding. + * 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. @@ -57,11 +56,12 @@ typedef struct { typedef struct { - huffd_common_fields; /* Fields shared with other entropy decoders */ + struct jpeg_entropy_decoder pub; /* public fields */ /* These fields are loaded into local variables at start of each MCU. * In case of suspension, we exit WITHOUT updating them. */ + bitread_perm_state bitstate; /* Bit buffer at start of MCU */ savable_state saved; /* Other state at start of MCU */ /* These fields are NOT loaded into local working state. */ @@ -93,8 +93,7 @@ METHODDEF(boolean) decode_mcu_AC_refine JPP((j_decompress_ptr cinfo, METHODDEF(void) start_pass_phuff_decoder (j_decompress_ptr cinfo) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyd->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; boolean is_DC_band, bad; int ci, coefi, tbl; int *coef_bit_ptr; @@ -151,14 +150,14 @@ start_pass_phuff_decoder (j_decompress_ptr cinfo) /* Select MCU decoding routine */ if (cinfo->Ah == 0) { if (is_DC_band) - lossyd->entropy_decode_mcu = decode_mcu_DC_first; + entropy->pub.decode_mcu = decode_mcu_DC_first; else - lossyd->entropy_decode_mcu = decode_mcu_AC_first; + entropy->pub.decode_mcu = decode_mcu_AC_first; } else { if (is_DC_band) - lossyd->entropy_decode_mcu = decode_mcu_DC_refine; + entropy->pub.decode_mcu = decode_mcu_DC_refine; else - lossyd->entropy_decode_mcu = decode_mcu_AC_refine; + entropy->pub.decode_mcu = decode_mcu_AC_refine; } for (ci = 0; ci < cinfo->comps_in_scan; ci++) { @@ -186,7 +185,7 @@ start_pass_phuff_decoder (j_decompress_ptr cinfo) /* Initialize bitread state variables */ entropy->bitstate.bits_left = 0; entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ - entropy->insufficient_data = FALSE; + entropy->pub.insufficient_data = FALSE; /* Initialize private state variables */ entropy->saved.EOBRUN = 0; @@ -230,8 +229,7 @@ static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ LOCAL(boolean) process_restart (j_decompress_ptr cinfo) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyd->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; int ci; /* Throw away any unused bits remaining in bit buffer; */ @@ -258,7 +256,7 @@ process_restart (j_decompress_ptr cinfo) * leaving the flag set. */ if (cinfo->unread_marker == 0) - entropy->insufficient_data = FALSE; + entropy->pub.insufficient_data = FALSE; return TRUE; } @@ -289,8 +287,7 @@ process_restart (j_decompress_ptr cinfo) METHODDEF(boolean) decode_mcu_DC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyd->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; int Al = cinfo->Al; register int s, r; int blkn, ci; @@ -310,7 +307,7 @@ decode_mcu_DC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) /* If we've run out of data, just leave the MCU set to zeroes. * This way, we return uniform gray for the remainder of the segment. */ - if (! entropy->insufficient_data) { + if (! entropy->pub.insufficient_data) { /* Load up working state */ BITREAD_LOAD_STATE(cinfo,entropy->bitstate); @@ -318,7 +315,7 @@ decode_mcu_DC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) /* Outer loop handles each block in the MCU */ - for (blkn = 0; blkn < cinfo->data_units_in_MCU; blkn++) { + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { block = MCU_data[blkn]; ci = cinfo->MCU_membership[blkn]; compptr = cinfo->cur_comp_info[ci]; @@ -361,8 +358,7 @@ decode_mcu_DC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) METHODDEF(boolean) decode_mcu_AC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyd->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; int Se = cinfo->Se; int Al = cinfo->Al; register int s, k, r; @@ -381,7 +377,7 @@ decode_mcu_AC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) /* If we've run out of data, just leave the MCU set to zeroes. * This way, we return uniform gray for the remainder of the segment. */ - if (! entropy->insufficient_data) { + if (! entropy->pub.insufficient_data) { /* Load up working state. * We can avoid loading/saving bitread state if in an EOB run. @@ -447,8 +443,7 @@ decode_mcu_AC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) METHODDEF(boolean) decode_mcu_DC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyd->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; int p1 = 1 << cinfo->Al; /* 1 in the bit position being coded */ int blkn; JBLOCKROW block; @@ -470,7 +465,7 @@ decode_mcu_DC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) /* Outer loop handles each block in the MCU */ - for (blkn = 0; blkn < cinfo->data_units_in_MCU; blkn++) { + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { block = MCU_data[blkn]; /* Encoded data is simply the next bit of the two's-complement DC value */ @@ -497,8 +492,7 @@ decode_mcu_DC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) METHODDEF(boolean) decode_mcu_AC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyd->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; int Se = cinfo->Se; int p1 = 1 << cinfo->Al; /* 1 in the bit position being coded */ int m1 = (-1) << cinfo->Al; /* -1 in the bit position being coded */ @@ -520,7 +514,7 @@ decode_mcu_AC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) /* If we've run out of data, don't modify the MCU. */ - if (! entropy->insufficient_data) { + if (! entropy->pub.insufficient_data) { /* Load up working state */ BITREAD_LOAD_STATE(cinfo,entropy->bitstate); @@ -648,7 +642,6 @@ decode_mcu_AC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) GLOBAL(void) jinit_phuff_decoder (j_decompress_ptr cinfo) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; phuff_entropy_ptr entropy; int *coef_bit_ptr; int ci, i; @@ -656,8 +649,8 @@ jinit_phuff_decoder (j_decompress_ptr cinfo) entropy = (phuff_entropy_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, SIZEOF(phuff_entropy_decoder)); - lossyd->entropy_private = (void *) entropy; - lossyd->entropy_start_pass = start_pass_phuff_decoder; + cinfo->entropy = (struct jpeg_entropy_decoder *) entropy; + entropy->pub.start_pass = start_pass_phuff_decoder; /* Mark derived tables unallocated */ for (i = 0; i < NUM_HUFF_TBLS; i++) { diff --git a/jdpred.c b/jdpred.c deleted file mode 100644 index 796d1950b..000000000 --- a/jdpred.c +++ /dev/null @@ -1,249 +0,0 @@ -/* - * jdpred.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains sample undifferencing (reconstruction) for lossless JPEG. - * - * In order to avoid paying the performance penalty of having to check the - * predictor being used and the row being processed for each call of the - * undifferencer, and to promote optimization, we have separate undifferencing - * functions for each case. - * - * We are able to avoid duplicating source code by implementing the predictors - * and undifferencers as macros. Each of the undifferencing functions are - * simply wrappers around an UNDIFFERENCE macro with the appropriate PREDICTOR - * macro passed as an argument. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jlossls.h" /* Private declarations for lossless codec */ - - -#ifdef D_LOSSLESS_SUPPORTED - -/* Predictor for the first column of the first row: 2^(P-Pt-1) */ -#define INITIAL_PREDICTORx (1 << (cinfo->data_precision - cinfo->Al - 1)) - -/* Predictor for the first column of the remaining rows: Rb */ -#define INITIAL_PREDICTOR2 GETJSAMPLE(prev_row[0]) - - -/* - * 1-Dimensional undifferencer routine. - * - * This macro implements the 1-D horizontal predictor (1). INITIAL_PREDICTOR - * is used as the special case predictor for the first column, which must be - * either INITIAL_PREDICTOR2 or INITIAL_PREDICTORx. The remaining samples - * use PREDICTOR1. - * - * The reconstructed sample is supposed to be calculated modulo 2^16, so we - * logically AND the result with 0xFFFF. -*/ - -#define UNDIFFERENCE_1D(INITIAL_PREDICTOR) \ - int xindex; \ - int Ra; \ - \ - Ra = (diff_buf[0] + INITIAL_PREDICTOR) & 0xFFFF; \ - undiff_buf[0] = Ra; \ - \ - for (xindex = 1; xindex < width; xindex++) { \ - Ra = (diff_buf[xindex] + PREDICTOR1) & 0xFFFF; \ - undiff_buf[xindex] = Ra; \ - } - -/* - * 2-Dimensional undifferencer routine. - * - * This macro implements the 2-D horizontal predictors (#2-7). PREDICTOR2 is - * used as the special case predictor for the first column. The remaining - * samples use PREDICTOR, which is a function of Ra, Rb, Rc. - * - * Because prev_row and output_buf may point to the same storage area (in an - * interleaved image with Vi=1, for example), we must take care to buffer Rb/Rc - * before writing the current reconstructed sample value into output_buf. - * - * The reconstructed sample is supposed to be calculated modulo 2^16, so we - * logically AND the result with 0xFFFF. - */ - -#define UNDIFFERENCE_2D(PREDICTOR) \ - int xindex; \ - int Ra, Rb, Rc; \ - \ - Rb = GETJSAMPLE(prev_row[0]); \ - Ra = (diff_buf[0] + PREDICTOR2) & 0xFFFF; \ - undiff_buf[0] = Ra; \ - \ - for (xindex = 1; xindex < width; xindex++) { \ - Rc = Rb; \ - Rb = GETJSAMPLE(prev_row[xindex]); \ - Ra = (diff_buf[xindex] + PREDICTOR) & 0xFFFF; \ - undiff_buf[xindex] = Ra; \ - } - - -/* - * Undifferencers for the all rows but the first in a scan or restart interval. - * The first sample in the row is undifferenced using the vertical - * predictor (2). The rest of the samples are undifferenced using the - * predictor specified in the scan header. - */ - -METHODDEF(void) -jpeg_undifference1(j_decompress_ptr cinfo, int comp_index, - JDIFFROW diff_buf, JDIFFROW prev_row, - JDIFFROW undiff_buf, JDIMENSION width) -{ - UNDIFFERENCE_1D(INITIAL_PREDICTOR2); -} - -METHODDEF(void) -jpeg_undifference2(j_decompress_ptr cinfo, int comp_index, - JDIFFROW diff_buf, JDIFFROW prev_row, - JDIFFROW undiff_buf, JDIMENSION width) -{ - UNDIFFERENCE_2D(PREDICTOR2); -} - -METHODDEF(void) -jpeg_undifference3(j_decompress_ptr cinfo, int comp_index, - JDIFFROW diff_buf, JDIFFROW prev_row, - JDIFFROW undiff_buf, JDIMENSION width) -{ - UNDIFFERENCE_2D(PREDICTOR3); -} - -METHODDEF(void) -jpeg_undifference4(j_decompress_ptr cinfo, int comp_index, - JDIFFROW diff_buf, JDIFFROW prev_row, - JDIFFROW undiff_buf, JDIMENSION width) -{ - UNDIFFERENCE_2D(PREDICTOR4); -} - -METHODDEF(void) -jpeg_undifference5(j_decompress_ptr cinfo, int comp_index, - JDIFFROW diff_buf, JDIFFROW prev_row, - JDIFFROW undiff_buf, JDIMENSION width) -{ - UNDIFFERENCE_2D(PREDICTOR5); -} - -METHODDEF(void) -jpeg_undifference6(j_decompress_ptr cinfo, int comp_index, - JDIFFROW diff_buf, JDIFFROW prev_row, - JDIFFROW undiff_buf, JDIMENSION width) -{ - UNDIFFERENCE_2D(PREDICTOR6); -} - -METHODDEF(void) -jpeg_undifference7(j_decompress_ptr cinfo, int comp_index, - JDIFFROW diff_buf, JDIFFROW prev_row, - JDIFFROW undiff_buf, JDIMENSION width) -{ - UNDIFFERENCE_2D(PREDICTOR7); -} - - -/* - * Undifferencer for the first row in a scan or restart interval. The first - * sample in the row is undifferenced using the special predictor constant - * x=2^(P-Pt-1). The rest of the samples are undifferenced using the - * 1-D horizontal predictor (1). - */ - -METHODDEF(void) -jpeg_undifference_first_row(j_decompress_ptr cinfo, int comp_index, - JDIFFROW diff_buf, JDIFFROW prev_row, - JDIFFROW undiff_buf, JDIMENSION width) -{ - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - - UNDIFFERENCE_1D(INITIAL_PREDICTORx); - - /* - * Now that we have undifferenced the first row, we want to use the - * undifferencer which corresponds to the predictor specified in the - * scan header. - */ - switch (cinfo->Ss) { - case 1: - losslsd->predict_undifference[comp_index] = jpeg_undifference1; - break; - case 2: - losslsd->predict_undifference[comp_index] = jpeg_undifference2; - break; - case 3: - losslsd->predict_undifference[comp_index] = jpeg_undifference3; - break; - case 4: - losslsd->predict_undifference[comp_index] = jpeg_undifference4; - break; - case 5: - losslsd->predict_undifference[comp_index] = jpeg_undifference5; - break; - case 6: - losslsd->predict_undifference[comp_index] = jpeg_undifference6; - break; - case 7: - losslsd->predict_undifference[comp_index] = jpeg_undifference7; - break; - } -} - - -/* - * Initialize for an input processing pass. - */ - -METHODDEF(void) -predict_start_pass (j_decompress_ptr cinfo) -{ - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - int ci; - - /* Check that the scan parameters Ss, Se, Ah, Al are OK for lossless JPEG. - * - * Ss is the predictor selection value (psv). Legal values for sequential - * lossless JPEG are: 1 <= psv <= 7. - * - * Se and Ah are not used and should be zero. - * - * Al specifies the point transform (Pt). Legal values are: 0 <= Pt <= 15. - */ - if (cinfo->Ss < 1 || cinfo->Ss > 7 || - cinfo->Se != 0 || cinfo->Ah != 0 || - cinfo->Al > 15) /* need not check for < 0 */ - ERREXIT4(cinfo, JERR_BAD_LOSSLESS, - cinfo->Ss, cinfo->Se, cinfo->Ah, cinfo->Al); - - /* Set undifference functions to first row function */ - for (ci = 0; ci < cinfo->num_components; ci++) - losslsd->predict_undifference[ci] = jpeg_undifference_first_row; -} - - -/* - * Module initialization routine for the undifferencer. - */ - -GLOBAL(void) -jinit_undifferencer (j_decompress_ptr cinfo) -{ - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - - losslsd->predict_start_pass = predict_start_pass; - losslsd->predict_process_restart = predict_start_pass; -} - -#endif /* D_LOSSLESS_SUPPORTED */ - diff --git a/jdsample.c b/jdsample.c index 14c3f8bb0..80ffefb2a 100644 --- a/jdsample.c +++ b/jdsample.c @@ -1,16 +1,14 @@ /* * jdsample.c * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * * This file contains upsampling routines. * * Upsampling input data is counted in "row groups". A row group - * is defined to be (v_samp_factor * codec_data_unit / min_codec_data_unit) + * 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). @@ -417,10 +415,10 @@ jinit_upsampler (j_decompress_ptr cinfo) if (cinfo->CCIR601_sampling) /* this isn't supported */ ERREXIT(cinfo, JERR_CCIR601_NOTIMPL); - /* jdmainct.c doesn't support context rows when min_codec_data_unit = 1, + /* jdmainct.c doesn't support context rows when min_DCT_scaled_size = 1, * so don't ask for it. */ - do_fancy = cinfo->do_fancy_upsampling && cinfo->min_codec_data_unit > 1; + do_fancy = cinfo->do_fancy_upsampling && cinfo->min_DCT_scaled_size > 1; /* Verify we can handle the sampling factors, select per-component methods, * and create storage as needed. @@ -430,10 +428,10 @@ jinit_upsampler (j_decompress_ptr cinfo) /* 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->codec_data_unit) / - cinfo->min_codec_data_unit; - v_in_group = (compptr->v_samp_factor * compptr->codec_data_unit) / - cinfo->min_codec_data_unit; + h_in_group = (compptr->h_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; + v_in_group = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; h_out_group = cinfo->max_h_samp_factor; v_out_group = cinfo->max_v_samp_factor; upsample->rowgroup_height[ci] = v_in_group; /* save for use later */ diff --git a/jdscale.c b/jdscale.c deleted file mode 100644 index 4025d6182..000000000 --- a/jdscale.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * jdscale.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains sample scaling for lossless JPEG. This is a - * combination of upscaling the undifferenced sample by 2^Pt and downscaling - * the sample to fit into JSAMPLE. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jlossls.h" /* Private declarations for lossless codec */ - - -#ifdef D_LOSSLESS_SUPPORTED - -/* - * Private scaler object for lossless decoding. - */ - -typedef struct { - int scale_factor; -} scaler; - -typedef scaler * scaler_ptr; - - -/* - * Scalers for packing sample differences into JSAMPLEs. - */ - -METHODDEF(void) -simple_upscale(j_decompress_ptr cinfo, - JDIFFROW diff_buf, JSAMPROW output_buf, - JDIMENSION width) -{ - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - scaler_ptr scaler = (scaler_ptr) losslsd->scaler_private; - int scale_factor = scaler->scale_factor; - int xindex; - - for (xindex = 0; xindex < width; xindex++) - output_buf[xindex] = (JSAMPLE) (diff_buf[xindex] << scale_factor); -} - -METHODDEF(void) -simple_downscale(j_decompress_ptr cinfo, - JDIFFROW diff_buf, JSAMPROW output_buf, - JDIMENSION width) -{ - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - scaler_ptr scaler = (scaler_ptr) losslsd->scaler_private; - int scale_factor = scaler->scale_factor; - int xindex; - - for (xindex = 0; xindex < width; xindex++) - output_buf[xindex] = (JSAMPLE) RIGHT_SHIFT(diff_buf[xindex], scale_factor); -} - -METHODDEF(void) -noscale(j_decompress_ptr cinfo, - JDIFFROW diff_buf, JSAMPROW output_buf, - JDIMENSION width) -{ - int xindex; - - for (xindex = 0; xindex < width; xindex++) - output_buf[xindex] = (JSAMPLE) diff_buf[xindex]; -} - - -METHODDEF(void) -scaler_start_pass (j_decompress_ptr cinfo) -{ - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - scaler_ptr scaler = (scaler_ptr) losslsd->scaler_private; - int downscale; - - /* - * Downscale by the difference in the input vs. output precision. If the - * output precision >= input precision, then do not downscale. - */ - downscale = BITS_IN_JSAMPLE < cinfo->data_precision ? - cinfo->data_precision - BITS_IN_JSAMPLE : 0; - - scaler->scale_factor = cinfo->Al - downscale; - - /* Set scaler functions based on scale_factor (positive = left shift) */ - if (scaler->scale_factor > 0) - losslsd->scaler_scale = simple_upscale; - else if (scaler->scale_factor < 0) { - scaler->scale_factor = -scaler->scale_factor; - losslsd->scaler_scale = simple_downscale; - } - else - losslsd->scaler_scale = noscale; -} - - -GLOBAL(void) -jinit_d_scaler (j_decompress_ptr cinfo) -{ - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - scaler_ptr scaler; - - scaler = (scaler_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(scaler)); - losslsd->scaler_private = (void *) scaler; - losslsd->scaler_start_pass = scaler_start_pass; -} - -#endif /* D_LOSSLESS_SUPPORTED */ - diff --git a/jdshuff.c b/jdshuff.c deleted file mode 100644 index d15a6d155..000000000 --- a/jdshuff.c +++ /dev/null @@ -1,362 +0,0 @@ -/* - * jdshuff.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains Huffman entropy decoding routines for sequential JPEG. - * - * Much of the complexity here has to do with supporting input suspension. - * If the data source module demands suspension, we want to be able to back - * up to the start of the current MCU. To do this, we copy state variables - * into local working storage, and update them back to the permanent - * storage only upon successful completion of an MCU. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jlossy.h" /* Private declarations for lossy codec */ -#include "jdhuff.h" /* Declarations shared with jd*huff.c */ - - -/* - * Private entropy decoder object for Huffman decoding. - * - * The savable_state subrecord contains fields that change within an MCU, - * but must not be updated permanently until we complete the MCU. - */ - -typedef struct { - int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ -} savable_state; - -/* This macro is to work around compilers with missing or broken - * structure assignment. You'll need to fix this code if you have - * such a compiler and you change MAX_COMPS_IN_SCAN. - */ - -#ifndef NO_STRUCT_ASSIGN -#define ASSIGN_STATE(dest,src) ((dest) = (src)) -#else -#if MAX_COMPS_IN_SCAN == 4 -#define ASSIGN_STATE(dest,src) \ - ((dest).last_dc_val[0] = (src).last_dc_val[0], \ - (dest).last_dc_val[1] = (src).last_dc_val[1], \ - (dest).last_dc_val[2] = (src).last_dc_val[2], \ - (dest).last_dc_val[3] = (src).last_dc_val[3]) -#endif -#endif - - -typedef struct { - huffd_common_fields; /* Fields shared with other entropy decoders */ - - /* These fields are loaded into local variables at start of each MCU. - * In case of suspension, we exit WITHOUT updating them. - */ - savable_state saved; /* Other state at start of MCU */ - - /* These fields are NOT loaded into local working state. */ - unsigned int restarts_to_go; /* MCUs left in this restart interval */ - - /* Pointers to derived tables (these workspaces have image lifespan) */ - d_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; - d_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; - - /* Precalculated info set up by start_pass for use in decode_mcu: */ - - /* Pointers to derived tables to be used for each block within an MCU */ - d_derived_tbl * dc_cur_tbls[D_MAX_DATA_UNITS_IN_MCU]; - d_derived_tbl * ac_cur_tbls[D_MAX_DATA_UNITS_IN_MCU]; - /* Whether we care about the DC and AC coefficient values for each block */ - boolean dc_needed[D_MAX_DATA_UNITS_IN_MCU]; - boolean ac_needed[D_MAX_DATA_UNITS_IN_MCU]; -} shuff_entropy_decoder; - -typedef shuff_entropy_decoder * shuff_entropy_ptr; - - -/* - * Initialize for a Huffman-compressed scan. - */ - -METHODDEF(void) -start_pass_huff_decoder (j_decompress_ptr cinfo) -{ - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - shuff_entropy_ptr entropy = (shuff_entropy_ptr) lossyd->entropy_private; - int ci, blkn, dctbl, actbl; - jpeg_component_info * compptr; - - /* Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG. - * This ought to be an error condition, but we make it a warning because - * there are some baseline files out there with all zeroes in these bytes. - */ - if (cinfo->Ss != 0 || cinfo->Se != DCTSIZE2-1 || - cinfo->Ah != 0 || cinfo->Al != 0) - WARNMS(cinfo, JWRN_NOT_SEQUENTIAL); - - for (ci = 0; ci < cinfo->comps_in_scan; ci++) { - compptr = cinfo->cur_comp_info[ci]; - dctbl = compptr->dc_tbl_no; - actbl = compptr->ac_tbl_no; - /* Compute derived values for Huffman tables */ - /* We may do this more than once for a table, but it's not expensive */ - jpeg_make_d_derived_tbl(cinfo, TRUE, dctbl, - & entropy->dc_derived_tbls[dctbl]); - jpeg_make_d_derived_tbl(cinfo, FALSE, actbl, - & entropy->ac_derived_tbls[actbl]); - /* Initialize DC predictions to 0 */ - entropy->saved.last_dc_val[ci] = 0; - } - - /* Precalculate decoding info for each block in an MCU of this scan */ - for (blkn = 0; blkn < cinfo->data_units_in_MCU; blkn++) { - ci = cinfo->MCU_membership[blkn]; - compptr = cinfo->cur_comp_info[ci]; - /* Precalculate which table to use for each block */ - entropy->dc_cur_tbls[blkn] = entropy->dc_derived_tbls[compptr->dc_tbl_no]; - entropy->ac_cur_tbls[blkn] = entropy->ac_derived_tbls[compptr->ac_tbl_no]; - /* Decide whether we really care about the coefficient values */ - if (compptr->component_needed) { - entropy->dc_needed[blkn] = TRUE; - /* we don't need the ACs if producing a 1/8th-size image */ - entropy->ac_needed[blkn] = (compptr->codec_data_unit > 1); - } else { - entropy->dc_needed[blkn] = entropy->ac_needed[blkn] = FALSE; - } - } - - /* Initialize bitread state variables */ - entropy->bitstate.bits_left = 0; - entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ - entropy->insufficient_data = FALSE; - - /* Initialize restart counter */ - entropy->restarts_to_go = cinfo->restart_interval; -} - - -/* - * Figure F.12: extend sign bit. - * On some machines, a shift and add will be faster than a table lookup. - */ - -#ifdef AVOID_TABLES - -#define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x)) - -#else - -#define HUFF_EXTEND(x,s) ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)) - -static const int extend_test[16] = /* entry n is 2**(n-1) */ - { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, - 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; - -static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ - { 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, - ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, - ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, - ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 }; - -#endif /* AVOID_TABLES */ - - -/* - * Check for a restart marker & resynchronize decoder. - * Returns FALSE if must suspend. - */ - -LOCAL(boolean) -process_restart (j_decompress_ptr cinfo) -{ - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - shuff_entropy_ptr entropy = (shuff_entropy_ptr) lossyd->entropy_private; - int ci; - - /* Throw away any unused bits remaining in bit buffer; */ - /* include any full bytes in next_marker's count of discarded bytes */ - cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8; - entropy->bitstate.bits_left = 0; - - /* Advance past the RSTn marker */ - if (! (*cinfo->marker->read_restart_marker) (cinfo)) - return FALSE; - - /* Re-initialize DC predictions to 0 */ - for (ci = 0; ci < cinfo->comps_in_scan; ci++) - entropy->saved.last_dc_val[ci] = 0; - - /* Reset restart counter */ - entropy->restarts_to_go = cinfo->restart_interval; - - /* Reset out-of-data flag, unless read_restart_marker left us smack up - * against a marker. In that case we will end up treating the next data - * segment as empty, and we can avoid producing bogus output pixels by - * leaving the flag set. - */ - if (cinfo->unread_marker == 0) - entropy->insufficient_data = FALSE; - - return TRUE; -} - - -/* - * Decode and return one MCU's worth of Huffman-compressed coefficients. - * The coefficients are reordered from zigzag order into natural array order, - * but are not dequantized. - * - * The i'th block of the MCU is stored into the block pointed to by - * MCU_data[i]. WE ASSUME THIS AREA HAS BEEN ZEROED BY THE CALLER. - * (Wholesale zeroing is usually a little faster than retail...) - * - * Returns FALSE if data source requested suspension. In that case no - * changes have been made to permanent state. (Exception: some output - * coefficients may already have been assigned. This is harmless for - * this module, since we'll just re-assign them on the next call.) - */ - -METHODDEF(boolean) -decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) -{ - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - shuff_entropy_ptr entropy = (shuff_entropy_ptr) lossyd->entropy_private; - int blkn; - BITREAD_STATE_VARS; - savable_state state; - - /* Process restart marker if needed; may have to suspend */ - if (cinfo->restart_interval) { - if (entropy->restarts_to_go == 0) - if (! process_restart(cinfo)) - return FALSE; - } - - /* If we've run out of data, just leave the MCU set to zeroes. - * This way, we return uniform gray for the remainder of the segment. - */ - if (! entropy->insufficient_data) { - - /* Load up working state */ - BITREAD_LOAD_STATE(cinfo,entropy->bitstate); - ASSIGN_STATE(state, entropy->saved); - - /* Outer loop handles each block in the MCU */ - - for (blkn = 0; blkn < cinfo->data_units_in_MCU; blkn++) { - JBLOCKROW block = MCU_data[blkn]; - d_derived_tbl * dctbl = entropy->dc_cur_tbls[blkn]; - d_derived_tbl * actbl = entropy->ac_cur_tbls[blkn]; - register int s, k, r; - - /* Decode a single block's worth of coefficients */ - - /* Section F.2.2.1: decode the DC coefficient difference */ - HUFF_DECODE(s, br_state, dctbl, return FALSE, label1); - if (s) { - CHECK_BIT_BUFFER(br_state, s, return FALSE); - r = GET_BITS(s); - s = HUFF_EXTEND(r, s); - } - - if (entropy->dc_needed[blkn]) { - /* Convert DC difference to actual value, update last_dc_val */ - int ci = cinfo->MCU_membership[blkn]; - s += state.last_dc_val[ci]; - state.last_dc_val[ci] = s; - /* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */ - (*block)[0] = (JCOEF) s; - } - - if (entropy->ac_needed[blkn]) { - - /* Section F.2.2.2: decode the AC coefficients */ - /* Since zeroes are skipped, output area must be cleared beforehand */ - for (k = 1; k < DCTSIZE2; k++) { - HUFF_DECODE(s, br_state, actbl, return FALSE, label2); - - r = s >> 4; - s &= 15; - - if (s) { - k += r; - CHECK_BIT_BUFFER(br_state, s, return FALSE); - r = GET_BITS(s); - s = HUFF_EXTEND(r, s); - /* Output coefficient in natural (dezigzagged) order. - * Note: the extra entries in jpeg_natural_order[] will save us - * if k >= DCTSIZE2, which could happen if the data is corrupted. - */ - (*block)[jpeg_natural_order[k]] = (JCOEF) s; - } else { - if (r != 15) - break; - k += 15; - } - } - - } else { - - /* Section F.2.2.2: decode the AC coefficients */ - /* In this path we just discard the values */ - for (k = 1; k < DCTSIZE2; k++) { - HUFF_DECODE(s, br_state, actbl, return FALSE, label3); - - r = s >> 4; - s &= 15; - - if (s) { - k += r; - CHECK_BIT_BUFFER(br_state, s, return FALSE); - DROP_BITS(s); - } else { - if (r != 15) - break; - k += 15; - } - } - - } - } - - /* Completed MCU, so update state */ - BITREAD_SAVE_STATE(cinfo,entropy->bitstate); - ASSIGN_STATE(entropy->saved, state); - } - - /* Account for restart interval (no-op if not using restarts) */ - entropy->restarts_to_go--; - - return TRUE; -} - - -/* - * Module initialization routine for Huffman entropy decoding. - */ - -GLOBAL(void) -jinit_shuff_decoder (j_decompress_ptr cinfo) -{ - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - shuff_entropy_ptr entropy; - int i; - - entropy = (shuff_entropy_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(shuff_entropy_decoder)); - lossyd->entropy_private = (void *) entropy; - lossyd->entropy_start_pass = start_pass_huff_decoder; - lossyd->entropy_decode_mcu = decode_mcu; - - /* Mark tables unallocated */ - for (i = 0; i < NUM_HUFF_TBLS; i++) { - entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL; - } -} diff --git a/jdtrans.c b/jdtrans.c index c05c7e62a..7296289c2 100644 --- a/jdtrans.c +++ b/jdtrans.c @@ -2,9 +2,9 @@ * jdtrans.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1995-1998, Thomas G. Lane. + * Copyright (C) 1995-1997, Thomas G. Lane. * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains library routines for transcoding decompression, @@ -15,7 +15,6 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossy.h" /* Forward declarations */ @@ -47,13 +46,8 @@ LOCAL(void) transdecode_master_selection JPP((j_decompress_ptr cinfo)); GLOBAL(jvirt_barray_ptr *) jpeg_read_coefficients (j_decompress_ptr cinfo) { - j_lossy_d_ptr decomp; - - /* Can't read coefficients from lossless streams */ - if (cinfo->process == JPROC_LOSSLESS) { - ERREXIT(cinfo, JERR_CANT_TRANSCODE); - return NULL; - } + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); if (cinfo->global_state == DSTATE_READY) { /* First call: initialize active modules */ @@ -91,7 +85,7 @@ jpeg_read_coefficients (j_decompress_ptr cinfo) */ if ((cinfo->global_state == DSTATE_STOPPING || cinfo->global_state == DSTATE_BUFIMAGE) && cinfo->buffered_image) { - return ((j_lossy_d_ptr) cinfo->codec)->coef_arrays; + return cinfo->coef->coef_arrays; } /* Oops, improper usage */ ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); @@ -110,8 +104,22 @@ transdecode_master_selection (j_decompress_ptr cinfo) /* This is effectively a buffered-image operation. */ cinfo->buffered_image = TRUE; - /* Initialize decompression codec */ - jinit_d_codec(cinfo); + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef D_PROGRESSIVE_SUPPORTED + jinit_phuff_decoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_decoder(cinfo); + } + + /* Always get a full-image coefficient buffer. */ + jinit_d_coef_controller(cinfo, TRUE); /* We can now tell the memory manager to allocate virtual arrays. */ (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); @@ -123,7 +131,7 @@ transdecode_master_selection (j_decompress_ptr cinfo) if (cinfo->progress != NULL) { int nscans; /* Estimate number of scans to set pass_limit. */ - if (cinfo->process == JPROC_PROGRESSIVE) { + if (cinfo->progressive_mode) { /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ nscans = 2 + 3 * cinfo->num_components; } else if (cinfo->inputctl->has_multiple_scans) { diff --git a/jerror.h b/jerror.h index 096003d43..f57148bde 100644 --- a/jerror.h +++ b/jerror.h @@ -2,9 +2,10 @@ * jerror.h * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. + * Copyright (C) 1994-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file defines the error and message codes for the JPEG library. @@ -47,27 +48,22 @@ JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix") JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix") JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode") JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") -JMESSAGE(JERR_BAD_DCT_COEF, "DCT coefficient out of range") +JMESSAGE(JERR_BAD_DCT_COEF, + "DCT coefficient (lossy) or spatial difference (lossless) out of range") JMESSAGE(JERR_BAD_DCTSIZE, "IDCT output block size %d not supported") -JMESSAGE(JERR_BAD_DIFF, "spatial difference out of range") JMESSAGE(JERR_BAD_HUFF_TABLE, "Bogus Huffman table definition") JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace") JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace") JMESSAGE(JERR_BAD_LENGTH, "Bogus marker length") JMESSAGE(JERR_BAD_LIB_VERSION, "Wrong JPEG library version: library is %d, caller expects %d") -JMESSAGE(JERR_BAD_LOSSLESS, - "Invalid lossless parameters Ss=%d Se=%d Ah=%d Al=%d") -JMESSAGE(JERR_BAD_LOSSLESS_SCRIPT, - "Invalid lossless parameters at scan script entry %d") JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan") JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d") JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d") JMESSAGE(JERR_BAD_PROGRESSION, - "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d") + "Invalid progressive/lossless parameters Ss=%d Se=%d Ah=%d Al=%d") JMESSAGE(JERR_BAD_PROG_SCRIPT, - "Invalid progressive parameters at scan script entry %d") -JMESSAGE(JERR_BAD_RESTART, "Invalid restart interval: %d, must be an integer multiple of the number of MCUs in an MCU_row (%d)") + "Invalid progressive/lossless parameters at scan script entry %d") JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors") JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d") JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d") @@ -76,8 +72,6 @@ JMESSAGE(JERR_BAD_STRUCT_SIZE, JMESSAGE(JERR_BAD_VIRTUAL_ACCESS, "Bogus virtual array access") JMESSAGE(JERR_BUFFER_SIZE, "Buffer passed to JPEG library is too small") JMESSAGE(JERR_CANT_SUSPEND, "Suspension not allowed here") -JMESSAGE(JERR_CANT_TRANSCODE, - "Cannot transcode to/from lossless JPEG datastreams") JMESSAGE(JERR_CCIR601_NOTIMPL, "CCIR601 sampling not implemented yet") JMESSAGE(JERR_COMPONENT_COUNT, "Too many color components: %d, max %d") JMESSAGE(JERR_CONVERSION_NOTIMPL, "Unsupported color conversion request") @@ -106,7 +100,6 @@ JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time") JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported") JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined") JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image") -JMESSAGE(JERR_NO_LOSSLESS_SCRIPT, "Lossless encoding was requested but no scan script was supplied") JMESSAGE(JERR_NO_QUANT_TABLE, "Quantization table 0x%02x was not defined") JMESSAGE(JERR_NO_SOI, "Not a JPEG file: starts with 0x%02x 0x%02x") JMESSAGE(JERR_OUT_OF_MEMORY, "Insufficient memory (case %d)") @@ -176,10 +169,8 @@ JMESSAGE(JTRC_THUMB_PALETTE, "JFIF extension marker: palette thumbnail image, length %u") JMESSAGE(JTRC_THUMB_RGB, "JFIF extension marker: RGB thumbnail image, length %u") -JMESSAGE(JTRC_UNKNOWN_LOSSLESS_IDS, - "Unrecognized component IDs %d %d %d, assuming RGB") -JMESSAGE(JTRC_UNKNOWN_LOSSY_IDS, - "Unrecognized component IDs %d %d %d, assuming YCbCr") +JMESSAGE(JTRC_UNKNOWN_IDS, + "Unrecognized component IDs %d %d %d, assuming YCbCr (lossy) or RGB (lossless)") JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") @@ -191,12 +182,13 @@ JMESSAGE(JWRN_HIT_MARKER, "Corrupt JPEG data: premature end of data segment") JMESSAGE(JWRN_HUFF_BAD_CODE, "Corrupt JPEG data: bad Huffman code") JMESSAGE(JWRN_JFIF_MAJOR, "Warning: unknown JFIF revision number %d.%02d") JMESSAGE(JWRN_JPEG_EOF, "Premature end of JPEG file") -JMESSAGE(JWRN_MUST_DOWNSCALE, - "Must downscale data from %d bits to %d") JMESSAGE(JWRN_MUST_RESYNC, "Corrupt JPEG data: found marker 0x%02x instead of RST%d") JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG") JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") +JMESSAGE(JERR_BAD_RESTART, "Invalid restart interval %d; must be an integer multiple of the number of MCUs in an MCU row (%d)") +JMESSAGE(JWRN_MUST_DOWNSCALE, + "Must downscale data from %d bits to %d") #ifdef JMAKE_ENUM_LIST diff --git a/jlossls.h b/jlossls.h index dfeb70e06..97f9c2f7d 100644 --- a/jlossls.h +++ b/jlossls.h @@ -5,6 +5,7 @@ * Copyright (C) 1998, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This include file contains common declarations for the lossless JPEG @@ -14,6 +15,17 @@ #ifndef JLOSSLS_H #define JLOSSLS_H +#if defined(C_LOSSLESS_SUPPORTED) || defined(D_LOSSLESS_SUPPORTED) + +#define JPEG_INTERNALS +#include "jpeglib.h" + + +#define ALLOC_DARRAY(pool_id, diffsperrow, numrows) \ + (JDIFFARRAY)(*cinfo->mem->alloc_sarray) \ + ((j_common_ptr) cinfo, pool_id, \ + (diffsperrow) * sizeof(JDIFF) / sizeof(JSAMPLE), numrows) + /* * Table H.1: Predictors for lossless coding. @@ -27,125 +39,62 @@ #define PREDICTOR6 (int) ((INT32) Rb + RIGHT_SHIFT((INT32) Ra - (INT32) Rc, 1)) #define PREDICTOR7 (int) RIGHT_SHIFT((INT32) Ra + (INT32) Rb, 1) +#endif + + +#ifdef C_LOSSLESS_SUPPORTED typedef JMETHOD(void, predict_difference_method_ptr, (j_compress_ptr cinfo, int ci, JSAMPROW input_buf, JSAMPROW prev_row, JDIFFROW diff_buf, JDIMENSION width)); -typedef JMETHOD(void, scaler_method_ptr, - (j_compress_ptr cinfo, int ci, - JSAMPROW input_buf, JSAMPROW output_buf, - JDIMENSION width)); - -/* Lossless-specific compression codec (compressor proper) */ +/* Lossless compressor */ typedef struct { - struct jpeg_c_codec pub; /* public fields */ - - - /* Difference buffer control */ - JMETHOD(void, diff_start_pass, (j_compress_ptr cinfo, - J_BUF_MODE pass_mode)); - - /* Pointer to data which is private to diff controller */ - void *diff_private; - - - /* Entropy encoding */ - JMETHOD(JDIMENSION, entropy_encode_mcus, (j_compress_ptr cinfo, - JDIFFIMAGE diff_buf, - JDIMENSION MCU_row_num, - JDIMENSION MCU_col_num, - JDIMENSION nMCU)); - - /* Pointer to data which is private to entropy module */ - void *entropy_private; - - - /* Prediction, differencing */ - JMETHOD(void, predict_start_pass, (j_compress_ptr cinfo)); + struct jpeg_forward_dct pub; /* public fields */ /* It is useful to allow each component to have a separate diff method. */ predict_difference_method_ptr predict_difference[MAX_COMPONENTS]; - /* Pointer to data which is private to predictor module */ - void *pred_private; + /* MCU rows left in the restart interval for each component */ + unsigned int restart_rows_to_go[MAX_COMPONENTS]; /* Sample scaling */ - JMETHOD(void, scaler_start_pass, (j_compress_ptr cinfo)); JMETHOD(void, scaler_scale, (j_compress_ptr cinfo, JSAMPROW input_buf, JSAMPROW output_buf, JDIMENSION width)); +} jpeg_lossless_compressor; - /* Pointer to data which is private to scaler module */ - void *scaler_private; +typedef jpeg_lossless_compressor * lossless_comp_ptr; -} jpeg_lossless_c_codec; +#endif /* C_LOSSLESS_SUPPORTED */ -typedef jpeg_lossless_c_codec * j_lossless_c_ptr; +#ifdef D_LOSSLESS_SUPPORTED typedef JMETHOD(void, predict_undifference_method_ptr, (j_decompress_ptr cinfo, int comp_index, JDIFFROW diff_buf, JDIFFROW prev_row, JDIFFROW undiff_buf, JDIMENSION width)); -/* Lossless-specific decompression codec (decompressor proper) */ +/* Lossless decompressor */ typedef struct { - struct jpeg_d_codec pub; /* public fields */ - - - /* Difference buffer control */ - JMETHOD(void, diff_start_input_pass, (j_decompress_ptr cinfo)); - - /* Pointer to data which is private to diff controller */ - void *diff_private; - - - /* Entropy decoding */ - JMETHOD(void, entropy_start_pass, (j_decompress_ptr cinfo)); - JMETHOD(boolean, entropy_process_restart, (j_decompress_ptr cinfo)); - JMETHOD(JDIMENSION, entropy_decode_mcus, (j_decompress_ptr cinfo, - JDIFFIMAGE diff_buf, - JDIMENSION MCU_row_num, - JDIMENSION MCU_col_num, - JDIMENSION nMCU)); - - /* Pointer to data which is private to entropy module */ - void *entropy_private; - - - /* Prediction, undifferencing */ - JMETHOD(void, predict_start_pass, (j_decompress_ptr cinfo)); - JMETHOD(void, predict_process_restart, (j_decompress_ptr cinfo)); + struct jpeg_inverse_dct pub; /* public fields */ /* It is useful to allow each component to have a separate undiff method. */ predict_undifference_method_ptr predict_undifference[MAX_COMPONENTS]; - /* Pointer to data which is private to predictor module */ - void *pred_private; - /* Sample scaling */ - JMETHOD(void, scaler_start_pass, (j_decompress_ptr cinfo)); JMETHOD(void, scaler_scale, (j_decompress_ptr cinfo, JDIFFROW diff_buf, JSAMPROW output_buf, JDIMENSION width)); - /* Pointer to data which is private to scaler module */ - void *scaler_private; - -} jpeg_lossless_d_codec; + int scale_factor; -typedef jpeg_lossless_d_codec * j_lossless_d_ptr; +} jpeg_lossless_decompressor; +typedef jpeg_lossless_decompressor * lossless_decomp_ptr; -/* Compression module initialization routines */ -EXTERN(void) jinit_lhuff_encoder JPP((j_compress_ptr cinfo)); -EXTERN(void) jinit_differencer JPP((j_compress_ptr cinfo)); -EXTERN(void) jinit_c_scaler JPP((j_compress_ptr cinfo)); -/* Decompression module initialization routines */ -EXTERN(void) jinit_lhuff_decoder JPP((j_decompress_ptr cinfo)); -EXTERN(void) jinit_undifferencer JPP((j_decompress_ptr cinfo)); -EXTERN(void) jinit_d_scaler JPP((j_decompress_ptr cinfo)); +#endif /* D_LOSSLESS_SUPPORTED */ #endif /* JLOSSLS_H */ diff --git a/jlossy.h b/jlossy.h deleted file mode 100644 index 8229ca836..000000000 --- a/jlossy.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * jlossy.h - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. - * For conditions of distribution and use, see the accompanying README file. - * - * This include file contains common declarations for the lossy (DCT-based) - * JPEG codec modules. - */ - -#ifndef JLOSSY_H -#define JLOSSY_H - - -/* Lossy-specific compression codec (compressor proper) */ -typedef struct { - struct jpeg_c_codec pub; /* public fields */ - - - /* Coefficient buffer control */ - JMETHOD(void, coef_start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); - /* JMETHOD(boolean, coef_compress_data, (j_compress_ptr cinfo, - JSAMPIMAGE input_buf));*/ - - /* Pointer to data which is private to coef module */ - void *coef_private; - - - /* Forward DCT (also controls coefficient quantization) */ - JMETHOD(void, fdct_start_pass, (j_compress_ptr cinfo)); - /* perhaps this should be an array??? */ - JMETHOD(void, fdct_forward_DCT, (j_compress_ptr cinfo, - jpeg_component_info * compptr, - JSAMPARRAY sample_data, JBLOCKROW coef_blocks, - JDIMENSION start_row, JDIMENSION start_col, - JDIMENSION num_blocks)); - - /* Pointer to data which is private to fdct module */ - void *fdct_private; - - - /* Entropy encoding */ - JMETHOD(boolean, entropy_encode_mcu, (j_compress_ptr cinfo, - JBLOCKROW *MCU_data)); - - /* Pointer to data which is private to entropy module */ - void *entropy_private; - -} jpeg_lossy_c_codec; - -typedef jpeg_lossy_c_codec * j_lossy_c_ptr; - - - -typedef JMETHOD(void, inverse_DCT_method_ptr, - (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col)); - -/* Lossy-specific decompression codec (decompressor proper) */ -typedef struct { - struct jpeg_d_codec pub; /* public fields */ - - - /* Coefficient buffer control */ - JMETHOD(void, coef_start_input_pass, (j_decompress_ptr cinfo)); - JMETHOD(void, coef_start_output_pass, (j_decompress_ptr cinfo)); - - /* Pointer to array of coefficient virtual arrays, or NULL if none */ - jvirt_barray_ptr *coef_arrays; - - /* Pointer to data which is private to coef module */ - void *coef_private; - - - /* Entropy decoding */ - JMETHOD(void, entropy_start_pass, (j_decompress_ptr cinfo)); - JMETHOD(boolean, entropy_decode_mcu, (j_decompress_ptr cinfo, - JBLOCKROW *MCU_data)); - - /* This is here to share code between baseline and progressive decoders; */ - /* other modules probably should not use it */ - boolean entropy_insufficient_data; /* set TRUE after emitting warning */ - - /* Pointer to data which is private to entropy module */ - void *entropy_private; - - - /* Inverse DCT (also performs dequantization) */ - JMETHOD(void, idct_start_pass, (j_decompress_ptr cinfo)); - - /* It is useful to allow each component to have a separate IDCT method. */ - inverse_DCT_method_ptr inverse_DCT[MAX_COMPONENTS]; - - /* Pointer to data which is private to idct module */ - void *idct_private; - -} jpeg_lossy_d_codec; - -typedef jpeg_lossy_d_codec * j_lossy_d_ptr; - - -/* Compression module initialization routines */ -EXTERN(void) jinit_lossy_c_codec JPP((j_compress_ptr cinfo)); -EXTERN(void) jinit_c_coef_controller JPP((j_compress_ptr cinfo, - boolean need_full_buffer)); -EXTERN(void) jinit_forward_dct JPP((j_compress_ptr cinfo)); -EXTERN(void) jinit_shuff_encoder JPP((j_compress_ptr cinfo)); -EXTERN(void) jinit_phuff_encoder JPP((j_compress_ptr cinfo)); - -/* Decompression module initialization routines */ -EXTERN(void) jinit_lossy_d_codec JPP((j_decompress_ptr cinfo)); -EXTERN(void) jinit_d_coef_controller JPP((j_decompress_ptr cinfo, - boolean need_full_buffer)); -EXTERN(void) jinit_shuff_decoder JPP((j_decompress_ptr cinfo)); -EXTERN(void) jinit_phuff_decoder JPP((j_decompress_ptr cinfo)); -EXTERN(void) jinit_inverse_dct JPP((j_decompress_ptr cinfo)); - -#endif /* JLOSSY_H */ diff --git a/jmemmgr.c b/jmemmgr.c index 78e14a24b..d801b322d 100644 --- a/jmemmgr.c +++ b/jmemmgr.c @@ -1,10 +1,8 @@ /* * jmemmgr.c * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * * This file contains the JPEG system-independent memory management @@ -44,12 +42,11 @@ extern char * getenv JPP((const char * name)); * The allocation routines provided here must never return NULL. * They should exit to error_exit if unsuccessful. * - * It's not a good idea to try to merge the sarray, barray and darray - * routines, even though they are textually almost the same, because - * samples are usually stored as bytes while coefficients and differenced - * 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. + * 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. */ @@ -485,58 +482,6 @@ alloc_barray (j_common_ptr cinfo, int pool_id, } -#ifdef NEED_DARRAY - -/* - * Creation of 2-D difference arrays. - * This is essentially the same as the code for sample arrays, above. - */ - -METHODDEF(JDIFFARRAY) -alloc_darray (j_common_ptr cinfo, int pool_id, - JDIMENSION diffsperrow, JDIMENSION numrows) -/* Allocate a 2-D difference array */ -{ - my_mem_ptr mem = (my_mem_ptr) cinfo->mem; - JDIFFARRAY result; - JDIFFROW workspace; - JDIMENSION rowsperchunk, currow, i; - long ltemp; - - /* Calculate max # of rows allowed in one allocation chunk */ - ltemp = (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) / - ((long) diffsperrow * SIZEOF(JDIFF)); - if (ltemp <= 0) - ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); - if (ltemp < (long) numrows) - rowsperchunk = (JDIMENSION) ltemp; - else - rowsperchunk = numrows; - mem->last_rowsperchunk = rowsperchunk; - - /* Get space for row pointers (small object) */ - result = (JDIFFARRAY) alloc_small(cinfo, pool_id, - (size_t) (numrows * SIZEOF(JDIFFROW))); - - /* Get the rows themselves (large objects) */ - currow = 0; - while (currow < numrows) { - rowsperchunk = MIN(rowsperchunk, numrows - currow); - workspace = (JDIFFROW) alloc_large(cinfo, pool_id, - (size_t) ((size_t) rowsperchunk * (size_t) diffsperrow - * SIZEOF(JDIFF))); - for (i = rowsperchunk; i > 0; i--) { - result[currow++] = workspace; - workspace += diffsperrow; - } - } - - return result; -} - -#endif - - /* * About virtual array management: * @@ -1123,9 +1068,6 @@ jinit_memory_mgr (j_common_ptr cinfo) mem->pub.alloc_large = alloc_large; mem->pub.alloc_sarray = alloc_sarray; mem->pub.alloc_barray = alloc_barray; -#ifdef NEED_DARRAY - mem->pub.alloc_darray = alloc_darray; -#endif mem->pub.request_virt_sarray = request_virt_sarray; mem->pub.request_virt_barray = request_virt_barray; mem->pub.realize_virt_arrays = realize_virt_arrays; diff --git a/jmorecfg.h b/jmorecfg.h index bab851520..66c2b241e 100644 --- a/jmorecfg.h +++ b/jmorecfg.h @@ -2,7 +2,7 @@ * jmorecfg.h * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. * For conditions of distribution and use, see the accompanying README file. @@ -128,13 +128,6 @@ typedef short JSAMPLE; typedef short JCOEF; -/* Representation of a spatial difference value. - * This should be a signed value of at least 16 bits; int is usually OK. - */ - -typedef int JDIFF; - - /* 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 @@ -309,7 +302,7 @@ typedef int boolean; #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 jcshuff.c normally uses entropy optimization to compute + * 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 and lossless JPEG: diff --git a/jpegint.h b/jpegint.h index 97b01a465..9a143ec4e 100644 --- a/jpegint.h +++ b/jpegint.h @@ -2,9 +2,10 @@ * jpegint.h * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file provides common declarations for the various JPEG modules. @@ -13,6 +14,17 @@ */ +/* Representation of a spatial difference value. + * This should be a signed value of at least 16 bits; int is usually OK. + */ + +typedef int JDIFF; + +typedef JDIFF FAR *JDIFFROW; /* pointer to one row of difference values */ +typedef JDIFFROW *JDIFFARRAY; /* ptr to some rows (a 2-D diff array) */ +typedef JDIFFARRAY *JDIFFIMAGE; /* a 3-D diff array: top index is color */ + + /* Declarations for both compression & decompression */ typedef enum { /* Operating modes for buffer controllers */ @@ -52,6 +64,7 @@ struct jpeg_comp_master { /* State variables made visible to other modules */ boolean call_pass_startup; /* True if pass_startup must be called */ boolean is_last_pass; /* True during last pass */ + boolean lossless; /* True if lossless mode is enabled */ }; /* Main buffer control (downsampled-data buffer) */ @@ -74,12 +87,10 @@ struct jpeg_c_prep_controller { JDIMENSION out_row_groups_avail)); }; -/* Compression codec (compressor proper) */ -struct jpeg_c_codec { - JMETHOD(void, entropy_start_pass, (j_compress_ptr cinfo, - boolean gather_statistics)); - JMETHOD(void, entropy_finish_pass, (j_compress_ptr cinfo)); - JMETHOD(boolean, need_optimization_pass, (j_compress_ptr cinfo)); +/* Lossy mode: Coefficient buffer control + * Lossless mode: Difference buffer control + */ +struct jpeg_c_coef_controller { JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); JMETHOD(boolean, compress_data, (j_compress_ptr cinfo, JSAMPIMAGE input_buf)); @@ -104,6 +115,36 @@ struct jpeg_downsampler { boolean need_context_rows; /* TRUE if need rows above & below */ }; +/* Lossy mode: Forward DCT (also controls coefficient quantization) + * Lossless mode: Prediction, sample differencing, and point transform + */ +struct jpeg_forward_dct { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + + /* Lossy mode */ + /* perhaps this should be an array??? */ + JMETHOD(void, forward_DCT, (j_compress_ptr cinfo, + jpeg_component_info * compptr, + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks)); +}; + +/* Entropy encoding */ +struct jpeg_entropy_encoder { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, boolean gather_statistics)); + + /* Lossy mode */ + JMETHOD(boolean, encode_mcu, (j_compress_ptr cinfo, JBLOCKROW *MCU_data)); + /* Lossless mode */ + JMETHOD(JDIMENSION, encode_mcus, (j_compress_ptr cinfo, + JDIFFIMAGE diff_buf, + JDIMENSION MCU_row_num, + JDIMENSION MCU_col_num, JDIMENSION nMCU)); + + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); +}; + /* Marker writing */ struct jpeg_marker_writer { JMETHOD(void, write_file_header, (j_compress_ptr cinfo)); @@ -128,6 +169,7 @@ struct jpeg_decomp_master { /* State variables made visible to other modules */ boolean is_dummy_pass; /* True during 1st pass for 2-pass quant */ + boolean lossless; /* True if decompressing a lossless image */ }; /* Input control module */ @@ -150,14 +192,19 @@ struct jpeg_d_main_controller { JDIMENSION out_rows_avail)); }; -/* Decompression codec (decompressor proper) */ -struct jpeg_d_codec { - JMETHOD(void, calc_output_dimensions, (j_decompress_ptr cinfo)); +/* Lossy mode: Coefficient buffer control + * Lossless mode: Difference buffer control + */ +struct jpeg_d_coef_controller { JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); JMETHOD(int, consume_data, (j_decompress_ptr cinfo)); JMETHOD(void, start_output_pass, (j_decompress_ptr cinfo)); JMETHOD(int, decompress_data, (j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); + + /* Lossy mode */ + /* Pointer to array of coefficient virtual arrays, or NULL if none */ + jvirt_barray_ptr *coef_arrays; }; /* Decompression postprocessing (color quantization buffer control) */ @@ -192,6 +239,42 @@ struct jpeg_marker_reader { unsigned int discarded_bytes; /* # of bytes skipped looking for a marker */ }; +/* Entropy decoding */ +struct jpeg_entropy_decoder { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + + /* Lossy mode */ + JMETHOD(boolean, decode_mcu, (j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); + /* Lossless mode */ + JMETHOD(JDIMENSION, decode_mcus, (j_decompress_ptr cinfo, + JDIFFIMAGE diff_buf, + JDIMENSION MCU_row_num, + JDIMENSION MCU_col_num, JDIMENSION nMCU)); + JMETHOD(boolean, process_restart, (j_decompress_ptr cinfo)); + + /* This is here to share code between baseline and progressive decoders; */ + /* other modules probably should not use it */ + boolean insufficient_data; /* set TRUE after emitting warning */ +}; + +/* Lossy mode: Inverse DCT (also performs dequantization) + * Lossless mode: Prediction, sample undifferencing, point transform, and + * sample size scaling + */ +typedef JMETHOD(void, inverse_DCT_method_ptr, + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col)); + +struct jpeg_inverse_dct { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + + /* Lossy mode */ + /* It is useful to allow each component to have a separate IDCT method. */ + inverse_DCT_method_ptr inverse_DCT[MAX_COMPONENTS]; +}; + /* Upsampling (note that upsampler must also call color converter) */ struct jpeg_upsampler { JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); @@ -258,8 +341,6 @@ struct jpeg_color_quantizer { /* Short forms of external names for systems with brain-damaged linkers. */ #ifdef NEED_SHORT_EXTERNAL_NAMES -#define jinit_c_codec jICCodec -#define jinit_lossy_c_codec jILossyC #define jinit_compress_master jICompress #define jinit_c_master_control jICMaster #define jinit_c_main_controller jICMainC @@ -268,24 +349,17 @@ struct jpeg_color_quantizer { #define jinit_color_converter jICColor #define jinit_downsampler jIDownsampler #define jinit_forward_dct jIFDCT -#define jinit_shuff_encoder jISHEncoder +#define jinit_huff_encoder jIHEncoder #define jinit_phuff_encoder jIPHEncoder #define jinit_marker_writer jIMWriter -#define jinit_d_codec jIDCodec -#define jinit_lossy_d_codec jILossyD -#define jinit_lossless_d_codec jILosslsD #define jinit_master_decompress jIDMaster #define jinit_d_main_controller jIDMainC #define jinit_d_coef_controller jIDCoefC -#define jinit_d_diff_controller jIDDiffC #define jinit_d_post_controller jIDPostC #define jinit_input_controller jIInCtlr #define jinit_marker_reader jIMReader -#define jinit_shuff_decoder jISHDecoder +#define jinit_huff_decoder jIHDecoder #define jinit_phuff_decoder jIPHDecoder -#define jinit_lhuff_decoder jILHDecoder -#define jinit_undifferencer jIUndiff -#define jinit_d_scaler jIDScaler #define jinit_inverse_dct jIIDCT #define jinit_upsampler jIUpsampler #define jinit_color_deconverter jIDColor @@ -305,38 +379,50 @@ struct jpeg_color_quantizer { /* Compression module initialization routines */ EXTERN(void) jinit_compress_master JPP((j_compress_ptr cinfo)); -EXTERN(void) jinit_c_codec JPP((j_compress_ptr cinfo)); -EXTERN(void) jinit_c_diff_controller JPP((j_compress_ptr cinfo, - boolean need_full_buffer)); EXTERN(void) jinit_c_master_control JPP((j_compress_ptr cinfo, boolean transcode_only)); EXTERN(void) jinit_c_main_controller JPP((j_compress_ptr cinfo, boolean need_full_buffer)); EXTERN(void) jinit_c_prep_controller JPP((j_compress_ptr cinfo, boolean need_full_buffer)); -EXTERN(void) jinit_compressor JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_c_coef_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); EXTERN(void) jinit_color_converter JPP((j_compress_ptr cinfo)); EXTERN(void) jinit_downsampler JPP((j_compress_ptr cinfo)); -EXTERN(void) jinit_lossless_c_codec JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_forward_dct JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_huff_encoder JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_phuff_encoder JPP((j_compress_ptr cinfo)); EXTERN(void) jinit_marker_writer JPP((j_compress_ptr cinfo)); +#ifdef C_LOSSLESS_SUPPORTED +EXTERN(void) jinit_c_diff_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_lhuff_encoder JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_lossless_compressor JPP((j_compress_ptr cinfo)); +#endif /* Decompression module initialization routines */ EXTERN(void) jinit_master_decompress JPP((j_decompress_ptr cinfo)); -EXTERN(void) jinit_d_codec JPP((j_decompress_ptr cinfo)); -EXTERN(void) jinit_d_diff_controller JPP((j_decompress_ptr cinfo, - boolean need_full_buffer)); EXTERN(void) jinit_d_main_controller JPP((j_decompress_ptr cinfo, boolean need_full_buffer)); -EXTERN(void) jinit_decompressor JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_d_coef_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); EXTERN(void) jinit_d_post_controller JPP((j_decompress_ptr cinfo, boolean need_full_buffer)); EXTERN(void) jinit_input_controller JPP((j_decompress_ptr cinfo)); -EXTERN(void) jinit_lossless_d_codec JPP((j_decompress_ptr cinfo)); EXTERN(void) jinit_marker_reader JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_huff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_phuff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_inverse_dct JPP((j_decompress_ptr cinfo)); EXTERN(void) jinit_upsampler JPP((j_decompress_ptr cinfo)); EXTERN(void) jinit_color_deconverter JPP((j_decompress_ptr cinfo)); EXTERN(void) jinit_1pass_quantizer JPP((j_decompress_ptr cinfo)); EXTERN(void) jinit_2pass_quantizer JPP((j_decompress_ptr cinfo)); EXTERN(void) jinit_merged_upsampler JPP((j_decompress_ptr cinfo)); +#ifdef D_LOSSLESS_SUPPORTED +EXTERN(void) jinit_d_diff_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_lhuff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_lossless_decompressor JPP((j_decompress_ptr cinfo)); +#endif /* Memory manager initialization */ EXTERN(void) jinit_memory_mgr JPP((j_common_ptr cinfo)); diff --git a/jpeglib.h b/jpeglib.h index ed9563272..0892e5470 100644 --- a/jpeglib.h +++ b/jpeglib.h @@ -5,6 +5,7 @@ * Copyright (C) 1991-1998, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file defines the application interface for the JPEG library. @@ -40,6 +41,13 @@ * if you want to be compatible. */ +/* NOTE: In lossless mode, an MCU contains one or more samples rather than one + * or more 8x8 DCT blocks, so the term "data unit" is used to generically + * describe a sample in lossless mode or an 8x8 DCT block in lossy mode. To + * preserve backward API/ABI compatibility, the field and macro names retain + * the "block" terminology. + */ + #define DCTSIZE 8 /* The basic DCT block is 8x8 samples */ #define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ #define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ @@ -48,16 +56,15 @@ #define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ #define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ /* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; - * the PostScript DCT filter can emit files with many more than 10 data units - * per MCU. - * If you happen to run across such a file, you can up D_MAX_DATA_UNITS_IN_MCU + * 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_DATA_UNITS_IN_MCU; just because Adobe + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe * sometimes emits noncompliant files doesn't mean you should too. */ -#define C_MAX_DATA_UNITS_IN_MCU 10 /* compressor's limit on data units/MCU */ -#ifndef D_MAX_DATA_UNITS_IN_MCU -#define D_MAX_DATA_UNITS_IN_MCU 10 /* decompressor's limit on data units/MCU */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on data units/MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on data units/MCU */ #endif @@ -77,10 +84,6 @@ typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ typedef JCOEF FAR *JCOEFPTR; /* useful in a couple of places */ -typedef JDIFF FAR *JDIFFROW; /* pointer to one row of difference values */ -typedef JDIFFROW *JDIFFARRAY; /* ptr to some rows (a 2-D diff array) */ -typedef JDIFFARRAY *JDIFFIMAGE; /* a 3-D diff array: top index is color */ - /* Types for JPEG compression parameters and working tables. */ @@ -140,25 +143,28 @@ typedef struct { /* These values are computed during compression or decompression startup: */ /* Component's size in data units. - * Any dummy data units added to complete an MCU are not counted; therefore - * these values do not depend on whether a scan is interleaved or not. + * In lossy mode, 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. In lossless mode, these are always equal to the image width and + * height. */ - JDIMENSION width_in_data_units; - JDIMENSION height_in_data_units; - /* Size of a data unit in/output by the codec (in samples). Always - * data_unit for compression. For decompression this is the size of the - * output from one data_unit, reflecting any processing performed by the - * codec. For example, in the DCT-based codec, scaling may be applied - * during the IDCT step. Values of 1,2,4,8 are likely to be supported. - * Note that different components may have different codec_data_unit sizes. + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a data unit in samples. Always DCTSIZE for lossy compression. + * For lossy 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. In lossless mode, this is + * always equal to 1. */ - int codec_data_unit; + int DCT_scaled_size; /* The downsampled dimensions are the component's actual, unpadded number * of samples at the main buffer (preprocessing/compression interface), thus * downsampled_width = ceil(image_width * Hi/Hmax) - * and similarly for height. For decompression, codec-based processing is - * included (ie, IDCT scaling), so - * downsampled_width = ceil(image_width * Hi/Hmax * codec_data_unit/data_unit) + * and similarly for height. For lossy decompression, IDCT scaling is + * included, so + * downsampled_width = ceil(image_width * Hi/Hmax * DCT_scaled_size/DCTSIZE) + * In lossless mode, these are always equal to the image width and height. */ JDIMENSION downsampled_width; /* actual width in samples */ JDIMENSION downsampled_height; /* actual height in samples */ @@ -172,10 +178,10 @@ typedef struct { /* The decompressor output side may not use these variables. */ int MCU_width; /* number of data units per MCU, horizontally */ int MCU_height; /* number of data units per MCU, vertically */ - int MCU_data_units; /* MCU_width * MCU_height */ - int MCU_sample_width; /* MCU width in samples, MCU_width*codec_data_unit */ - int last_col_width; /* # of non-dummy data_units across in last MCU */ - int last_row_height; /* # of non-dummy data_units down in last MCU */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples, MCU_width*DCT_scaled_size */ + int last_col_width; /* # of non-dummy data units across in last MCU */ + int last_row_height; /* # of non-dummy data units down in last MCU */ /* Saved quantization table for component; NULL if none yet saved. * See jdinput.c comments about the need for this information. @@ -194,9 +200,11 @@ typedef struct { int comps_in_scan; /* number of components encoded in this scan */ int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ int Ss, Se; /* progressive JPEG spectral selection parms - lossless JPEG predictor select parm (Ss) */ + (Ss is the predictor selection value in + lossless mode) */ int Ah, Al; /* progressive JPEG successive approx. parms - lossless JPEG point transform parm (Al) */ + (Al is the point transform value in lossless + mode) */ } jpeg_scan_info; /* The decompressor can save APPn and COM markers in a list of these: */ @@ -212,14 +220,6 @@ struct jpeg_marker_struct { /* the marker length word is not counted in data_length or original_length */ }; -/* Known codec processes. */ - -typedef enum { - JPROC_SEQUENTIAL, /* baseline/extended sequential DCT */ - JPROC_PROGRESSIVE, /* progressive DCT */ - JPROC_LOSSLESS /* lossless (sequential) */ -} J_CODEC_PROCESS; - /* Known color spaces. */ typedef enum { @@ -310,8 +310,6 @@ struct jpeg_compress_struct { * helper routines to simplify changing parameters. */ - boolean lossless; /* TRUE=lossless encoding, FALSE=lossy */ - int data_precision; /* bits of precision in image data */ int num_components; /* # of color components in JPEG image */ @@ -381,17 +379,17 @@ struct jpeg_compress_struct { /* * These fields are computed during compression startup */ - int data_unit; /* size of data unit in samples */ - J_CODEC_PROCESS process; /* encoding process of JPEG image */ - + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ int max_h_samp_factor; /* largest h_samp_factor */ int max_v_samp_factor; /* largest v_samp_factor */ - JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to codec */ - /* The codec 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 * data_unit sample rows of each component in an - * "iMCU" (interleaved MCU) row. + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coefficient or + difference controller */ + /* The coefficient or difference controller receives data in units of MCU + * rows as defined for fully interleaved scans (whether the JPEG file is + * interleaved or not). In lossy mode, there are v_samp_factor * DCTSIZE + * sample rows of each component in an "iMCU" (interleaved MCU) row. In + * lossless mode, total_iMCU_rows is always equal to the image height. */ /* @@ -405,12 +403,13 @@ struct jpeg_compress_struct { JDIMENSION MCUs_per_row; /* # of MCUs across the image */ JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ - int data_units_in_MCU; /* # of data units per MCU */ - int MCU_membership[C_MAX_DATA_UNITS_IN_MCU]; + int blocks_in_MCU; /* # of data units per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; /* MCU_membership[i] is index in cur_comp_info of component owning */ - /* i'th block in an MCU */ + /* i'th data unit in an MCU */ - int Ss, Se, Ah, Al; /* progressive/lossless JPEG parameters for scan */ + int Ss, Se, Ah, Al; /* progressive/lossless JPEG parameters for + scan */ /* * Links to compression subobjects (methods and private variables of modules) @@ -418,10 +417,12 @@ struct jpeg_compress_struct { struct jpeg_comp_master * master; struct jpeg_c_main_controller * main; struct jpeg_c_prep_controller * prep; - struct jpeg_c_codec * codec; + struct jpeg_c_coef_controller * coef; struct jpeg_marker_writer * marker; struct jpeg_color_converter * cconvert; struct jpeg_downsampler * downsample; + struct jpeg_forward_dct * fdct; + struct jpeg_entropy_encoder * entropy; jpeg_scan_info * script_space; /* workspace for jpeg_simple_progression */ int script_space_size; }; @@ -556,6 +557,7 @@ struct jpeg_decompress_struct { jpeg_component_info * comp_info; /* comp_info[i] describes component that appears i'th in SOF */ + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ @@ -592,21 +594,19 @@ struct jpeg_decompress_struct { /* * These fields are computed during decompression startup */ - int data_unit; /* size of data unit in samples */ - J_CODEC_PROCESS process; /* decoding process of JPEG image */ - int max_h_samp_factor; /* largest h_samp_factor */ int max_v_samp_factor; /* largest v_samp_factor */ - int min_codec_data_unit; /* smallest codec_data_unit of any component */ + int min_DCT_scaled_size; /* smallest DCT_scaled_size of any component */ JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ - /* The codec'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 data_unit rows of each - * component. Therefore, the codec output contains - * v_samp_factor*codec_data_unit sample rows of a component per iMCU row. + /* The coefficient or difference 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. In lossy mode, 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. In + * lossless mode, total_iMCU_rows is always equal to the image height. */ JSAMPLE * sample_range_limit; /* table for fast range-limiting */ @@ -623,12 +623,13 @@ struct jpeg_decompress_struct { JDIMENSION MCUs_per_row; /* # of MCUs across the image */ JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ - int data_units_in_MCU; /* # of data _units per MCU */ - int MCU_membership[D_MAX_DATA_UNITS_IN_MCU]; + int blocks_in_MCU; /* # of data units per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; /* MCU_membership[i] is index in cur_comp_info of component owning */ /* i'th data unit in an MCU */ - int Ss, Se, Ah, Al; /* progressive/lossless JPEG parms for scan */ + int Ss, Se, Ah, Al; /* progressive/lossless 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 @@ -641,10 +642,12 @@ struct jpeg_decompress_struct { */ struct jpeg_decomp_master * master; struct jpeg_d_main_controller * main; - struct jpeg_d_codec * codec; + struct jpeg_d_coef_controller * coef; struct jpeg_d_post_controller * post; struct jpeg_input_controller * inputctl; struct jpeg_marker_reader * marker; + struct jpeg_entropy_decoder * entropy; + struct jpeg_inverse_dct * idct; struct jpeg_upsampler * upsample; struct jpeg_color_deconverter * cconvert; struct jpeg_color_quantizer * cquantize; @@ -774,14 +777,6 @@ typedef struct jvirt_sarray_control * jvirt_sarray_ptr; typedef struct jvirt_barray_control * jvirt_barray_ptr; -#ifdef C_LOSSLESS_SUPPORTED -#define NEED_DARRAY -#else -#ifdef D_LOSSLESS_SUPPORTED -#define NEED_DARRAY -#endif -#endif - struct jpeg_memory_mgr { /* Method pointers */ JMETHOD(void *, alloc_small, (j_common_ptr cinfo, int pool_id, @@ -794,11 +789,6 @@ struct jpeg_memory_mgr { JMETHOD(JBLOCKARRAY, alloc_barray, (j_common_ptr cinfo, int pool_id, JDIMENSION blocksperrow, JDIMENSION numrows)); -#ifdef NEED_DARRAY - JMETHOD(JDIFFARRAY, alloc_darray, (j_common_ptr cinfo, int pool_id, - JDIMENSION diffsperrow, - JDIMENSION numrows)); -#endif JMETHOD(jvirt_sarray_ptr, request_virt_sarray, (j_common_ptr cinfo, int pool_id, boolean pre_zero, @@ -877,7 +867,6 @@ typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo)); #define jpeg_set_linear_quality jSetLQuality #define jpeg_add_quant_table jAddQuantTable #define jpeg_quality_scaling jQualityScaling -#define jpeg_simple_lossless jSimLossless #define jpeg_simple_progression jSimProgress #define jpeg_suppress_tables jSuppressTables #define jpeg_alloc_quant_table jAlcQTable @@ -961,8 +950,9 @@ EXTERN(void) jpeg_add_quant_table JPP((j_compress_ptr cinfo, int which_tbl, int scale_factor, boolean force_baseline)); EXTERN(int) jpeg_quality_scaling JPP((int quality)); -EXTERN(void) jpeg_simple_lossless JPP((j_compress_ptr cinfo, - int predictor, int point_transform)); +EXTERN(void) jpeg_enable_lossless JPP((j_compress_ptr cinfo, + int predictor_selection_value, + int point_transform)); EXTERN(void) jpeg_simple_progression JPP((j_compress_ptr cinfo)); EXTERN(void) jpeg_suppress_tables JPP((j_compress_ptr cinfo, boolean suppress)); diff --git a/libjpeg.doc b/libjpeg.doc index 2817aafcf..4be2d8339 100644 --- a/libjpeg.doc +++ b/libjpeg.doc @@ -4,6 +4,7 @@ This file was part of the Independent JPEG Group's software: Copyright (C) 1994-1998, Thomas G. Lane. Lossless JPEG Modifications: Copyright (C) 1999, Ken Murchison. +Copyright (C) 2022, D. R. Commander. For conditions of distribution and use, see the accompanying README file. @@ -871,11 +872,27 @@ jpeg_simple_progression (j_compress_ptr cinfo) unless you want to make a custom scan sequence. You must ensure that the JPEG color space is set correctly before calling this routine. -jpeg_simple_lossless (j_compress_ptr cinfo, int predictor, int point_transform) - Generates a default scan script for writing a lossless-JPEG file. - This is the recommended method of creating a lossless file, - unless you want to make a custom scan sequence. You must ensure that - the JPEG color space is set correctly before calling this routine. +jpeg_enable_lossless (j_compress_ptr cinfo, int predictor_selection_value, + int point_transform) + Enables lossless mode with the specified predictor selection value + (1 - 7) and optional point transform (0 - {precision}-1, where + {precision} is the JPEG data precision). A point transform value of 0 + is necessary in order to create a fully lossless JPEG image. (A + non-zero point transform value right-shifts the input samples by the + specified number of bits, which is effectively a form of lossy color + quantization.) Note that the following features will be unavailable + when compressing or decompressing lossless JPEG images: + * Quality/quantization table selection + * DCT/IDCT algorithm selection + * Smoothing + * Downsampling/upsampling + * Color conversion (the JPEG image will use the same color space as + the input image) + * IDCT scaling + * Raw (downsampled) data input/output + * Transcoding of DCT coefficients + Any parameters used to enable or configure those features will be + ignored. Compression parameters (cinfo fields) include: @@ -915,15 +932,15 @@ boolean optimize_coding unsigned int restart_interval int restart_in_rows To emit restart markers in the JPEG file, set one of these nonzero. - Set restart_interval to specify the exact interval in MCU blocks. - Set restart_in_rows to specify the interval in MCU rows. (If - restart_in_rows is not 0, then restart_interval is set after the - image width in MCUs is computed.) Defaults are zero (no restarts). - One restart marker per MCU row is often a good choice. - NOTE: the overhead of restart markers is higher in grayscale JPEG - files than in color files, and MUCH higher in progressive JPEGs. - If you use restarts, you may want to use larger intervals in those - cases. + Set restart_interval to specify the exact interval in MCU blocks + (samples in lossless mode). Set restart_in_rows to specify the + interval in MCU rows. (If restart_in_rows is not 0, then + restart_interval is set after the image width in MCUs is computed.) + Defaults are zero (no restarts). One restart marker per MCU row is + often a good choice. NOTE: the overhead of restart markers is higher + in grayscale JPEG files than in color files, and MUCH higher in + progressive JPEGs. If you use restarts, you may want to use larger + intervals in those cases. const jpeg_scan_info * scan_info int num_scans diff --git a/makefile.cfg b/makefile.cfg index 73fdfb54f..70280bfb1 100644 --- a/makefile.cfg +++ b/makefile.cfg @@ -74,15 +74,14 @@ INSTALL_DATA= @INSTALL_DATA@ # source files: JPEG library proper LIBSOURCES= jcapimin.c jcapistd.c jccoefct.c jccolor.c jcdctmgr.c jcdiffct.c \ - jchuff.c jcinit.c jclhuff.c jclossls.c jclossy.c jcmainct.c \ - jcmarker.c jcmaster.c jcodec.c jcomapi.c jcparam.c jcphuff.c jcpred.c \ - jcprepct.c jcsample.c jcscale.c jcshuff.c jctrans.c jdapimin.c \ - jdapistd.c jdatadst.c jdatasrc.c jdcoefct.c jdcolor.c jddctmgr.c \ - jddiffct.c jdhuff.c jdinput.c jdlhuff.c jdlossls.c jdlossy.c \ - jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdphuff.c jdpostct.c \ - jdpred.c jdsample.c jdscale.c jdshuff.c jdtrans.c jerror.c jfdctflt.c \ - jfdctfst.c jfdctint.c jidctflt.c jidctfst.c jidctint.c jidctred.c \ - jquant1.c jquant2.c jutils.c jmemmgr.c + jchuff.c jcinit.c jclhuff.c jclossls.c jcmainct.c jcmarker.c \ + jcmaster.c jcomapi.c jcparam.c jcphuff.c jcprepct.c jcsample.c \ + jctrans.c jdapimin.c jdapistd.c jdatadst.c jdatasrc.c jdcoefct.c \ + jdcolor.c jddctmgr.c jddiffct.c jdhuff.c jdinput.c jdlhuff.c \ + jdlossls.c jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdphuff.c \ + jdpostct.c jdsample.c jdtrans.c jerror.c jfdctflt.c jfdctfst.c \ + jfdctint.c jidctflt.c jidctfst.c jidctint.c jidctred.c jquant1.c \ + jquant2.c jutils.c jmemmgr.c # memmgr back ends: compile only one of these into a working library SYSDEPSOURCES= jmemansi.c jmemname.c jmemnobs.c jmemdos.c jmemmac.c # source files: cjpeg/djpeg/jpegtran applications, also rdjpgcom/wrjpgcom @@ -91,9 +90,8 @@ APPSOURCES= cjpeg.c djpeg.c jpegtran.c rdjpgcom.c wrjpgcom.c cdjpeg.c \ rdtarga.c wrtarga.c rdbmp.c wrbmp.c rdrle.c wrrle.c SOURCES= $(LIBSOURCES) $(SYSDEPSOURCES) $(APPSOURCES) # files included by source files -INCLUDES= jchuff.h jdhuff.h jdct.h jerror.h jinclude.h jlossls.h jlossy.h \ - jmemsys.h jmorecfg.h jpegint.h jpeglib.h jversion.h cdjpeg.h \ - cderror.h transupp.h +INCLUDES= jchuff.h jdhuff.h jdct.h jerror.h jinclude.h jlossls.h jmemsys.h \ + jmorecfg.h jpegint.h jpeglib.h jversion.h cdjpeg.h cderror.h transupp.h # documentation, test, and support files DOCS= README install.doc usage.doc cjpeg.1 djpeg.1 jpegtran.1 rdjpgcom.1 \ wrjpgcom.1 wizard.doc example.c libjpeg.doc structure.doc \ @@ -113,22 +111,20 @@ TESTFILES= testorig.jpg testimg.ppm testimg.bmp testimg.jpg testprog.jpg \ DISTFILES= $(DOCS) $(MKFILES) $(CONFIGFILES) $(SOURCES) $(INCLUDES) \ $(CONFIGUREFILES) $(OTHERFILES) $(TESTFILES) # library object files common to compression and decompression -COMOBJECTS= jcomapi.$(O) jcodec.$(O) jutils.$(O) jerror.$(O) jmemmgr.$(O) \ - $(SYSDEPMEM) +COMOBJECTS= jcomapi.$(O) jutils.$(O) jerror.$(O) jmemmgr.$(O) $(SYSDEPMEM) # compression library object files CLIBOBJECTS= jcapimin.$(O) jcapistd.$(O) jctrans.$(O) jcparam.$(O) \ jdatadst.$(O) jcinit.$(O) jcmaster.$(O) jcmarker.$(O) jcmainct.$(O) \ - jcprepct.$(O) jclossls.$(O) jclossy.o jccoefct.$(O) jccolor.$(O) \ - jcsample.$(O) jchuff.$(O) jcphuff.$(O) jcshuff.$(O) jclhuff.$(O) \ - jcpred.$(O) jcscale.$(O) jcdiffct.$(O) jcdctmgr.$(O) jfdctfst.$(O) \ - jfdctflt.$(O) jfdctint.$(O) + jcprepct.$(O) jclossls.$(O) jccoefct.$(O) jccolor.$(O) jcsample.$(O) \ + jchuff.$(O) jcphuff.$(O) jclhuff.$(O) jcdiffct.$(O) jcdctmgr.$(O) \ + jfdctfst.$(O) jfdctflt.$(O) jfdctint.$(O) # decompression library object files DLIBOBJECTS= jdapimin.$(O) jdapistd.$(O) jdtrans.$(O) jdatasrc.$(O) \ - jdmaster.$(O) jdinput.$(O) jdmarker.$(O) jdlossls.$(O) jdlossy.$(O) \ - jdhuff.$(O) jdlhuff.$(O) jdphuff.$(O) jdshuff.$(O) jdpred.$(O) \ - jdscale.$(O) jddiffct.$(O) jdmainct.$(O) jdcoefct.$(O) jdpostct.$(O) \ - jddctmgr.$(O) jidctfst.$(O) jidctflt.$(O) jidctint.$(O) jidctred.$(O) \ - jdsample.$(O) jdcolor.$(O) jquant1.$(O) jquant2.$(O) jdmerge.$(O) + jdmaster.$(O) jdinput.$(O) jdmarker.$(O) jdlossls.$(O) jdhuff.$(O) \ + jdlhuff.$(O) jdphuff.$(O) jddiffct.$(O) jdmainct.$(O) jdcoefct.$(O) \ + jdpostct.$(O) jddctmgr.$(O) jidctfst.$(O) jidctflt.$(O) jidctint.$(O) \ + jidctred.$(O) jdsample.$(O) jdcolor.$(O) jquant1.$(O) jquant2.$(O) \ + jdmerge.$(O) # These objectfiles are included in libjpeg.a LIBOBJECTS= $(CLIBOBJECTS) $(DLIBOBJECTS) $(COMOBJECTS) # object files for sample applications (excluding library files) @@ -260,52 +256,44 @@ jconfig.h: jconfig.doc jcapimin.$(O): jcapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jcapistd.$(O): jcapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h -jccoefct.$(O): jccoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h -jcodec.$(O): jcodec.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h jlossls.h +jccoefct.$(O): jccoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jccolor.$(O): jccolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h -jcdctmgr.$(O): jcdctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h jdct.h +jcdctmgr.$(O): jcdctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h jcdiffct.$(O): jcdiffct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossls.h -jchuff.$(O): jchuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h jlossls.h jchuff.h +jchuff.$(O): jchuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h jcinit.$(O): jcinit.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jclhuff.$(O): jclhuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossls.h jchuff.h jclossls.$(O): jclossls.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossls.h -jclossy.$(O): jclossy.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h + jcmainct.$(O): jcmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jcmarker.$(O): jcmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jcmaster.$(O): jcmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jcomapi.$(O): jcomapi.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jcparam.$(O): jcparam.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h -jcphuff.$(O): jcphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h jchuff.h -jcpred.$(O): jcpred.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossls.h +jcphuff.$(O): jcphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h jcprepct.$(O): jcprepct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jcsample.$(O): jcsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h -jcscale.$(O): jcscale.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossls.h -jcshuff.$(O): jcshuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h jlossls.h jchuff.h -jctrans.$(O): jctrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h +jctrans.$(O): jctrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdapimin.$(O): jdapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdapistd.$(O): jdapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdatadst.$(O): jdatadst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h jdatasrc.$(O): jdatasrc.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h -jdcoefct.$(O): jdcoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h +jdcoefct.$(O): jdcoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdcolor.$(O): jdcolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h -jddctmgr.$(O): jddctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h jdct.h +jddctmgr.$(O): jddctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h jddiffct.$(O): jddiffct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossls.h -jdhuff.$(O): jdhuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h jlossls.h jdhuff.h +jdhuff.$(O): jdhuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h jdinput.$(O): jdinput.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdlhuff.$(O): jdlhuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossls.h jdhuff.h jdlossls.$(O): jdlossls.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossls.h -jdlossy.$(O): jdlossy.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h jdmainct.$(O): jdmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdmarker.$(O): jdmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdmaster.$(O): jdmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdmerge.$(O): jdmerge.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h -jdphuff.$(O): jdphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h jdhuff.h +jdphuff.$(O): jdphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h jdpostct.$(O): jdpostct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h -jdpred.$(O): jdpred.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossls.h jdsample.$(O): jdsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h -jdscale.$(O): jdscale.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossls.h -jdshuff.$(O): jdshuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h jdhuff.h -jdtrans.$(O): jdtrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h +jdtrans.$(O): jdtrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jerror.$(O): jerror.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jversion.h jerror.h jfdctflt.$(O): jfdctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h jfdctfst.$(O): jfdctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h diff --git a/rdswitch.c b/rdswitch.c index a60c84066..4f4bb4f58 100644 --- a/rdswitch.c +++ b/rdswitch.c @@ -1,10 +1,8 @@ /* * rdswitch.c * - * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1996, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * * This file contains routines to process some of cjpeg's more complicated @@ -332,29 +330,3 @@ set_sample_factors (j_compress_ptr cinfo, char *arg) } return TRUE; } - - -#ifdef C_LOSSLESS_SUPPORTED - -GLOBAL(boolean) -set_simple_lossless (j_compress_ptr cinfo, char *arg) -{ - int pred, pt = 0; - char ch; - - ch = ','; /* if not set by sscanf, will be ',' */ - if (sscanf(arg, "%d%c", &pred, &ch) < 1) - return FALSE; - if (ch != ',') /* syntax check */ - return FALSE; - while (*arg && *arg++ != ',') /* advance to next segment of arg string */ - ; - if (*arg) { - if (sscanf(arg, "%d", &pt) != 1) - pt = 0; - } - jpeg_simple_lossless(cinfo, pred, pt); - return TRUE; -} - -#endif /* C_LOSSLESS_SUPPORTED */ diff --git a/structure.doc b/structure.doc index 266b3ac92..a127dd3e1 100644 --- a/structure.doc +++ b/structure.doc @@ -4,6 +4,7 @@ This file was part of the Independent JPEG Group's software: Copyright (C) 1991-1995, Thomas G. Lane. Lossless JPEG Modifications: Copyright (C) 1999, Ken Murchison. +Copyright (C) 2022, D. R. Commander. For conditions of distribution and use, see the accompanying README file. @@ -23,7 +24,7 @@ In this document, JPEG-specific terminology follows the JPEG standard: A "sample" is a single component value (i.e., one number in the image data). A "coefficient" is a frequency coefficient (a DCT transform output number). A "block" is an 8x8 group of samples or coefficients. - A "data unit" is an abstract data type which is either a block for lossy + A "data unit" is an abstract data type that is either a block for lossy (DCT-based) codecs or a sample for lossless (predictive) codecs. An "MCU" (minimum coded unit) is an interleaved set of data units of size determined by the sampling factors, or a single data unit in a @@ -47,8 +48,8 @@ command-line user interface and I/O routines for several uncompressed image formats. This document concentrates on the library itself. We desire the library to be capable of supporting all JPEG baseline, extended -sequential, and progressive DCT processes, as well as the lossless (spatial) -process. Hierarchical processes are not supported. +sequential, progressive DCT, and lossless (spatial) processes. Hierarchical +processes are not supported. Within these limits, any set of compression parameters allowed by the JPEG spec should be readable for decompression. (We can be more restrictive about @@ -319,52 +320,31 @@ overall system structuring principle, not as a complete description of the task performed by any one controller. -*** Codec object structure *** - -As noted above, this library supports both the lossy (DCT-based) and lossless -JPEG processes. Because these processes have little in common with one another -(and their implementations share very little code), we need to provide a way to -isloate the underlying JPEG process from the rest of the library. This is -accomplished by introducing an abstract "codec object" which acts a generic -interface to the JPEG (de)compressor proper. - -Using the power of the object-oriented scheme described above, we build the -lossy and lossless modules as two separate implementations of the codec object. -Switching between lossy and lossless processes then becomes as trivial as -assigning the appropriate method pointers during initialization of the library. - - *** Compression object structure *** -Here is a sketch of the logical structure of the JPEG compression library: +Here is a sketch of the logical structure of the JPEG compression library in +lossy mode: |-- Colorspace conversion |-- Preprocessing controller --| | |-- Downsampling - | Main controller --| - | /--> Lossy codec - | / - |-- Compression codec < *OR* - \ - \--> Lossless codec - - -where the lossy codec looks like: + | |-- Forward DCT, quantize + |-- Coefficient controller --| + |-- Entropy encoding - |-- Forward DCT, quantize -<-- Coefficient controller --| - |-- Entropy encoding - - -and the lossless codec looks like: - - |-- Point transformation - | -<-- Difference controller --|-- Prediction, differencing - | - |-- Lossless entropy encoding +... and in lossless mode: + |-- Colorspace conversion + |-- Preprocessing controller --| + | |-- Downsampling +Main controller --| + | |-- Point transform + | | + |-- Difference controller --|-- Prediction, differencing + | + |-- Lossless mode entropy + encoding This sketch also describes the flow of control (subroutine calls) during typical image data processing. Each of the components shown in the diagram is @@ -425,16 +405,15 @@ The objects shown above are: of subsampled data is processed per call, even when the JPEG file is noninterleaved. -* Point transformation: Scale the data down by the point transformation - parameter. +* Point transform: Downscale the data by the point transform value. * Prediction and differencing: Calculate the predictor and subtract it from the input. Works on one scanline per call. The difference - controller supplies the prior scanline which is used for prediction. + controller supplies the prior scanline, which is used for prediction. -* Lossless entropy encoding: Perform Huffman or arithmetic entropy coding and - emit the coded data to the data destination module. This module handles MCU - assembly. Works on one MCU-row per call. +* Lossless mode entropy encoding: Perform Huffman or arithmetic entropy coding + and emit the coded data to the data destination module. This module handles + MCU assembly. Works on one MCU row per call. In addition to the above objects, the compression library includes these objects: @@ -475,36 +454,32 @@ decompression; the progress monitor, if used, may be shared as well. *** Decompression object structure *** -Here is a sketch of the logical structure of the JPEG decompression library: +Here is a sketch of the logical structure of the JPEG decompression library in +lossy mode: - /--> Lossy codec - / - |-- Decompression codec < *OR* - | \ - | \--> Lossless codec + |-- Entropy decoding + |-- Coefficient controller --| + | |-- Dequantize, Inverse DCT Main controller --| - | | |-- Upsampling |-- Postprocessing controller --| |-- Colorspace conversion |-- Color quantization |-- Color precision reduction +... and in lossless mode: -where the lossy codec looks like: - - |-- Entropy decoding -<-- Coefficient controller --| - |-- Dequantize, Inverse DCT - - -and the lossless codec looks like: - - |-- Lossless entropy decoding - | -<-- Difference controller --|-- Prediction, undifferencing - | - |-- Point transformation, sample size scaling - + |-- Lossless mode entropy + | decoding + | + |-- Difference controller --|-- Prediction, undifferencing + | | + | |-- Point transform, sample size + | scaling +Main controller --| + | |-- Upsampling + |-- Postprocessing controller --| |-- Colorspace conversion + |-- Color quantization + |-- Color precision reduction As before, this diagram also represents typical control flow. The objects shown are: @@ -544,17 +519,16 @@ shown are: buffering the full image. The equivalent of one fully interleaved MCU row is processed per call, even when the source JPEG file is noninterleaved. -* Lossless entropy decoding: Read coded data from the data source module and - perform Huffman or arithmetic entropy decoding. Works on one MCU-row per +* Lossless mode entropy decoding: Read coded data from the data source module + and perform Huffman or arithmetic entropy decoding. Works on one MCU row per call. * Prediction and undifferencing: Calculate the predictor and add it to the decoded difference. Works on one scanline per call. The difference - controller supplies the prior scanline which is used for prediction. + controller supplies the prior scanline, which is used for prediction. -* Point transform and sample size scaling: Scale the data up by the point - transformation parameter and scale it down to fit into the compiled-in - sample size. +* Point transform and sample size scaling: Upscale the data by the point + transform value and downscale it to fit into the compiled-in sample size. * Postprocessing controller: buffer controller for the color quantization input buffer, when quantization is in use. (Without quantization, this diff --git a/transupp.c b/transupp.c index 9491548bd..e5ec5642f 100644 --- a/transupp.c +++ b/transupp.c @@ -1,10 +1,8 @@ /* * transupp.c * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1997-1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 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 image transformation routines and other utility code @@ -86,7 +84,7 @@ do_flip_h (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, for (ci = 0; ci < dstinfo->num_components; ci++) { compptr = dstinfo->comp_info + ci; comp_width = MCU_cols * compptr->h_samp_factor; - for (blk_y = 0; blk_y < compptr->height_in_data_units; + for (blk_y = 0; blk_y < compptr->height_in_blocks; blk_y += compptr->v_samp_factor) { buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, src_coef_arrays[ci], blk_y, @@ -138,7 +136,7 @@ do_flip_v (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, for (ci = 0; ci < dstinfo->num_components; ci++) { compptr = dstinfo->comp_info + ci; comp_height = MCU_rows * compptr->v_samp_factor; - for (dst_blk_y = 0; dst_blk_y < compptr->height_in_data_units; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; dst_blk_y += compptr->v_samp_factor) { dst_buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, @@ -160,7 +158,7 @@ do_flip_v (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, /* Row is within the mirrorable area. */ dst_row_ptr = dst_buffer[offset_y]; src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1]; - for (dst_blk_x = 0; dst_blk_x < compptr->width_in_data_units; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { dst_ptr = dst_row_ptr[dst_blk_x]; src_ptr = src_row_ptr[dst_blk_x]; @@ -176,7 +174,7 @@ do_flip_v (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, } else { /* Just copy row verbatim. */ jcopy_block_row(src_buffer[offset_y], dst_buffer[offset_y], - compptr->width_in_data_units); + compptr->width_in_blocks); } } } @@ -203,13 +201,13 @@ do_transpose (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, */ for (ci = 0; ci < dstinfo->num_components; ci++) { compptr = dstinfo->comp_info + ci; - for (dst_blk_y = 0; dst_blk_y < compptr->height_in_data_units; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; dst_blk_y += compptr->v_samp_factor) { dst_buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, (JDIMENSION) compptr->v_samp_factor, TRUE); for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { - for (dst_blk_x = 0; dst_blk_x < compptr->width_in_data_units; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x += compptr->h_samp_factor) { src_buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, @@ -253,13 +251,13 @@ do_rot_90 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, for (ci = 0; ci < dstinfo->num_components; ci++) { compptr = dstinfo->comp_info + ci; comp_width = MCU_cols * compptr->h_samp_factor; - for (dst_blk_y = 0; dst_blk_y < compptr->height_in_data_units; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; dst_blk_y += compptr->v_samp_factor) { dst_buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, (JDIMENSION) compptr->v_samp_factor, TRUE); for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { - for (dst_blk_x = 0; dst_blk_x < compptr->width_in_data_units; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x += compptr->h_samp_factor) { src_buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, @@ -317,13 +315,13 @@ do_rot_270 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, for (ci = 0; ci < dstinfo->num_components; ci++) { compptr = dstinfo->comp_info + ci; comp_height = MCU_rows * compptr->v_samp_factor; - for (dst_blk_y = 0; dst_blk_y < compptr->height_in_data_units; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; dst_blk_y += compptr->v_samp_factor) { dst_buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, (JDIMENSION) compptr->v_samp_factor, TRUE); for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { - for (dst_blk_x = 0; dst_blk_x < compptr->width_in_data_units; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x += compptr->h_samp_factor) { src_buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, @@ -380,7 +378,7 @@ do_rot_180 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, compptr = dstinfo->comp_info + ci; comp_width = MCU_cols * compptr->h_samp_factor; comp_height = MCU_rows * compptr->v_samp_factor; - for (dst_blk_y = 0; dst_blk_y < compptr->height_in_data_units; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; dst_blk_y += compptr->v_samp_factor) { dst_buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, @@ -420,7 +418,7 @@ do_rot_180 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, } } /* Any remaining right-edge blocks are only mirrored vertically. */ - for (; dst_blk_x < compptr->width_in_data_units; dst_blk_x++) { + for (; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { dst_ptr = dst_row_ptr[dst_blk_x]; src_ptr = src_row_ptr[dst_blk_x]; for (i = 0; i < DCTSIZE; i += 2) { @@ -444,7 +442,7 @@ do_rot_180 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, } } /* Any remaining right-edge blocks are only copied. */ - for (; dst_blk_x < compptr->width_in_data_units; dst_blk_x++) { + for (; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { dst_ptr = dst_row_ptr[dst_blk_x]; src_ptr = src_row_ptr[dst_blk_x]; for (i = 0; i < DCTSIZE2; i++) @@ -484,13 +482,13 @@ do_transverse (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, compptr = dstinfo->comp_info + ci; comp_width = MCU_cols * compptr->h_samp_factor; comp_height = MCU_rows * compptr->v_samp_factor; - for (dst_blk_y = 0; dst_blk_y < compptr->height_in_data_units; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; dst_blk_y += compptr->v_samp_factor) { dst_buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, (JDIMENSION) compptr->v_samp_factor, TRUE); for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { - for (dst_blk_x = 0; dst_blk_x < compptr->width_in_data_units; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x += compptr->h_samp_factor) { src_buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, @@ -602,9 +600,9 @@ jtransform_request_workspace (j_decompress_ptr srcinfo, compptr = srcinfo->comp_info + ci; coef_arrays[ci] = (*srcinfo->mem->request_virt_barray) ((j_common_ptr) srcinfo, JPOOL_IMAGE, FALSE, - (JDIMENSION) jround_up((long) compptr->width_in_data_units, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, (long) compptr->h_samp_factor), - (JDIMENSION) jround_up((long) compptr->height_in_data_units, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, (long) compptr->v_samp_factor), (JDIMENSION) compptr->v_samp_factor); } @@ -624,9 +622,9 @@ jtransform_request_workspace (j_decompress_ptr srcinfo, compptr = srcinfo->comp_info + ci; coef_arrays[ci] = (*srcinfo->mem->request_virt_barray) ((j_common_ptr) srcinfo, JPOOL_IMAGE, FALSE, - (JDIMENSION) jround_up((long) compptr->height_in_data_units, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, (long) compptr->v_samp_factor), - (JDIMENSION) jround_up((long) compptr->width_in_data_units, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, (long) compptr->h_samp_factor), (JDIMENSION) compptr->h_samp_factor); } diff --git a/usage.doc b/usage.doc index 8c4970af0..5bc21be91 100644 --- a/usage.doc +++ b/usage.doc @@ -140,6 +140,30 @@ progressive JPEG file at all. Switches for advanced users: + -lossless psv[,Pt] Create a lossless JPEG file using the specified + predictor selection value (1 - 7) and optional point + transform (0 - {precision}-1, where {precision} is the + JPEG data precision in bits). A point transform value + of 0 (the default) is necessary in order to create a + fully lossless JPEG file. (A non-zero point transform + value right-shifts the input samples by the specified + number of bits, which is effectively a form of lossy + color quantization.) CAUTION: lossless JPEG is not yet + widely implemented, so many decoders will be unable to + view a lossless JPEG file at all. Note that the + following features will be unavailable when compressing + or decompressing a lossless JPEG file: + * Quality/quantization table selection + * Color conversion (the JPEG image will use the same + color space as the input image) + * DCT/IDCT algorithm selection + * Smoothing + * Downsampling/upsampling + * IDCT scaling + * Transformations using jpegtran + Any switches used to enable or configure those features + will be ignored. + -dct int Use integer DCT method (default). -dct fast Use fast integer DCT (less accurate). -dct float Use floating-point DCT method. @@ -152,8 +176,9 @@ Switches for advanced users: is much less accurate than the other two. -restart N Emit a JPEG restart marker every N MCU rows, or every - N MCU blocks if "B" is attached to the number. - -restart 0 (the default) means no restart markers. + N MCU blocks (samples in lossless mode) if "B" is + attached to the number. -restart 0 (the default) means + no restart markers. -smooth N Smooth the input image to eliminate dithering noise. N, ranging from 1 to 100, indicates the strength of diff --git a/wizard.doc b/wizard.doc index 54170b227..dc4e8e45f 100644 --- a/wizard.doc +++ b/wizard.doc @@ -115,23 +115,23 @@ Multiple Scan / Progression Control By default, cjpeg emits a single-scan sequential JPEG file. The -progressive switch generates a progressive JPEG file using a default series -of progression parameters. You can create multiple-scan sequential JPEG -files or progressive JPEG files with custom progression parameters by using -the -scans switch: +of progression parameters. You can create multiple-scan sequential or lossless +JPEG files or progressive JPEG files with custom progression parameters by +using the -scans switch: -scans file Use the scan sequence given in the named file. The specified file should be a text file containing a "scan script". The script specifies the contents and ordering of the scans to be emitted. Each entry in the script defines one scan. A scan definition specifies -the components to be included in the scan, and for progressive JPEG it also -specifies the progression parameters Ss,Se,Ah,Al for the scan. Scan -definitions are separated by semicolons (';'). A semicolon after the last -scan definition is optional. +the components to be included in the scan, and for progressive and lossless +JPEG it also specifies the progression/lossless parameters Ss,Se,Ah,Al for the +scan. Scan definitions are separated by semicolons (';'). A semicolon after +the last scan definition is optional. Each scan definition contains one to four component indexes, optionally -followed by a colon (':') and the four progressive-JPEG parameters. The -component indexes denote which color component(s) are to be transmitted in +followed by a colon (':') and the four progressive/lossless-JPEG parameters. +The component indexes denote which color component(s) are to be transmitted in the scan. Components are numbered in the order in which they appear in the JPEG SOF marker, with the first component being numbered 0. (Note that these indexes are not the "component ID" codes assigned to the components, just @@ -142,13 +142,23 @@ The progression parameters for each scan are: Se Zigzag index of last coefficient included in scan Ah Zero for first scan of a coefficient, else Al of prior scan Al Successive approximation low bit position for scan -If the progression parameters are omitted, the values 0,63,0,0 are used, -producing a sequential JPEG file. cjpeg automatically determines whether + +The lossless parameters for each scan are: + Ss Predictor selection value + Se Must be zero + Ah Must be zero + Al Point transform value + +If the progression/lossless parameters are omitted, the values 0,63,0,0 are +used, producing a sequential JPEG file. cjpeg automatically determines whether the script represents a progressive or sequential file, by observing whether -Ss and Se values other than 0 and 63 appear. (The -progressive switch is -not needed to specify this; in fact, it is ignored when -scans appears.) -The scan script must meet the JPEG restrictions on progression sequences. -(cjpeg checks that the spec's requirements are obeyed.) +Ss and Se values other than 0 and 63 appear. cjpeg also automatically +determines whether the script represents a lossless file, by observing whether +Ss (the predictor selection value) is non-zero and Se is zero, which are +illegal values for progressive and sequential files. (The -progressive and +-lossless switches are not needed to specify this; in fact, they are ignored +when -scans appears.) The scan script must meet the JPEG restrictions on +progression sequences. (cjpeg checks that the spec's requirements are obeyed.) Scan script files are free format, in that arbitrary whitespace can appear between numbers and around punctuation. Also, comments can be included: a From 918ace631ed0d2a2312e37b98f88be6f222bbdc3 Mon Sep 17 00:00:00 2001 From: DRC Date: Wed, 16 Nov 2022 10:36:57 -0600 Subject: [PATCH 041/162] example.c: Fix compiler warning --- example.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example.c b/example.c index dedf61d11..837254c21 100644 --- a/example.c +++ b/example.c @@ -394,8 +394,8 @@ do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, char *infilename, /* More stuff */ FILE *infile; /* source file */ FILE *outfile; /* output file */ - JSAMPARRAY buffer; /* Output row buffer */ - J12SAMPARRAY buffer12; /* 12-bit output row buffer */ + JSAMPARRAY buffer = NULL; /* Output row buffer */ + J12SAMPARRAY buffer12 = NULL; /* 12-bit output row buffer */ int col; int row_stride; /* physical row width in output buffer */ From af618ffe093de6563fc61355d4d28f2038ec6e9e Mon Sep 17 00:00:00 2001 From: DRC Date: Tue, 8 Nov 2022 15:01:18 -0600 Subject: [PATCH 042/162] Clean up the lossless JPEG feature - Rename jpeg_simple_lossless() to jpeg_enable_lossless() and modify the function so that it stores the lossless parameters directly in the Ss and Al fields of jpeg_compress_struct rather than using a scan script. - Move the cjpeg -lossless switch into "Switches for advanced users". - Document the libjpeg API and run-time features that are unavailable in lossless mode, and ensure that all parameters, functions, and switches related to unavailable features are ignored or generate errors in lossless mode. - Defer any action that depends on whether lossless mode is enabled until jpeg_start_compress()/jpeg_start_decompress() is called. - Document the purpose of the point transform value. - "Codec" stands for coder/decoder, so it is a bit awkward to say "lossless compression codec" and "lossless decompression codec". Use "lossless compressor" and "lossless decompressor" instead. - Restore backward API/ABI compatibility with libjpeg v6b: * Move the new 'lossless' field from the exposed jpeg_compress_struct and jpeg_decompress_struct structures into the opaque jpeg_comp_master and jpeg_decomp_master structures, and allocate the master structures in the body of jpeg_create_compress() and jpeg_create_decompress(). * Remove the new 'process' field from jpeg_compress_struct and jpeg_decompress_struct and replace it with the old 'progressive_mode' field and the new 'lossless' field. * Remove the new 'data_unit' field from jpeg_compress_struct and jpeg_decompress_struct and replace it with a locally-computed data unit variable. * Restore the names of macros and fields that refer to DCT blocks, and document that they have a different meaning in lossless mode. (Most of them aren't very meaningful in lossless mode anyhow.) * Remove the new alloc_darray() method from jpeg_memory_mgr and replace it with an internal macro that wraps the alloc_sarray() method. * Move the JDIFF* data types from jpeglib.h and jmorecfg.h into jpegint.h. * Remove the new 'codec' field from jpeg_compress_struct and jpeg_decompress_struct and instead reuse the existing internal coefficient control, forward/inverse DCT, and entropy encoding/decoding structures for lossless compression/decompression. * Repurpose existing error codes rather than introducing new ones. (The new JERR_BAD_RESTART and JWRN_MUST_DOWNSCALE codes remain, although JWRN_MUST_DOWNSCALE will probably be removed in libjpeg-turbo, since we have a different way of handling multiple data precisions.) - Automatically enable lossless mode when a scan script with parameters that are only valid for lossless mode is detected, and document the use of scan scripts to generate lossless JPEG images. - Move the sequential and shared Huffman routines back into jchuff.c and jdhuff.c, and document that those routines are shared with jclhuff.c and jdlhuff.c as well as with jcphuff.c and jdphuff.c. - Move MAX_DIFF_BITS from jchuff.h into jclhuff.c, the only place where it is used. - Move the predictor and scaler code into jclossls.c and jdlossls.c. - Streamline register usage in the [un]differencers (inspired by similar optimizations in the color [de]converters.) - Restructure the logic in a few places to reduce duplicated code. - Ensure that all lossless-specific code is guarded by C_LOSSLESS_SUPPORTED or D_LOSSLESS_SUPPORTED and that the library can be built successfully if either or both of those macros is undefined. - Remove all short forms of external names introduced by the lossless JPEG patch. (These will not be needed by libjpeg-turbo, so there is no use cleaning them up.) - Various wordsmithing, formatting, and punctuation tweaks - Eliminate various compiler warnings. --- README | 8 +- TODO | 5 - cdjpeg.h | 5 +- cjpeg.1 | 50 +++- cjpeg.c | 31 ++- filelist.doc | 26 +- jcapimin.c | 13 +- jcapistd.c | 9 +- jccoefct.c | 81 +++--- jccolor.c | 24 +- jcdctmgr.c | 39 ++- jcdiffct.c | 158 ++++++------ jchuff.c | 653 ++++++++++++++++++++++++++++++++++++++++++++++++- jchuff.h | 11 +- jcinit.c | 43 +++- jclhuff.c | 92 ++++--- jclossls.c | 313 +++++++++++++++++++++--- jclossy.c | 78 ------ jcmainct.c | 19 +- jcmarker.c | 20 +- jcmaster.c | 200 +++++++-------- jcmaster.h | 30 +++ jcodec.c | 55 ----- jcparam.c | 173 ++++++------- jcphuff.c | 55 ++--- jcpred.c | 299 ----------------------- jcprepct.c | 12 +- jcsample.c | 22 +- jcscale.c | 64 ----- jcshuff.c | 663 -------------------------------------------------- jctrans.c | 112 ++++----- jdapimin.c | 19 +- jdapistd.c | 11 +- jdcoefct.c | 115 ++++----- jdcolor.c | 15 +- jddctmgr.c | 33 ++- jddiffct.c | 144 +++++------ jdhuff.c | 371 ++++++++++++++++++++++++++-- jdhuff.h | 35 +-- jdinput.c | 134 ++++++---- jdlhuff.c | 67 ++--- jdlossls.c | 317 ++++++++++++++++++++---- jdlossy.c | 230 ----------------- jdmainct.c | 95 ++++---- jdmarker.c | 21 +- jdmaster.c | 179 +++++++++++--- jdmaster.h | 27 ++ jdphuff.c | 53 ++-- jdpred.c | 249 ------------------- jdsample.c | 20 +- jdscale.c | 120 --------- jdshuff.c | 362 --------------------------- jdtrans.c | 36 +-- jerror.h | 30 +-- jlossls.h | 111 +++------ jlossy.h | 122 ---------- jmemmgr.c | 72 +----- jmorecfg.h | 11 +- jpegint.h | 148 ++++++++--- jpeglib.h | 158 ++++++------ libjpeg.doc | 45 ++-- makefile.cfg | 72 +++--- rdswitch.c | 30 +-- structure.doc | 122 ++++------ transupp.c | 44 ++-- usage.doc | 29 ++- wizard.doc | 40 +-- 67 files changed, 3178 insertions(+), 3872 deletions(-) delete mode 100644 jclossy.c create mode 100644 jcmaster.h delete mode 100644 jcodec.c delete mode 100644 jcpred.c delete mode 100644 jcscale.c delete mode 100644 jcshuff.c delete mode 100644 jdlossy.c create mode 100644 jdmaster.h delete mode 100644 jdpred.c delete mode 100644 jdscale.c delete mode 100644 jdshuff.c delete mode 100644 jlossy.h diff --git a/README b/README index 242359479..369f1a221 100644 --- a/README +++ b/README @@ -74,10 +74,10 @@ remarkably high compression levels are possible if you can tolerate a low-quality image. For more details, see the references, or just experiment with various compression settings. -This software implements JPEG baseline, extended-sequential, progressive -and lossless compression processes. Provision is made for supporting all -variants of these processes, although some uncommon parameter settings aren't -implemented yet. For legal reasons, we are not distributing code for the +This software implements JPEG baseline, extended-sequential, progressive, and +lossless compression processes. Provision is made for supporting all variants +of these processes, although some uncommon parameter settings aren't +implemented yet. For legal reasons, we are not distributing code for the arithmetic-coding variants of JPEG; see LEGAL ISSUES. We have made no provision for supporting the hierarchical processes defined in the standard. diff --git a/TODO b/TODO index 9f21066fb..17f6d748c 100644 --- a/TODO +++ b/TODO @@ -6,8 +6,3 @@ List of things to complete for lossless codec: * How to check BITS_PER_JSAMPLE for lossy mode (ie, 16-bit data)? - see jdinput.c. - -* Check comment blocks for errors/changes. - -* Review new filenames. Try to avoid filename conflicts with possible JPEG-LS - codec. diff --git a/cdjpeg.h b/cdjpeg.h index 84db6a061..2b387b6e5 100644 --- a/cdjpeg.h +++ b/cdjpeg.h @@ -1,10 +1,8 @@ /* * cdjpeg.h * - * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1997, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * 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 common declarations for the sample applications @@ -137,7 +135,6 @@ EXTERN(boolean) read_quant_tables JPP((j_compress_ptr cinfo, char * filename, EXTERN(boolean) read_scan_script JPP((j_compress_ptr cinfo, char * filename)); EXTERN(boolean) set_quant_slots JPP((j_compress_ptr cinfo, char *arg)); EXTERN(boolean) set_sample_factors JPP((j_compress_ptr cinfo, char *arg)); -EXTERN(boolean) set_simple_lossless JPP((j_compress_ptr cinfo, char *arg)); /* djpeg support routines (in rdcolmap.c) */ diff --git a/cjpeg.1 b/cjpeg.1 index b6903dea8..38477a010 100644 --- a/cjpeg.1 +++ b/cjpeg.1 @@ -1,4 +1,4 @@ -.TH CJPEG 1 "27 April 1999" +.TH CJPEG 1 "11 November 2022" .SH NAME cjpeg \- compress an image file to a JPEG file .SH SYNOPSIS @@ -62,13 +62,6 @@ decompression are unaffected by .B \-progressive Create progressive JPEG file (see below). .TP -.BI \-lossless " psv[,Pt]" -Create a lossless JPEG file using the specified predictor selection value (1-7) -and optional point transform. -.B Caution: -lossless JPEG is not widely implemented, so many decoders will be -unable to view a lossless JPEG file at all. -.TP .B \-targa Input file is Targa format. Targa files that contain an "identification" field will not be automatically recognized by @@ -130,6 +123,43 @@ unable to view a progressive JPEG file at all. .PP Switches for advanced users: .TP +.BI \-lossless " psv[,Pt]" +Create a lossless JPEG file using the specified predictor selection value +(1 through 7) and optional point transform (0 through +.nh +.I precision +.hy +- 1, where +.nh +.I precision +.hy +is the JPEG data precision in bits). A point transform value of 0 (the +default) is necessary in order to create a fully lossless JPEG file. (A +non-zero point transform value right-shifts the input samples by the specified +number of bits, which is effectively a form of lossy color quantization.) +.B Caution: +lossless JPEG is not yet widely implemented, so many decoders will be unable to +view a lossless JPEG file at all. Note that the following features will be +unavailable when compressing or decompressing a lossless JPEG file: +.IP +- Quality/quantization table selection +.IP +- Color conversion (the JPEG image will use the same color space as the input +image) +.IP +- DCT/IDCT algorithm selection +.IP +- Smoothing +.IP +- Downsampling/upsampling +.IP +- IDCT scaling +.IP +- Transformations using +.B jpegtran +.IP +Any switches used to enable or configure those features will be ignored. +.TP .B \-dct int Use integer DCT method (default). .TP @@ -145,8 +175,8 @@ machines, while the integer methods should give the same results everywhere. The fast integer method is much less accurate than the other two. .TP .BI \-restart " N" -Emit a JPEG restart marker every N MCU rows, or every N MCU blocks if "B" is -attached to the number. +Emit a JPEG restart marker every N MCU rows, or every N MCU blocks (samples in +lossless mode) if "B" is attached to the number. .B \-restart 0 (the default) means no restart markers. .TP diff --git a/cjpeg.c b/cjpeg.c index 3fc9150df..b012567b7 100644 --- a/cjpeg.c +++ b/cjpeg.c @@ -5,6 +5,7 @@ * Copyright (C) 1991-1998, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains a command-line user interface for the JPEG compressor. @@ -159,13 +160,13 @@ usage (void) #ifdef C_PROGRESSIVE_SUPPORTED fprintf(stderr, " -progressive Create progressive JPEG file\n"); #endif -#ifdef C_LOSSLESS_SUPPORTED - fprintf(stderr, " -lossless psv[,Pt] Create lossless JPEG file\n"); -#endif #ifdef TARGA_SUPPORTED fprintf(stderr, " -targa Input file is Targa format (usually not needed)\n"); #endif fprintf(stderr, "Switches for advanced users:\n"); +#ifdef C_LOSSLESS_SUPPORTED + fprintf(stderr, " -lossless psv[,Pt] Create lossless JPEG file\n"); +#endif #ifdef DCT_ISLOW_SUPPORTED fprintf(stderr, " -dct int Use integer DCT method%s\n", (JDCT_DEFAULT == JDCT_ISLOW ? " (default)" : "")); @@ -214,6 +215,9 @@ parse_switches (j_compress_ptr cinfo, int argc, char **argv, { int argn; char * arg; +#ifdef C_LOSSLESS_SUPPORTED + int psv, pt = 0; +#endif int quality; /* -quality parameter */ int q_scale_factor; /* scaling percentage for -qtables */ boolean force_baseline; @@ -222,7 +226,6 @@ parse_switches (j_compress_ptr cinfo, int argc, char **argv, char * qslotsarg = NULL; /* saves -qslots parm if any */ char * samplearg = NULL; /* saves -sample parm if any */ char * scansarg = NULL; /* saves -scans parm if any */ - char * losslsarg = NULL; /* saves -lossless parm if any */ /* Set up default JPEG parameters. */ /* Note that default -quality level need not, and does not, @@ -294,12 +297,20 @@ parse_switches (j_compress_ptr cinfo, int argc, char **argv, jpeg_set_colorspace(cinfo, JCS_GRAYSCALE); } else if (keymatch(arg, "lossless", 1)) { -/* Select simple lossless mode. */ + /* Enable lossless mode. */ #ifdef C_LOSSLESS_SUPPORTED + char ch = ',', *ptr; + if (++argn >= argc) /* advance to next argument */ usage(); - losslsarg = argv[argn]; - /* We must postpone execution until num_components is known. */ + if (sscanf(argv[argn], "%d%c", &psv, &ch) < 1 || ch != ',') + usage(); + ptr = argv[argn]; + while (*ptr && *ptr++ != ',') /* advance to next segment of arg string */ + ; + if (*ptr) + sscanf(ptr, "%d", &pt); + jpeg_enable_lossless(cinfo, psv, pt); #else fprintf(stderr, "%s: sorry, lossless output was not compiled\n", progname); @@ -461,12 +472,6 @@ parse_switches (j_compress_ptr cinfo, int argc, char **argv, jpeg_simple_progression(cinfo); #endif -#ifdef C_LOSSLESS_SUPPORTED - if (losslsarg != NULL) /* process -lossless if it was present */ - if (! set_simple_lossless(cinfo, losslsarg)) - usage(); -#endif - #ifdef C_MULTISCAN_FILES_SUPPORTED if (scansarg != NULL) /* process -scans if it was present */ if (! read_scan_script(cinfo, scansarg)) diff --git a/filelist.doc b/filelist.doc index 322c87598..4dfcca3f5 100644 --- a/filelist.doc +++ b/filelist.doc @@ -1,9 +1,10 @@ IJG JPEG LIBRARY: FILE LIST This file was part of the Independent JPEG Group's software: -Copyright (C) 1994-1997, Thomas G. Lane. +Copyright (C) 1994-1998, Thomas G. Lane. Lossless JPEG Modifications: Copyright (C) 1999, Ken Murchison. +Copyright (C) 2022, D. R. Commander. For conditions of distribution and use, see the accompanying README file. @@ -31,7 +32,6 @@ jinclude.h Central include file used by all IJG .c files to reference system include files. jpegint.h JPEG library's internal data structures. jlossls.h JPEG library's lossless codec data structures. -jlossy.h JPEG library's lossy codec structures. jchuff.h Private declarations for Huffman encoder modules. jdhuff.h Private declarations for Huffman decoder modules. jdct.h Private declarations for forward & reverse DCT subsystems. @@ -68,30 +68,27 @@ Compression side of the library: jcinit.c Initialization: determines which other modules to use. jcmaster.c Master control: setup and inter-pass sequencing logic. jcmainct.c Main buffer controller (preprocessor => JPEG compressor). -jchuff.c Codec-independent Huffman entropy encoding routines. jcprepct.c Preprocessor buffer controller. jccolor.c Color space conversion. jcsample.c Downsampling. +jchuff.c Shared Huffman entropy encoding routines. jcmarker.c JPEG marker writing. jdatadst.c Data destination manager for stdio output. Lossy (DCT) codec: -jlossy.c Lossy compressor proper. jccoefct.c Buffer controller for DCT coefficient buffer. jcdctmgr.c DCT manager (DCT implementation selection & control). jfdctint.c Forward DCT using slow-but-accurate integer method. jfdctfst.c Forward DCT using faster, less accurate integer method. jfdctflt.c Forward DCT using floating-point arithmetic. -jcshuff.c Huffman entropy coding for sequential JPEG. +jchuff.c Huffman entropy coding for sequential JPEG. jcphuff.c Huffman entropy coding for progressive JPEG. Lossless (spatial) codec: -jclossls.c Lossless compressor proper. jcdiffct.c Buffer controller for difference buffer. -jcscale.c Point transformation. -jcpred.c Sample predictor and differencer. +jclossls.c Prediction, sample differencing, and point transform jclhuff.c Huffman entropy encoding for lossless JPEG. Decompression side of the library: @@ -99,9 +96,9 @@ Decompression side of the library: jdmaster.c Master control: determines which other modules to use. jdinput.c Input controller: controls input processing modules. jdmainct.c Main buffer controller (JPEG decompressor => postprocessor). -jdhuff.c Codec-independent Huffman entropy decoding routines. jdpostct.c Postprocessor buffer controller. jdmarker.c JPEG marker reading. +jdhuff.c Shared Huffman entropy decoding routines. jdsample.c Upsampling. jdcolor.c Color space conversion. jdmerge.c Merged upsampling/color conversion (faster, lower quality). @@ -112,9 +109,8 @@ jdatasrc.c Data source manager for stdio input. Lossy (DCT) codec: -jdlossy.c Lossy decompressor proper. jdcoefct.c Buffer controller for DCT coefficient buffer. -jdshuff.c Huffman entropy decoding for sequential JPEG. +jdhuff.c Huffman entropy decoding for sequential JPEG. jdphuff.c Huffman entropy decoding for progressive JPEG. jddctmgr.c IDCT manager (IDCT implementation selection & control). jidctint.c Inverse DCT using slow-but-accurate integer method. @@ -124,17 +120,15 @@ jidctred.c Inverse DCTs with reduced-size outputs. Lossless (spatial) codec: -jdlossls.c Lossless decompressor proper. -jddiffct.c Buffer controller for difference buffers. +jddiffct.c Buffer controller for difference buffer. +jdlossls.c Prediction, sample undifferencing, point transform, and sample + scaling jdlhuff.c Huffman entropy decoding for lossless JPEG. -jdpred.c Sample predictor and undifferencer. -jdscale.c Point transformation, sample size scaling. Support files for both compression and decompression: jerror.c Standard error handling routines (application replaceable). jmemmgr.c System-independent (more or less) memory management code. -jcodec.c Codec-independent utility routines. jutils.c Miscellaneous utility routines. jmemmgr.c relies on a system-dependent memory management module. The IJG diff --git a/jcapimin.c b/jcapimin.c index f30f01417..21147447d 100644 --- a/jcapimin.c +++ b/jcapimin.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1998, Thomas G. Lane. * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains application interface code for the compression half @@ -21,6 +21,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" +#include "jcmaster.h" /* @@ -79,6 +80,14 @@ jpeg_CreateCompress (j_compress_ptr cinfo, int version, size_t structsize) /* OK, I'm ready */ cinfo->global_state = CSTATE_START; + + /* The master struct is used to store extension parameters, so we allocate it + * here. + */ + cinfo->master = (struct jpeg_comp_master *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_comp_master)); + MEMZERO(cinfo->master, SIZEOF(my_comp_master)); } @@ -170,7 +179,7 @@ jpeg_finish_compress (j_compress_ptr cinfo) /* We bypass the main controller and invoke coef controller directly; * all work is being done from the coefficient buffer. */ - if (! (*cinfo->codec->compress_data) (cinfo, (JSAMPIMAGE) NULL)) + if (! (*cinfo->coef->compress_data) (cinfo, (JSAMPIMAGE) NULL)) ERREXIT(cinfo, JERR_CANT_SUSPEND); } (*cinfo->master->finish_pass) (cinfo); diff --git a/jcapistd.c b/jcapistd.c index f258cb237..45b0f12cd 100644 --- a/jcapistd.c +++ b/jcapistd.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains application interface code for the compression half @@ -124,6 +124,9 @@ jpeg_write_raw_data (j_compress_ptr cinfo, JSAMPIMAGE data, { JDIMENSION lines_per_iMCU_row; + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); + if (cinfo->global_state != CSTATE_RAW_OK) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); if (cinfo->next_scanline >= cinfo->image_height) { @@ -147,12 +150,12 @@ jpeg_write_raw_data (j_compress_ptr cinfo, JSAMPIMAGE data, (*cinfo->master->pass_startup) (cinfo); /* Verify that at least one iMCU row has been passed. */ - lines_per_iMCU_row = cinfo->max_v_samp_factor * cinfo->data_unit; + lines_per_iMCU_row = cinfo->max_v_samp_factor * DCTSIZE; if (num_lines < lines_per_iMCU_row) ERREXIT(cinfo, JERR_BUFFER_SIZE); /* Directly compress the row. */ - if (! (*cinfo->codec->compress_data) (cinfo, data)) { + if (! (*cinfo->coef->compress_data) (cinfo, data)) { /* If compressor did not consume the whole row, suspend processing. */ return 0; } diff --git a/jccoefct.c b/jccoefct.c index 65f709ae6..fc6983c08 100644 --- a/jccoefct.c +++ b/jccoefct.c @@ -2,20 +2,19 @@ * jccoefct.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. + * Copyright (C) 1994-1997, Thomas G. Lane. * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains the coefficient buffer controller for compression. - * This controller is the top level of the JPEG compressor proper. + * This controller is the top level of the lossy JPEG compressor proper. * The coefficient buffer lies between forward-DCT and entropy encoding steps. */ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossy.h" /* Private declarations for lossy codec */ /* We use a full-image coefficient buffer when doing Huffman optimization, @@ -35,6 +34,8 @@ /* Private buffer controller object */ typedef struct { + struct jpeg_c_coef_controller pub; /* public fields */ + JDIMENSION iMCU_row_num; /* iMCU row # within image */ JDIMENSION mcu_ctr; /* counts MCUs processed in current row */ int MCU_vert_offset; /* counts MCU rows within iMCU row */ @@ -42,20 +43,20 @@ typedef struct { /* 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_DATA_UNITS_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.) + * workspace of C_MAX_BLOCKS_IN_MCU coefficient blocks, and reuse it for each + * MCU constructed and sent. (On 80x86, the workspace is FAR even though + * it's not really very big; this is to keep the module interfaces unchanged + * when a large coefficient buffer is necessary.) * In multi-pass modes, this array points to the current MCU's blocks * within the virtual arrays. */ - JBLOCKROW MCU_buffer[C_MAX_DATA_UNITS_IN_MCU]; + JBLOCKROW MCU_buffer[C_MAX_BLOCKS_IN_MCU]; /* In multi-pass modes, we need a virtual block array for each component. */ jvirt_barray_ptr whole_image[MAX_COMPONENTS]; -} c_coef_controller; +} my_coef_controller; -typedef c_coef_controller * c_coef_ptr; +typedef my_coef_controller * my_coef_ptr; /* Forward declarations */ @@ -73,8 +74,7 @@ LOCAL(void) start_iMCU_row (j_compress_ptr cinfo) /* Reset within-iMCU-row counters for a new row */ { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; /* In an interleaved scan, an MCU row is the same as an iMCU row. * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. @@ -101,8 +101,7 @@ start_iMCU_row (j_compress_ptr cinfo) METHODDEF(void) start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; coef->iMCU_row_num = 0; start_iMCU_row(cinfo); @@ -111,18 +110,18 @@ start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) case JBUF_PASS_THRU: if (coef->whole_image[0] != NULL) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - lossyc->pub.compress_data = compress_data; + coef->pub.compress_data = compress_data; break; #ifdef FULL_COEF_BUFFER_SUPPORTED case JBUF_SAVE_AND_PASS: if (coef->whole_image[0] == NULL) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - lossyc->pub.compress_data = compress_first_pass; + coef->pub.compress_data = compress_first_pass; break; case JBUF_CRANK_DEST: if (coef->whole_image[0] == NULL) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - lossyc->pub.compress_data = compress_output; + coef->pub.compress_data = compress_output; break; #endif default: @@ -145,8 +144,7 @@ start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) METHODDEF(boolean) compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; JDIMENSION MCU_col_num; /* index of current MCU within row */ JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; @@ -178,10 +176,10 @@ compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) for (yindex = 0; yindex < compptr->MCU_height; yindex++) { if (coef->iMCU_row_num < last_iMCU_row || yoffset+yindex < compptr->last_row_height) { - (*lossyc->fdct_forward_DCT) (cinfo, compptr, - input_buf[compptr->component_index], - coef->MCU_buffer[blkn], - ypos, xpos, (JDIMENSION) blockcnt); + (*cinfo->fdct->forward_DCT) (cinfo, compptr, + input_buf[compptr->component_index], + coef->MCU_buffer[blkn], + ypos, xpos, (JDIMENSION) blockcnt); if (blockcnt < compptr->MCU_width) { /* Create some dummy blocks at the right edge of the image. */ jzero_far((void FAR *) coef->MCU_buffer[blkn + blockcnt], @@ -205,7 +203,7 @@ compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) /* 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 (! (*lossyc->entropy_encode_mcu) (cinfo, coef->MCU_buffer)) { + if (! (*cinfo->entropy->encode_mcu) (cinfo, coef->MCU_buffer)) { /* Suspension forced; update state counters and exit */ coef->MCU_vert_offset = yoffset; coef->mcu_ctr = MCU_col_num; @@ -248,8 +246,7 @@ compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) METHODDEF(boolean) compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; JDIMENSION blocks_across, MCUs_across, MCUindex; int bi, ci, h_samp_factor, block_row, block_rows, ndummy; @@ -270,10 +267,10 @@ compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) block_rows = compptr->v_samp_factor; else { /* NB: can't use last_row_height here, since may not be set! */ - block_rows = (int) (compptr->height_in_data_units % compptr->v_samp_factor); + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); if (block_rows == 0) block_rows = compptr->v_samp_factor; } - blocks_across = compptr->width_in_data_units; + blocks_across = compptr->width_in_blocks; h_samp_factor = compptr->h_samp_factor; /* Count number of dummy blocks to be added at the right margin. */ ndummy = (int) (blocks_across % h_samp_factor); @@ -284,7 +281,7 @@ compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) */ for (block_row = 0; block_row < block_rows; block_row++) { thisblockrow = buffer[block_row]; - (*lossyc->fdct_forward_DCT) (cinfo, compptr, + (*cinfo->fdct->forward_DCT) (cinfo, compptr, input_buf[ci], thisblockrow, (JDIMENSION) (block_row * DCTSIZE), (JDIMENSION) 0, blocks_across); @@ -345,8 +342,7 @@ compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) METHODDEF(boolean) compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; JDIMENSION MCU_col_num; /* index of current MCU within row */ int blkn, ci, xindex, yindex, yoffset; JDIMENSION start_col; @@ -384,7 +380,7 @@ compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) } } /* Try to write the MCU. */ - if (! (*lossyc->entropy_encode_mcu) (cinfo, coef->MCU_buffer)) { + if (! (*cinfo->entropy->encode_mcu) (cinfo, coef->MCU_buffer)) { /* Suspension forced; update state counters and exit */ coef->MCU_vert_offset = yoffset; coef->mcu_ctr = MCU_col_num; @@ -410,14 +406,13 @@ compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) GLOBAL(void) jinit_c_coef_controller (j_compress_ptr cinfo, boolean need_full_buffer) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - c_coef_ptr coef; + my_coef_ptr coef; - coef = (c_coef_ptr) + coef = (my_coef_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(c_coef_controller)); - lossyc->coef_private = (struct jpeg_c_coef_controller *) coef; - lossyc->coef_start_pass = start_pass_coef; + SIZEOF(my_coef_controller)); + cinfo->coef = (struct jpeg_c_coef_controller *) coef; + coef->pub.start_pass = start_pass_coef; /* Create the coefficient buffer. */ if (need_full_buffer) { @@ -431,9 +426,9 @@ jinit_c_coef_controller (j_compress_ptr cinfo, boolean need_full_buffer) ci++, compptr++) { coef->whole_image[ci] = (*cinfo->mem->request_virt_barray) ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, - (JDIMENSION) jround_up((long) compptr->width_in_data_units, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, (long) compptr->h_samp_factor), - (JDIMENSION) jround_up((long) compptr->height_in_data_units, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, (long) compptr->v_samp_factor), (JDIMENSION) compptr->v_samp_factor); } @@ -447,8 +442,8 @@ jinit_c_coef_controller (j_compress_ptr cinfo, boolean need_full_buffer) buffer = (JBLOCKROW) (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, - C_MAX_DATA_UNITS_IN_MCU * SIZEOF(JBLOCK)); - for (i = 0; i < C_MAX_DATA_UNITS_IN_MCU; i++) { + C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + for (i = 0; i < C_MAX_BLOCKS_IN_MCU; i++) { coef->MCU_buffer[i] = buffer + i; } coef->whole_image[0] = NULL; /* flag for no virtual arrays */ diff --git a/jccolor.c b/jccolor.c index 0a8a4b5d1..4d7a3d62d 100644 --- a/jccolor.c +++ b/jccolor.c @@ -1,8 +1,10 @@ /* * jccolor.c * + * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. + * Lossless JPEG Modifications: + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains input colorspace conversion routines. @@ -391,9 +393,15 @@ jinit_color_converter (j_compress_ptr cinfo) break; } - /* Check num_components, set conversion method based on requested space */ + /* Check num_components, set conversion method based on requested space. + * NOTE: We do not allow any lossy color conversion algorithms in lossless + * mode. + */ switch (cinfo->jpeg_color_space) { case JCS_GRAYSCALE: + if (cinfo->master->lossless && + cinfo->in_color_space != cinfo->jpeg_color_space) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); if (cinfo->num_components != 1) ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); if (cinfo->in_color_space == JCS_GRAYSCALE) @@ -408,6 +416,9 @@ jinit_color_converter (j_compress_ptr cinfo) break; case JCS_RGB: + if (cinfo->master->lossless && + cinfo->in_color_space != cinfo->jpeg_color_space) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); if (cinfo->num_components != 3) ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); if (cinfo->in_color_space == JCS_RGB && RGB_PIXELSIZE == 3) @@ -417,6 +428,9 @@ jinit_color_converter (j_compress_ptr cinfo) break; case JCS_YCbCr: + if (cinfo->master->lossless && + cinfo->in_color_space != cinfo->jpeg_color_space) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); if (cinfo->num_components != 3) ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); if (cinfo->in_color_space == JCS_RGB) { @@ -429,6 +443,9 @@ jinit_color_converter (j_compress_ptr cinfo) break; case JCS_CMYK: + if (cinfo->master->lossless && + cinfo->in_color_space != cinfo->jpeg_color_space) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); if (cinfo->num_components != 4) ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); if (cinfo->in_color_space == JCS_CMYK) @@ -438,6 +455,9 @@ jinit_color_converter (j_compress_ptr cinfo) break; case JCS_YCCK: + if (cinfo->master->lossless && + cinfo->in_color_space != cinfo->jpeg_color_space) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); if (cinfo->num_components != 4) ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); if (cinfo->in_color_space == JCS_CMYK) { diff --git a/jcdctmgr.c b/jcdctmgr.c index de4dafda2..61fa79b9e 100644 --- a/jcdctmgr.c +++ b/jcdctmgr.c @@ -1,10 +1,8 @@ /* * jcdctmgr.c * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * 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. @@ -16,13 +14,14 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossy.h" /* Private declarations for lossy codec */ #include "jdct.h" /* Private declarations for DCT subsystem */ /* Private subobject for this module */ typedef struct { + struct jpeg_forward_dct pub; /* public fields */ + /* Pointer to the DCT routine actually in use */ forward_DCT_method_ptr do_dct; @@ -37,9 +36,9 @@ typedef struct { float_DCT_method_ptr do_float_dct; FAST_FLOAT * float_divisors[NUM_QUANT_TBLS]; #endif -} fdct_controller; +} my_fdct_controller; -typedef fdct_controller * fdct_ptr; +typedef my_fdct_controller * my_fdct_ptr; /* @@ -54,8 +53,7 @@ typedef fdct_controller * fdct_ptr; METHODDEF(void) start_pass_fdctmgr (j_compress_ptr cinfo) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - fdct_ptr fdct = (fdct_ptr) lossyc->fdct_private; + my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct; int ci, qtblno, i; jpeg_component_info *compptr; JQUANT_TBL * qtbl; @@ -186,8 +184,7 @@ forward_DCT (j_compress_ptr cinfo, jpeg_component_info * compptr, /* This version is used for integer DCT implementations. */ { /* This routine is heavily used, so it's worth coding it tightly. */ - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - fdct_ptr fdct = (fdct_ptr) lossyc->fdct_private; + my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct; forward_DCT_method_ptr do_dct = fdct->do_dct; DCTELEM * divisors = fdct->divisors[compptr->quant_tbl_no]; DCTELEM workspace[DCTSIZE2]; /* work area for FDCT subroutine */ @@ -277,8 +274,7 @@ forward_DCT_float (j_compress_ptr cinfo, jpeg_component_info * compptr, /* This version is used for floating-point DCT implementations. */ { /* This routine is heavily used, so it's worth coding it tightly. */ - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - fdct_ptr fdct = (fdct_ptr) lossyc->fdct_private; + my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct; float_DCT_method_ptr do_dct = fdct->do_float_dct; FAST_FLOAT * divisors = fdct->float_divisors[compptr->quant_tbl_no]; FAST_FLOAT workspace[DCTSIZE2]; /* work area for FDCT subroutine */ @@ -348,32 +344,31 @@ forward_DCT_float (j_compress_ptr cinfo, jpeg_component_info * compptr, GLOBAL(void) jinit_forward_dct (j_compress_ptr cinfo) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - fdct_ptr fdct; + my_fdct_ptr fdct; int i; - fdct = (fdct_ptr) + fdct = (my_fdct_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(fdct_controller)); - lossyc->fdct_private = (struct jpeg_forward_dct *) fdct; - lossyc->fdct_start_pass = start_pass_fdctmgr; + SIZEOF(my_fdct_controller)); + cinfo->fdct = (struct jpeg_forward_dct *) fdct; + fdct->pub.start_pass = start_pass_fdctmgr; switch (cinfo->dct_method) { #ifdef DCT_ISLOW_SUPPORTED case JDCT_ISLOW: - lossyc->fdct_forward_DCT = forward_DCT; + fdct->pub.forward_DCT = forward_DCT; fdct->do_dct = jpeg_fdct_islow; break; #endif #ifdef DCT_IFAST_SUPPORTED case JDCT_IFAST: - lossyc->fdct_forward_DCT = forward_DCT; + fdct->pub.forward_DCT = forward_DCT; fdct->do_dct = jpeg_fdct_ifast; break; #endif #ifdef DCT_FLOAT_SUPPORTED case JDCT_FLOAT: - lossyc->fdct_forward_DCT = forward_DCT_float; + fdct->pub.forward_DCT = forward_DCT_float; fdct->do_float_dct = jpeg_fdct_float; break; #endif diff --git a/jcdiffct.c b/jcdiffct.c index 6ad2e8dd3..a5c951ebc 100644 --- a/jcdiffct.c +++ b/jcdiffct.c @@ -2,15 +2,16 @@ * jcdiffct.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. + * Copyright (C) 1994-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains the difference buffer controller for compression. * This controller is the top level of the lossless JPEG compressor proper. - * The difference buffer lies between prediction/differencing and entropy - * encoding. + * The difference buffer lies between the prediction/differencing and entropy + * encoding steps. */ #define JPEG_INTERNALS @@ -38,20 +39,22 @@ /* Private buffer controller object */ typedef struct { + struct jpeg_c_coef_controller pub; /* public fields */ + JDIMENSION iMCU_row_num; /* iMCU row # within image */ JDIMENSION mcu_ctr; /* counts MCUs processed in current row */ int MCU_vert_offset; /* counts MCU rows within iMCU row */ int MCU_rows_per_iMCU_row; /* number of such rows needed */ - JSAMPROW cur_row[MAX_COMPONENTS]; /* row of point transformed samples */ + JSAMPROW cur_row[MAX_COMPONENTS]; /* row of point-transformed samples */ JSAMPROW prev_row[MAX_COMPONENTS]; /* previous row of Pt'd samples */ JDIFFARRAY diff_buf[MAX_COMPONENTS]; /* iMCU row of differences */ /* In multi-pass modes, we need a virtual sample array for each component. */ jvirt_sarray_ptr whole_image[MAX_COMPONENTS]; -} c_diff_controller; +} my_diff_controller; -typedef c_diff_controller * c_diff_ptr; +typedef my_diff_controller * my_diff_ptr; /* Forward declarations */ @@ -69,8 +72,7 @@ LOCAL(void) start_iMCU_row (j_compress_ptr cinfo) /* Reset within-iMCU-row counters for a new row */ { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - c_diff_ptr diff = (c_diff_ptr) losslsc->diff_private; + my_diff_ptr diff = (my_diff_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. @@ -97,8 +99,15 @@ start_iMCU_row (j_compress_ptr cinfo) METHODDEF(void) start_pass_diff (j_compress_ptr cinfo, J_BUF_MODE pass_mode) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - c_diff_ptr diff = (c_diff_ptr) losslsc->diff_private; + my_diff_ptr diff = (my_diff_ptr) cinfo->coef; + + /* Because it is hitching a ride on the jpeg_forward_dct struct, + * start_pass_lossless() will be called at the start of the initial pass. + * This ensures that it will be called at the start of the Huffman + * optimization and output passes as well. + */ + if (pass_mode == JBUF_CRANK_DEST) + (*cinfo->fdct->start_pass) (cinfo); diff->iMCU_row_num = 0; start_iMCU_row(cinfo); @@ -107,18 +116,18 @@ start_pass_diff (j_compress_ptr cinfo, J_BUF_MODE pass_mode) case JBUF_PASS_THRU: if (diff->whole_image[0] != NULL) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - losslsc->pub.compress_data = compress_data; + diff->pub.compress_data = compress_data; break; #ifdef FULL_SAMP_BUFFER_SUPPORTED case JBUF_SAVE_AND_PASS: if (diff->whole_image[0] == NULL) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - losslsc->pub.compress_data = compress_first_pass; + diff->pub.compress_data = compress_first_pass; break; case JBUF_CRANK_DEST: if (diff->whole_image[0] == NULL) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - losslsc->pub.compress_data = compress_output; + diff->pub.compress_data = compress_output; break; #endif default: @@ -143,13 +152,12 @@ start_pass_diff (j_compress_ptr cinfo, J_BUF_MODE pass_mode) METHODDEF(boolean) compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - c_diff_ptr diff = (c_diff_ptr) losslsc->diff_private; + my_diff_ptr diff = (my_diff_ptr) cinfo->coef; + lossless_comp_ptr losslessc = (lossless_comp_ptr) cinfo->fdct; JDIMENSION MCU_col_num; /* index of current MCU within row */ JDIMENSION MCU_count; /* number of MCUs encoded */ - JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; - int comp, ci, yoffset, samp_row, samp_rows, samps_across; + int ci, compi, yoffset, samp_row, samp_rows, samps_across; jpeg_component_info *compptr; /* Loop to write as much as one whole iMCU row */ @@ -158,20 +166,20 @@ compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) MCU_col_num = diff->mcu_ctr; - /* Scale and predict each scanline of the MCU-row separately. + /* Scale and predict each scanline of the MCU row separately. * - * Note: We only do this if we are at the start of a MCU-row, ie, + * Note: We only do this if we are at the start of an MCU row, ie, * we don't want to reprocess a row suspended by the output. */ if (MCU_col_num == 0) { - for (comp = 0; comp < cinfo->comps_in_scan; comp++) { - compptr = cinfo->cur_comp_info[comp]; - ci = compptr->component_index; + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + compi = compptr->component_index; if (diff->iMCU_row_num < last_iMCU_row) samp_rows = compptr->v_samp_factor; else { /* NB: can't use last_row_height here, since may not be set! */ - samp_rows = (int) (compptr->height_in_data_units % compptr->v_samp_factor); + samp_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); if (samp_rows == 0) samp_rows = compptr->v_samp_factor; else { /* Fill dummy difference rows at the bottom edge with zeros, which @@ -179,43 +187,39 @@ compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) */ for (samp_row = samp_rows; samp_row < compptr->v_samp_factor; samp_row++) - MEMZERO(diff->diff_buf[ci][samp_row], - jround_up((long) compptr->width_in_data_units, + MEMZERO(diff->diff_buf[compi][samp_row], + jround_up((long) compptr->width_in_blocks, (long) compptr->h_samp_factor) * SIZEOF(JDIFF)); } } - samps_across = compptr->width_in_data_units; + samps_across = compptr->width_in_blocks; for (samp_row = 0; samp_row < samp_rows; samp_row++) { - (*losslsc->scaler_scale) (cinfo, - input_buf[ci][samp_row], - diff->cur_row[ci], samps_across); - (*losslsc->predict_difference[ci]) (cinfo, ci, - diff->cur_row[ci], - diff->prev_row[ci], - diff->diff_buf[ci][samp_row], - samps_across); - SWAP_ROWS(diff->cur_row[ci], diff->prev_row[ci]); + (*losslessc->scaler_scale) (cinfo, + input_buf[compi][samp_row], + diff->cur_row[compi], + samps_across); + (*losslessc->predict_difference[compi]) + (cinfo, compi, diff->cur_row[compi], diff->prev_row[compi], + diff->diff_buf[compi][samp_row], samps_across); + SWAP_ROWS(diff->cur_row[compi], diff->prev_row[compi]); } } } - - /* Try to write the MCU-row (or remaining portion of suspended MCU-row). */ + /* Try to write the MCU row (or remaining portion of suspended MCU row). */ MCU_count = - (*losslsc->entropy_encode_mcus) (cinfo, - diff->diff_buf, yoffset, MCU_col_num, - cinfo->MCUs_per_row - MCU_col_num); + (*cinfo->entropy->encode_mcus) (cinfo, + diff->diff_buf, yoffset, MCU_col_num, + cinfo->MCUs_per_row - MCU_col_num); if (MCU_count != cinfo->MCUs_per_row - MCU_col_num) { /* Suspension forced; update state counters and exit */ diff->MCU_vert_offset = yoffset; diff->mcu_ctr += MCU_col_num; return FALSE; } - /* Completed an MCU row, but perhaps not an iMCU row */ diff->mcu_ctr = 0; } - /* Completed the iMCU row, advance counters for next one */ diff->iMCU_row_num++; start_iMCU_row(cinfo); @@ -246,18 +250,17 @@ compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) METHODDEF(boolean) compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - c_diff_ptr diff = (c_diff_ptr) losslsc->diff_private; + my_diff_ptr diff = (my_diff_ptr) cinfo->coef; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; JDIMENSION samps_across; int ci, samp_row, samp_rows; - JSAMPARRAY buffer[MAX_COMPONENTS]; + JSAMPARRAY buffer; jpeg_component_info *compptr; for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - /* Align the virtual buffers for this component. */ - buffer[ci] = (*cinfo->mem->access_virt_sarray) + /* Align the virtual buffer for this component. */ + buffer = (*cinfo->mem->access_virt_sarray) ((j_common_ptr) cinfo, diff->whole_image[ci], diff->iMCU_row_num * compptr->v_samp_factor, (JDIMENSION) compptr->v_samp_factor, TRUE); @@ -267,21 +270,20 @@ compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) samp_rows = compptr->v_samp_factor; else { /* NB: can't use last_row_height here, since may not be set! */ - samp_rows = (int) (compptr->height_in_data_units % compptr->v_samp_factor); + samp_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); if (samp_rows == 0) samp_rows = compptr->v_samp_factor; } - samps_across = compptr->width_in_data_units; + samps_across = compptr->width_in_blocks; /* Perform point transform scaling and prediction/differencing for all * non-dummy rows in this iMCU row. Each call on these functions - * process a complete row of samples. + * processes a complete row of samples. */ for (samp_row = 0; samp_row < samp_rows; samp_row++) { - MEMCOPY(buffer[ci][samp_row], input_buf[ci][samp_row], + MEMCOPY(buffer[samp_row], input_buf[ci][samp_row], samps_across * SIZEOF(JSAMPLE)); } } - /* NB: compress_output will increment iMCU_row_num if successful. * A suspension return will result in redoing all the work above next time. */ @@ -304,23 +306,19 @@ compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) METHODDEF(boolean) compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - c_diff_ptr diff = (c_diff_ptr) losslsc->diff_private; - JDIMENSION MCU_col_num; /* index of current MCU within row */ - JDIMENSION MCU_count; /* number of MCUs encoded */ - int comp, ci, yoffset; - JSAMPARRAY buffer[MAX_COMPONENTS]; + my_diff_ptr diff = (my_diff_ptr) cinfo->coef; + int ci; + JSAMPARRAY buffer[MAX_COMPS_IN_SCAN]; jpeg_component_info *compptr; /* Align the virtual buffers for the components used in this scan. * NB: during first pass, this is safe only because the buffers will * already be aligned properly, so jmemmgr.c won't need to do any I/O. */ - for (comp = 0; comp < cinfo->comps_in_scan; comp++) { - compptr = cinfo->cur_comp_info[comp]; - ci = compptr->component_index; - buffer[ci] = (*cinfo->mem->access_virt_sarray) - ((j_common_ptr) cinfo, diff->whole_image[ci], + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + buffer[compptr->component_index] = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, diff->whole_image[compptr->component_index], diff->iMCU_row_num * compptr->v_samp_factor, (JDIMENSION) compptr->v_samp_factor, FALSE); } @@ -338,28 +336,27 @@ compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) GLOBAL(void) jinit_c_diff_controller (j_compress_ptr cinfo, boolean need_full_buffer) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - c_diff_ptr diff; + my_diff_ptr diff; int ci, row; jpeg_component_info *compptr; - diff = (c_diff_ptr) + diff = (my_diff_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(c_diff_controller)); - losslsc->diff_private = (void *) diff; - losslsc->diff_start_pass = start_pass_diff; + SIZEOF(my_diff_controller)); + cinfo->coef = (struct jpeg_c_coef_controller *) diff; + diff->pub.start_pass = start_pass_diff; /* Create the prediction row buffers. */ for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { diff->cur_row[ci] = *(*cinfo->mem->alloc_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, - (JDIMENSION) jround_up((long) compptr->width_in_data_units, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, (long) compptr->h_samp_factor), (JDIMENSION) 1); diff->prev_row[ci] = *(*cinfo->mem->alloc_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, - (JDIMENSION) jround_up((long) compptr->width_in_data_units, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, (long) compptr->h_samp_factor), (JDIMENSION) 1); } @@ -367,11 +364,11 @@ jinit_c_diff_controller (j_compress_ptr cinfo, boolean need_full_buffer) /* Create the difference buffer. */ for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - diff->diff_buf[ci] = (*cinfo->mem->alloc_darray) - ((j_common_ptr) cinfo, JPOOL_IMAGE, - (JDIMENSION) jround_up((long) compptr->width_in_data_units, - (long) compptr->h_samp_factor), - (JDIMENSION) compptr->v_samp_factor); + diff->diff_buf[ci] = + ALLOC_DARRAY(JPOOL_IMAGE, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) compptr->v_samp_factor); /* Prefill difference rows with zeros. We do this because only actual * data is placed in the buffers during prediction/differencing, leaving * any dummy differences at the right edge as zeros, which will encode @@ -379,7 +376,7 @@ jinit_c_diff_controller (j_compress_ptr cinfo, boolean need_full_buffer) */ for (row = 0; row < compptr->v_samp_factor; row++) MEMZERO(diff->diff_buf[ci][row], - jround_up((long) compptr->width_in_data_units, + jround_up((long) compptr->width_in_blocks, (long) compptr->h_samp_factor) * SIZEOF(JDIFF)); } @@ -388,16 +385,13 @@ jinit_c_diff_controller (j_compress_ptr cinfo, boolean need_full_buffer) #ifdef FULL_SAMP_BUFFER_SUPPORTED /* Allocate a full-image virtual array for each component, */ /* padded to a multiple of samp_factor differences in each direction. */ - int ci; - jpeg_component_info *compptr; - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { diff->whole_image[ci] = (*cinfo->mem->request_virt_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, - (JDIMENSION) jround_up((long) compptr->width_in_data_units, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, (long) compptr->h_samp_factor), - (JDIMENSION) jround_up((long) compptr->height_in_data_units, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, (long) compptr->v_samp_factor), (JDIMENSION) compptr->v_samp_factor); } diff --git a/jchuff.c b/jchuff.c index 26e1442b2..27e544aa4 100644 --- a/jchuff.c +++ b/jchuff.c @@ -2,13 +2,19 @@ * jchuff.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * - * This file contains Huffman entropy decoding routines which are shared - * by the sequential, progressive and lossless decoders. + * This file contains Huffman entropy encoding routines. + * + * Much of the complexity here has to do with supporting output suspension. + * If the data destination module demands suspension, we want to be able to + * back up to the start of the current MCU. To do this, we copy state + * variables into local working storage, and update them back to the + * permanent JPEG objects only upon successful completion of an MCU. */ #define JPEG_INTERNALS @@ -17,9 +23,159 @@ #include "jchuff.h" /* Declarations shared with jc*huff.c */ +/* Expanded entropy encoder object for Huffman encoding. + * + * The savable_state subrecord contains fields that change within an MCU, + * but must not be updated permanently until we complete the MCU. + */ + +typedef struct { + INT32 put_buffer; /* current bit-accumulation buffer */ + int put_bits; /* # of bits now in it */ + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ +} savable_state; + +/* This macro is to work around compilers with missing or broken + * structure assignment. You'll need to fix this code if you have + * such a compiler and you change MAX_COMPS_IN_SCAN. + */ + +#ifndef NO_STRUCT_ASSIGN +#define ASSIGN_STATE(dest,src) ((dest) = (src)) +#else +#if MAX_COMPS_IN_SCAN == 4 +#define ASSIGN_STATE(dest,src) \ + ((dest).put_buffer = (src).put_buffer, \ + (dest).put_bits = (src).put_bits, \ + (dest).last_dc_val[0] = (src).last_dc_val[0], \ + (dest).last_dc_val[1] = (src).last_dc_val[1], \ + (dest).last_dc_val[2] = (src).last_dc_val[2], \ + (dest).last_dc_val[3] = (src).last_dc_val[3]) +#endif +#endif + + +typedef struct { + struct jpeg_entropy_encoder pub; /* public fields */ + + savable_state saved; /* Bit buffer & DC state at start of MCU */ + + /* These fields are NOT loaded into local working state. */ + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + int next_restart_num; /* next restart number to write (0-7) */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + c_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; + c_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; + +#ifdef ENTROPY_OPT_SUPPORTED /* Statistics tables for optimization */ + long * dc_count_ptrs[NUM_HUFF_TBLS]; + long * ac_count_ptrs[NUM_HUFF_TBLS]; +#endif +} huff_entropy_encoder; + +typedef huff_entropy_encoder * huff_entropy_ptr; + +/* Working state while writing an MCU. + * This struct contains all the fields that are needed by subroutines. + */ + +typedef struct { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + savable_state cur; /* Current bit buffer & DC state */ + j_compress_ptr cinfo; /* dump_buffer needs access to this */ +} working_state; + + +/* Forward declarations */ +METHODDEF(boolean) encode_mcu_huff JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(void) finish_pass_huff JPP((j_compress_ptr cinfo)); +#ifdef ENTROPY_OPT_SUPPORTED +METHODDEF(boolean) encode_mcu_gather JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(void) finish_pass_gather JPP((j_compress_ptr cinfo)); +#endif + + +/* + * Initialize for a Huffman-compressed scan. + * If gather_statistics is TRUE, we do not output anything during the scan, + * just count the Huffman symbols used and generate Huffman code tables. + */ + +METHODDEF(void) +start_pass_huff (j_compress_ptr cinfo, boolean gather_statistics) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci, dctbl, actbl; + jpeg_component_info * compptr; + + if (gather_statistics) { +#ifdef ENTROPY_OPT_SUPPORTED + entropy->pub.encode_mcu = encode_mcu_gather; + entropy->pub.finish_pass = finish_pass_gather; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + entropy->pub.encode_mcu = encode_mcu_huff; + entropy->pub.finish_pass = finish_pass_huff; + } + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + actbl = compptr->ac_tbl_no; + if (gather_statistics) { +#ifdef ENTROPY_OPT_SUPPORTED + /* Check for invalid table indexes */ + /* (make_c_derived_tbl does this in the other path) */ + if (dctbl < 0 || dctbl >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, dctbl); + if (actbl < 0 || actbl >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, actbl); + /* Allocate and zero the statistics tables */ + /* Note that jpeg_gen_optimal_table expects 257 entries in each table! */ + if (entropy->dc_count_ptrs[dctbl] == NULL) + entropy->dc_count_ptrs[dctbl] = (long *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + 257 * SIZEOF(long)); + MEMZERO(entropy->dc_count_ptrs[dctbl], 257 * SIZEOF(long)); + if (entropy->ac_count_ptrs[actbl] == NULL) + entropy->ac_count_ptrs[actbl] = (long *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + 257 * SIZEOF(long)); + MEMZERO(entropy->ac_count_ptrs[actbl], 257 * SIZEOF(long)); +#endif + } else { + /* Compute derived values for Huffman tables */ + /* We may do this more than once for a table, but it's not expensive */ + jpeg_make_c_derived_tbl(cinfo, TRUE, dctbl, + & entropy->dc_derived_tbls[dctbl]); + jpeg_make_c_derived_tbl(cinfo, FALSE, actbl, + & entropy->ac_derived_tbls[actbl]); + } + /* Initialize DC predictions to 0 */ + entropy->saved.last_dc_val[ci] = 0; + } + + /* Initialize bit buffer to empty */ + entropy->saved.put_buffer = 0; + entropy->saved.put_bits = 0; + + /* Initialize restart stuff */ + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num = 0; +} + + /* * Compute the derived values for a Huffman table. * This routine also performs some validation checks on the table. + * + * Note this is also used by jcphuff.c and jclhuff.c. */ GLOBAL(void) @@ -94,12 +250,12 @@ jpeg_make_c_derived_tbl (j_compress_ptr cinfo, boolean isDC, int tblno, */ 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..16 for DC. (We could constrain them further - * based on data depth and mode, but this seems enough.) + /* 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 in + * lossy mode and 0..16 for DC in lossless mode. (We could constrain them + * further based on data depth and mode, but this seems enough.) */ - maxsymbol = isDC ? 16 : 255; + maxsymbol = isDC ? (cinfo->master->lossless ? 16 : 15) : 255; for (p = 0; p < lastp; p++) { i = htbl->huffval[p]; @@ -111,8 +267,418 @@ jpeg_make_c_derived_tbl (j_compress_ptr cinfo, boolean isDC, int tblno, } +/* Outputting bytes to the file */ + +/* Emit a byte, taking 'action' if must suspend. */ +#define emit_byte(state,val,action) \ + { *(state)->next_output_byte++ = (JOCTET) (val); \ + if (--(state)->free_in_buffer == 0) \ + if (! dump_buffer(state)) \ + { action; } } + + +LOCAL(boolean) +dump_buffer (working_state * state) +/* Empty the output buffer; return TRUE if successful, FALSE if must suspend */ +{ + struct jpeg_destination_mgr * dest = state->cinfo->dest; + + if (! (*dest->empty_output_buffer) (state->cinfo)) + return FALSE; + /* After a successful buffer dump, must reset buffer pointers */ + state->next_output_byte = dest->next_output_byte; + state->free_in_buffer = dest->free_in_buffer; + return TRUE; +} + + +/* Outputting bits to the file */ + +/* Only the right 24 bits of put_buffer are used; the valid bits are + * left-justified in this part. At most 16 bits can be passed to emit_bits + * in one call, and we never retain more than 7 bits in put_buffer + * between calls, so 24 bits are sufficient. + */ + +INLINE +LOCAL(boolean) +emit_bits (working_state * state, unsigned int code, int size) +/* Emit some bits; return TRUE if successful, FALSE if must suspend */ +{ + /* This routine is heavily used, so it's worth coding tightly. */ + register INT32 put_buffer = (INT32) code; + register int put_bits = state->cur.put_bits; + + /* if size is 0, caller used an invalid Huffman table entry */ + if (size == 0) + ERREXIT(state->cinfo, JERR_HUFF_MISSING_CODE); + + put_buffer &= (((INT32) 1)<cur.put_buffer; /* and merge with old buffer contents */ + + while (put_bits >= 8) { + int c = (int) ((put_buffer >> 16) & 0xFF); + + emit_byte(state, c, return FALSE); + if (c == 0xFF) { /* need to stuff a zero byte? */ + emit_byte(state, 0, return FALSE); + } + put_buffer <<= 8; + put_bits -= 8; + } + + state->cur.put_buffer = put_buffer; /* update state variables */ + state->cur.put_bits = put_bits; + + return TRUE; +} + + +LOCAL(boolean) +flush_bits (working_state * state) +{ + if (! emit_bits(state, 0x7F, 7)) /* fill any partial byte with ones */ + return FALSE; + state->cur.put_buffer = 0; /* and reset bit-buffer to empty */ + state->cur.put_bits = 0; + return TRUE; +} + + +/* Encode a single block's worth of coefficients */ + +LOCAL(boolean) +encode_one_block (working_state * state, JCOEFPTR block, int last_dc_val, + c_derived_tbl *dctbl, c_derived_tbl *actbl) +{ + register int temp, temp2; + register int nbits; + register int k, r, i; + + /* Encode the DC coefficient difference per section F.1.2.1 */ + + temp = temp2 = block[0] - last_dc_val; + + if (temp < 0) { + temp = -temp; /* temp is abs value of input */ + /* For a negative input, want temp2 = bitwise complement of abs(input) */ + /* This code assumes we are on a two's complement machine */ + temp2--; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 0; + while (temp) { + nbits++; + temp >>= 1; + } + /* Check for out-of-range coefficient values. + * Since we're encoding a difference, the range limit is twice as much. + */ + if (nbits > MAX_COEF_BITS+1) + ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); + + /* Emit the Huffman-coded symbol for the number of bits */ + if (! emit_bits(state, dctbl->ehufco[nbits], dctbl->ehufsi[nbits])) + return FALSE; + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + if (nbits) /* emit_bits rejects calls with size 0 */ + if (! emit_bits(state, (unsigned int) temp2, nbits)) + return FALSE; + + /* Encode the AC coefficients per section F.1.2.2 */ + + r = 0; /* r = run length of zeros */ + + for (k = 1; k < DCTSIZE2; k++) { + if ((temp = block[jpeg_natural_order[k]]) == 0) { + r++; + } else { + /* if run length > 15, must emit special run-length-16 codes (0xF0) */ + while (r > 15) { + if (! emit_bits(state, actbl->ehufco[0xF0], actbl->ehufsi[0xF0])) + return FALSE; + r -= 16; + } + + temp2 = temp; + if (temp < 0) { + temp = -temp; /* temp is abs value of input */ + /* This code assumes we are on a two's complement machine */ + temp2--; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 1; /* there must be at least one 1 bit */ + while ((temp >>= 1)) + nbits++; + /* Check for out-of-range coefficient values */ + if (nbits > MAX_COEF_BITS) + ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); + + /* Emit Huffman symbol for run length / number of bits */ + i = (r << 4) + nbits; + if (! emit_bits(state, actbl->ehufco[i], actbl->ehufsi[i])) + return FALSE; + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + if (! emit_bits(state, (unsigned int) temp2, nbits)) + return FALSE; + + r = 0; + } + } + + /* If the last coef(s) were zero, emit an end-of-block code */ + if (r > 0) + if (! emit_bits(state, actbl->ehufco[0], actbl->ehufsi[0])) + return FALSE; + + return TRUE; +} + + +/* + * Emit a restart marker & resynchronize predictions. + */ + +LOCAL(boolean) +emit_restart (working_state * state, int restart_num) +{ + int ci; + + if (! flush_bits(state)) + return FALSE; + + emit_byte(state, 0xFF, return FALSE); + emit_byte(state, JPEG_RST0 + restart_num, return FALSE); + + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < state->cinfo->comps_in_scan; ci++) + state->cur.last_dc_val[ci] = 0; + + /* The restart counter is not updated until we successfully write the MCU. */ + + return TRUE; +} + + +/* + * Encode and output one MCU's worth of Huffman-compressed coefficients. + */ + +METHODDEF(boolean) +encode_mcu_huff (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + working_state state; + int blkn, ci; + jpeg_component_info * compptr; + + /* Load up working state */ + state.next_output_byte = cinfo->dest->next_output_byte; + state.free_in_buffer = cinfo->dest->free_in_buffer; + ASSIGN_STATE(state.cur, entropy->saved); + state.cinfo = cinfo; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! emit_restart(&state, entropy->next_restart_num)) + return FALSE; + } + + /* Encode the MCU data blocks */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + if (! encode_one_block(&state, + MCU_data[blkn][0], state.cur.last_dc_val[ci], + entropy->dc_derived_tbls[compptr->dc_tbl_no], + entropy->ac_derived_tbls[compptr->ac_tbl_no])) + return FALSE; + /* Update last_dc_val */ + state.cur.last_dc_val[ci] = MCU_data[blkn][0][0]; + } + + /* Completed MCU, so update state */ + cinfo->dest->next_output_byte = state.next_output_byte; + cinfo->dest->free_in_buffer = state.free_in_buffer; + ASSIGN_STATE(entropy->saved, state.cur); + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + + +/* + * Finish up at the end of a Huffman-compressed scan. + */ + +METHODDEF(void) +finish_pass_huff (j_compress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + working_state state; + + /* Load up working state ... flush_bits needs it */ + state.next_output_byte = cinfo->dest->next_output_byte; + state.free_in_buffer = cinfo->dest->free_in_buffer; + ASSIGN_STATE(state.cur, entropy->saved); + state.cinfo = cinfo; + + /* Flush out the last data */ + if (! flush_bits(&state)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + + /* Update state */ + cinfo->dest->next_output_byte = state.next_output_byte; + cinfo->dest->free_in_buffer = state.free_in_buffer; + ASSIGN_STATE(entropy->saved, state.cur); +} + + +/* + * Huffman coding optimization. + * + * We first scan the supplied data and count the number of uses of each symbol + * that is to be Huffman-coded. (This process MUST agree with the code above.) + * Then we build a Huffman coding tree for the observed counts. + * Symbols which are not needed at all for the particular image are not + * assigned any code, which saves space in the DHT marker as well as in + * the compressed data. + */ + +#ifdef ENTROPY_OPT_SUPPORTED + + +/* Process a single block's worth of coefficients */ + +LOCAL(void) +htest_one_block (j_compress_ptr cinfo, JCOEFPTR block, int last_dc_val, + long dc_counts[], long ac_counts[]) +{ + register int temp; + register int nbits; + register int k, r; + + /* Encode the DC coefficient difference per section F.1.2.1 */ + + temp = block[0] - last_dc_val; + if (temp < 0) + temp = -temp; + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 0; + while (temp) { + nbits++; + temp >>= 1; + } + /* Check for out-of-range coefficient values. + * Since we're encoding a difference, the range limit is twice as much. + */ + if (nbits > MAX_COEF_BITS+1) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count the Huffman symbol for the number of bits */ + dc_counts[nbits]++; + + /* Encode the AC coefficients per section F.1.2.2 */ + + r = 0; /* r = run length of zeros */ + + for (k = 1; k < DCTSIZE2; k++) { + if ((temp = block[jpeg_natural_order[k]]) == 0) { + r++; + } else { + /* if run length > 15, must emit special run-length-16 codes (0xF0) */ + while (r > 15) { + ac_counts[0xF0]++; + r -= 16; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + if (temp < 0) + temp = -temp; + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 1; /* there must be at least one 1 bit */ + while ((temp >>= 1)) + nbits++; + /* Check for out-of-range coefficient values */ + if (nbits > MAX_COEF_BITS) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count Huffman symbol for run length / number of bits */ + ac_counts[(r << 4) + nbits]++; + + r = 0; + } + } + + /* If the last coef(s) were zero, emit an end-of-block code */ + if (r > 0) + ac_counts[0]++; +} + + +/* + * Trial-encode one MCU's worth of Huffman-compressed coefficients. + * No data is actually output, so no suspension return is possible. + */ + +METHODDEF(boolean) +encode_mcu_gather (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int blkn, ci; + jpeg_component_info * compptr; + + /* Take care of restart intervals if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) + entropy->saved.last_dc_val[ci] = 0; + /* Update restart state */ + entropy->restarts_to_go = cinfo->restart_interval; + } + entropy->restarts_to_go--; + } + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + htest_one_block(cinfo, MCU_data[blkn][0], entropy->saved.last_dc_val[ci], + entropy->dc_count_ptrs[compptr->dc_tbl_no], + entropy->ac_count_ptrs[compptr->ac_tbl_no]); + entropy->saved.last_dc_val[ci] = MCU_data[blkn][0][0]; + } + + return TRUE; +} + + /* * Generate the best Huffman code table for the given counts, fill htbl. + * Note this is also used by jcphuff.c and jclhuff.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 @@ -273,3 +839,74 @@ jpeg_gen_optimal_table (j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[]) /* Set sent_table FALSE so updated table will be written to JPEG file. */ htbl->sent_table = FALSE; } + + +/* + * Finish up a statistics-gathering pass and create the new Huffman tables. + */ + +METHODDEF(void) +finish_pass_gather (j_compress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci, dctbl, actbl; + jpeg_component_info * compptr; + JHUFF_TBL **htblptr; + boolean did_dc[NUM_HUFF_TBLS]; + boolean did_ac[NUM_HUFF_TBLS]; + + /* It's important not to apply jpeg_gen_optimal_table more than once + * per table, because it clobbers the input frequency counts! + */ + MEMZERO(did_dc, SIZEOF(did_dc)); + MEMZERO(did_ac, SIZEOF(did_ac)); + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + actbl = compptr->ac_tbl_no; + if (! did_dc[dctbl]) { + htblptr = & cinfo->dc_huff_tbl_ptrs[dctbl]; + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + jpeg_gen_optimal_table(cinfo, *htblptr, entropy->dc_count_ptrs[dctbl]); + did_dc[dctbl] = TRUE; + } + if (! did_ac[actbl]) { + htblptr = & cinfo->ac_huff_tbl_ptrs[actbl]; + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + jpeg_gen_optimal_table(cinfo, *htblptr, entropy->ac_count_ptrs[actbl]); + did_ac[actbl] = TRUE; + } + } +} + + +#endif /* ENTROPY_OPT_SUPPORTED */ + + +/* + * Module initialization routine for Huffman entropy encoding. + */ + +GLOBAL(void) +jinit_huff_encoder (j_compress_ptr cinfo) +{ + huff_entropy_ptr entropy; + int i; + + entropy = (huff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(huff_entropy_encoder)); + cinfo->entropy = (struct jpeg_entropy_encoder *) entropy; + entropy->pub.start_pass = start_pass_huff; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL; +#ifdef ENTROPY_OPT_SUPPORTED + entropy->dc_count_ptrs[i] = entropy->ac_count_ptrs[i] = NULL; +#endif + } +} diff --git a/jchuff.h b/jchuff.h index b175ca349..a9599fc1e 100644 --- a/jchuff.h +++ b/jchuff.h @@ -1,10 +1,8 @@ /* * jchuff.h * - * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * * This file contains declarations for Huffman entropy encoding routines @@ -24,13 +22,6 @@ #define MAX_COEF_BITS 14 #endif -/* The legal range of a spatial difference is - * -32767 .. +32768. - * Hence the magnitude should always fit in 16 bits. - */ - -#define MAX_DIFF_BITS 16 - /* Derived data constructed for each Huffman table */ typedef struct { diff --git a/jcinit.c b/jcinit.c index e46c03dab..008512d21 100644 --- a/jcinit.c +++ b/jcinit.c @@ -5,6 +5,7 @@ * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains initialization logic for the JPEG compressor. @@ -34,9 +35,6 @@ jinit_compress_master (j_compress_ptr cinfo) /* Initialize master control (includes parameter checking/processing) */ jinit_c_master_control(cinfo, FALSE /* full compression */); - /* Initialize compression codec */ - jinit_c_codec(cinfo); - /* Preprocessing */ if (! cinfo->raw_data_in) { jinit_color_converter(cinfo); @@ -44,6 +42,45 @@ jinit_compress_master (j_compress_ptr cinfo) jinit_c_prep_controller(cinfo, FALSE /* never need full buffer here */); } + if (cinfo->master->lossless) { +#ifdef C_LOSSLESS_SUPPORTED + /* Prediction, sample differencing, and point transform */ + jinit_lossless_compressor(cinfo); + /* Entropy encoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + jinit_lhuff_encoder(cinfo); + } + + /* Need a full-image difference buffer in any multi-pass mode. */ + jinit_c_diff_controller(cinfo, + (boolean) (cinfo->num_scans > 1 || cinfo->optimize_coding)); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + /* Forward DCT */ + jinit_forward_dct(cinfo); + /* Entropy encoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef C_PROGRESSIVE_SUPPORTED + jinit_phuff_encoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_encoder(cinfo); + } + + /* Need a full-image coefficient buffer in any multi-pass mode. */ + jinit_c_coef_controller(cinfo, + (boolean) (cinfo->num_scans > 1 || cinfo->optimize_coding)); + } + jinit_c_main_controller(cinfo, FALSE /* never need full buffer here */); jinit_marker_writer(cinfo); diff --git a/jclhuff.c b/jclhuff.c index 7764f5b75..a83943c17 100644 --- a/jclhuff.c +++ b/jclhuff.c @@ -2,9 +2,10 @@ * jclhuff.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains Huffman entropy encoding routines for lossless JPEG. @@ -23,7 +24,17 @@ #include "jchuff.h" /* Declarations shared with jc*huff.c */ -/* Expanded entropy encoder object for Huffman encoding. +#ifdef C_LOSSLESS_SUPPORTED + +/* The legal range of a spatial difference is + * -32767 .. +32768. + * Hence the magnitude should always fit in 16 bits. + */ + +#define MAX_DIFF_BITS 16 + + +/* Expanded entropy encoder object for Huffman encoding in lossless mode. * * The savable_state subrecord contains fields that change within an MCU, * but must not be updated permanently until we complete the MCU. @@ -54,6 +65,8 @@ typedef struct { typedef struct { + struct jpeg_entropy_encoder pub; /* public fields */ + savable_state saved; /* Bit buffer at start of MCU */ /* These fields are NOT loaded into local working state. */ @@ -64,19 +77,19 @@ typedef struct { c_derived_tbl * derived_tbls[NUM_HUFF_TBLS]; /* Pointers to derived tables to be used for each data unit within an MCU */ - c_derived_tbl * cur_tbls[C_MAX_DATA_UNITS_IN_MCU]; + c_derived_tbl * cur_tbls[C_MAX_BLOCKS_IN_MCU]; #ifdef ENTROPY_OPT_SUPPORTED /* Statistics tables for optimization */ long * count_ptrs[NUM_HUFF_TBLS]; /* Pointers to stats tables to be used for each data unit within an MCU */ - long * cur_counts[C_MAX_DATA_UNITS_IN_MCU]; + long * cur_counts[C_MAX_BLOCKS_IN_MCU]; #endif /* Pointers to the proper input difference row for each group of data units * within an MCU. For each component, there are Vi groups of Hi data units. */ - JDIFFROW input_ptr[C_MAX_DATA_UNITS_IN_MCU]; + JDIFFROW input_ptr[C_MAX_BLOCKS_IN_MCU]; /* Number of input pointers in use for the current MCU. This is the sum * of all Vi in the MCU. @@ -86,10 +99,10 @@ typedef struct { /* Information used for positioning the input pointers within the input * difference rows. */ - lhe_input_ptr_info input_ptr_info[C_MAX_DATA_UNITS_IN_MCU]; + lhe_input_ptr_info input_ptr_info[C_MAX_BLOCKS_IN_MCU]; /* Index of the proper input pointer for each data unit within an MCU */ - int input_ptr_index[C_MAX_DATA_UNITS_IN_MCU]; + int input_ptr_index[C_MAX_BLOCKS_IN_MCU]; } lhuff_entropy_encoder; @@ -131,23 +144,22 @@ METHODDEF(void) finish_pass_gather JPP((j_compress_ptr cinfo)); */ METHODDEF(void) -start_pass_huff (j_compress_ptr cinfo, boolean gather_statistics) +start_pass_lhuff (j_compress_ptr cinfo, boolean gather_statistics) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) losslsc->entropy_private; + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) cinfo->entropy; int ci, dctbl, sampn, ptrn, yoffset, xoffset; jpeg_component_info * compptr; if (gather_statistics) { #ifdef ENTROPY_OPT_SUPPORTED - losslsc->entropy_encode_mcus = encode_mcus_gather; - losslsc->pub.entropy_finish_pass = finish_pass_gather; + entropy->pub.encode_mcus = encode_mcus_gather; + entropy->pub.finish_pass = finish_pass_gather; #else ERREXIT(cinfo, JERR_NOT_COMPILED); #endif } else { - losslsc->entropy_encode_mcus = encode_mcus_huff; - losslsc->pub.entropy_finish_pass = finish_pass_huff; + entropy->pub.encode_mcus = encode_mcus_huff; + entropy->pub.finish_pass = finish_pass_huff; } for (ci = 0; ci < cinfo->comps_in_scan; ci++) { @@ -176,11 +188,9 @@ start_pass_huff (j_compress_ptr cinfo, boolean gather_statistics) } /* Precalculate encoding info for each sample in an MCU of this scan */ - for (sampn = 0, ptrn = 0; sampn < cinfo->data_units_in_MCU;) { + for (sampn = 0, ptrn = 0; sampn < cinfo->blocks_in_MCU;) { compptr = cinfo->cur_comp_info[cinfo->MCU_membership[sampn]]; ci = compptr->component_index; - /* ci = cinfo->MCU_membership[sampn]; - compptr = cinfo->cur_comp_info[ci];*/ for (yoffset = 0; yoffset < compptr->MCU_height; yoffset++, ptrn++) { /* Precalculate the setup info for each input pointer */ entropy->input_ptr_info[ptrn].ci = ci; @@ -297,8 +307,6 @@ flush_bits (working_state * state) LOCAL(boolean) emit_restart (working_state * state, int restart_num) { - int ci; - if (! flush_bits(state)) return FALSE; @@ -312,7 +320,7 @@ emit_restart (working_state * state, int restart_num) /* - * Encode and output one nMCU's worth of Huffman-compressed differences. + * Encode and output nMCU MCUs' worth of Huffman-compressed differences. */ METHODDEF(JDIMENSION) @@ -320,11 +328,9 @@ encode_mcus_huff (j_compress_ptr cinfo, JDIFFIMAGE diff_buf, JDIMENSION MCU_row_num, JDIMENSION MCU_col_num, JDIMENSION nMCU) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) losslsc->entropy_private; + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) cinfo->entropy; working_state state; int mcu_num, sampn, ci, yoffset, MCU_width, ptrn; - jpeg_component_info * compptr; /* Load up working state */ state.next_output_byte = cinfo->dest->next_output_byte; @@ -351,8 +357,8 @@ encode_mcus_huff (j_compress_ptr cinfo, JDIFFIMAGE diff_buf, for (mcu_num = 0; mcu_num < nMCU; mcu_num++) { /* Inner loop handles the samples in the MCU */ - for (sampn = 0; sampn < cinfo->data_units_in_MCU; sampn++) { - register int temp, temp2, temp3; + for (sampn = 0; sampn < cinfo->blocks_in_MCU; sampn++) { + register int temp, temp2; register int nbits; c_derived_tbl *dctbl = entropy->cur_tbls[sampn]; @@ -380,7 +386,7 @@ encode_mcus_huff (j_compress_ptr cinfo, JDIFFIMAGE diff_buf, /* Check for out-of-range difference values. */ if (nbits > MAX_DIFF_BITS) - ERREXIT(cinfo, JERR_BAD_DIFF); + ERREXIT(cinfo, JERR_BAD_DCT_COEF); /* Emit the Huffman-coded symbol for the number of bits */ if (! emit_bits(&state, dctbl->ehufco[nbits], dctbl->ehufsi[nbits])) @@ -422,8 +428,7 @@ encode_mcus_huff (j_compress_ptr cinfo, JDIFFIMAGE diff_buf, METHODDEF(void) finish_pass_huff (j_compress_ptr cinfo) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) losslsc->entropy_private; + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) cinfo->entropy; working_state state; /* Load up working state ... flush_bits needs it */ @@ -457,7 +462,7 @@ finish_pass_huff (j_compress_ptr cinfo) #ifdef ENTROPY_OPT_SUPPORTED /* - * Trial-encode one nMCU's worth of Huffman-compressed differences. + * Trial-encode nMCU MCUs' worth of Huffman-compressed differences. * No data is actually output, so no suspension return is possible. */ @@ -466,10 +471,8 @@ encode_mcus_gather (j_compress_ptr cinfo, JDIFFIMAGE diff_buf, JDIMENSION MCU_row_num, JDIMENSION MCU_col_num, JDIMENSION nMCU) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) losslsc->entropy_private; + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) cinfo->entropy; int mcu_num, sampn, ci, yoffset, MCU_width, ptrn; - jpeg_component_info * compptr; /* Take care of restart intervals if needed */ if (cinfo->restart_interval) { @@ -492,10 +495,9 @@ encode_mcus_gather (j_compress_ptr cinfo, JDIFFIMAGE diff_buf, for (mcu_num = 0; mcu_num < nMCU; mcu_num++) { /* Inner loop handles the samples in the MCU */ - for (sampn = 0; sampn < cinfo->data_units_in_MCU; sampn++) { + for (sampn = 0; sampn < cinfo->blocks_in_MCU; sampn++) { register int temp; register int nbits; - c_derived_tbl *dctbl = entropy->cur_tbls[sampn]; long * counts = entropy->cur_counts[sampn]; /* Encode the difference per section H.1.2.2 */ @@ -519,7 +521,7 @@ encode_mcus_gather (j_compress_ptr cinfo, JDIFFIMAGE diff_buf, /* Check for out-of-range difference values. */ if (nbits > MAX_DIFF_BITS) - ERREXIT(cinfo, JERR_BAD_DIFF); + ERREXIT(cinfo, JERR_BAD_DCT_COEF); /* Count the Huffman symbol for the number of bits */ counts[nbits]++; @@ -537,8 +539,7 @@ encode_mcus_gather (j_compress_ptr cinfo, JDIFFIMAGE diff_buf, METHODDEF(void) finish_pass_gather (j_compress_ptr cinfo) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) losslsc->entropy_private; + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) cinfo->entropy; int ci, dctbl; jpeg_component_info * compptr; JHUFF_TBL **htblptr; @@ -566,30 +567,21 @@ finish_pass_gather (j_compress_ptr cinfo) #endif /* ENTROPY_OPT_SUPPORTED */ -METHODDEF(boolean) -need_optimization_pass (j_compress_ptr cinfo) -{ - return TRUE; -} - - /* - * Module initialization routine for Huffman entropy encoding. + * Module initialization routine for lossless mode Huffman entropy encoding. */ GLOBAL(void) jinit_lhuff_encoder (j_compress_ptr cinfo) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; lhuff_entropy_ptr entropy; int i; entropy = (lhuff_entropy_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, SIZEOF(lhuff_entropy_encoder)); - losslsc->entropy_private = (struct jpeg_entropy_encoder *) entropy; - losslsc->pub.entropy_start_pass = start_pass_huff; - losslsc->pub.need_optimization_pass = need_optimization_pass; + cinfo->entropy = (struct jpeg_entropy_encoder *) entropy; + entropy->pub.start_pass = start_pass_lhuff; /* Mark tables unallocated */ for (i = 0; i < NUM_HUFF_TBLS; i++) { @@ -599,3 +591,5 @@ jinit_lhuff_encoder (j_compress_ptr cinfo) #endif } } + +#endif /* C_LOSSLESS_SUPPORTED */ diff --git a/jclossls.c b/jclossls.c index 1cd2b3d7e..6e4a4a804 100644 --- a/jclossls.c +++ b/jclossls.c @@ -5,9 +5,11 @@ * Copyright (C) 1998, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * - * This file contains the control logic for the lossless JPEG compressor. + * This file contains prediction, sample differencing, and point transform + * routines for the lossless JPEG compressor. */ #define JPEG_INTERNALS @@ -15,66 +17,301 @@ #include "jpeglib.h" #include "jlossls.h" - #ifdef C_LOSSLESS_SUPPORTED + +/************************** Sample differencing **************************/ + /* - * Initialize for a processing pass. + * In order to avoid a performance penalty for checking which predictor is + * being used and which row is being processed for each call of the + * undifferencer, and to promote optimization, we have separate differencing + * functions for each predictor selection value. + * + * We are able to avoid duplicating source code by implementing the predictors + * and differencers as macros. Each of the differencing functions is simply a + * wrapper around a DIFFERENCE macro with the appropriate PREDICTOR macro + * passed as an argument. + */ + +/* Forward declarations */ +LOCAL(void) reset_predictor JPP((j_compress_ptr cinfo, int ci)); + + +/* Predictor for the first column of the first row: 2^(P-Pt-1) */ +#define INITIAL_PREDICTORx (1 << (cinfo->data_precision - cinfo->Al - 1)) + +/* Predictor for the first column of the remaining rows: Rb */ +#define INITIAL_PREDICTOR2 GETJSAMPLE(prev_row[0]) + + +/* + * 1-Dimensional differencer routine. + * + * This macro implements the 1-D horizontal predictor (1). INITIAL_PREDICTOR + * is used as the special case predictor for the first column, which must be + * either INITIAL_PREDICTOR2 or INITIAL_PREDICTORx. The remaining samples + * use PREDICTOR1. + */ + +#define DIFFERENCE_1D(INITIAL_PREDICTOR) \ + lossless_comp_ptr losslessc = (lossless_comp_ptr) cinfo->fdct; \ + boolean restart = FALSE; \ + int samp, Ra; \ + \ + samp = GETJSAMPLE(*input_buf++); \ + *diff_buf++ = samp - INITIAL_PREDICTOR; \ + \ + while (--width) { \ + Ra = samp; \ + samp = GETJSAMPLE(*input_buf++); \ + *diff_buf++ = samp - PREDICTOR1; \ + } \ + \ + /* Account for restart interval (no-op if not using restarts) */ \ + if (cinfo->restart_interval) { \ + if (--(losslessc->restart_rows_to_go[ci]) == 0) { \ + reset_predictor(cinfo, ci); \ + restart = TRUE; \ + } \ + } + + +/* + * 2-Dimensional differencer routine. + * + * This macro implements the 2-D horizontal predictors (#2-7). PREDICTOR2 is + * used as the special case predictor for the first column. The remaining + * samples use PREDICTOR, which is a function of Ra, Rb, and Rc. + * + * Because prev_row and output_buf may point to the same storage area (in an + * interleaved image with Vi=1, for example), we must take care to buffer Rb/Rc + * before writing the current reconstructed sample value into output_buf. + */ + +#define DIFFERENCE_2D(PREDICTOR) \ + lossless_comp_ptr losslessc = (lossless_comp_ptr) cinfo->fdct; \ + int samp, Ra, Rb, Rc; \ + \ + Rb = GETJSAMPLE(*prev_row++); \ + samp = GETJSAMPLE(*input_buf++); \ + *diff_buf++ = samp - PREDICTOR2; \ + \ + while (--width) { \ + Rc = Rb; \ + Rb = GETJSAMPLE(*prev_row++); \ + Ra = samp; \ + samp = GETJSAMPLE(*input_buf++); \ + *diff_buf++ = samp - PREDICTOR; \ + } \ + \ + /* Account for restart interval (no-op if not using restarts) */ \ + if (cinfo->restart_interval) { \ + if (--losslessc->restart_rows_to_go[ci] == 0) \ + reset_predictor(cinfo, ci); \ + } + + +/* + * Differencers for the second and subsequent rows in a scan or restart + * interval. The first sample in the row is differenced using the vertical + * predictor (2). The rest of the samples are differenced using the predictor + * specified in the scan header. */ METHODDEF(void) -start_pass (j_compress_ptr cinfo, J_BUF_MODE pass_mode) +jpeg_difference1(j_compress_ptr cinfo, int ci, + JSAMPROW input_buf, JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_1D(INITIAL_PREDICTOR2); + (void)(restart); +} + +METHODDEF(void) +jpeg_difference2(j_compress_ptr cinfo, int ci, + JSAMPROW input_buf, JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR2); + (void)(Ra); + (void)(Rc); +} + +METHODDEF(void) +jpeg_difference3(j_compress_ptr cinfo, int ci, + JSAMPROW input_buf, JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR3); + (void)(Ra); +} + +METHODDEF(void) +jpeg_difference4(j_compress_ptr cinfo, int ci, + JSAMPROW input_buf, JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR4); +} + +METHODDEF(void) +jpeg_difference5(j_compress_ptr cinfo, int ci, + JSAMPROW input_buf, JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR5); +} + +METHODDEF(void) +jpeg_difference6(j_compress_ptr cinfo, int ci, + JSAMPROW input_buf, JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) { - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; + DIFFERENCE_2D(PREDICTOR6); +} - (*losslsc->scaler_start_pass) (cinfo); - (*losslsc->predict_start_pass) (cinfo); - (*losslsc->diff_start_pass) (cinfo, pass_mode); +METHODDEF(void) +jpeg_difference7(j_compress_ptr cinfo, int ci, + JSAMPROW input_buf, JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR7); + (void)(Rc); } /* - * Initialize the lossless compression codec. - * This is called only once, during master selection. + * Differencer for the first row in a scan or restart interval. The first + * sample in the row is differenced using the special predictor constant + * x=2^(P-Pt-1). The rest of the samples are differenced using the + * 1-D horizontal predictor (1). */ -GLOBAL(void) -jinit_lossless_c_codec(j_compress_ptr cinfo) +METHODDEF(void) +jpeg_difference_first_row(j_compress_ptr cinfo, int ci, + JSAMPROW input_buf, JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) { - j_lossless_c_ptr losslsc; + DIFFERENCE_1D(INITIAL_PREDICTORx); - /* Create subobject in permanent pool */ - losslsc = (j_lossless_c_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, - SIZEOF(jpeg_lossless_c_codec)); - cinfo->codec = (struct jpeg_c_codec *) losslsc; + /* + * Now that we have differenced the first row, we want to use the + * differencer that corresponds to the predictor specified in the + * scan header. + * + * Note that we don't do this if we have just reset the predictor + * for a new restart interval. + */ + if (! restart) { + switch (cinfo->Ss) { + case 1: + losslessc->predict_difference[ci] = jpeg_difference1; + break; + case 2: + losslessc->predict_difference[ci] = jpeg_difference2; + break; + case 3: + losslessc->predict_difference[ci] = jpeg_difference3; + break; + case 4: + losslessc->predict_difference[ci] = jpeg_difference4; + break; + case 5: + losslessc->predict_difference[ci] = jpeg_difference5; + break; + case 6: + losslessc->predict_difference[ci] = jpeg_difference6; + break; + case 7: + losslessc->predict_difference[ci] = jpeg_difference7; + break; + } + } +} + +/* + * Reset predictor at the start of a pass or restart interval. + */ - /* Initialize sub-modules */ +LOCAL(void) +reset_predictor (j_compress_ptr cinfo, int ci) +{ + lossless_comp_ptr losslessc = (lossless_comp_ptr) cinfo->fdct; - /* Scaler */ - jinit_c_scaler(cinfo); + /* Initialize restart counter */ + losslessc->restart_rows_to_go[ci] = + cinfo->restart_interval / cinfo->MCUs_per_row; - /* Differencer */ - jinit_differencer(cinfo); + /* Set difference function to first row function */ + losslessc->predict_difference[ci] = jpeg_difference_first_row; +} - /* Entropy encoding: either Huffman or arithmetic coding. */ - if (cinfo->arith_code) { - ERREXIT(cinfo, JERR_ARITH_NOTIMPL); - } else { - jinit_lhuff_encoder(cinfo); - } - /* Need a full-image difference buffer in any multi-pass mode. */ - jinit_c_diff_controller(cinfo, - (boolean) (cinfo->num_scans > 1 || - cinfo->optimize_coding)); +/********************** Sample downscaling by 2^Pt ***********************/ - /* Initialize method pointers. - * - * Note: entropy_start_pass and entropy_finish_pass are assigned in - * jclhuff.c and compress_data is assigned in jcdiffct.c. +METHODDEF(void) +simple_downscale(j_compress_ptr cinfo, + JSAMPROW input_buf, JSAMPROW output_buf, JDIMENSION width) +{ + while (width--) + *output_buf++ = (JSAMPLE) RIGHT_SHIFT(GETJSAMPLE(*input_buf++), cinfo->Al); +} + + +METHODDEF(void) +noscale(j_compress_ptr cinfo, + JSAMPROW input_buf, JSAMPROW output_buf, JDIMENSION width) +{ + MEMCOPY(output_buf, input_buf, width * SIZEOF(JSAMPLE)); + return; +} + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_lossless (j_compress_ptr cinfo) +{ + lossless_comp_ptr losslessc = (lossless_comp_ptr) cinfo->fdct; + int ci; + + /* Set scaler function based on Pt */ + if (cinfo->Al) + losslessc->scaler_scale = simple_downscale; + else + losslessc->scaler_scale = noscale; + + /* Check that the restart interval is an integer multiple of the number + * of MCUs in an MCU row. */ - losslsc->pub.start_pass = start_pass; + if (cinfo->restart_interval % cinfo->MCUs_per_row != 0) + ERREXIT2(cinfo, JERR_BAD_RESTART, + cinfo->restart_interval, cinfo->MCUs_per_row); + + /* Set predictors for start of pass */ + for (ci = 0; ci < cinfo->num_components; ci++) + reset_predictor(cinfo, ci); +} + + +/* + * Initialize the lossless compressor. + */ + +GLOBAL(void) +jinit_lossless_compressor(j_compress_ptr cinfo) +{ + lossless_comp_ptr losslessc; + + /* Create subobject in permanent pool */ + losslessc = (lossless_comp_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(jpeg_lossless_compressor)); + cinfo->fdct = (struct jpeg_forward_dct *) losslessc; + losslessc->pub.start_pass = start_pass_lossless; } #endif /* C_LOSSLESS_SUPPORTED */ diff --git a/jclossy.c b/jclossy.c deleted file mode 100644 index 07f508c88..000000000 --- a/jclossy.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * jclossy.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains the control logic for the lossy JPEG compressor. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jlossy.h" - - -/* - * Initialize for a processing pass. - */ - -METHODDEF(void) -start_pass (j_compress_ptr cinfo, J_BUF_MODE pass_mode) -{ - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - - (*lossyc->fdct_start_pass) (cinfo); - (*lossyc->coef_start_pass) (cinfo, pass_mode); -} - - -/* - * Initialize the lossy compression codec. - * This is called only once, during master selection. - */ - -GLOBAL(void) -jinit_lossy_c_codec (j_compress_ptr cinfo) -{ - j_lossy_c_ptr lossyc; - - /* Create subobject in permanent pool */ - lossyc = (j_lossy_c_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, - SIZEOF(jpeg_lossy_c_codec)); - cinfo->codec = (struct jpeg_c_codec *) lossyc; - - /* Initialize sub-modules */ - - /* Forward DCT */ - jinit_forward_dct(cinfo); - /* Entropy encoding: either Huffman or arithmetic coding. */ - if (cinfo->arith_code) { - ERREXIT(cinfo, JERR_ARITH_NOTIMPL); - } else { - if (cinfo->process == JPROC_PROGRESSIVE) { -#ifdef C_PROGRESSIVE_SUPPORTED - jinit_phuff_encoder(cinfo); -#else - ERREXIT(cinfo, JERR_NOT_COMPILED); -#endif - } else - jinit_shuff_encoder(cinfo); - } - - /* Need a full-image coefficient buffer in any multi-pass mode. */ - jinit_c_coef_controller(cinfo, - (boolean) (cinfo->num_scans > 1 || - cinfo->optimize_coding)); - - /* Initialize method pointers. - * - * Note: entropy_start_pass and entropy_finish_pass are assigned in - * jcshuff.c or jcphuff.c and compress_data is assigned in jccoefct.c. - */ - lossyc->pub.start_pass = start_pass; -} diff --git a/jcmainct.c b/jcmainct.c index 5e8686fca..7623bf0bb 100644 --- a/jcmainct.c +++ b/jcmainct.c @@ -2,9 +2,10 @@ * jcmainct.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. + * Copyright (C) 1994-1996, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains the main buffer controller for compression. @@ -117,7 +118,7 @@ process_data_simple_main (j_compress_ptr cinfo, JDIMENSION in_rows_avail) { my_main_ptr main = (my_main_ptr) cinfo->main; - int data_unit = cinfo->data_unit; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; while (main->cur_iMCU_row < cinfo->total_iMCU_rows) { /* Read input data if we haven't filled the main buffer yet */ @@ -135,7 +136,7 @@ process_data_simple_main (j_compress_ptr cinfo, return; /* Send the completed row to the compressor */ - if (! (*cinfo->codec->compress_data) (cinfo, main->buffer)) { + if (! (*cinfo->coef->compress_data) (cinfo, main->buffer)) { /* If compressor did not consume the whole row, then we must need to * suspend processing and return to the application. In this situation * we pretend we didn't yet consume the last input row; otherwise, if @@ -177,7 +178,7 @@ process_data_buffer_main (j_compress_ptr cinfo, int ci; jpeg_component_info *compptr; boolean writing = (main->pass_mode != JBUF_CRANK_DEST); - int data_unit = cinfo->data_unit; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; while (main->cur_iMCU_row < cinfo->total_iMCU_rows) { /* Realign the virtual buffers if at the start of an iMCU row. */ @@ -210,7 +211,7 @@ process_data_buffer_main (j_compress_ptr cinfo, /* Emit data, unless this is a sink-only pass. */ if (main->pass_mode != JBUF_SAVE_SOURCE) { - if (! (*cinfo->codec->compress_data) (cinfo, main->buffer)) { + if (! (*cinfo->coef->compress_data) (cinfo, main->buffer)) { /* If compressor did not consume the whole row, then we must need to * suspend processing and return to the application. In this situation * we pretend we didn't yet consume the last input row; otherwise, if @@ -251,7 +252,7 @@ jinit_c_main_controller (j_compress_ptr cinfo, boolean need_full_buffer) my_main_ptr main; int ci; jpeg_component_info *compptr; - int data_unit = cinfo->data_unit; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; main = (my_main_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, @@ -274,8 +275,8 @@ jinit_c_main_controller (j_compress_ptr cinfo, boolean need_full_buffer) ci++, compptr++) { main->whole_image[ci] = (*cinfo->mem->request_virt_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, - compptr->width_in_data_units * data_unit, - (JDIMENSION) jround_up((long) compptr->height_in_data_units, + compptr->width_in_blocks * data_unit, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, (long) compptr->v_samp_factor) * data_unit, (JDIMENSION) (compptr->v_samp_factor * data_unit)); } @@ -291,7 +292,7 @@ jinit_c_main_controller (j_compress_ptr cinfo, boolean need_full_buffer) ci++, compptr++) { main->buffer[ci] = (*cinfo->mem->alloc_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, - compptr->width_in_data_units * data_unit, + compptr->width_in_blocks * data_unit, (JDIMENSION) (compptr->v_samp_factor * data_unit)); } } diff --git a/jcmarker.c b/jcmarker.c index c174a517a..24e1e9f1c 100644 --- a/jcmarker.c +++ b/jcmarker.c @@ -5,6 +5,7 @@ * Copyright (C) 1991-1998, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains routines to write JPEG datastream markers. @@ -324,7 +325,7 @@ emit_sos (j_compress_ptr cinfo) emit_byte(cinfo, compptr->component_id); td = compptr->dc_tbl_no; ta = compptr->ac_tbl_no; - if (cinfo->process == JPROC_PROGRESSIVE) { + if (cinfo->progressive_mode) { /* Progressive mode: only DC or only AC tables are used in one scan; * furthermore, Huffman coding of DC refinement uses no table at all. * We emit 0 for unused field(s); this is recommended by the P&M text @@ -495,15 +496,14 @@ write_file_header (j_compress_ptr cinfo) METHODDEF(void) write_frame_header (j_compress_ptr cinfo) { - int ci, prec; + int ci, prec = 0; boolean is_baseline; jpeg_component_info *compptr; - if (cinfo->process != JPROC_LOSSLESS) { + if (! cinfo->master->lossless) { /* Emit DQT for each quantization table. * Note that emit_dqt() suppresses any duplicate tables. */ - prec = 0; for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { prec += emit_dqt(cinfo, compptr->quant_tbl_no); @@ -514,8 +514,8 @@ write_frame_header (j_compress_ptr cinfo) /* Check for a non-baseline specification. * Note we assume that Huffman table numbers won't be changed later. */ - if (cinfo->arith_code || cinfo->process != JPROC_SEQUENTIAL || - cinfo->data_precision != 8) { + if (cinfo->arith_code || cinfo->progressive_mode || + cinfo->master->lossless || cinfo->data_precision != 8) { is_baseline = FALSE; } else { is_baseline = TRUE; @@ -535,9 +535,9 @@ write_frame_header (j_compress_ptr cinfo) if (cinfo->arith_code) { emit_sof(cinfo, M_SOF9); /* SOF code for arithmetic coding */ } else { - if (cinfo->process == JPROC_PROGRESSIVE) + if (cinfo->progressive_mode) emit_sof(cinfo, M_SOF2); /* SOF code for progressive Huffman */ - else if (cinfo->process == JPROC_LOSSLESS) + else if (cinfo->master->lossless) emit_sof(cinfo, M_SOF3); /* SOF code for lossless Huffman */ else if (is_baseline) emit_sof(cinfo, M_SOF0); /* SOF code for baseline implementation */ @@ -572,7 +572,7 @@ write_scan_header (j_compress_ptr cinfo) */ for (i = 0; i < cinfo->comps_in_scan; i++) { compptr = cinfo->cur_comp_info[i]; - if (cinfo->process == JPROC_PROGRESSIVE) { + if (cinfo->progressive_mode) { /* Progressive mode: only DC or only AC tables are used in one scan */ if (cinfo->Ss == 0) { if (cinfo->Ah == 0) /* DC needs no table for refinement scan */ @@ -580,7 +580,7 @@ write_scan_header (j_compress_ptr cinfo) } else { emit_dht(cinfo, compptr->ac_tbl_no, TRUE); } - } else if (cinfo->process == JPROC_LOSSLESS) { + } else if (cinfo->master->lossless) { /* Lossless mode: only DC tables are used */ emit_dht(cinfo, compptr->dc_tbl_no, FALSE); } else { diff --git a/jcmaster.c b/jcmaster.c index ba16ced9c..02138868a 100644 --- a/jcmaster.c +++ b/jcmaster.c @@ -2,9 +2,10 @@ * jcmaster.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains master control logic for the JPEG compressor. @@ -16,29 +17,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossy.h" /* Private declarations for lossy codec */ - - -/* Private state */ - -typedef enum { - main_pass, /* input data, also do first output step */ - huff_opt_pass, /* Huffman code optimization pass */ - output_pass /* data output pass */ -} c_pass_type; - -typedef struct { - struct jpeg_comp_master pub; /* public fields */ - - c_pass_type pass_type; /* the type of the current pass */ - - int pass_number; /* # of passes completed */ - int total_passes; /* total # of passes needed */ - - int scan_number; /* current index in scan_info[] */ -} my_comp_master; - -typedef my_comp_master * my_master_ptr; +#include "jcmaster.h" /* @@ -53,7 +32,7 @@ initial_setup (j_compress_ptr cinfo) jpeg_component_info *compptr; long samplesperrow; JDIMENSION jd_samplesperrow; - int data_unit = cinfo->data_unit; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; /* Sanity check on image dimensions */ if (cinfo->image_height <= 0 || cinfo->image_width <= 0 @@ -99,13 +78,13 @@ initial_setup (j_compress_ptr cinfo) ci++, compptr++) { /* Fill in the correct component_index value; don't rely on application */ compptr->component_index = ci; - /* For compression, we never do any codec-based processing. */ - compptr->codec_data_unit = data_unit; + /* For compression, we never do DCT scaling. */ + compptr->DCT_scaled_size = data_unit; /* Size in data units */ - compptr->width_in_data_units = (JDIMENSION) + compptr->width_in_blocks = (JDIMENSION) jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, (long) (cinfo->max_h_samp_factor * data_unit)); - compptr->height_in_data_units = (JDIMENSION) + compptr->height_in_blocks = (JDIMENSION) jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, (long) (cinfo->max_v_samp_factor * data_unit)); /* Size in samples */ @@ -120,27 +99,24 @@ initial_setup (j_compress_ptr cinfo) } /* Compute number of fully interleaved MCU rows (number of times that - * main controller will call coefficient controller). + * main controller will call coefficient or difference controller). */ cinfo->total_iMCU_rows = (JDIMENSION) jdiv_round_up((long) cinfo->image_height, (long) (cinfo->max_v_samp_factor*data_unit)); } -#ifdef C_MULTISCAN_FILES_SUPPORTED -#define NEED_SCAN_SCRIPT -#else -#ifdef C_LOSSLESS_SUPPORTED + +#if defined(C_MULTISCAN_FILES_SUPPORTED) || defined(C_LOSSLESS_SUPPORTED) #define NEED_SCAN_SCRIPT #endif -#endif #ifdef NEED_SCAN_SCRIPT LOCAL(void) validate_script (j_compress_ptr cinfo) /* Verify that the scan script in cinfo->scan_info[] is valid; also - * determine whether it uses progressive JPEG, and set cinfo->process. + * determine whether it uses progressive JPEG, and set cinfo->progressive_mode. */ { const jpeg_scan_info * scanptr; @@ -162,9 +138,10 @@ validate_script (j_compress_ptr cinfo) #endif scanptr = cinfo->scan_info; - if (cinfo->lossless) { + if (scanptr->Ss != 0 && scanptr->Se == 0) { #ifdef C_LOSSLESS_SUPPORTED - cinfo->process = JPROC_LOSSLESS; + cinfo->master->lossless = TRUE; + cinfo->progressive_mode = FALSE; for (ci = 0; ci < cinfo->num_components; ci++) component_sent[ci] = FALSE; #else @@ -176,7 +153,8 @@ validate_script (j_compress_ptr cinfo) */ else if (scanptr->Ss != 0 || scanptr->Se != DCTSIZE2-1) { #ifdef C_PROGRESSIVE_SUPPORTED - cinfo->process = JPROC_PROGRESSIVE; + cinfo->progressive_mode = TRUE; + cinfo->master->lossless = FALSE; last_bitpos_ptr = & last_bitpos[0][0]; for (ci = 0; ci < cinfo->num_components; ci++) for (coefi = 0; coefi < DCTSIZE2; coefi++) @@ -185,7 +163,7 @@ validate_script (j_compress_ptr cinfo) ERREXIT(cinfo, JERR_NOT_COMPILED); #endif } else { - cinfo->process = JPROC_SEQUENTIAL; + cinfo->progressive_mode = cinfo->master->lossless = FALSE; for (ci = 0; ci < cinfo->num_components; ci++) component_sent[ci] = FALSE; } @@ -208,26 +186,7 @@ validate_script (j_compress_ptr cinfo) Se = scanptr->Se; Ah = scanptr->Ah; Al = scanptr->Al; - if (cinfo->process == JPROC_LOSSLESS) { -#ifdef C_LOSSLESS_SUPPORTED - /* The JPEG spec simply gives the range 0..15 for Al (Pt), but that - * seems wrong: the upper bound ought to depend on data precision. - * Perhaps they really meant 0..N-1 for N-bit precision, which is what - * we allow here. - */ - if (Ss < 1 || Ss > 7 || /* predictor selector */ - Se != 0 || Ah != 0 || - Al < 0 || Al >= cinfo->data_precision) /* point transform */ - ERREXIT1(cinfo, JERR_BAD_LOSSLESS_SCRIPT, scanno); - /* Make sure components are not sent twice */ - for (ci = 0; ci < ncomps; ci++) { - thisi = scanptr->component_index[ci]; - if (component_sent[thisi]) - ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno); - component_sent[thisi] = TRUE; - } -#endif - } else if (cinfo->process == JPROC_PROGRESSIVE) { + if (cinfo->progressive_mode) { #ifdef C_PROGRESSIVE_SUPPORTED /* The JPEG spec simply gives the ranges 0..13 for Ah and Al, but that * seems wrong: the upper bound ought to depend on data precision. @@ -270,9 +229,25 @@ validate_script (j_compress_ptr cinfo) } #endif } else { - /* For sequential JPEG, all progression parameters must be these: */ - if (Ss != 0 || Se != DCTSIZE2-1 || Ah != 0 || Al != 0) - ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); +#ifdef C_LOSSLESS_SUPPORTED + if (cinfo->master->lossless) { + /* The JPEG spec simply gives the range 0..15 for Al (Pt), but that + * seems wrong: the upper bound ought to depend on data precision. + * Perhaps they really meant 0..N-1 for N-bit precision, which is what + * we allow here. Values greater than or equal to the data precision + * will result in a blank image. + */ + if (Ss < 1 || Ss > 7 || /* predictor selection value */ + Se != 0 || Ah != 0 || + Al < 0 || Al >= cinfo->data_precision) /* point transform */ + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } else +#endif + { + /* For sequential JPEG, all progression parameters must be these: */ + if (Ss != 0 || Se != DCTSIZE2-1 || Ah != 0 || Al != 0) + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } /* Make sure components are not sent twice */ for (ci = 0; ci < ncomps; ci++) { thisi = scanptr->component_index[ci]; @@ -284,7 +259,7 @@ validate_script (j_compress_ptr cinfo) } /* Now verify that everything got sent. */ - if (cinfo->process == JPROC_PROGRESSIVE) { + if (cinfo->progressive_mode) { #ifdef C_PROGRESSIVE_SUPPORTED /* For progressive mode, we only check that at least some DC data * got sent for each component; the spec does not require that all bits @@ -339,15 +314,7 @@ select_scan_parameters (j_compress_ptr cinfo) for (ci = 0; ci < cinfo->num_components; ci++) { cinfo->cur_comp_info[ci] = &cinfo->comp_info[ci]; } - if (cinfo->lossless) { -#ifdef C_LOSSLESS_SUPPORTED - /* If we fall through to here, the user specified lossless, but did not - * provide a scan script. - */ - ERREXIT(cinfo, JERR_NO_LOSSLESS_SCRIPT); -#endif - } else { - cinfo->process = JPROC_SEQUENTIAL; + if (! cinfo->master->lossless) { cinfo->Ss = 0; cinfo->Se = DCTSIZE2-1; cinfo->Ah = 0; @@ -364,7 +331,7 @@ per_scan_setup (j_compress_ptr cinfo) { int ci, mcublks, tmp; jpeg_component_info *compptr; - int data_unit = cinfo->data_unit; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; if (cinfo->comps_in_scan == 1) { @@ -372,24 +339,24 @@ per_scan_setup (j_compress_ptr cinfo) compptr = cinfo->cur_comp_info[0]; /* Overall image size in MCUs */ - cinfo->MCUs_per_row = compptr->width_in_data_units; - cinfo->MCU_rows_in_scan = compptr->height_in_data_units; + 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_data_units = 1; + compptr->MCU_blocks = 1; compptr->MCU_sample_width = data_unit; 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_data_units % compptr->v_samp_factor); + tmp = (int) (compptr->height_in_blocks % compptr->v_samp_factor); if (tmp == 0) tmp = compptr->v_samp_factor; compptr->last_row_height = tmp; /* Prepare array describing MCU composition */ - cinfo->data_units_in_MCU = 1; + cinfo->blocks_in_MCU = 1; cinfo->MCU_membership[0] = 0; } else { @@ -407,28 +374,28 @@ per_scan_setup (j_compress_ptr cinfo) jdiv_round_up((long) cinfo->image_height, (long) (cinfo->max_v_samp_factor*data_unit)); - cinfo->data_units_in_MCU = 0; + cinfo->blocks_in_MCU = 0; for (ci = 0; ci < cinfo->comps_in_scan; ci++) { compptr = cinfo->cur_comp_info[ci]; /* Sampling factors give # of blocks of component in each MCU */ compptr->MCU_width = compptr->h_samp_factor; compptr->MCU_height = compptr->v_samp_factor; - compptr->MCU_data_units = compptr->MCU_width * compptr->MCU_height; + compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height; compptr->MCU_sample_width = compptr->MCU_width * data_unit; /* Figure number of non-dummy blocks in last MCU column & row */ - tmp = (int) (compptr->width_in_data_units % compptr->MCU_width); + tmp = (int) (compptr->width_in_blocks % compptr->MCU_width); if (tmp == 0) tmp = compptr->MCU_width; compptr->last_col_width = tmp; - tmp = (int) (compptr->height_in_data_units % compptr->MCU_height); + tmp = (int) (compptr->height_in_blocks % compptr->MCU_height); if (tmp == 0) tmp = compptr->MCU_height; compptr->last_row_height = tmp; /* Prepare array describing MCU composition */ - mcublks = compptr->MCU_data_units; - if (cinfo->data_units_in_MCU + mcublks > C_MAX_DATA_UNITS_IN_MCU) + mcublks = compptr->MCU_blocks; + if (cinfo->blocks_in_MCU + mcublks > C_MAX_BLOCKS_IN_MCU) ERREXIT(cinfo, JERR_BAD_MCU_SIZE); while (mcublks-- > 0) { - cinfo->MCU_membership[cinfo->data_units_in_MCU++] = ci; + cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci; } } @@ -454,7 +421,6 @@ per_scan_setup (j_compress_ptr cinfo) METHODDEF(void) prepare_for_pass (j_compress_ptr cinfo) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; my_master_ptr master = (my_master_ptr) cinfo->master; switch (master->pass_type) { @@ -469,10 +435,11 @@ prepare_for_pass (j_compress_ptr cinfo) (*cinfo->downsample->start_pass) (cinfo); (*cinfo->prep->start_pass) (cinfo, JBUF_PASS_THRU); } - (*cinfo->codec->entropy_start_pass) (cinfo, cinfo->optimize_coding); - (*cinfo->codec->start_pass) (cinfo, - (master->total_passes > 1 ? - JBUF_SAVE_AND_PASS : JBUF_PASS_THRU)); + (*cinfo->fdct->start_pass) (cinfo); + (*cinfo->entropy->start_pass) (cinfo, cinfo->optimize_coding); + (*cinfo->coef->start_pass) (cinfo, + (master->total_passes > 1 ? + JBUF_SAVE_AND_PASS : JBUF_PASS_THRU)); (*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU); if (cinfo->optimize_coding) { /* No immediate data output; postpone writing frame/scan headers */ @@ -487,9 +454,10 @@ prepare_for_pass (j_compress_ptr cinfo) /* Do Huffman optimization for a scan after the first one. */ select_scan_parameters(cinfo); per_scan_setup(cinfo); - if ((*cinfo->codec->need_optimization_pass) (cinfo) || cinfo->arith_code) { - (*cinfo->codec->entropy_start_pass) (cinfo, TRUE); - (*cinfo->codec->start_pass) (cinfo, JBUF_CRANK_DEST); + if (cinfo->Ss != 0 || cinfo->Ah == 0 || cinfo->arith_code || + cinfo->master->lossless) { + (*cinfo->entropy->start_pass) (cinfo, TRUE); + (*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST); master->pub.call_pass_startup = FALSE; break; } @@ -507,8 +475,8 @@ prepare_for_pass (j_compress_ptr cinfo) select_scan_parameters(cinfo); per_scan_setup(cinfo); } - (*cinfo->codec->entropy_start_pass) (cinfo, FALSE); - (*cinfo->codec->start_pass) (cinfo, JBUF_CRANK_DEST); + (*cinfo->entropy->start_pass) (cinfo, FALSE); + (*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST); /* We emit frame/scan headers now */ if (master->scan_number == 0) (*cinfo->marker->write_frame_header) (cinfo); @@ -556,13 +524,12 @@ pass_startup (j_compress_ptr cinfo) METHODDEF(void) finish_pass_master (j_compress_ptr cinfo) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; my_master_ptr master = (my_master_ptr) cinfo->master; /* The entropy coder always needs an end-of-pass call, * either to analyze statistics or to flush its output buffer. */ - (*lossyc->pub.entropy_finish_pass) (cinfo); + (*cinfo->entropy->finish_pass) (cinfo); /* Update state for next pass */ switch (master->pass_type) { @@ -597,22 +564,13 @@ finish_pass_master (j_compress_ptr cinfo) GLOBAL(void) jinit_c_master_control (j_compress_ptr cinfo, boolean transcode_only) { - my_master_ptr master; + my_master_ptr master = (my_master_ptr) cinfo->master; - master = (my_master_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(my_comp_master)); - cinfo->master = (struct jpeg_comp_master *) master; master->pub.prepare_for_pass = prepare_for_pass; master->pub.pass_startup = pass_startup; master->pub.finish_pass = finish_pass_master; master->pub.is_last_pass = FALSE; - cinfo->data_unit = cinfo->lossless ? 1 : DCTSIZE; - - /* Validate parameters, determine derived values */ - initial_setup(cinfo); - if (cinfo->scan_info != NULL) { #ifdef NEED_SCAN_SCRIPT validate_script(cinfo); @@ -620,12 +578,32 @@ jinit_c_master_control (j_compress_ptr cinfo, boolean transcode_only) ERREXIT(cinfo, JERR_NOT_COMPILED); #endif } else { - cinfo->process = JPROC_SEQUENTIAL; + cinfo->progressive_mode = FALSE; cinfo->num_scans = 1; } - if (cinfo->process == JPROC_PROGRESSIVE || /* TEMPORARY HACK ??? */ - cinfo->process == JPROC_LOSSLESS) + /* Disable smoothing and subsampling in lossless mode, since those are lossy + * algorithms. Set the JPEG colorspace to the input colorspace. Disable raw + * (downsampled) data input, because it isn't particularly useful without + * subsampling and has not been tested in lossless mode. + */ + if (cinfo->master->lossless) { + int ci; + jpeg_component_info * compptr; + + cinfo->raw_data_in = FALSE; + cinfo->smoothing_factor = 0; + jpeg_default_colorspace(cinfo); + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) + compptr->h_samp_factor = compptr->v_samp_factor = 1; + } + + /* Validate parameters, determine derived values */ + initial_setup(cinfo); + + if (cinfo->progressive_mode || /* TEMPORARY HACK ??? */ + cinfo->master->lossless) cinfo->optimize_coding = TRUE; /* assume default tables no good for * progressive mode or lossless mode */ diff --git a/jcmaster.h b/jcmaster.h new file mode 100644 index 000000000..aead386b2 --- /dev/null +++ b/jcmaster.h @@ -0,0 +1,30 @@ +/* + * jcmaster.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1991-1995, Thomas G. Lane. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains master control structure for the JPEG compressor. + */ + +/* Private state */ + +typedef enum { + main_pass, /* input data, also do first output step */ + huff_opt_pass, /* Huffman code optimization pass */ + output_pass /* data output pass */ +} c_pass_type; + +typedef struct { + struct jpeg_comp_master pub; /* public fields */ + + c_pass_type pass_type; /* the type of the current pass */ + + int pass_number; /* # of passes completed */ + int total_passes; /* total # of passes needed */ + + int scan_number; /* current index in scan_info[] */ +} my_comp_master; + +typedef my_comp_master * my_master_ptr; diff --git a/jcodec.c b/jcodec.c deleted file mode 100644 index 5eae931e7..000000000 --- a/jcodec.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * jcodec.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains utility functions for the JPEG codec(s). - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jlossy.h" -#include "jlossls.h" - - -/* - * Initialize the compression codec. - * This is called only once, during master selection. - */ - -GLOBAL(void) -jinit_c_codec (j_compress_ptr cinfo) -{ - if (cinfo->process == JPROC_LOSSLESS) { -#ifdef C_LOSSLESS_SUPPORTED - jinit_lossless_c_codec(cinfo); -#else - ERREXIT(cinfo, JERR_NOT_COMPILED); -#endif - } else - jinit_lossy_c_codec(cinfo); -} - - -/* - * Initialize the decompression codec. - * This is called only once, during master selection. - */ - -GLOBAL(void) -jinit_d_codec (j_decompress_ptr cinfo) -{ - if (cinfo->process == JPROC_LOSSLESS) { -#ifdef D_LOSSLESS_SUPPORTED - jinit_lossless_d_codec(cinfo); -#else - ERREXIT(cinfo, JERR_NOT_COMPILED); -#endif - } else - jinit_lossy_d_codec(cinfo); -} diff --git a/jcparam.c b/jcparam.c index 3c14bd937..690666ee4 100644 --- a/jcparam.c +++ b/jcparam.c @@ -5,6 +5,7 @@ * Copyright (C) 1991-1998, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains optional default-setting code for the JPEG compressor. @@ -286,7 +287,6 @@ jpeg_set_defaults (j_compress_ptr cinfo) /* Initialize everything not dependent on the color space */ - cinfo->lossless = FALSE; cinfo->data_precision = BITS_IN_JSAMPLE; /* Set up two quantization tables using default quality of 75 */ jpeg_set_quality(cinfo, 75, TRUE); @@ -361,31 +361,30 @@ jpeg_set_defaults (j_compress_ptr cinfo) GLOBAL(void) jpeg_default_colorspace (j_compress_ptr cinfo) { - if (cinfo->lossless) - jpeg_set_colorspace(cinfo, cinfo->in_color_space); - else { /* lossy */ - switch (cinfo->in_color_space) { - case JCS_GRAYSCALE: - jpeg_set_colorspace(cinfo, JCS_GRAYSCALE); - break; - case JCS_RGB: - jpeg_set_colorspace(cinfo, JCS_YCbCr); - break; - case JCS_YCbCr: + switch (cinfo->in_color_space) { + case JCS_GRAYSCALE: + jpeg_set_colorspace(cinfo, JCS_GRAYSCALE); + break; + case JCS_RGB: + if (cinfo->master->lossless) + jpeg_set_colorspace(cinfo, JCS_RGB); + else jpeg_set_colorspace(cinfo, JCS_YCbCr); - break; - case JCS_CMYK: - jpeg_set_colorspace(cinfo, JCS_CMYK); /* By default, no translation */ - break; - case JCS_YCCK: - jpeg_set_colorspace(cinfo, JCS_YCCK); - break; - case JCS_UNKNOWN: - jpeg_set_colorspace(cinfo, JCS_UNKNOWN); - break; - default: - ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); - } + break; + case JCS_YCbCr: + jpeg_set_colorspace(cinfo, JCS_YCbCr); + break; + case JCS_CMYK: + jpeg_set_colorspace(cinfo, JCS_CMYK); /* By default, no translation */ + break; + case JCS_YCCK: + jpeg_set_colorspace(cinfo, JCS_YCCK); + break; + case JCS_UNKNOWN: + jpeg_set_colorspace(cinfo, JCS_UNKNOWN); + break; + default: + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); } } @@ -440,16 +439,10 @@ jpeg_set_colorspace (j_compress_ptr cinfo, J_COLOR_SPACE colorspace) cinfo->write_JFIF_header = TRUE; /* Write a JFIF marker */ cinfo->num_components = 3; /* JFIF specifies component IDs 1,2,3 */ - if (cinfo->lossless) { - SET_COMP(0, 1, 1,1, 0, 0,0); - SET_COMP(1, 2, 1,1, 1, 1,1); - SET_COMP(2, 3, 1,1, 1, 1,1); - } else { /* lossy */ - /* 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); - } + /* We default to 2x2 subsamples of chrominance */ + SET_COMP(0, 1, 2,2, 0, 0,0); + SET_COMP(1, 2, 1,1, 1, 1,1); + SET_COMP(2, 3, 1,1, 1, 1,1); break; case JCS_CMYK: cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag CMYK */ @@ -462,17 +455,10 @@ jpeg_set_colorspace (j_compress_ptr cinfo, J_COLOR_SPACE colorspace) case JCS_YCCK: cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag YCCK */ cinfo->num_components = 4; - if (cinfo->lossless) { - SET_COMP(0, 1, 1,1, 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, 1,1, 0, 0,0); - } else { /* lossy */ - 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); - } + SET_COMP(0, 1, 2,2, 0, 0,0); + SET_COMP(1, 2, 1,1, 1, 1,1); + SET_COMP(2, 3, 1,1, 1, 1,1); + SET_COMP(3, 4, 2,2, 0, 0,0); break; case JCS_UNKNOWN: cinfo->num_components = cinfo->input_components; @@ -491,6 +477,21 @@ jpeg_set_colorspace (j_compress_ptr cinfo, J_COLOR_SPACE colorspace) #ifdef C_PROGRESSIVE_SUPPORTED +LOCAL(jpeg_scan_info *) +fill_a_scan (jpeg_scan_info * scanptr, int ci, + int Ss, int Se, int Ah, int Al) +/* Support routine: generate one scan for specified component */ +{ + scanptr->comps_in_scan = 1; + scanptr->component_index[0] = ci; + scanptr->Ss = Ss; + scanptr->Se = Se; + scanptr->Ah = Ah; + scanptr->Al = Al; + scanptr++; + return scanptr; +} + LOCAL(jpeg_scan_info *) fill_scans (jpeg_scan_info * scanptr, int ncomps, int Ss, int Se, int Ah, int Al) @@ -510,22 +511,6 @@ fill_scans (jpeg_scan_info * scanptr, int ncomps, return scanptr; } - -LOCAL(jpeg_scan_info *) -fill_a_scan (jpeg_scan_info * scanptr, int ci, - int Ss, int Se, int Ah, int Al) -/* Support routine: generate one scan for specified component */ -{ - scanptr->comps_in_scan = 1; - scanptr->component_index[0] = ci; - scanptr->Ss = Ss; - scanptr->Se = Se; - scanptr->Ah = Ah; - scanptr->Al = Al; - scanptr++; - return scanptr; -} - LOCAL(jpeg_scan_info *) fill_dc_scans (jpeg_scan_info * scanptr, int ncomps, int Ah, int Al) /* Support routine: generate interleaved DC scan if possible, else N scans */ @@ -565,6 +550,11 @@ jpeg_simple_progression (j_compress_ptr cinfo) if (cinfo->global_state != CSTATE_START) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->master->lossless) { + cinfo->master->lossless = FALSE; + jpeg_default_colorspace(cinfo); + } + /* Figure space needed for script. Calculation must match code below! */ if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) { /* Custom script for YCbCr color images. */ @@ -634,56 +624,33 @@ jpeg_simple_progression (j_compress_ptr cinfo) #ifdef C_LOSSLESS_SUPPORTED /* - * Create a single-entry lossless-JPEG script containing all components. - * cinfo->num_components must be correct. + * Enable lossless mode. */ GLOBAL(void) -jpeg_simple_lossless (j_compress_ptr cinfo, int predictor, int point_transform) +jpeg_enable_lossless (j_compress_ptr cinfo, int predictor_selection_value, + int point_transform) { - int ncomps = cinfo->num_components; - int nscans = 1; - int ci; - jpeg_scan_info * scanptr; - /* Safety check to ensure start_compress not called yet. */ if (cinfo->global_state != CSTATE_START) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); - cinfo->lossless = TRUE; - - /* Set jpeg_color_space. */ - jpeg_default_colorspace(cinfo); - - /* Check to ensure that all components will fit in one scan. */ - if (cinfo->num_components > MAX_COMPS_IN_SCAN) - ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, - MAX_COMPS_IN_SCAN); - - /* 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_lossless is called repeatedly for the same JPEG - * object, we try to re-use previously allocated space. + cinfo->master->lossless = TRUE; + cinfo->Ss = predictor_selection_value; + cinfo->Se = 0; + cinfo->Ah = 0; + cinfo->Al = point_transform; + + /* The JPEG spec simply gives the range 0..15 for Al (Pt), but that seems + * wrong: the upper bound ought to depend on data precision. Perhaps they + * really meant 0..N-1 for N-bit precision, which is what we allow here. + * Values greater than or equal to the data precision will result in a blank + * image. */ - if (cinfo->script_space == NULL || cinfo->script_space_size < nscans) { - cinfo->script_space_size = nscans; - cinfo->script_space = (jpeg_scan_info *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, - cinfo->script_space_size * SIZEOF(jpeg_scan_info)); - } - scanptr = cinfo->script_space; - cinfo->scan_info = scanptr; - cinfo->num_scans = nscans; - - /* Fill the script. */ - scanptr->comps_in_scan = ncomps; - for (ci = 0; ci < ncomps; ci++) - scanptr->component_index[ci] = ci; - scanptr->Ss = predictor; - scanptr->Se = 0; - scanptr->Ah = 0; - scanptr->Al = point_transform; + if (cinfo->Ss < 1 || cinfo->Ss > 7 || + cinfo->Al < 0 || cinfo->Al >= cinfo->data_precision) + ERREXIT4(cinfo, JERR_BAD_PROGRESSION, + cinfo->Ss, cinfo->Se, cinfo->Ah, cinfo->Al); } #endif /* C_LOSSLESS_SUPPORTED */ diff --git a/jcphuff.c b/jcphuff.c index 6ecef0898..49f93c556 100644 --- a/jcphuff.c +++ b/jcphuff.c @@ -2,7 +2,7 @@ * jcphuff.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1995-1998, Thomas G. Lane. + * Copyright (C) 1995-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. * For conditions of distribution and use, see the accompanying README file. @@ -17,7 +17,6 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossy.h" /* Private declarations for lossy codec */ #include "jchuff.h" /* Declarations shared with jc*huff.c */ #ifdef C_PROGRESSIVE_SUPPORTED @@ -25,6 +24,8 @@ /* Expanded entropy encoder object for progressive Huffman encoding. */ typedef struct { + struct jpeg_entropy_encoder pub; /* public fields */ + /* Mode flag: TRUE for optimization, FALSE for actual data output */ boolean gather_statistics; @@ -106,8 +107,7 @@ METHODDEF(void) finish_pass_gather_phuff JPP((j_compress_ptr cinfo)); METHODDEF(void) start_pass_phuff (j_compress_ptr cinfo, boolean gather_statistics) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyc->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; boolean is_DC_band; int ci, tbl; jpeg_component_info * compptr; @@ -122,14 +122,14 @@ start_pass_phuff (j_compress_ptr cinfo, boolean gather_statistics) /* Select execution routines */ if (cinfo->Ah == 0) { if (is_DC_band) - lossyc->entropy_encode_mcu = encode_mcu_DC_first; + entropy->pub.encode_mcu = encode_mcu_DC_first; else - lossyc->entropy_encode_mcu = encode_mcu_AC_first; + entropy->pub.encode_mcu = encode_mcu_AC_first; } else { if (is_DC_band) - lossyc->entropy_encode_mcu = encode_mcu_DC_refine; + entropy->pub.encode_mcu = encode_mcu_DC_refine; else { - lossyc->entropy_encode_mcu = encode_mcu_AC_refine; + entropy->pub.encode_mcu = encode_mcu_AC_refine; /* AC refinement needs a correction bit buffer */ if (entropy->bit_buffer == NULL) entropy->bit_buffer = (char *) @@ -138,9 +138,9 @@ start_pass_phuff (j_compress_ptr cinfo, boolean gather_statistics) } } if (gather_statistics) - lossyc->pub.entropy_finish_pass = finish_pass_gather_phuff; + entropy->pub.finish_pass = finish_pass_gather_phuff; else - lossyc->pub.entropy_finish_pass = finish_pass_phuff; + entropy->pub.finish_pass = finish_pass_phuff; /* Only DC coefficients may be interleaved, so cinfo->comps_in_scan = 1 * for AC coefficients. @@ -378,8 +378,7 @@ emit_restart (phuff_entropy_ptr entropy, int restart_num) METHODDEF(boolean) encode_mcu_DC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyc->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; register int temp, temp2; register int nbits; int blkn, ci; @@ -397,7 +396,7 @@ encode_mcu_DC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) emit_restart(entropy, entropy->next_restart_num); /* Encode the MCU data blocks */ - for (blkn = 0; blkn < cinfo->data_units_in_MCU; blkn++) { + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { block = MCU_data[blkn]; ci = cinfo->MCU_membership[blkn]; compptr = cinfo->cur_comp_info[ci]; @@ -466,8 +465,7 @@ encode_mcu_DC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) METHODDEF(boolean) encode_mcu_AC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyc->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; register int temp, temp2; register int nbits; register int r, k; @@ -574,8 +572,7 @@ encode_mcu_AC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) METHODDEF(boolean) encode_mcu_DC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyc->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; register int temp; int blkn; int Al = cinfo->Al; @@ -590,7 +587,7 @@ encode_mcu_DC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) emit_restart(entropy, entropy->next_restart_num); /* Encode the MCU data blocks */ - for (blkn = 0; blkn < cinfo->data_units_in_MCU; blkn++) { + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { block = MCU_data[blkn]; /* We simply emit the Al'th bit of the DC coefficient value. */ @@ -622,8 +619,7 @@ encode_mcu_DC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) METHODDEF(boolean) encode_mcu_AC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyc->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; register int temp; register int r, k; int EOB; @@ -751,8 +747,7 @@ encode_mcu_AC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) METHODDEF(void) finish_pass_phuff (j_compress_ptr cinfo) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyc->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; entropy->next_output_byte = cinfo->dest->next_output_byte; entropy->free_in_buffer = cinfo->dest->free_in_buffer; @@ -773,8 +768,7 @@ finish_pass_phuff (j_compress_ptr cinfo) METHODDEF(void) finish_pass_gather_phuff (j_compress_ptr cinfo) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyc->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; boolean is_DC_band; int ci, tbl; jpeg_component_info * compptr; @@ -814,13 +808,6 @@ finish_pass_gather_phuff (j_compress_ptr cinfo) } -METHODDEF(boolean) -need_optimization_pass (j_compress_ptr cinfo) -{ - return (cinfo->Ss != 0 || cinfo->Ah == 0); -} - - /* * Module initialization routine for progressive Huffman entropy encoding. */ @@ -828,16 +815,14 @@ need_optimization_pass (j_compress_ptr cinfo) GLOBAL(void) jinit_phuff_encoder (j_compress_ptr cinfo) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; phuff_entropy_ptr entropy; int i; entropy = (phuff_entropy_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, SIZEOF(phuff_entropy_encoder)); - lossyc->entropy_private = (struct jpeg_entropy_encoder *) entropy; - lossyc->pub.entropy_start_pass = start_pass_phuff; - lossyc->pub.need_optimization_pass = need_optimization_pass; + cinfo->entropy = (struct jpeg_entropy_encoder *) entropy; + entropy->pub.start_pass = start_pass_phuff; /* Mark tables unallocated */ for (i = 0; i < NUM_HUFF_TBLS; i++) { diff --git a/jcpred.c b/jcpred.c deleted file mode 100644 index 22d3f3c4e..000000000 --- a/jcpred.c +++ /dev/null @@ -1,299 +0,0 @@ -/* - * jcpred.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains sample differencing for lossless JPEG. - * - * In order to avoid paying the performance penalty of having to check the - * predictor being used and the row being processed for each call of the - * undifferencer, and to promote optimization, we have separate differencing - * functions for each case. - * - * We are able to avoid duplicating source code by implementing the predictors - * and differencers as macros. Each of the differencing functions are - * simply wrappers around a DIFFERENCE macro with the appropriate PREDICTOR - * macro passed as an argument. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jlossls.h" /* Private declarations for lossless codec */ - - -#ifdef C_LOSSLESS_SUPPORTED - -/* Private predictor object */ - -typedef struct { - /* MCU-rows left in the restart interval for each component */ - unsigned int restart_rows_to_go[MAX_COMPONENTS]; -} c_predictor; - -typedef c_predictor * c_pred_ptr; - -/* Forward declarations */ -LOCAL(void) reset_predictor - JPP((j_compress_ptr cinfo, int ci)); -METHODDEF(void) start_pass - JPP((j_compress_ptr cinfo)); - - -/* Predictor for the first column of the first row: 2^(P-Pt-1) */ -#define INITIAL_PREDICTORx (1 << (cinfo->data_precision - cinfo->Al - 1)) - -/* Predictor for the first column of the remaining rows: Rb */ -#define INITIAL_PREDICTOR2 GETJSAMPLE(prev_row[0]) - - -/* - * 1-Dimensional differencer routine. - * - * This macro implements the 1-D horizontal predictor (1). INITIAL_PREDICTOR - * is used as the special case predictor for the first column, which must be - * either INITIAL_PREDICTOR2 or INITIAL_PREDICTORx. The remaining samples - * use PREDICTOR1. - */ - -#define DIFFERENCE_1D(INITIAL_PREDICTOR) \ - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; \ - c_pred_ptr pred = (c_pred_ptr) losslsc->pred_private; \ - boolean restart = FALSE; \ - int xindex; \ - int samp, Ra; \ - \ - samp = GETJSAMPLE(input_buf[0]); \ - diff_buf[0] = samp - INITIAL_PREDICTOR; \ - \ - for (xindex = 1; xindex < width; xindex++) { \ - Ra = samp; \ - samp = GETJSAMPLE(input_buf[xindex]); \ - diff_buf[xindex] = samp - PREDICTOR1; \ - } \ - \ - /* Account for restart interval (no-op if not using restarts) */ \ - if (cinfo->restart_interval) { \ - if (--(pred->restart_rows_to_go[ci]) == 0) { \ - reset_predictor(cinfo, ci); \ - restart = TRUE; \ - } \ - } - - -/* - * 2-Dimensional differencer routine. - * - * This macro implements the 2-D horizontal predictors (#2-7). PREDICTOR2 is - * used as the special case predictor for the first column. The remaining - * samples use PREDICTOR, which is a function of Ra, Rb, Rc. - * - * Because prev_row and output_buf may point to the same storage area (in an - * interleaved image with Vi=1, for example), we must take care to buffer Rb/Rc - * before writing the current reconstructed sample value into output_buf. - */ - -#define DIFFERENCE_2D(PREDICTOR) \ - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; \ - c_pred_ptr pred = (c_pred_ptr) losslsc->pred_private; \ - int xindex; \ - int samp, Ra, Rb, Rc; \ - \ - Rb = GETJSAMPLE(prev_row[0]); \ - samp = GETJSAMPLE(input_buf[0]); \ - diff_buf[0] = samp - PREDICTOR2; \ - \ - for (xindex = 1; xindex < width; xindex++) { \ - Rc = Rb; \ - Rb = GETJSAMPLE(prev_row[xindex]); \ - Ra = samp; \ - samp = GETJSAMPLE(input_buf[xindex]); \ - diff_buf[xindex] = samp - PREDICTOR; \ - } \ - \ - /* Account for restart interval (no-op if not using restarts) */ \ - if (cinfo->restart_interval) { \ - if (--pred->restart_rows_to_go[ci] == 0) \ - reset_predictor(cinfo, ci); \ - } - - -/* - * Differencers for the all rows but the first in a scan or restart interval. - * The first sample in the row is differenced using the vertical - * predictor (2). The rest of the samples are differenced using the - * predictor specified in the scan header. - */ - -METHODDEF(void) -jpeg_difference1(j_compress_ptr cinfo, int ci, - JSAMPROW input_buf, JSAMPROW prev_row, - JDIFFROW diff_buf, JDIMENSION width) -{ - DIFFERENCE_1D(INITIAL_PREDICTOR2); -} - -METHODDEF(void) -jpeg_difference2(j_compress_ptr cinfo, int ci, - JSAMPROW input_buf, JSAMPROW prev_row, - JDIFFROW diff_buf, JDIMENSION width) -{ - DIFFERENCE_2D(PREDICTOR2); -} - -METHODDEF(void) -jpeg_difference3(j_compress_ptr cinfo, int ci, - JSAMPROW input_buf, JSAMPROW prev_row, - JDIFFROW diff_buf, JDIMENSION width) -{ - DIFFERENCE_2D(PREDICTOR3); -} - -METHODDEF(void) -jpeg_difference4(j_compress_ptr cinfo, int ci, - JSAMPROW input_buf, JSAMPROW prev_row, - JDIFFROW diff_buf, JDIMENSION width) -{ - DIFFERENCE_2D(PREDICTOR4); -} - -METHODDEF(void) -jpeg_difference5(j_compress_ptr cinfo, int ci, - JSAMPROW input_buf, JSAMPROW prev_row, - JDIFFROW diff_buf, JDIMENSION width) -{ - DIFFERENCE_2D(PREDICTOR5); -} - -METHODDEF(void) -jpeg_difference6(j_compress_ptr cinfo, int ci, - JSAMPROW input_buf, JSAMPROW prev_row, - JDIFFROW diff_buf, JDIMENSION width) -{ - DIFFERENCE_2D(PREDICTOR6); -} - -METHODDEF(void) -jpeg_difference7(j_compress_ptr cinfo, int ci, - JSAMPROW input_buf, JSAMPROW prev_row, - JDIFFROW diff_buf, JDIMENSION width) -{ - DIFFERENCE_2D(PREDICTOR7); -} - - -/* - * Differencer for the first row in a scan or restart interval. The first - * sample in the row is differenced using the special predictor constant - * x=2^(P-Pt-1). The rest of the samples are differenced using the - * 1-D horizontal predictor (1). - */ - -METHODDEF(void) -jpeg_difference_first_row(j_compress_ptr cinfo, int ci, - JSAMPROW input_buf, JSAMPROW prev_row, - JDIFFROW diff_buf, JDIMENSION width) -{ - DIFFERENCE_1D(INITIAL_PREDICTORx); - - /* - * Now that we have differenced the first row, we want to use the - * differencer which corresponds to the predictor specified in the - * scan header. - * - * Note that we don't to do this if we have just reset the predictor - * for a new restart interval. - */ - if (!restart) { - switch (cinfo->Ss) { - case 1: - losslsc->predict_difference[ci] = jpeg_difference1; - break; - case 2: - losslsc->predict_difference[ci] = jpeg_difference2; - break; - case 3: - losslsc->predict_difference[ci] = jpeg_difference3; - break; - case 4: - losslsc->predict_difference[ci] = jpeg_difference4; - break; - case 5: - losslsc->predict_difference[ci] = jpeg_difference5; - break; - case 6: - losslsc->predict_difference[ci] = jpeg_difference6; - break; - case 7: - losslsc->predict_difference[ci] = jpeg_difference7; - break; - } - } -} - -/* - * Reset predictor at the start of a pass or restart interval. - */ - -LOCAL(void) -reset_predictor (j_compress_ptr cinfo, int ci) -{ - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - c_pred_ptr pred = (c_pred_ptr) losslsc->pred_private; - - /* Initialize restart counter */ - pred->restart_rows_to_go[ci] = - cinfo->restart_interval / cinfo->MCUs_per_row; - - /* Set difference function to first row function */ - losslsc->predict_difference[ci] = jpeg_difference_first_row; -} - - -/* - * Initialize for an input processing pass. - */ - -METHODDEF(void) -start_pass (j_compress_ptr cinfo) -{ - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - c_pred_ptr pred = (c_pred_ptr) losslsc->pred_private; - int ci; - - /* Check that the restart interval is an integer multiple of the number - * of MCU in an MCU-row. - */ - if (cinfo->restart_interval % cinfo->MCUs_per_row != 0) - ERREXIT2(cinfo, JERR_BAD_RESTART, - cinfo->restart_interval, cinfo->MCUs_per_row); - - /* Set predictors for start of pass */ - for (ci = 0; ci < cinfo->num_components; ci++) - reset_predictor(cinfo, ci); -} - - -/* - * Module initialization routine for the differencer. - */ - -GLOBAL(void) -jinit_differencer (j_compress_ptr cinfo) -{ - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - c_pred_ptr pred; - - pred = (c_pred_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(c_predictor)); - losslsc->pred_private = (void *) pred; - losslsc->predict_start_pass = start_pass; -} - -#endif /* C_LOSSLESS_SUPPORTED */ - diff --git a/jcprepct.c b/jcprepct.c index f7bf939a2..62eaab52d 100644 --- a/jcprepct.c +++ b/jcprepct.c @@ -2,9 +2,10 @@ * jcprepct.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. + * Copyright (C) 1994-1996, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains the compression preprocessing controller. @@ -137,6 +138,7 @@ pre_process_data (j_compress_ptr cinfo, int numrows, ci; JDIMENSION inrows; jpeg_component_info * compptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; while (*in_row_ctr < in_rows_avail && *out_row_group_ctr < out_row_groups_avail) { @@ -176,7 +178,7 @@ pre_process_data (j_compress_ptr cinfo, for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { expand_bottom_edge(output_buf[ci], - compptr->width_in_data_units * cinfo->data_unit, + compptr->width_in_blocks * data_unit, (int) (*out_row_group_ctr * compptr->v_samp_factor), (int) (out_row_groups_avail * compptr->v_samp_factor)); } @@ -273,6 +275,7 @@ create_context_buffer (j_compress_ptr cinfo) int ci, i; jpeg_component_info * compptr; JSAMPARRAY true_buffer, fake_buffer; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; /* Grab enough space for fake row pointers for all the components; * we need five row groups' worth of pointers for each component. @@ -290,7 +293,7 @@ create_context_buffer (j_compress_ptr cinfo) */ true_buffer = (*cinfo->mem->alloc_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, - (JDIMENSION) (((long) compptr->width_in_data_units * cinfo->data_unit * + (JDIMENSION) (((long) compptr->width_in_blocks * data_unit * cinfo->max_h_samp_factor) / compptr->h_samp_factor), (JDIMENSION) (3 * rgroup_height)); /* Copy true buffer row pointers into the middle of the fake row array */ @@ -319,6 +322,7 @@ jinit_c_prep_controller (j_compress_ptr cinfo, boolean need_full_buffer) my_prep_ptr prep; int ci; jpeg_component_info * compptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; if (need_full_buffer) /* safety check */ ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); @@ -348,7 +352,7 @@ jinit_c_prep_controller (j_compress_ptr cinfo, boolean need_full_buffer) ci++, compptr++) { prep->color_buf[ci] = (*cinfo->mem->alloc_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, - (JDIMENSION) (((long) compptr->width_in_data_units * cinfo->data_unit * + (JDIMENSION) (((long) compptr->width_in_blocks * data_unit * cinfo->max_h_samp_factor) / compptr->h_samp_factor), (JDIMENSION) cinfo->max_v_samp_factor); } diff --git a/jcsample.c b/jcsample.c index d97c8b85e..70a8c43bb 100644 --- a/jcsample.c +++ b/jcsample.c @@ -2,9 +2,10 @@ * jcsample.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1996, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains downsampling routines. @@ -144,7 +145,8 @@ int_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, { int inrow, outrow, h_expand, v_expand, numpix, numpix2, h, v; JDIMENSION outcol, outcol_h; /* outcol_h == outcol*h_expand */ - JDIMENSION output_cols = compptr->width_in_data_units * cinfo->data_unit; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + JDIMENSION output_cols = compptr->width_in_blocks * data_unit; JSAMPROW inptr, outptr; INT32 outvalue; @@ -189,12 +191,14 @@ METHODDEF(void) fullsize_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, JSAMPARRAY input_data, JSAMPARRAY output_data) { + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + /* 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_data_units * cinfo->data_unit); + cinfo->image_width, compptr->width_in_blocks * data_unit); } @@ -216,7 +220,8 @@ h2v1_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, { int outrow; JDIMENSION outcol; - JDIMENSION output_cols = compptr->width_in_data_units * cinfo->data_unit; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + JDIMENSION output_cols = compptr->width_in_blocks * data_unit; register JSAMPROW inptr, outptr; register int bias; @@ -253,7 +258,8 @@ h2v2_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, { int inrow, outrow; JDIMENSION outcol; - JDIMENSION output_cols = compptr->width_in_data_units * cinfo->data_unit; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + JDIMENSION output_cols = compptr->width_in_blocks * data_unit; register JSAMPROW inptr0, inptr1, outptr; register int bias; @@ -296,7 +302,8 @@ h2v2_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, { int inrow, outrow; JDIMENSION colctr; - JDIMENSION output_cols = compptr->width_in_data_units * cinfo->data_unit; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + JDIMENSION output_cols = compptr->width_in_blocks * data_unit; register JSAMPROW inptr0, inptr1, above_ptr, below_ptr, outptr; INT32 membersum, neighsum, memberscale, neighscale; @@ -396,7 +403,8 @@ fullsize_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info *compptr, { int outrow; JDIMENSION colctr; - JDIMENSION output_cols = compptr->width_in_data_units * cinfo->data_unit; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + JDIMENSION output_cols = compptr->width_in_blocks * data_unit; register JSAMPROW inptr, above_ptr, below_ptr, outptr; INT32 membersum, neighsum, memberscale, neighscale; int colsum, lastcolsum, nextcolsum; diff --git a/jcscale.c b/jcscale.c deleted file mode 100644 index ce945bfdc..000000000 --- a/jcscale.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * jcscale.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains sample downscaling by 2^Pt for lossless JPEG. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jlossls.h" /* Private declarations for lossless codec */ - - -#ifdef C_LOSSLESS_SUPPORTED - -METHODDEF(void) -simple_downscale(j_compress_ptr cinfo, - JSAMPROW input_buf, JSAMPROW output_buf, JDIMENSION width) -{ - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - int xindex; - - for (xindex = 0; xindex < width; xindex++) - output_buf[xindex] = (JSAMPLE) RIGHT_SHIFT(GETJSAMPLE(input_buf[xindex]), - cinfo->Al); -} - - -METHODDEF(void) -noscale(j_compress_ptr cinfo, - JSAMPROW input_buf, JSAMPROW output_buf, JDIMENSION width) -{ - MEMCOPY(output_buf, input_buf, width * SIZEOF(JSAMPLE)); - return; -} - - -METHODDEF(void) -scaler_start_pass (j_compress_ptr cinfo) -{ - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - - /* Set scaler function based on Pt */ - if (cinfo->Al) - losslsc->scaler_scale = simple_downscale; - else - losslsc->scaler_scale = noscale; -} - - -GLOBAL(void) -jinit_c_scaler (j_compress_ptr cinfo) -{ - j_lossless_c_ptr losslsc = (j_lossless_c_ptr) cinfo->codec; - - losslsc->scaler_start_pass = scaler_start_pass; -} - -#endif /* C_LOSSLESS_SUPPORTED */ diff --git a/jcshuff.c b/jcshuff.c deleted file mode 100644 index 829f26e2c..000000000 --- a/jcshuff.c +++ /dev/null @@ -1,663 +0,0 @@ -/* - * jcshuff.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains Huffman entropy encoding routines for sequential JPEG. - * - * Much of the complexity here has to do with supporting output suspension. - * If the data destination module demands suspension, we want to be able to - * back up to the start of the current MCU. To do this, we copy state - * variables into local working storage, and update them back to the - * permanent JPEG objects only upon successful completion of an MCU. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jlossy.h" /* Private declarations for lossy codec */ -#include "jchuff.h" /* Declarations shared with jc*huff.c */ - - -/* Expanded entropy encoder object for Huffman encoding. - * - * The savable_state subrecord contains fields that change within an MCU, - * but must not be updated permanently until we complete the MCU. - */ - -typedef struct { - INT32 put_buffer; /* current bit-accumulation buffer */ - int put_bits; /* # of bits now in it */ - int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ -} savable_state; - -/* This macro is to work around compilers with missing or broken - * structure assignment. You'll need to fix this code if you have - * such a compiler and you change MAX_COMPS_IN_SCAN. - */ - -#ifndef NO_STRUCT_ASSIGN -#define ASSIGN_STATE(dest,src) ((dest) = (src)) -#else -#if MAX_COMPS_IN_SCAN == 4 -#define ASSIGN_STATE(dest,src) \ - ((dest).put_buffer = (src).put_buffer, \ - (dest).put_bits = (src).put_bits, \ - (dest).last_dc_val[0] = (src).last_dc_val[0], \ - (dest).last_dc_val[1] = (src).last_dc_val[1], \ - (dest).last_dc_val[2] = (src).last_dc_val[2], \ - (dest).last_dc_val[3] = (src).last_dc_val[3]) -#endif -#endif - - -typedef struct { - savable_state saved; /* Bit buffer & DC state at start of MCU */ - - /* These fields are NOT loaded into local working state. */ - unsigned int restarts_to_go; /* MCUs left in this restart interval */ - int next_restart_num; /* next restart number to write (0-7) */ - - /* Pointers to derived tables (these workspaces have image lifespan) */ - c_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; - c_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; - -#ifdef ENTROPY_OPT_SUPPORTED /* Statistics tables for optimization */ - long * dc_count_ptrs[NUM_HUFF_TBLS]; - long * ac_count_ptrs[NUM_HUFF_TBLS]; -#endif -} shuff_entropy_encoder; - -typedef shuff_entropy_encoder * shuff_entropy_ptr; - -/* Working state while writing an MCU. - * This struct contains all the fields that are needed by subroutines. - */ - -typedef struct { - JOCTET * next_output_byte; /* => next byte to write in buffer */ - size_t free_in_buffer; /* # of byte spaces remaining in buffer */ - savable_state cur; /* Current bit buffer & DC state */ - j_compress_ptr cinfo; /* dump_buffer needs access to this */ -} working_state; - - -/* Forward declarations */ -METHODDEF(boolean) encode_mcu_huff JPP((j_compress_ptr cinfo, - JBLOCKROW *MCU_data)); -METHODDEF(void) finish_pass_huff JPP((j_compress_ptr cinfo)); -#ifdef ENTROPY_OPT_SUPPORTED -METHODDEF(boolean) encode_mcu_gather JPP((j_compress_ptr cinfo, - JBLOCKROW *MCU_data)); -METHODDEF(void) finish_pass_gather JPP((j_compress_ptr cinfo)); -#endif - - -/* - * Initialize for a Huffman-compressed scan. - * If gather_statistics is TRUE, we do not output anything during the scan, - * just count the Huffman symbols used and generate Huffman code tables. - */ - -METHODDEF(void) -start_pass_huff (j_compress_ptr cinfo, boolean gather_statistics) -{ - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - shuff_entropy_ptr entropy = (shuff_entropy_ptr) lossyc->entropy_private; - int ci, dctbl, actbl; - jpeg_component_info * compptr; - - if (gather_statistics) { -#ifdef ENTROPY_OPT_SUPPORTED - lossyc->entropy_encode_mcu = encode_mcu_gather; - lossyc->pub.entropy_finish_pass = finish_pass_gather; -#else - ERREXIT(cinfo, JERR_NOT_COMPILED); -#endif - } else { - lossyc->entropy_encode_mcu = encode_mcu_huff; - lossyc->pub.entropy_finish_pass = finish_pass_huff; - } - - for (ci = 0; ci < cinfo->comps_in_scan; ci++) { - compptr = cinfo->cur_comp_info[ci]; - dctbl = compptr->dc_tbl_no; - actbl = compptr->ac_tbl_no; - if (gather_statistics) { -#ifdef ENTROPY_OPT_SUPPORTED - /* Check for invalid table indexes */ - /* (make_c_derived_tbl does this in the other path) */ - if (dctbl < 0 || dctbl >= NUM_HUFF_TBLS) - ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, dctbl); - if (actbl < 0 || actbl >= NUM_HUFF_TBLS) - ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, actbl); - /* Allocate and zero the statistics tables */ - /* Note that jpeg_gen_optimal_table expects 257 entries in each table! */ - if (entropy->dc_count_ptrs[dctbl] == NULL) - entropy->dc_count_ptrs[dctbl] = (long *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - 257 * SIZEOF(long)); - MEMZERO(entropy->dc_count_ptrs[dctbl], 257 * SIZEOF(long)); - if (entropy->ac_count_ptrs[actbl] == NULL) - entropy->ac_count_ptrs[actbl] = (long *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - 257 * SIZEOF(long)); - MEMZERO(entropy->ac_count_ptrs[actbl], 257 * SIZEOF(long)); -#endif - } else { - /* Compute derived values for Huffman tables */ - /* We may do this more than once for a table, but it's not expensive */ - jpeg_make_c_derived_tbl(cinfo, TRUE, dctbl, - & entropy->dc_derived_tbls[dctbl]); - jpeg_make_c_derived_tbl(cinfo, FALSE, actbl, - & entropy->ac_derived_tbls[actbl]); - } - /* Initialize DC predictions to 0 */ - entropy->saved.last_dc_val[ci] = 0; - } - - /* Initialize bit buffer to empty */ - entropy->saved.put_buffer = 0; - entropy->saved.put_bits = 0; - - /* Initialize restart stuff */ - entropy->restarts_to_go = cinfo->restart_interval; - entropy->next_restart_num = 0; -} - - -/* Outputting bytes to the file */ - -/* Emit a byte, taking 'action' if must suspend. */ -#define emit_byte(state,val,action) \ - { *(state)->next_output_byte++ = (JOCTET) (val); \ - if (--(state)->free_in_buffer == 0) \ - if (! dump_buffer(state)) \ - { action; } } - - -LOCAL(boolean) -dump_buffer (working_state * state) -/* Empty the output buffer; return TRUE if successful, FALSE if must suspend */ -{ - struct jpeg_destination_mgr * dest = state->cinfo->dest; - - if (! (*dest->empty_output_buffer) (state->cinfo)) - return FALSE; - /* After a successful buffer dump, must reset buffer pointers */ - state->next_output_byte = dest->next_output_byte; - state->free_in_buffer = dest->free_in_buffer; - return TRUE; -} - - -/* Outputting bits to the file */ - -/* Only the right 24 bits of put_buffer are used; the valid bits are - * left-justified in this part. At most 16 bits can be passed to emit_bits - * in one call, and we never retain more than 7 bits in put_buffer - * between calls, so 24 bits are sufficient. - */ - -INLINE -LOCAL(boolean) -emit_bits (working_state * state, unsigned int code, int size) -/* Emit some bits; return TRUE if successful, FALSE if must suspend */ -{ - /* This routine is heavily used, so it's worth coding tightly. */ - register INT32 put_buffer = (INT32) code; - register int put_bits = state->cur.put_bits; - - /* if size is 0, caller used an invalid Huffman table entry */ - if (size == 0) - ERREXIT(state->cinfo, JERR_HUFF_MISSING_CODE); - - put_buffer &= (((INT32) 1)<cur.put_buffer; /* and merge with old buffer contents */ - - while (put_bits >= 8) { - int c = (int) ((put_buffer >> 16) & 0xFF); - - emit_byte(state, c, return FALSE); - if (c == 0xFF) { /* need to stuff a zero byte? */ - emit_byte(state, 0, return FALSE); - } - put_buffer <<= 8; - put_bits -= 8; - } - - state->cur.put_buffer = put_buffer; /* update state variables */ - state->cur.put_bits = put_bits; - - return TRUE; -} - - -LOCAL(boolean) -flush_bits (working_state * state) -{ - if (! emit_bits(state, 0x7F, 7)) /* fill any partial byte with ones */ - return FALSE; - state->cur.put_buffer = 0; /* and reset bit-buffer to empty */ - state->cur.put_bits = 0; - return TRUE; -} - - -/* Encode a single block's worth of coefficients */ - -LOCAL(boolean) -encode_one_block (working_state * state, JCOEFPTR block, int last_dc_val, - c_derived_tbl *dctbl, c_derived_tbl *actbl) -{ - register int temp, temp2; - register int nbits; - register int k, r, i; - - /* Encode the DC coefficient difference per section F.1.2.1 */ - - temp = temp2 = block[0] - last_dc_val; - - if (temp < 0) { - temp = -temp; /* temp is abs value of input */ - /* For a negative input, want temp2 = bitwise complement of abs(input) */ - /* This code assumes we are on a two's complement machine */ - temp2--; - } - - /* Find the number of bits needed for the magnitude of the coefficient */ - nbits = 0; - while (temp) { - nbits++; - temp >>= 1; - } - /* Check for out-of-range coefficient values. - * Since we're encoding a difference, the range limit is twice as much. - */ - if (nbits > MAX_COEF_BITS+1) - ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); - - /* Emit the Huffman-coded symbol for the number of bits */ - if (! emit_bits(state, dctbl->ehufco[nbits], dctbl->ehufsi[nbits])) - return FALSE; - - /* Emit that number of bits of the value, if positive, */ - /* or the complement of its magnitude, if negative. */ - if (nbits) /* emit_bits rejects calls with size 0 */ - if (! emit_bits(state, (unsigned int) temp2, nbits)) - return FALSE; - - /* Encode the AC coefficients per section F.1.2.2 */ - - r = 0; /* r = run length of zeros */ - - for (k = 1; k < DCTSIZE2; k++) { - if ((temp = block[jpeg_natural_order[k]]) == 0) { - r++; - } else { - /* if run length > 15, must emit special run-length-16 codes (0xF0) */ - while (r > 15) { - if (! emit_bits(state, actbl->ehufco[0xF0], actbl->ehufsi[0xF0])) - return FALSE; - r -= 16; - } - - temp2 = temp; - if (temp < 0) { - temp = -temp; /* temp is abs value of input */ - /* This code assumes we are on a two's complement machine */ - temp2--; - } - - /* Find the number of bits needed for the magnitude of the coefficient */ - nbits = 1; /* there must be at least one 1 bit */ - while ((temp >>= 1)) - nbits++; - /* Check for out-of-range coefficient values */ - if (nbits > MAX_COEF_BITS) - ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); - - /* Emit Huffman symbol for run length / number of bits */ - i = (r << 4) + nbits; - if (! emit_bits(state, actbl->ehufco[i], actbl->ehufsi[i])) - return FALSE; - - /* Emit that number of bits of the value, if positive, */ - /* or the complement of its magnitude, if negative. */ - if (! emit_bits(state, (unsigned int) temp2, nbits)) - return FALSE; - - r = 0; - } - } - - /* If the last coef(s) were zero, emit an end-of-block code */ - if (r > 0) - if (! emit_bits(state, actbl->ehufco[0], actbl->ehufsi[0])) - return FALSE; - - return TRUE; -} - - -/* - * Emit a restart marker & resynchronize predictions. - */ - -LOCAL(boolean) -emit_restart (working_state * state, int restart_num) -{ - int ci; - - if (! flush_bits(state)) - return FALSE; - - emit_byte(state, 0xFF, return FALSE); - emit_byte(state, JPEG_RST0 + restart_num, return FALSE); - - /* Re-initialize DC predictions to 0 */ - for (ci = 0; ci < state->cinfo->comps_in_scan; ci++) - state->cur.last_dc_val[ci] = 0; - - /* The restart counter is not updated until we successfully write the MCU. */ - - return TRUE; -} - - -/* - * Encode and output one MCU's worth of Huffman-compressed coefficients. - */ - -METHODDEF(boolean) -encode_mcu_huff (j_compress_ptr cinfo, JBLOCKROW *MCU_data) -{ - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - shuff_entropy_ptr entropy = (shuff_entropy_ptr) lossyc->entropy_private; - working_state state; - int blkn, ci; - jpeg_component_info * compptr; - - /* Load up working state */ - state.next_output_byte = cinfo->dest->next_output_byte; - state.free_in_buffer = cinfo->dest->free_in_buffer; - ASSIGN_STATE(state.cur, entropy->saved); - state.cinfo = cinfo; - - /* Emit restart marker if needed */ - if (cinfo->restart_interval) { - if (entropy->restarts_to_go == 0) - if (! emit_restart(&state, entropy->next_restart_num)) - return FALSE; - } - - /* Encode the MCU data blocks */ - for (blkn = 0; blkn < cinfo->data_units_in_MCU; blkn++) { - ci = cinfo->MCU_membership[blkn]; - compptr = cinfo->cur_comp_info[ci]; - if (! encode_one_block(&state, - MCU_data[blkn][0], state.cur.last_dc_val[ci], - entropy->dc_derived_tbls[compptr->dc_tbl_no], - entropy->ac_derived_tbls[compptr->ac_tbl_no])) - return FALSE; - /* Update last_dc_val */ - state.cur.last_dc_val[ci] = MCU_data[blkn][0][0]; - } - - /* Completed MCU, so update state */ - cinfo->dest->next_output_byte = state.next_output_byte; - cinfo->dest->free_in_buffer = state.free_in_buffer; - ASSIGN_STATE(entropy->saved, state.cur); - - /* Update restart-interval state too */ - if (cinfo->restart_interval) { - if (entropy->restarts_to_go == 0) { - entropy->restarts_to_go = cinfo->restart_interval; - entropy->next_restart_num++; - entropy->next_restart_num &= 7; - } - entropy->restarts_to_go--; - } - - return TRUE; -} - - -/* - * Finish up at the end of a Huffman-compressed scan. - */ - -METHODDEF(void) -finish_pass_huff (j_compress_ptr cinfo) -{ - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - shuff_entropy_ptr entropy = (shuff_entropy_ptr) lossyc->entropy_private; - working_state state; - - /* Load up working state ... flush_bits needs it */ - state.next_output_byte = cinfo->dest->next_output_byte; - state.free_in_buffer = cinfo->dest->free_in_buffer; - ASSIGN_STATE(state.cur, entropy->saved); - state.cinfo = cinfo; - - /* Flush out the last data */ - if (! flush_bits(&state)) - ERREXIT(cinfo, JERR_CANT_SUSPEND); - - /* Update state */ - cinfo->dest->next_output_byte = state.next_output_byte; - cinfo->dest->free_in_buffer = state.free_in_buffer; - ASSIGN_STATE(entropy->saved, state.cur); -} - - -/* - * Huffman coding optimization. - * - * We first scan the supplied data and count the number of uses of each symbol - * that is to be Huffman-coded. (This process MUST agree with the code above.) - * Then we build a Huffman coding tree for the observed counts. - * Symbols which are not needed at all for the particular image are not - * assigned any code, which saves space in the DHT marker as well as in - * the compressed data. - */ - -#ifdef ENTROPY_OPT_SUPPORTED - - -/* Process a single block's worth of coefficients */ - -LOCAL(void) -htest_one_block (j_compress_ptr cinfo, JCOEFPTR block, int last_dc_val, - long dc_counts[], long ac_counts[]) -{ - register int temp; - register int nbits; - register int k, r; - - /* Encode the DC coefficient difference per section F.1.2.1 */ - - temp = block[0] - last_dc_val; - if (temp < 0) - temp = -temp; - - /* Find the number of bits needed for the magnitude of the coefficient */ - nbits = 0; - while (temp) { - nbits++; - temp >>= 1; - } - /* Check for out-of-range coefficient values. - * Since we're encoding a difference, the range limit is twice as much. - */ - if (nbits > MAX_COEF_BITS+1) - ERREXIT(cinfo, JERR_BAD_DCT_COEF); - - /* Count the Huffman symbol for the number of bits */ - dc_counts[nbits]++; - - /* Encode the AC coefficients per section F.1.2.2 */ - - r = 0; /* r = run length of zeros */ - - for (k = 1; k < DCTSIZE2; k++) { - if ((temp = block[jpeg_natural_order[k]]) == 0) { - r++; - } else { - /* if run length > 15, must emit special run-length-16 codes (0xF0) */ - while (r > 15) { - ac_counts[0xF0]++; - r -= 16; - } - - /* Find the number of bits needed for the magnitude of the coefficient */ - if (temp < 0) - temp = -temp; - - /* Find the number of bits needed for the magnitude of the coefficient */ - nbits = 1; /* there must be at least one 1 bit */ - while ((temp >>= 1)) - nbits++; - /* Check for out-of-range coefficient values */ - if (nbits > MAX_COEF_BITS) - ERREXIT(cinfo, JERR_BAD_DCT_COEF); - - /* Count Huffman symbol for run length / number of bits */ - ac_counts[(r << 4) + nbits]++; - - r = 0; - } - } - - /* If the last coef(s) were zero, emit an end-of-block code */ - if (r > 0) - ac_counts[0]++; -} - - -/* - * Trial-encode one MCU's worth of Huffman-compressed coefficients. - * No data is actually output, so no suspension return is possible. - */ - -METHODDEF(boolean) -encode_mcu_gather (j_compress_ptr cinfo, JBLOCKROW *MCU_data) -{ - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - shuff_entropy_ptr entropy = (shuff_entropy_ptr) lossyc->entropy_private; - int blkn, ci; - jpeg_component_info * compptr; - - /* Take care of restart intervals if needed */ - if (cinfo->restart_interval) { - if (entropy->restarts_to_go == 0) { - /* Re-initialize DC predictions to 0 */ - for (ci = 0; ci < cinfo->comps_in_scan; ci++) - entropy->saved.last_dc_val[ci] = 0; - /* Update restart state */ - entropy->restarts_to_go = cinfo->restart_interval; - } - entropy->restarts_to_go--; - } - - for (blkn = 0; blkn < cinfo->data_units_in_MCU; blkn++) { - ci = cinfo->MCU_membership[blkn]; - compptr = cinfo->cur_comp_info[ci]; - htest_one_block(cinfo, MCU_data[blkn][0], entropy->saved.last_dc_val[ci], - entropy->dc_count_ptrs[compptr->dc_tbl_no], - entropy->ac_count_ptrs[compptr->ac_tbl_no]); - entropy->saved.last_dc_val[ci] = MCU_data[blkn][0][0]; - } - - return TRUE; -} - - -/* - * Finish up a statistics-gathering pass and create the new Huffman tables. - */ - -METHODDEF(void) -finish_pass_gather (j_compress_ptr cinfo) -{ - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - shuff_entropy_ptr entropy = (shuff_entropy_ptr) lossyc->entropy_private; - int ci, dctbl, actbl; - jpeg_component_info * compptr; - JHUFF_TBL **htblptr; - boolean did_dc[NUM_HUFF_TBLS]; - boolean did_ac[NUM_HUFF_TBLS]; - - /* It's important not to apply jpeg_gen_optimal_table more than once - * per table, because it clobbers the input frequency counts! - */ - MEMZERO(did_dc, SIZEOF(did_dc)); - MEMZERO(did_ac, SIZEOF(did_ac)); - - for (ci = 0; ci < cinfo->comps_in_scan; ci++) { - compptr = cinfo->cur_comp_info[ci]; - dctbl = compptr->dc_tbl_no; - actbl = compptr->ac_tbl_no; - if (! did_dc[dctbl]) { - htblptr = & cinfo->dc_huff_tbl_ptrs[dctbl]; - if (*htblptr == NULL) - *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); - jpeg_gen_optimal_table(cinfo, *htblptr, entropy->dc_count_ptrs[dctbl]); - did_dc[dctbl] = TRUE; - } - if (! did_ac[actbl]) { - htblptr = & cinfo->ac_huff_tbl_ptrs[actbl]; - if (*htblptr == NULL) - *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); - jpeg_gen_optimal_table(cinfo, *htblptr, entropy->ac_count_ptrs[actbl]); - did_ac[actbl] = TRUE; - } - } -} - - -#endif /* ENTROPY_OPT_SUPPORTED */ - - -METHODDEF(boolean) -need_optimization_pass (j_compress_ptr cinfo) -{ - return TRUE; -} - - -/* - * Module initialization routine for Huffman entropy encoding. - */ - -GLOBAL(void) -jinit_shuff_encoder (j_compress_ptr cinfo) -{ - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - shuff_entropy_ptr entropy; - int i; - - entropy = (shuff_entropy_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(shuff_entropy_encoder)); - lossyc->entropy_private = (struct jpeg_entropy_encoder *) entropy; - lossyc->pub.entropy_start_pass = start_pass_huff; - lossyc->pub.need_optimization_pass = need_optimization_pass; - - /* Mark tables unallocated */ - for (i = 0; i < NUM_HUFF_TBLS; i++) { - entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL; -#ifdef ENTROPY_OPT_SUPPORTED - entropy->dc_count_ptrs[i] = entropy->ac_count_ptrs[i] = NULL; -#endif - } -} diff --git a/jctrans.c b/jctrans.c index ee57e330e..8cac98392 100644 --- a/jctrans.c +++ b/jctrans.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1995-1998, Thomas G. Lane. * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains library routines for transcoding compression, @@ -15,14 +15,11 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossy.h" /* Private declarations for lossy codec */ /* Forward declarations */ LOCAL(void) transencode_master_selection JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays)); -LOCAL(void) transencode_codec - JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays)); LOCAL(void) transencode_coef_controller JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays)); @@ -42,6 +39,9 @@ LOCAL(void) transencode_coef_controller GLOBAL(void) jpeg_write_coefficients (j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays) { + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); + if (cinfo->global_state != CSTATE_START) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); /* Mark all tables to be written */ @@ -73,6 +73,9 @@ jpeg_copy_critical_parameters (j_decompress_ptr srcinfo, JQUANT_TBL *c_quant, *slot_quant; int tblno, ci, coefi; + if (srcinfo->master->lossless) + ERREXIT(dstinfo, JERR_NOTIMPL); + /* Safety check to ensure start_compress not called yet. */ if (dstinfo->global_state != CSTATE_START) ERREXIT1(dstinfo, JERR_BAD_STATE, dstinfo->global_state); @@ -163,7 +166,6 @@ LOCAL(void) transencode_master_selection (j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays) { - cinfo->data_unit = DCTSIZE; /* Although we don't actually use input_components for transcoding, * jcmaster.c's initial_setup will complain if input_components is 0. */ @@ -171,8 +173,22 @@ transencode_master_selection (j_compress_ptr cinfo, /* Initialize master control (includes parameter checking/processing) */ jinit_c_master_control(cinfo, TRUE /* transcode only */); - /* We need a special compression codec. */ - transencode_codec(cinfo, coef_arrays); + /* Entropy encoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef C_PROGRESSIVE_SUPPORTED + jinit_phuff_encoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_encoder(cinfo); + } + + /* We need a special coefficient buffer controller. */ + transencode_coef_controller(cinfo, coef_arrays); jinit_marker_writer(cinfo); @@ -198,6 +214,8 @@ transencode_master_selection (j_compress_ptr cinfo, /* Private buffer controller object */ typedef struct { + struct jpeg_c_coef_controller pub; /* public fields */ + JDIMENSION iMCU_row_num; /* iMCU row # within image */ JDIMENSION mcu_ctr; /* counts MCUs processed in current row */ int MCU_vert_offset; /* counts MCU rows within iMCU row */ @@ -207,18 +225,17 @@ typedef struct { jvirt_barray_ptr * whole_image; /* Workspace for constructing dummy blocks at right/bottom edges. */ - JBLOCKROW dummy_buffer[C_MAX_DATA_UNITS_IN_MCU]; -} c_coef_controller; + JBLOCKROW dummy_buffer[C_MAX_BLOCKS_IN_MCU]; +} my_coef_controller; -typedef c_coef_controller * c_coef_ptr; +typedef my_coef_controller * my_coef_ptr; LOCAL(void) start_iMCU_row (j_compress_ptr cinfo) /* Reset within-iMCU-row counters for a new row */ { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; /* In an interleaved scan, an MCU row is the same as an iMCU row. * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. @@ -245,8 +262,7 @@ start_iMCU_row (j_compress_ptr cinfo) METHODDEF(void) start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; if (pass_mode != JBUF_CRANK_DEST) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); @@ -269,15 +285,14 @@ start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) METHODDEF(boolean) compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; JDIMENSION MCU_col_num; /* index of current MCU within row */ JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; int blkn, ci, xindex, yindex, yoffset, blockcnt; JDIMENSION start_col; JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN]; - JBLOCKROW MCU_buffer[C_MAX_DATA_UNITS_IN_MCU]; + JBLOCKROW MCU_buffer[C_MAX_BLOCKS_IN_MCU]; JBLOCKROW buffer_ptr; jpeg_component_info *compptr; @@ -327,7 +342,7 @@ compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) } } /* Try to write the MCU. */ - if (! (*lossyc->entropy_encode_mcu) (cinfo, MCU_buffer)) { + if (! (*cinfo->entropy->encode_mcu) (cinfo, MCU_buffer)) { /* Suspension forced; update state counters and exit */ coef->MCU_vert_offset = yoffset; coef->mcu_ctr = MCU_col_num; @@ -348,7 +363,7 @@ compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) * Initialize coefficient buffer controller. * * Each passed coefficient array must be the right size for that - * coefficient: width_in_data_units wide and height_in_data_units high, + * coefficient: width_in_blocks wide and height_in_blocks high, * with unitheight at least v_samp_factor. */ @@ -356,15 +371,16 @@ LOCAL(void) transencode_coef_controller (j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays) { - j_lossy_c_ptr lossyc = (j_lossy_c_ptr) cinfo->codec; - c_coef_ptr coef; + my_coef_ptr coef; JBLOCKROW buffer; int i; - coef = (c_coef_ptr) + coef = (my_coef_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(c_coef_controller)); - lossyc->coef_private = (struct jpeg_c_coef_controller *) coef; + SIZEOF(my_coef_controller)); + cinfo->coef = (struct jpeg_c_coef_controller *) coef; + coef->pub.start_pass = start_pass_coef; + coef->pub.compress_data = compress_output; /* Save pointer to virtual arrays */ coef->whole_image = coef_arrays; @@ -372,51 +388,9 @@ transencode_coef_controller (j_compress_ptr cinfo, /* Allocate and pre-zero space for dummy DCT blocks. */ buffer = (JBLOCKROW) (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, - C_MAX_DATA_UNITS_IN_MCU * SIZEOF(JBLOCK)); - jzero_far((void FAR *) buffer, C_MAX_DATA_UNITS_IN_MCU * SIZEOF(JBLOCK)); - for (i = 0; i < C_MAX_DATA_UNITS_IN_MCU; i++) { + C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + jzero_far((void FAR *) buffer, C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + for (i = 0; i < C_MAX_BLOCKS_IN_MCU; i++) { coef->dummy_buffer[i] = buffer + i; } } - - -/* - * Initialize the transencoer codec. - * This is called only once, during master selection. - */ - -LOCAL(void) -transencode_codec (j_compress_ptr cinfo, - jvirt_barray_ptr * coef_arrays) -{ - j_lossy_c_ptr lossyc; - - /* Create subobject in permanent pool */ - lossyc = (j_lossy_c_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, - SIZEOF(jpeg_lossy_c_codec)); - cinfo->codec = (struct jpeg_c_codec *) lossyc; - - /* Initialize sub-modules */ - - /* Entropy encoding: either Huffman or arithmetic coding. */ - if (cinfo->arith_code) { - ERREXIT(cinfo, JERR_ARITH_NOTIMPL); - } else { - if (cinfo->process == JPROC_PROGRESSIVE) { -#ifdef C_PROGRESSIVE_SUPPORTED - jinit_phuff_encoder(cinfo); -#else - ERREXIT(cinfo, JERR_NOT_COMPILED); -#endif - } else - jinit_shuff_encoder(cinfo); - } - - /* We need a special coefficient buffer controller. */ - transencode_coef_controller(cinfo, coef_arrays); - - /* Initialize method pointers */ - lossyc->pub.start_pass = start_pass_coef; - lossyc->pub.compress_data = compress_output; -} diff --git a/jdapimin.c b/jdapimin.c index d7e2edfde..37eefffd7 100644 --- a/jdapimin.c +++ b/jdapimin.c @@ -5,6 +5,7 @@ * Copyright (C) 1994-1998, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains application interface code for the decompression half @@ -21,6 +22,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" +#include "jdmaster.h" /* @@ -82,6 +84,14 @@ jpeg_CreateDecompress (j_decompress_ptr cinfo, int version, size_t structsize) /* OK, I'm ready */ cinfo->global_state = DSTATE_START; + + /* The master struct is used to store extension parameters, so we allocate it + * here. + */ + cinfo->master = (struct jpeg_decomp_master *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_decomp_master)); + MEMZERO(cinfo->master, SIZEOF(my_decomp_master)); } @@ -151,14 +161,11 @@ default_decompress_parms (j_decompress_ptr cinfo) else if (cid0 == 82 && cid1 == 71 && cid2 == 66) cinfo->jpeg_color_space = JCS_RGB; /* ASCII 'R', 'G', 'B' */ else { - if (cinfo->process == JPROC_LOSSLESS) { - TRACEMS3(cinfo, 1, JTRC_UNKNOWN_LOSSLESS_IDS, cid0, cid1, cid2); + TRACEMS3(cinfo, 1, JTRC_UNKNOWN_IDS, cid0, cid1, cid2); + if (cinfo->master->lossless) cinfo->jpeg_color_space = JCS_RGB; /* assume it's RGB */ - } - else { /* Lossy processes */ - TRACEMS3(cinfo, 1, JTRC_UNKNOWN_LOSSY_IDS, cid0, cid1, cid2); + else cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ - } } } /* Always guess RGB is proper output colorspace. */ diff --git a/jdapistd.c b/jdapistd.c index 6df95e52a..1ae681518 100644 --- a/jdapistd.c +++ b/jdapistd.c @@ -2,9 +2,9 @@ * jdapistd.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. + * Copyright (C) 1994-1996, Thomas G. Lane. * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains application interface code for the decompression half @@ -189,6 +189,9 @@ jpeg_read_raw_data (j_decompress_ptr cinfo, JSAMPIMAGE data, { JDIMENSION lines_per_iMCU_row; + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); + if (cinfo->global_state != DSTATE_RAW_OK) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); if (cinfo->output_scanline >= cinfo->output_height) { @@ -204,12 +207,12 @@ jpeg_read_raw_data (j_decompress_ptr cinfo, JSAMPIMAGE data, } /* Verify that at least one iMCU row can be returned. */ - lines_per_iMCU_row = cinfo->max_v_samp_factor * cinfo->min_codec_data_unit; + lines_per_iMCU_row = cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size; if (max_lines < lines_per_iMCU_row) ERREXIT(cinfo, JERR_BUFFER_SIZE); /* Decompress directly into user's buffer. */ - if (! (*cinfo->codec->decompress_data) (cinfo, data)) + if (! (*cinfo->coef->decompress_data) (cinfo, data)) return 0; /* suspension forced, can do nothing more */ /* OK, we processed one iMCU row. */ diff --git a/jdcoefct.c b/jdcoefct.c index 390d844d1..bc4b63681 100644 --- a/jdcoefct.c +++ b/jdcoefct.c @@ -2,9 +2,9 @@ * jdcoefct.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. + * Copyright (C) 1994-1997, Thomas G. Lane. * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains the coefficient buffer controller for decompression. @@ -19,7 +19,6 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossy.h" /* Block smoothing is only applicable for progressive JPEG, so: */ #ifndef D_PROGRESSIVE_SUPPORTED @@ -29,6 +28,8 @@ /* Private buffer controller object */ typedef struct { + struct jpeg_d_coef_controller pub; /* public fields */ + /* These variables keep track of the current location of the input side. */ /* cinfo->input_iMCU_row is also used for this. */ JDIMENSION MCU_ctr; /* counts MCUs processed in current row */ @@ -38,7 +39,7 @@ typedef struct { /* 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_DATA_UNITS_IN_MCU coefficient blocks, + * 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 @@ -46,7 +47,7 @@ typedef struct { * In multi-pass modes, this array points to the current MCU's blocks * within the virtual arrays; it is used only by the input side. */ - JBLOCKROW MCU_buffer[D_MAX_DATA_UNITS_IN_MCU]; + JBLOCKROW MCU_buffer[D_MAX_BLOCKS_IN_MCU]; #ifdef D_MULTISCAN_FILES_SUPPORTED /* In multi-pass modes, we need a virtual block array for each component. */ @@ -58,9 +59,9 @@ typedef struct { int * coef_bits_latch; #define SAVED_COEFS 6 /* we save coef_bits[0..5] */ #endif -} d_coef_controller; +} my_coef_controller; -typedef d_coef_controller * d_coef_ptr; +typedef my_coef_controller * my_coef_ptr; /* Forward declarations */ METHODDEF(int) decompress_onepass @@ -80,8 +81,7 @@ LOCAL(void) start_iMCU_row (j_decompress_ptr cinfo) /* Reset within-iMCU-row counters for a new row (input side) */ { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - d_coef_ptr coef = (d_coef_ptr) lossyd->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; /* In an interleaved scan, an MCU row is the same as an iMCU row. * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. @@ -121,15 +121,14 @@ METHODDEF(void) start_output_pass (j_decompress_ptr cinfo) { #ifdef BLOCK_SMOOTHING_SUPPORTED - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - d_coef_ptr coef = (d_coef_ptr) lossyd->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; /* If multipass, check to see whether to use block smoothing on this pass */ - if (lossyd->coef_arrays != NULL) { + if (coef->pub.coef_arrays != NULL) { if (cinfo->do_block_smoothing && smoothing_ok(cinfo)) - lossyd->pub.decompress_data = decompress_smooth_data; + coef->pub.decompress_data = decompress_smooth_data; else - lossyd->pub.decompress_data = decompress_data; + coef->pub.decompress_data = decompress_data; } #endif cinfo->output_iMCU_row = 0; @@ -149,8 +148,7 @@ start_output_pass (j_decompress_ptr cinfo) METHODDEF(int) decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - d_coef_ptr coef = (d_coef_ptr) lossyd->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; JDIMENSION MCU_col_num; /* index of current MCU within row */ JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; @@ -167,8 +165,8 @@ decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) MCU_col_num++) { /* Try to fetch an MCU. Entropy decoder expects buffer to be zeroed. */ jzero_far((void FAR *) coef->MCU_buffer[0], - (size_t) (cinfo->data_units_in_MCU * SIZEOF(JBLOCK))); - if (! (*lossyd->entropy_decode_mcu) (cinfo, coef->MCU_buffer)) { + (size_t) (cinfo->blocks_in_MCU * SIZEOF(JBLOCK))); + if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) { /* Suspension forced; update state counters and exit */ coef->MCU_vert_offset = yoffset; coef->MCU_ctr = MCU_col_num; @@ -184,14 +182,14 @@ decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) compptr = cinfo->cur_comp_info[ci]; /* Don't bother to IDCT an uninteresting component. */ if (! compptr->component_needed) { - blkn += compptr->MCU_data_units; + blkn += compptr->MCU_blocks; continue; } - inverse_DCT = lossyd->inverse_DCT[compptr->component_index]; + inverse_DCT = cinfo->idct->inverse_DCT[compptr->component_index]; useful_width = (MCU_col_num < last_MCU_col) ? compptr->MCU_width : compptr->last_col_width; output_ptr = output_buf[compptr->component_index] + - yoffset * compptr->codec_data_unit; + yoffset * compptr->DCT_scaled_size; start_col = MCU_col_num * compptr->MCU_sample_width; for (yindex = 0; yindex < compptr->MCU_height; yindex++) { if (cinfo->input_iMCU_row < last_iMCU_row || @@ -201,11 +199,11 @@ decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) (*inverse_DCT) (cinfo, compptr, (JCOEFPTR) coef->MCU_buffer[blkn+xindex], output_ptr, output_col); - output_col += compptr->codec_data_unit; + output_col += compptr->DCT_scaled_size; } } blkn += compptr->MCU_width; - output_ptr += compptr->codec_data_unit; + output_ptr += compptr->DCT_scaled_size; } } } @@ -247,8 +245,7 @@ dummy_consume_data (j_decompress_ptr cinfo) METHODDEF(int) consume_data (j_decompress_ptr cinfo) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - d_coef_ptr coef = (d_coef_ptr) lossyd->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; JDIMENSION MCU_col_num; /* index of current MCU within row */ int blkn, ci, xindex, yindex, yoffset; JDIMENSION start_col; @@ -287,7 +284,7 @@ consume_data (j_decompress_ptr cinfo) } } /* Try to fetch the MCU. */ - if (! (*lossyd->entropy_decode_mcu) (cinfo, coef->MCU_buffer)) { + if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) { /* Suspension forced; update state counters and exit */ coef->MCU_vert_offset = yoffset; coef->MCU_ctr = MCU_col_num; @@ -319,8 +316,7 @@ consume_data (j_decompress_ptr cinfo) METHODDEF(int) decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - d_coef_ptr coef = (d_coef_ptr) lossyd->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; JDIMENSION block_num; int ci, block_row, block_rows; @@ -355,22 +351,22 @@ decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) block_rows = compptr->v_samp_factor; else { /* NB: can't use last_row_height here; it is input-side-dependent! */ - block_rows = (int) (compptr->height_in_data_units % compptr->v_samp_factor); + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); if (block_rows == 0) block_rows = compptr->v_samp_factor; } - inverse_DCT = lossyd->inverse_DCT[ci]; + inverse_DCT = cinfo->idct->inverse_DCT[ci]; output_ptr = output_buf[ci]; /* Loop over all DCT blocks to be processed. */ for (block_row = 0; block_row < block_rows; block_row++) { buffer_ptr = buffer[block_row]; output_col = 0; - for (block_num = 0; block_num < compptr->width_in_data_units; block_num++) { + for (block_num = 0; block_num < compptr->width_in_blocks; block_num++) { (*inverse_DCT) (cinfo, compptr, (JCOEFPTR) buffer_ptr, output_ptr, output_col); buffer_ptr++; - output_col += compptr->codec_data_unit; + output_col += compptr->DCT_scaled_size; } - output_ptr += compptr->codec_data_unit; + output_ptr += compptr->DCT_scaled_size; } } @@ -410,8 +406,7 @@ decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) LOCAL(boolean) smoothing_ok (j_decompress_ptr cinfo) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - d_coef_ptr coef = (d_coef_ptr) lossyd->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; boolean smoothing_useful = FALSE; int ci, coefi; jpeg_component_info *compptr; @@ -419,7 +414,7 @@ smoothing_ok (j_decompress_ptr cinfo) int * coef_bits; int * coef_bits_latch; - if (! cinfo->process == JPROC_PROGRESSIVE || cinfo->coef_bits == NULL) + if (! cinfo->progressive_mode || cinfo->coef_bits == NULL) return FALSE; /* Allocate latch area if not already done */ @@ -467,8 +462,7 @@ smoothing_ok (j_decompress_ptr cinfo) METHODDEF(int) decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - d_coef_ptr coef = (d_coef_ptr) lossyd->coef_private; + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; JDIMENSION block_num, last_block_column; int ci, block_row, block_rows, access_rows; @@ -516,7 +510,7 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) last_row = FALSE; } else { /* NB: can't use last_row_height here; it is input-side-dependent! */ - block_rows = (int) (compptr->height_in_data_units % compptr->v_samp_factor); + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); if (block_rows == 0) block_rows = compptr->v_samp_factor; access_rows = block_rows; /* this iMCU row only */ last_row = TRUE; @@ -545,7 +539,7 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) Q20 = quanttbl->quantval[Q20_POS]; Q11 = quanttbl->quantval[Q11_POS]; Q02 = quanttbl->quantval[Q02_POS]; - inverse_DCT = lossyd->inverse_DCT[ci]; + inverse_DCT = cinfo->idct->inverse_DCT[ci]; output_ptr = output_buf[ci]; /* Loop over all DCT blocks to be processed. */ for (block_row = 0; block_row < block_rows; block_row++) { @@ -565,7 +559,7 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) DC4 = DC5 = DC6 = (int) buffer_ptr[0][0]; DC7 = DC8 = DC9 = (int) next_block_row[0][0]; output_col = 0; - last_block_column = compptr->width_in_data_units - 1; + last_block_column = compptr->width_in_blocks - 1; for (block_num = 0; block_num <= last_block_column; block_num++) { /* Fetch current DCT block into workspace so we can modify it. */ jcopy_block_row(buffer_ptr, (JBLOCKROW) workspace, (JDIMENSION) 1); @@ -662,9 +656,9 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) DC4 = DC5; DC5 = DC6; DC7 = DC8; DC8 = DC9; buffer_ptr++, prev_block_row++, next_block_row++; - output_col += compptr->codec_data_unit; + output_col += compptr->DCT_scaled_size; } - output_ptr += compptr->codec_data_unit; + output_ptr += compptr->DCT_scaled_size; } } @@ -683,15 +677,14 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) GLOBAL(void) jinit_d_coef_controller (j_decompress_ptr cinfo, boolean need_full_buffer) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - d_coef_ptr coef; + my_coef_ptr coef; - coef = (d_coef_ptr) + coef = (my_coef_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(d_coef_controller)); - lossyd->coef_private = (void *) coef; - lossyd->coef_start_input_pass = start_input_pass; - lossyd->coef_start_output_pass = start_output_pass; + SIZEOF(my_coef_controller)); + cinfo->coef = (struct jpeg_d_coef_controller *) coef; + coef->pub.start_input_pass = start_input_pass; + coef->pub.start_output_pass = start_output_pass; #ifdef BLOCK_SMOOTHING_SUPPORTED coef->coef_bits_latch = NULL; #endif @@ -710,20 +703,20 @@ jinit_d_coef_controller (j_decompress_ptr cinfo, boolean need_full_buffer) access_rows = compptr->v_samp_factor; #ifdef BLOCK_SMOOTHING_SUPPORTED /* If block smoothing could be used, need a bigger window */ - if (cinfo->process == JPROC_PROGRESSIVE) + if (cinfo->progressive_mode) access_rows *= 3; #endif coef->whole_image[ci] = (*cinfo->mem->request_virt_barray) ((j_common_ptr) cinfo, JPOOL_IMAGE, TRUE, - (JDIMENSION) jround_up((long) compptr->width_in_data_units, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, (long) compptr->h_samp_factor), - (JDIMENSION) jround_up((long) compptr->height_in_data_units, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, (long) compptr->v_samp_factor), (JDIMENSION) access_rows); } - lossyd->pub.consume_data = consume_data; - lossyd->pub.decompress_data = decompress_data; - lossyd->coef_arrays = coef->whole_image; /* link to virtual arrays */ + coef->pub.consume_data = consume_data; + coef->pub.decompress_data = decompress_data; + coef->pub.coef_arrays = coef->whole_image; /* link to virtual arrays */ #else ERREXIT(cinfo, JERR_NOT_COMPILED); #endif @@ -734,12 +727,12 @@ jinit_d_coef_controller (j_decompress_ptr cinfo, boolean need_full_buffer) buffer = (JBLOCKROW) (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, - D_MAX_DATA_UNITS_IN_MCU * SIZEOF(JBLOCK)); - for (i = 0; i < D_MAX_DATA_UNITS_IN_MCU; i++) { + D_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + for (i = 0; i < D_MAX_BLOCKS_IN_MCU; i++) { coef->MCU_buffer[i] = buffer + i; } - lossyd->pub.consume_data = dummy_consume_data; - lossyd->pub.decompress_data = decompress_onepass; - lossyd->coef_arrays = NULL; /* flag for no virtual arrays */ + coef->pub.consume_data = dummy_consume_data; + coef->pub.decompress_data = decompress_onepass; + coef->pub.coef_arrays = NULL; /* flag for no virtual arrays */ } } diff --git a/jdcolor.c b/jdcolor.c index 6c04dfe8a..cc52729dd 100644 --- a/jdcolor.c +++ b/jdcolor.c @@ -1,8 +1,10 @@ /* * jdcolor.c * + * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. + * Lossless JPEG Modifications: + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains output colorspace conversion routines. @@ -340,10 +342,15 @@ jinit_color_deconverter (j_decompress_ptr cinfo) /* 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. + * NOTE: We do not allow any lossy color conversion algorithms in lossless + * mode. */ switch (cinfo->out_color_space) { case JCS_GRAYSCALE: + if (cinfo->master->lossless && + cinfo->jpeg_color_space != cinfo->out_color_space) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); cinfo->out_color_components = 1; if (cinfo->jpeg_color_space == JCS_GRAYSCALE || cinfo->jpeg_color_space == JCS_YCbCr) { @@ -356,6 +363,9 @@ jinit_color_deconverter (j_decompress_ptr cinfo) break; case JCS_RGB: + if (cinfo->master->lossless && + cinfo->jpeg_color_space != JCS_RGB) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); cinfo->out_color_components = RGB_PIXELSIZE; if (cinfo->jpeg_color_space == JCS_YCbCr) { cconvert->pub.color_convert = ycc_rgb_convert; @@ -369,6 +379,9 @@ jinit_color_deconverter (j_decompress_ptr cinfo) break; case JCS_CMYK: + if (cinfo->master->lossless && + cinfo->jpeg_color_space != cinfo->out_color_space) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); cinfo->out_color_components = 4; if (cinfo->jpeg_color_space == JCS_YCCK) { cconvert->pub.color_convert = ycck_cmyk_convert; diff --git a/jddctmgr.c b/jddctmgr.c index 3b9bcea16..bbf8d0e92 100644 --- a/jddctmgr.c +++ b/jddctmgr.c @@ -1,10 +1,8 @@ /* * jddctmgr.c * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * * This file contains the inverse-DCT management logic. @@ -20,7 +18,6 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossy.h" /* Private declarations for lossy subsystem */ #include "jdct.h" /* Private declarations for DCT subsystem */ @@ -44,15 +41,17 @@ /* Private subobject for this module */ typedef struct { + struct jpeg_inverse_dct pub; /* public fields */ + /* This array contains the IDCT method code that each multiplier table * is currently set up for, or -1 if it's not yet set up. * The actual multiplier tables are pointed to by dct_table in the * per-component comp_info structures. */ int cur_method[MAX_COMPONENTS]; -} idct_controller; +} my_idct_controller; -typedef idct_controller * idct_ptr; +typedef my_idct_controller * my_idct_ptr; /* Allocated multiplier tables: big enough for any supported variant */ @@ -89,8 +88,7 @@ typedef union { METHODDEF(void) start_pass (j_decompress_ptr cinfo) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - idct_ptr idct = (idct_ptr) lossyd->idct_private; + my_idct_ptr idct = (my_idct_ptr) cinfo->idct; int ci, i; jpeg_component_info *compptr; int method = 0; @@ -100,7 +98,7 @@ start_pass (j_decompress_ptr cinfo) for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { /* Select the proper IDCT routine for this component's scaling */ - switch (compptr->codec_data_unit) { + switch (compptr->DCT_scaled_size) { #ifdef IDCT_SCALING_SUPPORTED case 1: method_ptr = jpeg_idct_1x1; @@ -141,10 +139,10 @@ start_pass (j_decompress_ptr cinfo) } break; default: - ERREXIT1(cinfo, JERR_BAD_DCTSIZE, compptr->codec_data_unit); + ERREXIT1(cinfo, JERR_BAD_DCTSIZE, compptr->DCT_scaled_size); break; } - lossyd->inverse_DCT[ci] = method_ptr; + 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 @@ -248,16 +246,15 @@ start_pass (j_decompress_ptr cinfo) GLOBAL(void) jinit_inverse_dct (j_decompress_ptr cinfo) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - idct_ptr idct; + my_idct_ptr idct; int ci; jpeg_component_info *compptr; - idct = (idct_ptr) + idct = (my_idct_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(idct_controller)); - lossyd->idct_private = (void *) idct; - lossyd->idct_start_pass = start_pass; + SIZEOF(my_idct_controller)); + cinfo->idct = (struct jpeg_inverse_dct *) idct; + idct->pub.start_pass = start_pass; for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { diff --git a/jddiffct.c b/jddiffct.c index ba7fde26f..7f8fbb66b 100644 --- a/jddiffct.c +++ b/jddiffct.c @@ -2,9 +2,10 @@ * jddiffct.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. + * Copyright (C) 1994-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains the [un]difference buffer controller for decompression. @@ -20,7 +21,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossls.h" +#include "jlossls.h" /* Private declarations for lossless codec */ #ifdef D_LOSSLESS_SUPPORTED @@ -28,10 +29,13 @@ /* Private buffer controller object */ typedef struct { + struct jpeg_d_coef_controller pub; /* public fields */ + /* These variables keep track of the current location of the input side. */ /* cinfo->input_iMCU_row is also used for this. */ JDIMENSION MCU_ctr; /* counts MCUs processed in current row */ - unsigned int restart_rows_to_go; /* MCU-rows left in this restart interval */ + unsigned int restart_rows_to_go; /* MCU rows left in this restart + interval */ unsigned int MCU_vert_offset; /* counts MCU rows within iMCU row */ unsigned int MCU_rows_per_iMCU_row; /* number of such rows needed */ @@ -44,9 +48,9 @@ typedef struct { /* In multi-pass modes, we need a virtual sample array for each component. */ jvirt_sarray_ptr whole_image[MAX_COMPONENTS]; #endif -} d_diff_controller; +} my_diff_controller; -typedef d_diff_controller * d_diff_ptr; +typedef my_diff_controller * my_diff_ptr; /* Forward declarations */ METHODDEF(int) decompress_data @@ -61,8 +65,7 @@ LOCAL(void) start_iMCU_row (j_decompress_ptr cinfo) /* Reset within-iMCU-row counters for a new row (input side) */ { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - d_diff_ptr diff = (d_diff_ptr) losslsd->diff_private; + my_diff_ptr diff = (my_diff_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. @@ -89,11 +92,17 @@ start_iMCU_row (j_decompress_ptr cinfo) METHODDEF(void) start_input_pass (j_decompress_ptr cinfo) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - d_diff_ptr diff = (d_diff_ptr) losslsd->diff_private; + my_diff_ptr diff = (my_diff_ptr) cinfo->coef; + + /* Because it is hitching a ride on the jpeg_inverse_dct struct, + * start_pass_lossless() will be called at the start of the output pass. + * This ensures that it will be called at the start of the input pass as + * well. + */ + (*cinfo->idct->start_pass) (cinfo); /* Check that the restart interval is an integer multiple of the number - * of MCU in an MCU-row. + * of MCUs in an MCU row. */ if (cinfo->restart_interval % cinfo->MCUs_per_row != 0) ERREXIT2(cinfo, JERR_BAD_RESTART, @@ -115,13 +124,12 @@ start_input_pass (j_decompress_ptr cinfo) METHODDEF(boolean) process_restart (j_decompress_ptr cinfo) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - d_diff_ptr diff = (d_diff_ptr) losslsd->diff_private; + my_diff_ptr diff = (my_diff_ptr) cinfo->coef; - if (! (*losslsd->entropy_process_restart) (cinfo)) + if (! (*cinfo->entropy->process_restart) (cinfo)) return FALSE; - (*losslsd->predict_process_restart) (cinfo); + (*cinfo->idct->start_pass) (cinfo); /* Reset restart counter */ diff->restart_rows_to_go = cinfo->restart_interval / cinfo->MCUs_per_row; @@ -154,12 +162,12 @@ start_output_pass (j_decompress_ptr cinfo) METHODDEF(int) decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - d_diff_ptr diff = (d_diff_ptr) losslsd->diff_private; + my_diff_ptr diff = (my_diff_ptr) cinfo->coef; + lossless_decomp_ptr losslessd = (lossless_decomp_ptr) cinfo->idct; JDIMENSION MCU_col_num; /* index of current MCU within row */ JDIMENSION MCU_count; /* number of MCUs decoded */ JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; - int comp, ci, yoffset, row, prev_row; + int ci, compi, yoffset, row, prev_row; jpeg_component_info *compptr; /* Loop to process as much as one whole iMCU row */ @@ -174,11 +182,11 @@ decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) } MCU_col_num = diff->MCU_ctr; - /* Try to fetch an MCU-row (or remaining portion of suspended MCU-row). */ + /* Try to fetch an MCU row (or remaining portion of suspended MCU row). */ MCU_count = - (*losslsd->entropy_decode_mcus) (cinfo, - diff->diff_buf, yoffset, MCU_col_num, - cinfo->MCUs_per_row - MCU_col_num); + (*cinfo->entropy->decode_mcus) (cinfo, + diff->diff_buf, yoffset, MCU_col_num, + cinfo->MCUs_per_row - MCU_col_num); if (MCU_count != cinfo->MCUs_per_row - MCU_col_num) { /* Suspension forced; update state counters and exit */ diff->MCU_vert_offset = yoffset; @@ -194,25 +202,24 @@ decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) } /* - * Undifference and scale each scanline of the disassembled MCU-row + * Undifference and scale each scanline of the disassembled MCU row * separately. We do not process dummy samples at the end of a scanline * or dummy rows at the end of the image. */ - for (comp = 0; comp < cinfo->comps_in_scan; comp++) { - compptr = cinfo->cur_comp_info[comp]; - ci = compptr->component_index; + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + compi = compptr->component_index; for (row = 0, prev_row = compptr->v_samp_factor - 1; row < (cinfo->input_iMCU_row == last_iMCU_row ? compptr->last_row_height : compptr->v_samp_factor); prev_row = row, row++) { - (*losslsd->predict_undifference[ci]) (cinfo, ci, - diff->diff_buf[ci][row], - diff->undiff_buf[ci][prev_row], - diff->undiff_buf[ci][row], - compptr->width_in_data_units); - (*losslsd->scaler_scale) (cinfo, diff->undiff_buf[ci][row], - output_buf[ci][row], - compptr->width_in_data_units); + (*losslessd->predict_undifference[compi]) + (cinfo, compi, diff->diff_buf[compi][row], + diff->undiff_buf[compi][prev_row], diff->undiff_buf[compi][row], + compptr->width_in_blocks); + (*losslessd->scaler_scale) (cinfo, diff->undiff_buf[compi][row], + output_buf[compi][row], + compptr->width_in_blocks); } } @@ -255,21 +262,16 @@ dummy_consume_data (j_decompress_ptr cinfo) METHODDEF(int) consume_data (j_decompress_ptr cinfo) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - d_diff_ptr diff = (d_diff_ptr) losslsd->diff_private; - JDIMENSION MCU_col_num; /* index of current MCU within row */ - JDIMENSION MCU_count; /* number of MCUs decoded */ - JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; - int comp, ci, yoffset, row, prev_row; + my_diff_ptr diff = (my_diff_ptr) cinfo->coef; + int ci; JSAMPARRAY buffer[MAX_COMPS_IN_SCAN]; jpeg_component_info *compptr; /* Align the virtual buffers for the components used in this scan. */ - for (comp = 0; comp < cinfo->comps_in_scan; comp++) { - compptr = cinfo->cur_comp_info[comp]; - ci = compptr->component_index; - buffer[ci] = (*cinfo->mem->access_virt_sarray) - ((j_common_ptr) cinfo, diff->whole_image[ci], + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + buffer[compptr->component_index] = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, diff->whole_image[compptr->component_index], cinfo->input_iMCU_row * compptr->v_samp_factor, (JDIMENSION) compptr->v_samp_factor, TRUE); } @@ -279,7 +281,7 @@ consume_data (j_decompress_ptr cinfo) /* - * Output some data from the full-image buffer sample in the multi-pass case. + * Output some data from the full-image sample buffer 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. * @@ -289,8 +291,7 @@ consume_data (j_decompress_ptr cinfo) METHODDEF(int) output_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - d_diff_ptr diff = (d_diff_ptr) losslsd->diff_private; + my_diff_ptr diff = (my_diff_ptr) cinfo->coef; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; int ci, samp_rows, row; JSAMPARRAY buffer; @@ -317,13 +318,13 @@ output_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) samp_rows = compptr->v_samp_factor; else { /* NB: can't use last_row_height here; it is input-side-dependent! */ - samp_rows = (int) (compptr->height_in_data_units % compptr->v_samp_factor); + samp_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); if (samp_rows == 0) samp_rows = compptr->v_samp_factor; } for (row = 0; row < samp_rows; row++) { MEMCOPY(output_buf[ci][row], buffer[row], - compptr->width_in_data_units * SIZEOF(JSAMPLE)); + compptr->width_in_blocks * SIZEOF(JSAMPLE)); } } @@ -342,31 +343,30 @@ output_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) GLOBAL(void) jinit_d_diff_controller (j_decompress_ptr cinfo, boolean need_full_buffer) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - d_diff_ptr diff; + my_diff_ptr diff; int ci; jpeg_component_info *compptr; - diff = (d_diff_ptr) + diff = (my_diff_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(d_diff_controller)); - losslsd->diff_private = (void *) diff; - losslsd->diff_start_input_pass = start_input_pass; - losslsd->pub.start_output_pass = start_output_pass; + SIZEOF(my_diff_controller)); + cinfo->coef = (struct jpeg_d_coef_controller *) diff; + diff->pub.start_input_pass = start_input_pass; + diff->pub.start_output_pass = start_output_pass; /* Create the [un]difference buffers. */ for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - diff->diff_buf[ci] = (*cinfo->mem->alloc_darray) - ((j_common_ptr) cinfo, JPOOL_IMAGE, - (JDIMENSION) jround_up((long) compptr->width_in_data_units, - (long) compptr->h_samp_factor), - (JDIMENSION) compptr->v_samp_factor); - diff->undiff_buf[ci] = (*cinfo->mem->alloc_darray) - ((j_common_ptr) cinfo, JPOOL_IMAGE, - (JDIMENSION) jround_up((long) compptr->width_in_data_units, - (long) compptr->h_samp_factor), - (JDIMENSION) compptr->v_samp_factor); + diff->diff_buf[ci] = + ALLOC_DARRAY(JPOOL_IMAGE, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) compptr->v_samp_factor); + diff->undiff_buf[ci] = + ALLOC_DARRAY(JPOOL_IMAGE, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) compptr->v_samp_factor); } if (need_full_buffer) { @@ -379,20 +379,20 @@ jinit_d_diff_controller (j_decompress_ptr cinfo, boolean need_full_buffer) access_rows = compptr->v_samp_factor; diff->whole_image[ci] = (*cinfo->mem->request_virt_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, - (JDIMENSION) jround_up((long) compptr->width_in_data_units, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, (long) compptr->h_samp_factor), - (JDIMENSION) jround_up((long) compptr->height_in_data_units, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, (long) compptr->v_samp_factor), (JDIMENSION) access_rows); } - losslsd->pub.consume_data = consume_data; - losslsd->pub.decompress_data = output_data; + diff->pub.consume_data = consume_data; + diff->pub.decompress_data = output_data; #else ERREXIT(cinfo, JERR_NOT_COMPILED); #endif } else { - losslsd->pub.consume_data = dummy_consume_data; - losslsd->pub.decompress_data = decompress_data; + diff->pub.consume_data = dummy_consume_data; + diff->pub.decompress_data = decompress_data; diff->whole_image[0] = NULL; /* flag for no virtual arrays */ } } diff --git a/jdhuff.c b/jdhuff.c index 807b7fe36..83c76707c 100644 --- a/jdhuff.c +++ b/jdhuff.c @@ -2,26 +2,150 @@ * jdhuff.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * - * This file contains Huffman entropy decoding routines which are shared - * by the sequential, progressive and lossless decoders. + * This file contains Huffman entropy decoding routines. + * + * Much of the complexity here has to do with supporting input suspension. + * If the data source module demands suspension, we want to be able to back + * up to the start of the current MCU. To do this, we copy state variables + * into local working storage, and update them back to the permanent + * storage only upon successful completion of an MCU. */ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossy.h" /* Private declarations for lossy codec */ -#include "jlossls.h" /* Private declarations for lossless codec */ #include "jdhuff.h" /* Declarations shared with jd*huff.c */ +/* + * Expanded entropy decoder object for Huffman decoding. + * + * The savable_state subrecord contains fields that change within an MCU, + * but must not be updated permanently until we complete the MCU. + */ + +typedef struct { + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ +} savable_state; + +/* This macro is to work around compilers with missing or broken + * structure assignment. You'll need to fix this code if you have + * such a compiler and you change MAX_COMPS_IN_SCAN. + */ + +#ifndef NO_STRUCT_ASSIGN +#define ASSIGN_STATE(dest,src) ((dest) = (src)) +#else +#if MAX_COMPS_IN_SCAN == 4 +#define ASSIGN_STATE(dest,src) \ + ((dest).last_dc_val[0] = (src).last_dc_val[0], \ + (dest).last_dc_val[1] = (src).last_dc_val[1], \ + (dest).last_dc_val[2] = (src).last_dc_val[2], \ + (dest).last_dc_val[3] = (src).last_dc_val[3]) +#endif +#endif + + +typedef struct { + struct jpeg_entropy_decoder pub; /* public fields */ + + /* These fields are loaded into local variables at start of each MCU. + * In case of suspension, we exit WITHOUT updating them. + */ + bitread_perm_state bitstate; /* Bit buffer at start of MCU */ + savable_state saved; /* Other state at start of MCU */ + + /* These fields are NOT loaded into local working state. */ + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + d_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; + d_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; + + /* Precalculated info set up by start_pass for use in decode_mcu: */ + + /* Pointers to derived tables to be used for each block within an MCU */ + d_derived_tbl * dc_cur_tbls[D_MAX_BLOCKS_IN_MCU]; + d_derived_tbl * ac_cur_tbls[D_MAX_BLOCKS_IN_MCU]; + /* Whether we care about the DC and AC coefficient values for each block */ + boolean dc_needed[D_MAX_BLOCKS_IN_MCU]; + boolean ac_needed[D_MAX_BLOCKS_IN_MCU]; +} huff_entropy_decoder; + +typedef huff_entropy_decoder * huff_entropy_ptr; + + +/* + * Initialize for a Huffman-compressed scan. + */ + +METHODDEF(void) +start_pass_huff_decoder (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci, blkn, dctbl, actbl; + jpeg_component_info * compptr; + + /* Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG. + * This ought to be an error condition, but we make it a warning because + * there are some baseline files out there with all zeroes in these bytes. + */ + if (cinfo->Ss != 0 || cinfo->Se != DCTSIZE2-1 || + cinfo->Ah != 0 || cinfo->Al != 0) + WARNMS(cinfo, JWRN_NOT_SEQUENTIAL); + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + actbl = compptr->ac_tbl_no; + /* Compute derived values for Huffman tables */ + /* We may do this more than once for a table, but it's not expensive */ + jpeg_make_d_derived_tbl(cinfo, TRUE, dctbl, + & entropy->dc_derived_tbls[dctbl]); + jpeg_make_d_derived_tbl(cinfo, FALSE, actbl, + & entropy->ac_derived_tbls[actbl]); + /* Initialize DC predictions to 0 */ + entropy->saved.last_dc_val[ci] = 0; + } + + /* Precalculate decoding info for each block in an MCU of this scan */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + /* Precalculate which table to use for each block */ + entropy->dc_cur_tbls[blkn] = entropy->dc_derived_tbls[compptr->dc_tbl_no]; + entropy->ac_cur_tbls[blkn] = entropy->ac_derived_tbls[compptr->ac_tbl_no]; + /* Decide whether we really care about the coefficient values */ + if (compptr->component_needed) { + entropy->dc_needed[blkn] = TRUE; + /* we don't need the ACs if producing a 1/8th-size image */ + entropy->ac_needed[blkn] = (compptr->DCT_scaled_size > 1); + } else { + entropy->dc_needed[blkn] = entropy->ac_needed[blkn] = FALSE; + } + } + + /* Initialize bitread state variables */ + entropy->bitstate.bits_left = 0; + entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ + entropy->pub.insufficient_data = FALSE; + + /* Initialize restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; +} + + /* * Compute the derived values for a Huffman table. * This routine also performs some validation checks on the table. + * + * Note this is also used by jdphuff.c and jdlhuff.c. */ GLOBAL(void) @@ -131,14 +255,14 @@ jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, boolean isDC, int tblno, /* 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..16. - * (Tighter bounds could be applied depending on the data depth and mode, - * but this is sufficient to ensure safe decoding.) + * For DC tables, we require the symbols to be in range 0..15 in lossy mode + * and 0..16 in lossless mode. (Tighter bounds could be applied depending on + * the data depth and mode, but this is sufficient to ensure safe decoding.) */ if (isDC) { for (i = 0; i < numsymbols; i++) { int sym = htbl->huffval[i]; - if (sym < 0 || sym > 16) + if (sym < 0 || sym > (cinfo->master->lossless ? 16 : 15)) ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); } } @@ -146,7 +270,7 @@ jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, boolean isDC, int tblno, /* - * Out-of-line code for bit fetching. + * Out-of-line code for bit fetching (shared with jdphuff.c and jdlhuff.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. @@ -248,14 +372,9 @@ jpeg_fill_bit_buffer (bitread_working_state * state, * We use a nonvolatile flag to ensure that only one warning message * appears per data segment. */ - huffd_common_ptr huffd; - if (cinfo->process == JPROC_LOSSLESS) - huffd = (huffd_common_ptr) ((j_lossless_d_ptr) cinfo->codec)->entropy_private; - else - huffd = (huffd_common_ptr) ((j_lossy_d_ptr) cinfo->codec)->entropy_private; - if (! huffd->insufficient_data) { + if (! cinfo->entropy->insufficient_data) { WARNMS(cinfo, JWRN_HIT_MARKER); - huffd->insufficient_data = TRUE; + cinfo->entropy->insufficient_data = TRUE; } /* Fill the buffer with zero bits */ get_buffer <<= MIN_GET_BITS - bits_left; @@ -315,3 +434,221 @@ jpeg_huff_decode (bitread_working_state * state, return htbl->pub->huffval[ (int) (code + htbl->valoffset[l]) ]; } + + +/* + * Figure F.12: extend sign bit. + * On some machines, a shift and add will be faster than a table lookup. + */ + +#ifdef AVOID_TABLES + +#define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x)) + +#else + +#define HUFF_EXTEND(x,s) ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)) + +static const int extend_test[16] = /* entry n is 2**(n-1) */ + { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, + 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; + +static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ + { 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, + ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, + ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, + ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 }; + +#endif /* AVOID_TABLES */ + + +/* + * Check for a restart marker & resynchronize decoder. + * Returns FALSE if must suspend. + */ + +LOCAL(boolean) +process_restart (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci; + + /* Throw away any unused bits remaining in bit buffer; */ + /* include any full bytes in next_marker's count of discarded bytes */ + cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8; + entropy->bitstate.bits_left = 0; + + /* Advance past the RSTn marker */ + if (! (*cinfo->marker->read_restart_marker) (cinfo)) + return FALSE; + + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) + entropy->saved.last_dc_val[ci] = 0; + + /* Reset restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; + + /* Reset out-of-data flag, unless read_restart_marker left us smack up + * against a marker. In that case we will end up treating the next data + * segment as empty, and we can avoid producing bogus output pixels by + * leaving the flag set. + */ + if (cinfo->unread_marker == 0) + entropy->pub.insufficient_data = FALSE; + + return TRUE; +} + + +/* + * Decode and return one MCU's worth of Huffman-compressed coefficients. + * The coefficients are reordered from zigzag order into natural array order, + * but are not dequantized. + * + * The i'th block of the MCU is stored into the block pointed to by + * MCU_data[i]. WE ASSUME THIS AREA HAS BEEN ZEROED BY THE CALLER. + * (Wholesale zeroing is usually a little faster than retail...) + * + * Returns FALSE if data source requested suspension. In that case no + * changes have been made to permanent state. (Exception: some output + * coefficients may already have been assigned. This is harmless for + * this module, since we'll just re-assign them on the next call.) + */ + +METHODDEF(boolean) +decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int blkn; + BITREAD_STATE_VARS; + savable_state state; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* If we've run out of data, just leave the MCU set to zeroes. + * This way, we return uniform gray for the remainder of the segment. + */ + if (! entropy->pub.insufficient_data) { + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(state, entropy->saved); + + /* Outer loop handles each block in the MCU */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + JBLOCKROW block = MCU_data[blkn]; + d_derived_tbl * dctbl = entropy->dc_cur_tbls[blkn]; + d_derived_tbl * actbl = entropy->ac_cur_tbls[blkn]; + register int s, k, r; + + /* Decode a single block's worth of coefficients */ + + /* Section F.2.2.1: decode the DC coefficient difference */ + HUFF_DECODE(s, br_state, dctbl, return FALSE, label1); + if (s) { + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + } + + if (entropy->dc_needed[blkn]) { + /* Convert DC difference to actual value, update last_dc_val */ + int ci = cinfo->MCU_membership[blkn]; + s += state.last_dc_val[ci]; + state.last_dc_val[ci] = s; + /* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */ + (*block)[0] = (JCOEF) s; + } + + if (entropy->ac_needed[blkn]) { + + /* Section F.2.2.2: decode the AC coefficients */ + /* Since zeroes are skipped, output area must be cleared beforehand */ + for (k = 1; k < DCTSIZE2; k++) { + HUFF_DECODE(s, br_state, actbl, return FALSE, label2); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + /* Output coefficient in natural (dezigzagged) order. + * Note: the extra entries in jpeg_natural_order[] will save us + * if k >= DCTSIZE2, which could happen if the data is corrupted. + */ + (*block)[jpeg_natural_order[k]] = (JCOEF) s; + } else { + if (r != 15) + break; + k += 15; + } + } + + } else { + + /* Section F.2.2.2: decode the AC coefficients */ + /* In this path we just discard the values */ + for (k = 1; k < DCTSIZE2; k++) { + HUFF_DECODE(s, br_state, actbl, return FALSE, label3); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + DROP_BITS(s); + } else { + if (r != 15) + break; + k += 15; + } + } + + } + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(entropy->saved, state); + } + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + + +/* + * Module initialization routine for Huffman entropy decoding. + */ + +GLOBAL(void) +jinit_huff_decoder (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy; + int i; + + entropy = (huff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(huff_entropy_decoder)); + cinfo->entropy = (struct jpeg_entropy_decoder *) entropy; + entropy->pub.start_pass = start_pass_huff_decoder; + entropy->pub.decode_mcu = decode_mcu; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL; + } +} diff --git a/jdhuff.h b/jdhuff.h index 77c5296a5..cc8c30f87 100644 --- a/jdhuff.h +++ b/jdhuff.h @@ -2,15 +2,15 @@ * jdhuff.h * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. * For conditions of distribution and use, see the accompanying README file. * * This file contains declarations for Huffman entropy decoding routines - * that are shared between the sequential decoder (jdhuff.c), the - * progressive decoder (jdphuff.c) and the lossless decoder (jdlhuff.c). - * No other modules need to see these. + * that are shared between the sequential decoder (jdhuff.c), the progressive + * decoder (jdphuff.c), and the lossless decoder (jdlhuff.c). No other modules + * need to see these. */ /* Short forms of external names for systems with brain-damaged linkers. */ @@ -202,30 +202,3 @@ slowlabel: \ EXTERN(int) jpeg_huff_decode JPP((bitread_working_state * state, register bit_buf_type get_buffer, register int bits_left, d_derived_tbl * htbl, int min_bits)); - - -/* Common fields between sequential, progressive and lossless Huffman entropy - * decoder master structs. - */ - -#define huffd_common_fields \ - boolean insufficient_data; /* set TRUE after emmitting warning */ \ - /* These fields are loaded into local variables at start of each MCU. \ - * In case of suspension, we exit WITHOUT updating them. \ - */ \ - bitread_perm_state bitstate /* Bit buffer at start of MCU */ - -/* Routines that are to be used by any or all of the entropy decoders are - * declared to receive a pointer to this structure. There are no actual - * instances of huffd_common_struct, only of shuff_entropy_decoder, - * phuff_entropy_decoder and lhuff_entropy_decoder. - */ -struct huffd_common_struct { - huffd_common_fields; /* Fields common to all decoder struct types */ - /* Additional fields follow in an actual shuff_entropy_decoder, - * phuff_entropy_decoder or lhuff_entropy_decoder struct. All four structs - * must agree on these initial fields! (This would be a lot cleaner in C++.) - */ -}; - -typedef struct huffd_common_struct * huffd_common_ptr; diff --git a/jdinput.c b/jdinput.c index 471ab3333..a4df92041 100644 --- a/jdinput.c +++ b/jdinput.c @@ -2,9 +2,10 @@ * jdinput.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains input control logic for the JPEG decompressor. @@ -44,19 +45,19 @@ initial_setup (j_decompress_ptr cinfo) { int ci; jpeg_component_info *compptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; /* Make sure image isn't bigger than I can handle */ if ((long) cinfo->image_height > (long) JPEG_MAX_DIMENSION || (long) cinfo->image_width > (long) JPEG_MAX_DIMENSION) ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) JPEG_MAX_DIMENSION); - if (cinfo->process == JPROC_LOSSLESS) { + if (cinfo->master->lossless) { /* If precision > compiled-in value, we must downscale */ if (cinfo->data_precision > BITS_IN_JSAMPLE) WARNMS2(cinfo, JWRN_MUST_DOWNSCALE, cinfo->data_precision, BITS_IN_JSAMPLE); - } - else { /* Lossy processes */ + } else { /* For now, precision must match compiled-in value... */ if (cinfo->data_precision != BITS_IN_JSAMPLE) ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); @@ -81,23 +82,23 @@ initial_setup (j_decompress_ptr cinfo) compptr->v_samp_factor); } - /* We initialize codec_data_unit and min_codec_data_unit to data_unit. - * In the full decompressor, this will be overridden by jdmaster.c; + /* We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE in lossy + * mode. 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_codec_data_unit = cinfo->data_unit; + cinfo->min_DCT_scaled_size = data_unit; /* Compute dimensions of components */ for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - compptr->codec_data_unit = cinfo->data_unit; + compptr->DCT_scaled_size = data_unit; /* Size in data units */ - compptr->width_in_data_units = (JDIMENSION) + compptr->width_in_blocks = (JDIMENSION) jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, - (long) (cinfo->max_h_samp_factor * cinfo->data_unit)); - compptr->height_in_data_units = (JDIMENSION) + (long) (cinfo->max_h_samp_factor * data_unit)); + compptr->height_in_blocks = (JDIMENSION) jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, - (long) (cinfo->max_v_samp_factor * cinfo->data_unit)); + (long) (cinfo->max_v_samp_factor * data_unit)); /* 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. @@ -118,11 +119,10 @@ initial_setup (j_decompress_ptr cinfo) /* 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*cinfo->data_unit)); + (long) (cinfo->max_v_samp_factor*data_unit)); /* Decide whether file contains multiple scans */ - if (cinfo->comps_in_scan < cinfo->num_components || - cinfo->process == JPROC_PROGRESSIVE) + if (cinfo->comps_in_scan < cinfo->num_components || cinfo->progressive_mode) cinfo->inputctl->has_multiple_scans = TRUE; else cinfo->inputctl->has_multiple_scans = FALSE; @@ -136,31 +136,32 @@ per_scan_setup (j_decompress_ptr cinfo) { int ci, mcublks, tmp; jpeg_component_info *compptr; - + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + if (cinfo->comps_in_scan == 1) { /* Noninterleaved (single-component) scan */ compptr = cinfo->cur_comp_info[0]; /* Overall image size in MCUs */ - cinfo->MCUs_per_row = compptr->width_in_data_units; - cinfo->MCU_rows_in_scan = compptr->height_in_data_units; + cinfo->MCUs_per_row = compptr->width_in_blocks; + cinfo->MCU_rows_in_scan = compptr->height_in_blocks; /* For noninterleaved scan, always one data unit per MCU */ compptr->MCU_width = 1; compptr->MCU_height = 1; - compptr->MCU_data_units = 1; - compptr->MCU_sample_width = compptr->codec_data_unit; + 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 data unit rows present in the last iMCU row. */ - tmp = (int) (compptr->height_in_data_units % compptr->v_samp_factor); + tmp = (int) (compptr->height_in_blocks % compptr->v_samp_factor); if (tmp == 0) tmp = compptr->v_samp_factor; compptr->last_row_height = tmp; /* Prepare array describing MCU composition */ - cinfo->data_units_in_MCU = 1; + cinfo->blocks_in_MCU = 1; cinfo->MCU_membership[0] = 0; } else { @@ -173,33 +174,33 @@ per_scan_setup (j_decompress_ptr cinfo) /* Overall image size in MCUs */ cinfo->MCUs_per_row = (JDIMENSION) jdiv_round_up((long) cinfo->image_width, - (long) (cinfo->max_h_samp_factor*cinfo->data_unit)); + (long) (cinfo->max_h_samp_factor*data_unit)); cinfo->MCU_rows_in_scan = (JDIMENSION) jdiv_round_up((long) cinfo->image_height, - (long) (cinfo->max_v_samp_factor*cinfo->data_unit)); + (long) (cinfo->max_v_samp_factor*data_unit)); - cinfo->data_units_in_MCU = 0; + cinfo->blocks_in_MCU = 0; for (ci = 0; ci < cinfo->comps_in_scan; ci++) { compptr = cinfo->cur_comp_info[ci]; /* Sampling factors give # of data units of component in each MCU */ compptr->MCU_width = compptr->h_samp_factor; compptr->MCU_height = compptr->v_samp_factor; - compptr->MCU_data_units = compptr->MCU_width * compptr->MCU_height; - compptr->MCU_sample_width = compptr->MCU_width * compptr->codec_data_unit; + 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 data units in last MCU column & row */ - tmp = (int) (compptr->width_in_data_units % compptr->MCU_width); + tmp = (int) (compptr->width_in_blocks % compptr->MCU_width); if (tmp == 0) tmp = compptr->MCU_width; compptr->last_col_width = tmp; - tmp = (int) (compptr->height_in_data_units % compptr->MCU_height); + tmp = (int) (compptr->height_in_blocks % compptr->MCU_height); if (tmp == 0) tmp = compptr->MCU_height; compptr->last_row_height = tmp; /* Prepare array describing MCU composition */ - mcublks = compptr->MCU_data_units; - if (cinfo->data_units_in_MCU + mcublks > D_MAX_DATA_UNITS_IN_MCU) + mcublks = compptr->MCU_blocks; + if (cinfo->blocks_in_MCU + mcublks > D_MAX_BLOCKS_IN_MCU) ERREXIT(cinfo, JERR_BAD_MCU_SIZE); while (mcublks-- > 0) { - cinfo->MCU_membership[cinfo->data_units_in_MCU++] = ci; + cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci; } } @@ -207,6 +208,54 @@ per_scan_setup (j_decompress_ptr cinfo) } +/* + * Save away a copy of the Q-table referenced by each component present + * in the current scan, unless already saved during a prior scan. + * + * In a multiple-scan JPEG file, the encoder could assign different components + * the same Q-table slot number, but change table definitions between scans + * so that each component uses a different Q-table. (The IJG encoder is not + * currently capable of doing this, but other encoders might.) Since we want + * to be able to dequantize all the components at the end of the file, this + * means that we have to save away the table actually used for each component. + * We do this by copying the table at the start of the first scan containing + * the component. + * The JPEG spec prohibits the encoder from changing the contents of a Q-table + * slot between scans of a component using that slot. If the encoder does so + * anyway, this decoder will simply use the Q-table values that were current + * at the start of the first scan for the component. + * + * The decompressor output side looks only at the saved quant tables, + * not at the current Q-table slots. + */ + +LOCAL(void) +latch_quant_tables (j_decompress_ptr cinfo) +{ + int ci, qtblno; + jpeg_component_info *compptr; + JQUANT_TBL * qtbl; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* No work if we already saved Q-table for this component */ + if (compptr->quant_table != NULL) + continue; + /* Make sure specified quantization table is present */ + qtblno = compptr->quant_tbl_no; + if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS || + cinfo->quant_tbl_ptrs[qtblno] == NULL) + ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno); + /* OK, save away the quantization table */ + qtbl = (JQUANT_TBL *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(JQUANT_TBL)); + MEMCOPY(qtbl, cinfo->quant_tbl_ptrs[qtblno], SIZEOF(JQUANT_TBL)); + compptr->quant_table = qtbl; + } +} + + /* * Initialize the input modules to read a scan of compressed data. * The first call to this is done by jdmaster.c after initializing @@ -218,15 +267,18 @@ METHODDEF(void) start_input_pass (j_decompress_ptr cinfo) { per_scan_setup(cinfo); - (*cinfo->codec->start_input_pass) (cinfo); - cinfo->inputctl->consume_input = cinfo->codec->consume_data; + if (! cinfo->master->lossless) + latch_quant_tables(cinfo); + (*cinfo->entropy->start_pass) (cinfo); + (*cinfo->coef->start_input_pass) (cinfo); + cinfo->inputctl->consume_input = cinfo->coef->consume_data; } /* * Finish up after inputting a compressed-data scan. - * This is called by the coefficient controller after it's read all - * the expected data of the scan. + * This is called by the coefficient or difference controller after it's read + * all the expected data of the scan. */ METHODDEF(void) @@ -242,8 +294,8 @@ finish_input_pass (j_decompress_ptr cinfo) * 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. + * coefficient or difference controller's consume_data routine, depending on + * whether we are reading a compressed data segment or inter-segment markers. */ METHODDEF(int) @@ -261,12 +313,6 @@ consume_markers (j_decompress_ptr cinfo) case JPEG_REACHED_SOS: /* Found SOS */ if (inputctl->inheaders) { /* 1st SOS */ initial_setup(cinfo); - /* - * Initialize the decompression codec. We need to do this here so that - * any codec-specific fields and function pointers are available to - * the rest of the library. - */ - jinit_d_codec(cinfo); inputctl->inheaders = FALSE; /* Note: start_input_pass must be called by jdmaster.c * before any more input can be consumed. jdapimin.c is diff --git a/jdlhuff.c b/jdlhuff.c index 6f69a0114..7cb5885fc 100644 --- a/jdlhuff.c +++ b/jdlhuff.c @@ -2,9 +2,10 @@ * jdlhuff.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains Huffman entropy decoding routines for lossless JPEG. @@ -30,11 +31,16 @@ typedef struct { } lhd_output_ptr_info; /* - * Private entropy decoder object for lossless Huffman decoding. + * Expanded entropy decoder object for Huffman decoding in lossless mode. */ typedef struct { - huffd_common_fields; /* Fields shared with other entropy decoders */ + struct jpeg_entropy_decoder pub; /* public fields */ + + /* These fields are loaded into local variables at start of each MCU. + * In case of suspension, we exit WITHOUT updating them. + */ + bitread_perm_state bitstate; /* Bit buffer at start of MCU */ /* Pointers to derived tables (these workspaces have image lifespan) */ d_derived_tbl * derived_tbls[NUM_HUFF_TBLS]; @@ -42,12 +48,12 @@ typedef struct { /* Precalculated info set up by start_pass for use in decode_mcus: */ /* Pointers to derived tables to be used for each data unit within an MCU */ - d_derived_tbl * cur_tbls[D_MAX_DATA_UNITS_IN_MCU]; + d_derived_tbl * cur_tbls[D_MAX_BLOCKS_IN_MCU]; /* Pointers to the proper output difference row for each group of data units * within an MCU. For each component, there are Vi groups of Hi data units. */ - JDIFFROW output_ptr[D_MAX_DATA_UNITS_IN_MCU]; + JDIFFROW output_ptr[D_MAX_BLOCKS_IN_MCU]; /* Number of output pointers in use for the current MCU. This is the sum * of all Vi in the MCU. @@ -57,10 +63,10 @@ typedef struct { /* Information used for positioning the output pointers within the output * difference rows. */ - lhd_output_ptr_info output_ptr_info[D_MAX_DATA_UNITS_IN_MCU]; + lhd_output_ptr_info output_ptr_info[D_MAX_BLOCKS_IN_MCU]; /* Index of the proper output pointer for each data unit within an MCU */ - int output_ptr_index[D_MAX_DATA_UNITS_IN_MCU]; + int output_ptr_index[D_MAX_BLOCKS_IN_MCU]; } lhuff_entropy_decoder; @@ -74,8 +80,7 @@ typedef lhuff_entropy_decoder * lhuff_entropy_ptr; METHODDEF(void) start_pass_lhuff_decoder (j_decompress_ptr cinfo) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) losslsd->entropy_private; + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) cinfo->entropy; int ci, dctbl, sampn, ptrn, yoffset, xoffset; jpeg_component_info * compptr; @@ -93,7 +98,7 @@ start_pass_lhuff_decoder (j_decompress_ptr cinfo) } /* Precalculate decoding info for each sample in an MCU of this scan */ - for (sampn = 0, ptrn = 0; sampn < cinfo->data_units_in_MCU;) { + for (sampn = 0, ptrn = 0; sampn < cinfo->blocks_in_MCU;) { compptr = cinfo->cur_comp_info[cinfo->MCU_membership[sampn]]; ci = compptr->component_index; for (yoffset = 0; yoffset < compptr->MCU_height; yoffset++, ptrn++) { @@ -114,7 +119,7 @@ start_pass_lhuff_decoder (j_decompress_ptr cinfo) /* Initialize bitread state variables */ entropy->bitstate.bits_left = 0; entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ - entropy->insufficient_data = FALSE; + entropy->pub.insufficient_data = FALSE; } @@ -149,12 +154,10 @@ static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ * Returns FALSE if must suspend. */ -METHODDEF(boolean) +LOCAL(boolean) process_restart (j_decompress_ptr cinfo) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) losslsd->entropy_private; - int ci; + lhuff_entropy_ptr entropy = (lhuff_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 */ @@ -171,23 +174,23 @@ process_restart (j_decompress_ptr cinfo) * leaving the flag set. */ if (cinfo->unread_marker == 0) - entropy->insufficient_data = FALSE; + entropy->pub.insufficient_data = FALSE; return TRUE; } /* - * Decode and return nMCU's worth of Huffman-compressed differences. + * Decode and return nMCU MCUs' worth of Huffman-compressed differences. * Each MCU is also disassembled and placed accordingly in diff_buf. * * MCU_col_num specifies the column of the first MCU being requested within - * the MCU-row. This tells us where to position the output row pointers in + * the MCU row. This tells us where to position the output row pointers in * diff_buf. * - * Returns the number of MCUs decoded. This may be less than nMCU if data - * source requested suspension. In that case no changes have been made to - * permanent state. (Exception: some output differences may already have + * Returns the number of MCUs decoded. This may be less than nMCU MCUs if + * data source requested suspension. In that case no changes have been made + * to permanent state. (Exception: some output differences may already have * been assigned. This is harmless for this module, since we'll just * re-assign them on the next call.) */ @@ -196,8 +199,7 @@ METHODDEF(JDIMENSION) decode_mcus (j_decompress_ptr cinfo, JDIFFIMAGE diff_buf, JDIMENSION MCU_row_num, JDIMENSION MCU_col_num, JDIMENSION nMCU) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) losslsd->entropy_private; + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr) cinfo->entropy; int mcu_num, sampn, ci, yoffset, MCU_width, ptrn; BITREAD_STATE_VARS; @@ -217,12 +219,12 @@ decode_mcus (j_decompress_ptr cinfo, JDIFFIMAGE diff_buf, * NB: We should find a way to do this without interacting with the * undifferencer module directly. */ - if (entropy->insufficient_data) { + if (entropy->pub.insufficient_data) { for (ptrn = 0; ptrn < entropy->num_output_ptrs; ptrn++) jzero_far((void FAR *) entropy->output_ptr[ptrn], nMCU * entropy->output_ptr_info[ptrn].MCU_width * SIZEOF(JDIFF)); - (*losslsd->predict_process_restart) (cinfo); + (*cinfo->idct->start_pass) (cinfo); } else { @@ -230,12 +232,12 @@ decode_mcus (j_decompress_ptr cinfo, JDIFFIMAGE diff_buf, /* Load up working state */ BITREAD_LOAD_STATE(cinfo,entropy->bitstate); - /* Outer loop handles the number of MCU requested */ + /* Outer loop handles the number of MCUs requested */ for (mcu_num = 0; mcu_num < nMCU; mcu_num++) { /* Inner loop handles the samples in the MCU */ - for (sampn = 0; sampn < cinfo->data_units_in_MCU; sampn++) { + for (sampn = 0; sampn < cinfo->blocks_in_MCU; sampn++) { d_derived_tbl * dctbl = entropy->cur_tbls[sampn]; register int s, r; @@ -265,23 +267,22 @@ decode_mcus (j_decompress_ptr cinfo, JDIFFIMAGE diff_buf, /* - * Module initialization routine for lossless Huffman entropy decoding. + * Module initialization routine for lossless mode Huffman entropy decoding. */ GLOBAL(void) jinit_lhuff_decoder (j_decompress_ptr cinfo) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; lhuff_entropy_ptr entropy; int i; entropy = (lhuff_entropy_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, SIZEOF(lhuff_entropy_decoder)); - losslsd->entropy_private = (void *) entropy; - losslsd->entropy_start_pass = start_pass_lhuff_decoder; - losslsd->entropy_process_restart = process_restart; - losslsd->entropy_decode_mcus = decode_mcus; + cinfo->entropy = (struct jpeg_entropy_decoder *) entropy; + entropy->pub.start_pass = start_pass_lhuff_decoder; + entropy->pub.decode_mcus = decode_mcus; + entropy->pub.process_restart = process_restart; /* Mark tables unallocated */ for (i = 0; i < NUM_HUFF_TBLS; i++) { diff --git a/jdlossls.c b/jdlossls.c index 3ef163c73..7a61facc6 100644 --- a/jdlossls.c +++ b/jdlossls.c @@ -5,9 +5,11 @@ * Copyright (C) 1998, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * - * This file contains the control logic for the lossless JPEG decompressor. + * This file contains prediction, sample undifferencing, point transform, and + * sample scaling routines for the lossless JPEG decompressor. */ #define JPEG_INTERNALS @@ -15,82 +17,303 @@ #include "jpeglib.h" #include "jlossls.h" - #ifdef D_LOSSLESS_SUPPORTED + +/**************** Sample undifferencing (reconstruction) *****************/ + +/* + * In order to avoid a performance penalty for checking which predictor is + * being used and which row is being processed for each call of the + * undifferencer, and to promote optimization, we have separate undifferencing + * functions for each predictor selection value. + * + * We are able to avoid duplicating source code by implementing the predictors + * and undifferencers as macros. Each of the undifferencing functions is + * simply a wrapper around an UNDIFFERENCE macro with the appropriate PREDICTOR + * macro passed as an argument. + */ + +/* Predictor for the first column of the first row: 2^(P-Pt-1) */ +#define INITIAL_PREDICTORx (1 << (cinfo->data_precision - cinfo->Al - 1)) + +/* Predictor for the first column of the remaining rows: Rb */ +#define INITIAL_PREDICTOR2 GETJSAMPLE(prev_row[0]) + + +/* + * 1-Dimensional undifferencer routine. + * + * This macro implements the 1-D horizontal predictor (1). INITIAL_PREDICTOR + * is used as the special case predictor for the first column, which must be + * either INITIAL_PREDICTOR2 or INITIAL_PREDICTORx. The remaining samples + * use PREDICTOR1. + * + * The reconstructed sample is supposed to be calculated modulo 2^16, so we + * logically AND the result with 0xFFFF. +*/ + +#define UNDIFFERENCE_1D(INITIAL_PREDICTOR) \ + int Ra; \ + \ + Ra = (*diff_buf++ + INITIAL_PREDICTOR) & 0xFFFF; \ + *undiff_buf++ = Ra; \ + \ + while (--width) { \ + Ra = (*diff_buf++ + PREDICTOR1) & 0xFFFF; \ + *undiff_buf++ = Ra; \ + } + + +/* + * 2-Dimensional undifferencer routine. + * + * This macro implements the 2-D horizontal predictors (#2-7). PREDICTOR2 is + * used as the special case predictor for the first column. The remaining + * samples use PREDICTOR, which is a function of Ra, Rb, and Rc. + * + * Because prev_row and output_buf may point to the same storage area (in an + * interleaved image with Vi=1, for example), we must take care to buffer Rb/Rc + * before writing the current reconstructed sample value into output_buf. + * + * The reconstructed sample is supposed to be calculated modulo 2^16, so we + * logically AND the result with 0xFFFF. + */ + +#define UNDIFFERENCE_2D(PREDICTOR) \ + int Ra, Rb, Rc; \ + \ + Rb = GETJSAMPLE(*prev_row++); \ + Ra = (*diff_buf++ + PREDICTOR2) & 0xFFFF; \ + *undiff_buf++ = Ra; \ + \ + while (--width) { \ + Rc = Rb; \ + Rb = GETJSAMPLE(*prev_row++); \ + Ra = (*diff_buf++ + PREDICTOR) & 0xFFFF; \ + *undiff_buf++ = Ra; \ + } + + /* - * Compute output image dimensions and related values. + * Undifferencers for the second and subsequent rows in a scan or restart + * interval. The first sample in the row is undifferenced using the vertical + * predictor (2). The rest of the samples are undifferenced using the + * predictor specified in the scan header. */ METHODDEF(void) -calc_output_dimensions (j_decompress_ptr cinfo) +jpeg_undifference1(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) { - /* Hardwire it to "no scaling" */ - cinfo->output_width = cinfo->image_width; - cinfo->output_height = cinfo->image_height; - /* jdinput.c has already initialized codec_data_unit to 1, - * and has computed unscaled downsampled_width and downsampled_height. - */ + UNDIFFERENCE_1D(INITIAL_PREDICTOR2); +} + +METHODDEF(void) +jpeg_undifference2(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR2); + (void)(Rc); +} + +METHODDEF(void) +jpeg_undifference3(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR3); +} + +METHODDEF(void) +jpeg_undifference4(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR4); +} + +METHODDEF(void) +jpeg_undifference5(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR5); +} + +METHODDEF(void) +jpeg_undifference6(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR6); +} + +METHODDEF(void) +jpeg_undifference7(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR7); + (void)(Rc); } /* - * Initialize for an input processing pass. + * Undifferencer for the first row in a scan or restart interval. The first + * sample in the row is undifferenced using the special predictor constant + * x=2^(P-Pt-1). The rest of the samples are undifferenced using the + * 1-D horizontal predictor (1). */ METHODDEF(void) -start_input_pass (j_decompress_ptr cinfo) +jpeg_undifference_first_row(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) { - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; + lossless_decomp_ptr losslessd = (lossless_decomp_ptr) cinfo->idct; + + UNDIFFERENCE_1D(INITIAL_PREDICTORx); - (*losslsd->entropy_start_pass) (cinfo); - (*losslsd->predict_start_pass) (cinfo); - (*losslsd->scaler_start_pass) (cinfo); - (*losslsd->diff_start_input_pass) (cinfo); + /* + * Now that we have undifferenced the first row, we want to use the + * undifferencer that corresponds to the predictor specified in the + * scan header. + */ + switch (cinfo->Ss) { + case 1: + losslessd->predict_undifference[comp_index] = jpeg_undifference1; + break; + case 2: + losslessd->predict_undifference[comp_index] = jpeg_undifference2; + break; + case 3: + losslessd->predict_undifference[comp_index] = jpeg_undifference3; + break; + case 4: + losslessd->predict_undifference[comp_index] = jpeg_undifference4; + break; + case 5: + losslessd->predict_undifference[comp_index] = jpeg_undifference5; + break; + case 6: + losslessd->predict_undifference[comp_index] = jpeg_undifference6; + break; + case 7: + losslessd->predict_undifference[comp_index] = jpeg_undifference7; + break; + } } +/**************************** Sample scaling *****************************/ + /* - * Initialize the lossless decompression codec. - * This is called only once, during master selection. + * This is a combination of upscaling the undifferenced sample by 2^Pt and + * downscaling the sample to fit into JSAMPLE. */ -GLOBAL(void) -jinit_lossless_d_codec(j_decompress_ptr cinfo) +METHODDEF(void) +simple_upscale(j_decompress_ptr cinfo, + JDIFFROW diff_buf, JSAMPROW output_buf, + JDIMENSION width) { - j_lossless_d_ptr losslsd; - boolean use_c_buffer; + lossless_decomp_ptr losslessd = (lossless_decomp_ptr) cinfo->idct; - /* Create subobject in permanent pool */ - losslsd = (j_lossless_d_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, - SIZEOF(jpeg_lossless_d_codec)); - cinfo->codec = (struct jpeg_d_codec *) losslsd; - - /* Initialize sub-modules */ - /* Entropy decoding: either Huffman or arithmetic coding. */ - if (cinfo->arith_code) { - ERREXIT(cinfo, JERR_ARITH_NOTIMPL); - } else { - jinit_lhuff_decoder(cinfo); - } + while (width--) + *output_buf++ = (JSAMPLE) (*diff_buf++ << losslessd->scale_factor); +} + +METHODDEF(void) +simple_downscale(j_decompress_ptr cinfo, + JDIFFROW diff_buf, JSAMPROW output_buf, + JDIMENSION width) +{ + lossless_decomp_ptr losslessd = (lossless_decomp_ptr) cinfo->idct; - /* Undifferencer */ - jinit_undifferencer(cinfo); + while (width--) + *output_buf++ = (JSAMPLE) RIGHT_SHIFT(*diff_buf++, + losslessd->scale_factor); +} - /* Scaler */ - jinit_d_scaler(cinfo); +METHODDEF(void) +noscale(j_decompress_ptr cinfo, + JDIFFROW diff_buf, JSAMPROW output_buf, + JDIMENSION width) +{ + while (width--) + *output_buf++ = (JSAMPLE) *diff_buf++; +} - use_c_buffer = cinfo->inputctl->has_multiple_scans || cinfo->buffered_image; - jinit_d_diff_controller(cinfo, use_c_buffer); - /* Initialize method pointers. +/* + * Initialize for an input processing pass. + */ + +METHODDEF(void) +start_pass_lossless (j_decompress_ptr cinfo) +{ + lossless_decomp_ptr losslessd = (lossless_decomp_ptr) cinfo->idct; + int ci, downscale; + + /* Check that the scan parameters Ss, Se, Ah, Al are OK for lossless JPEG. + * + * Ss is the predictor selection value (psv). Legal values for sequential + * lossless JPEG are: 1 <= psv <= 7. + * + * Se and Ah are not used and should be zero. * - * Note: consume_data, start_output_pass and decompress_data are - * assigned in jddiffct.c. + * Al specifies the point transform (Pt). + * Legal values are: 0 <= Pt <= (data precision - 1). */ - losslsd->pub.calc_output_dimensions = calc_output_dimensions; - losslsd->pub.start_input_pass = start_input_pass; + if (cinfo->Ss < 1 || cinfo->Ss > 7 || + cinfo->Se != 0 || cinfo->Ah != 0 || + cinfo->Al < 0 || cinfo->Al >= cinfo->data_precision) + ERREXIT4(cinfo, JERR_BAD_PROGRESSION, + cinfo->Ss, cinfo->Se, cinfo->Ah, cinfo->Al); + + /* Set undifference functions to first row function */ + for (ci = 0; ci < cinfo->num_components; ci++) + losslessd->predict_undifference[ci] = jpeg_undifference_first_row; + + /* + * Downscale by the difference in the input vs. output precision. If the + * output precision >= input precision, then do not downscale. + */ + downscale = BITS_IN_JSAMPLE < cinfo->data_precision ? + cinfo->data_precision - BITS_IN_JSAMPLE : 0; + + losslessd->scale_factor = cinfo->Al - downscale; + + /* Set scaler functions based on scale_factor (positive = left shift) */ + if (losslessd->scale_factor > 0) + losslessd->scaler_scale = simple_upscale; + else if (losslessd->scale_factor < 0) { + losslessd->scale_factor = -losslessd->scale_factor; + losslessd->scaler_scale = simple_downscale; + } + else + losslessd->scaler_scale = noscale; +} + + +/* + * Initialize the lossless decompressor. + */ + +GLOBAL(void) +jinit_lossless_decompressor(j_decompress_ptr cinfo) +{ + lossless_decomp_ptr losslessd; + + /* Create subobject in permanent pool */ + losslessd = (lossless_decomp_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(jpeg_lossless_decompressor)); + cinfo->idct = (struct jpeg_inverse_dct *) losslessd; + losslessd->pub.start_pass = start_pass_lossless; } #endif /* D_LOSSLESS_SUPPORTED */ diff --git a/jdlossy.c b/jdlossy.c deleted file mode 100644 index fb4ebe6df..000000000 --- a/jdlossy.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * jdlossy.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains the control logic for the lossy JPEG decompressor. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jlossy.h" - - -/* - * Compute output image dimensions and related values. - */ - -METHODDEF(void) -calc_output_dimensions (j_decompress_ptr cinfo) -{ -#ifdef IDCT_SCALING_SUPPORTED - int ci; - jpeg_component_info *compptr; - - /* Compute actual output image dimensions and DCT scaling choices. */ - if (cinfo->scale_num * 8 <= cinfo->scale_denom) { - /* Provide 1/8 scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width, 8L); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height, 8L); - cinfo->min_codec_data_unit = 1; - } else if (cinfo->scale_num * 4 <= cinfo->scale_denom) { - /* Provide 1/4 scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width, 4L); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height, 4L); - cinfo->min_codec_data_unit = 2; - } else if (cinfo->scale_num * 2 <= cinfo->scale_denom) { - /* Provide 1/2 scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width, 2L); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height, 2L); - cinfo->min_codec_data_unit = 4; - } else { - /* Provide 1/1 scaling */ - cinfo->output_width = cinfo->image_width; - cinfo->output_height = cinfo->image_height; - cinfo->min_codec_data_unit = DCTSIZE; - } - /* In selecting the actual DCT scaling for each component, we try to - * scale up the chroma components via IDCT scaling rather than upsampling. - * This saves time if the upsampler gets to use 1:1 scaling. - * Note this code assumes that the supported DCT scalings are powers of 2. - */ - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - int ssize = cinfo->min_codec_data_unit; - while (ssize < DCTSIZE && - (compptr->h_samp_factor * ssize * 2 <= - cinfo->max_h_samp_factor * cinfo->min_codec_data_unit) && - (compptr->v_samp_factor * ssize * 2 <= - cinfo->max_v_samp_factor * cinfo->min_codec_data_unit)) { - ssize = ssize * 2; - } - compptr->codec_data_unit = ssize; - } - - /* Recompute downsampled dimensions of components; - * application needs to know these if using raw downsampled data. - */ - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - /* Size in samples, after IDCT scaling */ - compptr->downsampled_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width * - (long) (compptr->h_samp_factor * compptr->codec_data_unit), - (long) (cinfo->max_h_samp_factor * DCTSIZE)); - compptr->downsampled_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height * - (long) (compptr->v_samp_factor * compptr->codec_data_unit), - (long) (cinfo->max_v_samp_factor * DCTSIZE)); - } - -#else /* !IDCT_SCALING_SUPPORTED */ - - /* Hardwire it to "no scaling" */ - cinfo->output_width = cinfo->image_width; - cinfo->output_height = cinfo->image_height; - /* jdinput.c has already initialized codec_data_unit to DCTSIZE, - * and has computed unscaled downsampled_width and downsampled_height. - */ - -#endif /* IDCT_SCALING_SUPPORTED */ -} - - -/* - * Save away a copy of the Q-table referenced by each component present - * in the current scan, unless already saved during a prior scan. - * - * In a multiple-scan JPEG file, the encoder could assign different components - * the same Q-table slot number, but change table definitions between scans - * so that each component uses a different Q-table. (The IJG encoder is not - * currently capable of doing this, but other encoders might.) Since we want - * to be able to dequantize all the components at the end of the file, this - * means that we have to save away the table actually used for each component. - * We do this by copying the table at the start of the first scan containing - * the component. - * The JPEG spec prohibits the encoder from changing the contents of a Q-table - * slot between scans of a component using that slot. If the encoder does so - * anyway, this decoder will simply use the Q-table values that were current - * at the start of the first scan for the component. - * - * The decompressor output side looks only at the saved quant tables, - * not at the current Q-table slots. - */ - -LOCAL(void) -latch_quant_tables (j_decompress_ptr cinfo) -{ - int ci, qtblno; - jpeg_component_info *compptr; - JQUANT_TBL * qtbl; - - for (ci = 0; ci < cinfo->comps_in_scan; ci++) { - compptr = cinfo->cur_comp_info[ci]; - /* No work if we already saved Q-table for this component */ - if (compptr->quant_table != NULL) - continue; - /* Make sure specified quantization table is present */ - qtblno = compptr->quant_tbl_no; - if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS || - cinfo->quant_tbl_ptrs[qtblno] == NULL) - ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno); - /* OK, save away the quantization table */ - qtbl = (JQUANT_TBL *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(JQUANT_TBL)); - MEMCOPY(qtbl, cinfo->quant_tbl_ptrs[qtblno], SIZEOF(JQUANT_TBL)); - compptr->quant_table = qtbl; - } -} - - -/* - * Initialize for an input processing pass. - */ - -METHODDEF(void) -start_input_pass (j_decompress_ptr cinfo) -{ - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - - latch_quant_tables(cinfo); - (*lossyd->entropy_start_pass) (cinfo); - (*lossyd->coef_start_input_pass) (cinfo); -} - - -/* - * Initialize for an output processing pass. - */ - -METHODDEF(void) -start_output_pass (j_decompress_ptr cinfo) -{ - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - - (*lossyd->idct_start_pass) (cinfo); - (*lossyd->coef_start_output_pass) (cinfo); -} - -/* - * Initialize the lossy decompression codec. - * This is called only once, during master selection. - */ - -GLOBAL(void) -jinit_lossy_d_codec (j_decompress_ptr cinfo) -{ - j_lossy_d_ptr lossyd; - boolean use_c_buffer; - - /* Create subobject in permanent pool */ - lossyd = (j_lossy_d_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, - SIZEOF(jpeg_lossy_d_codec)); - cinfo->codec = (struct jpeg_d_codec *) lossyd; - - /* Initialize sub-modules */ - - /* Inverse DCT */ - jinit_inverse_dct(cinfo); - /* Entropy decoding: either Huffman or arithmetic coding. */ - if (cinfo->arith_code) { - ERREXIT(cinfo, JERR_ARITH_NOTIMPL); - } else { - if (cinfo->process == JPROC_PROGRESSIVE) { -#ifdef D_PROGRESSIVE_SUPPORTED - jinit_phuff_decoder(cinfo); -#else - ERREXIT(cinfo, JERR_NOT_COMPILED); -#endif - } else - jinit_shuff_decoder(cinfo); - } - - use_c_buffer = cinfo->inputctl->has_multiple_scans || cinfo->buffered_image; - jinit_d_coef_controller(cinfo, use_c_buffer); - - /* Initialize method pointers. - * - * Note: consume_data and decompress_data are assigned in jdcoefct.c. - */ - lossyd->pub.calc_output_dimensions = calc_output_dimensions; - lossyd->pub.start_input_pass = start_input_pass; - lossyd->pub.start_output_pass = start_output_pass; -} - - - - diff --git a/jdmainct.c b/jdmainct.c index 40ab10d31..d30064ae8 100644 --- a/jdmainct.c +++ b/jdmainct.c @@ -2,9 +2,9 @@ * jdmainct.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. + * Copyright (C) 1994-1996, Thomas G. Lane. * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains the main buffer controller for decompression. @@ -22,35 +22,36 @@ /* * 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. + * buffer; any full-height buffers will be found inside the coefficient, + * difference, 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 * codec_data_unit / min_codec_data_unit) - * sample rows of each component. (We require codec_data_unit values to be - * chosen such that these numbers are integers. In practice codec_data_unit + * 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 codec_data_unit / min_codec_data_unit is an integer.) + * 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 decompression codec will deliver data to us one iMCU row at a time; - * each iMCU row contains v_samp_factor * codec_data_unit sample rows, or - * exactly min_codec_data_unit 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. + * The coefficient or difference 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. + * obtain one iMCU row at a time from the coefficient or difference 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 @@ -66,7 +67,7 @@ * 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 codec_data_unit values, so that the actual scale factor at + * 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 @@ -76,7 +77,7 @@ * 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_codec_data_unit is the number + * 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: @@ -101,11 +102,11 @@ * 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_codec_data_unit is 1. In that + * 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_codec_data_unit is 1. That combination seems unlikely to + * 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. */ @@ -163,7 +164,7 @@ alloc_funny_pointers (j_decompress_ptr cinfo) { my_main_ptr main = (my_main_ptr) cinfo->main; int ci, rgroup; - int M = cinfo->min_codec_data_unit; + int M = cinfo->min_DCT_scaled_size; jpeg_component_info *compptr; JSAMPARRAY xbuf; @@ -177,8 +178,8 @@ alloc_funny_pointers (j_decompress_ptr cinfo) for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - rgroup = (compptr->v_samp_factor * compptr->codec_data_unit) / - cinfo->min_codec_data_unit; /* height of a row group of component */ + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ /* Get space for pointer lists --- M+4 row groups in each list. * We alloc both pointer lists with one call to save a few cycles. */ @@ -204,14 +205,14 @@ make_funny_pointers (j_decompress_ptr cinfo) { my_main_ptr main = (my_main_ptr) cinfo->main; int ci, i, rgroup; - int M = cinfo->min_codec_data_unit; + int M = cinfo->min_DCT_scaled_size; jpeg_component_info *compptr; JSAMPARRAY buf, xbuf0, xbuf1; for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - rgroup = (compptr->v_samp_factor * compptr->codec_data_unit) / - cinfo->min_codec_data_unit; /* height of a row group of component */ + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ xbuf0 = main->xbuffer[0][ci]; xbuf1 = main->xbuffer[1][ci]; /* First copy the workspace pointers as-is */ @@ -244,14 +245,14 @@ set_wraparound_pointers (j_decompress_ptr cinfo) { my_main_ptr main = (my_main_ptr) cinfo->main; int ci, i, rgroup; - int M = cinfo->min_codec_data_unit; + int M = cinfo->min_DCT_scaled_size; jpeg_component_info *compptr; JSAMPARRAY xbuf0, xbuf1; for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - rgroup = (compptr->v_samp_factor * compptr->codec_data_unit) / - cinfo->min_codec_data_unit; /* height of a row group of component */ + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ xbuf0 = main->xbuffer[0][ci]; xbuf1 = main->xbuffer[1][ci]; for (i = 0; i < rgroup; i++) { @@ -279,8 +280,8 @@ set_bottom_pointers (j_decompress_ptr cinfo) for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { /* Count sample rows in one iMCU row and in one row group */ - iMCUheight = compptr->v_samp_factor * compptr->codec_data_unit; - rgroup = iMCUheight / cinfo->min_codec_data_unit; + iMCUheight = compptr->v_samp_factor * compptr->DCT_scaled_size; + rgroup = iMCUheight / cinfo->min_DCT_scaled_size; /* Count nondummy sample rows remaining for this component */ rows_left = (int) (compptr->downsampled_height % (JDIMENSION) iMCUheight); if (rows_left == 0) rows_left = iMCUheight; @@ -353,13 +354,13 @@ process_data_simple_main (j_decompress_ptr cinfo, /* Read input data if we haven't filled the main buffer yet */ if (! main->buffer_full) { - if (! (*cinfo->codec->decompress_data) (cinfo, main->buffer)) + if (! (*cinfo->coef->decompress_data) (cinfo, main->buffer)) return; /* suspension forced, can do nothing more */ main->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ } - /* There are always min_codec_data_unit row groups in an iMCU row. */ - rowgroups_avail = (JDIMENSION) cinfo->min_codec_data_unit; + /* 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. @@ -392,7 +393,7 @@ process_data_context_main (j_decompress_ptr cinfo, /* Read input data if we haven't filled the main buffer yet */ if (! main->buffer_full) { - if (! (*cinfo->codec->decompress_data) (cinfo, + if (! (*cinfo->coef->decompress_data) (cinfo, main->xbuffer[main->whichptr])) return; /* suspension forced, can do nothing more */ main->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ @@ -419,7 +420,7 @@ process_data_context_main (j_decompress_ptr cinfo, case CTX_PREPARE_FOR_IMCU: /* Prepare to process first M-1 row groups of this iMCU row */ main->rowgroup_ctr = 0; - main->rowgroups_avail = (JDIMENSION) (cinfo->min_codec_data_unit - 1); + 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. */ @@ -442,8 +443,8 @@ process_data_context_main (j_decompress_ptr cinfo, 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_codec_data_unit + 1); - main->rowgroups_avail = (JDIMENSION) (cinfo->min_codec_data_unit + 2); + 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; } } @@ -494,21 +495,21 @@ jinit_d_main_controller (j_decompress_ptr cinfo, boolean need_full_buffer) * ngroups is the number of row groups we need. */ if (cinfo->upsample->need_context_rows) { - if (cinfo->min_codec_data_unit < 2) /* unsupported, see comments above */ + if (cinfo->min_DCT_scaled_size < 2) /* unsupported, see comments above */ ERREXIT(cinfo, JERR_NOTIMPL); alloc_funny_pointers(cinfo); /* Alloc space for xbuffer[] lists */ - ngroups = cinfo->min_codec_data_unit + 2; + ngroups = cinfo->min_DCT_scaled_size + 2; } else { - ngroups = cinfo->min_codec_data_unit; + ngroups = cinfo->min_DCT_scaled_size; } for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - rgroup = (compptr->v_samp_factor * compptr->codec_data_unit) / - cinfo->min_codec_data_unit; /* height of a row group of component */ + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ main->buffer[ci] = (*cinfo->mem->alloc_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, - compptr->width_in_data_units * compptr->codec_data_unit, + compptr->width_in_blocks * compptr->DCT_scaled_size, (JDIMENSION) (rgroup * ngroups)); } } diff --git a/jdmarker.c b/jdmarker.c index 62f7762da..5670817a7 100644 --- a/jdmarker.c +++ b/jdmarker.c @@ -5,6 +5,7 @@ * Copyright (C) 1991-1998, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains routines to decode JPEG datastream markers. @@ -236,8 +237,8 @@ get_soi (j_decompress_ptr cinfo) LOCAL(boolean) -get_sof (j_decompress_ptr cinfo, J_CODEC_PROCESS process, boolean is_arith, - int data_unit) +get_sof (j_decompress_ptr cinfo, boolean is_prog, boolean is_lossless, + boolean is_arith) /* Process a SOFn marker */ { INT32 length; @@ -245,8 +246,8 @@ get_sof (j_decompress_ptr cinfo, J_CODEC_PROCESS process, boolean is_arith, jpeg_component_info * compptr; INPUT_VARS(cinfo); - cinfo->data_unit = data_unit; - cinfo->process = process; + cinfo->progressive_mode = is_prog; + cinfo->master->lossless = is_lossless; cinfo->arith_code = is_arith; INPUT_2BYTES(cinfo, length, return FALSE); @@ -980,32 +981,32 @@ read_markers (j_decompress_ptr cinfo) case M_SOF0: /* Baseline */ case M_SOF1: /* Extended sequential, Huffman */ - if (! get_sof(cinfo, JPROC_SEQUENTIAL, FALSE, DCTSIZE)) + if (! get_sof(cinfo, FALSE, FALSE, FALSE)) return JPEG_SUSPENDED; break; case M_SOF2: /* Progressive, Huffman */ - if (! get_sof(cinfo, JPROC_PROGRESSIVE, FALSE, DCTSIZE)) + if (! get_sof(cinfo, TRUE, FALSE, FALSE)) return JPEG_SUSPENDED; break; case M_SOF3: /* Lossless, Huffman */ - if (! get_sof(cinfo, JPROC_LOSSLESS, FALSE, 1)) + if (! get_sof(cinfo, FALSE, TRUE, FALSE)) return JPEG_SUSPENDED; break; case M_SOF9: /* Extended sequential, arithmetic */ - if (! get_sof(cinfo, JPROC_SEQUENTIAL, TRUE, DCTSIZE)) + if (! get_sof(cinfo, FALSE, FALSE, TRUE)) return JPEG_SUSPENDED; break; case M_SOF10: /* Progressive, arithmetic */ - if (! get_sof(cinfo, JPROC_PROGRESSIVE, TRUE, DCTSIZE)) + if (! get_sof(cinfo, TRUE, FALSE, TRUE)) return JPEG_SUSPENDED; break; case M_SOF11: /* Lossless, arithmetic */ - if (! get_sof(cinfo, JPROC_LOSSLESS, TRUE, 1)) + if (! get_sof(cinfo, FALSE, TRUE, TRUE)) return JPEG_SUSPENDED; break; diff --git a/jdmaster.c b/jdmaster.c index 9c9842034..450d0d045 100644 --- a/jdmaster.c +++ b/jdmaster.c @@ -2,9 +2,10 @@ * jdmaster.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains master control logic for the JPEG decompressor. @@ -16,25 +17,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" - - -/* Private state */ - -typedef struct { - struct jpeg_decomp_master pub; /* public fields */ - - int pass_number; /* # of passes completed */ - - boolean using_merged_upsample; /* TRUE if using merged upsample/cconvert */ - - /* Saved references to initialized quantizer modules, - * in case we need to switch modes. - */ - struct jpeg_color_quantizer * quantizer_1pass; - struct jpeg_color_quantizer * quantizer_2pass; -} my_decomp_master; - -typedef my_decomp_master * my_master_ptr; +#include "jdmaster.h" /* @@ -62,11 +45,10 @@ use_merged_upsample (j_decompress_ptr cinfo) cinfo->comp_info[1].v_samp_factor != 1 || cinfo->comp_info[2].v_samp_factor != 1) return FALSE; - /* furthermore, it doesn't work if each component has been - processed differently */ - if (cinfo->comp_info[0].codec_data_unit != cinfo->min_codec_data_unit || - cinfo->comp_info[1].codec_data_unit != cinfo->min_codec_data_unit || - cinfo->comp_info[2].codec_data_unit != cinfo->min_codec_data_unit) + /* furthermore, it doesn't work if we've scaled the IDCTs differently */ + if (cinfo->comp_info[0].DCT_scaled_size != cinfo->min_DCT_scaled_size || + cinfo->comp_info[1].DCT_scaled_size != cinfo->min_DCT_scaled_size || + cinfo->comp_info[2].DCT_scaled_size != cinfo->min_DCT_scaled_size) return FALSE; /* ??? also need to test for upsample-time rescaling, when & if supported */ return TRUE; /* by golly, it'll work... */ @@ -87,11 +69,89 @@ GLOBAL(void) jpeg_calc_output_dimensions (j_decompress_ptr cinfo) /* Do computations that are needed before master selection phase */ { +#ifdef IDCT_SCALING_SUPPORTED + int ci; + jpeg_component_info *compptr; +#endif + /* Prevent application from calling me at wrong times */ if (cinfo->global_state != DSTATE_READY) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); - (*cinfo->codec->calc_output_dimensions) (cinfo); +#ifdef IDCT_SCALING_SUPPORTED + + if (! cinfo->master->lossless) { + /* Compute actual output image dimensions and DCT scaling choices. */ + if (cinfo->scale_num * 8 <= cinfo->scale_denom) { + /* Provide 1/8 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 8L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 8L); + cinfo->min_DCT_scaled_size = 1; + } else if (cinfo->scale_num * 4 <= cinfo->scale_denom) { + /* Provide 1/4 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 4L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 4L); + cinfo->min_DCT_scaled_size = 2; + } else if (cinfo->scale_num * 2 <= cinfo->scale_denom) { + /* Provide 1/2 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 2L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 2L); + cinfo->min_DCT_scaled_size = 4; + } else { + /* Provide 1/1 scaling */ + cinfo->output_width = cinfo->image_width; + cinfo->output_height = cinfo->image_height; + cinfo->min_DCT_scaled_size = DCTSIZE; + } + /* In selecting the actual DCT scaling for each component, we try to + * scale up the chroma components via IDCT scaling rather than upsampling. + * This saves time if the upsampler gets to use 1:1 scaling. + * Note this code assumes that the supported DCT scalings are powers of 2. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + int ssize = cinfo->min_DCT_scaled_size; + while (ssize < DCTSIZE && + (compptr->h_samp_factor * ssize * 2 <= + cinfo->max_h_samp_factor * cinfo->min_DCT_scaled_size) && + (compptr->v_samp_factor * ssize * 2 <= + cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size)) { + ssize = ssize * 2; + } + compptr->DCT_scaled_size = ssize; + } + + /* Recompute downsampled dimensions of components; + * application needs to know these if using raw downsampled data. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Size in samples, after IDCT scaling */ + compptr->downsampled_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * + (long) (compptr->h_samp_factor * compptr->DCT_scaled_size), + (long) (cinfo->max_h_samp_factor * DCTSIZE)); + compptr->downsampled_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * + (long) (compptr->v_samp_factor * compptr->DCT_scaled_size), + (long) (cinfo->max_v_samp_factor * DCTSIZE)); + } + } else +#endif /* !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. + */ + } /* Report number of components in selected colorspace. */ /* Probably this should be in the color conversion module... */ @@ -213,9 +273,21 @@ LOCAL(void) master_selection (j_decompress_ptr cinfo) { my_master_ptr master = (my_master_ptr) cinfo->master; + boolean use_c_buffer; long samplesperrow; JDIMENSION jd_samplesperrow; + /* Disable IDCT scaling and raw (downsampled) data output in lossless mode. + * IDCT scaling is not useful in lossless mode, and it must be disabled in + * order to properly calculate the output dimensions. Raw data output isn't + * particularly useful without subsampling and has not been tested in + * lossless mode. + */ + if (cinfo->master->lossless) { + cinfo->raw_data_out = FALSE; + cinfo->scale_num = cinfo->scale_denom = 1; + } + /* Initialize dimensions and other stuff */ jpeg_calc_output_dimensions(cinfo); prepare_range_limit_table(cinfo); @@ -294,7 +366,49 @@ master_selection (j_decompress_ptr cinfo) jinit_d_post_controller(cinfo, cinfo->enable_2pass_quant); } - /* Initialize principal buffer controllers. */ + if (cinfo->master->lossless) { +#ifdef D_LOSSLESS_SUPPORTED + /* Prediction, sample undifferencing, point transform, and sample size + * scaling + */ + jinit_lossless_decompressor(cinfo); + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + jinit_lhuff_decoder(cinfo); + } + + /* Initialize principal buffer controllers. */ + use_c_buffer = cinfo->inputctl->has_multiple_scans || + cinfo->buffered_image; + jinit_d_diff_controller(cinfo, use_c_buffer); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + /* Inverse DCT */ + jinit_inverse_dct(cinfo); + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef D_PROGRESSIVE_SUPPORTED + jinit_phuff_decoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_decoder(cinfo); + } + + /* Initialize principal buffer controllers. */ + use_c_buffer = cinfo->inputctl->has_multiple_scans || + cinfo->buffered_image; + jinit_d_coef_controller(cinfo, use_c_buffer); + } + if (! cinfo->raw_data_out) jinit_d_main_controller(cinfo, FALSE /* never need full buffer here */); @@ -313,7 +427,7 @@ master_selection (j_decompress_ptr cinfo) cinfo->inputctl->has_multiple_scans) { int nscans; /* Estimate number of scans to set pass_limit. */ - if (cinfo->process == JPROC_PROGRESSIVE) { + if (cinfo->progressive_mode) { /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ nscans = 2 + 3 * cinfo->num_components; } else { @@ -367,7 +481,8 @@ prepare_for_output_pass (j_decompress_ptr cinfo) ERREXIT(cinfo, JERR_MODE_CHANGE); } } - (*cinfo->codec->start_output_pass) (cinfo); + (*cinfo->idct->start_pass) (cinfo); + (*cinfo->coef->start_output_pass) (cinfo); if (! cinfo->raw_data_out) { if (! master->using_merged_upsample) (*cinfo->cconvert->start_pass) (cinfo); @@ -447,12 +562,8 @@ jpeg_new_colormap (j_decompress_ptr cinfo) GLOBAL(void) jinit_master_decompress (j_decompress_ptr cinfo) { - my_master_ptr master; + my_master_ptr master = (my_master_ptr) cinfo->master; - master = (my_master_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(my_decomp_master)); - cinfo->master = (struct jpeg_decomp_master *) master; master->pub.prepare_for_output_pass = prepare_for_output_pass; master->pub.finish_output_pass = finish_output_pass; diff --git a/jdmaster.h b/jdmaster.h new file mode 100644 index 000000000..cab0932c8 --- /dev/null +++ b/jdmaster.h @@ -0,0 +1,27 @@ +/* + * jdmaster.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1991-1995, Thomas G. Lane. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the master control structure for the JPEG decompressor. + */ + +/* Private state */ + +typedef struct { + struct jpeg_decomp_master pub; /* public fields */ + + int pass_number; /* # of passes completed */ + + boolean using_merged_upsample; /* TRUE if using merged upsample/cconvert */ + + /* Saved references to initialized quantizer modules, + * in case we need to switch modes. + */ + struct jpeg_color_quantizer * quantizer_1pass; + struct jpeg_color_quantizer * quantizer_2pass; +} my_decomp_master; + +typedef my_decomp_master * my_master_ptr; diff --git a/jdphuff.c b/jdphuff.c index 528313f57..b4d87f355 100644 --- a/jdphuff.c +++ b/jdphuff.c @@ -2,7 +2,7 @@ * jdphuff.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1995-1998, Thomas G. Lane. + * Copyright (C) 1995-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. * For conditions of distribution and use, see the accompanying README file. @@ -19,14 +19,13 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossy.h" /* Private declarations for lossy subsystem */ #include "jdhuff.h" /* Declarations shared with jd*huff.c */ #ifdef D_PROGRESSIVE_SUPPORTED /* - * Private entropy decoder object for progressive Huffman decoding. + * 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. @@ -57,11 +56,12 @@ typedef struct { typedef struct { - huffd_common_fields; /* Fields shared with other entropy decoders */ + struct jpeg_entropy_decoder pub; /* public fields */ /* These fields are loaded into local variables at start of each MCU. * In case of suspension, we exit WITHOUT updating them. */ + bitread_perm_state bitstate; /* Bit buffer at start of MCU */ savable_state saved; /* Other state at start of MCU */ /* These fields are NOT loaded into local working state. */ @@ -93,8 +93,7 @@ METHODDEF(boolean) decode_mcu_AC_refine JPP((j_decompress_ptr cinfo, METHODDEF(void) start_pass_phuff_decoder (j_decompress_ptr cinfo) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyd->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; boolean is_DC_band, bad; int ci, coefi, tbl; int *coef_bit_ptr; @@ -151,14 +150,14 @@ start_pass_phuff_decoder (j_decompress_ptr cinfo) /* Select MCU decoding routine */ if (cinfo->Ah == 0) { if (is_DC_band) - lossyd->entropy_decode_mcu = decode_mcu_DC_first; + entropy->pub.decode_mcu = decode_mcu_DC_first; else - lossyd->entropy_decode_mcu = decode_mcu_AC_first; + entropy->pub.decode_mcu = decode_mcu_AC_first; } else { if (is_DC_band) - lossyd->entropy_decode_mcu = decode_mcu_DC_refine; + entropy->pub.decode_mcu = decode_mcu_DC_refine; else - lossyd->entropy_decode_mcu = decode_mcu_AC_refine; + entropy->pub.decode_mcu = decode_mcu_AC_refine; } for (ci = 0; ci < cinfo->comps_in_scan; ci++) { @@ -186,7 +185,7 @@ start_pass_phuff_decoder (j_decompress_ptr cinfo) /* Initialize bitread state variables */ entropy->bitstate.bits_left = 0; entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ - entropy->insufficient_data = FALSE; + entropy->pub.insufficient_data = FALSE; /* Initialize private state variables */ entropy->saved.EOBRUN = 0; @@ -230,8 +229,7 @@ static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ LOCAL(boolean) process_restart (j_decompress_ptr cinfo) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyd->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; int ci; /* Throw away any unused bits remaining in bit buffer; */ @@ -258,7 +256,7 @@ process_restart (j_decompress_ptr cinfo) * leaving the flag set. */ if (cinfo->unread_marker == 0) - entropy->insufficient_data = FALSE; + entropy->pub.insufficient_data = FALSE; return TRUE; } @@ -289,8 +287,7 @@ process_restart (j_decompress_ptr cinfo) METHODDEF(boolean) decode_mcu_DC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyd->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; int Al = cinfo->Al; register int s, r; int blkn, ci; @@ -310,7 +307,7 @@ decode_mcu_DC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) /* If we've run out of data, just leave the MCU set to zeroes. * This way, we return uniform gray for the remainder of the segment. */ - if (! entropy->insufficient_data) { + if (! entropy->pub.insufficient_data) { /* Load up working state */ BITREAD_LOAD_STATE(cinfo,entropy->bitstate); @@ -318,7 +315,7 @@ decode_mcu_DC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) /* Outer loop handles each block in the MCU */ - for (blkn = 0; blkn < cinfo->data_units_in_MCU; blkn++) { + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { block = MCU_data[blkn]; ci = cinfo->MCU_membership[blkn]; compptr = cinfo->cur_comp_info[ci]; @@ -361,8 +358,7 @@ decode_mcu_DC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) METHODDEF(boolean) decode_mcu_AC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyd->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; int Se = cinfo->Se; int Al = cinfo->Al; register int s, k, r; @@ -381,7 +377,7 @@ decode_mcu_AC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) /* If we've run out of data, just leave the MCU set to zeroes. * This way, we return uniform gray for the remainder of the segment. */ - if (! entropy->insufficient_data) { + if (! entropy->pub.insufficient_data) { /* Load up working state. * We can avoid loading/saving bitread state if in an EOB run. @@ -447,8 +443,7 @@ decode_mcu_AC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) METHODDEF(boolean) decode_mcu_DC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyd->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; int p1 = 1 << cinfo->Al; /* 1 in the bit position being coded */ int blkn; JBLOCKROW block; @@ -470,7 +465,7 @@ decode_mcu_DC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) /* Outer loop handles each block in the MCU */ - for (blkn = 0; blkn < cinfo->data_units_in_MCU; blkn++) { + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { block = MCU_data[blkn]; /* Encoded data is simply the next bit of the two's-complement DC value */ @@ -497,8 +492,7 @@ decode_mcu_DC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) METHODDEF(boolean) decode_mcu_AC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - phuff_entropy_ptr entropy = (phuff_entropy_ptr) lossyd->entropy_private; + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; int Se = cinfo->Se; int p1 = 1 << cinfo->Al; /* 1 in the bit position being coded */ int m1 = (-1) << cinfo->Al; /* -1 in the bit position being coded */ @@ -520,7 +514,7 @@ decode_mcu_AC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) /* If we've run out of data, don't modify the MCU. */ - if (! entropy->insufficient_data) { + if (! entropy->pub.insufficient_data) { /* Load up working state */ BITREAD_LOAD_STATE(cinfo,entropy->bitstate); @@ -648,7 +642,6 @@ decode_mcu_AC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) GLOBAL(void) jinit_phuff_decoder (j_decompress_ptr cinfo) { - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; phuff_entropy_ptr entropy; int *coef_bit_ptr; int ci, i; @@ -656,8 +649,8 @@ jinit_phuff_decoder (j_decompress_ptr cinfo) entropy = (phuff_entropy_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, SIZEOF(phuff_entropy_decoder)); - lossyd->entropy_private = (void *) entropy; - lossyd->entropy_start_pass = start_pass_phuff_decoder; + cinfo->entropy = (struct jpeg_entropy_decoder *) entropy; + entropy->pub.start_pass = start_pass_phuff_decoder; /* Mark derived tables unallocated */ for (i = 0; i < NUM_HUFF_TBLS; i++) { diff --git a/jdpred.c b/jdpred.c deleted file mode 100644 index 796d1950b..000000000 --- a/jdpred.c +++ /dev/null @@ -1,249 +0,0 @@ -/* - * jdpred.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains sample undifferencing (reconstruction) for lossless JPEG. - * - * In order to avoid paying the performance penalty of having to check the - * predictor being used and the row being processed for each call of the - * undifferencer, and to promote optimization, we have separate undifferencing - * functions for each case. - * - * We are able to avoid duplicating source code by implementing the predictors - * and undifferencers as macros. Each of the undifferencing functions are - * simply wrappers around an UNDIFFERENCE macro with the appropriate PREDICTOR - * macro passed as an argument. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jlossls.h" /* Private declarations for lossless codec */ - - -#ifdef D_LOSSLESS_SUPPORTED - -/* Predictor for the first column of the first row: 2^(P-Pt-1) */ -#define INITIAL_PREDICTORx (1 << (cinfo->data_precision - cinfo->Al - 1)) - -/* Predictor for the first column of the remaining rows: Rb */ -#define INITIAL_PREDICTOR2 GETJSAMPLE(prev_row[0]) - - -/* - * 1-Dimensional undifferencer routine. - * - * This macro implements the 1-D horizontal predictor (1). INITIAL_PREDICTOR - * is used as the special case predictor for the first column, which must be - * either INITIAL_PREDICTOR2 or INITIAL_PREDICTORx. The remaining samples - * use PREDICTOR1. - * - * The reconstructed sample is supposed to be calculated modulo 2^16, so we - * logically AND the result with 0xFFFF. -*/ - -#define UNDIFFERENCE_1D(INITIAL_PREDICTOR) \ - int xindex; \ - int Ra; \ - \ - Ra = (diff_buf[0] + INITIAL_PREDICTOR) & 0xFFFF; \ - undiff_buf[0] = Ra; \ - \ - for (xindex = 1; xindex < width; xindex++) { \ - Ra = (diff_buf[xindex] + PREDICTOR1) & 0xFFFF; \ - undiff_buf[xindex] = Ra; \ - } - -/* - * 2-Dimensional undifferencer routine. - * - * This macro implements the 2-D horizontal predictors (#2-7). PREDICTOR2 is - * used as the special case predictor for the first column. The remaining - * samples use PREDICTOR, which is a function of Ra, Rb, Rc. - * - * Because prev_row and output_buf may point to the same storage area (in an - * interleaved image with Vi=1, for example), we must take care to buffer Rb/Rc - * before writing the current reconstructed sample value into output_buf. - * - * The reconstructed sample is supposed to be calculated modulo 2^16, so we - * logically AND the result with 0xFFFF. - */ - -#define UNDIFFERENCE_2D(PREDICTOR) \ - int xindex; \ - int Ra, Rb, Rc; \ - \ - Rb = GETJSAMPLE(prev_row[0]); \ - Ra = (diff_buf[0] + PREDICTOR2) & 0xFFFF; \ - undiff_buf[0] = Ra; \ - \ - for (xindex = 1; xindex < width; xindex++) { \ - Rc = Rb; \ - Rb = GETJSAMPLE(prev_row[xindex]); \ - Ra = (diff_buf[xindex] + PREDICTOR) & 0xFFFF; \ - undiff_buf[xindex] = Ra; \ - } - - -/* - * Undifferencers for the all rows but the first in a scan or restart interval. - * The first sample in the row is undifferenced using the vertical - * predictor (2). The rest of the samples are undifferenced using the - * predictor specified in the scan header. - */ - -METHODDEF(void) -jpeg_undifference1(j_decompress_ptr cinfo, int comp_index, - JDIFFROW diff_buf, JDIFFROW prev_row, - JDIFFROW undiff_buf, JDIMENSION width) -{ - UNDIFFERENCE_1D(INITIAL_PREDICTOR2); -} - -METHODDEF(void) -jpeg_undifference2(j_decompress_ptr cinfo, int comp_index, - JDIFFROW diff_buf, JDIFFROW prev_row, - JDIFFROW undiff_buf, JDIMENSION width) -{ - UNDIFFERENCE_2D(PREDICTOR2); -} - -METHODDEF(void) -jpeg_undifference3(j_decompress_ptr cinfo, int comp_index, - JDIFFROW diff_buf, JDIFFROW prev_row, - JDIFFROW undiff_buf, JDIMENSION width) -{ - UNDIFFERENCE_2D(PREDICTOR3); -} - -METHODDEF(void) -jpeg_undifference4(j_decompress_ptr cinfo, int comp_index, - JDIFFROW diff_buf, JDIFFROW prev_row, - JDIFFROW undiff_buf, JDIMENSION width) -{ - UNDIFFERENCE_2D(PREDICTOR4); -} - -METHODDEF(void) -jpeg_undifference5(j_decompress_ptr cinfo, int comp_index, - JDIFFROW diff_buf, JDIFFROW prev_row, - JDIFFROW undiff_buf, JDIMENSION width) -{ - UNDIFFERENCE_2D(PREDICTOR5); -} - -METHODDEF(void) -jpeg_undifference6(j_decompress_ptr cinfo, int comp_index, - JDIFFROW diff_buf, JDIFFROW prev_row, - JDIFFROW undiff_buf, JDIMENSION width) -{ - UNDIFFERENCE_2D(PREDICTOR6); -} - -METHODDEF(void) -jpeg_undifference7(j_decompress_ptr cinfo, int comp_index, - JDIFFROW diff_buf, JDIFFROW prev_row, - JDIFFROW undiff_buf, JDIMENSION width) -{ - UNDIFFERENCE_2D(PREDICTOR7); -} - - -/* - * Undifferencer for the first row in a scan or restart interval. The first - * sample in the row is undifferenced using the special predictor constant - * x=2^(P-Pt-1). The rest of the samples are undifferenced using the - * 1-D horizontal predictor (1). - */ - -METHODDEF(void) -jpeg_undifference_first_row(j_decompress_ptr cinfo, int comp_index, - JDIFFROW diff_buf, JDIFFROW prev_row, - JDIFFROW undiff_buf, JDIMENSION width) -{ - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - - UNDIFFERENCE_1D(INITIAL_PREDICTORx); - - /* - * Now that we have undifferenced the first row, we want to use the - * undifferencer which corresponds to the predictor specified in the - * scan header. - */ - switch (cinfo->Ss) { - case 1: - losslsd->predict_undifference[comp_index] = jpeg_undifference1; - break; - case 2: - losslsd->predict_undifference[comp_index] = jpeg_undifference2; - break; - case 3: - losslsd->predict_undifference[comp_index] = jpeg_undifference3; - break; - case 4: - losslsd->predict_undifference[comp_index] = jpeg_undifference4; - break; - case 5: - losslsd->predict_undifference[comp_index] = jpeg_undifference5; - break; - case 6: - losslsd->predict_undifference[comp_index] = jpeg_undifference6; - break; - case 7: - losslsd->predict_undifference[comp_index] = jpeg_undifference7; - break; - } -} - - -/* - * Initialize for an input processing pass. - */ - -METHODDEF(void) -predict_start_pass (j_decompress_ptr cinfo) -{ - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - int ci; - - /* Check that the scan parameters Ss, Se, Ah, Al are OK for lossless JPEG. - * - * Ss is the predictor selection value (psv). Legal values for sequential - * lossless JPEG are: 1 <= psv <= 7. - * - * Se and Ah are not used and should be zero. - * - * Al specifies the point transform (Pt). Legal values are: 0 <= Pt <= 15. - */ - if (cinfo->Ss < 1 || cinfo->Ss > 7 || - cinfo->Se != 0 || cinfo->Ah != 0 || - cinfo->Al > 15) /* need not check for < 0 */ - ERREXIT4(cinfo, JERR_BAD_LOSSLESS, - cinfo->Ss, cinfo->Se, cinfo->Ah, cinfo->Al); - - /* Set undifference functions to first row function */ - for (ci = 0; ci < cinfo->num_components; ci++) - losslsd->predict_undifference[ci] = jpeg_undifference_first_row; -} - - -/* - * Module initialization routine for the undifferencer. - */ - -GLOBAL(void) -jinit_undifferencer (j_decompress_ptr cinfo) -{ - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - - losslsd->predict_start_pass = predict_start_pass; - losslsd->predict_process_restart = predict_start_pass; -} - -#endif /* D_LOSSLESS_SUPPORTED */ - diff --git a/jdsample.c b/jdsample.c index 14c3f8bb0..80ffefb2a 100644 --- a/jdsample.c +++ b/jdsample.c @@ -1,16 +1,14 @@ /* * jdsample.c * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * * This file contains upsampling routines. * * Upsampling input data is counted in "row groups". A row group - * is defined to be (v_samp_factor * codec_data_unit / min_codec_data_unit) + * 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). @@ -417,10 +415,10 @@ jinit_upsampler (j_decompress_ptr cinfo) if (cinfo->CCIR601_sampling) /* this isn't supported */ ERREXIT(cinfo, JERR_CCIR601_NOTIMPL); - /* jdmainct.c doesn't support context rows when min_codec_data_unit = 1, + /* jdmainct.c doesn't support context rows when min_DCT_scaled_size = 1, * so don't ask for it. */ - do_fancy = cinfo->do_fancy_upsampling && cinfo->min_codec_data_unit > 1; + do_fancy = cinfo->do_fancy_upsampling && cinfo->min_DCT_scaled_size > 1; /* Verify we can handle the sampling factors, select per-component methods, * and create storage as needed. @@ -430,10 +428,10 @@ jinit_upsampler (j_decompress_ptr cinfo) /* 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->codec_data_unit) / - cinfo->min_codec_data_unit; - v_in_group = (compptr->v_samp_factor * compptr->codec_data_unit) / - cinfo->min_codec_data_unit; + h_in_group = (compptr->h_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; + v_in_group = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; h_out_group = cinfo->max_h_samp_factor; v_out_group = cinfo->max_v_samp_factor; upsample->rowgroup_height[ci] = v_in_group; /* save for use later */ diff --git a/jdscale.c b/jdscale.c deleted file mode 100644 index 4025d6182..000000000 --- a/jdscale.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * jdscale.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains sample scaling for lossless JPEG. This is a - * combination of upscaling the undifferenced sample by 2^Pt and downscaling - * the sample to fit into JSAMPLE. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jlossls.h" /* Private declarations for lossless codec */ - - -#ifdef D_LOSSLESS_SUPPORTED - -/* - * Private scaler object for lossless decoding. - */ - -typedef struct { - int scale_factor; -} scaler; - -typedef scaler * scaler_ptr; - - -/* - * Scalers for packing sample differences into JSAMPLEs. - */ - -METHODDEF(void) -simple_upscale(j_decompress_ptr cinfo, - JDIFFROW diff_buf, JSAMPROW output_buf, - JDIMENSION width) -{ - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - scaler_ptr scaler = (scaler_ptr) losslsd->scaler_private; - int scale_factor = scaler->scale_factor; - int xindex; - - for (xindex = 0; xindex < width; xindex++) - output_buf[xindex] = (JSAMPLE) (diff_buf[xindex] << scale_factor); -} - -METHODDEF(void) -simple_downscale(j_decompress_ptr cinfo, - JDIFFROW diff_buf, JSAMPROW output_buf, - JDIMENSION width) -{ - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - scaler_ptr scaler = (scaler_ptr) losslsd->scaler_private; - int scale_factor = scaler->scale_factor; - int xindex; - - for (xindex = 0; xindex < width; xindex++) - output_buf[xindex] = (JSAMPLE) RIGHT_SHIFT(diff_buf[xindex], scale_factor); -} - -METHODDEF(void) -noscale(j_decompress_ptr cinfo, - JDIFFROW diff_buf, JSAMPROW output_buf, - JDIMENSION width) -{ - int xindex; - - for (xindex = 0; xindex < width; xindex++) - output_buf[xindex] = (JSAMPLE) diff_buf[xindex]; -} - - -METHODDEF(void) -scaler_start_pass (j_decompress_ptr cinfo) -{ - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - scaler_ptr scaler = (scaler_ptr) losslsd->scaler_private; - int downscale; - - /* - * Downscale by the difference in the input vs. output precision. If the - * output precision >= input precision, then do not downscale. - */ - downscale = BITS_IN_JSAMPLE < cinfo->data_precision ? - cinfo->data_precision - BITS_IN_JSAMPLE : 0; - - scaler->scale_factor = cinfo->Al - downscale; - - /* Set scaler functions based on scale_factor (positive = left shift) */ - if (scaler->scale_factor > 0) - losslsd->scaler_scale = simple_upscale; - else if (scaler->scale_factor < 0) { - scaler->scale_factor = -scaler->scale_factor; - losslsd->scaler_scale = simple_downscale; - } - else - losslsd->scaler_scale = noscale; -} - - -GLOBAL(void) -jinit_d_scaler (j_decompress_ptr cinfo) -{ - j_lossless_d_ptr losslsd = (j_lossless_d_ptr) cinfo->codec; - scaler_ptr scaler; - - scaler = (scaler_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(scaler)); - losslsd->scaler_private = (void *) scaler; - losslsd->scaler_start_pass = scaler_start_pass; -} - -#endif /* D_LOSSLESS_SUPPORTED */ - diff --git a/jdshuff.c b/jdshuff.c deleted file mode 100644 index d15a6d155..000000000 --- a/jdshuff.c +++ /dev/null @@ -1,362 +0,0 @@ -/* - * jdshuff.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains Huffman entropy decoding routines for sequential JPEG. - * - * Much of the complexity here has to do with supporting input suspension. - * If the data source module demands suspension, we want to be able to back - * up to the start of the current MCU. To do this, we copy state variables - * into local working storage, and update them back to the permanent - * storage only upon successful completion of an MCU. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jlossy.h" /* Private declarations for lossy codec */ -#include "jdhuff.h" /* Declarations shared with jd*huff.c */ - - -/* - * Private entropy decoder object for Huffman decoding. - * - * The savable_state subrecord contains fields that change within an MCU, - * but must not be updated permanently until we complete the MCU. - */ - -typedef struct { - int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ -} savable_state; - -/* This macro is to work around compilers with missing or broken - * structure assignment. You'll need to fix this code if you have - * such a compiler and you change MAX_COMPS_IN_SCAN. - */ - -#ifndef NO_STRUCT_ASSIGN -#define ASSIGN_STATE(dest,src) ((dest) = (src)) -#else -#if MAX_COMPS_IN_SCAN == 4 -#define ASSIGN_STATE(dest,src) \ - ((dest).last_dc_val[0] = (src).last_dc_val[0], \ - (dest).last_dc_val[1] = (src).last_dc_val[1], \ - (dest).last_dc_val[2] = (src).last_dc_val[2], \ - (dest).last_dc_val[3] = (src).last_dc_val[3]) -#endif -#endif - - -typedef struct { - huffd_common_fields; /* Fields shared with other entropy decoders */ - - /* These fields are loaded into local variables at start of each MCU. - * In case of suspension, we exit WITHOUT updating them. - */ - savable_state saved; /* Other state at start of MCU */ - - /* These fields are NOT loaded into local working state. */ - unsigned int restarts_to_go; /* MCUs left in this restart interval */ - - /* Pointers to derived tables (these workspaces have image lifespan) */ - d_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; - d_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; - - /* Precalculated info set up by start_pass for use in decode_mcu: */ - - /* Pointers to derived tables to be used for each block within an MCU */ - d_derived_tbl * dc_cur_tbls[D_MAX_DATA_UNITS_IN_MCU]; - d_derived_tbl * ac_cur_tbls[D_MAX_DATA_UNITS_IN_MCU]; - /* Whether we care about the DC and AC coefficient values for each block */ - boolean dc_needed[D_MAX_DATA_UNITS_IN_MCU]; - boolean ac_needed[D_MAX_DATA_UNITS_IN_MCU]; -} shuff_entropy_decoder; - -typedef shuff_entropy_decoder * shuff_entropy_ptr; - - -/* - * Initialize for a Huffman-compressed scan. - */ - -METHODDEF(void) -start_pass_huff_decoder (j_decompress_ptr cinfo) -{ - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - shuff_entropy_ptr entropy = (shuff_entropy_ptr) lossyd->entropy_private; - int ci, blkn, dctbl, actbl; - jpeg_component_info * compptr; - - /* Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG. - * This ought to be an error condition, but we make it a warning because - * there are some baseline files out there with all zeroes in these bytes. - */ - if (cinfo->Ss != 0 || cinfo->Se != DCTSIZE2-1 || - cinfo->Ah != 0 || cinfo->Al != 0) - WARNMS(cinfo, JWRN_NOT_SEQUENTIAL); - - for (ci = 0; ci < cinfo->comps_in_scan; ci++) { - compptr = cinfo->cur_comp_info[ci]; - dctbl = compptr->dc_tbl_no; - actbl = compptr->ac_tbl_no; - /* Compute derived values for Huffman tables */ - /* We may do this more than once for a table, but it's not expensive */ - jpeg_make_d_derived_tbl(cinfo, TRUE, dctbl, - & entropy->dc_derived_tbls[dctbl]); - jpeg_make_d_derived_tbl(cinfo, FALSE, actbl, - & entropy->ac_derived_tbls[actbl]); - /* Initialize DC predictions to 0 */ - entropy->saved.last_dc_val[ci] = 0; - } - - /* Precalculate decoding info for each block in an MCU of this scan */ - for (blkn = 0; blkn < cinfo->data_units_in_MCU; blkn++) { - ci = cinfo->MCU_membership[blkn]; - compptr = cinfo->cur_comp_info[ci]; - /* Precalculate which table to use for each block */ - entropy->dc_cur_tbls[blkn] = entropy->dc_derived_tbls[compptr->dc_tbl_no]; - entropy->ac_cur_tbls[blkn] = entropy->ac_derived_tbls[compptr->ac_tbl_no]; - /* Decide whether we really care about the coefficient values */ - if (compptr->component_needed) { - entropy->dc_needed[blkn] = TRUE; - /* we don't need the ACs if producing a 1/8th-size image */ - entropy->ac_needed[blkn] = (compptr->codec_data_unit > 1); - } else { - entropy->dc_needed[blkn] = entropy->ac_needed[blkn] = FALSE; - } - } - - /* Initialize bitread state variables */ - entropy->bitstate.bits_left = 0; - entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ - entropy->insufficient_data = FALSE; - - /* Initialize restart counter */ - entropy->restarts_to_go = cinfo->restart_interval; -} - - -/* - * Figure F.12: extend sign bit. - * On some machines, a shift and add will be faster than a table lookup. - */ - -#ifdef AVOID_TABLES - -#define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x)) - -#else - -#define HUFF_EXTEND(x,s) ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)) - -static const int extend_test[16] = /* entry n is 2**(n-1) */ - { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, - 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; - -static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ - { 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, - ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, - ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, - ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 }; - -#endif /* AVOID_TABLES */ - - -/* - * Check for a restart marker & resynchronize decoder. - * Returns FALSE if must suspend. - */ - -LOCAL(boolean) -process_restart (j_decompress_ptr cinfo) -{ - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - shuff_entropy_ptr entropy = (shuff_entropy_ptr) lossyd->entropy_private; - int ci; - - /* Throw away any unused bits remaining in bit buffer; */ - /* include any full bytes in next_marker's count of discarded bytes */ - cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8; - entropy->bitstate.bits_left = 0; - - /* Advance past the RSTn marker */ - if (! (*cinfo->marker->read_restart_marker) (cinfo)) - return FALSE; - - /* Re-initialize DC predictions to 0 */ - for (ci = 0; ci < cinfo->comps_in_scan; ci++) - entropy->saved.last_dc_val[ci] = 0; - - /* Reset restart counter */ - entropy->restarts_to_go = cinfo->restart_interval; - - /* Reset out-of-data flag, unless read_restart_marker left us smack up - * against a marker. In that case we will end up treating the next data - * segment as empty, and we can avoid producing bogus output pixels by - * leaving the flag set. - */ - if (cinfo->unread_marker == 0) - entropy->insufficient_data = FALSE; - - return TRUE; -} - - -/* - * Decode and return one MCU's worth of Huffman-compressed coefficients. - * The coefficients are reordered from zigzag order into natural array order, - * but are not dequantized. - * - * The i'th block of the MCU is stored into the block pointed to by - * MCU_data[i]. WE ASSUME THIS AREA HAS BEEN ZEROED BY THE CALLER. - * (Wholesale zeroing is usually a little faster than retail...) - * - * Returns FALSE if data source requested suspension. In that case no - * changes have been made to permanent state. (Exception: some output - * coefficients may already have been assigned. This is harmless for - * this module, since we'll just re-assign them on the next call.) - */ - -METHODDEF(boolean) -decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) -{ - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - shuff_entropy_ptr entropy = (shuff_entropy_ptr) lossyd->entropy_private; - int blkn; - BITREAD_STATE_VARS; - savable_state state; - - /* Process restart marker if needed; may have to suspend */ - if (cinfo->restart_interval) { - if (entropy->restarts_to_go == 0) - if (! process_restart(cinfo)) - return FALSE; - } - - /* If we've run out of data, just leave the MCU set to zeroes. - * This way, we return uniform gray for the remainder of the segment. - */ - if (! entropy->insufficient_data) { - - /* Load up working state */ - BITREAD_LOAD_STATE(cinfo,entropy->bitstate); - ASSIGN_STATE(state, entropy->saved); - - /* Outer loop handles each block in the MCU */ - - for (blkn = 0; blkn < cinfo->data_units_in_MCU; blkn++) { - JBLOCKROW block = MCU_data[blkn]; - d_derived_tbl * dctbl = entropy->dc_cur_tbls[blkn]; - d_derived_tbl * actbl = entropy->ac_cur_tbls[blkn]; - register int s, k, r; - - /* Decode a single block's worth of coefficients */ - - /* Section F.2.2.1: decode the DC coefficient difference */ - HUFF_DECODE(s, br_state, dctbl, return FALSE, label1); - if (s) { - CHECK_BIT_BUFFER(br_state, s, return FALSE); - r = GET_BITS(s); - s = HUFF_EXTEND(r, s); - } - - if (entropy->dc_needed[blkn]) { - /* Convert DC difference to actual value, update last_dc_val */ - int ci = cinfo->MCU_membership[blkn]; - s += state.last_dc_val[ci]; - state.last_dc_val[ci] = s; - /* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */ - (*block)[0] = (JCOEF) s; - } - - if (entropy->ac_needed[blkn]) { - - /* Section F.2.2.2: decode the AC coefficients */ - /* Since zeroes are skipped, output area must be cleared beforehand */ - for (k = 1; k < DCTSIZE2; k++) { - HUFF_DECODE(s, br_state, actbl, return FALSE, label2); - - r = s >> 4; - s &= 15; - - if (s) { - k += r; - CHECK_BIT_BUFFER(br_state, s, return FALSE); - r = GET_BITS(s); - s = HUFF_EXTEND(r, s); - /* Output coefficient in natural (dezigzagged) order. - * Note: the extra entries in jpeg_natural_order[] will save us - * if k >= DCTSIZE2, which could happen if the data is corrupted. - */ - (*block)[jpeg_natural_order[k]] = (JCOEF) s; - } else { - if (r != 15) - break; - k += 15; - } - } - - } else { - - /* Section F.2.2.2: decode the AC coefficients */ - /* In this path we just discard the values */ - for (k = 1; k < DCTSIZE2; k++) { - HUFF_DECODE(s, br_state, actbl, return FALSE, label3); - - r = s >> 4; - s &= 15; - - if (s) { - k += r; - CHECK_BIT_BUFFER(br_state, s, return FALSE); - DROP_BITS(s); - } else { - if (r != 15) - break; - k += 15; - } - } - - } - } - - /* Completed MCU, so update state */ - BITREAD_SAVE_STATE(cinfo,entropy->bitstate); - ASSIGN_STATE(entropy->saved, state); - } - - /* Account for restart interval (no-op if not using restarts) */ - entropy->restarts_to_go--; - - return TRUE; -} - - -/* - * Module initialization routine for Huffman entropy decoding. - */ - -GLOBAL(void) -jinit_shuff_decoder (j_decompress_ptr cinfo) -{ - j_lossy_d_ptr lossyd = (j_lossy_d_ptr) cinfo->codec; - shuff_entropy_ptr entropy; - int i; - - entropy = (shuff_entropy_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - SIZEOF(shuff_entropy_decoder)); - lossyd->entropy_private = (void *) entropy; - lossyd->entropy_start_pass = start_pass_huff_decoder; - lossyd->entropy_decode_mcu = decode_mcu; - - /* Mark tables unallocated */ - for (i = 0; i < NUM_HUFF_TBLS; i++) { - entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL; - } -} diff --git a/jdtrans.c b/jdtrans.c index c05c7e62a..7296289c2 100644 --- a/jdtrans.c +++ b/jdtrans.c @@ -2,9 +2,9 @@ * jdtrans.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1995-1998, Thomas G. Lane. + * Copyright (C) 1995-1997, Thomas G. Lane. * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file contains library routines for transcoding decompression, @@ -15,7 +15,6 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jlossy.h" /* Forward declarations */ @@ -47,13 +46,8 @@ LOCAL(void) transdecode_master_selection JPP((j_decompress_ptr cinfo)); GLOBAL(jvirt_barray_ptr *) jpeg_read_coefficients (j_decompress_ptr cinfo) { - j_lossy_d_ptr decomp; - - /* Can't read coefficients from lossless streams */ - if (cinfo->process == JPROC_LOSSLESS) { - ERREXIT(cinfo, JERR_CANT_TRANSCODE); - return NULL; - } + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); if (cinfo->global_state == DSTATE_READY) { /* First call: initialize active modules */ @@ -91,7 +85,7 @@ jpeg_read_coefficients (j_decompress_ptr cinfo) */ if ((cinfo->global_state == DSTATE_STOPPING || cinfo->global_state == DSTATE_BUFIMAGE) && cinfo->buffered_image) { - return ((j_lossy_d_ptr) cinfo->codec)->coef_arrays; + return cinfo->coef->coef_arrays; } /* Oops, improper usage */ ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); @@ -110,8 +104,22 @@ transdecode_master_selection (j_decompress_ptr cinfo) /* This is effectively a buffered-image operation. */ cinfo->buffered_image = TRUE; - /* Initialize decompression codec */ - jinit_d_codec(cinfo); + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef D_PROGRESSIVE_SUPPORTED + jinit_phuff_decoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_decoder(cinfo); + } + + /* Always get a full-image coefficient buffer. */ + jinit_d_coef_controller(cinfo, TRUE); /* We can now tell the memory manager to allocate virtual arrays. */ (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); @@ -123,7 +131,7 @@ transdecode_master_selection (j_decompress_ptr cinfo) if (cinfo->progress != NULL) { int nscans; /* Estimate number of scans to set pass_limit. */ - if (cinfo->process == JPROC_PROGRESSIVE) { + if (cinfo->progressive_mode) { /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ nscans = 2 + 3 * cinfo->num_components; } else if (cinfo->inputctl->has_multiple_scans) { diff --git a/jerror.h b/jerror.h index 096003d43..f57148bde 100644 --- a/jerror.h +++ b/jerror.h @@ -2,9 +2,10 @@ * jerror.h * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. + * Copyright (C) 1994-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file defines the error and message codes for the JPEG library. @@ -47,27 +48,22 @@ JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix") JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix") JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode") JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") -JMESSAGE(JERR_BAD_DCT_COEF, "DCT coefficient out of range") +JMESSAGE(JERR_BAD_DCT_COEF, + "DCT coefficient (lossy) or spatial difference (lossless) out of range") JMESSAGE(JERR_BAD_DCTSIZE, "IDCT output block size %d not supported") -JMESSAGE(JERR_BAD_DIFF, "spatial difference out of range") JMESSAGE(JERR_BAD_HUFF_TABLE, "Bogus Huffman table definition") JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace") JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace") JMESSAGE(JERR_BAD_LENGTH, "Bogus marker length") JMESSAGE(JERR_BAD_LIB_VERSION, "Wrong JPEG library version: library is %d, caller expects %d") -JMESSAGE(JERR_BAD_LOSSLESS, - "Invalid lossless parameters Ss=%d Se=%d Ah=%d Al=%d") -JMESSAGE(JERR_BAD_LOSSLESS_SCRIPT, - "Invalid lossless parameters at scan script entry %d") JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan") JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d") JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d") JMESSAGE(JERR_BAD_PROGRESSION, - "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d") + "Invalid progressive/lossless parameters Ss=%d Se=%d Ah=%d Al=%d") JMESSAGE(JERR_BAD_PROG_SCRIPT, - "Invalid progressive parameters at scan script entry %d") -JMESSAGE(JERR_BAD_RESTART, "Invalid restart interval: %d, must be an integer multiple of the number of MCUs in an MCU_row (%d)") + "Invalid progressive/lossless parameters at scan script entry %d") JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors") JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d") JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d") @@ -76,8 +72,6 @@ JMESSAGE(JERR_BAD_STRUCT_SIZE, JMESSAGE(JERR_BAD_VIRTUAL_ACCESS, "Bogus virtual array access") JMESSAGE(JERR_BUFFER_SIZE, "Buffer passed to JPEG library is too small") JMESSAGE(JERR_CANT_SUSPEND, "Suspension not allowed here") -JMESSAGE(JERR_CANT_TRANSCODE, - "Cannot transcode to/from lossless JPEG datastreams") JMESSAGE(JERR_CCIR601_NOTIMPL, "CCIR601 sampling not implemented yet") JMESSAGE(JERR_COMPONENT_COUNT, "Too many color components: %d, max %d") JMESSAGE(JERR_CONVERSION_NOTIMPL, "Unsupported color conversion request") @@ -106,7 +100,6 @@ JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time") JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported") JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined") JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image") -JMESSAGE(JERR_NO_LOSSLESS_SCRIPT, "Lossless encoding was requested but no scan script was supplied") JMESSAGE(JERR_NO_QUANT_TABLE, "Quantization table 0x%02x was not defined") JMESSAGE(JERR_NO_SOI, "Not a JPEG file: starts with 0x%02x 0x%02x") JMESSAGE(JERR_OUT_OF_MEMORY, "Insufficient memory (case %d)") @@ -176,10 +169,8 @@ JMESSAGE(JTRC_THUMB_PALETTE, "JFIF extension marker: palette thumbnail image, length %u") JMESSAGE(JTRC_THUMB_RGB, "JFIF extension marker: RGB thumbnail image, length %u") -JMESSAGE(JTRC_UNKNOWN_LOSSLESS_IDS, - "Unrecognized component IDs %d %d %d, assuming RGB") -JMESSAGE(JTRC_UNKNOWN_LOSSY_IDS, - "Unrecognized component IDs %d %d %d, assuming YCbCr") +JMESSAGE(JTRC_UNKNOWN_IDS, + "Unrecognized component IDs %d %d %d, assuming YCbCr (lossy) or RGB (lossless)") JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") @@ -191,12 +182,13 @@ JMESSAGE(JWRN_HIT_MARKER, "Corrupt JPEG data: premature end of data segment") JMESSAGE(JWRN_HUFF_BAD_CODE, "Corrupt JPEG data: bad Huffman code") JMESSAGE(JWRN_JFIF_MAJOR, "Warning: unknown JFIF revision number %d.%02d") JMESSAGE(JWRN_JPEG_EOF, "Premature end of JPEG file") -JMESSAGE(JWRN_MUST_DOWNSCALE, - "Must downscale data from %d bits to %d") JMESSAGE(JWRN_MUST_RESYNC, "Corrupt JPEG data: found marker 0x%02x instead of RST%d") JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG") JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") +JMESSAGE(JERR_BAD_RESTART, "Invalid restart interval %d; must be an integer multiple of the number of MCUs in an MCU row (%d)") +JMESSAGE(JWRN_MUST_DOWNSCALE, + "Must downscale data from %d bits to %d") #ifdef JMAKE_ENUM_LIST diff --git a/jlossls.h b/jlossls.h index dfeb70e06..97f9c2f7d 100644 --- a/jlossls.h +++ b/jlossls.h @@ -5,6 +5,7 @@ * Copyright (C) 1998, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This include file contains common declarations for the lossless JPEG @@ -14,6 +15,17 @@ #ifndef JLOSSLS_H #define JLOSSLS_H +#if defined(C_LOSSLESS_SUPPORTED) || defined(D_LOSSLESS_SUPPORTED) + +#define JPEG_INTERNALS +#include "jpeglib.h" + + +#define ALLOC_DARRAY(pool_id, diffsperrow, numrows) \ + (JDIFFARRAY)(*cinfo->mem->alloc_sarray) \ + ((j_common_ptr) cinfo, pool_id, \ + (diffsperrow) * sizeof(JDIFF) / sizeof(JSAMPLE), numrows) + /* * Table H.1: Predictors for lossless coding. @@ -27,125 +39,62 @@ #define PREDICTOR6 (int) ((INT32) Rb + RIGHT_SHIFT((INT32) Ra - (INT32) Rc, 1)) #define PREDICTOR7 (int) RIGHT_SHIFT((INT32) Ra + (INT32) Rb, 1) +#endif + + +#ifdef C_LOSSLESS_SUPPORTED typedef JMETHOD(void, predict_difference_method_ptr, (j_compress_ptr cinfo, int ci, JSAMPROW input_buf, JSAMPROW prev_row, JDIFFROW diff_buf, JDIMENSION width)); -typedef JMETHOD(void, scaler_method_ptr, - (j_compress_ptr cinfo, int ci, - JSAMPROW input_buf, JSAMPROW output_buf, - JDIMENSION width)); - -/* Lossless-specific compression codec (compressor proper) */ +/* Lossless compressor */ typedef struct { - struct jpeg_c_codec pub; /* public fields */ - - - /* Difference buffer control */ - JMETHOD(void, diff_start_pass, (j_compress_ptr cinfo, - J_BUF_MODE pass_mode)); - - /* Pointer to data which is private to diff controller */ - void *diff_private; - - - /* Entropy encoding */ - JMETHOD(JDIMENSION, entropy_encode_mcus, (j_compress_ptr cinfo, - JDIFFIMAGE diff_buf, - JDIMENSION MCU_row_num, - JDIMENSION MCU_col_num, - JDIMENSION nMCU)); - - /* Pointer to data which is private to entropy module */ - void *entropy_private; - - - /* Prediction, differencing */ - JMETHOD(void, predict_start_pass, (j_compress_ptr cinfo)); + struct jpeg_forward_dct pub; /* public fields */ /* It is useful to allow each component to have a separate diff method. */ predict_difference_method_ptr predict_difference[MAX_COMPONENTS]; - /* Pointer to data which is private to predictor module */ - void *pred_private; + /* MCU rows left in the restart interval for each component */ + unsigned int restart_rows_to_go[MAX_COMPONENTS]; /* Sample scaling */ - JMETHOD(void, scaler_start_pass, (j_compress_ptr cinfo)); JMETHOD(void, scaler_scale, (j_compress_ptr cinfo, JSAMPROW input_buf, JSAMPROW output_buf, JDIMENSION width)); +} jpeg_lossless_compressor; - /* Pointer to data which is private to scaler module */ - void *scaler_private; +typedef jpeg_lossless_compressor * lossless_comp_ptr; -} jpeg_lossless_c_codec; +#endif /* C_LOSSLESS_SUPPORTED */ -typedef jpeg_lossless_c_codec * j_lossless_c_ptr; +#ifdef D_LOSSLESS_SUPPORTED typedef JMETHOD(void, predict_undifference_method_ptr, (j_decompress_ptr cinfo, int comp_index, JDIFFROW diff_buf, JDIFFROW prev_row, JDIFFROW undiff_buf, JDIMENSION width)); -/* Lossless-specific decompression codec (decompressor proper) */ +/* Lossless decompressor */ typedef struct { - struct jpeg_d_codec pub; /* public fields */ - - - /* Difference buffer control */ - JMETHOD(void, diff_start_input_pass, (j_decompress_ptr cinfo)); - - /* Pointer to data which is private to diff controller */ - void *diff_private; - - - /* Entropy decoding */ - JMETHOD(void, entropy_start_pass, (j_decompress_ptr cinfo)); - JMETHOD(boolean, entropy_process_restart, (j_decompress_ptr cinfo)); - JMETHOD(JDIMENSION, entropy_decode_mcus, (j_decompress_ptr cinfo, - JDIFFIMAGE diff_buf, - JDIMENSION MCU_row_num, - JDIMENSION MCU_col_num, - JDIMENSION nMCU)); - - /* Pointer to data which is private to entropy module */ - void *entropy_private; - - - /* Prediction, undifferencing */ - JMETHOD(void, predict_start_pass, (j_decompress_ptr cinfo)); - JMETHOD(void, predict_process_restart, (j_decompress_ptr cinfo)); + struct jpeg_inverse_dct pub; /* public fields */ /* It is useful to allow each component to have a separate undiff method. */ predict_undifference_method_ptr predict_undifference[MAX_COMPONENTS]; - /* Pointer to data which is private to predictor module */ - void *pred_private; - /* Sample scaling */ - JMETHOD(void, scaler_start_pass, (j_decompress_ptr cinfo)); JMETHOD(void, scaler_scale, (j_decompress_ptr cinfo, JDIFFROW diff_buf, JSAMPROW output_buf, JDIMENSION width)); - /* Pointer to data which is private to scaler module */ - void *scaler_private; - -} jpeg_lossless_d_codec; + int scale_factor; -typedef jpeg_lossless_d_codec * j_lossless_d_ptr; +} jpeg_lossless_decompressor; +typedef jpeg_lossless_decompressor * lossless_decomp_ptr; -/* Compression module initialization routines */ -EXTERN(void) jinit_lhuff_encoder JPP((j_compress_ptr cinfo)); -EXTERN(void) jinit_differencer JPP((j_compress_ptr cinfo)); -EXTERN(void) jinit_c_scaler JPP((j_compress_ptr cinfo)); -/* Decompression module initialization routines */ -EXTERN(void) jinit_lhuff_decoder JPP((j_decompress_ptr cinfo)); -EXTERN(void) jinit_undifferencer JPP((j_decompress_ptr cinfo)); -EXTERN(void) jinit_d_scaler JPP((j_decompress_ptr cinfo)); +#endif /* D_LOSSLESS_SUPPORTED */ #endif /* JLOSSLS_H */ diff --git a/jlossy.h b/jlossy.h deleted file mode 100644 index 8229ca836..000000000 --- a/jlossy.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * jlossy.h - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. - * For conditions of distribution and use, see the accompanying README file. - * - * This include file contains common declarations for the lossy (DCT-based) - * JPEG codec modules. - */ - -#ifndef JLOSSY_H -#define JLOSSY_H - - -/* Lossy-specific compression codec (compressor proper) */ -typedef struct { - struct jpeg_c_codec pub; /* public fields */ - - - /* Coefficient buffer control */ - JMETHOD(void, coef_start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); - /* JMETHOD(boolean, coef_compress_data, (j_compress_ptr cinfo, - JSAMPIMAGE input_buf));*/ - - /* Pointer to data which is private to coef module */ - void *coef_private; - - - /* Forward DCT (also controls coefficient quantization) */ - JMETHOD(void, fdct_start_pass, (j_compress_ptr cinfo)); - /* perhaps this should be an array??? */ - JMETHOD(void, fdct_forward_DCT, (j_compress_ptr cinfo, - jpeg_component_info * compptr, - JSAMPARRAY sample_data, JBLOCKROW coef_blocks, - JDIMENSION start_row, JDIMENSION start_col, - JDIMENSION num_blocks)); - - /* Pointer to data which is private to fdct module */ - void *fdct_private; - - - /* Entropy encoding */ - JMETHOD(boolean, entropy_encode_mcu, (j_compress_ptr cinfo, - JBLOCKROW *MCU_data)); - - /* Pointer to data which is private to entropy module */ - void *entropy_private; - -} jpeg_lossy_c_codec; - -typedef jpeg_lossy_c_codec * j_lossy_c_ptr; - - - -typedef JMETHOD(void, inverse_DCT_method_ptr, - (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col)); - -/* Lossy-specific decompression codec (decompressor proper) */ -typedef struct { - struct jpeg_d_codec pub; /* public fields */ - - - /* Coefficient buffer control */ - JMETHOD(void, coef_start_input_pass, (j_decompress_ptr cinfo)); - JMETHOD(void, coef_start_output_pass, (j_decompress_ptr cinfo)); - - /* Pointer to array of coefficient virtual arrays, or NULL if none */ - jvirt_barray_ptr *coef_arrays; - - /* Pointer to data which is private to coef module */ - void *coef_private; - - - /* Entropy decoding */ - JMETHOD(void, entropy_start_pass, (j_decompress_ptr cinfo)); - JMETHOD(boolean, entropy_decode_mcu, (j_decompress_ptr cinfo, - JBLOCKROW *MCU_data)); - - /* This is here to share code between baseline and progressive decoders; */ - /* other modules probably should not use it */ - boolean entropy_insufficient_data; /* set TRUE after emitting warning */ - - /* Pointer to data which is private to entropy module */ - void *entropy_private; - - - /* Inverse DCT (also performs dequantization) */ - JMETHOD(void, idct_start_pass, (j_decompress_ptr cinfo)); - - /* It is useful to allow each component to have a separate IDCT method. */ - inverse_DCT_method_ptr inverse_DCT[MAX_COMPONENTS]; - - /* Pointer to data which is private to idct module */ - void *idct_private; - -} jpeg_lossy_d_codec; - -typedef jpeg_lossy_d_codec * j_lossy_d_ptr; - - -/* Compression module initialization routines */ -EXTERN(void) jinit_lossy_c_codec JPP((j_compress_ptr cinfo)); -EXTERN(void) jinit_c_coef_controller JPP((j_compress_ptr cinfo, - boolean need_full_buffer)); -EXTERN(void) jinit_forward_dct JPP((j_compress_ptr cinfo)); -EXTERN(void) jinit_shuff_encoder JPP((j_compress_ptr cinfo)); -EXTERN(void) jinit_phuff_encoder JPP((j_compress_ptr cinfo)); - -/* Decompression module initialization routines */ -EXTERN(void) jinit_lossy_d_codec JPP((j_decompress_ptr cinfo)); -EXTERN(void) jinit_d_coef_controller JPP((j_decompress_ptr cinfo, - boolean need_full_buffer)); -EXTERN(void) jinit_shuff_decoder JPP((j_decompress_ptr cinfo)); -EXTERN(void) jinit_phuff_decoder JPP((j_decompress_ptr cinfo)); -EXTERN(void) jinit_inverse_dct JPP((j_decompress_ptr cinfo)); - -#endif /* JLOSSY_H */ diff --git a/jmemmgr.c b/jmemmgr.c index 78e14a24b..d801b322d 100644 --- a/jmemmgr.c +++ b/jmemmgr.c @@ -1,10 +1,8 @@ /* * jmemmgr.c * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * * This file contains the JPEG system-independent memory management @@ -44,12 +42,11 @@ extern char * getenv JPP((const char * name)); * The allocation routines provided here must never return NULL. * They should exit to error_exit if unsuccessful. * - * It's not a good idea to try to merge the sarray, barray and darray - * routines, even though they are textually almost the same, because - * samples are usually stored as bytes while coefficients and differenced - * 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. + * 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. */ @@ -485,58 +482,6 @@ alloc_barray (j_common_ptr cinfo, int pool_id, } -#ifdef NEED_DARRAY - -/* - * Creation of 2-D difference arrays. - * This is essentially the same as the code for sample arrays, above. - */ - -METHODDEF(JDIFFARRAY) -alloc_darray (j_common_ptr cinfo, int pool_id, - JDIMENSION diffsperrow, JDIMENSION numrows) -/* Allocate a 2-D difference array */ -{ - my_mem_ptr mem = (my_mem_ptr) cinfo->mem; - JDIFFARRAY result; - JDIFFROW workspace; - JDIMENSION rowsperchunk, currow, i; - long ltemp; - - /* Calculate max # of rows allowed in one allocation chunk */ - ltemp = (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) / - ((long) diffsperrow * SIZEOF(JDIFF)); - if (ltemp <= 0) - ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); - if (ltemp < (long) numrows) - rowsperchunk = (JDIMENSION) ltemp; - else - rowsperchunk = numrows; - mem->last_rowsperchunk = rowsperchunk; - - /* Get space for row pointers (small object) */ - result = (JDIFFARRAY) alloc_small(cinfo, pool_id, - (size_t) (numrows * SIZEOF(JDIFFROW))); - - /* Get the rows themselves (large objects) */ - currow = 0; - while (currow < numrows) { - rowsperchunk = MIN(rowsperchunk, numrows - currow); - workspace = (JDIFFROW) alloc_large(cinfo, pool_id, - (size_t) ((size_t) rowsperchunk * (size_t) diffsperrow - * SIZEOF(JDIFF))); - for (i = rowsperchunk; i > 0; i--) { - result[currow++] = workspace; - workspace += diffsperrow; - } - } - - return result; -} - -#endif - - /* * About virtual array management: * @@ -1123,9 +1068,6 @@ jinit_memory_mgr (j_common_ptr cinfo) mem->pub.alloc_large = alloc_large; mem->pub.alloc_sarray = alloc_sarray; mem->pub.alloc_barray = alloc_barray; -#ifdef NEED_DARRAY - mem->pub.alloc_darray = alloc_darray; -#endif mem->pub.request_virt_sarray = request_virt_sarray; mem->pub.request_virt_barray = request_virt_barray; mem->pub.realize_virt_arrays = realize_virt_arrays; diff --git a/jmorecfg.h b/jmorecfg.h index bab851520..66c2b241e 100644 --- a/jmorecfg.h +++ b/jmorecfg.h @@ -2,7 +2,7 @@ * jmorecfg.h * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. * For conditions of distribution and use, see the accompanying README file. @@ -128,13 +128,6 @@ typedef short JSAMPLE; typedef short JCOEF; -/* Representation of a spatial difference value. - * This should be a signed value of at least 16 bits; int is usually OK. - */ - -typedef int JDIFF; - - /* 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 @@ -309,7 +302,7 @@ typedef int boolean; #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 jcshuff.c normally uses entropy optimization to compute + * 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 and lossless JPEG: diff --git a/jpegint.h b/jpegint.h index 97b01a465..9a143ec4e 100644 --- a/jpegint.h +++ b/jpegint.h @@ -2,9 +2,10 @@ * jpegint.h * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 1991-1997, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file provides common declarations for the various JPEG modules. @@ -13,6 +14,17 @@ */ +/* Representation of a spatial difference value. + * This should be a signed value of at least 16 bits; int is usually OK. + */ + +typedef int JDIFF; + +typedef JDIFF FAR *JDIFFROW; /* pointer to one row of difference values */ +typedef JDIFFROW *JDIFFARRAY; /* ptr to some rows (a 2-D diff array) */ +typedef JDIFFARRAY *JDIFFIMAGE; /* a 3-D diff array: top index is color */ + + /* Declarations for both compression & decompression */ typedef enum { /* Operating modes for buffer controllers */ @@ -52,6 +64,7 @@ struct jpeg_comp_master { /* State variables made visible to other modules */ boolean call_pass_startup; /* True if pass_startup must be called */ boolean is_last_pass; /* True during last pass */ + boolean lossless; /* True if lossless mode is enabled */ }; /* Main buffer control (downsampled-data buffer) */ @@ -74,12 +87,10 @@ struct jpeg_c_prep_controller { JDIMENSION out_row_groups_avail)); }; -/* Compression codec (compressor proper) */ -struct jpeg_c_codec { - JMETHOD(void, entropy_start_pass, (j_compress_ptr cinfo, - boolean gather_statistics)); - JMETHOD(void, entropy_finish_pass, (j_compress_ptr cinfo)); - JMETHOD(boolean, need_optimization_pass, (j_compress_ptr cinfo)); +/* Lossy mode: Coefficient buffer control + * Lossless mode: Difference buffer control + */ +struct jpeg_c_coef_controller { JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); JMETHOD(boolean, compress_data, (j_compress_ptr cinfo, JSAMPIMAGE input_buf)); @@ -104,6 +115,36 @@ struct jpeg_downsampler { boolean need_context_rows; /* TRUE if need rows above & below */ }; +/* Lossy mode: Forward DCT (also controls coefficient quantization) + * Lossless mode: Prediction, sample differencing, and point transform + */ +struct jpeg_forward_dct { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + + /* Lossy mode */ + /* perhaps this should be an array??? */ + JMETHOD(void, forward_DCT, (j_compress_ptr cinfo, + jpeg_component_info * compptr, + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks)); +}; + +/* Entropy encoding */ +struct jpeg_entropy_encoder { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, boolean gather_statistics)); + + /* Lossy mode */ + JMETHOD(boolean, encode_mcu, (j_compress_ptr cinfo, JBLOCKROW *MCU_data)); + /* Lossless mode */ + JMETHOD(JDIMENSION, encode_mcus, (j_compress_ptr cinfo, + JDIFFIMAGE diff_buf, + JDIMENSION MCU_row_num, + JDIMENSION MCU_col_num, JDIMENSION nMCU)); + + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); +}; + /* Marker writing */ struct jpeg_marker_writer { JMETHOD(void, write_file_header, (j_compress_ptr cinfo)); @@ -128,6 +169,7 @@ struct jpeg_decomp_master { /* State variables made visible to other modules */ boolean is_dummy_pass; /* True during 1st pass for 2-pass quant */ + boolean lossless; /* True if decompressing a lossless image */ }; /* Input control module */ @@ -150,14 +192,19 @@ struct jpeg_d_main_controller { JDIMENSION out_rows_avail)); }; -/* Decompression codec (decompressor proper) */ -struct jpeg_d_codec { - JMETHOD(void, calc_output_dimensions, (j_decompress_ptr cinfo)); +/* Lossy mode: Coefficient buffer control + * Lossless mode: Difference buffer control + */ +struct jpeg_d_coef_controller { JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); JMETHOD(int, consume_data, (j_decompress_ptr cinfo)); JMETHOD(void, start_output_pass, (j_decompress_ptr cinfo)); JMETHOD(int, decompress_data, (j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); + + /* Lossy mode */ + /* Pointer to array of coefficient virtual arrays, or NULL if none */ + jvirt_barray_ptr *coef_arrays; }; /* Decompression postprocessing (color quantization buffer control) */ @@ -192,6 +239,42 @@ struct jpeg_marker_reader { unsigned int discarded_bytes; /* # of bytes skipped looking for a marker */ }; +/* Entropy decoding */ +struct jpeg_entropy_decoder { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + + /* Lossy mode */ + JMETHOD(boolean, decode_mcu, (j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); + /* Lossless mode */ + JMETHOD(JDIMENSION, decode_mcus, (j_decompress_ptr cinfo, + JDIFFIMAGE diff_buf, + JDIMENSION MCU_row_num, + JDIMENSION MCU_col_num, JDIMENSION nMCU)); + JMETHOD(boolean, process_restart, (j_decompress_ptr cinfo)); + + /* This is here to share code between baseline and progressive decoders; */ + /* other modules probably should not use it */ + boolean insufficient_data; /* set TRUE after emitting warning */ +}; + +/* Lossy mode: Inverse DCT (also performs dequantization) + * Lossless mode: Prediction, sample undifferencing, point transform, and + * sample size scaling + */ +typedef JMETHOD(void, inverse_DCT_method_ptr, + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col)); + +struct jpeg_inverse_dct { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + + /* Lossy mode */ + /* It is useful to allow each component to have a separate IDCT method. */ + inverse_DCT_method_ptr inverse_DCT[MAX_COMPONENTS]; +}; + /* Upsampling (note that upsampler must also call color converter) */ struct jpeg_upsampler { JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); @@ -258,8 +341,6 @@ struct jpeg_color_quantizer { /* Short forms of external names for systems with brain-damaged linkers. */ #ifdef NEED_SHORT_EXTERNAL_NAMES -#define jinit_c_codec jICCodec -#define jinit_lossy_c_codec jILossyC #define jinit_compress_master jICompress #define jinit_c_master_control jICMaster #define jinit_c_main_controller jICMainC @@ -268,24 +349,17 @@ struct jpeg_color_quantizer { #define jinit_color_converter jICColor #define jinit_downsampler jIDownsampler #define jinit_forward_dct jIFDCT -#define jinit_shuff_encoder jISHEncoder +#define jinit_huff_encoder jIHEncoder #define jinit_phuff_encoder jIPHEncoder #define jinit_marker_writer jIMWriter -#define jinit_d_codec jIDCodec -#define jinit_lossy_d_codec jILossyD -#define jinit_lossless_d_codec jILosslsD #define jinit_master_decompress jIDMaster #define jinit_d_main_controller jIDMainC #define jinit_d_coef_controller jIDCoefC -#define jinit_d_diff_controller jIDDiffC #define jinit_d_post_controller jIDPostC #define jinit_input_controller jIInCtlr #define jinit_marker_reader jIMReader -#define jinit_shuff_decoder jISHDecoder +#define jinit_huff_decoder jIHDecoder #define jinit_phuff_decoder jIPHDecoder -#define jinit_lhuff_decoder jILHDecoder -#define jinit_undifferencer jIUndiff -#define jinit_d_scaler jIDScaler #define jinit_inverse_dct jIIDCT #define jinit_upsampler jIUpsampler #define jinit_color_deconverter jIDColor @@ -305,38 +379,50 @@ struct jpeg_color_quantizer { /* Compression module initialization routines */ EXTERN(void) jinit_compress_master JPP((j_compress_ptr cinfo)); -EXTERN(void) jinit_c_codec JPP((j_compress_ptr cinfo)); -EXTERN(void) jinit_c_diff_controller JPP((j_compress_ptr cinfo, - boolean need_full_buffer)); EXTERN(void) jinit_c_master_control JPP((j_compress_ptr cinfo, boolean transcode_only)); EXTERN(void) jinit_c_main_controller JPP((j_compress_ptr cinfo, boolean need_full_buffer)); EXTERN(void) jinit_c_prep_controller JPP((j_compress_ptr cinfo, boolean need_full_buffer)); -EXTERN(void) jinit_compressor JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_c_coef_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); EXTERN(void) jinit_color_converter JPP((j_compress_ptr cinfo)); EXTERN(void) jinit_downsampler JPP((j_compress_ptr cinfo)); -EXTERN(void) jinit_lossless_c_codec JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_forward_dct JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_huff_encoder JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_phuff_encoder JPP((j_compress_ptr cinfo)); EXTERN(void) jinit_marker_writer JPP((j_compress_ptr cinfo)); +#ifdef C_LOSSLESS_SUPPORTED +EXTERN(void) jinit_c_diff_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_lhuff_encoder JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_lossless_compressor JPP((j_compress_ptr cinfo)); +#endif /* Decompression module initialization routines */ EXTERN(void) jinit_master_decompress JPP((j_decompress_ptr cinfo)); -EXTERN(void) jinit_d_codec JPP((j_decompress_ptr cinfo)); -EXTERN(void) jinit_d_diff_controller JPP((j_decompress_ptr cinfo, - boolean need_full_buffer)); EXTERN(void) jinit_d_main_controller JPP((j_decompress_ptr cinfo, boolean need_full_buffer)); -EXTERN(void) jinit_decompressor JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_d_coef_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); EXTERN(void) jinit_d_post_controller JPP((j_decompress_ptr cinfo, boolean need_full_buffer)); EXTERN(void) jinit_input_controller JPP((j_decompress_ptr cinfo)); -EXTERN(void) jinit_lossless_d_codec JPP((j_decompress_ptr cinfo)); EXTERN(void) jinit_marker_reader JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_huff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_phuff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_inverse_dct JPP((j_decompress_ptr cinfo)); EXTERN(void) jinit_upsampler JPP((j_decompress_ptr cinfo)); EXTERN(void) jinit_color_deconverter JPP((j_decompress_ptr cinfo)); EXTERN(void) jinit_1pass_quantizer JPP((j_decompress_ptr cinfo)); EXTERN(void) jinit_2pass_quantizer JPP((j_decompress_ptr cinfo)); EXTERN(void) jinit_merged_upsampler JPP((j_decompress_ptr cinfo)); +#ifdef D_LOSSLESS_SUPPORTED +EXTERN(void) jinit_d_diff_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_lhuff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_lossless_decompressor JPP((j_decompress_ptr cinfo)); +#endif /* Memory manager initialization */ EXTERN(void) jinit_memory_mgr JPP((j_common_ptr cinfo)); diff --git a/jpeglib.h b/jpeglib.h index ed9563272..0892e5470 100644 --- a/jpeglib.h +++ b/jpeglib.h @@ -5,6 +5,7 @@ * Copyright (C) 1991-1998, Thomas G. Lane. * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README file. * * This file defines the application interface for the JPEG library. @@ -40,6 +41,13 @@ * if you want to be compatible. */ +/* NOTE: In lossless mode, an MCU contains one or more samples rather than one + * or more 8x8 DCT blocks, so the term "data unit" is used to generically + * describe a sample in lossless mode or an 8x8 DCT block in lossy mode. To + * preserve backward API/ABI compatibility, the field and macro names retain + * the "block" terminology. + */ + #define DCTSIZE 8 /* The basic DCT block is 8x8 samples */ #define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ #define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ @@ -48,16 +56,15 @@ #define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ #define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ /* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; - * the PostScript DCT filter can emit files with many more than 10 data units - * per MCU. - * If you happen to run across such a file, you can up D_MAX_DATA_UNITS_IN_MCU + * 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_DATA_UNITS_IN_MCU; just because Adobe + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe * sometimes emits noncompliant files doesn't mean you should too. */ -#define C_MAX_DATA_UNITS_IN_MCU 10 /* compressor's limit on data units/MCU */ -#ifndef D_MAX_DATA_UNITS_IN_MCU -#define D_MAX_DATA_UNITS_IN_MCU 10 /* decompressor's limit on data units/MCU */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on data units/MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on data units/MCU */ #endif @@ -77,10 +84,6 @@ typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ typedef JCOEF FAR *JCOEFPTR; /* useful in a couple of places */ -typedef JDIFF FAR *JDIFFROW; /* pointer to one row of difference values */ -typedef JDIFFROW *JDIFFARRAY; /* ptr to some rows (a 2-D diff array) */ -typedef JDIFFARRAY *JDIFFIMAGE; /* a 3-D diff array: top index is color */ - /* Types for JPEG compression parameters and working tables. */ @@ -140,25 +143,28 @@ typedef struct { /* These values are computed during compression or decompression startup: */ /* Component's size in data units. - * Any dummy data units added to complete an MCU are not counted; therefore - * these values do not depend on whether a scan is interleaved or not. + * In lossy mode, 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. In lossless mode, these are always equal to the image width and + * height. */ - JDIMENSION width_in_data_units; - JDIMENSION height_in_data_units; - /* Size of a data unit in/output by the codec (in samples). Always - * data_unit for compression. For decompression this is the size of the - * output from one data_unit, reflecting any processing performed by the - * codec. For example, in the DCT-based codec, scaling may be applied - * during the IDCT step. Values of 1,2,4,8 are likely to be supported. - * Note that different components may have different codec_data_unit sizes. + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a data unit in samples. Always DCTSIZE for lossy compression. + * For lossy 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. In lossless mode, this is + * always equal to 1. */ - int codec_data_unit; + int DCT_scaled_size; /* The downsampled dimensions are the component's actual, unpadded number * of samples at the main buffer (preprocessing/compression interface), thus * downsampled_width = ceil(image_width * Hi/Hmax) - * and similarly for height. For decompression, codec-based processing is - * included (ie, IDCT scaling), so - * downsampled_width = ceil(image_width * Hi/Hmax * codec_data_unit/data_unit) + * and similarly for height. For lossy decompression, IDCT scaling is + * included, so + * downsampled_width = ceil(image_width * Hi/Hmax * DCT_scaled_size/DCTSIZE) + * In lossless mode, these are always equal to the image width and height. */ JDIMENSION downsampled_width; /* actual width in samples */ JDIMENSION downsampled_height; /* actual height in samples */ @@ -172,10 +178,10 @@ typedef struct { /* The decompressor output side may not use these variables. */ int MCU_width; /* number of data units per MCU, horizontally */ int MCU_height; /* number of data units per MCU, vertically */ - int MCU_data_units; /* MCU_width * MCU_height */ - int MCU_sample_width; /* MCU width in samples, MCU_width*codec_data_unit */ - int last_col_width; /* # of non-dummy data_units across in last MCU */ - int last_row_height; /* # of non-dummy data_units down in last MCU */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples, MCU_width*DCT_scaled_size */ + int last_col_width; /* # of non-dummy data units across in last MCU */ + int last_row_height; /* # of non-dummy data units down in last MCU */ /* Saved quantization table for component; NULL if none yet saved. * See jdinput.c comments about the need for this information. @@ -194,9 +200,11 @@ typedef struct { int comps_in_scan; /* number of components encoded in this scan */ int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ int Ss, Se; /* progressive JPEG spectral selection parms - lossless JPEG predictor select parm (Ss) */ + (Ss is the predictor selection value in + lossless mode) */ int Ah, Al; /* progressive JPEG successive approx. parms - lossless JPEG point transform parm (Al) */ + (Al is the point transform value in lossless + mode) */ } jpeg_scan_info; /* The decompressor can save APPn and COM markers in a list of these: */ @@ -212,14 +220,6 @@ struct jpeg_marker_struct { /* the marker length word is not counted in data_length or original_length */ }; -/* Known codec processes. */ - -typedef enum { - JPROC_SEQUENTIAL, /* baseline/extended sequential DCT */ - JPROC_PROGRESSIVE, /* progressive DCT */ - JPROC_LOSSLESS /* lossless (sequential) */ -} J_CODEC_PROCESS; - /* Known color spaces. */ typedef enum { @@ -310,8 +310,6 @@ struct jpeg_compress_struct { * helper routines to simplify changing parameters. */ - boolean lossless; /* TRUE=lossless encoding, FALSE=lossy */ - int data_precision; /* bits of precision in image data */ int num_components; /* # of color components in JPEG image */ @@ -381,17 +379,17 @@ struct jpeg_compress_struct { /* * These fields are computed during compression startup */ - int data_unit; /* size of data unit in samples */ - J_CODEC_PROCESS process; /* encoding process of JPEG image */ - + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ int max_h_samp_factor; /* largest h_samp_factor */ int max_v_samp_factor; /* largest v_samp_factor */ - JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to codec */ - /* The codec 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 * data_unit sample rows of each component in an - * "iMCU" (interleaved MCU) row. + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coefficient or + difference controller */ + /* The coefficient or difference controller receives data in units of MCU + * rows as defined for fully interleaved scans (whether the JPEG file is + * interleaved or not). In lossy mode, there are v_samp_factor * DCTSIZE + * sample rows of each component in an "iMCU" (interleaved MCU) row. In + * lossless mode, total_iMCU_rows is always equal to the image height. */ /* @@ -405,12 +403,13 @@ struct jpeg_compress_struct { JDIMENSION MCUs_per_row; /* # of MCUs across the image */ JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ - int data_units_in_MCU; /* # of data units per MCU */ - int MCU_membership[C_MAX_DATA_UNITS_IN_MCU]; + int blocks_in_MCU; /* # of data units per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; /* MCU_membership[i] is index in cur_comp_info of component owning */ - /* i'th block in an MCU */ + /* i'th data unit in an MCU */ - int Ss, Se, Ah, Al; /* progressive/lossless JPEG parameters for scan */ + int Ss, Se, Ah, Al; /* progressive/lossless JPEG parameters for + scan */ /* * Links to compression subobjects (methods and private variables of modules) @@ -418,10 +417,12 @@ struct jpeg_compress_struct { struct jpeg_comp_master * master; struct jpeg_c_main_controller * main; struct jpeg_c_prep_controller * prep; - struct jpeg_c_codec * codec; + struct jpeg_c_coef_controller * coef; struct jpeg_marker_writer * marker; struct jpeg_color_converter * cconvert; struct jpeg_downsampler * downsample; + struct jpeg_forward_dct * fdct; + struct jpeg_entropy_encoder * entropy; jpeg_scan_info * script_space; /* workspace for jpeg_simple_progression */ int script_space_size; }; @@ -556,6 +557,7 @@ struct jpeg_decompress_struct { jpeg_component_info * comp_info; /* comp_info[i] describes component that appears i'th in SOF */ + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ @@ -592,21 +594,19 @@ struct jpeg_decompress_struct { /* * These fields are computed during decompression startup */ - int data_unit; /* size of data unit in samples */ - J_CODEC_PROCESS process; /* decoding process of JPEG image */ - int max_h_samp_factor; /* largest h_samp_factor */ int max_v_samp_factor; /* largest v_samp_factor */ - int min_codec_data_unit; /* smallest codec_data_unit of any component */ + int min_DCT_scaled_size; /* smallest DCT_scaled_size of any component */ JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ - /* The codec'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 data_unit rows of each - * component. Therefore, the codec output contains - * v_samp_factor*codec_data_unit sample rows of a component per iMCU row. + /* The coefficient or difference 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. In lossy mode, 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. In + * lossless mode, total_iMCU_rows is always equal to the image height. */ JSAMPLE * sample_range_limit; /* table for fast range-limiting */ @@ -623,12 +623,13 @@ struct jpeg_decompress_struct { JDIMENSION MCUs_per_row; /* # of MCUs across the image */ JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ - int data_units_in_MCU; /* # of data _units per MCU */ - int MCU_membership[D_MAX_DATA_UNITS_IN_MCU]; + int blocks_in_MCU; /* # of data units per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; /* MCU_membership[i] is index in cur_comp_info of component owning */ /* i'th data unit in an MCU */ - int Ss, Se, Ah, Al; /* progressive/lossless JPEG parms for scan */ + int Ss, Se, Ah, Al; /* progressive/lossless 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 @@ -641,10 +642,12 @@ struct jpeg_decompress_struct { */ struct jpeg_decomp_master * master; struct jpeg_d_main_controller * main; - struct jpeg_d_codec * codec; + struct jpeg_d_coef_controller * coef; struct jpeg_d_post_controller * post; struct jpeg_input_controller * inputctl; struct jpeg_marker_reader * marker; + struct jpeg_entropy_decoder * entropy; + struct jpeg_inverse_dct * idct; struct jpeg_upsampler * upsample; struct jpeg_color_deconverter * cconvert; struct jpeg_color_quantizer * cquantize; @@ -774,14 +777,6 @@ typedef struct jvirt_sarray_control * jvirt_sarray_ptr; typedef struct jvirt_barray_control * jvirt_barray_ptr; -#ifdef C_LOSSLESS_SUPPORTED -#define NEED_DARRAY -#else -#ifdef D_LOSSLESS_SUPPORTED -#define NEED_DARRAY -#endif -#endif - struct jpeg_memory_mgr { /* Method pointers */ JMETHOD(void *, alloc_small, (j_common_ptr cinfo, int pool_id, @@ -794,11 +789,6 @@ struct jpeg_memory_mgr { JMETHOD(JBLOCKARRAY, alloc_barray, (j_common_ptr cinfo, int pool_id, JDIMENSION blocksperrow, JDIMENSION numrows)); -#ifdef NEED_DARRAY - JMETHOD(JDIFFARRAY, alloc_darray, (j_common_ptr cinfo, int pool_id, - JDIMENSION diffsperrow, - JDIMENSION numrows)); -#endif JMETHOD(jvirt_sarray_ptr, request_virt_sarray, (j_common_ptr cinfo, int pool_id, boolean pre_zero, @@ -877,7 +867,6 @@ typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo)); #define jpeg_set_linear_quality jSetLQuality #define jpeg_add_quant_table jAddQuantTable #define jpeg_quality_scaling jQualityScaling -#define jpeg_simple_lossless jSimLossless #define jpeg_simple_progression jSimProgress #define jpeg_suppress_tables jSuppressTables #define jpeg_alloc_quant_table jAlcQTable @@ -961,8 +950,9 @@ EXTERN(void) jpeg_add_quant_table JPP((j_compress_ptr cinfo, int which_tbl, int scale_factor, boolean force_baseline)); EXTERN(int) jpeg_quality_scaling JPP((int quality)); -EXTERN(void) jpeg_simple_lossless JPP((j_compress_ptr cinfo, - int predictor, int point_transform)); +EXTERN(void) jpeg_enable_lossless JPP((j_compress_ptr cinfo, + int predictor_selection_value, + int point_transform)); EXTERN(void) jpeg_simple_progression JPP((j_compress_ptr cinfo)); EXTERN(void) jpeg_suppress_tables JPP((j_compress_ptr cinfo, boolean suppress)); diff --git a/libjpeg.doc b/libjpeg.doc index 2817aafcf..4be2d8339 100644 --- a/libjpeg.doc +++ b/libjpeg.doc @@ -4,6 +4,7 @@ This file was part of the Independent JPEG Group's software: Copyright (C) 1994-1998, Thomas G. Lane. Lossless JPEG Modifications: Copyright (C) 1999, Ken Murchison. +Copyright (C) 2022, D. R. Commander. For conditions of distribution and use, see the accompanying README file. @@ -871,11 +872,27 @@ jpeg_simple_progression (j_compress_ptr cinfo) unless you want to make a custom scan sequence. You must ensure that the JPEG color space is set correctly before calling this routine. -jpeg_simple_lossless (j_compress_ptr cinfo, int predictor, int point_transform) - Generates a default scan script for writing a lossless-JPEG file. - This is the recommended method of creating a lossless file, - unless you want to make a custom scan sequence. You must ensure that - the JPEG color space is set correctly before calling this routine. +jpeg_enable_lossless (j_compress_ptr cinfo, int predictor_selection_value, + int point_transform) + Enables lossless mode with the specified predictor selection value + (1 - 7) and optional point transform (0 - {precision}-1, where + {precision} is the JPEG data precision). A point transform value of 0 + is necessary in order to create a fully lossless JPEG image. (A + non-zero point transform value right-shifts the input samples by the + specified number of bits, which is effectively a form of lossy color + quantization.) Note that the following features will be unavailable + when compressing or decompressing lossless JPEG images: + * Quality/quantization table selection + * DCT/IDCT algorithm selection + * Smoothing + * Downsampling/upsampling + * Color conversion (the JPEG image will use the same color space as + the input image) + * IDCT scaling + * Raw (downsampled) data input/output + * Transcoding of DCT coefficients + Any parameters used to enable or configure those features will be + ignored. Compression parameters (cinfo fields) include: @@ -915,15 +932,15 @@ boolean optimize_coding unsigned int restart_interval int restart_in_rows To emit restart markers in the JPEG file, set one of these nonzero. - Set restart_interval to specify the exact interval in MCU blocks. - Set restart_in_rows to specify the interval in MCU rows. (If - restart_in_rows is not 0, then restart_interval is set after the - image width in MCUs is computed.) Defaults are zero (no restarts). - One restart marker per MCU row is often a good choice. - NOTE: the overhead of restart markers is higher in grayscale JPEG - files than in color files, and MUCH higher in progressive JPEGs. - If you use restarts, you may want to use larger intervals in those - cases. + Set restart_interval to specify the exact interval in MCU blocks + (samples in lossless mode). Set restart_in_rows to specify the + interval in MCU rows. (If restart_in_rows is not 0, then + restart_interval is set after the image width in MCUs is computed.) + Defaults are zero (no restarts). One restart marker per MCU row is + often a good choice. NOTE: the overhead of restart markers is higher + in grayscale JPEG files than in color files, and MUCH higher in + progressive JPEGs. If you use restarts, you may want to use larger + intervals in those cases. const jpeg_scan_info * scan_info int num_scans diff --git a/makefile.cfg b/makefile.cfg index 73fdfb54f..70280bfb1 100644 --- a/makefile.cfg +++ b/makefile.cfg @@ -74,15 +74,14 @@ INSTALL_DATA= @INSTALL_DATA@ # source files: JPEG library proper LIBSOURCES= jcapimin.c jcapistd.c jccoefct.c jccolor.c jcdctmgr.c jcdiffct.c \ - jchuff.c jcinit.c jclhuff.c jclossls.c jclossy.c jcmainct.c \ - jcmarker.c jcmaster.c jcodec.c jcomapi.c jcparam.c jcphuff.c jcpred.c \ - jcprepct.c jcsample.c jcscale.c jcshuff.c jctrans.c jdapimin.c \ - jdapistd.c jdatadst.c jdatasrc.c jdcoefct.c jdcolor.c jddctmgr.c \ - jddiffct.c jdhuff.c jdinput.c jdlhuff.c jdlossls.c jdlossy.c \ - jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdphuff.c jdpostct.c \ - jdpred.c jdsample.c jdscale.c jdshuff.c jdtrans.c jerror.c jfdctflt.c \ - jfdctfst.c jfdctint.c jidctflt.c jidctfst.c jidctint.c jidctred.c \ - jquant1.c jquant2.c jutils.c jmemmgr.c + jchuff.c jcinit.c jclhuff.c jclossls.c jcmainct.c jcmarker.c \ + jcmaster.c jcomapi.c jcparam.c jcphuff.c jcprepct.c jcsample.c \ + jctrans.c jdapimin.c jdapistd.c jdatadst.c jdatasrc.c jdcoefct.c \ + jdcolor.c jddctmgr.c jddiffct.c jdhuff.c jdinput.c jdlhuff.c \ + jdlossls.c jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdphuff.c \ + jdpostct.c jdsample.c jdtrans.c jerror.c jfdctflt.c jfdctfst.c \ + jfdctint.c jidctflt.c jidctfst.c jidctint.c jidctred.c jquant1.c \ + jquant2.c jutils.c jmemmgr.c # memmgr back ends: compile only one of these into a working library SYSDEPSOURCES= jmemansi.c jmemname.c jmemnobs.c jmemdos.c jmemmac.c # source files: cjpeg/djpeg/jpegtran applications, also rdjpgcom/wrjpgcom @@ -91,9 +90,8 @@ APPSOURCES= cjpeg.c djpeg.c jpegtran.c rdjpgcom.c wrjpgcom.c cdjpeg.c \ rdtarga.c wrtarga.c rdbmp.c wrbmp.c rdrle.c wrrle.c SOURCES= $(LIBSOURCES) $(SYSDEPSOURCES) $(APPSOURCES) # files included by source files -INCLUDES= jchuff.h jdhuff.h jdct.h jerror.h jinclude.h jlossls.h jlossy.h \ - jmemsys.h jmorecfg.h jpegint.h jpeglib.h jversion.h cdjpeg.h \ - cderror.h transupp.h +INCLUDES= jchuff.h jdhuff.h jdct.h jerror.h jinclude.h jlossls.h jmemsys.h \ + jmorecfg.h jpegint.h jpeglib.h jversion.h cdjpeg.h cderror.h transupp.h # documentation, test, and support files DOCS= README install.doc usage.doc cjpeg.1 djpeg.1 jpegtran.1 rdjpgcom.1 \ wrjpgcom.1 wizard.doc example.c libjpeg.doc structure.doc \ @@ -113,22 +111,20 @@ TESTFILES= testorig.jpg testimg.ppm testimg.bmp testimg.jpg testprog.jpg \ DISTFILES= $(DOCS) $(MKFILES) $(CONFIGFILES) $(SOURCES) $(INCLUDES) \ $(CONFIGUREFILES) $(OTHERFILES) $(TESTFILES) # library object files common to compression and decompression -COMOBJECTS= jcomapi.$(O) jcodec.$(O) jutils.$(O) jerror.$(O) jmemmgr.$(O) \ - $(SYSDEPMEM) +COMOBJECTS= jcomapi.$(O) jutils.$(O) jerror.$(O) jmemmgr.$(O) $(SYSDEPMEM) # compression library object files CLIBOBJECTS= jcapimin.$(O) jcapistd.$(O) jctrans.$(O) jcparam.$(O) \ jdatadst.$(O) jcinit.$(O) jcmaster.$(O) jcmarker.$(O) jcmainct.$(O) \ - jcprepct.$(O) jclossls.$(O) jclossy.o jccoefct.$(O) jccolor.$(O) \ - jcsample.$(O) jchuff.$(O) jcphuff.$(O) jcshuff.$(O) jclhuff.$(O) \ - jcpred.$(O) jcscale.$(O) jcdiffct.$(O) jcdctmgr.$(O) jfdctfst.$(O) \ - jfdctflt.$(O) jfdctint.$(O) + jcprepct.$(O) jclossls.$(O) jccoefct.$(O) jccolor.$(O) jcsample.$(O) \ + jchuff.$(O) jcphuff.$(O) jclhuff.$(O) jcdiffct.$(O) jcdctmgr.$(O) \ + jfdctfst.$(O) jfdctflt.$(O) jfdctint.$(O) # decompression library object files DLIBOBJECTS= jdapimin.$(O) jdapistd.$(O) jdtrans.$(O) jdatasrc.$(O) \ - jdmaster.$(O) jdinput.$(O) jdmarker.$(O) jdlossls.$(O) jdlossy.$(O) \ - jdhuff.$(O) jdlhuff.$(O) jdphuff.$(O) jdshuff.$(O) jdpred.$(O) \ - jdscale.$(O) jddiffct.$(O) jdmainct.$(O) jdcoefct.$(O) jdpostct.$(O) \ - jddctmgr.$(O) jidctfst.$(O) jidctflt.$(O) jidctint.$(O) jidctred.$(O) \ - jdsample.$(O) jdcolor.$(O) jquant1.$(O) jquant2.$(O) jdmerge.$(O) + jdmaster.$(O) jdinput.$(O) jdmarker.$(O) jdlossls.$(O) jdhuff.$(O) \ + jdlhuff.$(O) jdphuff.$(O) jddiffct.$(O) jdmainct.$(O) jdcoefct.$(O) \ + jdpostct.$(O) jddctmgr.$(O) jidctfst.$(O) jidctflt.$(O) jidctint.$(O) \ + jidctred.$(O) jdsample.$(O) jdcolor.$(O) jquant1.$(O) jquant2.$(O) \ + jdmerge.$(O) # These objectfiles are included in libjpeg.a LIBOBJECTS= $(CLIBOBJECTS) $(DLIBOBJECTS) $(COMOBJECTS) # object files for sample applications (excluding library files) @@ -260,52 +256,44 @@ jconfig.h: jconfig.doc jcapimin.$(O): jcapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jcapistd.$(O): jcapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h -jccoefct.$(O): jccoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h -jcodec.$(O): jcodec.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h jlossls.h +jccoefct.$(O): jccoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jccolor.$(O): jccolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h -jcdctmgr.$(O): jcdctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h jdct.h +jcdctmgr.$(O): jcdctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h jcdiffct.$(O): jcdiffct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossls.h -jchuff.$(O): jchuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h jlossls.h jchuff.h +jchuff.$(O): jchuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h jcinit.$(O): jcinit.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jclhuff.$(O): jclhuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossls.h jchuff.h jclossls.$(O): jclossls.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossls.h -jclossy.$(O): jclossy.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h + jcmainct.$(O): jcmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jcmarker.$(O): jcmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jcmaster.$(O): jcmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jcomapi.$(O): jcomapi.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jcparam.$(O): jcparam.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h -jcphuff.$(O): jcphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h jchuff.h -jcpred.$(O): jcpred.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossls.h +jcphuff.$(O): jcphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h jcprepct.$(O): jcprepct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jcsample.$(O): jcsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h -jcscale.$(O): jcscale.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossls.h -jcshuff.$(O): jcshuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h jlossls.h jchuff.h -jctrans.$(O): jctrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h +jctrans.$(O): jctrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdapimin.$(O): jdapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdapistd.$(O): jdapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdatadst.$(O): jdatadst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h jdatasrc.$(O): jdatasrc.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h -jdcoefct.$(O): jdcoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h +jdcoefct.$(O): jdcoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdcolor.$(O): jdcolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h -jddctmgr.$(O): jddctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h jdct.h +jddctmgr.$(O): jddctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h jddiffct.$(O): jddiffct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossls.h -jdhuff.$(O): jdhuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h jlossls.h jdhuff.h +jdhuff.$(O): jdhuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h jdinput.$(O): jdinput.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdlhuff.$(O): jdlhuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossls.h jdhuff.h jdlossls.$(O): jdlossls.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossls.h -jdlossy.$(O): jdlossy.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h jdmainct.$(O): jdmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdmarker.$(O): jdmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdmaster.$(O): jdmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdmerge.$(O): jdmerge.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h -jdphuff.$(O): jdphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h jdhuff.h +jdphuff.$(O): jdphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h jdpostct.$(O): jdpostct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h -jdpred.$(O): jdpred.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossls.h jdsample.$(O): jdsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h -jdscale.$(O): jdscale.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossls.h -jdshuff.$(O): jdshuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h jdhuff.h -jdtrans.$(O): jdtrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jlossy.h +jdtrans.$(O): jdtrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jerror.$(O): jerror.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jversion.h jerror.h jfdctflt.$(O): jfdctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h jfdctfst.$(O): jfdctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h diff --git a/rdswitch.c b/rdswitch.c index a60c84066..4f4bb4f58 100644 --- a/rdswitch.c +++ b/rdswitch.c @@ -1,10 +1,8 @@ /* * rdswitch.c * - * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1996, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * * This file contains routines to process some of cjpeg's more complicated @@ -332,29 +330,3 @@ set_sample_factors (j_compress_ptr cinfo, char *arg) } return TRUE; } - - -#ifdef C_LOSSLESS_SUPPORTED - -GLOBAL(boolean) -set_simple_lossless (j_compress_ptr cinfo, char *arg) -{ - int pred, pt = 0; - char ch; - - ch = ','; /* if not set by sscanf, will be ',' */ - if (sscanf(arg, "%d%c", &pred, &ch) < 1) - return FALSE; - if (ch != ',') /* syntax check */ - return FALSE; - while (*arg && *arg++ != ',') /* advance to next segment of arg string */ - ; - if (*arg) { - if (sscanf(arg, "%d", &pt) != 1) - pt = 0; - } - jpeg_simple_lossless(cinfo, pred, pt); - return TRUE; -} - -#endif /* C_LOSSLESS_SUPPORTED */ diff --git a/structure.doc b/structure.doc index 266b3ac92..a127dd3e1 100644 --- a/structure.doc +++ b/structure.doc @@ -4,6 +4,7 @@ This file was part of the Independent JPEG Group's software: Copyright (C) 1991-1995, Thomas G. Lane. Lossless JPEG Modifications: Copyright (C) 1999, Ken Murchison. +Copyright (C) 2022, D. R. Commander. For conditions of distribution and use, see the accompanying README file. @@ -23,7 +24,7 @@ In this document, JPEG-specific terminology follows the JPEG standard: A "sample" is a single component value (i.e., one number in the image data). A "coefficient" is a frequency coefficient (a DCT transform output number). A "block" is an 8x8 group of samples or coefficients. - A "data unit" is an abstract data type which is either a block for lossy + A "data unit" is an abstract data type that is either a block for lossy (DCT-based) codecs or a sample for lossless (predictive) codecs. An "MCU" (minimum coded unit) is an interleaved set of data units of size determined by the sampling factors, or a single data unit in a @@ -47,8 +48,8 @@ command-line user interface and I/O routines for several uncompressed image formats. This document concentrates on the library itself. We desire the library to be capable of supporting all JPEG baseline, extended -sequential, and progressive DCT processes, as well as the lossless (spatial) -process. Hierarchical processes are not supported. +sequential, progressive DCT, and lossless (spatial) processes. Hierarchical +processes are not supported. Within these limits, any set of compression parameters allowed by the JPEG spec should be readable for decompression. (We can be more restrictive about @@ -319,52 +320,31 @@ overall system structuring principle, not as a complete description of the task performed by any one controller. -*** Codec object structure *** - -As noted above, this library supports both the lossy (DCT-based) and lossless -JPEG processes. Because these processes have little in common with one another -(and their implementations share very little code), we need to provide a way to -isloate the underlying JPEG process from the rest of the library. This is -accomplished by introducing an abstract "codec object" which acts a generic -interface to the JPEG (de)compressor proper. - -Using the power of the object-oriented scheme described above, we build the -lossy and lossless modules as two separate implementations of the codec object. -Switching between lossy and lossless processes then becomes as trivial as -assigning the appropriate method pointers during initialization of the library. - - *** Compression object structure *** -Here is a sketch of the logical structure of the JPEG compression library: +Here is a sketch of the logical structure of the JPEG compression library in +lossy mode: |-- Colorspace conversion |-- Preprocessing controller --| | |-- Downsampling - | Main controller --| - | /--> Lossy codec - | / - |-- Compression codec < *OR* - \ - \--> Lossless codec - - -where the lossy codec looks like: + | |-- Forward DCT, quantize + |-- Coefficient controller --| + |-- Entropy encoding - |-- Forward DCT, quantize -<-- Coefficient controller --| - |-- Entropy encoding - - -and the lossless codec looks like: - - |-- Point transformation - | -<-- Difference controller --|-- Prediction, differencing - | - |-- Lossless entropy encoding +... and in lossless mode: + |-- Colorspace conversion + |-- Preprocessing controller --| + | |-- Downsampling +Main controller --| + | |-- Point transform + | | + |-- Difference controller --|-- Prediction, differencing + | + |-- Lossless mode entropy + encoding This sketch also describes the flow of control (subroutine calls) during typical image data processing. Each of the components shown in the diagram is @@ -425,16 +405,15 @@ The objects shown above are: of subsampled data is processed per call, even when the JPEG file is noninterleaved. -* Point transformation: Scale the data down by the point transformation - parameter. +* Point transform: Downscale the data by the point transform value. * Prediction and differencing: Calculate the predictor and subtract it from the input. Works on one scanline per call. The difference - controller supplies the prior scanline which is used for prediction. + controller supplies the prior scanline, which is used for prediction. -* Lossless entropy encoding: Perform Huffman or arithmetic entropy coding and - emit the coded data to the data destination module. This module handles MCU - assembly. Works on one MCU-row per call. +* Lossless mode entropy encoding: Perform Huffman or arithmetic entropy coding + and emit the coded data to the data destination module. This module handles + MCU assembly. Works on one MCU row per call. In addition to the above objects, the compression library includes these objects: @@ -475,36 +454,32 @@ decompression; the progress monitor, if used, may be shared as well. *** Decompression object structure *** -Here is a sketch of the logical structure of the JPEG decompression library: +Here is a sketch of the logical structure of the JPEG decompression library in +lossy mode: - /--> Lossy codec - / - |-- Decompression codec < *OR* - | \ - | \--> Lossless codec + |-- Entropy decoding + |-- Coefficient controller --| + | |-- Dequantize, Inverse DCT Main controller --| - | | |-- Upsampling |-- Postprocessing controller --| |-- Colorspace conversion |-- Color quantization |-- Color precision reduction +... and in lossless mode: -where the lossy codec looks like: - - |-- Entropy decoding -<-- Coefficient controller --| - |-- Dequantize, Inverse DCT - - -and the lossless codec looks like: - - |-- Lossless entropy decoding - | -<-- Difference controller --|-- Prediction, undifferencing - | - |-- Point transformation, sample size scaling - + |-- Lossless mode entropy + | decoding + | + |-- Difference controller --|-- Prediction, undifferencing + | | + | |-- Point transform, sample size + | scaling +Main controller --| + | |-- Upsampling + |-- Postprocessing controller --| |-- Colorspace conversion + |-- Color quantization + |-- Color precision reduction As before, this diagram also represents typical control flow. The objects shown are: @@ -544,17 +519,16 @@ shown are: buffering the full image. The equivalent of one fully interleaved MCU row is processed per call, even when the source JPEG file is noninterleaved. -* Lossless entropy decoding: Read coded data from the data source module and - perform Huffman or arithmetic entropy decoding. Works on one MCU-row per +* Lossless mode entropy decoding: Read coded data from the data source module + and perform Huffman or arithmetic entropy decoding. Works on one MCU row per call. * Prediction and undifferencing: Calculate the predictor and add it to the decoded difference. Works on one scanline per call. The difference - controller supplies the prior scanline which is used for prediction. + controller supplies the prior scanline, which is used for prediction. -* Point transform and sample size scaling: Scale the data up by the point - transformation parameter and scale it down to fit into the compiled-in - sample size. +* Point transform and sample size scaling: Upscale the data by the point + transform value and downscale it to fit into the compiled-in sample size. * Postprocessing controller: buffer controller for the color quantization input buffer, when quantization is in use. (Without quantization, this diff --git a/transupp.c b/transupp.c index 9491548bd..e5ec5642f 100644 --- a/transupp.c +++ b/transupp.c @@ -1,10 +1,8 @@ /* * transupp.c * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1997-1998, Thomas G. Lane. - * Lossless JPEG Modifications: - * Copyright (C) 1999, Ken Murchison. + * Copyright (C) 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 image transformation routines and other utility code @@ -86,7 +84,7 @@ do_flip_h (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, for (ci = 0; ci < dstinfo->num_components; ci++) { compptr = dstinfo->comp_info + ci; comp_width = MCU_cols * compptr->h_samp_factor; - for (blk_y = 0; blk_y < compptr->height_in_data_units; + for (blk_y = 0; blk_y < compptr->height_in_blocks; blk_y += compptr->v_samp_factor) { buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, src_coef_arrays[ci], blk_y, @@ -138,7 +136,7 @@ do_flip_v (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, for (ci = 0; ci < dstinfo->num_components; ci++) { compptr = dstinfo->comp_info + ci; comp_height = MCU_rows * compptr->v_samp_factor; - for (dst_blk_y = 0; dst_blk_y < compptr->height_in_data_units; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; dst_blk_y += compptr->v_samp_factor) { dst_buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, @@ -160,7 +158,7 @@ do_flip_v (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, /* Row is within the mirrorable area. */ dst_row_ptr = dst_buffer[offset_y]; src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1]; - for (dst_blk_x = 0; dst_blk_x < compptr->width_in_data_units; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { dst_ptr = dst_row_ptr[dst_blk_x]; src_ptr = src_row_ptr[dst_blk_x]; @@ -176,7 +174,7 @@ do_flip_v (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, } else { /* Just copy row verbatim. */ jcopy_block_row(src_buffer[offset_y], dst_buffer[offset_y], - compptr->width_in_data_units); + compptr->width_in_blocks); } } } @@ -203,13 +201,13 @@ do_transpose (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, */ for (ci = 0; ci < dstinfo->num_components; ci++) { compptr = dstinfo->comp_info + ci; - for (dst_blk_y = 0; dst_blk_y < compptr->height_in_data_units; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; dst_blk_y += compptr->v_samp_factor) { dst_buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, (JDIMENSION) compptr->v_samp_factor, TRUE); for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { - for (dst_blk_x = 0; dst_blk_x < compptr->width_in_data_units; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x += compptr->h_samp_factor) { src_buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, @@ -253,13 +251,13 @@ do_rot_90 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, for (ci = 0; ci < dstinfo->num_components; ci++) { compptr = dstinfo->comp_info + ci; comp_width = MCU_cols * compptr->h_samp_factor; - for (dst_blk_y = 0; dst_blk_y < compptr->height_in_data_units; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; dst_blk_y += compptr->v_samp_factor) { dst_buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, (JDIMENSION) compptr->v_samp_factor, TRUE); for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { - for (dst_blk_x = 0; dst_blk_x < compptr->width_in_data_units; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x += compptr->h_samp_factor) { src_buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, @@ -317,13 +315,13 @@ do_rot_270 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, for (ci = 0; ci < dstinfo->num_components; ci++) { compptr = dstinfo->comp_info + ci; comp_height = MCU_rows * compptr->v_samp_factor; - for (dst_blk_y = 0; dst_blk_y < compptr->height_in_data_units; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; dst_blk_y += compptr->v_samp_factor) { dst_buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, (JDIMENSION) compptr->v_samp_factor, TRUE); for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { - for (dst_blk_x = 0; dst_blk_x < compptr->width_in_data_units; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x += compptr->h_samp_factor) { src_buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, @@ -380,7 +378,7 @@ do_rot_180 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, compptr = dstinfo->comp_info + ci; comp_width = MCU_cols * compptr->h_samp_factor; comp_height = MCU_rows * compptr->v_samp_factor; - for (dst_blk_y = 0; dst_blk_y < compptr->height_in_data_units; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; dst_blk_y += compptr->v_samp_factor) { dst_buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, @@ -420,7 +418,7 @@ do_rot_180 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, } } /* Any remaining right-edge blocks are only mirrored vertically. */ - for (; dst_blk_x < compptr->width_in_data_units; dst_blk_x++) { + for (; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { dst_ptr = dst_row_ptr[dst_blk_x]; src_ptr = src_row_ptr[dst_blk_x]; for (i = 0; i < DCTSIZE; i += 2) { @@ -444,7 +442,7 @@ do_rot_180 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, } } /* Any remaining right-edge blocks are only copied. */ - for (; dst_blk_x < compptr->width_in_data_units; dst_blk_x++) { + for (; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { dst_ptr = dst_row_ptr[dst_blk_x]; src_ptr = src_row_ptr[dst_blk_x]; for (i = 0; i < DCTSIZE2; i++) @@ -484,13 +482,13 @@ do_transverse (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, compptr = dstinfo->comp_info + ci; comp_width = MCU_cols * compptr->h_samp_factor; comp_height = MCU_rows * compptr->v_samp_factor; - for (dst_blk_y = 0; dst_blk_y < compptr->height_in_data_units; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; dst_blk_y += compptr->v_samp_factor) { dst_buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, (JDIMENSION) compptr->v_samp_factor, TRUE); for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { - for (dst_blk_x = 0; dst_blk_x < compptr->width_in_data_units; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x += compptr->h_samp_factor) { src_buffer = (*srcinfo->mem->access_virt_barray) ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, @@ -602,9 +600,9 @@ jtransform_request_workspace (j_decompress_ptr srcinfo, compptr = srcinfo->comp_info + ci; coef_arrays[ci] = (*srcinfo->mem->request_virt_barray) ((j_common_ptr) srcinfo, JPOOL_IMAGE, FALSE, - (JDIMENSION) jround_up((long) compptr->width_in_data_units, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, (long) compptr->h_samp_factor), - (JDIMENSION) jround_up((long) compptr->height_in_data_units, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, (long) compptr->v_samp_factor), (JDIMENSION) compptr->v_samp_factor); } @@ -624,9 +622,9 @@ jtransform_request_workspace (j_decompress_ptr srcinfo, compptr = srcinfo->comp_info + ci; coef_arrays[ci] = (*srcinfo->mem->request_virt_barray) ((j_common_ptr) srcinfo, JPOOL_IMAGE, FALSE, - (JDIMENSION) jround_up((long) compptr->height_in_data_units, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, (long) compptr->v_samp_factor), - (JDIMENSION) jround_up((long) compptr->width_in_data_units, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, (long) compptr->h_samp_factor), (JDIMENSION) compptr->h_samp_factor); } diff --git a/usage.doc b/usage.doc index 8c4970af0..5bc21be91 100644 --- a/usage.doc +++ b/usage.doc @@ -140,6 +140,30 @@ progressive JPEG file at all. Switches for advanced users: + -lossless psv[,Pt] Create a lossless JPEG file using the specified + predictor selection value (1 - 7) and optional point + transform (0 - {precision}-1, where {precision} is the + JPEG data precision in bits). A point transform value + of 0 (the default) is necessary in order to create a + fully lossless JPEG file. (A non-zero point transform + value right-shifts the input samples by the specified + number of bits, which is effectively a form of lossy + color quantization.) CAUTION: lossless JPEG is not yet + widely implemented, so many decoders will be unable to + view a lossless JPEG file at all. Note that the + following features will be unavailable when compressing + or decompressing a lossless JPEG file: + * Quality/quantization table selection + * Color conversion (the JPEG image will use the same + color space as the input image) + * DCT/IDCT algorithm selection + * Smoothing + * Downsampling/upsampling + * IDCT scaling + * Transformations using jpegtran + Any switches used to enable or configure those features + will be ignored. + -dct int Use integer DCT method (default). -dct fast Use fast integer DCT (less accurate). -dct float Use floating-point DCT method. @@ -152,8 +176,9 @@ Switches for advanced users: is much less accurate than the other two. -restart N Emit a JPEG restart marker every N MCU rows, or every - N MCU blocks if "B" is attached to the number. - -restart 0 (the default) means no restart markers. + N MCU blocks (samples in lossless mode) if "B" is + attached to the number. -restart 0 (the default) means + no restart markers. -smooth N Smooth the input image to eliminate dithering noise. N, ranging from 1 to 100, indicates the strength of diff --git a/wizard.doc b/wizard.doc index 54170b227..dc4e8e45f 100644 --- a/wizard.doc +++ b/wizard.doc @@ -115,23 +115,23 @@ Multiple Scan / Progression Control By default, cjpeg emits a single-scan sequential JPEG file. The -progressive switch generates a progressive JPEG file using a default series -of progression parameters. You can create multiple-scan sequential JPEG -files or progressive JPEG files with custom progression parameters by using -the -scans switch: +of progression parameters. You can create multiple-scan sequential or lossless +JPEG files or progressive JPEG files with custom progression parameters by +using the -scans switch: -scans file Use the scan sequence given in the named file. The specified file should be a text file containing a "scan script". The script specifies the contents and ordering of the scans to be emitted. Each entry in the script defines one scan. A scan definition specifies -the components to be included in the scan, and for progressive JPEG it also -specifies the progression parameters Ss,Se,Ah,Al for the scan. Scan -definitions are separated by semicolons (';'). A semicolon after the last -scan definition is optional. +the components to be included in the scan, and for progressive and lossless +JPEG it also specifies the progression/lossless parameters Ss,Se,Ah,Al for the +scan. Scan definitions are separated by semicolons (';'). A semicolon after +the last scan definition is optional. Each scan definition contains one to four component indexes, optionally -followed by a colon (':') and the four progressive-JPEG parameters. The -component indexes denote which color component(s) are to be transmitted in +followed by a colon (':') and the four progressive/lossless-JPEG parameters. +The component indexes denote which color component(s) are to be transmitted in the scan. Components are numbered in the order in which they appear in the JPEG SOF marker, with the first component being numbered 0. (Note that these indexes are not the "component ID" codes assigned to the components, just @@ -142,13 +142,23 @@ The progression parameters for each scan are: Se Zigzag index of last coefficient included in scan Ah Zero for first scan of a coefficient, else Al of prior scan Al Successive approximation low bit position for scan -If the progression parameters are omitted, the values 0,63,0,0 are used, -producing a sequential JPEG file. cjpeg automatically determines whether + +The lossless parameters for each scan are: + Ss Predictor selection value + Se Must be zero + Ah Must be zero + Al Point transform value + +If the progression/lossless parameters are omitted, the values 0,63,0,0 are +used, producing a sequential JPEG file. cjpeg automatically determines whether the script represents a progressive or sequential file, by observing whether -Ss and Se values other than 0 and 63 appear. (The -progressive switch is -not needed to specify this; in fact, it is ignored when -scans appears.) -The scan script must meet the JPEG restrictions on progression sequences. -(cjpeg checks that the spec's requirements are obeyed.) +Ss and Se values other than 0 and 63 appear. cjpeg also automatically +determines whether the script represents a lossless file, by observing whether +Ss (the predictor selection value) is non-zero and Se is zero, which are +illegal values for progressive and sequential files. (The -progressive and +-lossless switches are not needed to specify this; in fact, they are ignored +when -scans appears.) The scan script must meet the JPEG restrictions on +progression sequences. (cjpeg checks that the spec's requirements are obeyed.) Scan script files are free format, in that arbitrary whitespace can appear between numbers and around punctuation. Also, comments can be included: a From 25ccad99a0b752a9c10c16e4b237a1d503817527 Mon Sep 17 00:00:00 2001 From: DRC Date: Wed, 16 Nov 2022 15:57:25 -0600 Subject: [PATCH 043/162] TurboJPEG: 8-bit lossless JPEG support --- CMakeLists.txt | 12 ++ ChangeLog.md | 9 +- doc/html/group___turbo_j_p_e_g.html | 32 ++++- doc/html/search/all_6.js | 131 +++++++++--------- doc/html/search/all_7.js | 2 +- doc/html/search/all_8.js | 2 +- doc/html/search/all_9.js | 2 +- doc/html/search/classes_0.js | 6 +- doc/html/search/enums_0.js | 10 +- doc/html/search/enumvalues_0.js | 68 ++++----- doc/html/search/functions_0.js | 56 ++++---- doc/html/search/groups_0.js | 2 +- doc/html/search/typedefs_0.js | 4 +- doc/html/search/variables_0.js | 2 +- doc/html/search/variables_1.js | 4 +- doc/html/search/variables_2.js | 2 +- doc/html/search/variables_3.js | 2 +- doc/html/search/variables_4.js | 4 +- doc/html/search/variables_5.js | 2 +- doc/html/search/variables_6.js | 14 +- doc/html/search/variables_7.js | 2 +- doc/html/search/variables_8.js | 2 +- doc/html/search/variables_9.js | 2 +- java/TJBench.java | 9 ++ java/TJUnitTest.java | 91 ++++++++---- java/doc/constant-values.html | 53 ++++--- java/doc/index-all.html | 4 + java/doc/org/libjpegturbo/turbojpeg/TJ.html | 77 +++++++--- .../libjpegturbo/turbojpeg/TJCompressor.html | 9 +- java/org/libjpegturbo/turbojpeg/TJ.java | 34 +++-- .../libjpegturbo/turbojpeg/TJCompressor.java | 12 +- .../libjpegturbo/turbojpeg/TJTransform.java | 16 +-- tjbench.c | 9 ++ tjunittest.c | 70 +++++++--- turbojpeg.c | 31 ++++- turbojpeg.h | 58 +++++--- 36 files changed, 541 insertions(+), 304 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 11337a166..a28240f96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -753,6 +753,10 @@ if(WITH_JAVA) ${Java_JAVA_EXECUTABLE} ${JAVAARGS} -cp java/turbojpeg.jar -Djava.library.path=${CMAKE_CURRENT_BINARY_DIR}/${OBJDIR} TJUnitTest -yuv -noyuvpad) + add_test(TJUnitTest-lossless + ${Java_JAVA_EXECUTABLE} ${JAVAARGS} -cp java/turbojpeg.jar + -Djava.library.path=${CMAKE_CURRENT_BINARY_DIR}/${OBJDIR} + TJUnitTest -lossless) add_test(TJUnitTest-bi ${Java_JAVA_EXECUTABLE} ${JAVAARGS} -cp java/turbojpeg.jar -Djava.library.path=${CMAKE_CURRENT_BINARY_DIR}/${OBJDIR} @@ -765,6 +769,10 @@ if(WITH_JAVA) ${Java_JAVA_EXECUTABLE} ${JAVAARGS} -cp java/turbojpeg.jar -Djava.library.path=${CMAKE_CURRENT_BINARY_DIR}/${OBJDIR} TJUnitTest -bi -yuv -noyuvpad) + add_test(TJUnitTest-bi-lossless + ${Java_JAVA_EXECUTABLE} ${JAVAARGS} -cp java/turbojpeg.jar + -Djava.library.path=${CMAKE_CURRENT_BINARY_DIR}/${OBJDIR} + TJUnitTest -bi -lossless) endif() set(TEST_LIBTYPES "") @@ -890,6 +898,10 @@ foreach(libtype ${TEST_LIBTYPES}) ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -yuv -alloc) add_test(tjunittest-${libtype}-yuv-nopad ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -yuv -noyuvpad) + add_test(tjunittest-${libtype}-lossless + ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -lossless) + add_test(tjunittest-${libtype}-lossless-alloc + ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -lossless -alloc) add_test(tjunittest-${libtype}-bmp ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -bmp) diff --git a/ChangeLog.md b/ChangeLog.md index fa9625a44..cc5df1e4e 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -31,9 +31,12 @@ tables. have been removed. 4. Added support for 8-bit and 12-bit lossless JPEG images. A new libjpeg API -function (`jpeg_enable_lossless()`) and cjpeg command-line argument -(`-lossless`) can be used to create a lossless JPEG image. (Decompression of -lossless JPEG images is handled automatically.) +function (`jpeg_enable_lossless()`), TurboJPEG API flag (`TJFLAG_LOSSLESS` in +the C API and `TJ.FLAG_LOSSLESS` in the Java API), and cjpeg/TJBench +command-line argument (`-lossless`) can be used to create a lossless JPEG +image. (Decompression of lossless JPEG images is handled automatically.) Note +that the TurboJPEG API and TJBench can currently only be used to create and +decompress 8-bit lossless JPEG images. 5. Introduced a new flag in the TurboJPEG C and Java APIs (`TJFLAG_ARITHMETIC` and `TJ.FLAG_ARITHMETIC`, respectively) that causes the library to use diff --git a/doc/html/group___turbo_j_p_e_g.html b/doc/html/group___turbo_j_p_e_g.html index b02b44c41..ccfa88662 100644 --- a/doc/html/group___turbo_j_p_e_g.html +++ b/doc/html/group___turbo_j_p_e_g.html @@ -128,6 +128,9 @@ #define TJFLAG_ARITHMETIC  Use arithmetic entropy coding in JPEG images generated by the compression and transform functions. More...
  +#define TJFLAG_LOSSLESS + Generate a lossless JPEG image when compressing. More...
+  #define TJ_NUMERR  The number of error codes. More...
  @@ -540,6 +543,33 @@

this report.

+ + + +

◆ TJFLAG_LOSSLESS

+ +
+
+ + + + +
#define TJFLAG_LOSSLESS
+
+ +

Generate a lossless JPEG image when compressing.

+

In most cases, compressing and decompressing lossless JPEG images is considerably slower than compressing and decompressing lossy JPEG images. Also note that the following features are not available with lossless JPEG images:

    +
  • Colorspace conversion
  • +
  • Chrominance subsampling
  • +
  • JPEG quality selection
  • +
  • DCT/IDCT algorithm selection
  • +
  • Progressive entropy coding
  • +
  • Arithmetic entropy coding
  • +
  • Compression from/decompression to YUV planar images
  • +
  • Decompression scaling
  • +
  • Lossless transformations
  • +
+
@@ -1225,7 +1255,7 @@

TJFLAG_NOREALLOC, you should always check *jpegBuf upon return from this function, as it may have changed. jpegSizepointer to an unsigned long variable that holds the size of the JPEG image buffer. If *jpegBuf points to a pre-allocated buffer, then *jpegSize should be set to the size of the buffer. Upon return, *jpegSize will contain the size of the JPEG image (in bytes.) If *jpegBuf points to a JPEG image buffer that is being reused from a previous call to one of the JPEG compression functions, then *jpegSize is ignored. jpegSubsampthe level of chrominance subsampling to be used when generating the JPEG image (see Chrominance subsampling options.) - jpegQualthe image quality of the generated JPEG image (1 = worst, 100 = best) + jpegQualthe image quality of the generated JPEG image (1 = worst, 100 = best.) When generating a lossless JPEG image (see TJFLAG_LOSSLESS), jpegQual is psv * 10 + Pt, where psv is the predictor selection value (1-7) and Pt is the point transform (0-7). A point transform value of 0 is necessary in order to create a fully lossless JPEG image. (A non-zero point transform value right-shifts the input samples by the specified number of bits, which is effectively a form of lossy color quantization.) flagsthe bitwise OR of one or more of the flags diff --git a/doc/html/search/all_6.js b/doc/html/search/all_6.js index d981d12d1..2ac83e759 100644 --- a/doc/html/search/all_6.js +++ b/doc/html/search/all_6.js @@ -37,69 +37,70 @@ var searchData= ['tjflag_5ffastdct_42',['TJFLAG_FASTDCT',['../group___turbo_j_p_e_g.html#gaabce235db80d3f698b27f36cbd453da2',1,'turbojpeg.h']]], ['tjflag_5ffastupsample_43',['TJFLAG_FASTUPSAMPLE',['../group___turbo_j_p_e_g.html#ga4ee4506c81177a06f77e2504a22efd2d',1,'turbojpeg.h']]], ['tjflag_5flimitscans_44',['TJFLAG_LIMITSCANS',['../group___turbo_j_p_e_g.html#ga163e6482dc5096831feef9c79ff3f805',1,'turbojpeg.h']]], - ['tjflag_5fnorealloc_45',['TJFLAG_NOREALLOC',['../group___turbo_j_p_e_g.html#ga8808d403c68b62aaa58a4c1e58e98963',1,'turbojpeg.h']]], - ['tjflag_5fprogressive_46',['TJFLAG_PROGRESSIVE',['../group___turbo_j_p_e_g.html#ga43b426750b46190a25d34a67ef76df1b',1,'turbojpeg.h']]], - ['tjflag_5fstoponwarning_47',['TJFLAG_STOPONWARNING',['../group___turbo_j_p_e_g.html#ga519cfa4ef6c18d9e5b455fdf59306a3a',1,'turbojpeg.h']]], - ['tjfree_48',['tjFree',['../group___turbo_j_p_e_g.html#gaea863d2da0cdb609563aabdf9196514b',1,'turbojpeg.h']]], - ['tjgeterrorcode_49',['tjGetErrorCode',['../group___turbo_j_p_e_g.html#ga414feeffbf860ebd31c745df203de410',1,'turbojpeg.h']]], - ['tjgeterrorstr2_50',['tjGetErrorStr2',['../group___turbo_j_p_e_g.html#ga1ead8574f9f39fbafc6b497124e7aafa',1,'turbojpeg.h']]], - ['tjgetscalingfactors_51',['tjGetScalingFactors',['../group___turbo_j_p_e_g.html#gac3854476006b10787bd128f7ede48057',1,'turbojpeg.h']]], - ['tjgreenoffset_52',['tjGreenOffset',['../group___turbo_j_p_e_g.html#ga82d6e35da441112a411da41923c0ba2f',1,'turbojpeg.h']]], - ['tjhandle_53',['tjhandle',['../group___turbo_j_p_e_g.html#ga758d2634ecb4949de7815cba621f5763',1,'turbojpeg.h']]], - ['tjinitcompress_54',['tjInitCompress',['../group___turbo_j_p_e_g.html#ga9d63a05fc6d813f4aae06107041a37e8',1,'turbojpeg.h']]], - ['tjinitdecompress_55',['tjInitDecompress',['../group___turbo_j_p_e_g.html#ga52300eac3f3d9ef4bab303bc244f62d3',1,'turbojpeg.h']]], - ['tjinittransform_56',['tjInitTransform',['../group___turbo_j_p_e_g.html#ga928beff6ac248ceadf01089fc6b41957',1,'turbojpeg.h']]], - ['tjloadimage_57',['tjLoadImage',['../group___turbo_j_p_e_g.html#gaffbd83c375e79f5db4b5c5d8ad4466e7',1,'turbojpeg.h']]], - ['tjmcuheight_58',['tjMCUHeight',['../group___turbo_j_p_e_g.html#gabd247bb9fecb393eca57366feb8327bf',1,'turbojpeg.h']]], - ['tjmcuwidth_59',['tjMCUWidth',['../group___turbo_j_p_e_g.html#ga9e61e7cd47a15a173283ba94e781308c',1,'turbojpeg.h']]], - ['tjpad_60',['TJPAD',['../group___turbo_j_p_e_g.html#ga0aba955473315e405295d978f0c16511',1,'turbojpeg.h']]], - ['tjpf_61',['TJPF',['../group___turbo_j_p_e_g.html#gac916144e26c3817ac514e64ae5d12e2a',1,'turbojpeg.h']]], - ['tjpf_5fabgr_62',['TJPF_ABGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa1ba1a7f1631dbeaa49a0a85fc4a40081',1,'turbojpeg.h']]], - ['tjpf_5fargb_63',['TJPF_ARGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aae8f846ed9d9de99b6e1dfe448848765c',1,'turbojpeg.h']]], - ['tjpf_5fbgr_64',['TJPF_BGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aab10624437fb8ef495a0b153e65749839',1,'turbojpeg.h']]], - ['tjpf_5fbgra_65',['TJPF_BGRA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aac037ff1845cf9b74bb81a3659c2b9fb4',1,'turbojpeg.h']]], - ['tjpf_5fbgrx_66',['TJPF_BGRX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa2a1fbf569ca79897eae886e3376ca4c8',1,'turbojpeg.h']]], - ['tjpf_5fcmyk_67',['TJPF_CMYK',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7f5100ec44c91994e243f1cf55553f8b',1,'turbojpeg.h']]], - ['tjpf_5fgray_68',['TJPF_GRAY',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa5431b54b015337705f13118073711a1a',1,'turbojpeg.h']]], - ['tjpf_5frgb_69',['TJPF_RGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7ce93230bff449518ce387c17e6ed37c',1,'turbojpeg.h']]], - ['tjpf_5frgba_70',['TJPF_RGBA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa88d2e88fab67f6503cf972e14851cc12',1,'turbojpeg.h']]], - ['tjpf_5frgbx_71',['TJPF_RGBX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa83973bebb7e2dc6fa8bae89ff3f42e01',1,'turbojpeg.h']]], - ['tjpf_5funknown_72',['TJPF_UNKNOWN',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa84c1a6cead7952998e2fb895844a21ed',1,'turbojpeg.h']]], - ['tjpf_5fxbgr_73',['TJPF_XBGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aaf6603b27147de47e212e75dac027b2af',1,'turbojpeg.h']]], - ['tjpf_5fxrgb_74',['TJPF_XRGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aadae996905efcfa3b42a0bb3bea7f9d84',1,'turbojpeg.h']]], - ['tjpixelsize_75',['tjPixelSize',['../group___turbo_j_p_e_g.html#gad77cf8fe5b2bfd3cb3f53098146abb4c',1,'turbojpeg.h']]], - ['tjplaneheight_76',['tjPlaneHeight',['../group___turbo_j_p_e_g.html#ga1a209696c6a80748f20e134b3c64789f',1,'turbojpeg.h']]], - ['tjplanesizeyuv_77',['tjPlaneSizeYUV',['../group___turbo_j_p_e_g.html#gab4ab7b24f6e797d79abaaa670373961d',1,'turbojpeg.h']]], - ['tjplanewidth_78',['tjPlaneWidth',['../group___turbo_j_p_e_g.html#ga63fb66bb1e36c74008c4634360becbb1',1,'turbojpeg.h']]], - ['tjredoffset_79',['tjRedOffset',['../group___turbo_j_p_e_g.html#gadd9b446742ac8a3923f7992c7988fea8',1,'turbojpeg.h']]], - ['tjregion_80',['tjregion',['../structtjregion.html',1,'']]], - ['tjsamp_81',['TJSAMP',['../group___turbo_j_p_e_g.html#ga1d047060ea80bb9820d540bb928e9074',1,'turbojpeg.h']]], - ['tjsamp_5f411_82',['TJSAMP_411',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a28ec62575e5ea295c3fde3001dc628e2',1,'turbojpeg.h']]], - ['tjsamp_5f420_83',['TJSAMP_420',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a63085dbf683cfe39e513cdb6343e3737',1,'turbojpeg.h']]], - ['tjsamp_5f422_84',['TJSAMP_422',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a136130902cc578f11f32429b59368404',1,'turbojpeg.h']]], - ['tjsamp_5f440_85',['TJSAMP_440',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074accf740e6f3aa6ba20ba922cad13cb974',1,'turbojpeg.h']]], - ['tjsamp_5f444_86',['TJSAMP_444',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074afb8da4f44197837bdec0a4f593dacae3',1,'turbojpeg.h']]], - ['tjsamp_5fgray_87',['TJSAMP_GRAY',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a3f1c9504842ddc7a48d0f690754b6248',1,'turbojpeg.h']]], - ['tjsaveimage_88',['tjSaveImage',['../group___turbo_j_p_e_g.html#ga6f445b22d8933ae4815b3370a538d879',1,'turbojpeg.h']]], - ['tjscaled_89',['TJSCALED',['../group___turbo_j_p_e_g.html#ga84878bb65404204743aa18cac02781df',1,'turbojpeg.h']]], - ['tjscalingfactor_90',['tjscalingfactor',['../structtjscalingfactor.html',1,'']]], - ['tjtransform_91',['tjtransform',['../structtjtransform.html',1,'tjtransform'],['../group___turbo_j_p_e_g.html#ga504805ec0161f1b505397ca0118bf8fd',1,'tjtransform(): turbojpeg.h'],['../group___turbo_j_p_e_g.html#ga9cb8abf4cc91881e04a0329b2270be25',1,'tjTransform(tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, int n, unsigned char **dstBufs, unsigned long *dstSizes, tjtransform *transforms, int flags): turbojpeg.h']]], - ['tjxop_92',['TJXOP',['../group___turbo_j_p_e_g.html#ga2de531af4e7e6c4f124908376b354866',1,'turbojpeg.h']]], - ['tjxop_5fhflip_93',['TJXOP_HFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aa0df69776caa30f0fa28e26332d311ce',1,'turbojpeg.h']]], - ['tjxop_5fnone_94',['TJXOP_NONE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aad88c0366cd3f7d0eac9d7a3fa1c2c27',1,'turbojpeg.h']]], - ['tjxop_5frot180_95',['TJXOP_ROT180',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a140952eb8dd0300accfcc22726d69692',1,'turbojpeg.h']]], - ['tjxop_5frot270_96',['TJXOP_ROT270',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a3064ee5dfb7f032df332818587567a08',1,'turbojpeg.h']]], - ['tjxop_5frot90_97',['TJXOP_ROT90',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a43b2bbb23bc4bd548422d43fbe9af128',1,'turbojpeg.h']]], - ['tjxop_5ftranspose_98',['TJXOP_TRANSPOSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a31060aed199f886afdd417f80499c32d',1,'turbojpeg.h']]], - ['tjxop_5ftransverse_99',['TJXOP_TRANSVERSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866af3b14d488aea6ece9e5b3df73a74d6a4',1,'turbojpeg.h']]], - ['tjxop_5fvflip_100',['TJXOP_VFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a324eddfbec53b7e691f61e56929d0d5d',1,'turbojpeg.h']]], - ['tjxopt_5farithmetic_101',['TJXOPT_ARITHMETIC',['../group___turbo_j_p_e_g.html#gaecaaa3b7e2af812592c015d83207f010',1,'turbojpeg.h']]], - ['tjxopt_5fcopynone_102',['TJXOPT_COPYNONE',['../group___turbo_j_p_e_g.html#ga153b468cfb905d0de61706c838986fe8',1,'turbojpeg.h']]], - ['tjxopt_5fcrop_103',['TJXOPT_CROP',['../group___turbo_j_p_e_g.html#ga9c771a757fc1294add611906b89ab2d2',1,'turbojpeg.h']]], - ['tjxopt_5fgray_104',['TJXOPT_GRAY',['../group___turbo_j_p_e_g.html#ga3acee7b48ade1b99e5588736007c2589',1,'turbojpeg.h']]], - ['tjxopt_5fnooutput_105',['TJXOPT_NOOUTPUT',['../group___turbo_j_p_e_g.html#gafbf992bbf6e006705886333703ffab31',1,'turbojpeg.h']]], - ['tjxopt_5fperfect_106',['TJXOPT_PERFECT',['../group___turbo_j_p_e_g.html#ga50e03cb5ed115330e212417429600b00',1,'turbojpeg.h']]], - ['tjxopt_5fprogressive_107',['TJXOPT_PROGRESSIVE',['../group___turbo_j_p_e_g.html#gad2371c80674584ecc1a7d75e564cf026',1,'turbojpeg.h']]], - ['tjxopt_5ftrim_108',['TJXOPT_TRIM',['../group___turbo_j_p_e_g.html#ga319826b7eb1583c0595bbe7b95428709',1,'turbojpeg.h']]], - ['turbojpeg_109',['TurboJPEG',['../group___turbo_j_p_e_g.html',1,'']]] + ['tjflag_5flossless_45',['TJFLAG_LOSSLESS',['../group___turbo_j_p_e_g.html#gaaf0e8b612bb5b981329db9f30e2115bd',1,'turbojpeg.h']]], + ['tjflag_5fnorealloc_46',['TJFLAG_NOREALLOC',['../group___turbo_j_p_e_g.html#ga8808d403c68b62aaa58a4c1e58e98963',1,'turbojpeg.h']]], + ['tjflag_5fprogressive_47',['TJFLAG_PROGRESSIVE',['../group___turbo_j_p_e_g.html#ga43b426750b46190a25d34a67ef76df1b',1,'turbojpeg.h']]], + ['tjflag_5fstoponwarning_48',['TJFLAG_STOPONWARNING',['../group___turbo_j_p_e_g.html#ga519cfa4ef6c18d9e5b455fdf59306a3a',1,'turbojpeg.h']]], + ['tjfree_49',['tjFree',['../group___turbo_j_p_e_g.html#gaea863d2da0cdb609563aabdf9196514b',1,'turbojpeg.h']]], + ['tjgeterrorcode_50',['tjGetErrorCode',['../group___turbo_j_p_e_g.html#ga414feeffbf860ebd31c745df203de410',1,'turbojpeg.h']]], + ['tjgeterrorstr2_51',['tjGetErrorStr2',['../group___turbo_j_p_e_g.html#ga1ead8574f9f39fbafc6b497124e7aafa',1,'turbojpeg.h']]], + ['tjgetscalingfactors_52',['tjGetScalingFactors',['../group___turbo_j_p_e_g.html#gac3854476006b10787bd128f7ede48057',1,'turbojpeg.h']]], + ['tjgreenoffset_53',['tjGreenOffset',['../group___turbo_j_p_e_g.html#ga82d6e35da441112a411da41923c0ba2f',1,'turbojpeg.h']]], + ['tjhandle_54',['tjhandle',['../group___turbo_j_p_e_g.html#ga758d2634ecb4949de7815cba621f5763',1,'turbojpeg.h']]], + ['tjinitcompress_55',['tjInitCompress',['../group___turbo_j_p_e_g.html#ga9d63a05fc6d813f4aae06107041a37e8',1,'turbojpeg.h']]], + ['tjinitdecompress_56',['tjInitDecompress',['../group___turbo_j_p_e_g.html#ga52300eac3f3d9ef4bab303bc244f62d3',1,'turbojpeg.h']]], + ['tjinittransform_57',['tjInitTransform',['../group___turbo_j_p_e_g.html#ga928beff6ac248ceadf01089fc6b41957',1,'turbojpeg.h']]], + ['tjloadimage_58',['tjLoadImage',['../group___turbo_j_p_e_g.html#gaffbd83c375e79f5db4b5c5d8ad4466e7',1,'turbojpeg.h']]], + ['tjmcuheight_59',['tjMCUHeight',['../group___turbo_j_p_e_g.html#gabd247bb9fecb393eca57366feb8327bf',1,'turbojpeg.h']]], + ['tjmcuwidth_60',['tjMCUWidth',['../group___turbo_j_p_e_g.html#ga9e61e7cd47a15a173283ba94e781308c',1,'turbojpeg.h']]], + ['tjpad_61',['TJPAD',['../group___turbo_j_p_e_g.html#ga0aba955473315e405295d978f0c16511',1,'turbojpeg.h']]], + ['tjpf_62',['TJPF',['../group___turbo_j_p_e_g.html#gac916144e26c3817ac514e64ae5d12e2a',1,'turbojpeg.h']]], + ['tjpf_5fabgr_63',['TJPF_ABGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa1ba1a7f1631dbeaa49a0a85fc4a40081',1,'turbojpeg.h']]], + ['tjpf_5fargb_64',['TJPF_ARGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aae8f846ed9d9de99b6e1dfe448848765c',1,'turbojpeg.h']]], + ['tjpf_5fbgr_65',['TJPF_BGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aab10624437fb8ef495a0b153e65749839',1,'turbojpeg.h']]], + ['tjpf_5fbgra_66',['TJPF_BGRA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aac037ff1845cf9b74bb81a3659c2b9fb4',1,'turbojpeg.h']]], + ['tjpf_5fbgrx_67',['TJPF_BGRX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa2a1fbf569ca79897eae886e3376ca4c8',1,'turbojpeg.h']]], + ['tjpf_5fcmyk_68',['TJPF_CMYK',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7f5100ec44c91994e243f1cf55553f8b',1,'turbojpeg.h']]], + ['tjpf_5fgray_69',['TJPF_GRAY',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa5431b54b015337705f13118073711a1a',1,'turbojpeg.h']]], + ['tjpf_5frgb_70',['TJPF_RGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7ce93230bff449518ce387c17e6ed37c',1,'turbojpeg.h']]], + ['tjpf_5frgba_71',['TJPF_RGBA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa88d2e88fab67f6503cf972e14851cc12',1,'turbojpeg.h']]], + ['tjpf_5frgbx_72',['TJPF_RGBX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa83973bebb7e2dc6fa8bae89ff3f42e01',1,'turbojpeg.h']]], + ['tjpf_5funknown_73',['TJPF_UNKNOWN',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa84c1a6cead7952998e2fb895844a21ed',1,'turbojpeg.h']]], + ['tjpf_5fxbgr_74',['TJPF_XBGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aaf6603b27147de47e212e75dac027b2af',1,'turbojpeg.h']]], + ['tjpf_5fxrgb_75',['TJPF_XRGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aadae996905efcfa3b42a0bb3bea7f9d84',1,'turbojpeg.h']]], + ['tjpixelsize_76',['tjPixelSize',['../group___turbo_j_p_e_g.html#gad77cf8fe5b2bfd3cb3f53098146abb4c',1,'turbojpeg.h']]], + ['tjplaneheight_77',['tjPlaneHeight',['../group___turbo_j_p_e_g.html#ga1a209696c6a80748f20e134b3c64789f',1,'turbojpeg.h']]], + ['tjplanesizeyuv_78',['tjPlaneSizeYUV',['../group___turbo_j_p_e_g.html#gab4ab7b24f6e797d79abaaa670373961d',1,'turbojpeg.h']]], + ['tjplanewidth_79',['tjPlaneWidth',['../group___turbo_j_p_e_g.html#ga63fb66bb1e36c74008c4634360becbb1',1,'turbojpeg.h']]], + ['tjredoffset_80',['tjRedOffset',['../group___turbo_j_p_e_g.html#gadd9b446742ac8a3923f7992c7988fea8',1,'turbojpeg.h']]], + ['tjregion_81',['tjregion',['../structtjregion.html',1,'']]], + ['tjsamp_82',['TJSAMP',['../group___turbo_j_p_e_g.html#ga1d047060ea80bb9820d540bb928e9074',1,'turbojpeg.h']]], + ['tjsamp_5f411_83',['TJSAMP_411',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a28ec62575e5ea295c3fde3001dc628e2',1,'turbojpeg.h']]], + ['tjsamp_5f420_84',['TJSAMP_420',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a63085dbf683cfe39e513cdb6343e3737',1,'turbojpeg.h']]], + ['tjsamp_5f422_85',['TJSAMP_422',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a136130902cc578f11f32429b59368404',1,'turbojpeg.h']]], + ['tjsamp_5f440_86',['TJSAMP_440',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074accf740e6f3aa6ba20ba922cad13cb974',1,'turbojpeg.h']]], + ['tjsamp_5f444_87',['TJSAMP_444',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074afb8da4f44197837bdec0a4f593dacae3',1,'turbojpeg.h']]], + ['tjsamp_5fgray_88',['TJSAMP_GRAY',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a3f1c9504842ddc7a48d0f690754b6248',1,'turbojpeg.h']]], + ['tjsaveimage_89',['tjSaveImage',['../group___turbo_j_p_e_g.html#ga6f445b22d8933ae4815b3370a538d879',1,'turbojpeg.h']]], + ['tjscaled_90',['TJSCALED',['../group___turbo_j_p_e_g.html#ga84878bb65404204743aa18cac02781df',1,'turbojpeg.h']]], + ['tjscalingfactor_91',['tjscalingfactor',['../structtjscalingfactor.html',1,'']]], + ['tjtransform_92',['tjtransform',['../structtjtransform.html',1,'tjtransform'],['../group___turbo_j_p_e_g.html#ga9cb8abf4cc91881e04a0329b2270be25',1,'tjTransform(tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, int n, unsigned char **dstBufs, unsigned long *dstSizes, tjtransform *transforms, int flags): turbojpeg.h'],['../group___turbo_j_p_e_g.html#ga504805ec0161f1b505397ca0118bf8fd',1,'tjtransform(): turbojpeg.h']]], + ['tjxop_93',['TJXOP',['../group___turbo_j_p_e_g.html#ga2de531af4e7e6c4f124908376b354866',1,'turbojpeg.h']]], + ['tjxop_5fhflip_94',['TJXOP_HFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aa0df69776caa30f0fa28e26332d311ce',1,'turbojpeg.h']]], + ['tjxop_5fnone_95',['TJXOP_NONE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aad88c0366cd3f7d0eac9d7a3fa1c2c27',1,'turbojpeg.h']]], + ['tjxop_5frot180_96',['TJXOP_ROT180',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a140952eb8dd0300accfcc22726d69692',1,'turbojpeg.h']]], + ['tjxop_5frot270_97',['TJXOP_ROT270',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a3064ee5dfb7f032df332818587567a08',1,'turbojpeg.h']]], + ['tjxop_5frot90_98',['TJXOP_ROT90',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a43b2bbb23bc4bd548422d43fbe9af128',1,'turbojpeg.h']]], + ['tjxop_5ftranspose_99',['TJXOP_TRANSPOSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a31060aed199f886afdd417f80499c32d',1,'turbojpeg.h']]], + ['tjxop_5ftransverse_100',['TJXOP_TRANSVERSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866af3b14d488aea6ece9e5b3df73a74d6a4',1,'turbojpeg.h']]], + ['tjxop_5fvflip_101',['TJXOP_VFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a324eddfbec53b7e691f61e56929d0d5d',1,'turbojpeg.h']]], + ['tjxopt_5farithmetic_102',['TJXOPT_ARITHMETIC',['../group___turbo_j_p_e_g.html#gaecaaa3b7e2af812592c015d83207f010',1,'turbojpeg.h']]], + ['tjxopt_5fcopynone_103',['TJXOPT_COPYNONE',['../group___turbo_j_p_e_g.html#ga153b468cfb905d0de61706c838986fe8',1,'turbojpeg.h']]], + ['tjxopt_5fcrop_104',['TJXOPT_CROP',['../group___turbo_j_p_e_g.html#ga9c771a757fc1294add611906b89ab2d2',1,'turbojpeg.h']]], + ['tjxopt_5fgray_105',['TJXOPT_GRAY',['../group___turbo_j_p_e_g.html#ga3acee7b48ade1b99e5588736007c2589',1,'turbojpeg.h']]], + ['tjxopt_5fnooutput_106',['TJXOPT_NOOUTPUT',['../group___turbo_j_p_e_g.html#gafbf992bbf6e006705886333703ffab31',1,'turbojpeg.h']]], + ['tjxopt_5fperfect_107',['TJXOPT_PERFECT',['../group___turbo_j_p_e_g.html#ga50e03cb5ed115330e212417429600b00',1,'turbojpeg.h']]], + ['tjxopt_5fprogressive_108',['TJXOPT_PROGRESSIVE',['../group___turbo_j_p_e_g.html#gad2371c80674584ecc1a7d75e564cf026',1,'turbojpeg.h']]], + ['tjxopt_5ftrim_109',['TJXOPT_TRIM',['../group___turbo_j_p_e_g.html#ga319826b7eb1583c0595bbe7b95428709',1,'turbojpeg.h']]], + ['turbojpeg_110',['TurboJPEG',['../group___turbo_j_p_e_g.html',1,'']]] ]; diff --git a/doc/html/search/all_7.js b/doc/html/search/all_7.js index 37cc9b1c4..2f57008af 100644 --- a/doc/html/search/all_7.js +++ b/doc/html/search/all_7.js @@ -1,4 +1,4 @@ var searchData= [ - ['w_110',['w',['../structtjregion.html#ab6eb73ceef584fc23c8c8097926dce42',1,'tjregion']]] + ['w_111',['w',['../structtjregion.html#ab6eb73ceef584fc23c8c8097926dce42',1,'tjregion']]] ]; diff --git a/doc/html/search/all_8.js b/doc/html/search/all_8.js index e70f9eadd..98e47c02b 100644 --- a/doc/html/search/all_8.js +++ b/doc/html/search/all_8.js @@ -1,4 +1,4 @@ var searchData= [ - ['x_111',['x',['../structtjregion.html#a4b6a37a93997091b26a75831fa291ad9',1,'tjregion']]] + ['x_112',['x',['../structtjregion.html#a4b6a37a93997091b26a75831fa291ad9',1,'tjregion']]] ]; diff --git a/doc/html/search/all_9.js b/doc/html/search/all_9.js index 0d7cb60f5..8c5b0555a 100644 --- a/doc/html/search/all_9.js +++ b/doc/html/search/all_9.js @@ -1,4 +1,4 @@ var searchData= [ - ['y_112',['y',['../structtjregion.html#a7b3e0c24cfe87acc80e334cafdcf22c2',1,'tjregion']]] + ['y_113',['y',['../structtjregion.html#a7b3e0c24cfe87acc80e334cafdcf22c2',1,'tjregion']]] ]; diff --git a/doc/html/search/classes_0.js b/doc/html/search/classes_0.js index e36fcedc6..39971e8e4 100644 --- a/doc/html/search/classes_0.js +++ b/doc/html/search/classes_0.js @@ -1,6 +1,6 @@ var searchData= [ - ['tjregion_113',['tjregion',['../structtjregion.html',1,'']]], - ['tjscalingfactor_114',['tjscalingfactor',['../structtjscalingfactor.html',1,'']]], - ['tjtransform_115',['tjtransform',['../structtjtransform.html',1,'']]] + ['tjregion_114',['tjregion',['../structtjregion.html',1,'']]], + ['tjscalingfactor_115',['tjscalingfactor',['../structtjscalingfactor.html',1,'']]], + ['tjtransform_116',['tjtransform',['../structtjtransform.html',1,'']]] ]; diff --git a/doc/html/search/enums_0.js b/doc/html/search/enums_0.js index 04cd0ce1c..6d5b80ad7 100644 --- a/doc/html/search/enums_0.js +++ b/doc/html/search/enums_0.js @@ -1,8 +1,8 @@ var searchData= [ - ['tjcs_164',['TJCS',['../group___turbo_j_p_e_g.html#ga4f83ad3368e0e29d1957be0efa7c3720',1,'turbojpeg.h']]], - ['tjerr_165',['TJERR',['../group___turbo_j_p_e_g.html#gafbc17cfa57d0d5d11fea35ac025950fe',1,'turbojpeg.h']]], - ['tjpf_166',['TJPF',['../group___turbo_j_p_e_g.html#gac916144e26c3817ac514e64ae5d12e2a',1,'turbojpeg.h']]], - ['tjsamp_167',['TJSAMP',['../group___turbo_j_p_e_g.html#ga1d047060ea80bb9820d540bb928e9074',1,'turbojpeg.h']]], - ['tjxop_168',['TJXOP',['../group___turbo_j_p_e_g.html#ga2de531af4e7e6c4f124908376b354866',1,'turbojpeg.h']]] + ['tjcs_165',['TJCS',['../group___turbo_j_p_e_g.html#ga4f83ad3368e0e29d1957be0efa7c3720',1,'turbojpeg.h']]], + ['tjerr_166',['TJERR',['../group___turbo_j_p_e_g.html#gafbc17cfa57d0d5d11fea35ac025950fe',1,'turbojpeg.h']]], + ['tjpf_167',['TJPF',['../group___turbo_j_p_e_g.html#gac916144e26c3817ac514e64ae5d12e2a',1,'turbojpeg.h']]], + ['tjsamp_168',['TJSAMP',['../group___turbo_j_p_e_g.html#ga1d047060ea80bb9820d540bb928e9074',1,'turbojpeg.h']]], + ['tjxop_169',['TJXOP',['../group___turbo_j_p_e_g.html#ga2de531af4e7e6c4f124908376b354866',1,'turbojpeg.h']]] ]; diff --git a/doc/html/search/enumvalues_0.js b/doc/html/search/enumvalues_0.js index 3541fe47a..ff1d8a7ca 100644 --- a/doc/html/search/enumvalues_0.js +++ b/doc/html/search/enumvalues_0.js @@ -1,37 +1,37 @@ var searchData= [ - ['tjcs_5fcmyk_169',['TJCS_CMYK',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a6c8b636152ac8195b869587db315ee53',1,'turbojpeg.h']]], - ['tjcs_5fgray_170',['TJCS_GRAY',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720ab3e7d6a87f695e45b81c1b5262b5a50a',1,'turbojpeg.h']]], - ['tjcs_5frgb_171',['TJCS_RGB',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a677cb7ccb85c4038ac41964a2e09e555',1,'turbojpeg.h']]], - ['tjcs_5fycbcr_172',['TJCS_YCbCr',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a7389b8f65bb387ffedce3efd0d78ec75',1,'turbojpeg.h']]], - ['tjcs_5fycck_173',['TJCS_YCCK',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a53839e0fe867b76b58d16b0a1a7c598e',1,'turbojpeg.h']]], - ['tjerr_5ffatal_174',['TJERR_FATAL',['../group___turbo_j_p_e_g.html#ggafbc17cfa57d0d5d11fea35ac025950feafc9cceeada13122b09e4851e3788039a',1,'turbojpeg.h']]], - ['tjerr_5fwarning_175',['TJERR_WARNING',['../group___turbo_j_p_e_g.html#ggafbc17cfa57d0d5d11fea35ac025950fea342dd6e2aedb47bb257b4e7568329b59',1,'turbojpeg.h']]], - ['tjpf_5fabgr_176',['TJPF_ABGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa1ba1a7f1631dbeaa49a0a85fc4a40081',1,'turbojpeg.h']]], - ['tjpf_5fargb_177',['TJPF_ARGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aae8f846ed9d9de99b6e1dfe448848765c',1,'turbojpeg.h']]], - ['tjpf_5fbgr_178',['TJPF_BGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aab10624437fb8ef495a0b153e65749839',1,'turbojpeg.h']]], - ['tjpf_5fbgra_179',['TJPF_BGRA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aac037ff1845cf9b74bb81a3659c2b9fb4',1,'turbojpeg.h']]], - ['tjpf_5fbgrx_180',['TJPF_BGRX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa2a1fbf569ca79897eae886e3376ca4c8',1,'turbojpeg.h']]], - ['tjpf_5fcmyk_181',['TJPF_CMYK',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7f5100ec44c91994e243f1cf55553f8b',1,'turbojpeg.h']]], - ['tjpf_5fgray_182',['TJPF_GRAY',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa5431b54b015337705f13118073711a1a',1,'turbojpeg.h']]], - ['tjpf_5frgb_183',['TJPF_RGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7ce93230bff449518ce387c17e6ed37c',1,'turbojpeg.h']]], - ['tjpf_5frgba_184',['TJPF_RGBA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa88d2e88fab67f6503cf972e14851cc12',1,'turbojpeg.h']]], - ['tjpf_5frgbx_185',['TJPF_RGBX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa83973bebb7e2dc6fa8bae89ff3f42e01',1,'turbojpeg.h']]], - ['tjpf_5funknown_186',['TJPF_UNKNOWN',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa84c1a6cead7952998e2fb895844a21ed',1,'turbojpeg.h']]], - ['tjpf_5fxbgr_187',['TJPF_XBGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aaf6603b27147de47e212e75dac027b2af',1,'turbojpeg.h']]], - ['tjpf_5fxrgb_188',['TJPF_XRGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aadae996905efcfa3b42a0bb3bea7f9d84',1,'turbojpeg.h']]], - ['tjsamp_5f411_189',['TJSAMP_411',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a28ec62575e5ea295c3fde3001dc628e2',1,'turbojpeg.h']]], - ['tjsamp_5f420_190',['TJSAMP_420',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a63085dbf683cfe39e513cdb6343e3737',1,'turbojpeg.h']]], - ['tjsamp_5f422_191',['TJSAMP_422',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a136130902cc578f11f32429b59368404',1,'turbojpeg.h']]], - ['tjsamp_5f440_192',['TJSAMP_440',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074accf740e6f3aa6ba20ba922cad13cb974',1,'turbojpeg.h']]], - ['tjsamp_5f444_193',['TJSAMP_444',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074afb8da4f44197837bdec0a4f593dacae3',1,'turbojpeg.h']]], - ['tjsamp_5fgray_194',['TJSAMP_GRAY',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a3f1c9504842ddc7a48d0f690754b6248',1,'turbojpeg.h']]], - ['tjxop_5fhflip_195',['TJXOP_HFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aa0df69776caa30f0fa28e26332d311ce',1,'turbojpeg.h']]], - ['tjxop_5fnone_196',['TJXOP_NONE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aad88c0366cd3f7d0eac9d7a3fa1c2c27',1,'turbojpeg.h']]], - ['tjxop_5frot180_197',['TJXOP_ROT180',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a140952eb8dd0300accfcc22726d69692',1,'turbojpeg.h']]], - ['tjxop_5frot270_198',['TJXOP_ROT270',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a3064ee5dfb7f032df332818587567a08',1,'turbojpeg.h']]], - ['tjxop_5frot90_199',['TJXOP_ROT90',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a43b2bbb23bc4bd548422d43fbe9af128',1,'turbojpeg.h']]], - ['tjxop_5ftranspose_200',['TJXOP_TRANSPOSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a31060aed199f886afdd417f80499c32d',1,'turbojpeg.h']]], - ['tjxop_5ftransverse_201',['TJXOP_TRANSVERSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866af3b14d488aea6ece9e5b3df73a74d6a4',1,'turbojpeg.h']]], - ['tjxop_5fvflip_202',['TJXOP_VFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a324eddfbec53b7e691f61e56929d0d5d',1,'turbojpeg.h']]] + ['tjcs_5fcmyk_170',['TJCS_CMYK',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a6c8b636152ac8195b869587db315ee53',1,'turbojpeg.h']]], + ['tjcs_5fgray_171',['TJCS_GRAY',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720ab3e7d6a87f695e45b81c1b5262b5a50a',1,'turbojpeg.h']]], + ['tjcs_5frgb_172',['TJCS_RGB',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a677cb7ccb85c4038ac41964a2e09e555',1,'turbojpeg.h']]], + ['tjcs_5fycbcr_173',['TJCS_YCbCr',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a7389b8f65bb387ffedce3efd0d78ec75',1,'turbojpeg.h']]], + ['tjcs_5fycck_174',['TJCS_YCCK',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a53839e0fe867b76b58d16b0a1a7c598e',1,'turbojpeg.h']]], + ['tjerr_5ffatal_175',['TJERR_FATAL',['../group___turbo_j_p_e_g.html#ggafbc17cfa57d0d5d11fea35ac025950feafc9cceeada13122b09e4851e3788039a',1,'turbojpeg.h']]], + ['tjerr_5fwarning_176',['TJERR_WARNING',['../group___turbo_j_p_e_g.html#ggafbc17cfa57d0d5d11fea35ac025950fea342dd6e2aedb47bb257b4e7568329b59',1,'turbojpeg.h']]], + ['tjpf_5fabgr_177',['TJPF_ABGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa1ba1a7f1631dbeaa49a0a85fc4a40081',1,'turbojpeg.h']]], + ['tjpf_5fargb_178',['TJPF_ARGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aae8f846ed9d9de99b6e1dfe448848765c',1,'turbojpeg.h']]], + ['tjpf_5fbgr_179',['TJPF_BGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aab10624437fb8ef495a0b153e65749839',1,'turbojpeg.h']]], + ['tjpf_5fbgra_180',['TJPF_BGRA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aac037ff1845cf9b74bb81a3659c2b9fb4',1,'turbojpeg.h']]], + ['tjpf_5fbgrx_181',['TJPF_BGRX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa2a1fbf569ca79897eae886e3376ca4c8',1,'turbojpeg.h']]], + ['tjpf_5fcmyk_182',['TJPF_CMYK',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7f5100ec44c91994e243f1cf55553f8b',1,'turbojpeg.h']]], + ['tjpf_5fgray_183',['TJPF_GRAY',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa5431b54b015337705f13118073711a1a',1,'turbojpeg.h']]], + ['tjpf_5frgb_184',['TJPF_RGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7ce93230bff449518ce387c17e6ed37c',1,'turbojpeg.h']]], + ['tjpf_5frgba_185',['TJPF_RGBA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa88d2e88fab67f6503cf972e14851cc12',1,'turbojpeg.h']]], + ['tjpf_5frgbx_186',['TJPF_RGBX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa83973bebb7e2dc6fa8bae89ff3f42e01',1,'turbojpeg.h']]], + ['tjpf_5funknown_187',['TJPF_UNKNOWN',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa84c1a6cead7952998e2fb895844a21ed',1,'turbojpeg.h']]], + ['tjpf_5fxbgr_188',['TJPF_XBGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aaf6603b27147de47e212e75dac027b2af',1,'turbojpeg.h']]], + ['tjpf_5fxrgb_189',['TJPF_XRGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aadae996905efcfa3b42a0bb3bea7f9d84',1,'turbojpeg.h']]], + ['tjsamp_5f411_190',['TJSAMP_411',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a28ec62575e5ea295c3fde3001dc628e2',1,'turbojpeg.h']]], + ['tjsamp_5f420_191',['TJSAMP_420',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a63085dbf683cfe39e513cdb6343e3737',1,'turbojpeg.h']]], + ['tjsamp_5f422_192',['TJSAMP_422',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a136130902cc578f11f32429b59368404',1,'turbojpeg.h']]], + ['tjsamp_5f440_193',['TJSAMP_440',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074accf740e6f3aa6ba20ba922cad13cb974',1,'turbojpeg.h']]], + ['tjsamp_5f444_194',['TJSAMP_444',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074afb8da4f44197837bdec0a4f593dacae3',1,'turbojpeg.h']]], + ['tjsamp_5fgray_195',['TJSAMP_GRAY',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a3f1c9504842ddc7a48d0f690754b6248',1,'turbojpeg.h']]], + ['tjxop_5fhflip_196',['TJXOP_HFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aa0df69776caa30f0fa28e26332d311ce',1,'turbojpeg.h']]], + ['tjxop_5fnone_197',['TJXOP_NONE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aad88c0366cd3f7d0eac9d7a3fa1c2c27',1,'turbojpeg.h']]], + ['tjxop_5frot180_198',['TJXOP_ROT180',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a140952eb8dd0300accfcc22726d69692',1,'turbojpeg.h']]], + ['tjxop_5frot270_199',['TJXOP_ROT270',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a3064ee5dfb7f032df332818587567a08',1,'turbojpeg.h']]], + ['tjxop_5frot90_200',['TJXOP_ROT90',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a43b2bbb23bc4bd548422d43fbe9af128',1,'turbojpeg.h']]], + ['tjxop_5ftranspose_201',['TJXOP_TRANSPOSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a31060aed199f886afdd417f80499c32d',1,'turbojpeg.h']]], + ['tjxop_5ftransverse_202',['TJXOP_TRANSVERSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866af3b14d488aea6ece9e5b3df73a74d6a4',1,'turbojpeg.h']]], + ['tjxop_5fvflip_203',['TJXOP_VFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a324eddfbec53b7e691f61e56929d0d5d',1,'turbojpeg.h']]] ]; diff --git a/doc/html/search/functions_0.js b/doc/html/search/functions_0.js index 7e3cb3060..440716001 100644 --- a/doc/html/search/functions_0.js +++ b/doc/html/search/functions_0.js @@ -1,31 +1,31 @@ var searchData= [ - ['tjalloc_116',['tjAlloc',['../group___turbo_j_p_e_g.html#gaec627dd4c5f30b7a775a7aea3bec5d83',1,'turbojpeg.h']]], - ['tjbufsize_117',['tjBufSize',['../group___turbo_j_p_e_g.html#ga67ac12fee79073242cb216e07c9f1f90',1,'turbojpeg.h']]], - ['tjbufsizeyuv2_118',['tjBufSizeYUV2',['../group___turbo_j_p_e_g.html#ga2be2b9969d4df9ecce9b05deed273194',1,'turbojpeg.h']]], - ['tjcompress2_119',['tjCompress2',['../group___turbo_j_p_e_g.html#gafbdce0112fd78fd38efae841443a9bcf',1,'turbojpeg.h']]], - ['tjcompressfromyuv_120',['tjCompressFromYUV',['../group___turbo_j_p_e_g.html#ga7622a459b79aa1007e005b58783f875b',1,'turbojpeg.h']]], - ['tjcompressfromyuvplanes_121',['tjCompressFromYUVPlanes',['../group___turbo_j_p_e_g.html#ga29ec5dfbd2d84b8724e951d6fa0d5d9e',1,'turbojpeg.h']]], - ['tjdecodeyuv_122',['tjDecodeYUV',['../group___turbo_j_p_e_g.html#ga70abbf38f77a26fd6da8813bef96f695',1,'turbojpeg.h']]], - ['tjdecodeyuvplanes_123',['tjDecodeYUVPlanes',['../group___turbo_j_p_e_g.html#ga10e837c07fa9d25770565b237d3898d9',1,'turbojpeg.h']]], - ['tjdecompress2_124',['tjDecompress2',['../group___turbo_j_p_e_g.html#gae9eccef8b682a48f43a9117c231ed013',1,'turbojpeg.h']]], - ['tjdecompressheader3_125',['tjDecompressHeader3',['../group___turbo_j_p_e_g.html#ga0595681096bba7199cc6f3533cb25f77',1,'turbojpeg.h']]], - ['tjdecompresstoyuv2_126',['tjDecompressToYUV2',['../group___turbo_j_p_e_g.html#ga04d1e839ff9a0860dd1475cff78d3364',1,'turbojpeg.h']]], - ['tjdecompresstoyuvplanes_127',['tjDecompressToYUVPlanes',['../group___turbo_j_p_e_g.html#gaa59f901a5258ada5bd0185ad59368540',1,'turbojpeg.h']]], - ['tjdestroy_128',['tjDestroy',['../group___turbo_j_p_e_g.html#ga75f355fa27225ba1a4ee392c852394d2',1,'turbojpeg.h']]], - ['tjencodeyuv3_129',['tjEncodeYUV3',['../group___turbo_j_p_e_g.html#gac519b922cdf446e97d0cdcba513636bf',1,'turbojpeg.h']]], - ['tjencodeyuvplanes_130',['tjEncodeYUVPlanes',['../group___turbo_j_p_e_g.html#gae2d04c72457fe7f4d60cf78ab1b1feb1',1,'turbojpeg.h']]], - ['tjfree_131',['tjFree',['../group___turbo_j_p_e_g.html#gaea863d2da0cdb609563aabdf9196514b',1,'turbojpeg.h']]], - ['tjgeterrorcode_132',['tjGetErrorCode',['../group___turbo_j_p_e_g.html#ga414feeffbf860ebd31c745df203de410',1,'turbojpeg.h']]], - ['tjgeterrorstr2_133',['tjGetErrorStr2',['../group___turbo_j_p_e_g.html#ga1ead8574f9f39fbafc6b497124e7aafa',1,'turbojpeg.h']]], - ['tjgetscalingfactors_134',['tjGetScalingFactors',['../group___turbo_j_p_e_g.html#gac3854476006b10787bd128f7ede48057',1,'turbojpeg.h']]], - ['tjinitcompress_135',['tjInitCompress',['../group___turbo_j_p_e_g.html#ga9d63a05fc6d813f4aae06107041a37e8',1,'turbojpeg.h']]], - ['tjinitdecompress_136',['tjInitDecompress',['../group___turbo_j_p_e_g.html#ga52300eac3f3d9ef4bab303bc244f62d3',1,'turbojpeg.h']]], - ['tjinittransform_137',['tjInitTransform',['../group___turbo_j_p_e_g.html#ga928beff6ac248ceadf01089fc6b41957',1,'turbojpeg.h']]], - ['tjloadimage_138',['tjLoadImage',['../group___turbo_j_p_e_g.html#gaffbd83c375e79f5db4b5c5d8ad4466e7',1,'turbojpeg.h']]], - ['tjplaneheight_139',['tjPlaneHeight',['../group___turbo_j_p_e_g.html#ga1a209696c6a80748f20e134b3c64789f',1,'turbojpeg.h']]], - ['tjplanesizeyuv_140',['tjPlaneSizeYUV',['../group___turbo_j_p_e_g.html#gab4ab7b24f6e797d79abaaa670373961d',1,'turbojpeg.h']]], - ['tjplanewidth_141',['tjPlaneWidth',['../group___turbo_j_p_e_g.html#ga63fb66bb1e36c74008c4634360becbb1',1,'turbojpeg.h']]], - ['tjsaveimage_142',['tjSaveImage',['../group___turbo_j_p_e_g.html#ga6f445b22d8933ae4815b3370a538d879',1,'turbojpeg.h']]], - ['tjtransform_143',['tjTransform',['../group___turbo_j_p_e_g.html#ga9cb8abf4cc91881e04a0329b2270be25',1,'turbojpeg.h']]] + ['tjalloc_117',['tjAlloc',['../group___turbo_j_p_e_g.html#gaec627dd4c5f30b7a775a7aea3bec5d83',1,'turbojpeg.h']]], + ['tjbufsize_118',['tjBufSize',['../group___turbo_j_p_e_g.html#ga67ac12fee79073242cb216e07c9f1f90',1,'turbojpeg.h']]], + ['tjbufsizeyuv2_119',['tjBufSizeYUV2',['../group___turbo_j_p_e_g.html#ga2be2b9969d4df9ecce9b05deed273194',1,'turbojpeg.h']]], + ['tjcompress2_120',['tjCompress2',['../group___turbo_j_p_e_g.html#gafbdce0112fd78fd38efae841443a9bcf',1,'turbojpeg.h']]], + ['tjcompressfromyuv_121',['tjCompressFromYUV',['../group___turbo_j_p_e_g.html#ga7622a459b79aa1007e005b58783f875b',1,'turbojpeg.h']]], + ['tjcompressfromyuvplanes_122',['tjCompressFromYUVPlanes',['../group___turbo_j_p_e_g.html#ga29ec5dfbd2d84b8724e951d6fa0d5d9e',1,'turbojpeg.h']]], + ['tjdecodeyuv_123',['tjDecodeYUV',['../group___turbo_j_p_e_g.html#ga70abbf38f77a26fd6da8813bef96f695',1,'turbojpeg.h']]], + ['tjdecodeyuvplanes_124',['tjDecodeYUVPlanes',['../group___turbo_j_p_e_g.html#ga10e837c07fa9d25770565b237d3898d9',1,'turbojpeg.h']]], + ['tjdecompress2_125',['tjDecompress2',['../group___turbo_j_p_e_g.html#gae9eccef8b682a48f43a9117c231ed013',1,'turbojpeg.h']]], + ['tjdecompressheader3_126',['tjDecompressHeader3',['../group___turbo_j_p_e_g.html#ga0595681096bba7199cc6f3533cb25f77',1,'turbojpeg.h']]], + ['tjdecompresstoyuv2_127',['tjDecompressToYUV2',['../group___turbo_j_p_e_g.html#ga04d1e839ff9a0860dd1475cff78d3364',1,'turbojpeg.h']]], + ['tjdecompresstoyuvplanes_128',['tjDecompressToYUVPlanes',['../group___turbo_j_p_e_g.html#gaa59f901a5258ada5bd0185ad59368540',1,'turbojpeg.h']]], + ['tjdestroy_129',['tjDestroy',['../group___turbo_j_p_e_g.html#ga75f355fa27225ba1a4ee392c852394d2',1,'turbojpeg.h']]], + ['tjencodeyuv3_130',['tjEncodeYUV3',['../group___turbo_j_p_e_g.html#gac519b922cdf446e97d0cdcba513636bf',1,'turbojpeg.h']]], + ['tjencodeyuvplanes_131',['tjEncodeYUVPlanes',['../group___turbo_j_p_e_g.html#gae2d04c72457fe7f4d60cf78ab1b1feb1',1,'turbojpeg.h']]], + ['tjfree_132',['tjFree',['../group___turbo_j_p_e_g.html#gaea863d2da0cdb609563aabdf9196514b',1,'turbojpeg.h']]], + ['tjgeterrorcode_133',['tjGetErrorCode',['../group___turbo_j_p_e_g.html#ga414feeffbf860ebd31c745df203de410',1,'turbojpeg.h']]], + ['tjgeterrorstr2_134',['tjGetErrorStr2',['../group___turbo_j_p_e_g.html#ga1ead8574f9f39fbafc6b497124e7aafa',1,'turbojpeg.h']]], + ['tjgetscalingfactors_135',['tjGetScalingFactors',['../group___turbo_j_p_e_g.html#gac3854476006b10787bd128f7ede48057',1,'turbojpeg.h']]], + ['tjinitcompress_136',['tjInitCompress',['../group___turbo_j_p_e_g.html#ga9d63a05fc6d813f4aae06107041a37e8',1,'turbojpeg.h']]], + ['tjinitdecompress_137',['tjInitDecompress',['../group___turbo_j_p_e_g.html#ga52300eac3f3d9ef4bab303bc244f62d3',1,'turbojpeg.h']]], + ['tjinittransform_138',['tjInitTransform',['../group___turbo_j_p_e_g.html#ga928beff6ac248ceadf01089fc6b41957',1,'turbojpeg.h']]], + ['tjloadimage_139',['tjLoadImage',['../group___turbo_j_p_e_g.html#gaffbd83c375e79f5db4b5c5d8ad4466e7',1,'turbojpeg.h']]], + ['tjplaneheight_140',['tjPlaneHeight',['../group___turbo_j_p_e_g.html#ga1a209696c6a80748f20e134b3c64789f',1,'turbojpeg.h']]], + ['tjplanesizeyuv_141',['tjPlaneSizeYUV',['../group___turbo_j_p_e_g.html#gab4ab7b24f6e797d79abaaa670373961d',1,'turbojpeg.h']]], + ['tjplanewidth_142',['tjPlaneWidth',['../group___turbo_j_p_e_g.html#ga63fb66bb1e36c74008c4634360becbb1',1,'turbojpeg.h']]], + ['tjsaveimage_143',['tjSaveImage',['../group___turbo_j_p_e_g.html#ga6f445b22d8933ae4815b3370a538d879',1,'turbojpeg.h']]], + ['tjtransform_144',['tjTransform',['../group___turbo_j_p_e_g.html#ga9cb8abf4cc91881e04a0329b2270be25',1,'turbojpeg.h']]] ]; diff --git a/doc/html/search/groups_0.js b/doc/html/search/groups_0.js index b63e3fea3..dc5404cec 100644 --- a/doc/html/search/groups_0.js +++ b/doc/html/search/groups_0.js @@ -1,4 +1,4 @@ var searchData= [ - ['turbojpeg_203',['TurboJPEG',['../group___turbo_j_p_e_g.html',1,'']]] + ['turbojpeg_204',['TurboJPEG',['../group___turbo_j_p_e_g.html',1,'']]] ]; diff --git a/doc/html/search/typedefs_0.js b/doc/html/search/typedefs_0.js index 9f2ef8e75..6b01cef3f 100644 --- a/doc/html/search/typedefs_0.js +++ b/doc/html/search/typedefs_0.js @@ -1,5 +1,5 @@ var searchData= [ - ['tjhandle_162',['tjhandle',['../group___turbo_j_p_e_g.html#ga758d2634ecb4949de7815cba621f5763',1,'turbojpeg.h']]], - ['tjtransform_163',['tjtransform',['../group___turbo_j_p_e_g.html#ga504805ec0161f1b505397ca0118bf8fd',1,'turbojpeg.h']]] + ['tjhandle_163',['tjhandle',['../group___turbo_j_p_e_g.html#ga758d2634ecb4949de7815cba621f5763',1,'turbojpeg.h']]], + ['tjtransform_164',['tjtransform',['../group___turbo_j_p_e_g.html#ga504805ec0161f1b505397ca0118bf8fd',1,'turbojpeg.h']]] ]; diff --git a/doc/html/search/variables_0.js b/doc/html/search/variables_0.js index 016ed4b19..eb7bc5af6 100644 --- a/doc/html/search/variables_0.js +++ b/doc/html/search/variables_0.js @@ -1,4 +1,4 @@ var searchData= [ - ['customfilter_144',['customFilter',['../structtjtransform.html#afd7fc262df33f741e120ef4183202ef5',1,'tjtransform']]] + ['customfilter_145',['customFilter',['../structtjtransform.html#afd7fc262df33f741e120ef4183202ef5',1,'tjtransform']]] ]; diff --git a/doc/html/search/variables_1.js b/doc/html/search/variables_1.js index 83b345bc7..cd6ee7e37 100644 --- a/doc/html/search/variables_1.js +++ b/doc/html/search/variables_1.js @@ -1,5 +1,5 @@ var searchData= [ - ['data_145',['data',['../structtjtransform.html#a688fe8f1a8ecc12a538d9e561cf338e3',1,'tjtransform']]], - ['denom_146',['denom',['../structtjscalingfactor.html#aefbcdf3e9e62274b2d312c695f133ce3',1,'tjscalingfactor']]] + ['data_146',['data',['../structtjtransform.html#a688fe8f1a8ecc12a538d9e561cf338e3',1,'tjtransform']]], + ['denom_147',['denom',['../structtjscalingfactor.html#aefbcdf3e9e62274b2d312c695f133ce3',1,'tjscalingfactor']]] ]; diff --git a/doc/html/search/variables_2.js b/doc/html/search/variables_2.js index 3f6d4f899..5be414e83 100644 --- a/doc/html/search/variables_2.js +++ b/doc/html/search/variables_2.js @@ -1,4 +1,4 @@ var searchData= [ - ['h_147',['h',['../structtjregion.html#aecefc45a26f4d8b60dd4d825c1710115',1,'tjregion']]] + ['h_148',['h',['../structtjregion.html#aecefc45a26f4d8b60dd4d825c1710115',1,'tjregion']]] ]; diff --git a/doc/html/search/variables_3.js b/doc/html/search/variables_3.js index eea223718..586307e74 100644 --- a/doc/html/search/variables_3.js +++ b/doc/html/search/variables_3.js @@ -1,4 +1,4 @@ var searchData= [ - ['num_148',['num',['../structtjscalingfactor.html#a9b011e57f981ee23083e2c1aa5e640ec',1,'tjscalingfactor']]] + ['num_149',['num',['../structtjscalingfactor.html#a9b011e57f981ee23083e2c1aa5e640ec',1,'tjscalingfactor']]] ]; diff --git a/doc/html/search/variables_4.js b/doc/html/search/variables_4.js index a7f364b9c..7023c9ca5 100644 --- a/doc/html/search/variables_4.js +++ b/doc/html/search/variables_4.js @@ -1,5 +1,5 @@ var searchData= [ - ['op_149',['op',['../structtjtransform.html#a2525aab4ba6978a1c273f74fef50e498',1,'tjtransform']]], - ['options_150',['options',['../structtjtransform.html#ac0e74655baa4402209a21e1ae481c8f6',1,'tjtransform']]] + ['op_150',['op',['../structtjtransform.html#a2525aab4ba6978a1c273f74fef50e498',1,'tjtransform']]], + ['options_151',['options',['../structtjtransform.html#ac0e74655baa4402209a21e1ae481c8f6',1,'tjtransform']]] ]; diff --git a/doc/html/search/variables_5.js b/doc/html/search/variables_5.js index cbb4fa9a8..ca1e0cd26 100644 --- a/doc/html/search/variables_5.js +++ b/doc/html/search/variables_5.js @@ -1,4 +1,4 @@ var searchData= [ - ['r_151',['r',['../structtjtransform.html#ac324e5e442abec8a961e5bf219db12cf',1,'tjtransform']]] + ['r_152',['r',['../structtjtransform.html#ac324e5e442abec8a961e5bf219db12cf',1,'tjtransform']]] ]; diff --git a/doc/html/search/variables_6.js b/doc/html/search/variables_6.js index 0621d7c86..f7a582c41 100644 --- a/doc/html/search/variables_6.js +++ b/doc/html/search/variables_6.js @@ -1,10 +1,10 @@ var searchData= [ - ['tjalphaoffset_152',['tjAlphaOffset',['../group___turbo_j_p_e_g.html#ga5af0ab065feefd526debf1e20c43e837',1,'turbojpeg.h']]], - ['tjblueoffset_153',['tjBlueOffset',['../group___turbo_j_p_e_g.html#ga84e2e35d3f08025f976ec1ec53693dea',1,'turbojpeg.h']]], - ['tjgreenoffset_154',['tjGreenOffset',['../group___turbo_j_p_e_g.html#ga82d6e35da441112a411da41923c0ba2f',1,'turbojpeg.h']]], - ['tjmcuheight_155',['tjMCUHeight',['../group___turbo_j_p_e_g.html#gabd247bb9fecb393eca57366feb8327bf',1,'turbojpeg.h']]], - ['tjmcuwidth_156',['tjMCUWidth',['../group___turbo_j_p_e_g.html#ga9e61e7cd47a15a173283ba94e781308c',1,'turbojpeg.h']]], - ['tjpixelsize_157',['tjPixelSize',['../group___turbo_j_p_e_g.html#gad77cf8fe5b2bfd3cb3f53098146abb4c',1,'turbojpeg.h']]], - ['tjredoffset_158',['tjRedOffset',['../group___turbo_j_p_e_g.html#gadd9b446742ac8a3923f7992c7988fea8',1,'turbojpeg.h']]] + ['tjalphaoffset_153',['tjAlphaOffset',['../group___turbo_j_p_e_g.html#ga5af0ab065feefd526debf1e20c43e837',1,'turbojpeg.h']]], + ['tjblueoffset_154',['tjBlueOffset',['../group___turbo_j_p_e_g.html#ga84e2e35d3f08025f976ec1ec53693dea',1,'turbojpeg.h']]], + ['tjgreenoffset_155',['tjGreenOffset',['../group___turbo_j_p_e_g.html#ga82d6e35da441112a411da41923c0ba2f',1,'turbojpeg.h']]], + ['tjmcuheight_156',['tjMCUHeight',['../group___turbo_j_p_e_g.html#gabd247bb9fecb393eca57366feb8327bf',1,'turbojpeg.h']]], + ['tjmcuwidth_157',['tjMCUWidth',['../group___turbo_j_p_e_g.html#ga9e61e7cd47a15a173283ba94e781308c',1,'turbojpeg.h']]], + ['tjpixelsize_158',['tjPixelSize',['../group___turbo_j_p_e_g.html#gad77cf8fe5b2bfd3cb3f53098146abb4c',1,'turbojpeg.h']]], + ['tjredoffset_159',['tjRedOffset',['../group___turbo_j_p_e_g.html#gadd9b446742ac8a3923f7992c7988fea8',1,'turbojpeg.h']]] ]; diff --git a/doc/html/search/variables_7.js b/doc/html/search/variables_7.js index 0ea01e125..fe5bba704 100644 --- a/doc/html/search/variables_7.js +++ b/doc/html/search/variables_7.js @@ -1,4 +1,4 @@ var searchData= [ - ['w_159',['w',['../structtjregion.html#ab6eb73ceef584fc23c8c8097926dce42',1,'tjregion']]] + ['w_160',['w',['../structtjregion.html#ab6eb73ceef584fc23c8c8097926dce42',1,'tjregion']]] ]; diff --git a/doc/html/search/variables_8.js b/doc/html/search/variables_8.js index a846f4ea1..5e1016018 100644 --- a/doc/html/search/variables_8.js +++ b/doc/html/search/variables_8.js @@ -1,4 +1,4 @@ var searchData= [ - ['x_160',['x',['../structtjregion.html#a4b6a37a93997091b26a75831fa291ad9',1,'tjregion']]] + ['x_161',['x',['../structtjregion.html#a4b6a37a93997091b26a75831fa291ad9',1,'tjregion']]] ]; diff --git a/doc/html/search/variables_9.js b/doc/html/search/variables_9.js index a03e0e9b2..1010f64aa 100644 --- a/doc/html/search/variables_9.js +++ b/doc/html/search/variables_9.js @@ -1,4 +1,4 @@ var searchData= [ - ['y_161',['y',['../structtjregion.html#a7b3e0c24cfe87acc80e334cafdcf22c2',1,'tjregion']]] + ['y_162',['y',['../structtjregion.html#a7b3e0c24cfe87acc80e334cafdcf22c2',1,'tjregion']]] ]; diff --git a/java/TJBench.java b/java/TJBench.java index 151fc56e4..4084ea545 100644 --- a/java/TJBench.java +++ b/java/TJBench.java @@ -716,6 +716,11 @@ static void usage() throws Exception { System.out.println("-arithmetic = Use arithmetic entropy coding in JPEG images generated by"); System.out.println(" compression and transform operations. (Can be combined with"); System.out.println(" -progressive.)"); + System.out.println("-lossless = Generate lossless JPEG images (implies -subsamp 444). When"); + System.out.println(" generating lossless JPEG images, Quality is psv * 10 + Pt, where psv is"); + System.out.println(" the predictor selection value (1-7) and Pt is the point transform (0-7)."); + System.out.println(" A point transform value of 0 is necessary in order to create a fully"); + System.out.println(" lossless JPEG image."); System.out.println("-subsamp = When testing JPEG compression, this option specifies the level"); System.out.println(" of chrominance subsampling to use ( = 444, 422, 440, 420, 411, or"); System.out.println(" GRAY). The default is to test Grayscale, 4:2:0, 4:2:2, and 4:4:4 in"); @@ -822,6 +827,10 @@ public static void main(String[] argv) { } else if (argv[i].equalsIgnoreCase("-arithmetic")) { System.out.println("Using arithmetic entropy coding\n"); flags |= TJ.FLAG_ARITHMETIC; + } else if (argv[i].equalsIgnoreCase("-lossless")) { + System.out.println("Using lossless JPEG\n\n"); + flags |= TJ.FLAG_LOSSLESS; + subsamp = TJ.SAMP_444; } else if (argv[i].equalsIgnoreCase("-rgb")) pf = TJ.PF_RGB; else if (argv[i].equalsIgnoreCase("-rgbx")) diff --git a/java/TJUnitTest.java b/java/TJUnitTest.java index 91ad5fd95..b24a29a29 100644 --- a/java/TJUnitTest.java +++ b/java/TJUnitTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C)2011-2018 D. R. Commander. All Rights Reserved. + * Copyright (C)2011-2018, 2022 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -51,6 +51,7 @@ static void usage() { System.out.println("-yuv = test YUV encoding/decoding support"); System.out.println("-noyuvpad = do not pad each line of each Y, U, and V plane to the nearest"); System.out.println(" 4-byte boundary"); + System.out.println("-lossless = test lossless JPEG compression/decompression"); System.out.println("-bi = test BufferedImage support\n"); System.exit(1); } @@ -92,6 +93,8 @@ static void usage() { }; private static boolean doYUV = false; + private static boolean lossless = false; + private static int psv = 1; private static int pad = 4; private static boolean bi = false; @@ -269,7 +272,7 @@ static void initImg(BufferedImage img, int pf, int flags) throws Exception { static void checkVal(int row, int col, int v, String vname, int cv) throws Exception { v = (v < 0) ? v + 256 : v; - if (v < cv - 1 || v > cv + 1) { + if (v < cv - (lossless ? 0 : 1) || v > cv + (lossless ? 0 : 1)) { throw new Exception("Comp. " + vname + " at " + row + "," + col + " should be " + cv + ", not " + v); } @@ -278,7 +281,7 @@ static void checkVal(int row, int col, int v, String vname, int cv) static void checkVal0(int row, int col, int v, String vname) throws Exception { v = (v < 0) ? v + 256 : v; - if (v > 1) { + if (v > (lossless ? 0 : 1)) { throw new Exception("Comp. " + vname + " at " + row + "," + col + " should be 0, not " + v); } @@ -287,7 +290,7 @@ static void checkVal0(int row, int col, int v, String vname) static void checkVal255(int row, int col, int v, String vname) throws Exception { v = (v < 0) ? v + 256 : v; - if (v < 254) { + if (v < 255 - (lossless ? 0 : 1)) { throw new Exception("Comp. " + vname + " at " + row + "," + col + " should be 255, not " + v); } @@ -717,6 +720,8 @@ static void decompTest(TJDecompressor tjd, byte[] jpegBuf, int jpegSize, } tjd.setSourceImage(jpegBuf, jpegSize); + if (lossless && subsamp != TJ.SAMP_444 && subsamp != TJ.SAMP_GRAY) + subsamp = TJ.SAMP_444; if (tjd.getWidth() != w || tjd.getHeight() != h || tjd.getSubsamp() != subsamp) throw new Exception("Incorrect JPEG header"); @@ -780,6 +785,14 @@ static void decompTest(TJDecompressor tjd, byte[] jpegBuf, int jpegSize, int w, int h, int pf, String baseName, int subsamp, int flags) throws Exception { int i; + TJScalingFactor sf1 = new TJScalingFactor(1, 1); + + if (lossless) { + decompTest(tjd, jpegBuf, jpegSize, w, h, pf, baseName, subsamp, flags, + sf1); + return; + } + TJScalingFactor[] sf = TJ.getScalingFactors(); for (i = 0; i < sf.length; i++) { int num = sf[i].getNum(); @@ -798,7 +811,7 @@ static void doTest(int w, int h, int[] formats, int subsamp, String baseName) throws Exception { TJCompressor tjc = null; TJDecompressor tjd = null; - int size; + int size, quality = 100; byte[] dstBuf; dstBuf = new byte[TJ.bufSize(w, h, subsamp)]; @@ -811,12 +824,16 @@ static void doTest(int w, int h, int[] formats, int subsamp, String baseName) if (pf < 0) continue; for (int i = 0; i < 2; i++) { int flags = 0; + if (lossless) { + flags |= TJ.FLAG_LOSSLESS; + quality = (((psv++ - 1) % 7) + 1) * 10; + } if (subsamp == TJ.SAMP_422 || subsamp == TJ.SAMP_420 || subsamp == TJ.SAMP_440 || subsamp == TJ.SAMP_411) flags |= TJ.FLAG_FASTUPSAMPLE; if (i == 1) flags |= TJ.FLAG_BOTTOMUP; - size = compTest(tjc, dstBuf, w, h, pf, baseName, subsamp, 100, + size = compTest(tjc, dstBuf, w, h, pf, baseName, subsamp, quality, flags); decompTest(tjd, dstBuf, size, w, h, pf, baseName, subsamp, flags); if (pf >= TJ.PF_RGBX && pf <= TJ.PF_XRGB && !bi) { @@ -838,16 +855,22 @@ static void doTest(int w, int h, int[] formats, int subsamp, String baseName) } static void bufSizeTest() throws Exception { - int w, h, i, subsamp; + int w, h, i, subsamp, flags = 0, quality = 100, numSamp = TJ.NUMSAMP; byte[] srcBuf, dstBuf = null; YUVImage dstImage = null; TJCompressor tjc = null; Random r = new Random(); try { + if (lossless) { + flags |= TJ.FLAG_LOSSLESS; + quality = (((psv++ - 1) % 7) + 1) * 10; + numSamp = 1; + } + tjc = new TJCompressor(); System.out.println("Buffer size regression test"); - for (subsamp = 0; subsamp < TJ.NUMSAMP; subsamp++) { + for (subsamp = 0; subsamp < numSamp; subsamp++) { for (w = 1; w < 48; w++) { int maxh = (w == 1) ? 2048 : 48; for (h = 1; h < maxh; h++) { @@ -863,11 +886,11 @@ static void bufSizeTest() throws Exception { } tjc.setSourceImage(srcBuf, 0, 0, w, 0, h, TJ.PF_BGRX); tjc.setSubsamp(subsamp); - tjc.setJPEGQuality(100); + tjc.setJPEGQuality(quality); if (doYUV) tjc.encodeYUV(dstImage, 0); else - tjc.compress(dstBuf, 0); + tjc.compress(dstBuf, flags); srcBuf = new byte[h * w * 4]; if (doYUV) @@ -881,7 +904,7 @@ static void bufSizeTest() throws Exception { if (doYUV) tjc.encodeYUV(dstImage, 0); else - tjc.compress(dstBuf, 0); + tjc.compress(dstBuf, flags); } dstImage = null; dstBuf = null; @@ -904,12 +927,16 @@ public static void main(String[] argv) { doYUV = true; else if (argv[i].equalsIgnoreCase("-noyuvpad")) pad = 1; + else if (argv[i].equalsIgnoreCase("-lossless")) + lossless = true; else if (argv[i].equalsIgnoreCase("-bi")) { bi = true; testName = "javabitest"; } else usage(); } + if (lossless && doYUV) + throw new Exception("Lossless JPEG and YUV encoding/decoding are incompatible."); if (doYUV) FORMATS_4BYTE[4] = -1; doTest(35, 39, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_444, @@ -918,27 +945,31 @@ else if (argv[i].equalsIgnoreCase("-bi")) { testName); doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_422, testName); - doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_422, - testName); - doTest(39, 41, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_420, - testName); - doTest(41, 35, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_420, - testName); - doTest(35, 39, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_440, - testName); - doTest(39, 41, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_440, - testName); - doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_411, - testName); - doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_411, - testName); + if (!lossless) { + doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_422, + testName); + doTest(39, 41, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_420, + testName); + doTest(41, 35, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_420, + testName); + doTest(35, 39, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_440, + testName); + doTest(39, 41, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_440, + testName); + doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_411, + testName); + doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_411, + testName); + } doTest(39, 41, bi ? FORMATS_GRAYBI : FORMATS_GRAY, TJ.SAMP_GRAY, testName); - doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_GRAY, - testName); - FORMATS_4BYTE[4] = -1; - doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_GRAY, - testName); + if (!lossless) { + doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_GRAY, + testName); + FORMATS_4BYTE[4] = -1; + doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_GRAY, + testName); + } if (!bi) bufSizeTest(); if (doYUV && !bi) { diff --git a/java/doc/constant-values.html b/java/doc/constant-values.html index f159ee3e2..b4ddce952 100644 --- a/java/doc/constant-values.html +++ b/java/doc/constant-values.html @@ -175,167 +175,174 @@

org.libjpegturbo.*

32768 + + +public static final int +FLAG_LOSSLESS +131072 + + public static final int FLAG_PROGRESSIVE 16384 - + public static final int FLAG_STOPONWARNING 8192 - + public static final int NUMCS 5 - + public static final int NUMERR 2 - + public static final int NUMPF 12 - + public static final int NUMSAMP 6 - + public static final int PF_ABGR 9 - + public static final int PF_ARGB 10 - + public static final int PF_BGR 1 - + public static final int PF_BGRA 8 - + public static final int PF_BGRX 3 - + public static final int PF_CMYK 11 - + public static final int PF_GRAY 6 - + public static final int PF_RGB 0 - + public static final int PF_RGBA 7 - + public static final int PF_RGBX 2 - + public static final int PF_XBGR 4 - + public static final int PF_XRGB 5 - + public static final int SAMP_411 5 - + public static final int SAMP_420 2 - + public static final int SAMP_422 1 - + public static final int SAMP_440 4 - + public static final int SAMP_444 0 - + public static final int diff --git a/java/doc/index-all.html b/java/doc/index-all.html index c91bec178..eddddc92b 100644 --- a/java/doc/index-all.html +++ b/java/doc/index-all.html @@ -262,6 +262,10 @@

F

Limit the number of progressive JPEG scans that the decompression and transform operations will process.
+
FLAG_LOSSLESS - Static variable in class org.libjpegturbo.turbojpeg.TJ
+
+
Generate a lossless JPEG image when compressing.
+
FLAG_PROGRESSIVE - Static variable in class org.libjpegturbo.turbojpeg.TJ
Use progressive entropy coding in JPEG images generated by compression and diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJ.html b/java/doc/org/libjpegturbo/turbojpeg/TJ.html index 34a072400..89e103ab0 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJ.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJ.html @@ -204,145 +204,151 @@

Field Summary

static int +FLAG_LOSSLESS +
Generate a lossless JPEG image when compressing.
+ + + +static int FLAG_PROGRESSIVE
Use progressive entropy coding in JPEG images generated by compression and transform operations.
- + static int FLAG_STOPONWARNING
Immediately discontinue the current compression/decompression/transform operation if the underlying codec throws a warning (non-fatal error).
- + static int NUMCS
The number of JPEG colorspaces
- + static int NUMERR
The number of error codes
- + static int NUMPF
The number of pixel formats
- + static int NUMSAMP
The number of chrominance subsampling options
- + static int PF_ABGR
ABGR pixel format.
- + static int PF_ARGB
ARGB pixel format.
- + static int PF_BGR
BGR pixel format.
- + static int PF_BGRA
BGRA pixel format.
- + static int PF_BGRX
BGRX pixel format.
- + static int PF_CMYK
CMYK pixel format.
- + static int PF_GRAY
Grayscale pixel format.
- + static int PF_RGB
RGB pixel format.
- + static int PF_RGBA
RGBA pixel format.
- + static int PF_RGBX
RGBX pixel format.
- + static int PF_XBGR
XBGR pixel format.
- + static int PF_XRGB
XRGB pixel format.
- + static int SAMP_411
4:1:1 chrominance subsampling.
- + static int SAMP_420
4:2:0 chrominance subsampling.
- + static int SAMP_422
4:2:2 chrominance subsampling.
- + static int SAMP_440
4:4:0 chrominance subsampling.
- + static int SAMP_444
4:4:4 chrominance subsampling (no chrominance subsampling).
- + static int SAMP_GRAY
Grayscale.
@@ -978,6 +984,31 @@

FLAG_ARITHMETIC

See Also:
Constant Field Values
+ + + +
    +
  • +

    FLAG_LOSSLESS

    +
    public static final int FLAG_LOSSLESS
    +
    Generate a lossless JPEG image when compressing. In most cases, + compressing and decompressing lossless JPEG images is considerably slower + than compressing and decompressing lossy JPEG images. Also note that the + following features are not available with lossless JPEG images: +
      +
    • Colorspace conversion +
    • Chrominance subsampling +
    • JPEG quality selection +
    • DCT/IDCT algorithm selection +
    • Progressive entropy coding +
    • Arithmetic entropy coding +
    • Compression from/decompression to YUV planar images +
    • Decompression scaling +
    • Lossless transformations +
    +
    See Also:
    Constant Field Values
    +
  • +
diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html b/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html index e8d734271..b86891d84 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html @@ -471,7 +471,14 @@

setJPEGQuality

public void setJPEGQuality(int quality)
Set the JPEG image quality level for subsequent compress operations.
Parameters:
quality - the new JPEG image quality level (1 to 100, 1 = worst, - 100 = best)
+ 100 = best.) When generating a lossless JPEG image (see + TJ.FLAG_LOSSLESS), quality is + psv * 10 + Pt, where psv is the predictor + selection value (1-7) and Pt is the point transform (0-7). A + point transform value of 0 is necessary in order to create a fully + lossless JPEG image. (A non-zero point transform value right-shifts the + input samples by the specified number of bits, which is effectively a form + of lossy color quantization.)
diff --git a/java/org/libjpegturbo/turbojpeg/TJ.java b/java/org/libjpegturbo/turbojpeg/TJ.java index 92d28566d..98e01f149 100644 --- a/java/org/libjpegturbo/turbojpeg/TJ.java +++ b/java/org/libjpegturbo/turbojpeg/TJ.java @@ -377,7 +377,7 @@ public static int getAlphaOffset(int pixelFormat) { * The uncompressed source/destination image is stored in bottom-up (Windows, * OpenGL) order, not top-down (X11) order. */ - public static final int FLAG_BOTTOMUP = 2; + public static final int FLAG_BOTTOMUP = (1 << 1); /** * When decompressing an image that was compressed using chrominance @@ -386,7 +386,7 @@ public static int getAlphaOffset(int pixelFormat) { * creates a smooth transition between neighboring chrominance components in * order to reduce upsampling artifacts in the decompressed image. */ - public static final int FLAG_FASTUPSAMPLE = 256; + public static final int FLAG_FASTUPSAMPLE = (1 << 8); /** * Use the fastest DCT/IDCT algorithm available in the underlying codec. The * default if this flag is not specified is implementation-specific. For @@ -395,7 +395,7 @@ public static int getAlphaOffset(int pixelFormat) { * only a very slight effect on accuracy, but it uses the accurate algorithm * when decompressing, because this has been shown to have a larger effect. */ - public static final int FLAG_FASTDCT = 2048; + public static final int FLAG_FASTDCT = (1 << 11); /** * Use the most accurate DCT/IDCT algorithm available in the underlying * codec. The default if this flag is not specified is @@ -405,7 +405,7 @@ public static int getAlphaOffset(int pixelFormat) { * but it uses the accurate algorithm when decompressing, because this has * been shown to have a larger effect. */ - public static final int FLAG_ACCURATEDCT = 4096; + public static final int FLAG_ACCURATEDCT = (1 << 12); /** * Immediately discontinue the current compression/decompression/transform * operation if the underlying codec throws a warning (non-fatal error). The @@ -417,7 +417,7 @@ public static int getAlphaOffset(int pixelFormat) { * with a void return type) will complete and leave the output image in a * fully recoverable state after a non-fatal error occurs. */ - public static final int FLAG_STOPONWARNING = 8192; + public static final int FLAG_STOPONWARNING = (1 << 13); /** * Use progressive entropy coding in JPEG images generated by compression and * transform operations. Progressive entropy coding will generally improve @@ -425,7 +425,7 @@ public static int getAlphaOffset(int pixelFormat) { * reduce compression and decompression performance considerably. Can be * combined with {@link #FLAG_ARITHMETIC}. */ - public static final int FLAG_PROGRESSIVE = 16384; + public static final int FLAG_PROGRESSIVE = (1 << 14); /** * Limit the number of progressive JPEG scans that the decompression and * transform operations will process. If a progressive JPEG image contains @@ -435,7 +435,7 @@ public static int getAlphaOffset(int pixelFormat) { * against an exploit of the progressive JPEG format described in * this report. */ - public static final int FLAG_LIMITSCANS = 32768; + public static final int FLAG_LIMITSCANS = (1 << 15); /** * Use arithmetic entropy coding in JPEG images generated by compression and * transform operations. Arithmetic entropy coding will generally improve @@ -443,7 +443,25 @@ public static int getAlphaOffset(int pixelFormat) { * reduce compression and decompression performance considerably. Can be * combined with {@link #FLAG_PROGRESSIVE}. */ - public static final int FLAG_ARITHMETIC = 65536; + public static final int FLAG_ARITHMETIC = (1 << 16); + /** + * Generate a lossless JPEG image when compressing. In most cases, + * compressing and decompressing lossless JPEG images is considerably slower + * than compressing and decompressing lossy JPEG images. Also note that the + * following features are not available with lossless JPEG images: + *
    + *
  • Colorspace conversion + *
  • Chrominance subsampling + *
  • JPEG quality selection + *
  • DCT/IDCT algorithm selection + *
  • Progressive entropy coding + *
  • Arithmetic entropy coding + *
  • Compression from/decompression to YUV planar images + *
  • Decompression scaling + *
  • Lossless transformations + *
+ */ + public static final int FLAG_LOSSLESS = (1 << 17); /** * The number of error codes diff --git a/java/org/libjpegturbo/turbojpeg/TJCompressor.java b/java/org/libjpegturbo/turbojpeg/TJCompressor.java index 250f42d09..d7d2976fd 100644 --- a/java/org/libjpegturbo/turbojpeg/TJCompressor.java +++ b/java/org/libjpegturbo/turbojpeg/TJCompressor.java @@ -1,5 +1,6 @@ /* - * Copyright (C)2011-2015, 2018, 2020 D. R. Commander. All Rights Reserved. + * Copyright (C)2011-2015, 2018, 2020, 2022 D. R. Commander. + * All Rights Reserved. * Copyright (C)2015 Viktor Szathmáry. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without @@ -283,7 +284,14 @@ public void setSubsamp(int newSubsamp) { * Set the JPEG image quality level for subsequent compress operations. * * @param quality the new JPEG image quality level (1 to 100, 1 = worst, - * 100 = best) + * 100 = best.) When generating a lossless JPEG image (see + * {@link TJ#FLAG_LOSSLESS}), quality is + * psv * 10 + Pt, where psv is the predictor + * selection value (1-7) and Pt is the point transform (0-7). A + * point transform value of 0 is necessary in order to create a fully + * lossless JPEG image. (A non-zero point transform value right-shifts the + * input samples by the specified number of bits, which is effectively a form + * of lossy color quantization.) */ public void setJPEGQuality(int quality) { if (quality < 1 || quality > 100) diff --git a/java/org/libjpegturbo/turbojpeg/TJTransform.java b/java/org/libjpegturbo/turbojpeg/TJTransform.java index c655bf19e..214768cf1 100644 --- a/java/org/libjpegturbo/turbojpeg/TJTransform.java +++ b/java/org/libjpegturbo/turbojpeg/TJTransform.java @@ -103,21 +103,21 @@ public class TJTransform extends Rectangle { * partial MCU blocks that cannot be transformed will be left in place, which * will create odd-looking strips on the right or bottom edge of the image. */ - public static final int OPT_PERFECT = 1; + public static final int OPT_PERFECT = (1 << 0); /** * This option will discard any partial MCU blocks that cannot be * transformed. */ - public static final int OPT_TRIM = 2; + public static final int OPT_TRIM = (1 << 1); /** * This option will enable lossless cropping. */ - public static final int OPT_CROP = 4; + public static final int OPT_CROP = (1 << 2); /** * This option will discard the color data in the input image and produce * a grayscale output image. */ - public static final int OPT_GRAY = 8; + public static final int OPT_GRAY = (1 << 3); /** * This option will prevent {@link TJTransformer#transform * TJTransformer.transform()} from outputting a JPEG image for this @@ -125,7 +125,7 @@ public class TJTransform extends Rectangle { * filter to capture the transformed DCT coefficients without transcoding * them. */ - public static final int OPT_NOOUTPUT = 16; + public static final int OPT_NOOUTPUT = (1 << 4); /** * This option will enable progressive entropy coding in the output image * generated by this particular transform. Progressive entropy coding will @@ -133,13 +133,13 @@ public class TJTransform extends Rectangle { * default), but it will reduce compression and decompression performance * considerably. Can be combined with {@link #OPT_ARITHMETIC}. */ - public static final int OPT_PROGRESSIVE = 32; + public static final int OPT_PROGRESSIVE = (1 << 5); /** * This option will prevent {@link TJTransformer#transform * TJTransformer.transform()} from copying any extra markers (including EXIF * and ICC profile data) from the source image to the output image. */ - public static final int OPT_COPYNONE = 64; + public static final int OPT_COPYNONE = (1 << 6); /** * This option will enable arithmetic entropy coding in the output image * generated by this particular transform. Arithmetic entropy coding will @@ -147,7 +147,7 @@ public class TJTransform extends Rectangle { * default), but it will reduce compression and decompression performance * considerably. Can be combined with {@link #OPT_PROGRESSIVE}. */ - public static final int OPT_ARITHMETIC = 128; + public static final int OPT_ARITHMETIC = (1 << 7); /** diff --git a/tjbench.c b/tjbench.c index b6848c433..b26c8f2fb 100644 --- a/tjbench.c +++ b/tjbench.c @@ -775,6 +775,11 @@ static void usage(char *progName) printf("-arithmetic = Use arithmetic entropy coding in JPEG images generated by\n"); printf(" compression and transform operations. (Can be combined with\n"); printf(" -progressive.)\n"); + printf("-lossless = Generate lossless JPEG images (implies -subsamp 444). When\n"); + printf(" generating lossless JPEG images, Quality is psv * 10 + Pt, where psv is\n"); + printf(" the predictor selection value (1-7) and Pt is the point transform (0-7).\n"); + printf(" A point transform value of 0 is necessary in order to create a fully\n"); + printf(" lossless JPEG image.\n"); printf("-subsamp = When testing JPEG compression, this option specifies the level\n"); printf(" of chrominance subsampling to use ( = 444, 422, 440, 420, 411, or\n"); printf(" GRAY). The default is to test Grayscale, 4:2:0, 4:2:2, and 4:4:4 in\n"); @@ -874,6 +879,10 @@ int main(int argc, char *argv[]) } else if (!strcasecmp(argv[i], "-arithmetic")) { printf("Using arithmetic entropy coding\n\n"); flags |= TJFLAG_ARITHMETIC; + } else if (!strcasecmp(argv[i], "-lossless")) { + printf("Using lossless JPEG\n\n"); + flags |= TJFLAG_LOSSLESS; + subsamp = TJSAMP_444; } else if (!strcasecmp(argv[i], "-rgb")) pf = TJPF_RGB; else if (!strcasecmp(argv[i], "-rgbx")) diff --git a/tjunittest.c b/tjunittest.c index b3f031139..f010be251 100644 --- a/tjunittest.c +++ b/tjunittest.c @@ -58,6 +58,7 @@ static void usage(char *progName) printf("-yuv = test YUV encoding/decoding support\n"); printf("-noyuvpad = do not pad each line of each Y, U, and V plane to the nearest\n"); printf(" 4-byte boundary\n"); + printf("-lossless = test lossless JPEG compression/decompression\n"); printf("-alloc = test automatic buffer allocation\n"); printf("-bmp = tjLoadImage()/tjSaveImage() unit test\n\n"); exit(1); @@ -95,7 +96,7 @@ const int _4byteFormats[] = { const int _onlyGray[] = { TJPF_GRAY }; const int _onlyRGB[] = { TJPF_RGB }; -int doYUV = 0, alloc = 0, pad = 4; +int doYUV = 0, lossless = 0, alloc = 0, pad = 4, psv = 1; int exitStatus = 0; #define BAILOUT() { exitStatus = -1; goto bailout; } @@ -157,7 +158,7 @@ static void initBuf(unsigned char *buf, int w, int h, int pf, int flags) #define CHECKVAL(v, cv) { \ - if (v < cv - 1 || v > cv + 1) { \ + if (v < cv - (1 - lossless) || v > cv + (1 - lossless)) { \ printf("\nComp. %s at %d,%d should be %d, not %d\n", #v, row, col, cv, \ v); \ retval = 0; exitStatus = -1; goto bailout; \ @@ -165,14 +166,14 @@ static void initBuf(unsigned char *buf, int w, int h, int pf, int flags) } #define CHECKVAL0(v) { \ - if (v > 1) { \ + if (v > (1 - lossless)) { \ printf("\nComp. %s at %d,%d should be 0, not %d\n", #v, row, col, v); \ retval = 0; exitStatus = -1; goto bailout; \ } \ } #define CHECKVAL255(v) { \ - if (v < 254) { \ + if (v < 255 - (1 - lossless)) { \ printf("\nComp. %s at %d,%d should be 255, not %d\n", #v, row, col, v); \ retval = 0; exitStatus = -1; goto bailout; \ } \ @@ -433,6 +434,8 @@ static void _decompTest(tjhandle handle, unsigned char *jpegBuf, TRY_TJ(tjDecompressHeader2(handle, jpegBuf, jpegSize, &_hdrw, &_hdrh, &_hdrsubsamp)); + if (lossless && subsamp != TJSAMP_444 && subsamp != TJSAMP_GRAY) + subsamp = TJSAMP_444; if (_hdrw != w || _hdrh != h || _hdrsubsamp != subsamp) THROW("Incorrect JPEG header"); @@ -493,8 +496,15 @@ static void decompTest(tjhandle handle, unsigned char *jpegBuf, char *basename, int subsamp, int flags) { int i, n = 0; - tjscalingfactor *sf = tjGetScalingFactors(&n); + tjscalingfactor *sf = NULL, sf1 = { 1, 1 }; + if (lossless) { + _decompTest(handle, jpegBuf, jpegSize, w, h, pf, basename, subsamp, flags, + sf1); + return; + } + + sf = tjGetScalingFactors(&n); if (!sf || !n) THROW_TJ(); for (i = 0; i < n; i++) { @@ -518,7 +528,7 @@ static void doTest(int w, int h, const int *formats, int nformats, int subsamp, tjhandle chandle = NULL, dhandle = NULL; unsigned char *dstBuf = NULL; unsigned long size = 0; - int pfi, pf, i; + int pfi, pf, i, quality = 100; if (!alloc) size = tjBufSize(w, h, subsamp); @@ -534,12 +544,16 @@ static void doTest(int w, int h, const int *formats, int nformats, int subsamp, for (i = 0; i < 2; i++) { int flags = 0; + if (lossless) { + flags |= TJFLAG_LOSSLESS; + quality = (((psv++ - 1) % 7) + 1) * 10; + } if (subsamp == TJSAMP_422 || subsamp == TJSAMP_420 || subsamp == TJSAMP_440 || subsamp == TJSAMP_411) flags |= TJFLAG_FASTUPSAMPLE; if (i == 1) flags |= TJFLAG_BOTTOMUP; pf = formats[pfi]; - compTest(chandle, &dstBuf, &size, w, h, pf, basename, subsamp, 100, + compTest(chandle, &dstBuf, &size, w, h, pf, basename, subsamp, quality, flags); decompTest(dhandle, dstBuf, size, w, h, pf, basename, subsamp, flags); if (pf >= TJPF_RGBX && pf <= TJPF_XRGB) { @@ -601,11 +615,19 @@ static void bufSizeTest(void) unsigned char *srcBuf = NULL, *dstBuf = NULL; tjhandle handle = NULL; unsigned long dstSize = 0; + int flags = 0, quality = 100, numSamp = TJ_NUMSAMP; + + if (!alloc) flags |= TJFLAG_NOREALLOC; + if (lossless) { + flags |= TJFLAG_LOSSLESS; + quality = (((psv++ - 1) % 7) + 1) * 10; + numSamp = 1; + } if ((handle = tjInitCompress()) == NULL) THROW_TJ(); printf("Buffer size regression test\n"); - for (subsamp = 0; subsamp < TJ_NUMSAMP; subsamp++) { + for (subsamp = 0; subsamp < numSamp; subsamp++) { for (w = 1; w < 48; w++) { int maxh = (w == 1) ? 2048 : 48; @@ -630,8 +652,7 @@ static void bufSizeTest(void) subsamp, 0)); } else { TRY_TJ(tjCompress2(handle, srcBuf, w, 0, h, TJPF_BGRX, &dstBuf, - &dstSize, subsamp, 100, - alloc ? 0 : TJFLAG_NOREALLOC)); + &dstSize, subsamp, quality, flags)); } free(srcBuf); srcBuf = NULL; if (!alloc || doYUV) { @@ -657,8 +678,7 @@ static void bufSizeTest(void) subsamp, 0)); } else { TRY_TJ(tjCompress2(handle, srcBuf, h, 0, w, TJPF_BGRX, &dstBuf, - &dstSize, subsamp, 100, - alloc ? 0 : TJFLAG_NOREALLOC)); + &dstSize, subsamp, quality, flags)); } free(srcBuf); srcBuf = NULL; if (!alloc || doYUV) { @@ -899,27 +919,34 @@ int main(int argc, char *argv[]) for (i = 1; i < argc; i++) { if (!strcasecmp(argv[i], "-yuv")) doYUV = 1; else if (!strcasecmp(argv[i], "-noyuvpad")) pad = 1; + else if (!strcasecmp(argv[i], "-lossless")) lossless = 1; else if (!strcasecmp(argv[i], "-alloc")) alloc = 1; else if (!strcasecmp(argv[i], "-bmp")) return bmpTest(); else usage(argv[0]); } } + if (lossless && doYUV) + THROW("Lossless JPEG and YUV encoding/decoding are incompatible."); if (alloc) printf("Testing automatic buffer allocation\n"); if (doYUV) num4bf = 4; overflowTest(); doTest(35, 39, _3byteFormats, 2, TJSAMP_444, "test"); doTest(39, 41, _4byteFormats, num4bf, TJSAMP_444, "test"); doTest(41, 35, _3byteFormats, 2, TJSAMP_422, "test"); - doTest(35, 39, _4byteFormats, num4bf, TJSAMP_422, "test"); - doTest(39, 41, _3byteFormats, 2, TJSAMP_420, "test"); - doTest(41, 35, _4byteFormats, num4bf, TJSAMP_420, "test"); - doTest(35, 39, _3byteFormats, 2, TJSAMP_440, "test"); - doTest(39, 41, _4byteFormats, num4bf, TJSAMP_440, "test"); - doTest(41, 35, _3byteFormats, 2, TJSAMP_411, "test"); - doTest(35, 39, _4byteFormats, num4bf, TJSAMP_411, "test"); + if (!lossless) { + doTest(35, 39, _4byteFormats, num4bf, TJSAMP_422, "test"); + doTest(39, 41, _3byteFormats, 2, TJSAMP_420, "test"); + doTest(41, 35, _4byteFormats, num4bf, TJSAMP_420, "test"); + doTest(35, 39, _3byteFormats, 2, TJSAMP_440, "test"); + doTest(39, 41, _4byteFormats, num4bf, TJSAMP_440, "test"); + doTest(41, 35, _3byteFormats, 2, TJSAMP_411, "test"); + doTest(35, 39, _4byteFormats, num4bf, TJSAMP_411, "test"); + } doTest(39, 41, _onlyGray, 1, TJSAMP_GRAY, "test"); - doTest(41, 35, _3byteFormats, 2, TJSAMP_GRAY, "test"); - doTest(35, 39, _4byteFormats, 4, TJSAMP_GRAY, "test"); + if (!lossless) { + doTest(41, 35, _3byteFormats, 2, TJSAMP_GRAY, "test"); + doTest(35, 39, _4byteFormats, 4, TJSAMP_GRAY, "test"); + } bufSizeTest(); if (doYUV) { printf("\n--------------------\n\n"); @@ -932,5 +959,6 @@ int main(int argc, char *argv[]) doTest(48, 48, _onlyGray, 1, TJSAMP_GRAY, "test_yuv0"); } + bailout: return exitStatus; } diff --git a/turbojpeg.c b/turbojpeg.c index dc9d6d8cf..cb8abe1ab 100644 --- a/turbojpeg.c +++ b/turbojpeg.c @@ -272,13 +272,14 @@ static int getPixelFormat(int pixelSize, int flags) return -1; } -static void setCompDefaults(struct jpeg_compress_struct *cinfo, - int pixelFormat, int subsamp, int jpegQual, - int flags) +static int setCompDefaults(tjhandle handle, int pixelFormat, int subsamp, + int jpegQual, int flags) { #ifndef NO_GETENV char env[7] = { 0 }; #endif + int retval = 0; + GET_CINSTANCE(handle); cinfo->in_color_space = pf2cs[pixelFormat]; cinfo->input_components = tjPixelSize[pixelFormat]; @@ -307,7 +308,13 @@ static void setCompDefaults(struct jpeg_compress_struct *cinfo, } #endif - if (jpegQual >= 0) { + if (flags & TJFLAG_LOSSLESS) { + int psv = jpegQual / 10, pt = jpegQual % 10; + + if (psv < 1 || psv > 7 || pt < 0 || pt > 7) + THROW("Invalid lossless parameters"); + jpeg_enable_lossless(cinfo, psv, pt); + } else if (jpegQual >= 0) { jpeg_set_quality(cinfo, jpegQual, TRUE); if (jpegQual >= 96 || flags & TJFLAG_ACCURATEDCT) cinfo->dct_method = JDCT_ISLOW; @@ -344,6 +351,9 @@ static void setCompDefaults(struct jpeg_compress_struct *cinfo, cinfo->comp_info[2].v_samp_factor = 1; if (cinfo->num_components > 3) cinfo->comp_info[3].v_samp_factor = tjMCUHeight[subsamp] / 8; + + bailout: + return retval; } @@ -719,7 +729,10 @@ DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf, alloc = FALSE; *jpegSize = tjBufSize(width, height, jpegSubsamp); } jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc); - setCompDefaults(cinfo, pixelFormat, jpegSubsamp, jpegQual, flags); + if (setCompDefaults(cinfo, pixelFormat, jpegSubsamp, jpegQual, + flags) == -1) { + retval = -1; goto bailout; + } jpeg_start_compress(cinfo, TRUE); for (i = 0; i < height; i++) { @@ -817,7 +830,9 @@ DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf, else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1"); #endif - setCompDefaults(cinfo, pixelFormat, subsamp, -1, flags); + if (setCompDefaults(cinfo, pixelFormat, subsamp, -1, flags) == -1) { + retval = -1; goto bailout; + } /* Execute only the parts of jpeg_start_compress() that we need. If we were to call the whole jpeg_start_compress() function, then it would try @@ -1032,7 +1047,9 @@ DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle, alloc = FALSE; *jpegSize = tjBufSize(width, height, subsamp); } jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc); - setCompDefaults(cinfo, TJPF_RGB, subsamp, jpegQual, flags); + if (setCompDefaults(cinfo, TJPF_RGB, subsamp, jpegQual, flags) == -1) { + retval = -1; goto bailout; + } cinfo->raw_data_in = TRUE; jpeg_start_compress(cinfo, TRUE); diff --git a/turbojpeg.h b/turbojpeg.h index 570c7c6a9..77a90b5f2 100644 --- a/turbojpeg.h +++ b/turbojpeg.h @@ -370,7 +370,7 @@ enum TJCS { * The uncompressed source/destination image is stored in bottom-up (Windows, * OpenGL) order, not top-down (X11) order. */ -#define TJFLAG_BOTTOMUP 2 +#define TJFLAG_BOTTOMUP (1 << 1) /** * When decompressing an image that was compressed using chrominance * subsampling, use the fastest chrominance upsampling algorithm available in @@ -378,7 +378,7 @@ enum TJCS { * creates a smooth transition between neighboring chrominance components in * order to reduce upsampling artifacts in the decompressed image. */ -#define TJFLAG_FASTUPSAMPLE 256 +#define TJFLAG_FASTUPSAMPLE (1 << 8) /** * Disable buffer (re)allocation. If passed to one of the JPEG compression or * transform functions, this flag will cause those functions to generate an @@ -386,7 +386,7 @@ enum TJCS { * attempting to allocate or reallocate that buffer. This reproduces the * behavior of earlier versions of TurboJPEG. */ -#define TJFLAG_NOREALLOC 1024 +#define TJFLAG_NOREALLOC (1 << 10) /** * Use the fastest DCT/IDCT algorithm available in the underlying codec. The * default if this flag is not specified is implementation-specific. For @@ -395,7 +395,7 @@ enum TJCS { * only a very slight effect on accuracy, but it uses the accurate algorithm * when decompressing, because this has been shown to have a larger effect. */ -#define TJFLAG_FASTDCT 2048 +#define TJFLAG_FASTDCT (1 << 11) /** * Use the most accurate DCT/IDCT algorithm available in the underlying codec. * The default if this flag is not specified is implementation-specific. For @@ -404,14 +404,14 @@ enum TJCS { * only a very slight effect on accuracy, but it uses the accurate algorithm * when decompressing, because this has been shown to have a larger effect. */ -#define TJFLAG_ACCURATEDCT 4096 +#define TJFLAG_ACCURATEDCT (1 << 12) /** * Immediately discontinue the current compression/decompression/transform * operation if the underlying codec throws a warning (non-fatal error). The * default behavior is to allow the operation to complete unless a fatal error * is encountered. */ -#define TJFLAG_STOPONWARNING 8192 +#define TJFLAG_STOPONWARNING (1 << 13) /** * Use progressive entropy coding in JPEG images generated by the compression * and transform functions. Progressive entropy coding will generally improve @@ -419,7 +419,7 @@ enum TJCS { * reduce compression and decompression performance considerably. Can be * combined with #TJFLAG_ARITHMETIC. */ -#define TJFLAG_PROGRESSIVE 16384 +#define TJFLAG_PROGRESSIVE (1 << 14) /** * Limit the number of progressive JPEG scans that the decompression and * transform functions will process. If a progressive JPEG image contains an @@ -429,7 +429,7 @@ enum TJCS { * an exploit of the progressive JPEG format described in * this report. */ -#define TJFLAG_LIMITSCANS 32768 +#define TJFLAG_LIMITSCANS (1 << 15) /** * Use arithmetic entropy coding in JPEG images generated by the compression * and transform functions. Arithmetic entropy coding will generally improve @@ -437,7 +437,23 @@ enum TJCS { * reduce compression and decompression performance considerably. Can be * combined with #TJFLAG_PROGRESSIVE. */ -#define TJFLAG_ARITHMETIC 65536 +#define TJFLAG_ARITHMETIC (1 << 16) +/** + * Generate a lossless JPEG image when compressing. In most cases, compressing + * and decompressing lossless JPEG images is considerably slower than + * compressing and decompressing lossy JPEG images. Also note that the + * following features are not available with lossless JPEG images: + * - Colorspace conversion + * - Chrominance subsampling + * - JPEG quality selection + * - DCT/IDCT algorithm selection + * - Progressive entropy coding + * - Arithmetic entropy coding + * - Compression from/decompression to YUV planar images + * - Decompression scaling + * - Lossless transformations + */ +#define TJFLAG_LOSSLESS (1 << 17) /** @@ -527,29 +543,29 @@ enum TJXOP { * that cannot be transformed will be left in place, which will create * odd-looking strips on the right or bottom edge of the image. */ -#define TJXOPT_PERFECT 1 +#define TJXOPT_PERFECT (1 << 0) /** * This option will cause #tjTransform() to discard any partial MCU blocks that * cannot be transformed. */ -#define TJXOPT_TRIM 2 +#define TJXOPT_TRIM (1 << 1) /** * This option will enable lossless cropping. See #tjTransform() for more * information. */ -#define TJXOPT_CROP 4 +#define TJXOPT_CROP (1 << 2) /** * This option will discard the color data in the input image and produce * a grayscale output image. */ -#define TJXOPT_GRAY 8 +#define TJXOPT_GRAY (1 << 3) /** * This option will prevent #tjTransform() from outputting a JPEG image for * this particular transform (this can be used in conjunction with a custom * filter to capture the transformed DCT coefficients without transcoding * them.) */ -#define TJXOPT_NOOUTPUT 16 +#define TJXOPT_NOOUTPUT (1 << 4) /** * This option will enable progressive entropy coding in the output image * generated by this particular transform. Progressive entropy coding will @@ -557,13 +573,13 @@ enum TJXOP { * default), but it will reduce compression and decompression performance * considerably. Can be combined with #TJXOPT_ARITHMETIC. */ -#define TJXOPT_PROGRESSIVE 32 +#define TJXOPT_PROGRESSIVE (1 << 5) /** * This option will prevent #tjTransform() from copying any extra markers * (including EXIF and ICC profile data) from the source image to the output * image. */ -#define TJXOPT_COPYNONE 64 +#define TJXOPT_COPYNONE (1 << 6) /** * This option will enable arithmetic entropy coding in the output image * generated by this particular transform. Arithmetic entropy coding will @@ -571,7 +587,7 @@ enum TJXOP { * default), but it will reduce compression and decompression performance * considerably. Can be combined with #TJXOPT_PROGRESSIVE. */ -#define TJXOPT_ARITHMETIC 128 +#define TJXOPT_ARITHMETIC (1 << 7) /** @@ -760,7 +776,13 @@ DLLEXPORT tjhandle tjInitCompress(void); * "Chrominance subsampling options".) * * @param jpegQual the image quality of the generated JPEG image (1 = worst, - * 100 = best) + * 100 = best.) When generating a lossless JPEG image (see #TJFLAG_LOSSLESS), + * jpegQual is psv * 10 + Pt, where psv is the + * predictor selection value (1-7) and Pt is the point transform + * (0-7). A point transform value of 0 is necessary in order to create a fully + * lossless JPEG image. (A non-zero point transform value right-shifts the + * input samples by the specified number of bits, which is effectively a form + * of lossy color quantization.) * * @param flags the bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT * "flags" From 74d5b168f7a00250c1dc0001527d10175e00b779 Mon Sep 17 00:00:00 2001 From: DRC Date: Mon, 21 Nov 2022 22:41:46 -0600 Subject: [PATCH 044/162] Build: Update tjtest target dependencies --- CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cc50141a1..484cba559 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1429,7 +1429,8 @@ if(WITH_TURBOJPEG) COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest.java DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest.java - ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest) + ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest + ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest.java) else() add_custom_target(tjtest COMMAND echo tjbenchtest @@ -1446,7 +1447,8 @@ if(WITH_TURBOJPEG) COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -progressive -yuv COMMAND echo tjexampletest COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest) + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest + ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest) endif() endif() From 07129256e04475e9ff647995e233804f45988244 Mon Sep 17 00:00:00 2001 From: DRC Date: Wed, 16 Nov 2022 17:44:43 -0600 Subject: [PATCH 045/162] OSS-Fuzz: Add fuzz target for lossless JPEG --- fuzz/CMakeLists.txt | 2 + fuzz/build.sh | 1 + fuzz/cjpeg12.cc | 12 +++- fuzz/compress_lossless.cc | 131 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 fuzz/compress_lossless.cc diff --git a/fuzz/CMakeLists.txt b/fuzz/CMakeLists.txt index 377087137..01c12b79f 100644 --- a/fuzz/CMakeLists.txt +++ b/fuzz/CMakeLists.txt @@ -54,6 +54,8 @@ add_fuzz_target(compress compress.cc) add_fuzz_target(compress_yuv compress_yuv.cc) +add_fuzz_target(compress_lossless compress_lossless.cc) + # NOTE: This target is named libjpeg_turbo_fuzzer instead of decompress_fuzzer # in order to preserve the corpora from Google's OSS-Fuzz target for # libjpeg-turbo, which this target replaces. diff --git a/fuzz/build.sh b/fuzz/build.sh index 718b1b5e4..07b1b5190 100644 --- a/fuzz/build.sh +++ b/fuzz/build.sh @@ -21,6 +21,7 @@ cp $SRC/compress_fuzzer_seed_corpus.zip $OUT/cjpeg_fuzzer${FUZZER_SUFFIX}_seed_c cp $SRC/compress_fuzzer_seed_corpus.zip $OUT/cjpeg12_fuzzer${FUZZER_SUFFIX}_seed_corpus.zip cp $SRC/compress_fuzzer_seed_corpus.zip $OUT/compress_fuzzer${FUZZER_SUFFIX}_seed_corpus.zip cp $SRC/compress_fuzzer_seed_corpus.zip $OUT/compress_yuv_fuzzer${FUZZER_SUFFIX}_seed_corpus.zip +cp $SRC/compress_fuzzer_seed_corpus.zip $OUT/compress_lossless_fuzzer${FUZZER_SUFFIX}_seed_corpus.zip cp $SRC/decompress_fuzzer_seed_corpus.zip $OUT/libjpeg_turbo_fuzzer${FUZZER_SUFFIX}_seed_corpus.zip cp $SRC/decompress_fuzzer_seed_corpus.zip $OUT/decompress_yuv_fuzzer${FUZZER_SUFFIX}_seed_corpus.zip cp $SRC/decompress_fuzzer_seed_corpus.zip $OUT/transform_fuzzer${FUZZER_SUFFIX}_seed_corpus.zip diff --git a/fuzz/cjpeg12.cc b/fuzz/cjpeg12.cc index f845ead64..64f8ad662 100644 --- a/fuzz/cjpeg12.cc +++ b/fuzz/cjpeg12.cc @@ -56,6 +56,14 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) (char *)"-quality", (char *)"90,80,70", (char *)"-rgb", (char *)"-sample", (char *)"2x1", (char *)"-smooth", (char *)"50", NULL }; + char *argv3[] = { + (char *)"cjpeg", (char *)"-precision", (char *)"12", + (char *)"-lossless", (char *)"1,4", NULL + }; + char *argv4[] = { + (char *)"cjpeg", (char *)"-precision", (char *)"12", + (char *)"-lossless", (char *)"4,0", NULL + }; int fd = -1; #if defined(__has_feature) && __has_feature(memory_sanitizer) char env[18] = "JSIMD_FORCENONE=1"; @@ -69,10 +77,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) if ((fd = mkstemp(filename)) < 0 || write(fd, data, size) < 0) goto bailout; - argv1[12] = argv2[13] = filename; + argv1[12] = argv2[13] = argv3[5] = argv4[5] = filename; cjpeg_main(13, argv1); cjpeg_main(14, argv2); + cjpeg_main(6, argv3); + cjpeg_main(6, argv4); bailout: if (fd >= 0) { diff --git a/fuzz/compress_lossless.cc b/fuzz/compress_lossless.cc new file mode 100644 index 000000000..4ba156691 --- /dev/null +++ b/fuzz/compress_lossless.cc @@ -0,0 +1,131 @@ +/* + * Copyright (C)2021-2022 D. R. Commander. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the libjpeg-turbo Project nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + + +#define NUMTESTS 7 +/* Private flag that triggers different TurboJPEG API behavior when fuzzing */ +#define TJFLAG_FUZZING (1 << 30) + + +struct test { + enum TJPF pf; + int psv, pt; +}; + + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + tjhandle handle = NULL; + unsigned char *srcBuf = NULL, *dstBuf = NULL; + int width = 0, height = 0, fd = -1, i, ti; + char filename[FILENAME_MAX] = { 0 }; + struct test tests[NUMTESTS] = { + { TJPF_RGB, 1, 0 }, + { TJPF_BGR, 2, 2 }, + { TJPF_RGBX, 3, 4 }, + { TJPF_BGRA, 4, 7 }, + { TJPF_XRGB, 5, 5 }, + { TJPF_GRAY, 6, 3 }, + { TJPF_CMYK, 7, 0 } + }; +#if defined(__has_feature) && __has_feature(memory_sanitizer) + char env[18] = "JSIMD_FORCENONE=1"; + + /* The libjpeg-turbo SIMD extensions produce false positives with + MemorySanitizer. */ + putenv(env); +#endif + + snprintf(filename, FILENAME_MAX, "/tmp/libjpeg-turbo_compress_fuzz.XXXXXX"); + if ((fd = mkstemp(filename)) < 0 || write(fd, data, size) < 0) + goto bailout; + + if ((handle = tjInitCompress()) == NULL) + goto bailout; + + for (ti = 0; ti < NUMTESTS; ti++) { + int flags = TJFLAG_FUZZING | TJFLAG_LOSSLESS, sum = 0, pf = tests[ti].pf; + unsigned long dstSize = 0, maxBufSize; + + /* Test non-default compression options on specific iterations. */ + if (ti == 0) + flags |= TJFLAG_BOTTOMUP; + if (ti != 2) + flags |= TJFLAG_NOREALLOC; + + /* tjLoadImage() refuses to load images larger than 1 Megapixel when + FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION is defined (yes, that's a dirty + hack), so we don't need to check the width and height here. */ + if ((srcBuf = tjLoadImage(filename, &width, 1, &height, &pf, + flags)) == NULL) + continue; + + maxBufSize = tjBufSize(width, height, TJSAMP_444); + if (flags & TJFLAG_NOREALLOC) { + if ((dstBuf = (unsigned char *)malloc(maxBufSize)) == NULL) + goto bailout; + } else + dstBuf = NULL; + + if (tjCompress2(handle, srcBuf, width, 0, height, pf, &dstBuf, &dstSize, + TJSAMP_444, tests[ti].psv * 10 + tests[ti].pt, + flags) == 0) { + /* Touch all of the output pixels in order to catch uninitialized reads + when using MemorySanitizer. */ + for (i = 0; i < dstSize; i++) + sum += dstBuf[i]; + } + + free(dstBuf); + dstBuf = NULL; + tjFree(srcBuf); + srcBuf = NULL; + + /* Prevent the code above from being optimized out. This test should never + be true, but the compiler doesn't know that. */ + if (sum > 255 * maxBufSize) + goto bailout; + } + +bailout: + free(dstBuf); + tjFree(srcBuf); + if (fd >= 0) { + close(fd); + if (strlen(filename) > 0) unlink(filename); + } + if (handle) tjDestroy(handle); + return 0; +} From db9dd93da468c5b1229f58fe3c35985572ea74df Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 17 Nov 2022 11:31:29 -0600 Subject: [PATCH 046/162] Lossless: Fix innocuous UBSan warnings --- jclossls.c | 3 ++- jddiffct.c | 3 ++- jdlossls.c | 6 ++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/jclossls.c b/jclossls.c index 8459b6e7a..e9ba92a7d 100644 --- a/jclossls.c +++ b/jclossls.c @@ -256,8 +256,9 @@ METHODDEF(void) simple_downscale(j_compress_ptr cinfo, _JSAMPROW input_buf, _JSAMPROW output_buf, JDIMENSION width) { - while (width--) + do { *output_buf++ = (_JSAMPLE)RIGHT_SHIFT(*input_buf++, cinfo->Al); + } while (--width); } diff --git a/jddiffct.c b/jddiffct.c index 1eb3a9d0d..f1d7f61b5 100644 --- a/jddiffct.c +++ b/jddiffct.c @@ -196,7 +196,8 @@ decompress_data(j_decompress_ptr cinfo, _JSAMPIMAGE output_buf) } /* Account for restart interval (no-op if not using restarts) */ - diff->restart_rows_to_go--; + if (cinfo->restart_interval) + diff->restart_rows_to_go--; /* Completed an MCU row, but perhaps not an iMCU row */ diff->MCU_ctr = 0; diff --git a/jdlossls.c b/jdlossls.c index 96790bff7..f03164e49 100644 --- a/jdlossls.c +++ b/jdlossls.c @@ -216,16 +216,18 @@ METHODDEF(void) simple_upscale(j_decompress_ptr cinfo, JDIFFROW diff_buf, _JSAMPROW output_buf, JDIMENSION width) { - while (width--) + do { *output_buf++ = (_JSAMPLE)(*diff_buf++ << cinfo->Al); + } while (--width); } METHODDEF(void) noscale(j_decompress_ptr cinfo, JDIFFROW diff_buf, _JSAMPROW output_buf, JDIMENSION width) { - while (width--) + do { *output_buf++ = (_JSAMPLE)(*diff_buf++); + } while (--width); } From b85b028d2b06c2c9d12e6734187a888825ddfd97 Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 17 Nov 2022 11:32:11 -0600 Subject: [PATCH 047/162] OSS-Fuzz: Fix argument error in cjpeg12 target Smoothing can only be used with 4:2:0 subsampling. --- fuzz/cjpeg12.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/cjpeg12.cc b/fuzz/cjpeg12.cc index 64f8ad662..ca3ec7c1d 100644 --- a/fuzz/cjpeg12.cc +++ b/fuzz/cjpeg12.cc @@ -54,7 +54,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) (char *)"cjpeg", (char *)"-precision", (char *)"12", (char *)"-dct", (char *)"fast", (char *)"-memdst", (char *)"-quality", (char *)"90,80,70", (char *)"-rgb", - (char *)"-sample", (char *)"2x1", (char *)"-smooth", (char *)"50", NULL + (char *)"-sample", (char *)"2x2", (char *)"-smooth", (char *)"50", NULL }; char *argv3[] = { (char *)"cjpeg", (char *)"-precision", (char *)"12", From 98ff1fd10306d031cfcf8a6becf78b3ac0afe49b Mon Sep 17 00:00:00 2001 From: DRC Date: Mon, 21 Nov 2022 20:57:39 -0600 Subject: [PATCH 048/162] TurboJPEG: Add lossless JPEG detection capability Add a new TurboJPEG C API function (tjDecompressHeader4()) and Java API method (TJDecompressor.getFlags()) that return the bitwise OR of any flags that are relevant to the JPEG image being decompressed (currently TJFLAG_PROGRESSIVE, TJFLAG_ARITHMETIC, TJFLAG_LOSSLESS, and their Java equivalents.) This allows a calling program to determine whether the image being decompressed is a lossless JPEG image, which means that the decompression scaling feature will not be available and that a full-sized destination buffer should be allocated. More specifically, this fixes a buffer overrun in TJBench, TJExample, and the decompress* fuzz targets that occurred when attempting (in vain) to decompress a lossless JPEG image with decompression scaling enabled. --- CMakeLists.txt | 10 + ChangeLog.md | 8 +- doc/html/annotated.html | 2 +- doc/html/classes.html | 2 +- doc/html/functions.html | 2 +- doc/html/functions_vars.html | 2 +- doc/html/group___turbo_j_p_e_g.html | 23 +- doc/html/index.html | 2 +- doc/html/modules.html | 2 +- doc/html/search/all_6.js | 4 +- doc/html/search/functions_0.js | 2 +- doc/html/structtjregion.html | 2 +- doc/html/structtjscalingfactor.html | 2 +- doc/html/structtjtransform.html | 2 +- doxygen.config | 2 +- fuzz/decompress.cc | 10 +- fuzz/decompress_yuv.cc | 10 +- java/TJBench.java | 17 +- java/TJExample.java | 8 +- java/doc/index-all.html | 9 + .../turbojpeg/TJDecompressor.html | 61 ++++- .../libjpegturbo/turbojpeg/TJTransformer.html | 4 +- .../turbojpeg/TJDecompressor.java | 16 ++ tjbench.c | 21 +- tjbenchtest.in | 258 ++++++++++-------- tjbenchtest.java.in | 192 +++++++------ tjexample.c | 9 +- turbojpeg-jni.c | 12 +- turbojpeg-mapfile | 6 + turbojpeg-mapfile.jni | 6 + turbojpeg.c | 32 ++- turbojpeg.h | 16 +- 32 files changed, 476 insertions(+), 278 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c3a63fc9..70b7f9276 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1530,6 +1530,10 @@ if(WITH_TURBOJPEG) COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -arithmetic COMMAND echo tjbenchtest -arithmetic -yuv COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -arithmetic -yuv + COMMAND echo tjbenchtest -lossless + COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -lossless + COMMAND echo tjbenchtest -lossless -alloc + COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -lossless -alloc COMMAND echo tjexampletest COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest COMMAND echo tjbenchtest.java @@ -1546,6 +1550,8 @@ if(WITH_TURBOJPEG) COMMAND echo tjbenchtest.java -arithmetic -yuv COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest.java -arithmetic -yuv + COMMAND echo tjbenchtest.java -lossless + COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest.java -lossless COMMAND echo tjexampletest.java COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest.java DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest @@ -1566,6 +1572,10 @@ if(WITH_TURBOJPEG) COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -progressive COMMAND echo tjbenchtest -progressive -yuv COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -progressive -yuv + COMMAND echo tjbenchtest -lossless + COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -lossless + COMMAND echo tjbenchtest -lossless -alloc + COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -lossless -alloc COMMAND echo tjexampletest COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest diff --git a/ChangeLog.md b/ChangeLog.md index cc5df1e4e..a5597fada 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -36,7 +36,8 @@ the C API and `TJ.FLAG_LOSSLESS` in the Java API), and cjpeg/TJBench command-line argument (`-lossless`) can be used to create a lossless JPEG image. (Decompression of lossless JPEG images is handled automatically.) Note that the TurboJPEG API and TJBench can currently only be used to create and -decompress 8-bit lossless JPEG images. +decompress 8-bit lossless JPEG images. Refer to [libjpeg.txt](libjpeg.txt), +[usage.txt](usage.txt), and the TurboJPEG API documentation for more details. 5. Introduced a new flag in the TurboJPEG C and Java APIs (`TJFLAG_ARITHMETIC` and `TJ.FLAG_ARITHMETIC`, respectively) that causes the library to use @@ -46,6 +47,11 @@ API and `TJTransform.OPT_ARITHMETIC` in the Java API) has been introduced, allowing arithmetic entropy coding to be enabled for selected transforms in a multi-transform operation. +6. Added a new TurboJPEG C API function (`tjDecompressHeader4()`) and Java API +method (`TJDecompressor.getFlags()`) that allow calling programs to determine +whether the JPEG image being decompressed uses progressive and/or arithmetic +entropy coding or is a lossless JPEG image. + 2.1.5 ===== diff --git a/doc/html/annotated.html b/doc/html/annotated.html index c735a5a7e..de0462ee3 100644 --- a/doc/html/annotated.html +++ b/doc/html/annotated.html @@ -23,7 +23,7 @@
TurboJPEG -  2.1.4 +  2.2
diff --git a/doc/html/classes.html b/doc/html/classes.html index 42f38f6bc..ac3c535b4 100644 --- a/doc/html/classes.html +++ b/doc/html/classes.html @@ -23,7 +23,7 @@
TurboJPEG -  2.1.4 +  2.2
diff --git a/doc/html/functions.html b/doc/html/functions.html index 5de4c6fba..68fcde3f8 100644 --- a/doc/html/functions.html +++ b/doc/html/functions.html @@ -23,7 +23,7 @@
TurboJPEG -  2.1.4 +  2.2
diff --git a/doc/html/functions_vars.html b/doc/html/functions_vars.html index 2b010f40d..d918435dc 100644 --- a/doc/html/functions_vars.html +++ b/doc/html/functions_vars.html @@ -23,7 +23,7 @@
TurboJPEG -  2.1.4 +  2.2
diff --git a/doc/html/group___turbo_j_p_e_g.html b/doc/html/group___turbo_j_p_e_g.html index ccfa88662..8e2b22291 100644 --- a/doc/html/group___turbo_j_p_e_g.html +++ b/doc/html/group___turbo_j_p_e_g.html @@ -23,7 +23,7 @@
TurboJPEG -  2.1.4 +  2.2
@@ -281,9 +281,9 @@ DLLEXPORT tjhandle tjInitDecompress (void)  Create a TurboJPEG decompressor instance. More...
  -DLLEXPORT int tjDecompressHeader3 (tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height, int *jpegSubsamp, int *jpegColorspace) - Retrieve information about a JPEG image without decompressing it, or prime the decompressor with quantization and Huffman tables. More...
-  +DLLEXPORT int tjDecompressHeader4 (tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height, int *jpegSubsamp, int *jpegColorspace, int *jpegFlags) + Retrieve information about a JPEG image without decompressing it, or prime the decompressor with quantization and Huffman tables. More...
+  DLLEXPORT tjscalingfactortjGetScalingFactors (int *numscalingfactors)  Returns a list of fractional scaling factors that the JPEG decompressor in this implementation of TurboJPEG supports. More...
  @@ -1739,14 +1739,14 @@

-

◆ tjDecompressHeader3()

+ +

◆ tjDecompressHeader4()

- + @@ -1785,7 +1785,13 @@

- + + + + + + + @@ -1805,6 +1811,7 @@

height

+
DLLEXPORT int tjDecompressHeader3 DLLEXPORT int tjDecompressHeader4 ( tjhandle  handle, int * jpegColorspace jpegColorspace,
int * jpegFlags 
pointer to an integer variable that will receive the height (in pixels) of the JPEG image. If jpegBuf points to a tables-only datastream, then height is ignored.
jpegSubsamppointer to an integer variable that will receive the level of chrominance subsampling used when the JPEG image was compressed (see Chrominance subsampling options.) If jpegBuf points to a tables-only datastream, then jpegSubsamp is ignored.
jpegColorspacepointer to an integer variable that will receive one of the JPEG colorspace constants, indicating the colorspace of the JPEG image (see JPEG colorspaces.) If jpegBuf points to a tables-only datastream, then jpegColorspace is ignored.
jpegFlagspointer to an integer variable that will receive the bitwise OR of one or more of the flags, such as TJFLAG_PROGRESSIVE and TJFLAG_LOSSLESS, that describe the JPEG image. If jpegBuf points to a tables-only datastream, then jpegFlags is ignored.
diff --git a/doc/html/index.html b/doc/html/index.html index a6f272a39..8d06e0bcd 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -23,7 +23,7 @@
TurboJPEG -  2.1.4 +  2.2
diff --git a/doc/html/modules.html b/doc/html/modules.html index d48980a46..f0d4eb56f 100644 --- a/doc/html/modules.html +++ b/doc/html/modules.html @@ -23,7 +23,7 @@
TurboJPEG -  2.1.4 +  2.2
diff --git a/doc/html/search/all_6.js b/doc/html/search/all_6.js index 2ac83e759..76dc418b1 100644 --- a/doc/html/search/all_6.js +++ b/doc/html/search/all_6.js @@ -22,7 +22,7 @@ var searchData= ['tjdecodeyuv_27',['tjDecodeYUV',['../group___turbo_j_p_e_g.html#ga70abbf38f77a26fd6da8813bef96f695',1,'turbojpeg.h']]], ['tjdecodeyuvplanes_28',['tjDecodeYUVPlanes',['../group___turbo_j_p_e_g.html#ga10e837c07fa9d25770565b237d3898d9',1,'turbojpeg.h']]], ['tjdecompress2_29',['tjDecompress2',['../group___turbo_j_p_e_g.html#gae9eccef8b682a48f43a9117c231ed013',1,'turbojpeg.h']]], - ['tjdecompressheader3_30',['tjDecompressHeader3',['../group___turbo_j_p_e_g.html#ga0595681096bba7199cc6f3533cb25f77',1,'turbojpeg.h']]], + ['tjdecompressheader4_30',['tjDecompressHeader4',['../group___turbo_j_p_e_g.html#gac104e6e729f57f195009405949d198dc',1,'turbojpeg.h']]], ['tjdecompresstoyuv2_31',['tjDecompressToYUV2',['../group___turbo_j_p_e_g.html#ga04d1e839ff9a0860dd1475cff78d3364',1,'turbojpeg.h']]], ['tjdecompresstoyuvplanes_32',['tjDecompressToYUVPlanes',['../group___turbo_j_p_e_g.html#gaa59f901a5258ada5bd0185ad59368540',1,'turbojpeg.h']]], ['tjdestroy_33',['tjDestroy',['../group___turbo_j_p_e_g.html#ga75f355fa27225ba1a4ee392c852394d2',1,'turbojpeg.h']]], @@ -84,7 +84,7 @@ var searchData= ['tjsaveimage_89',['tjSaveImage',['../group___turbo_j_p_e_g.html#ga6f445b22d8933ae4815b3370a538d879',1,'turbojpeg.h']]], ['tjscaled_90',['TJSCALED',['../group___turbo_j_p_e_g.html#ga84878bb65404204743aa18cac02781df',1,'turbojpeg.h']]], ['tjscalingfactor_91',['tjscalingfactor',['../structtjscalingfactor.html',1,'']]], - ['tjtransform_92',['tjtransform',['../structtjtransform.html',1,'tjtransform'],['../group___turbo_j_p_e_g.html#ga9cb8abf4cc91881e04a0329b2270be25',1,'tjTransform(tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, int n, unsigned char **dstBufs, unsigned long *dstSizes, tjtransform *transforms, int flags): turbojpeg.h'],['../group___turbo_j_p_e_g.html#ga504805ec0161f1b505397ca0118bf8fd',1,'tjtransform(): turbojpeg.h']]], + ['tjtransform_92',['tjtransform',['../structtjtransform.html',1,'tjtransform'],['../group___turbo_j_p_e_g.html#ga504805ec0161f1b505397ca0118bf8fd',1,'tjtransform(): turbojpeg.h'],['../group___turbo_j_p_e_g.html#ga9cb8abf4cc91881e04a0329b2270be25',1,'tjTransform(tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, int n, unsigned char **dstBufs, unsigned long *dstSizes, tjtransform *transforms, int flags): turbojpeg.h']]], ['tjxop_93',['TJXOP',['../group___turbo_j_p_e_g.html#ga2de531af4e7e6c4f124908376b354866',1,'turbojpeg.h']]], ['tjxop_5fhflip_94',['TJXOP_HFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aa0df69776caa30f0fa28e26332d311ce',1,'turbojpeg.h']]], ['tjxop_5fnone_95',['TJXOP_NONE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aad88c0366cd3f7d0eac9d7a3fa1c2c27',1,'turbojpeg.h']]], diff --git a/doc/html/search/functions_0.js b/doc/html/search/functions_0.js index 440716001..e752acb3e 100644 --- a/doc/html/search/functions_0.js +++ b/doc/html/search/functions_0.js @@ -9,7 +9,7 @@ var searchData= ['tjdecodeyuv_123',['tjDecodeYUV',['../group___turbo_j_p_e_g.html#ga70abbf38f77a26fd6da8813bef96f695',1,'turbojpeg.h']]], ['tjdecodeyuvplanes_124',['tjDecodeYUVPlanes',['../group___turbo_j_p_e_g.html#ga10e837c07fa9d25770565b237d3898d9',1,'turbojpeg.h']]], ['tjdecompress2_125',['tjDecompress2',['../group___turbo_j_p_e_g.html#gae9eccef8b682a48f43a9117c231ed013',1,'turbojpeg.h']]], - ['tjdecompressheader3_126',['tjDecompressHeader3',['../group___turbo_j_p_e_g.html#ga0595681096bba7199cc6f3533cb25f77',1,'turbojpeg.h']]], + ['tjdecompressheader4_126',['tjDecompressHeader4',['../group___turbo_j_p_e_g.html#gac104e6e729f57f195009405949d198dc',1,'turbojpeg.h']]], ['tjdecompresstoyuv2_127',['tjDecompressToYUV2',['../group___turbo_j_p_e_g.html#ga04d1e839ff9a0860dd1475cff78d3364',1,'turbojpeg.h']]], ['tjdecompresstoyuvplanes_128',['tjDecompressToYUVPlanes',['../group___turbo_j_p_e_g.html#gaa59f901a5258ada5bd0185ad59368540',1,'turbojpeg.h']]], ['tjdestroy_129',['tjDestroy',['../group___turbo_j_p_e_g.html#ga75f355fa27225ba1a4ee392c852394d2',1,'turbojpeg.h']]], diff --git a/doc/html/structtjregion.html b/doc/html/structtjregion.html index 72d49d270..fc3989008 100644 --- a/doc/html/structtjregion.html +++ b/doc/html/structtjregion.html @@ -23,7 +23,7 @@
TurboJPEG -  2.1.4 +  2.2
diff --git a/doc/html/structtjscalingfactor.html b/doc/html/structtjscalingfactor.html index 1606a02cc..6b8b1ffd2 100644 --- a/doc/html/structtjscalingfactor.html +++ b/doc/html/structtjscalingfactor.html @@ -23,7 +23,7 @@
TurboJPEG -  2.1.4 +  2.2
diff --git a/doc/html/structtjtransform.html b/doc/html/structtjtransform.html index ba78980ed..c8f10a52d 100644 --- a/doc/html/structtjtransform.html +++ b/doc/html/structtjtransform.html @@ -23,7 +23,7 @@
TurboJPEG -  2.1.4 +  2.2
diff --git a/doxygen.config b/doxygen.config index 16708b03e..b051aa4cf 100644 --- a/doxygen.config +++ b/doxygen.config @@ -1,5 +1,5 @@ PROJECT_NAME = TurboJPEG -PROJECT_NUMBER = 2.1.4 +PROJECT_NUMBER = 2.2 OUTPUT_DIRECTORY = doc/ USE_WINDOWS_ENCODING = NO OPTIMIZE_OUTPUT_FOR_C = YES diff --git a/fuzz/decompress.cc b/fuzz/decompress.cc index c7fcb5001..b6604224b 100644 --- a/fuzz/decompress.cc +++ b/fuzz/decompress.cc @@ -1,5 +1,5 @@ /* - * Copyright (C)2021 D. R. Commander. All Rights Reserved. + * Copyright (C)2021-2022 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -38,7 +38,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { tjhandle handle = NULL; unsigned char *dstBuf = NULL; - int width = 0, height = 0, jpegSubsamp, jpegColorspace, pfi; + int width = 0, height = 0, jpegSubsamp, jpegColorspace, jpegFlags, pfi; /* TJPF_RGB-TJPF_BGR share the same code paths, as do TJPF_RGBX-TJPF_XRGB and TJPF_RGBA-TJPF_ARGB. Thus, the pixel formats below should be the minimum necessary to achieve full coverage. */ @@ -58,8 +58,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) /* We ignore the return value of tjDecompressHeader3(), because some JPEG images may have unusual subsampling configurations that the TurboJPEG API cannot identify but can still decompress. */ - tjDecompressHeader3(handle, data, size, &width, &height, &jpegSubsamp, - &jpegColorspace); + tjDecompressHeader4(handle, data, size, &width, &height, &jpegSubsamp, + &jpegColorspace, &jpegFlags); /* Ignore 0-pixel images and images larger than 1 Megapixel, as Google's OSS-Fuzz target for libjpeg-turbo did. Casting width to (uint64_t) @@ -75,7 +75,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) if (pfi == 0) flags |= TJFLAG_BOTTOMUP | TJFLAG_FASTUPSAMPLE | TJFLAG_FASTDCT; /* Test IDCT scaling on the second iteration. */ - else if (pfi == 1) { + else if (pfi == 1 && !(jpegFlags & TJFLAG_LOSSLESS)) { w = (width + 1) / 2; h = (height + 1) / 2; } diff --git a/fuzz/decompress_yuv.cc b/fuzz/decompress_yuv.cc index d603fd819..4b2c89de6 100644 --- a/fuzz/decompress_yuv.cc +++ b/fuzz/decompress_yuv.cc @@ -1,5 +1,5 @@ /* - * Copyright (C)2021 D. R. Commander. All Rights Reserved. + * Copyright (C)2021-2022 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -38,7 +38,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { tjhandle handle = NULL; unsigned char *dstBuf = NULL, *yuvBuf = NULL; - int width = 0, height = 0, jpegSubsamp, jpegColorspace, pfi; + int width = 0, height = 0, jpegSubsamp, jpegColorspace, jpegFlags, pfi; /* TJPF_RGB-TJPF_BGR share the same code paths, as do TJPF_RGBX-TJPF_XRGB and TJPF_RGBA-TJPF_ARGB. Thus, the pixel formats below should be the minimum necessary to achieve full coverage. */ @@ -55,8 +55,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) if ((handle = tjInitDecompress()) == NULL) goto bailout; - if (tjDecompressHeader3(handle, data, size, &width, &height, &jpegSubsamp, - &jpegColorspace) < 0) + if (tjDecompressHeader4(handle, data, size, &width, &height, &jpegSubsamp, + &jpegColorspace, &jpegFlags) < 0) goto bailout; /* Ignore 0-pixel images and images larger than 1 Megapixel. Casting width @@ -72,7 +72,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) if (pfi == 0) flags |= TJFLAG_BOTTOMUP | TJFLAG_FASTUPSAMPLE | TJFLAG_FASTDCT; /* Test IDCT scaling on the second iteration. */ - else if (pfi == 1) { + else if (pfi == 1 && !(jpegFlags & TJFLAG_LOSSLESS)) { w = (width + 3) / 4; h = (height + 3) / 4; } diff --git a/java/TJBench.java b/java/TJBench.java index 4084ea545..0cc0f8865 100644 --- a/java/TJBench.java +++ b/java/TJBench.java @@ -168,11 +168,16 @@ static void decomp(byte[] srcBuf, byte[][] jpegBuf, int[] jpegSize, TJDecompressor tjd; double elapsed, elapsedDecode; int ps = TJ.getPixelSize(pf), i, iter = 0; - int scaledw = sf.getScaled(w); - int scaledh = sf.getScaled(h); - int pitch = scaledw * ps; + int scaledw, scaledh, pitch; YUVImage yuvImage = null; + if ((flags & TJ.FLAG_LOSSLESS) != 0) + sf = new TJScalingFactor(1, 1); + + scaledw = sf.getScaled(w); + scaledh = sf.getScaled(h); + pitch = scaledw * ps; + if (jpegQual > 0) qualStr = new String("_Q" + jpegQual); @@ -498,7 +503,7 @@ static void decompTest(String fileName) throws Exception { // Original image int w = 0, h = 0, ntilesw = 1, ntilesh = 1, subsamp = -1, cs = -1; // Transformed image - int tw, th, ttilew, ttileh, tntilesw, tntilesh, tsubsamp; + int tw, th, ttilew, ttileh, tntilesw, tntilesh, tsubsamp, jpegFlags; FileInputStream fis = new FileInputStream(fileName); if (fis.getChannel().size() > (long)Integer.MAX_VALUE) @@ -521,6 +526,10 @@ static void decompTest(String fileName) throws Exception { h = tjt.getHeight(); subsamp = tjt.getSubsamp(); cs = tjt.getColorspace(); + jpegFlags = tjt.getFlags(); + + if ((jpegFlags & TJ.FLAG_LOSSLESS) != 0) + sf = new TJScalingFactor(1, 1); if (quiet == 1) { System.out.println("All performance values in Mpixels/sec\n"); diff --git a/java/TJExample.java b/java/TJExample.java index 785988698..9e6edd254 100644 --- a/java/TJExample.java +++ b/java/TJExample.java @@ -1,6 +1,6 @@ /* - * Copyright (C)2011-2012, 2014-2015, 2017-2018 D. R. Commander. - * All Rights Reserved. + * Copyright (C)2011-2012, 2014-2015, 2017-2018, 2022 D. R. Commander. + * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -304,6 +304,10 @@ else if (argv[i].equalsIgnoreCase("-fastupsample")) { height = tjd.getHeight(); int inSubsamp = tjd.getSubsamp(); int inColorspace = tjd.getColorspace(); + int inFlags = tjd.getFlags(); + + if ((inFlags & TJ.FLAG_LOSSLESS) != 0) + scalingFactor = new TJScalingFactor(1, 1); System.out.println((doTransform ? "Transformed" : "Input") + " Image (jpg): " + width + " x " + height + diff --git a/java/doc/index-all.html b/java/doc/index-all.html index eddddc92b..54f5807f3 100644 --- a/java/doc/index-all.html +++ b/java/doc/index-all.html @@ -316,6 +316,13 @@

G

Returns a code (one of TJ.ERR_*) indicating the severity of the last error.
+
getFlags() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
+
+
Returns the bitwise OR of one or more of the + flags, such as + TJ.FLAG_PROGRESSIVE and + TJ.FLAG_LOSSLESS, that describe the JPEG image.
+
getGreenOffset(int) - Static method in class org.libjpegturbo.turbojpeg.TJ
For the given pixel format, returns the number of bytes that the green @@ -462,6 +469,8 @@

J

 
jpegColorspace - Variable in class org.libjpegturbo.turbojpeg.TJDecompressor
 
+
jpegFlags - Variable in class org.libjpegturbo.turbojpeg.TJDecompressor
+
 
jpegHeight - Variable in class org.libjpegturbo.turbojpeg.TJDecompressor
 
jpegSubsamp - Variable in class org.libjpegturbo.turbojpeg.TJDecompressor
diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html b/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html index 0a0d100c4..c27a8abfb 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html @@ -144,17 +144,21 @@

Field Summary

protected int -jpegHeight  +jpegFlags  protected int -jpegSubsamp  +jpegHeight  protected int -jpegWidth  +jpegSubsamp  +protected int +jpegWidth  + + protected YUVImage yuvImage  @@ -324,25 +328,34 @@

Method Summary

int +getFlags() +
Returns the bitwise OR of one or more of the + flags, such as + TJ.FLAG_PROGRESSIVE and + TJ.FLAG_LOSSLESS, that describe the JPEG image.
+ + + +int getHeight()
Returns the height of the source image (JPEG or YUV) associated with this decompressor instance.
- + byte[] getJPEGBuf()
Returns the JPEG image buffer associated with this decompressor instance.
- + int getJPEGSize()
Returns the size of the JPEG image (in bytes) associated with this decompressor instance.
- + int getScaledHeight(int desiredWidth, int desiredHeight) @@ -351,7 +364,7 @@

Method Summary

height.
- + int getScaledWidth(int desiredWidth, int desiredHeight) @@ -360,21 +373,21 @@

Method Summary

height.
- + int getSubsamp()
Returns the level of chrominance subsampling used in the source image (JPEG or YUV) associated with this decompressor instance.
- + int getWidth()
Returns the width of the source image (JPEG or YUV) associated with this decompressor instance.
- + void setSourceImage(byte[] jpegImage, int imageSize) @@ -383,7 +396,7 @@

Method Summary

jpegImage with this decompressor instance. - + void setSourceImage(YUVImage srcImage)
Associate the specified YUV planar source image with this decompressor @@ -478,12 +491,21 @@

jpegSubsamp

-
    +
    • jpegColorspace

      protected int jpegColorspace
    + + + +
      +
    • +

      jpegFlags

      +
      protected int jpegFlags
      +
    • +
@@ -657,6 +679,21 @@

getColorspace

with this decompressor instance. + + + +
    +
  • +

    getFlags

    +
    public int getFlags()
    +
    Returns the bitwise OR of one or more of the + flags, such as + TJ.FLAG_PROGRESSIVE and + TJ.FLAG_LOSSLESS, that describe the JPEG image.
    +
    Returns:
    the bitwise OR of one or more of the + flags that describe the JPEG image.
    +
  • +
diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJTransformer.html b/java/doc/org/libjpegturbo/turbojpeg/TJTransformer.html index 8bad93f58..69923f333 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJTransformer.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJTransformer.html @@ -125,7 +125,7 @@

Field Summary

Fields inherited from class org.libjpegturbo.turbojpeg.TJDecompressor

-handle, jpegBuf, jpegBufSize, jpegColorspace, jpegHeight, jpegSubsamp, jpegWidth, yuvImage +handle, jpegBuf, jpegBufSize, jpegColorspace, jpegFlags, jpegHeight, jpegSubsamp, jpegWidth, yuvImage @@ -206,7 +206,7 @@

Method Summary

Methods inherited from class org.libjpegturbo.turbojpeg.TJDecompressor

-close, decompress, decompress, decompress, decompress, decompress, decompressToYUV, decompressToYUV, decompressToYUV, finalize, getColorspace, getHeight, getJPEGBuf, getJPEGSize, getScaledHeight, getScaledWidth, getSubsamp, getWidth, setSourceImage, setSourceImage +close, decompress, decompress, decompress, decompress, decompress, decompressToYUV, decompressToYUV, decompressToYUV, finalize, getColorspace, getFlags, getHeight, getJPEGBuf, getJPEGSize, getScaledHeight, getScaledWidth, getSubsamp, getWidth, setSourceImage, setSourceImage
@@ -464,7 +464,7 @@

-

The uncompressed source/destination image is stored in bottom-up (Windows, OpenGL) order, not top-down (X11) order.

+

Rows in the packed-pixel source/destination image are stored in bottom-up (Windows, OpenGL) order rather than in top-down (X11) order.

@@ -480,8 +480,8 @@

-

Use the fastest DCT/IDCT algorithm available in the underlying codec.

-

The default if this flag is not specified is implementation-specific. For example, the implementation of TurboJPEG for libjpeg[-turbo] uses the fast algorithm by default when compressing, because this has been shown to have only a very slight effect on accuracy, but it uses the accurate algorithm when decompressing, because this has been shown to have a larger effect.

+

Use the fastest DCT/IDCT algorithm available.

+

The default if this flag is not specified is implementation-specific. For example, the implementation of the TurboJPEG API in libjpeg-turbo uses the fast algorithm by default when compressing, because this has been shown to have only a very slight effect on accuracy, but it uses the accurate algorithm when decompressing, because this has been shown to have a larger effect.

@@ -497,7 +497,7 @@

-

When decompressing an image that was compressed using chrominance subsampling, use the fastest chrominance upsampling algorithm available in the underlying codec.

+

When decompressing an image that was compressed using chrominance subsampling, use the fastest chrominance upsampling algorithm available.

The default is to use smooth upsampling, which creates a smooth transition between neighboring chrominance components in order to reduce upsampling artifacts in the decompressed image.

@@ -531,8 +531,8 @@

-

Disable buffer (re)allocation.

-

If passed to one of the JPEG compression or transform functions, this flag will cause those functions to generate an error if the JPEG image buffer is invalid or too small rather than attempting to allocate or reallocate that buffer. This reproduces the behavior of earlier versions of TurboJPEG.

+

Disable JPEG buffer (re)allocation.

+

If passed to one of the JPEG compression or transform functions, this flag will cause those functions to generate an error if the JPEG destination buffer is invalid or too small, rather than attempt to allocate or reallocate that buffer.

@@ -565,7 +565,7 @@

-

Immediately discontinue the current compression/decompression/transform operation if the underlying codec throws a warning (non-fatal error).

+

Immediately discontinue the current compression/decompression/transform operation if a warning (non-fatal error) occurs.

The default behavior is to allow the operation to complete unless a fatal error is encountered.

@@ -586,7 +586,7 @@

-

Pad the given width to the nearest 32-bit boundary.

+

Pad the given width to the nearest multiple of 4.

@@ -633,7 +633,7 @@

-

This option will prevent tjTransform() from copying any extra markers (including EXIF and ICC profile data) from the source image to the output image.

+

This option will prevent tjTransform() from copying any extra markers (including EXIF and ICC profile data) from the source image to the destination image.

@@ -666,7 +666,7 @@

-

This option will discard the color data in the input image and produce a grayscale output image.

+

This option will discard the color data in the source image and produce a grayscale destination image.

@@ -682,7 +682,8 @@

-

This option will prevent tjTransform() from outputting a JPEG image for this particular transform (this can be used in conjunction with a custom filter to capture the transformed DCT coefficients without transcoding them.)

+

This option will prevent tjTransform() from outputting a JPEG image for this particular transform.

+

(This can be used in conjunction with a custom filter to capture the transformed DCT coefficients without transcoding them.)

@@ -715,7 +716,7 @@

-

This option will enable progressive entropy coding in the output image generated by this particular transform.

+

This option will enable progressive entropy coding in the JPEG image generated by this particular transform.

Progressive entropy coding will generally improve compression relative to baseline entropy coding (the default), but it will reduce compression and decompression performance considerably.

@@ -785,19 +786,19 @@

EnumeratorTJCS_RGB 

RGB colorspace.

-

When compressing the JPEG image, the R, G, and B components in the source image are reordered into image planes, but no colorspace conversion or subsampling is performed. RGB JPEG images can be decompressed to any of the extended RGB pixel formats or grayscale, but they cannot be decompressed to YUV images.

+

When compressing the JPEG image, the R, G, and B components in the source image are reordered into image planes, but no colorspace conversion or subsampling is performed. RGB JPEG images can be decompressed to packed-pixel images with any of the extended RGB or grayscale pixel formats, but they cannot be decompressed to planar YUV images.

TJCS_YCbCr 

YCbCr colorspace.

-

YCbCr is not an absolute colorspace but rather a mathematical transformation of RGB designed solely for storage and transmission. YCbCr images must be converted to RGB before they can actually be displayed. In the YCbCr colorspace, the Y (luminance) component represents the black & white portion of the original image, and the Cb and Cr (chrominance) components represent the color portion of the original image. Originally, the analog equivalent of this transformation allowed the same signal to drive both black & white and color televisions, but JPEG images use YCbCr primarily because it allows the color data to be optionally subsampled for the purposes of reducing bandwidth or disk space. YCbCr is the most common JPEG colorspace, and YCbCr JPEG images can be compressed from and decompressed to any of the extended RGB pixel formats or grayscale, or they can be decompressed to YUV planar images.

+

YCbCr is not an absolute colorspace but rather a mathematical transformation of RGB designed solely for storage and transmission. YCbCr images must be converted to RGB before they can actually be displayed. In the YCbCr colorspace, the Y (luminance) component represents the black & white portion of the original image, and the Cb and Cr (chrominance) components represent the color portion of the original image. Originally, the analog equivalent of this transformation allowed the same signal to drive both black & white and color televisions, but JPEG images use YCbCr primarily because it allows the color data to be optionally subsampled for the purposes of reducing network or disk usage. YCbCr is the most common JPEG colorspace, and YCbCr JPEG images can be compressed from and decompressed to packed-pixel images with any of the extended RGB or grayscale pixel formats. YCbCr JPEG images can also be compressed from and decompressed to planar YUV images.

TJCS_GRAY 

Grayscale colorspace.

-

The JPEG image retains only the luminance data (Y component), and any color data from the source image is discarded. Grayscale JPEG images can be compressed from and decompressed to any of the extended RGB pixel formats or grayscale, or they can be decompressed to YUV planar images.

+

The JPEG image retains only the luminance data (Y component), and any color data from the source image is discarded. Grayscale JPEG images can be compressed from and decompressed to packed-pixel images with any of the extended RGB or grayscale pixel formats, or they can be compressed from and decompressed to planar YUV images.

TJCS_CMYK 

CMYK colorspace.

-

When compressing the JPEG image, the C, M, Y, and K components in the source image are reordered into image planes, but no colorspace conversion or subsampling is performed. CMYK JPEG images can only be decompressed to CMYK pixels.

+

When compressing the JPEG image, the C, M, Y, and K components in the source image are reordered into image planes, but no colorspace conversion or subsampling is performed. CMYK JPEG images can only be decompressed to packed-pixel images with the CMYK pixel format.

TJCS_YCCK 

YCCK colorspace.

-

YCCK (AKA "YCbCrK") is not an absolute colorspace but rather a mathematical transformation of CMYK designed solely for storage and transmission. It is to CMYK as YCbCr is to RGB. CMYK pixels can be reversibly transformed into YCCK, and as with YCbCr, the chrominance components in the YCCK pixels can be subsampled without incurring major perceptual loss. YCCK JPEG images can only be compressed from and decompressed to CMYK pixels.

+

YCCK (AKA "YCbCrK") is not an absolute colorspace but rather a mathematical transformation of CMYK designed solely for storage and transmission. It is to CMYK as YCbCr is to RGB. CMYK pixels can be reversibly transformed into YCCK, and as with YCbCr, the chrominance components in the YCCK pixels can be subsampled without incurring major perceptual loss. YCCK JPEG images can only be compressed from and decompressed to packed-pixel images with the CMYK pixel format.

@@ -817,7 +818,7 @@

-EnumeratorTJERR_WARNING 

The error was non-fatal and recoverable, but the image may still be corrupt.

+EnumeratorTJERR_WARNING 

The error was non-fatal and recoverable, but the destination image may still be corrupt.

TJERR_FATAL 

The error was fatal and non-recoverable.

@@ -873,10 +874,10 @@

TJPF_XRGB, except that when decompressing, the X component is guaranteed to be 0xFF, which can be interpreted as an opaque alpha channel.

TJPF_CMYK 

CMYK pixel format.

-

Unlike RGB, which is an additive color model used primarily for display, CMYK (Cyan/Magenta/Yellow/Key) is a subtractive color model used primarily for printing. In the CMYK color model, the value of each color component typically corresponds to an amount of cyan, magenta, yellow, or black ink that is applied to a white background. In order to convert between CMYK and RGB, it is necessary to use a color management system (CMS.) A CMS will attempt to map colors within the printer's gamut to perceptually similar colors in the display's gamut and vice versa, but the mapping is typically not 1:1 or reversible, nor can it be defined with a simple formula. Thus, such a conversion is out of scope for a codec library. However, the TurboJPEG API allows for compressing CMYK pixels into a YCCK JPEG image (see TJCS_YCCK) and decompressing YCCK JPEG images into CMYK pixels.

+

Unlike RGB, which is an additive color model used primarily for display, CMYK (Cyan/Magenta/Yellow/Key) is a subtractive color model used primarily for printing. In the CMYK color model, the value of each color component typically corresponds to an amount of cyan, magenta, yellow, or black ink that is applied to a white background. In order to convert between CMYK and RGB, it is necessary to use a color management system (CMS.) A CMS will attempt to map colors within the printer's gamut to perceptually similar colors in the display's gamut and vice versa, but the mapping is typically not 1:1 or reversible, nor can it be defined with a simple formula. Thus, such a conversion is out of scope for a codec library. However, the TurboJPEG API allows for compressing packed-pixel CMYK images into YCCK JPEG images (see TJCS_YCCK) and decompressing YCCK JPEG images into packed-pixel CMYK images.

TJPF_UNKNOWN 

Unknown pixel format.

-

Currently this is only used by tjLoadImage().

+

Currently this is only used by tjLoadImage().

@@ -895,7 +896,7 @@

Chrominance subsampling options.

-

When pixels are converted from RGB to YCbCr (see TJCS_YCbCr) or from CMYK to YCCK (see TJCS_YCCK) as part of the JPEG compression process, some of the Cb and Cr (chrominance) components can be discarded or averaged together to produce a smaller image with little perceptible loss of image clarity (the human eye is more sensitive to small changes in brightness than to small changes in color.) This is called "chrominance subsampling".

+

When pixels are converted from RGB to YCbCr (see TJCS_YCbCr) or from CMYK to YCCK (see TJCS_YCCK) as part of the JPEG compression process, some of the Cb and Cr (chrominance) components can be discarded or averaged together to produce a smaller image with little perceptible loss of image clarity. (The human eye is more sensitive to small changes in brightness than to small changes in color.) This is called "chrominance subsampling".

- + @@ -1466,17 +1467,17 @@

-

Decode a YUV planar image into an RGB or grayscale image.

-

This function uses the accelerated color conversion routines in the underlying codec but does not execute any of the other steps in the JPEG decompression process.

+

Decode a unified planar YUV image into a packed-pixel RGB or grayscale image.

+

This function performs color conversion (which is accelerated in the libjpeg-turbo implementation) but does not execute any of the other steps in the JPEG decompression process.

Parameters

Enumerator
TJSAMP_444 

4:4:4 chrominance subsampling (no chrominance subsampling).

The JPEG or YUV image will contain one chrominance component for every pixel in the source image.

@@ -977,8 +978,8 @@

-

Allocate an image buffer for use with TurboJPEG.

-

You should always use this function to allocate the JPEG destination buffer(s) for the compression and transform functions unless you are disabling automatic buffer (re)allocation (by setting TJFLAG_NOREALLOC.)

+

Allocate a byte buffer for use with TurboJPEG.

+

You should always use this function to allocate the JPEG destination buffer(s) for the compression and transform functions unless you are disabling automatic buffer (re)allocation (by setting TJFLAG_NOREALLOC.)

Parameters
@@ -986,7 +987,7 @@

Returns
a pointer to a newly-allocated buffer with the specified number of bytes.
-
See also
tjFree()
+
See also
tjFree()
@@ -1023,7 +1024,7 @@

The maximum size of the buffer (in bytes) required to hold a JPEG image with the given parameters.

-

The number of bytes returned by this function is larger than the size of the uncompressed source image. The reason for this is that the JPEG format uses 16-bit coefficients, and it is thus possible for a very high-quality JPEG image with very high-frequency content to expand rather than compress when converted to the JPEG format. Such images represent a very rare corner case, but since there is no way to predict the size of a JPEG image prior to compression, the corner case has to be handled.

+

The number of bytes returned by this function is larger than the size of the uncompressed source image. The reason for this is that the JPEG format uses 16-bit coefficients, so it is possible for a very high-quality source image with very high-frequency content to expand rather than compress when converted to the JPEG format. Such images represent very rare corner cases, but since there is no way to predict the size of a JPEG image prior to compression, the corner cases have to be handled.

Parameters

bytesthe number of bytes to allocate
@@ -1036,8 +1037,8 @@

-

◆ tjBufSizeYUV2()

+ +

◆ tjBufSizeYUV2()

@@ -1052,7 +1053,7 @@

- + @@ -1074,11 +1075,11 @@

-

The size of the buffer (in bytes) required to hold a YUV planar image with the given parameters.

+

The size of the buffer (in bytes) required to hold a unified planar YUV image with the given parameters.

Parameters

widthwidth (in pixels) of the image
int pad, align,
- +
widthwidth (in pixels) of the image
padthe width of each line in each plane of the image is padded to the nearest multiple of this number of bytes (must be a power of 2.)
alignrow alignment (in bytes) of the image (must be a power of 2.) Setting this parameter to n specifies that each row in each plane of the image will be padded to the nearest multiple of n bytes (1 = unpadded.)
heightheight (in pixels) of the image
subsamplevel of chrominance subsampling in the image (see Chrominance subsampling options.)
@@ -1168,22 +1169,22 @@

-

Compress an RGB, grayscale, or CMYK image into a JPEG image.

+

Compress a packed-pixel RGB, grayscale, or CMYK image into a JPEG image.

Parameters
- + - + - - +If you choose option 1, then *jpegSize should be set to the size of your pre-allocated buffer. In any case, unless you have set TJFLAG_NOREALLOC, you should always check *jpegBuf upon return from this function, as it may have changed. + @@ -1194,8 +1195,8 @@

-

◆ tjCompressFromYUV()

+ +

◆ tjCompressFromYUV()

@@ -1222,7 +1223,7 @@

- + @@ -1268,22 +1269,22 @@

-

Compress a YUV planar image into a JPEG image.

+

Compress a unified planar YUV image into a JPEG image.

Parameters

handlea handle to a TurboJPEG compressor or transformer instance
srcBufpointer to an image buffer containing RGB, grayscale, or CMYK pixels to be compressed
srcBufpointer to a buffer containing a packed-pixel RGB, grayscale, or CMYK source image to be compressed
widthwidth (in pixels) of the source image
pitchbytes per line in the source image. Normally, this should be width * tjPixelSize[pixelFormat] if the image is unpadded, or TJPAD(width * tjPixelSize[pixelFormat]) if each line of the image is padded to the nearest 32-bit boundary, as is the case for Windows bitmaps. You can also be clever and use this parameter to skip lines, etc. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
pitchbytes per row in the source image. Normally this should be width * tjPixelSize[pixelFormat], if the image is unpadded, or TJPAD(width * tjPixelSize[pixelFormat]) if each row of the image is padded to the nearest multiple of 4 bytes, as is the case for Windows bitmaps. You can also be clever and use this parameter to skip rows, etc. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
heightheight (in pixels) of the source image
pixelFormatpixel format of the source image (see Pixel formats.)
jpegBufaddress of a pointer to an image buffer that will receive the JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to accommodate the size of the JPEG image. Thus, you can choose to:
    -
  1. pre-allocate the JPEG buffer with an arbitrary size using tjAlloc() and let TurboJPEG grow the buffer as needed,
  2. +
jpegBufaddress of a pointer to a byte buffer that will receive the JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to accommodate the size of the JPEG image. Thus, you can choose to:
    +
  1. pre-allocate the JPEG buffer with an arbitrary size using tjAlloc() and let TurboJPEG grow the buffer as needed,
  2. set *jpegBuf to NULL to tell TurboJPEG to allocate the buffer for you, or
  3. -
  4. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize(). This should ensure that the buffer never has to be re-allocated (setting TJFLAG_NOREALLOC guarantees that it won't be.)
  5. +
  6. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize(). This should ensure that the buffer never has to be re-allocated. (Setting TJFLAG_NOREALLOC guarantees that it won't be.)
-If you choose option 1, *jpegSize should be set to the size of your pre-allocated buffer. In any case, unless you have set TJFLAG_NOREALLOC, you should always check *jpegBuf upon return from this function, as it may have changed.
jpegSizepointer to an unsigned long variable that holds the size of the JPEG image buffer. If *jpegBuf points to a pre-allocated buffer, then *jpegSize should be set to the size of the buffer. Upon return, *jpegSize will contain the size of the JPEG image (in bytes.) If *jpegBuf points to a JPEG image buffer that is being reused from a previous call to one of the JPEG compression functions, then *jpegSize is ignored.
jpegSizepointer to an unsigned long variable that holds the size of the JPEG buffer. If *jpegBuf points to a pre-allocated buffer, then *jpegSize should be set to the size of the buffer. Upon return, *jpegSize will contain the size of the JPEG image (in bytes.) If *jpegBuf points to a JPEG buffer that is being reused from a previous call to one of the JPEG compression functions, then *jpegSize is ignored.
jpegSubsampthe level of chrominance subsampling to be used when generating the JPEG image (see Chrominance subsampling options.)
jpegQualthe image quality of the generated JPEG image (1 = worst, 100 = best)
flagsthe bitwise OR of one or more of the flags
int pad, align,
- - - - + + + + - - +If you choose option 1, then *jpegSize should be set to the size of your pre-allocated buffer. In any case, unless you have set TJFLAG_NOREALLOC, you should always check *jpegBuf upon return from this function, as it may have changed. +
handlea handle to a TurboJPEG compressor or transformer instance
srcBufpointer to an image buffer containing a YUV planar image to be compressed. The size of this buffer should match the value returned by tjBufSizeYUV2() for the given image width, height, padding, and level of chrominance subsampling. The Y, U (Cb), and V (Cr) image planes should be stored sequentially in the source buffer (refer to YUV Image Format Notes.)
widthwidth (in pixels) of the source image. If the width is not an even multiple of the MCU block width (see tjMCUWidth), then an intermediate buffer copy will be performed within TurboJPEG.
padthe line padding used in the source image (must be a power of 2.) For instance, if each line in each plane of the YUV image is padded to the nearest multiple of 4 bytes, then pad should be set to 4.
heightheight (in pixels) of the source image. If the height is not an even multiple of the MCU block height (see tjMCUHeight), then an intermediate buffer copy will be performed within TurboJPEG.
srcBufpointer to a buffer containing a unified planar YUV source image to be compressed. The size of this buffer should match the value returned by tjBufSizeYUV2() for the given image width, height, row alignment, and level of chrominance subsampling. The Y, U (Cb), and V (Cr) image planes should be stored sequentially in the buffer. (Refer to YUV Image Format Notes.)
widthwidth (in pixels) of the source image. If the width is not an even multiple of the MCU block width (see tjMCUWidth), then an intermediate buffer copy will be performed.
alignrow alignment (in bytes) of the source image (must be a power of 2.) Setting this parameter to n indicates that each row in each plane of the source image is padded to the nearest multiple of n bytes (1 = unpadded.)
heightheight (in pixels) of the source image. If the height is not an even multiple of the MCU block height (see tjMCUHeight), then an intermediate buffer copy will be performed.
subsampthe level of chrominance subsampling used in the source image (see Chrominance subsampling options.)
jpegBufaddress of a pointer to an image buffer that will receive the JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to accommodate the size of the JPEG image. Thus, you can choose to:
    -
  1. pre-allocate the JPEG buffer with an arbitrary size using tjAlloc() and let TurboJPEG grow the buffer as needed,
  2. +
jpegBufaddress of a pointer to a byte buffer that will receive the JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to accommodate the size of the JPEG image. Thus, you can choose to:
    +
  1. pre-allocate the JPEG buffer with an arbitrary size using tjAlloc() and let TurboJPEG grow the buffer as needed,
  2. set *jpegBuf to NULL to tell TurboJPEG to allocate the buffer for you, or
  3. -
  4. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize(). This should ensure that the buffer never has to be re-allocated (setting TJFLAG_NOREALLOC guarantees that it won't be.)
  5. +
  6. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize(). This should ensure that the buffer never has to be re-allocated. (Setting TJFLAG_NOREALLOC guarantees that it won't be.)
-If you choose option 1, *jpegSize should be set to the size of your pre-allocated buffer. In any case, unless you have set TJFLAG_NOREALLOC, you should always check *jpegBuf upon return from this function, as it may have changed.
jpegSizepointer to an unsigned long variable that holds the size of the JPEG image buffer. If *jpegBuf points to a pre-allocated buffer, then *jpegSize should be set to the size of the buffer. Upon return, *jpegSize will contain the size of the JPEG image (in bytes.) If *jpegBuf points to a JPEG image buffer that is being reused from a previous call to one of the JPEG compression functions, then *jpegSize is ignored.
jpegSizepointer to an unsigned long variable that holds the size of the JPEG buffer. If *jpegBuf points to a pre-allocated buffer, then *jpegSize should be set to the size of the buffer. Upon return, *jpegSize will contain the size of the JPEG image (in bytes.) If *jpegBuf points to a JPEG buffer that is being reused from a previous call to one of the JPEG compression functions, then *jpegSize is ignored.
jpegQualthe image quality of the generated JPEG image (1 = worst, 100 = best)
flagsthe bitwise OR of one or more of the flags
@@ -1371,18 +1372,18 @@

Parameters
- - - - + + + + - - +If you choose option 1, then *jpegSize should be set to the size of your pre-allocated buffer. In any case, unless you have set TJFLAG_NOREALLOC, you should always check *jpegBuf upon return from this function, as it may have changed. +
handlea handle to a TurboJPEG compressor or transformer instance
srcPlanesan array of pointers to Y, U (Cb), and V (Cr) image planes (or just a Y plane, if compressing a grayscale image) that contain a YUV image to be compressed. These planes can be contiguous or non-contiguous in memory. The size of each plane should match the value returned by tjPlaneSizeYUV() for the given image width, height, strides, and level of chrominance subsampling. Refer to YUV Image Format Notes for more details.
widthwidth (in pixels) of the source image. If the width is not an even multiple of the MCU block width (see tjMCUWidth), then an intermediate buffer copy will be performed within TurboJPEG.
stridesan array of integers, each specifying the number of bytes per line in the corresponding plane of the YUV source image. Setting the stride for any plane to 0 is the same as setting it to the plane width (see YUV Image Format Notes.) If strides is NULL, then the strides for all planes will be set to their respective plane widths. You can adjust the strides in order to specify an arbitrary amount of line padding in each plane or to create a JPEG image from a subregion of a larger YUV planar image.
heightheight (in pixels) of the source image. If the height is not an even multiple of the MCU block height (see tjMCUHeight), then an intermediate buffer copy will be performed within TurboJPEG.
srcPlanesan array of pointers to Y, U (Cb), and V (Cr) image planes (or just a Y plane, if compressing a grayscale image) that contain a YUV source image to be compressed. These planes can be contiguous or non-contiguous in memory. The size of each plane should match the value returned by tjPlaneSizeYUV() for the given image width, height, strides, and level of chrominance subsampling. Refer to YUV Image Format Notes for more details.
widthwidth (in pixels) of the source image. If the width is not an even multiple of the MCU block width (see tjMCUWidth), then an intermediate buffer copy will be performed.
stridesan array of integers, each specifying the number of bytes per row in the corresponding plane of the YUV source image. Setting the stride for any plane to 0 is the same as setting it to the plane width (see YUV Image Format Notes.) If strides is NULL, then the strides for all planes will be set to their respective plane widths. You can adjust the strides in order to specify an arbitrary amount of row padding in each plane or to create a JPEG image from a subregion of a larger planar YUV image.
heightheight (in pixels) of the source image. If the height is not an even multiple of the MCU block height (see tjMCUHeight), then an intermediate buffer copy will be performed.
subsampthe level of chrominance subsampling used in the source image (see Chrominance subsampling options.)
jpegBufaddress of a pointer to an image buffer that will receive the JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to accommodate the size of the JPEG image. Thus, you can choose to:
    -
  1. pre-allocate the JPEG buffer with an arbitrary size using tjAlloc() and let TurboJPEG grow the buffer as needed,
  2. +
jpegBufaddress of a pointer to a byte buffer that will receive the JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to accommodate the size of the JPEG image. Thus, you can choose to:
    +
  1. pre-allocate the JPEG buffer with an arbitrary size using tjAlloc() and let TurboJPEG grow the buffer as needed,
  2. set *jpegBuf to NULL to tell TurboJPEG to allocate the buffer for you, or
  3. -
  4. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize(). This should ensure that the buffer never has to be re-allocated (setting TJFLAG_NOREALLOC guarantees that it won't be.)
  5. +
  6. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize(). This should ensure that the buffer never has to be re-allocated. (Setting TJFLAG_NOREALLOC guarantees that it won't be.)
-If you choose option 1, *jpegSize should be set to the size of your pre-allocated buffer. In any case, unless you have set TJFLAG_NOREALLOC, you should always check *jpegBuf upon return from this function, as it may have changed.
jpegSizepointer to an unsigned long variable that holds the size of the JPEG image buffer. If *jpegBuf points to a pre-allocated buffer, then *jpegSize should be set to the size of the buffer. Upon return, *jpegSize will contain the size of the JPEG image (in bytes.) If *jpegBuf points to a JPEG image buffer that is being reused from a previous call to one of the JPEG compression functions, then *jpegSize is ignored.
jpegSizepointer to an unsigned long variable that holds the size of the JPEG buffer. If *jpegBuf points to a pre-allocated buffer, then *jpegSize should be set to the size of the buffer. Upon return, *jpegSize will contain the size of the JPEG image (in bytes.) If *jpegBuf points to a JPEG buffer that is being reused from a previous call to one of the JPEG compression functions, then *jpegSize is ignored.
jpegQualthe image quality of the generated JPEG image (1 = worst, 100 = best)
flagsthe bitwise OR of one or more of the flags
@@ -1392,8 +1393,8 @@

-

◆ tjDecodeYUV()

+ +

◆ tjDecodeYUV()

@@ -1414,7 +1415,7 @@

int pad, align,
- - + + - + - + @@ -1561,17 +1562,17 @@

-

Decode a set of Y, U (Cb), and V (Cr) image planes into an RGB or grayscale image.

-

This function uses the accelerated color conversion routines in the underlying codec but does not execute any of the other steps in the JPEG decompression process.

+

Decode a set of Y, U (Cb), and V (Cr) image planes into a packed-pixel RGB or grayscale image.

+

This function performs color conversion (which is accelerated in the libjpeg-turbo implementation) but does not execute any of the other steps in the JPEG decompression process.

Parameters

handlea handle to a TurboJPEG decompressor or transformer instance
srcBufpointer to an image buffer containing a YUV planar image to be decoded. The size of this buffer should match the value returned by tjBufSizeYUV2() for the given image width, height, padding, and level of chrominance subsampling. The Y, U (Cb), and V (Cr) image planes should be stored sequentially in the source buffer (refer to YUV Image Format Notes.)
padUse this parameter to specify that the width of each line in each plane of the YUV source image is padded to the nearest multiple of this number of bytes (must be a power of 2.)
srcBufpointer to a buffer containing a unified planar YUV source image to be decoded. The size of this buffer should match the value returned by tjBufSizeYUV2() for the given image width, height, row alignment, and level of chrominance subsampling. The Y, U (Cb), and V (Cr) image planes should be stored sequentially in the source buffer. (Refer to YUV Image Format Notes.)
alignrow alignment (in bytes) of the YUV source image (must be a power of 2.) Setting this parameter to n indicates that each row in each plane of the YUV source image is padded to the nearest multiple of n bytes (1 = unpadded.)
subsampthe level of chrominance subsampling used in the YUV source image (see Chrominance subsampling options.)
dstBufpointer to an image buffer that will receive the decoded image. This buffer should normally be pitch * height bytes in size, but the dstBuf pointer can also be used to decode into a specific region of a larger buffer.
dstBufpointer to a buffer that will receive the packed-pixel decoded image. This buffer should normally be pitch * height bytes in size, but the dstBuf pointer can also be used to decode into a specific region of a larger buffer.
widthwidth (in pixels) of the source and destination images
pitchbytes per line in the destination image. Normally, this should be width * tjPixelSize[pixelFormat] if the destination image is unpadded, or TJPAD(width * tjPixelSize[pixelFormat]) if each line of the destination image should be padded to the nearest 32-bit boundary, as is the case for Windows bitmaps. You can also be clever and use the pitch parameter to skip lines, etc. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
pitchbytes per row in the destination image. Normally this should be set to width * tjPixelSize[pixelFormat], if the destination image should be unpadded, or TJPAD(width * tjPixelSize[pixelFormat]) if each row of the destination image should be padded to the nearest multiple of 4 bytes, as is the case for Windows bitmaps. You can also be clever and use the pitch parameter to skip rows, etc. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
heightheight (in pixels) of the source and destination images
pixelFormatpixel format of the destination image (see Pixel formats.)
flagsthe bitwise OR of one or more of the flags
- + - + - + @@ -1650,15 +1651,15 @@

-

Decompress a JPEG image to an RGB, grayscale, or CMYK image.

+

Decompress a JPEG image into a packed-pixel RGB, grayscale, or CMYK image.

Parameters

handlea handle to a TurboJPEG decompressor or transformer instance
srcPlanesan array of pointers to Y, U (Cb), and V (Cr) image planes (or just a Y plane, if decoding a grayscale image) that contain a YUV image to be decoded. These planes can be contiguous or non-contiguous in memory. The size of each plane should match the value returned by tjPlaneSizeYUV() for the given image width, height, strides, and level of chrominance subsampling. Refer to YUV Image Format Notes for more details.
stridesan array of integers, each specifying the number of bytes per line in the corresponding plane of the YUV source image. Setting the stride for any plane to 0 is the same as setting it to the plane width (see YUV Image Format Notes.) If strides is NULL, then the strides for all planes will be set to their respective plane widths. You can adjust the strides in order to specify an arbitrary amount of line padding in each plane or to decode a subregion of a larger YUV planar image.
stridesan array of integers, each specifying the number of bytes per row in the corresponding plane of the YUV source image. Setting the stride for any plane to 0 is the same as setting it to the plane width (see YUV Image Format Notes.) If strides is NULL, then the strides for all planes will be set to their respective plane widths. You can adjust the strides in order to specify an arbitrary amount of row padding in each plane or to decode a subregion of a larger planar YUV image.
subsampthe level of chrominance subsampling used in the YUV source image (see Chrominance subsampling options.)
dstBufpointer to an image buffer that will receive the decoded image. This buffer should normally be pitch * height bytes in size, but the dstBuf pointer can also be used to decode into a specific region of a larger buffer.
dstBufpointer to a buffer that will receive the packed-pixel decoded image. This buffer should normally be pitch * height bytes in size, but the dstBuf pointer can also be used to decode into a specific region of a larger buffer.
widthwidth (in pixels) of the source and destination images
pitchbytes per line in the destination image. Normally, this should be width * tjPixelSize[pixelFormat] if the destination image is unpadded, or TJPAD(width * tjPixelSize[pixelFormat]) if each line of the destination image should be padded to the nearest 32-bit boundary, as is the case for Windows bitmaps. You can also be clever and use the pitch parameter to skip lines, etc. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
pitchbytes per row in the destination image. Normally this should be set to width * tjPixelSize[pixelFormat], if the destination image should be unpadded, or TJPAD(width * tjPixelSize[pixelFormat]) if each row of the destination image should be padded to the nearest multiple of 4 bytes, as is the case for Windows bitmaps. You can also be clever and use the pitch parameter to skip rows, etc. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
heightheight (in pixels) of the source and destination images
pixelFormatpixel format of the destination image (see Pixel formats.)
flagsthe bitwise OR of one or more of the flags
- + - + - + @@ -1729,7 +1730,7 @@

Parameters

handlea handle to a TurboJPEG decompressor or transformer instance
jpegBufpointer to a buffer containing the JPEG image to decompress
jpegBufpointer to a byte buffer containing the JPEG image to decompress
jpegSizesize of the JPEG image (in bytes)
dstBufpointer to an image buffer that will receive the decompressed image. This buffer should normally be pitch * scaledHeight bytes in size, where scaledHeight can be determined by calling TJSCALED() with the JPEG image height and one of the scaling factors returned by tjGetScalingFactors(). The dstBuf pointer may also be used to decompress into a specific region of a larger buffer.
dstBufpointer to a buffer that will receive the packed-pixel decompressed image. This buffer should normally be pitch * scaledHeight bytes in size, where scaledHeight can be determined by calling TJSCALED() with the JPEG image height and one of the scaling factors returned by tjGetScalingFactors(). The dstBuf pointer may also be used to decompress into a specific region of a larger buffer.
widthdesired width (in pixels) of the destination image. If this is different than the width of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired width. If width is set to 0, then only the height will be considered when determining the scaled image size.
pitchbytes per line in the destination image. Normally, this is scaledWidth * tjPixelSize[pixelFormat] if the decompressed image is unpadded, else TJPAD(scaledWidth * tjPixelSize[pixelFormat]) if each line of the decompressed image is padded to the nearest 32-bit boundary, as is the case for Windows bitmaps. (NOTE: scaledWidth can be determined by calling TJSCALED() with the JPEG image width and one of the scaling factors returned by tjGetScalingFactors().) You can also be clever and use the pitch parameter to skip lines, etc. Setting this parameter to 0 is the equivalent of setting it to scaledWidth * tjPixelSize[pixelFormat].
pitchbytes per row in the destination image. Normally this should be set to scaledWidth * tjPixelSize[pixelFormat], if the destination image should be unpadded, or TJPAD(scaledWidth * tjPixelSize[pixelFormat]) if each row of the destination image should be padded to the nearest multiple of 4 bytes, as is the case for Windows bitmaps. (NOTE: scaledWidth can be determined by calling TJSCALED() with the JPEG image width and one of the scaling factors returned by tjGetScalingFactors().) You can also be clever and use the pitch parameter to skip rows, etc. Setting this parameter to 0 is the equivalent of setting it to scaledWidth * tjPixelSize[pixelFormat].
heightdesired height (in pixels) of the destination image. If this is different than the height of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired height. If height is set to 0, then only the width will be considered when determining the scaled image size.
pixelFormatpixel format of the destination image (see Pixel formats.)
flagsthe bitwise OR of one or more of the flags
- + @@ -1742,8 +1743,8 @@

-

◆ tjDecompressToYUV2()

+ +

◆ tjDecompressToYUV2()

@@ -1782,7 +1783,7 @@

- + @@ -1804,17 +1805,17 @@

-

Decompress a JPEG image to a YUV planar image.

-

This function performs JPEG decompression but leaves out the color conversion step, so a planar YUV image is generated instead of an RGB image.

+

Decompress a JPEG image into a unified planar YUV image.

+

This function performs JPEG decompression but leaves out the color conversion step, so a planar YUV image is generated instead of a packed-pixel image.

Parameters

handlea handle to a TurboJPEG decompressor or transformer instance
jpegBufpointer to a buffer containing a JPEG image or an "abbreviated table specification" (AKA "tables-only") datastream. Passing a tables-only datastream to this function primes the decompressor with quantization and Huffman tables that can be used when decompressing subsequent "abbreviated image" datastreams. This is useful, for instance, when decompressing video streams in which all frames share the same quantization and Huffman tables.
jpegBufpointer to a byte buffer containing a JPEG image or an "abbreviated table specification" (AKA "tables-only") datastream. Passing a tables-only datastream to this function primes the decompressor with quantization and Huffman tables that can be used when decompressing subsequent "abbreviated image" datastreams. This is useful, for instance, when decompressing video streams in which all frames share the same quantization and Huffman tables.
jpegSizesize of the JPEG image or tables-only datastream (in bytes)
widthpointer to an integer variable that will receive the width (in pixels) of the JPEG image. If jpegBuf points to a tables-only datastream, then width is ignored.
heightpointer to an integer variable that will receive the height (in pixels) of the JPEG image. If jpegBuf points to a tables-only datastream, then height is ignored.
int pad, align,
- + - - - - + + + +
handlea handle to a TurboJPEG decompressor or transformer instance
jpegBufpointer to a buffer containing the JPEG image to decompress
jpegBufpointer to a byte buffer containing the JPEG image to decompress
jpegSizesize of the JPEG image (in bytes)
dstBufpointer to an image buffer that will receive the YUV image. Use tjBufSizeYUV2() to determine the appropriate size for this buffer based on the image width, height, padding, and level of subsampling. The Y, U (Cb), and V (Cr) image planes will be stored sequentially in the buffer (refer to YUV Image Format Notes.)
widthdesired width (in pixels) of the YUV image. If this is different than the width of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired width. If width is set to 0, then only the height will be considered when determining the scaled image size. If the scaled width is not an even multiple of the MCU block width (see tjMCUWidth), then an intermediate buffer copy will be performed within TurboJPEG.
padthe width of each line in each plane of the YUV image will be padded to the nearest multiple of this number of bytes (must be a power of 2.) To generate images suitable for X Video, pad should be set to 4.
heightdesired height (in pixels) of the YUV image. If this is different than the height of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired height. If height is set to 0, then only the width will be considered when determining the scaled image size. If the scaled height is not an even multiple of the MCU block height (see tjMCUHeight), then an intermediate buffer copy will be performed within TurboJPEG.
dstBufpointer to a buffer that will receive the unified planar YUV decompressed image. Use tjBufSizeYUV2() to determine the appropriate size for this buffer based on the scaled image width, scaled image height, row alignment, and level of chrominance subsampling. The Y, U (Cb), and V (Cr) image planes will be stored sequentially in the buffer. (Refer to YUV Image Format Notes.)
widthdesired width (in pixels) of the YUV image. If this is different than the width of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired width. If width is set to 0, then only the height will be considered when determining the scaled image size. If the scaled width is not an even multiple of the MCU block width (see tjMCUWidth), then an intermediate buffer copy will be performed.
alignrow alignment (in bytes) of the YUV image (must be a power of 2.) Setting this parameter to n will cause each row in each plane of the YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.) To generate images suitable for X Video, align should be set to 4.
heightdesired height (in pixels) of the YUV image. If this is different than the height of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired height. If height is set to 0, then only the width will be considered when determining the scaled image size. If the scaled height is not an even multiple of the MCU block height (see tjMCUHeight), then an intermediate buffer copy will be performed.
flagsthe bitwise OR of one or more of the flags
@@ -1886,16 +1887,16 @@

Decompress a JPEG image into separate Y, U (Cb), and V (Cr) image planes.

-

This function performs JPEG decompression but leaves out the color conversion step, so a planar YUV image is generated instead of an RGB image.

+

This function performs JPEG decompression but leaves out the color conversion step, so a planar YUV image is generated instead of a packed-pixel image.

Parameters
- + - - - - + + + +
handlea handle to a TurboJPEG decompressor or transformer instance
jpegBufpointer to a buffer containing the JPEG image to decompress
jpegBufpointer to a byte buffer containing the JPEG image to decompress
jpegSizesize of the JPEG image (in bytes)
dstPlanesan array of pointers to Y, U (Cb), and V (Cr) image planes (or just a Y plane, if decompressing a grayscale image) that will receive the YUV image. These planes can be contiguous or non-contiguous in memory. Use tjPlaneSizeYUV() to determine the appropriate size for each plane based on the scaled image width, scaled image height, strides, and level of chrominance subsampling. Refer to YUV Image Format Notes for more details.
widthdesired width (in pixels) of the YUV image. If this is different than the width of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired width. If width is set to 0, then only the height will be considered when determining the scaled image size. If the scaled width is not an even multiple of the MCU block width (see tjMCUWidth), then an intermediate buffer copy will be performed within TurboJPEG.
stridesan array of integers, each specifying the number of bytes per line in the corresponding plane of the output image. Setting the stride for any plane to 0 is the same as setting it to the scaled plane width (see YUV Image Format Notes.) If strides is NULL, then the strides for all planes will be set to their respective scaled plane widths. You can adjust the strides in order to add an arbitrary amount of line padding to each plane or to decompress the JPEG image into a subregion of a larger YUV planar image.
heightdesired height (in pixels) of the YUV image. If this is different than the height of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired height. If height is set to 0, then only the width will be considered when determining the scaled image size. If the scaled height is not an even multiple of the MCU block height (see tjMCUHeight), then an intermediate buffer copy will be performed within TurboJPEG.
dstPlanesan array of pointers to Y, U (Cb), and V (Cr) image planes (or just a Y plane, if decompressing a grayscale image) that will receive the decompressed image. These planes can be contiguous or non-contiguous in memory. Use tjPlaneSizeYUV() to determine the appropriate size for each plane based on the scaled image width, scaled image height, strides, and level of chrominance subsampling. Refer to YUV Image Format Notes for more details.
widthdesired width (in pixels) of the YUV image. If this is different than the width of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired width. If width is set to 0, then only the height will be considered when determining the scaled image size. If the scaled width is not an even multiple of the MCU block width (see tjMCUWidth), then an intermediate buffer copy will be performed.
stridesan array of integers, each specifying the number of bytes per row in the corresponding plane of the YUV image. Setting the stride for any plane to 0 is the same as setting it to the scaled plane width (see YUV Image Format Notes.) If strides is NULL, then the strides for all planes will be set to their respective scaled plane widths. You can adjust the strides in order to add an arbitrary amount of row padding to each plane or to decompress the JPEG image into a subregion of a larger planar YUV image.
heightdesired height (in pixels) of the YUV image. If this is different than the height of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired height. If height is set to 0, then only the width will be considered when determining the scaled image size. If the scaled height is not an even multiple of the MCU block height (see tjMCUHeight), then an intermediate buffer copy will be performed.
flagsthe bitwise OR of one or more of the flags
@@ -1931,8 +1932,8 @@

-

◆ tjEncodeYUV3()

+ +

◆ tjEncodeYUV3()

@@ -1983,7 +1984,7 @@

int  - pad, + align, @@ -2005,18 +2006,18 @@

-

Encode an RGB or grayscale image into a YUV planar image.

-

This function uses the accelerated color conversion routines in the underlying codec but does not execute any of the other steps in the JPEG compression process.

+

Encode a packed-pixel RGB or grayscale image into a unified planar YUV image.

+

This function performs color conversion (which is accelerated in the libjpeg-turbo implementation) but does not execute any of the other steps in the JPEG compression process.

Parameters
- + - + - - + +
handlea handle to a TurboJPEG compressor or transformer instance
srcBufpointer to an image buffer containing RGB or grayscale pixels to be encoded
srcBufpointer to a buffer containing a packed-pixel RGB or grayscale source image to be encoded
widthwidth (in pixels) of the source image
pitchbytes per line in the source image. Normally, this should be width * tjPixelSize[pixelFormat] if the image is unpadded, or TJPAD(width * tjPixelSize[pixelFormat]) if each line of the image is padded to the nearest 32-bit boundary, as is the case for Windows bitmaps. You can also be clever and use this parameter to skip lines, etc. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
pitchbytes per row in the source image. Normally this should be width * tjPixelSize[pixelFormat], if the image is unpadded, or TJPAD(width * tjPixelSize[pixelFormat]) if each row of the image is padded to the nearest multiple of 4 bytes, as is the case for Windows bitmaps. You can also be clever and use this parameter to skip rows, etc. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
heightheight (in pixels) of the source image
pixelFormatpixel format of the source image (see Pixel formats.)
dstBufpointer to an image buffer that will receive the YUV image. Use tjBufSizeYUV2() to determine the appropriate size for this buffer based on the image width, height, padding, and level of chrominance subsampling. The Y, U (Cb), and V (Cr) image planes will be stored sequentially in the buffer (refer to YUV Image Format Notes.)
padthe width of each line in each plane of the YUV image will be padded to the nearest multiple of this number of bytes (must be a power of 2.) To generate images suitable for X Video, pad should be set to 4.
dstBufpointer to a buffer that will receive the unified planar YUV image. Use tjBufSizeYUV2() to determine the appropriate size for this buffer based on the image width, height, row alignment, and level of chrominance subsampling. The Y, U (Cb), and V (Cr) image planes will be stored sequentially in the buffer. (Refer to YUV Image Format Notes.)
alignrow alignment (in bytes) of the YUV image (must be a power of 2.) Setting this parameter to n will cause each row in each plane of the YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.) To generate images suitable for X Video, align should be set to 4.
subsampthe level of chrominance subsampling to be used when generating the YUV image (see Chrominance subsampling options.) To generate images suitable for X Video, subsamp should be set to TJSAMP_420. This produces an image compatible with the I420 (AKA "YUV420P") format.
flagsthe bitwise OR of one or more of the flags
@@ -2100,18 +2101,18 @@

-

Encode an RGB or grayscale image into separate Y, U (Cb), and V (Cr) image planes.

-

This function uses the accelerated color conversion routines in the underlying codec but does not execute any of the other steps in the JPEG compression process.

+

Encode a packed-pixel RGB or grayscale image into separate Y, U (Cb), and V (Cr) image planes.

+

This function performs color conversion (which is accelerated in the libjpeg-turbo implementation) but does not execute any of the other steps in the JPEG compression process.

Parameters
- + - + - +
handlea handle to a TurboJPEG compressor or transformer instance
srcBufpointer to an image buffer containing RGB or grayscale pixels to be encoded
srcBufpointer to a buffer containing a packed-pixel RGB or grayscale source image to be encoded
widthwidth (in pixels) of the source image
pitchbytes per line in the source image. Normally, this should be width * tjPixelSize[pixelFormat] if the image is unpadded, or TJPAD(width * tjPixelSize[pixelFormat]) if each line of the image is padded to the nearest 32-bit boundary, as is the case for Windows bitmaps. You can also be clever and use this parameter to skip lines, etc. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
pitchbytes per row in the source image. Normally this should be width * tjPixelSize[pixelFormat], if the image is unpadded, or TJPAD(width * tjPixelSize[pixelFormat]) if each row of the image is padded to the nearest multiple of 4 bytes, as is the case for Windows bitmaps. You can also be clever and use this parameter to skip rows, etc. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
heightheight (in pixels) of the source image
pixelFormatpixel format of the source image (see Pixel formats.)
dstPlanesan array of pointers to Y, U (Cb), and V (Cr) image planes (or just a Y plane, if generating a grayscale image) that will receive the encoded image. These planes can be contiguous or non-contiguous in memory. Use tjPlaneSizeYUV() to determine the appropriate size for each plane based on the image width, height, strides, and level of chrominance subsampling. Refer to YUV Image Format Notes for more details.
stridesan array of integers, each specifying the number of bytes per line in the corresponding plane of the output image. Setting the stride for any plane to 0 is the same as setting it to the plane width (see YUV Image Format Notes.) If strides is NULL, then the strides for all planes will be set to their respective plane widths. You can adjust the strides in order to add an arbitrary amount of line padding to each plane or to encode an RGB or grayscale image into a subregion of a larger YUV planar image.
stridesan array of integers, each specifying the number of bytes per row in the corresponding plane of the YUV image. Setting the stride for any plane to 0 is the same as setting it to the plane width (see YUV Image Format Notes.) If strides is NULL, then the strides for all planes will be set to their respective plane widths. You can adjust the strides in order to add an arbitrary amount of row padding to each plane or to encode an RGB or grayscale image into a subregion of a larger planar YUV image.
subsampthe level of chrominance subsampling to be used when generating the YUV image (see Chrominance subsampling options.) To generate images suitable for X Video, subsamp should be set to TJSAMP_420. This produces an image compatible with the I420 (AKA "YUV420P") format.
flagsthe bitwise OR of one or more of the flags
@@ -2137,15 +2138,15 @@

-

Free an image buffer previously allocated by TurboJPEG.

-

You should always use this function to free JPEG destination buffer(s) that were automatically (re)allocated by the compression and transform functions or that were manually allocated using tjAlloc().

+

Free a byte buffer previously allocated by TurboJPEG.

+

You should always use this function to free JPEG destination buffer(s) that were automatically (re)allocated by the compression and transform functions or that were manually allocated using tjAlloc().

Parameters
bufferaddress of the buffer to free. If the address is NULL, then this function has no effect.
-
See also
tjAlloc()
+
See also
tjAlloc()

@@ -2204,8 +2205,8 @@

-

◆ tjGetScalingFactors()

+ +

◆ tjGetScalingFactors()

@@ -2214,16 +2215,16 @@

DLLEXPORT tjscalingfactor* tjGetScalingFactors ( int *  - numscalingfactors) + numScalingFactors)

-

Returns a list of fractional scaling factors that the JPEG decompressor in this implementation of TurboJPEG supports.

+

Returns a list of fractional scaling factors that the JPEG decompressor supports.

Parameters
- +
numscalingfactorspointer to an integer variable that will receive the number of elements in the list
numScalingFactorspointer to an integer variable that will receive the number of elements in the list
@@ -2344,25 +2345,25 @@

-

Load an uncompressed image from disk into memory.

+

Load a packed-pixel image from disk into memory.

Parameters
- - - - - + + + +
filenamename of a file containing an uncompressed image in Windows BMP or PBMPLUS (PPM/PGM) format
widthpointer to an integer variable that will receive the width (in pixels) of the uncompressed image
alignrow alignment of the image buffer to be returned (must be a power of 2.) For instance, setting this parameter to 4 will cause all rows in the image buffer to be padded to the nearest 32-bit boundary, and setting this parameter to 1 will cause all rows in the image buffer to be unpadded.
heightpointer to an integer variable that will receive the height (in pixels) of the uncompressed image
pixelFormatpointer to an integer variable that specifies or will receive the pixel format of the uncompressed image buffer. The behavior of tjLoadImage() will vary depending on the value of *pixelFormat passed to the function:
    -
  • TJPF_UNKNOWN : The uncompressed image buffer returned by the function will use the most optimal pixel format for the file type, and *pixelFormat will contain the ID of this pixel format upon successful return from the function.
  • -
  • TJPF_GRAY : Only PGM files and 8-bit BMP files with a grayscale colormap can be loaded.
  • -
  • TJPF_CMYK : The RGB or grayscale pixels stored in the file will be converted using a quick & dirty algorithm that is suitable only for testing purposes (proper conversion between CMYK and other formats requires a color management system.)
  • -
  • Other pixel formats : The uncompressed image buffer will use the specified pixel format, and pixel format conversion will be performed if necessary.
  • +
filenamename of a file containing a packed-pixel image in Windows BMP or PBMPLUS (PPM/PGM) format
widthpointer to an integer variable that will receive the width (in pixels) of the packed-pixel image
alignrow alignment of the packed-pixel buffer to be returned (must be a power of 2.) Setting this parameter to n will cause all rows in the buffer to be padded to the nearest multiple of n bytes (1 = unpadded.)
heightpointer to an integer variable that will receive the height (in pixels) of the packed-pixel image
pixelFormatpointer to an integer variable that specifies or will receive the pixel format of the packed-pixel buffer. The behavior of tjLoadImage() will vary depending on the value of *pixelFormat passed to the function:
    +
  • TJPF_UNKNOWN : The packed-pixel buffer returned by this function will use the most optimal pixel format for the file type, and *pixelFormat will contain the ID of that pixel format upon successful return from this function.
  • +
  • TJPF_GRAY : Only PGM files and 8-bit-per-pixel BMP files with a grayscale colormap can be loaded.
  • +
  • TJPF_CMYK : The RGB or grayscale pixels stored in the file will be converted using a quick & dirty algorithm that is suitable only for testing purposes. (Proper conversion between CMYK and other formats requires a color management system.)
  • +
  • Other pixel formats : The packed-pixel buffer will use the specified pixel format, and pixel format conversion will be performed if necessary.
flagsthe bitwise OR of one or more of the flags.
-
Returns
a pointer to a newly-allocated buffer containing the uncompressed image, converted to the chosen pixel format and with the chosen row alignment, or NULL if an error occurred (see tjGetErrorStr2().) This buffer should be freed using tjFree().
+
Returns
a pointer to a newly-allocated buffer containing the packed-pixel image, converted to the chosen pixel format and with the chosen row alignment, or NULL if an error occurred (see tjGetErrorStr2().) This buffer should be freed using tjFree().

@@ -2461,7 +2462,7 @@

componentIDID number of the image plane (0 = Y, 1 = U/Cb, 2 = V/Cr) widthwidth (in pixels) of the YUV image. NOTE: this is the width of the whole image, not the plane width. - stridebytes per line in the image plane. Setting this to 0 is the equivalent of setting it to the plane width. + stridebytes per row in the image plane. Setting this to 0 is the equivalent of setting it to the plane width. heightheight (in pixels) of the YUV image. NOTE: this is the height of the whole image, not the plane height. subsamplevel of chrominance subsampling in the image (see Chrominance subsampling options.) @@ -2573,15 +2574,15 @@

-

Save an uncompressed image from memory to disk.

+

Save a packed-pixel image from memory to disk.

Parameters
- - - - - - + + + + + +
filenamename of a file to which to save the uncompressed image. The image will be stored in Windows BMP or PBMPLUS (PPM/PGM) format, depending on the file extension.
bufferpointer to an image buffer containing RGB, grayscale, or CMYK pixels to be saved
widthwidth (in pixels) of the uncompressed image
pitchbytes per line in the image buffer. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
heightheight (in pixels) of the uncompressed image
pixelFormatpixel format of the image buffer (see Pixel formats.) If this parameter is set to TJPF_GRAY, then the image will be stored in PGM or 8-bit (indexed color) BMP format. Otherwise, the image will be stored in PPM or 24-bit BMP format. If this parameter is set to TJPF_CMYK, then the CMYK pixels will be converted to RGB using a quick & dirty algorithm that is suitable only for testing (proper conversion between CMYK and other formats requires a color management system.)
filenamename of a file to which to save the packed-pixel image. The image will be stored in Windows BMP or PBMPLUS (PPM/PGM) format, depending on the file extension.
bufferpointer to a buffer containing a packed-pixel RGB, grayscale, or CMYK image to be saved
widthwidth (in pixels) of the packed-pixel image
pitchbytes per row in the packed-pixel image. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
heightheight (in pixels) of the packed-pixel image
pixelFormatpixel format of the packed-pixel image (see Pixel formats.) If this parameter is set to TJPF_GRAY, then the image will be stored in PGM or 8-bit-per-pixel (indexed color) BMP format. Otherwise, the image will be stored in PPM or 24-bit-per-pixel BMP format. If this parameter is set to TJPF_CMYK, then the CMYK pixels will be converted to RGB using a quick & dirty algorithm that is suitable only for testing purposes. (Proper conversion between CMYK and other formats requires a color management system.)
flagsthe bitwise OR of one or more of the flags.
@@ -2657,17 +2658,17 @@

Parameters
- + - - - +If you choose option 1, then dstSizes[i] should be set to the size of your pre-allocated buffer. In any case, unless you have set TJFLAG_NOREALLOC, you should always check dstBufs[i] upon return from this function, as it may have changed. + +
handlea handle to a TurboJPEG transformer instance
jpegBufpointer to a buffer containing the JPEG source image to transform
jpegBufpointer to a byte buffer containing the JPEG source image to transform
jpegSizesize of the JPEG source image (in bytes)
nthe number of transformed JPEG images to generate
dstBufspointer to an array of n image buffers. dstBufs[i] will receive a JPEG image that has been transformed using the parameters in transforms[i]. TurboJPEG has the ability to reallocate the JPEG buffer to accommodate the size of the JPEG image. Thus, you can choose to:
    -
  1. pre-allocate the JPEG buffer with an arbitrary size using tjAlloc() and let TurboJPEG grow the buffer as needed,
  2. +
dstBufspointer to an array of n byte buffers. dstBufs[i] will receive a JPEG image that has been transformed using the parameters in transforms[i]. TurboJPEG has the ability to reallocate the JPEG destination buffer to accommodate the size of the transformed JPEG image. Thus, you can choose to:
    +
  1. pre-allocate the JPEG destination buffer with an arbitrary size using tjAlloc() and let TurboJPEG grow the buffer as needed,
  2. set dstBufs[i] to NULL to tell TurboJPEG to allocate the buffer for you, or
  3. -
  4. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize() with the transformed or cropped width and height. Under normal circumstances, this should ensure that the buffer never has to be re-allocated (setting TJFLAG_NOREALLOC guarantees that it won't be.) Note, however, that there are some rare cases (such as transforming images with a large amount of embedded EXIF or ICC profile data) in which the output image will be larger than the worst-case size, and TJFLAG_NOREALLOC cannot be used in those cases.
  5. +
  6. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize() with the transformed or cropped width and height. Under normal circumstances, this should ensure that the buffer never has to be re-allocated. (Setting TJFLAG_NOREALLOC guarantees that it won't be.) Note, however, that there are some rare cases (such as transforming images with a large amount of embedded EXIF or ICC profile data) in which the transformed JPEG image will be larger than the worst-case size, and TJFLAG_NOREALLOC cannot be used in those cases.
-If you choose option 1, dstSizes[i] should be set to the size of your pre-allocated buffer. In any case, unless you have set TJFLAG_NOREALLOC, you should always check dstBufs[i] upon return from this function, as it may have changed.
dstSizespointer to an array of n unsigned long variables that will receive the actual sizes (in bytes) of each transformed JPEG image. If dstBufs[i] points to a pre-allocated buffer, then dstSizes[i] should be set to the size of the buffer. Upon return, dstSizes[i] will contain the size of the JPEG image (in bytes.)
transformspointer to an array of n tjtransform structures, each of which specifies the transform parameters and/or cropping region for the corresponding transformed output image.
dstSizespointer to an array of n unsigned long variables that will receive the actual sizes (in bytes) of each transformed JPEG image. If dstBufs[i] points to a pre-allocated buffer, then dstSizes[i] should be set to the size of the buffer. Upon return, dstSizes[i] will contain the size of the transformed JPEG image (in bytes.)
transformspointer to an array of n tjtransform structures, each of which specifies the transform parameters and/or cropping region for the corresponding transformed JPEG image.
flagsthe bitwise OR of one or more of the flags
@@ -2698,7 +2699,7 @@

Alpha offset (in bytes) for a given pixel format.

-

This specifies the number of bytes that the Alpha component is offset from the start of the pixel. For instance, if a pixel of format TJ_BGRA is stored in char pixel[], then the alpha component will be pixel[tjAlphaOffset[TJ_BGRA]]. This will be -1 if the pixel format does not have an alpha component.

+

This specifies the number of bytes that the alpha component is offset from the start of the pixel. For instance, if a pixel of format TJPF_BGRA is stored in unsigned char pixel[], then the alpha component will be pixel[tjAlphaOffset[TJPF_BGRA]]. This will be -1 if the pixel format does not have an alpha component.

@@ -2723,7 +2724,7 @@

Blue offset (in bytes) for a given pixel format.

-

This specifies the number of bytes that the Blue component is offset from the start of the pixel. For instance, if a pixel of format TJ_BGRX is stored in char pixel[], then the blue component will be pixel[tjBlueOffset[TJ_BGRX]]. This will be -1 if the pixel format does not have a blue component.

+

This specifies the number of bytes that the blue component is offset from the start of the pixel. For instance, if a pixel of format TJPF_BGRX is stored in unsigned char pixel[], then the blue component will be pixel[tjBlueOffset[TJPF_BGRX]]. This will be -1 if the pixel format does not have a blue component.

@@ -2748,7 +2749,7 @@

Green offset (in bytes) for a given pixel format.

-

This specifies the number of bytes that the green component is offset from the start of the pixel. For instance, if a pixel of format TJ_BGRX is stored in char pixel[], then the green component will be pixel[tjGreenOffset[TJ_BGRX]]. This will be -1 if the pixel format does not have a green component.

+

This specifies the number of bytes that the green component is offset from the start of the pixel. For instance, if a pixel of format TJPF_BGRX is stored in unsigned char pixel[], then the green component will be pixel[tjGreenOffset[TJPF_BGRX]]. This will be -1 if the pixel format does not have a green component.

@@ -2859,7 +2860,7 @@

Red offset (in bytes) for a given pixel format.

-

This specifies the number of bytes that the red component is offset from the start of the pixel. For instance, if a pixel of format TJ_BGRX is stored in char pixel[], then the red component will be pixel[tjRedOffset[TJ_BGRX]]. This will be -1 if the pixel format does not have a red component.

+

This specifies the number of bytes that the red component is offset from the start of the pixel. For instance, if a pixel of format TJPF_BGRX is stored in unsigned char pixel[], then the red component will be pixel[tjRedOffset[TJPF_BGRX]]. This will be -1 if the pixel format does not have a red component.

diff --git a/doc/html/search/all_6.js b/doc/html/search/all_6.js index aa311074e..6b43b3060 100644 --- a/doc/html/search/all_6.js +++ b/doc/html/search/all_6.js @@ -9,9 +9,9 @@ var searchData= ['tjalphaoffset_14',['tjAlphaOffset',['../group___turbo_j_p_e_g.html#ga5af0ab065feefd526debf1e20c43e837',1,'turbojpeg.h']]], ['tjblueoffset_15',['tjBlueOffset',['../group___turbo_j_p_e_g.html#ga84e2e35d3f08025f976ec1ec53693dea',1,'turbojpeg.h']]], ['tjbufsize_16',['tjBufSize',['../group___turbo_j_p_e_g.html#ga67ac12fee79073242cb216e07c9f1f90',1,'turbojpeg.h']]], - ['tjbufsizeyuv2_17',['tjBufSizeYUV2',['../group___turbo_j_p_e_g.html#ga2be2b9969d4df9ecce9b05deed273194',1,'turbojpeg.h']]], + ['tjbufsizeyuv2_17',['tjBufSizeYUV2',['../group___turbo_j_p_e_g.html#ga5e5aac9e8bcf17049279301e2466474c',1,'turbojpeg.h']]], ['tjcompress2_18',['tjCompress2',['../group___turbo_j_p_e_g.html#gafbdce0112fd78fd38efae841443a9bcf',1,'turbojpeg.h']]], - ['tjcompressfromyuv_19',['tjCompressFromYUV',['../group___turbo_j_p_e_g.html#ga7622a459b79aa1007e005b58783f875b',1,'turbojpeg.h']]], + ['tjcompressfromyuv_19',['tjCompressFromYUV',['../group___turbo_j_p_e_g.html#gab40f5096a72fd7e5bda9d6b58fa37e2e',1,'turbojpeg.h']]], ['tjcompressfromyuvplanes_20',['tjCompressFromYUVPlanes',['../group___turbo_j_p_e_g.html#ga29ec5dfbd2d84b8724e951d6fa0d5d9e',1,'turbojpeg.h']]], ['tjcs_21',['TJCS',['../group___turbo_j_p_e_g.html#ga4f83ad3368e0e29d1957be0efa7c3720',1,'turbojpeg.h']]], ['tjcs_5fcmyk_22',['TJCS_CMYK',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a6c8b636152ac8195b869587db315ee53',1,'turbojpeg.h']]], @@ -19,14 +19,14 @@ var searchData= ['tjcs_5frgb_24',['TJCS_RGB',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a677cb7ccb85c4038ac41964a2e09e555',1,'turbojpeg.h']]], ['tjcs_5fycbcr_25',['TJCS_YCbCr',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a7389b8f65bb387ffedce3efd0d78ec75',1,'turbojpeg.h']]], ['tjcs_5fycck_26',['TJCS_YCCK',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a53839e0fe867b76b58d16b0a1a7c598e',1,'turbojpeg.h']]], - ['tjdecodeyuv_27',['tjDecodeYUV',['../group___turbo_j_p_e_g.html#ga70abbf38f77a26fd6da8813bef96f695',1,'turbojpeg.h']]], + ['tjdecodeyuv_27',['tjDecodeYUV',['../group___turbo_j_p_e_g.html#ga97c2cedc1e2bade15a84164c94e503c1',1,'turbojpeg.h']]], ['tjdecodeyuvplanes_28',['tjDecodeYUVPlanes',['../group___turbo_j_p_e_g.html#ga10e837c07fa9d25770565b237d3898d9',1,'turbojpeg.h']]], ['tjdecompress2_29',['tjDecompress2',['../group___turbo_j_p_e_g.html#gae9eccef8b682a48f43a9117c231ed013',1,'turbojpeg.h']]], ['tjdecompressheader3_30',['tjDecompressHeader3',['../group___turbo_j_p_e_g.html#ga0595681096bba7199cc6f3533cb25f77',1,'turbojpeg.h']]], - ['tjdecompresstoyuv2_31',['tjDecompressToYUV2',['../group___turbo_j_p_e_g.html#ga04d1e839ff9a0860dd1475cff78d3364',1,'turbojpeg.h']]], + ['tjdecompresstoyuv2_31',['tjDecompressToYUV2',['../group___turbo_j_p_e_g.html#ga5a3093e325598c17a9f004323af6fafa',1,'turbojpeg.h']]], ['tjdecompresstoyuvplanes_32',['tjDecompressToYUVPlanes',['../group___turbo_j_p_e_g.html#gaa59f901a5258ada5bd0185ad59368540',1,'turbojpeg.h']]], ['tjdestroy_33',['tjDestroy',['../group___turbo_j_p_e_g.html#ga75f355fa27225ba1a4ee392c852394d2',1,'turbojpeg.h']]], - ['tjencodeyuv3_34',['tjEncodeYUV3',['../group___turbo_j_p_e_g.html#gac519b922cdf446e97d0cdcba513636bf',1,'turbojpeg.h']]], + ['tjencodeyuv3_34',['tjEncodeYUV3',['../group___turbo_j_p_e_g.html#ga5d619e0a02b71e05a8dffb764f6d7a64',1,'turbojpeg.h']]], ['tjencodeyuvplanes_35',['tjEncodeYUVPlanes',['../group___turbo_j_p_e_g.html#gae2d04c72457fe7f4d60cf78ab1b1feb1',1,'turbojpeg.h']]], ['tjerr_36',['TJERR',['../group___turbo_j_p_e_g.html#gafbc17cfa57d0d5d11fea35ac025950fe',1,'turbojpeg.h']]], ['tjerr_5ffatal_37',['TJERR_FATAL',['../group___turbo_j_p_e_g.html#ggafbc17cfa57d0d5d11fea35ac025950feafc9cceeada13122b09e4851e3788039a',1,'turbojpeg.h']]], @@ -42,7 +42,7 @@ var searchData= ['tjfree_47',['tjFree',['../group___turbo_j_p_e_g.html#gaea863d2da0cdb609563aabdf9196514b',1,'turbojpeg.h']]], ['tjgeterrorcode_48',['tjGetErrorCode',['../group___turbo_j_p_e_g.html#ga414feeffbf860ebd31c745df203de410',1,'turbojpeg.h']]], ['tjgeterrorstr2_49',['tjGetErrorStr2',['../group___turbo_j_p_e_g.html#ga1ead8574f9f39fbafc6b497124e7aafa',1,'turbojpeg.h']]], - ['tjgetscalingfactors_50',['tjGetScalingFactors',['../group___turbo_j_p_e_g.html#gac3854476006b10787bd128f7ede48057',1,'turbojpeg.h']]], + ['tjgetscalingfactors_50',['tjGetScalingFactors',['../group___turbo_j_p_e_g.html#ga193d0977b3b9966d53a6c402e90899b1',1,'turbojpeg.h']]], ['tjgreenoffset_51',['tjGreenOffset',['../group___turbo_j_p_e_g.html#ga82d6e35da441112a411da41923c0ba2f',1,'turbojpeg.h']]], ['tjhandle_52',['tjhandle',['../group___turbo_j_p_e_g.html#ga758d2634ecb4949de7815cba621f5763',1,'turbojpeg.h']]], ['tjinitcompress_53',['tjInitCompress',['../group___turbo_j_p_e_g.html#ga9d63a05fc6d813f4aae06107041a37e8',1,'turbojpeg.h']]], @@ -82,7 +82,7 @@ var searchData= ['tjsaveimage_87',['tjSaveImage',['../group___turbo_j_p_e_g.html#ga6f445b22d8933ae4815b3370a538d879',1,'turbojpeg.h']]], ['tjscaled_88',['TJSCALED',['../group___turbo_j_p_e_g.html#ga84878bb65404204743aa18cac02781df',1,'turbojpeg.h']]], ['tjscalingfactor_89',['tjscalingfactor',['../structtjscalingfactor.html',1,'']]], - ['tjtransform_90',['tjtransform',['../structtjtransform.html',1,'tjtransform'],['../group___turbo_j_p_e_g.html#ga504805ec0161f1b505397ca0118bf8fd',1,'tjtransform(): turbojpeg.h'],['../group___turbo_j_p_e_g.html#ga9cb8abf4cc91881e04a0329b2270be25',1,'tjTransform(tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, int n, unsigned char **dstBufs, unsigned long *dstSizes, tjtransform *transforms, int flags): turbojpeg.h']]], + ['tjtransform_90',['tjtransform',['../structtjtransform.html',1,'tjtransform'],['../group___turbo_j_p_e_g.html#ga9cb8abf4cc91881e04a0329b2270be25',1,'tjTransform(tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, int n, unsigned char **dstBufs, unsigned long *dstSizes, tjtransform *transforms, int flags): turbojpeg.h'],['../group___turbo_j_p_e_g.html#ga504805ec0161f1b505397ca0118bf8fd',1,'tjtransform(): turbojpeg.h']]], ['tjxop_91',['TJXOP',['../group___turbo_j_p_e_g.html#ga2de531af4e7e6c4f124908376b354866',1,'turbojpeg.h']]], ['tjxop_5fhflip_92',['TJXOP_HFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aa0df69776caa30f0fa28e26332d311ce',1,'turbojpeg.h']]], ['tjxop_5fnone_93',['TJXOP_NONE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aad88c0366cd3f7d0eac9d7a3fa1c2c27',1,'turbojpeg.h']]], diff --git a/doc/html/search/functions_0.js b/doc/html/search/functions_0.js index 4a9ea5b4e..a608dabe2 100644 --- a/doc/html/search/functions_0.js +++ b/doc/html/search/functions_0.js @@ -2,23 +2,23 @@ var searchData= [ ['tjalloc_114',['tjAlloc',['../group___turbo_j_p_e_g.html#gaec627dd4c5f30b7a775a7aea3bec5d83',1,'turbojpeg.h']]], ['tjbufsize_115',['tjBufSize',['../group___turbo_j_p_e_g.html#ga67ac12fee79073242cb216e07c9f1f90',1,'turbojpeg.h']]], - ['tjbufsizeyuv2_116',['tjBufSizeYUV2',['../group___turbo_j_p_e_g.html#ga2be2b9969d4df9ecce9b05deed273194',1,'turbojpeg.h']]], + ['tjbufsizeyuv2_116',['tjBufSizeYUV2',['../group___turbo_j_p_e_g.html#ga5e5aac9e8bcf17049279301e2466474c',1,'turbojpeg.h']]], ['tjcompress2_117',['tjCompress2',['../group___turbo_j_p_e_g.html#gafbdce0112fd78fd38efae841443a9bcf',1,'turbojpeg.h']]], - ['tjcompressfromyuv_118',['tjCompressFromYUV',['../group___turbo_j_p_e_g.html#ga7622a459b79aa1007e005b58783f875b',1,'turbojpeg.h']]], + ['tjcompressfromyuv_118',['tjCompressFromYUV',['../group___turbo_j_p_e_g.html#gab40f5096a72fd7e5bda9d6b58fa37e2e',1,'turbojpeg.h']]], ['tjcompressfromyuvplanes_119',['tjCompressFromYUVPlanes',['../group___turbo_j_p_e_g.html#ga29ec5dfbd2d84b8724e951d6fa0d5d9e',1,'turbojpeg.h']]], - ['tjdecodeyuv_120',['tjDecodeYUV',['../group___turbo_j_p_e_g.html#ga70abbf38f77a26fd6da8813bef96f695',1,'turbojpeg.h']]], + ['tjdecodeyuv_120',['tjDecodeYUV',['../group___turbo_j_p_e_g.html#ga97c2cedc1e2bade15a84164c94e503c1',1,'turbojpeg.h']]], ['tjdecodeyuvplanes_121',['tjDecodeYUVPlanes',['../group___turbo_j_p_e_g.html#ga10e837c07fa9d25770565b237d3898d9',1,'turbojpeg.h']]], ['tjdecompress2_122',['tjDecompress2',['../group___turbo_j_p_e_g.html#gae9eccef8b682a48f43a9117c231ed013',1,'turbojpeg.h']]], ['tjdecompressheader3_123',['tjDecompressHeader3',['../group___turbo_j_p_e_g.html#ga0595681096bba7199cc6f3533cb25f77',1,'turbojpeg.h']]], - ['tjdecompresstoyuv2_124',['tjDecompressToYUV2',['../group___turbo_j_p_e_g.html#ga04d1e839ff9a0860dd1475cff78d3364',1,'turbojpeg.h']]], + ['tjdecompresstoyuv2_124',['tjDecompressToYUV2',['../group___turbo_j_p_e_g.html#ga5a3093e325598c17a9f004323af6fafa',1,'turbojpeg.h']]], ['tjdecompresstoyuvplanes_125',['tjDecompressToYUVPlanes',['../group___turbo_j_p_e_g.html#gaa59f901a5258ada5bd0185ad59368540',1,'turbojpeg.h']]], ['tjdestroy_126',['tjDestroy',['../group___turbo_j_p_e_g.html#ga75f355fa27225ba1a4ee392c852394d2',1,'turbojpeg.h']]], - ['tjencodeyuv3_127',['tjEncodeYUV3',['../group___turbo_j_p_e_g.html#gac519b922cdf446e97d0cdcba513636bf',1,'turbojpeg.h']]], + ['tjencodeyuv3_127',['tjEncodeYUV3',['../group___turbo_j_p_e_g.html#ga5d619e0a02b71e05a8dffb764f6d7a64',1,'turbojpeg.h']]], ['tjencodeyuvplanes_128',['tjEncodeYUVPlanes',['../group___turbo_j_p_e_g.html#gae2d04c72457fe7f4d60cf78ab1b1feb1',1,'turbojpeg.h']]], ['tjfree_129',['tjFree',['../group___turbo_j_p_e_g.html#gaea863d2da0cdb609563aabdf9196514b',1,'turbojpeg.h']]], ['tjgeterrorcode_130',['tjGetErrorCode',['../group___turbo_j_p_e_g.html#ga414feeffbf860ebd31c745df203de410',1,'turbojpeg.h']]], ['tjgeterrorstr2_131',['tjGetErrorStr2',['../group___turbo_j_p_e_g.html#ga1ead8574f9f39fbafc6b497124e7aafa',1,'turbojpeg.h']]], - ['tjgetscalingfactors_132',['tjGetScalingFactors',['../group___turbo_j_p_e_g.html#gac3854476006b10787bd128f7ede48057',1,'turbojpeg.h']]], + ['tjgetscalingfactors_132',['tjGetScalingFactors',['../group___turbo_j_p_e_g.html#ga193d0977b3b9966d53a6c402e90899b1',1,'turbojpeg.h']]], ['tjinitcompress_133',['tjInitCompress',['../group___turbo_j_p_e_g.html#ga9d63a05fc6d813f4aae06107041a37e8',1,'turbojpeg.h']]], ['tjinitdecompress_134',['tjInitDecompress',['../group___turbo_j_p_e_g.html#ga52300eac3f3d9ef4bab303bc244f62d3',1,'turbojpeg.h']]], ['tjinittransform_135',['tjInitTransform',['../group___turbo_j_p_e_g.html#ga928beff6ac248ceadf01089fc6b41957',1,'turbojpeg.h']]], diff --git a/doc/html/structtjtransform.html b/doc/html/structtjtransform.html index ba78980ed..9ff248d45 100644 --- a/doc/html/structtjtransform.html +++ b/doc/html/structtjtransform.html @@ -84,7 +84,7 @@  One of the
transform operations. More...
  int options - The bitwise OR of one of more of the transform options. More...
+ The bitwise OR of one of more of the transform options. More...
  void * data  Arbitrary data that can be accessed within the body of the callback function. More...
@@ -115,7 +115,7 @@

coeffspointer to an array of transformed DCT coefficients. (NOTE: this pointer is not guaranteed to be valid once the callback returns, so applications wishing to hand off the DCT coefficients to another function or library should make a copy of them within the body of the callback.) arrayRegiontjregion structure containing the width and height of the array pointed to by coeffs as well as its offset relative to the component plane. TurboJPEG implementations may choose to split each component plane into multiple DCT coefficient arrays and call the callback function once for each array. planeRegiontjregion structure containing the width and height of the component plane to which coeffs belongs - componentIDID number of the component plane to which coeffs belongs (Y, Cb, and Cr have, respectively, ID's of 0, 1, and 2 in typical JPEG images.) + componentIDID number of the component plane to which coeffs belongs. (Y, Cb, and Cr have, respectively, ID's of 0, 1, and 2 in typical JPEG images.) transformIDID number of the transformed image to which coeffs belongs. This is the same as the index of the transform in the transforms array that was passed to tjTransform(). transforma pointer to a tjtransform structure that specifies the parameters and/or cropping region for this transform @@ -169,7 +169,7 @@

-

The bitwise OR of one of more of the transform options.

+

The bitwise OR of one of more of the transform options.

diff --git a/java/TJBench.java b/java/TJBench.java index 3a061d876..b3b1a6525 100644 --- a/java/TJBench.java +++ b/java/TJBench.java @@ -1,6 +1,6 @@ /* - * Copyright (C)2009-2014, 2016-2019, 2021 D. R. Commander. - * All Rights Reserved. + * Copyright (C)2009-2014, 2016-2019, 2021, 2023 D. R. Commander. + * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -37,7 +37,7 @@ final class TJBench { private TJBench() {} - private static int flags = 0, quiet = 0, pf = TJ.PF_BGR, yuvPad = 1; + private static int flags = 0, quiet = 0, pf = TJ.PF_BGR, yuvAlign = 1; private static boolean compOnly, decompOnly, doTile, doYUV, write = true; static final String[] PIXFORMATSTR = { @@ -192,7 +192,7 @@ static void decomp(byte[] srcBuf, byte[][] jpegBuf, int[] jpegSize, int width = doTile ? tilew : scaledw; int height = doTile ? tileh : scaledh; - yuvImage = new YUVImage(width, yuvPad, height, subsamp); + yuvImage = new YUVImage(width, yuvAlign, height, subsamp); Arrays.fill(yuvImage.getBuf(), (byte)127); } @@ -212,7 +212,8 @@ static void decomp(byte[] srcBuf, byte[][] jpegBuf, int[] jpegSize, tjd.setSourceImage(jpegBuf[tile], jpegSize[tile]); } catch (TJException e) { handleTJException(e); } if (doYUV) { - yuvImage.setBuf(yuvImage.getBuf(), width, yuvPad, height, subsamp); + yuvImage.setBuf(yuvImage.getBuf(), width, yuvAlign, height, + subsamp); try { tjd.decompressToYUV(yuvImage, flags); } catch (TJException e) { handleTJException(e); } @@ -372,7 +373,7 @@ static void fullTest(byte[] srcBuf, int w, int h, int subsamp, int jpegQual, tjc.setSubsamp(subsamp); if (doYUV) { - yuvImage = new YUVImage(tilew, yuvPad, tileh, subsamp); + yuvImage = new YUVImage(tilew, yuvAlign, tileh, subsamp); Arrays.fill(yuvImage.getBuf(), (byte)127); } @@ -393,7 +394,7 @@ static void fullTest(byte[] srcBuf, int w, int h, int subsamp, int jpegQual, if (doYUV) { double startEncode = getTime(); - yuvImage.setBuf(yuvImage.getBuf(), width, yuvPad, height, + yuvImage.setBuf(yuvImage.getBuf(), width, yuvAlign, height, subsamp); tjc.encodeYUV(yuvImage, flags); if (iter >= 0) @@ -720,8 +721,8 @@ static void usage() throws Exception { System.out.println("-quiet = Output results in tabular rather than verbose format"); System.out.println("-yuv = Test YUV encoding/decoding functions"); System.out.println("-yuvpad

= If testing YUV encoding/decoding, this specifies the number of"); - System.out.println(" bytes to which each row of each plane in the intermediate YUV image is"); - System.out.println(" padded (default = 1)"); + System.out.println(" bytes by which each row of each plane in the intermediate YUV image is"); + System.out.println(" evenly divisible (default = 1)"); System.out.println("-scale M/N = Scale down the width/height of the decompressed JPEG image by a"); System.out.print(" factor of M/N (M/N = "); for (i = 0; i < nsf; i++) { @@ -902,7 +903,7 @@ else if (argv[i].equalsIgnoreCase("-benchtime") && } else usage(); } else if (argv[i].equalsIgnoreCase("-yuv")) { - System.out.println("Testing YUV planar encoding/decoding\n"); + System.out.println("Testing planar YUV encoding/decoding\n"); doYUV = true; } else if (argv[i].equalsIgnoreCase("-yuvpad") && i < argv.length - 1) { @@ -912,7 +913,7 @@ else if (argv[i].equalsIgnoreCase("-benchtime") && temp = Integer.parseInt(argv[++i]); } catch (NumberFormatException e) {} if (temp >= 1) - yuvPad = temp; + yuvAlign = temp; } else if (argv[i].equalsIgnoreCase("-subsamp") && i < argv.length - 1) { i++; diff --git a/java/TJUnitTest.java b/java/TJUnitTest.java index 91ad5fd95..6443c0621 100644 --- a/java/TJUnitTest.java +++ b/java/TJUnitTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C)2011-2018 D. R. Commander. All Rights Reserved. + * Copyright (C)2011-2018, 2023 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -50,7 +50,7 @@ static void usage() { System.out.println("Options:"); System.out.println("-yuv = test YUV encoding/decoding support"); System.out.println("-noyuvpad = do not pad each line of each Y, U, and V plane to the nearest"); - System.out.println(" 4-byte boundary"); + System.out.println(" multiple of 4 bytes"); System.out.println("-bi = test BufferedImage support\n"); System.exit(1); } @@ -92,7 +92,7 @@ static void usage() { }; private static boolean doYUV = false; - private static int pad = 4; + private static int yuvAlign = 4; private static boolean bi = false; private static int exitStatus = 0; @@ -532,7 +532,7 @@ static int checkBufYUV(byte[] buf, int size, int w, int h, int subsamp, int hsf = TJ.getMCUWidth(subsamp) / 8, vsf = TJ.getMCUHeight(subsamp) / 8; int pw = pad(w, hsf), ph = pad(h, vsf); int cw = pw / hsf, ch = ph / vsf; - int ypitch = pad(pw, pad), uvpitch = pad(cw, pad); + int ypitch = pad(pw, yuvAlign), uvpitch = pad(cw, yuvAlign); int retval = 1; int correctsize = ypitch * ph + (subsamp == TJ.SAMP_GRAY ? 0 : uvpitch * ch * 2); @@ -668,7 +668,7 @@ static int compTest(TJCompressor tjc, byte[] dstBuf, int w, int h, int pf, if (doYUV) { System.out.format("%s %s -> YUV %s ... ", pfStrLong, buStrLong, SUBNAME_LONG[subsamp]); - YUVImage yuvImage = tjc.encodeYUV(pad, flags); + YUVImage yuvImage = tjc.encodeYUV(yuvAlign, flags); if (checkBufYUV(yuvImage.getBuf(), yuvImage.getSize(), w, h, subsamp, new TJScalingFactor(1, 1)) == 1) System.out.print("Passed.\n"); @@ -733,8 +733,8 @@ static void decompTest(TJDecompressor tjd, byte[] jpegBuf, int jpegSize, if (!sf.isOne()) System.out.format("%d/%d ... ", sf.getNum(), sf.getDenom()); else System.out.print("... "); - YUVImage yuvImage = tjd.decompressToYUV(scaledWidth, pad, scaledHeight, - flags); + YUVImage yuvImage = tjd.decompressToYUV(scaledWidth, yuvAlign, + scaledHeight, flags); if (checkBufYUV(yuvImage.getBuf(), yuvImage.getSize(), scaledWidth, scaledHeight, subsamp, sf) == 1) System.out.print("Passed.\n"); @@ -855,7 +855,7 @@ static void bufSizeTest() throws Exception { System.out.format("%04d x %04d\b\b\b\b\b\b\b\b\b\b\b", w, h); srcBuf = new byte[w * h * 4]; if (doYUV) - dstImage = new YUVImage(w, pad, h, subsamp); + dstImage = new YUVImage(w, yuvAlign, h, subsamp); else dstBuf = new byte[TJ.bufSize(w, h, subsamp)]; for (i = 0; i < w * h * 4; i++) { @@ -871,7 +871,7 @@ static void bufSizeTest() throws Exception { srcBuf = new byte[h * w * 4]; if (doYUV) - dstImage = new YUVImage(h, pad, w, subsamp); + dstImage = new YUVImage(h, yuvAlign, w, subsamp); else dstBuf = new byte[TJ.bufSize(h, w, subsamp)]; for (i = 0; i < h * w * 4; i++) { @@ -903,7 +903,7 @@ public static void main(String[] argv) { if (argv[i].equalsIgnoreCase("-yuv")) doYUV = true; else if (argv[i].equalsIgnoreCase("-noyuvpad")) - pad = 1; + yuvAlign = 1; else if (argv[i].equalsIgnoreCase("-bi")) { bi = true; testName = "javabitest"; diff --git a/java/doc/index-all.html b/java/doc/index-all.html index 5def53eef..df8e653f5 100644 --- a/java/doc/index-all.html +++ b/java/doc/index-all.html @@ -74,8 +74,9 @@

B

bufSizeYUV(int, int, int, int) - Static method in class org.libjpegturbo.turbojpeg.TJ
-
Returns the size of the buffer (in bytes) required to hold a YUV planar - image with the given width, height, and level of chrominance subsampling.
+
Returns the size of the buffer (in bytes) required to hold a unified + planar YUV image with the given width, height, and level of chrominance + subsampling.
bufSizeYUV(int, int, int) - Static method in class org.libjpegturbo.turbojpeg.TJ
@@ -103,13 +104,14 @@

C

compress(byte[], int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
-
Compress the uncompressed source image associated with this compressor - instance and output a JPEG image to the given destination buffer.
+
Compress the packed-pixel or planar YUV source image associated with this + compressor instance and output a JPEG image to the given destination + buffer.
compress(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
-
Compress the uncompressed source image associated with this compressor - instance and return a buffer containing a JPEG image.
+
Compress the packed-pixel or planar YUV source image associated with this + compressor instance and return a buffer containing a JPEG image.
compress(BufferedImage, byte[], int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
@@ -161,9 +163,9 @@

D

decompress(byte[], int, int, int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
-
Decompress the JPEG source image or decode the YUV source image associated - with this decompressor instance and output a grayscale, RGB, or CMYK image - to the given destination buffer.
+
Decompress the JPEG source image or decode the planar YUV source image + associated with this decompressor instance and output a packed-pixel + grayscale, RGB, or CMYK image to the given destination buffer.
decompress(byte[], int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
@@ -174,32 +176,34 @@

D

decompress(int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
-
Decompress the JPEG source image associated with this decompressor - instance and return a buffer containing the decompressed image.
+
Decompress the JPEG source image or decode the planar YUV source image + associated with this decompressor instance and return a buffer containing + the packed-pixel decompressed image.
decompress(int[], int, int, int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
-
Decompress the JPEG source image or decode the YUV source image associated - with this decompressor instance and output a grayscale, RGB, or CMYK image - to the given destination buffer.
+
Decompress the JPEG source image or decode the planar YUV source image + associated with this decompressor instance and output a packed-pixel + grayscale, RGB, or CMYK image to the given destination buffer.
decompress(BufferedImage, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
-
Decompress the JPEG source image or decode the YUV source image associated - with this decompressor instance and output a decompressed/decoded image to - the given BufferedImage instance.
+
Decompress the JPEG source image or decode the planar YUV source image + associated with this decompressor instance and output a packed-pixel + decompressed/decoded image to the given BufferedImage + instance.
decompress(int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
Decompress the JPEG source image or decode the YUV source image associated with this decompressor instance and return a BufferedImage - instance containing the decompressed/decoded image.
+ instance containing the packed-pixel decompressed/decoded image.
decompressToYUV(YUVImage, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
Decompress the JPEG source image associated with this decompressor - instance into a YUV planar image and store it in the given - YUVImage instance.
+ instance into a planar YUV image and store it in the given + YUVImage instance.
decompressToYUV(byte[], int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
@@ -211,13 +215,13 @@

D

Decompress the JPEG source image associated with this decompressor instance into a set of Y, U (Cb), and V (Cr) image planes and return a - YUVImage instance containing the decompressed image planes.
+ YUVImage instance containing the decompressed image planes.
decompressToYUV(int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
Decompress the JPEG source image associated with this decompressor - instance into a unified YUV planar image buffer and return a - YUVImage instance containing the decompressed image.
+ instance into a unified planar YUV image and return a YUVImage + instance containing the decompressed image.
decompressToYUV(int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
@@ -233,9 +237,9 @@

E

encodeYUV(YUVImage, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
-
Encode the uncompressed source image associated with this compressor - instance into a YUV planar image and store it in the given - YUVImage instance.
+
Encode the packed-pixel source image associated with this compressor + instance into a planar YUV image and store it in the given + YUVImage instance.
encodeYUV(byte[], int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
@@ -245,15 +249,15 @@

E

encodeYUV(int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
-
Encode the uncompressed source image associated with this compressor - instance into a unified YUV planar image buffer and return a - YUVImage instance containing the encoded image.
+
Encode the packed-pixel source image associated with this compressor + instance into a unified planar YUV image and return a YUVImage + instance containing the encoded image.
encodeYUV(int[], int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
-
Encode the uncompressed source image associated with this compressor +
Encode the packed-pixel source image associated with this compressor instance into separate Y, U (Cb), and V (Cr) image planes and return a - YUVImage instance containing the encoded image planes.
+ YUVImage instance containing the encoded image planes.
encodeYUV(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
@@ -288,8 +292,8 @@

E

ERR_WARNING - Static variable in class org.libjpegturbo.turbojpeg.TJ
-
The error was non-fatal and recoverable, but the image may still be - corrupt.
+
The error was non-fatal and recoverable, but the destination image may + still be corrupt.
@@ -303,23 +307,21 @@

F

 
FLAG_ACCURATEDCT - Static variable in class org.libjpegturbo.turbojpeg.TJ
-
Use the most accurate DCT/IDCT algorithm available in the underlying - codec.
+
Use the most accurate DCT/IDCT algorithm available.
FLAG_BOTTOMUP - Static variable in class org.libjpegturbo.turbojpeg.TJ
-
The uncompressed source/destination image is stored in bottom-up (Windows, - OpenGL) order, not top-down (X11) order.
+
Rows in the packed-pixel source/destination image are stored in bottom-up + (Windows, OpenGL) order rather than in top-down (X11) order.
FLAG_FASTDCT - Static variable in class org.libjpegturbo.turbojpeg.TJ
-
Use the fastest DCT/IDCT algorithm available in the underlying codec.
+
Use the fastest DCT/IDCT algorithm available.
FLAG_FASTUPSAMPLE - Static variable in class org.libjpegturbo.turbojpeg.TJ
When decompressing an image that was compressed using chrominance - subsampling, use the fastest chrominance upsampling algorithm available in - the underlying codec.
+ subsampling, use the fastest chrominance upsampling algorithm available.
FLAG_FORCEMMX - Static variable in class org.libjpegturbo.turbojpeg.TJ
@@ -350,7 +352,7 @@

F

FLAG_STOPONWARNING - Static variable in class org.libjpegturbo.turbojpeg.TJ
Immediately discontinue the current compression/decompression/transform - operation if the underlying codec throws a warning (non-fatal error).
+ operation if a warning (non-fatal error) occurs.
@@ -370,8 +372,8 @@

G

getBuf() - Method in class org.libjpegturbo.turbojpeg.YUVImage
-
Returns the YUV image buffer (if this image is stored in a unified - buffer rather than separate image planes.)
+
Returns the YUV buffer (if this image is stored in a unified buffer rather + than separate image planes.)
getColorspace() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
@@ -408,7 +410,7 @@

G

getJPEGBuf() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
-
Returns the JPEG image buffer associated with this decompressor instance.
+
Returns the JPEG buffer associated with this decompressor instance.
getJPEGSize() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
@@ -436,7 +438,7 @@

G

getPad() - Method in class org.libjpegturbo.turbojpeg.YUVImage
-
Returns the line padding used in the YUV image buffer (if this image is +
Returns the row alignment (in bytes) of the YUV buffer (if this image is stored in a unified buffer rather than separate image planes.)
getPixelSize(int) - Static method in class org.libjpegturbo.turbojpeg.TJ
@@ -470,17 +472,17 @@

G

getScalingFactors() - Static method in class org.libjpegturbo.turbojpeg.TJ
-
Returns a list of fractional scaling factors that the JPEG decompressor in - this implementation of TurboJPEG supports.
+
Returns a list of fractional scaling factors that the JPEG decompressor + supports.
getSize() - Method in class org.libjpegturbo.turbojpeg.YUVImage
-
Returns the size (in bytes) of the YUV image buffer (if this image is - stored in a unified buffer rather than separate image planes.)
+
Returns the size (in bytes) of the YUV buffer (if this image is stored in + a unified buffer rather than separate image planes.)
getStrides() - Method in class org.libjpegturbo.turbojpeg.YUVImage
-
Returns the number of bytes per line of each plane in the YUV image.
+
Returns the number of bytes per row of each plane in the YUV image.
getSubsamp() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
@@ -494,7 +496,7 @@

G

getTransformedSizes() - Method in class org.libjpegturbo.turbojpeg.TJTransformer
Returns an array containing the sizes of the transformed JPEG images - generated by the most recent transform operation.
+ (in bytes) generated by the most recent transform operation.
getWidth() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
@@ -578,7 +580,7 @@

O

op - Variable in class org.libjpegturbo.turbojpeg.TJTransform
-
Transform operation (one of OP_*)
+
Transform operation (one of OP_*)
OP_HFLIP - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
@@ -616,7 +618,7 @@

O

OPT_COPYNONE - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
This option will prevent TJTransformer.transform() from copying any extra markers (including EXIF - and ICC profile data) from the source image to the output image.
+ and ICC profile data) from the source image to the destination image.
OPT_CROP - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
@@ -624,8 +626,8 @@

O

OPT_GRAY - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
-
This option will discard the color data in the input image and produce - a grayscale output image.
+
This option will discard the color data in the source image and produce a + grayscale destination image.
OPT_NOOUTPUT - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
@@ -639,7 +641,7 @@

O

OPT_PROGRESSIVE - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
-
This option will enable progressive entropy coding in the output image +
This option will enable progressive entropy coding in the JPEG image generated by this particular transform.
OPT_TRIM - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
@@ -649,7 +651,8 @@

O

options - Variable in class org.libjpegturbo.turbojpeg.TJTransform
-
Transform options (bitwise OR of one or more of OPT_*)
+
Transform options (bitwise OR of one or more of + OPT_*)
org.libjpegturbo.turbojpeg - package org.libjpegturbo.turbojpeg
 
@@ -756,7 +759,7 @@

S

setBuf(byte[], int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.YUVImage
-
Assign a unified image buffer to this YUVImage instance.
+
Assign a unified buffer to this YUVImage instance.
setJPEGImage(byte[], int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
@@ -770,7 +773,7 @@

S

setSourceImage(byte[], int, int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
-
Associate an uncompressed RGB, grayscale, or CMYK source image with this +
Associate a packed-pixel RGB, grayscale, or CMYK source image with this compressor instance.
setSourceImage(byte[], int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
@@ -782,13 +785,12 @@

S

setSourceImage(BufferedImage, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
-
Associate an uncompressed RGB or grayscale source image with this +
Associate a packed-pixel RGB or grayscale source image with this compressor instance.
setSourceImage(YUVImage) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
-
Associate an uncompressed YUV planar source image with this compressor - instance.
+
Associate a planar YUV source image with this compressor instance.
setSourceImage(byte[], int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
@@ -798,7 +800,7 @@

S

setSourceImage(YUVImage) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
-
Associate the specified YUV planar source image with this decompressor +
Associate the specified planar YUV source image with this decompressor instance.
setSubsamp(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
@@ -826,7 +828,7 @@

T

TJCompressor(byte[], int, int, int, int, int, int) - Constructor for class org.libjpegturbo.turbojpeg.TJCompressor
-
Create a TurboJPEG compressor instance and associate the uncompressed +
Create a TurboJPEG compressor instance and associate the packed-pixel source image stored in srcImage with the newly created instance.
@@ -839,7 +841,7 @@

T

TJCompressor(BufferedImage, int, int, int, int) - Constructor for class org.libjpegturbo.turbojpeg.TJCompressor
-
Create a TurboJPEG compressor instance and associate the uncompressed +
Create a TurboJPEG compressor instance and associate the packed-pixel source image stored in srcImage with the newly created instance.
@@ -858,17 +860,19 @@

T

TJDecompressor(byte[]) - Constructor for class org.libjpegturbo.turbojpeg.TJDecompressor
Create a TurboJPEG decompressor instance and associate the JPEG source - image stored in jpegImage with the newly created instance.
+ image or "abbreviated table specification" (AKA "tables-only") datastream + stored in jpegImage with the newly created instance.
TJDecompressor(byte[], int) - Constructor for class org.libjpegturbo.turbojpeg.TJDecompressor
Create a TurboJPEG decompressor instance and associate the JPEG source - image of length imageSize bytes stored in - jpegImage with the newly created instance.
+ image or "abbreviated table specification" (AKA "tables-only") datastream + of length imageSize bytes stored in jpegImage + with the newly created instance.
TJDecompressor(YUVImage) - Constructor for class org.libjpegturbo.turbojpeg.TJDecompressor
-
Create a TurboJPEG decompressor instance and associate the YUV planar +
Create a TurboJPEG decompressor instance and associate the planar YUV source image stored in yuvImage with the newly created instance.
@@ -919,25 +923,26 @@

T

TJTransformer(byte[]) - Constructor for class org.libjpegturbo.turbojpeg.TJTransformer
Create a TurboJPEG lossless transformer instance and associate the JPEG - image stored in jpegImage with the newly created instance.
+ source image stored in jpegImage with the newly created + instance.
TJTransformer(byte[], int) - Constructor for class org.libjpegturbo.turbojpeg.TJTransformer
Create a TurboJPEG lossless transformer instance and associate the JPEG - image of length imageSize bytes stored in + source image of length imageSize bytes stored in jpegImage with the newly created instance.
transform(byte[][], TJTransform[], int) - Method in class org.libjpegturbo.turbojpeg.TJTransformer
-
Losslessly transform the JPEG image associated with this transformer - instance into one or more JPEG images stored in the given destination - buffers.
+
Losslessly transform the JPEG source image associated with this + transformer instance into one or more JPEG images stored in the given + destination buffers.
transform(TJTransform[], int) - Method in class org.libjpegturbo.turbojpeg.TJTransformer
-
Losslessly transform the JPEG image associated with this transformer - instance and return an array of TJDecompressor instances, each of - which has a transformed JPEG image associated with it.
+
Losslessly transform the JPEG source image associated with this + transformer instance and return an array of TJDecompressor + instances, each of which has a transformed JPEG image associated with it.

@@ -945,13 +950,15 @@

T

Y

+
yuvAlign - Variable in class org.libjpegturbo.turbojpeg.YUVImage
+
 
yuvHeight - Variable in class org.libjpegturbo.turbojpeg.YUVImage
 
yuvImage - Variable in class org.libjpegturbo.turbojpeg.TJDecompressor
 
YUVImage - Class in org.libjpegturbo.turbojpeg
-
This class encapsulates a YUV planar image and the metadata +
This class encapsulates a planar YUV image and the metadata associated with it.
YUVImage(int, int[], int, int) - Constructor for class org.libjpegturbo.turbojpeg.YUVImage
@@ -961,8 +968,8 @@

Y

YUVImage(int, int, int, int) - Constructor for class org.libjpegturbo.turbojpeg.YUVImage
-
Create a new YUVImage instance backed by a unified image - buffer, and allocate memory for the image buffer.
+
Create a new YUVImage instance backed by a unified buffer, + and allocate memory for the buffer.
YUVImage(byte[][], int[], int, int[], int, int) - Constructor for class org.libjpegturbo.turbojpeg.YUVImage
@@ -971,13 +978,11 @@

Y

YUVImage(byte[], int, int, int, int) - Constructor for class org.libjpegturbo.turbojpeg.YUVImage
-
Create a new YUVImage instance from an existing unified image +
Create a new YUVImage instance from an existing unified buffer.
yuvOffsets - Variable in class org.libjpegturbo.turbojpeg.YUVImage
 
-
yuvPad - Variable in class org.libjpegturbo.turbojpeg.YUVImage
-
 
yuvPlanes - Variable in class org.libjpegturbo.turbojpeg.YUVImage
 
yuvStrides - Variable in class org.libjpegturbo.turbojpeg.YUVImage
diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJ.html b/java/doc/org/libjpegturbo/turbojpeg/TJ.html index 2a3de371c..f57baa7c4 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJ.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJ.html @@ -156,36 +156,34 @@

Field Summary

static int ERR_WARNING -
The error was non-fatal and recoverable, but the image may still be - corrupt.
+
The error was non-fatal and recoverable, but the destination image may + still be corrupt.
static int FLAG_ACCURATEDCT -
Use the most accurate DCT/IDCT algorithm available in the underlying - codec.
+
Use the most accurate DCT/IDCT algorithm available.
static int FLAG_BOTTOMUP -
The uncompressed source/destination image is stored in bottom-up (Windows, - OpenGL) order, not top-down (X11) order.
+
Rows in the packed-pixel source/destination image are stored in bottom-up + (Windows, OpenGL) order rather than in top-down (X11) order.
static int FLAG_FASTDCT -
Use the fastest DCT/IDCT algorithm available in the underlying codec.
+
Use the fastest DCT/IDCT algorithm available.
static int FLAG_FASTUPSAMPLE
When decompressing an image that was compressed using chrominance - subsampling, use the fastest chrominance upsampling algorithm available in - the underlying codec.
+ subsampling, use the fastest chrominance upsampling algorithm available. @@ -230,7 +228,7 @@

Field Summary

static int FLAG_STOPONWARNING
Immediately discontinue the current compression/decompression/transform - operation if the underlying codec throws a warning (non-fatal error).
+ operation if a warning (non-fatal error) occurs. @@ -402,11 +400,12 @@

Method Summary

static int bufSizeYUV(int width, - int pad, + int align, int height, int subsamp) -
Returns the size of the buffer (in bytes) required to hold a YUV planar - image with the given width, height, and level of chrominance subsampling.
+
Returns the size of the buffer (in bytes) required to hold a unified + planar YUV image with the given width, height, and level of chrominance + subsampling.
@@ -460,8 +459,8 @@

Method Summary

static TJScalingFactor[] getScalingFactors() -
Returns a list of fractional scaling factors that the JPEG decompressor in - this implementation of TurboJPEG supports.
+
Returns a list of fractional scaling factors that the JPEG decompressor + supports.
@@ -778,8 +777,8 @@

PF_CMYK

vice versa, but the mapping is typically not 1:1 or reversible, nor can it be defined with a simple formula. Thus, such a conversion is out of scope for a codec library. However, the TurboJPEG API allows for compressing - CMYK pixels into a YCCK JPEG image (see CS_YCCK) and - decompressing YCCK JPEG images into CMYK pixels. + packed-pixel CMYK images into YCCK JPEG images (see CS_YCCK) and + decompressing YCCK JPEG images into packed-pixel CMYK images.
See Also:
Constant Field Values
@@ -804,8 +803,9 @@

CS_RGB

RGB colorspace. When compressing the JPEG image, the R, G, and B components in the source image are reordered into image planes, but no colorspace conversion or subsampling is performed. RGB JPEG images can be - decompressed to any of the extended RGB pixel formats or grayscale, but - they cannot be decompressed to YUV images.
+ decompressed to packed-pixel images with any of the extended RGB or + grayscale pixel formats, but they cannot be decompressed to planar YUV + images.
See Also:
Constant Field Values
@@ -826,10 +826,11 @@

CS_YCbCr

transformation allowed the same signal to drive both black & white and color televisions, but JPEG images use YCbCr primarily because it allows the color data to be optionally subsampled for the purposes of reducing - bandwidth or disk space. YCbCr is the most common JPEG colorspace, and - YCbCr JPEG images can be compressed from and decompressed to any of the - extended RGB pixel formats or grayscale, or they can be decompressed to - YUV planar images. + network or disk usage. YCbCr is the most common JPEG colorspace, and + YCbCr JPEG images can be compressed from and decompressed to packed-pixel + images with any of the extended RGB or grayscale pixel formats. YCbCr + JPEG images can also be compressed from and decompressed to planar YUV + images.
See Also:
Constant Field Values
@@ -842,9 +843,10 @@

CS_GRAY

public static final int CS_GRAY
Grayscale colorspace. The JPEG image retains only the luminance data (Y component), and any color data from the source image is discarded. - Grayscale JPEG images can be compressed from and decompressed to any of - the extended RGB pixel formats or grayscale, or they can be decompressed - to YUV planar images.
+ Grayscale JPEG images can be compressed from and decompressed to + packed-pixel images with any of the extended RGB or grayscale pixel + formats, or they can be compressed from and decompressed to planar YUV + images.
See Also:
Constant Field Values
@@ -858,7 +860,7 @@

CS_CMYK

CMYK colorspace. When compressing the JPEG image, the C, M, Y, and K components in the source image are reordered into image planes, but no colorspace conversion or subsampling is performed. CMYK JPEG images can - only be decompressed to CMYK pixels.
+ only be decompressed to packed-pixel images with the CMYK pixel format.
See Also:
Constant Field Values
@@ -875,7 +877,7 @@

CS_YCCK

reversibly transformed into YCCK, and as with YCbCr, the chrominance components in the YCCK pixels can be subsampled without incurring major perceptual loss. YCCK JPEG images can only be compressed from and - decompressed to CMYK pixels. + decompressed to packed-pixel images with the CMYK pixel format.
See Also:
Constant Field Values
@@ -886,8 +888,8 @@

CS_YCCK

  • FLAG_BOTTOMUP

    public static final int FLAG_BOTTOMUP
    -
    The uncompressed source/destination image is stored in bottom-up (Windows, - OpenGL) order, not top-down (X11) order.
    +
    Rows in the packed-pixel source/destination image are stored in bottom-up + (Windows, OpenGL) order rather than in top-down (X11) order.
    See Also:
    Constant Field Values
  • @@ -947,10 +949,10 @@

    FLAG_FORCESSE3

    FLAG_FASTUPSAMPLE

    public static final int FLAG_FASTUPSAMPLE
    When decompressing an image that was compressed using chrominance - subsampling, use the fastest chrominance upsampling algorithm available in - the underlying codec. The default is to use smooth upsampling, which - creates a smooth transition between neighboring chrominance components in - order to reduce upsampling artifacts in the decompressed image.
    + subsampling, use the fastest chrominance upsampling algorithm available. + The default is to use smooth upsampling, which creates a smooth transition + between neighboring chrominance components in order to reduce upsampling + artifacts in the decompressed image.
    See Also:
    Constant Field Values
    @@ -961,12 +963,12 @@

    FLAG_FASTUPSAMPLE

  • FLAG_FASTDCT

    public static final int FLAG_FASTDCT
    -
    Use the fastest DCT/IDCT algorithm available in the underlying codec. The - default if this flag is not specified is implementation-specific. For - example, the implementation of TurboJPEG for libjpeg[-turbo] uses the fast - algorithm by default when compressing, because this has been shown to have - only a very slight effect on accuracy, but it uses the accurate algorithm - when decompressing, because this has been shown to have a larger effect.
    +
    Use the fastest DCT/IDCT algorithm available. The default if this flag is + not specified is implementation-specific. For example, the implementation + of the TurboJPEG API in libjpeg-turbo uses the fast algorithm by default + when compressing, because this has been shown to have only a very slight + effect on accuracy, but it uses the accurate algorithm when decompressing, + because this has been shown to have a larger effect.
    See Also:
    Constant Field Values
  • @@ -977,13 +979,12 @@

    FLAG_FASTDCT

  • FLAG_ACCURATEDCT

    public static final int FLAG_ACCURATEDCT
    -
    Use the most accurate DCT/IDCT algorithm available in the underlying - codec. The default if this flag is not specified is - implementation-specific. For example, the implementation of TurboJPEG for - libjpeg[-turbo] uses the fast algorithm by default when compressing, - because this has been shown to have only a very slight effect on accuracy, - but it uses the accurate algorithm when decompressing, because this has - been shown to have a larger effect.
    +
    Use the most accurate DCT/IDCT algorithm available. The default if this + flag is not specified is implementation-specific. For example, the + implementation of the TurboJPEG API in libjpeg-turbo uses the fast + algorithm by default when compressing, because this has been shown to have + only a very slight effect on accuracy, but it uses the accurate algorithm + when decompressing, because this has been shown to have a larger effect.
    See Also:
    Constant Field Values
  • @@ -995,14 +996,13 @@

    FLAG_ACCURATEDCT

    FLAG_STOPONWARNING

    public static final int FLAG_STOPONWARNING
    Immediately discontinue the current compression/decompression/transform - operation if the underlying codec throws a warning (non-fatal error). The - default behavior is to allow the operation to complete unless a fatal - error is encountered. + operation if a warning (non-fatal error) occurs. The default behavior is + to allow the operation to complete unless a fatal error is encountered.

    NOTE: due to the design of the TurboJPEG Java API, only certain methods (specifically, TJDecompressor.decompress*() methods - with a void return type) will complete and leave the output image in a - fully recoverable state after a non-fatal error occurs.

    + with a void return type) will complete and leave the destination image in + a fully recoverable state after a non-fatal error occurs.
    See Also:
    Constant Field Values
    @@ -1055,13 +1055,13 @@

    NUMERR

  • ERR_WARNING

    public static final int ERR_WARNING
    -
    The error was non-fatal and recoverable, but the image may still be - corrupt. +
    The error was non-fatal and recoverable, but the destination image may + still be corrupt.

    NOTE: due to the design of the TurboJPEG Java API, only certain methods (specifically, TJDecompressor.decompress*() methods - with a void return type) will complete and leave the output image in a - fully recoverable state after a non-fatal error occurs.

    + with a void return type) will complete and leave the destination image in + a fully recoverable state after a non-fatal error occurs.
    See Also:
    Constant Field Values
  • @@ -1094,7 +1094,7 @@

    getMCUWidth

    Returns the MCU block width for the given level of chrominance subsampling.
    Parameters:
    subsamp - the level of chrominance subsampling (one of - SAMP_*)
    + SAMP_*)
    Returns:
    the MCU block width for the given level of chrominance subsampling.
    @@ -1109,7 +1109,7 @@

    getMCUHeight

    Returns the MCU block height for the given level of chrominance subsampling.
    Parameters:
    subsamp - the level of chrominance subsampling (one of - SAMP_*)
    + SAMP_*)
    Returns:
    the MCU block height for the given level of chrominance subsampling.
    @@ -1122,7 +1122,7 @@

    getMCUHeight

    getPixelSize

    public static int getPixelSize(int pixelFormat)
    Returns the pixel size (in bytes) for the given pixel format.
    -
    Parameters:
    pixelFormat - the pixel format (one of PF_*)
    +
    Parameters:
    pixelFormat - the pixel format (one of PF_*)
    Returns:
    the pixel size (in bytes) for the given pixel format.
    @@ -1138,7 +1138,7 @@

    getRedOffset

    of format TJ.PF_BGRX is stored in char pixel[], then the red component will be pixel[TJ.getRedOffset(TJ.PF_BGRX)]. -
    Parameters:
    pixelFormat - the pixel format (one of PF_*)
    +
    Parameters:
    pixelFormat - the pixel format (one of PF_*)
    Returns:
    the red offset for the given pixel format, or -1 if the pixel format does not have a red component.
    @@ -1155,7 +1155,7 @@

    getGreenOffset

    of format TJ.PF_BGRX is stored in char pixel[], then the green component will be pixel[TJ.getGreenOffset(TJ.PF_BGRX)]. -
    Parameters:
    pixelFormat - the pixel format (one of PF_*)
    +
    Parameters:
    pixelFormat - the pixel format (one of PF_*)
    Returns:
    the green offset for the given pixel format, or -1 if the pixel format does not have a green component.
    @@ -1172,7 +1172,7 @@

    getBlueOffset

    of format TJ.PF_BGRX is stored in char pixel[], then the blue component will be pixel[TJ.getBlueOffset(TJ.PF_BGRX)]. -
    Parameters:
    pixelFormat - the pixel format (one of PF_*)
    +
    Parameters:
    pixelFormat - the pixel format (one of PF_*)
    Returns:
    the blue offset for the given pixel format, or -1 if the pixel format does not have a blue component.
    @@ -1189,7 +1189,7 @@

    getAlphaOffset

    of format TJ.PF_BGRA is stored in char pixel[], then the alpha component will be pixel[TJ.getAlphaOffset(TJ.PF_BGRA)]. -
    Parameters:
    pixelFormat - the pixel format (one of PF_*)
    +
    Parameters:
    pixelFormat - the pixel format (one of PF_*)
    Returns:
    the alpha offset for the given pixel format, or -1 if the pixel format does not have a alpha component.
    @@ -1206,7 +1206,7 @@

    bufSize

    Returns the maximum size of the buffer (in bytes) required to hold a JPEG image with the given width, height, and level of chrominance subsampling.
    Parameters:
    width - the width (in pixels) of the JPEG image
    height - the height (in pixels) of the JPEG image
    jpegSubsamp - the level of chrominance subsampling to be used when - generating the JPEG image (one of TJ.SAMP_*)
    + generating the JPEG image (one of TJ.SAMP_*)
    Returns:
    the maximum size of the buffer (in bytes) required to hold a JPEG image with the given width, height, and level of chrominance subsampling.
    @@ -1218,16 +1218,20 @@

    bufSize

  • bufSizeYUV

    public static int bufSizeYUV(int width,
    -             int pad,
    +             int align,
                  int height,
                  int subsamp)
    -
    Returns the size of the buffer (in bytes) required to hold a YUV planar - image with the given width, height, and level of chrominance subsampling.
    -
    Parameters:
    width - the width (in pixels) of the YUV image
    pad - the width of each line in each plane of the image is padded to - the nearest multiple of this number of bytes (must be a power of 2.)
    height - the height (in pixels) of the YUV image
    subsamp - the level of chrominance subsampling used in the YUV - image (one of TJ.SAMP_*)
    -
    Returns:
    the size of the buffer (in bytes) required to hold a YUV planar - image with the given width, height, and level of chrominance subsampling.
    +
    Returns the size of the buffer (in bytes) required to hold a unified + planar YUV image with the given width, height, and level of chrominance + subsampling.
    +
    Parameters:
    width - the width (in pixels) of the YUV image
    align - row alignment (in bytes) of the YUV image (must be a power of + 2.) Setting this parameter to n specifies that each row in each plane of + the YUV image will be padded to the nearest multiple of n bytes + (1 = unpadded.)
    height - the height (in pixels) of the YUV image
    subsamp - the level of chrominance subsampling used in the YUV + image (one of TJ.SAMP_*)
    +
    Returns:
    the size of the buffer (in bytes) required to hold a unified + planar YUV image with the given width, height, and level of chrominance + subsampling.
  • @@ -1258,11 +1262,11 @@

    planeSizeYUV

    plane with the given parameters.
    Parameters:
    componentID - ID number of the image plane (0 = Y, 1 = U/Cb, 2 = V/Cr)
    width - width (in pixels) of the YUV image. NOTE: this is the width - of the whole image, not the plane width.
    stride - bytes per line in the image plane.
    height - height (in pixels) of the YUV image. NOTE: this is the + of the whole image, not the plane width.
    stride - bytes per row in the image plane.
    height - height (in pixels) of the YUV image. NOTE: this is the height of the whole image, not the plane height.
    subsamp - the level of chrominance subsampling used in the YUV - image (one of TJ.SAMP_*)
    -
    Returns:
    the size of the buffer (in bytes) required to hold a YUV planar - image with the given parameters.
    + image (one of TJ.SAMP_*) +
    Returns:
    the size of the buffer (in bytes) required to hold a YUV image + plane with the given parameters.
    @@ -1278,7 +1282,7 @@

    planeWidth

    Refer to
    YUVImage for a description of plane width.
    Parameters:
    componentID - ID number of the image plane (0 = Y, 1 = U/Cb, 2 = V/Cr)
    width - width (in pixels) of the YUV image
    subsamp - the level of chrominance subsampling used in the YUV image - (one of TJ.SAMP_*)
    + (one of TJ.SAMP_*)
    Returns:
    the plane width of a YUV image plane with the given parameters.
    @@ -1295,7 +1299,7 @@

    planeHeight

    Refer to YUVImage for a description of plane height.
    Parameters:
    componentID - ID number of the image plane (0 = Y, 1 = U/Cb, 2 = V/Cr)
    height - height (in pixels) of the YUV image
    subsamp - the level of chrominance subsampling used in the YUV image - (one of TJ.SAMP_*)
    + (one of TJ.SAMP_*)
    Returns:
    the plane height of a YUV image plane with the given parameters.
    @@ -1306,10 +1310,10 @@

    planeHeight

  • getScalingFactors

    public static TJScalingFactor[] getScalingFactors()
    -
    Returns a list of fractional scaling factors that the JPEG decompressor in - this implementation of TurboJPEG supports.
    -
    Returns:
    a list of fractional scaling factors that the JPEG decompressor in - this implementation of TurboJPEG supports.
    +
    Returns a list of fractional scaling factors that the JPEG decompressor + supports.
    +
    Returns:
    a list of fractional scaling factors that the JPEG decompressor + supports.
  • diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html b/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html index a53f87973..84242e3fb 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html @@ -132,7 +132,7 @@

    Constructor Summary

    int y, int width, int height) -
    Create a TurboJPEG compressor instance and associate the uncompressed +
    Create a TurboJPEG compressor instance and associate the packed-pixel source image stored in srcImage with the newly created instance.
    @@ -157,7 +157,7 @@

    Constructor Summary

    int pitch, int height, int pixelFormat) -
    Create a TurboJPEG compressor instance and associate the uncompressed +
    Create a TurboJPEG compressor instance and associate the packed-pixel source image stored in srcImage with the newly created instance.
    @@ -210,15 +210,16 @@

    Method Summary

    void compress(byte[] dstBuf, int flags) -
    Compress the uncompressed source image associated with this compressor - instance and output a JPEG image to the given destination buffer.
    +
    Compress the packed-pixel or planar YUV source image associated with this + compressor instance and output a JPEG image to the given destination + buffer.
    byte[] compress(int flags) -
    Compress the uncompressed source image associated with this compressor - instance and return a buffer containing a JPEG image.
    +
    Compress the packed-pixel or planar YUV source image associated with this + compressor instance and return a buffer containing a JPEG image.
    @@ -265,27 +266,27 @@

    Method Summary

    YUVImage encodeYUV(int[] strides, int flags) -
    Encode the uncompressed source image associated with this compressor +
    Encode the packed-pixel source image associated with this compressor instance into separate Y, U (Cb), and V (Cr) image planes and return a - YUVImage instance containing the encoded image planes.
    + YUVImage instance containing the encoded image planes.
    YUVImage -encodeYUV(int pad, +encodeYUV(int align, int flags) -
    Encode the uncompressed source image associated with this compressor - instance into a unified YUV planar image buffer and return a - YUVImage instance containing the encoded image.
    +
    Encode the packed-pixel source image associated with this compressor + instance into a unified planar YUV image and return a YUVImage + instance containing the encoded image.
    void encodeYUV(YUVImage dstImage, int flags) -
    Encode the uncompressed source image associated with this compressor - instance into a YUV planar image and store it in the given - YUVImage instance.
    +
    Encode the packed-pixel source image associated with this compressor + instance into a planar YUV image and store it in the given + YUVImage instance.
    @@ -312,7 +313,7 @@

    Method Summary

    int y, int width, int height)
    -
    Associate an uncompressed RGB or grayscale source image with this +
    Associate a packed-pixel RGB or grayscale source image with this compressor instance.
    @@ -338,15 +339,14 @@

    Method Summary

    int pitch, int height, int pixelFormat) -
    Associate an uncompressed RGB, grayscale, or CMYK source image with this +
    Associate a packed-pixel RGB, grayscale, or CMYK source image with this compressor instance.
    void setSourceImage(YUVImage srcImage) -
    Associate an uncompressed YUV planar source image with this compressor - instance.
    +
    Associate a planar YUV source image with this compressor instance.
    @@ -405,7 +405,7 @@

    TJCompressor

    int height, int pixelFormat) throws TJException -
    Create a TurboJPEG compressor instance and associate the uncompressed +
    Create a TurboJPEG compressor instance and associate the packed-pixel source image stored in srcImage with the newly created instance.
    Parameters:
    srcImage - see setSourceImage(byte[], int, int, int, int, int, int) for description
    x - see setSourceImage(byte[], int, int, int, int, int, int) for description
    y - see setSourceImage(byte[], int, int, int, int, int, int) for description
    width - see setSourceImage(byte[], int, int, int, int, int, int) for description
    pitch - see setSourceImage(byte[], int, int, int, int, int, int) for description
    height - see setSourceImage(byte[], int, int, int, int, int, int) for description
    pixelFormat - pixel format of the source image (one of @@ -445,7 +445,7 @@

    TJCompressor

    int width, int height) throws TJException -
    Create a TurboJPEG compressor instance and associate the uncompressed +
    Create a TurboJPEG compressor instance and associate the packed-pixel source image stored in srcImage with the newly created instance.
    Parameters:
    srcImage - see @@ -480,19 +480,19 @@

    setSourceImage

    int height, int pixelFormat) throws TJException -
    Associate an uncompressed RGB, grayscale, or CMYK source image with this +
    Associate a packed-pixel RGB, grayscale, or CMYK source image with this compressor instance.
    -
    Parameters:
    srcImage - image buffer containing RGB, grayscale, or CMYK pixels to - be compressed or encoded. This buffer is not modified.
    x - x offset (in pixels) of the region in the source image from which +
    Parameters:
    srcImage - buffer containing a packed-pixel RGB, grayscale, or CMYK + source image to be compressed or encoded. This buffer is not modified.
    x - x offset (in pixels) of the region in the source image from which the JPEG or YUV image should be compressed/encoded
    y - y offset (in pixels) of the region in the source image from which the JPEG or YUV image should be compressed/encoded
    width - width (in pixels) of the region in the source image from - which the JPEG or YUV image should be compressed/encoded
    pitch - bytes per line of the source image. Normally, this should be - width * TJ.pixelSize(pixelFormat) if the source image is - unpadded, but you can use this parameter to, for instance, specify that - the scanlines in the source image are padded to a 4-byte boundary or to - compress/encode a JPEG or YUV image from a region of a larger source - image. You can also be clever and use this parameter to skip lines, etc. - Setting this parameter to 0 is the equivalent of setting it to + which the JPEG or YUV image should be compressed/encoded
    pitch - bytes per row in the source image. Normally this should be + width * TJ.pixelSize(pixelFormat), if the source image is + unpadded. However, you can use this parameter to, for instance, specify + that the rows in the source image are padded to the nearest multiple of 4 + bytes or to compress/encode a JPEG or YUV image from a region of a larger + source image. You can also be clever and use this parameter to skip rows, + etc. Setting this parameter to 0 is the equivalent of setting it to width * TJ.pixelSize(pixelFormat).
    height - height (in pixels) of the region in the source image from which the JPEG or YUV image should be compressed/encoded
    pixelFormat - pixel format of the source image (one of TJ.PF_*)
    @@ -531,10 +531,11 @@

    setSourceImage

    int width, int height) throws TJException -
    Associate an uncompressed RGB or grayscale source image with this +
    Associate a packed-pixel RGB or grayscale source image with this compressor instance.
    -
    Parameters:
    srcImage - a BufferedImage instance containing RGB or - grayscale pixels to be compressed or encoded. This image is not modified.
    x - x offset (in pixels) of the region in the source image from which +
    Parameters:
    srcImage - a BufferedImage instance containing a + packed-pixel RGB or grayscale source image to be compressed or encoded. + This image is not modified.
    x - x offset (in pixels) of the region in the source image from which the JPEG or YUV image should be compressed/encoded
    y - y offset (in pixels) of the region in the source image from which the JPEG or YUV image should be compressed/encoded
    width - width (in pixels) of the region in the source image from which the JPEG or YUV image should be compressed/encoded (0 = use the @@ -553,10 +554,9 @@

    setSourceImage

    setSourceImage

    public void setSourceImage(YUVImage srcImage)
                         throws TJException
    -
    Associate an uncompressed YUV planar source image with this compressor - instance.
    -
    Parameters:
    srcImage - YUV planar image to be compressed. This image is not - modified.
    +
    Associate a planar YUV source image with this compressor instance.
    +
    Parameters:
    srcImage - planar YUV source image to be compressed. This image is + not modified.
    Throws:
    TJException
    @@ -573,16 +573,16 @@

    setSubsamp

    TJ.CS_YCbCr) or from CMYK to YCCK (see TJ.CS_YCCK) as part of the JPEG compression process, some of the Cb and Cr (chrominance) components can be discarded or averaged together to produce a smaller - image with little perceptible loss of image clarity (the human eye is more - sensitive to small changes in brightness than to small changes in color.) - This is called "chrominance subsampling". + image with little perceptible loss of image clarity. (The human eye is + more sensitive to small changes in brightness than to small changes in + color.) This is called "chrominance subsampling".

    - NOTE: This method has no effect when compressing a JPEG image from a YUV - planar source. In that case, the level of chrominance subsampling in - the JPEG image is determined by the source. Furthermore, this method has - no effect when encoding to a pre-allocated YUVImage instance. In - that case, the level of chrominance subsampling is determined by the - destination.

    + NOTE: This method has no effect when compressing a JPEG image from a + planar YUV source image. In that case, the level of chrominance + subsampling in the JPEG image is determined by the source image. + Furthermore, this method has no effect when encoding to a pre-allocated + YUVImage instance. In that case, the level of chrominance + subsampling is determined by the destination image.
    Parameters:
    newSubsamp - the level of chrominance subsampling to use in subsequent compress/encode oeprations (one of TJ.SAMP_*)
    @@ -609,8 +609,9 @@

    compress

    public void compress(byte[] dstBuf,
                 int flags)
                   throws TJException
    -
    Compress the uncompressed source image associated with this compressor - instance and output a JPEG image to the given destination buffer.
    +
    Compress the packed-pixel or planar YUV source image associated with this + compressor instance and output a JPEG image to the given destination + buffer.
    Parameters:
    dstBuf - buffer that will receive the JPEG image. Use TJ.bufSize(int, int, int) to determine the maximum size for this buffer based on the source image's width and height and the desired level of chrominance @@ -628,8 +629,8 @@

    compress

    compress

    public byte[] compress(int flags)
                     throws TJException
    -
    Compress the uncompressed source image associated with this compressor - instance and return a buffer containing a JPEG image.
    +
    Compress the packed-pixel or planar YUV source image associated with this + compressor instance and return a buffer containing a JPEG image.
    Parameters:
    flags - the bitwise OR of one or more of TJ.FLAG_*
    Returns:
    a buffer containing a JPEG image. The length of this buffer will @@ -682,13 +683,13 @@

    encodeYUV

    public void encodeYUV(YUVImage dstImage,
                  int flags)
                    throws TJException
    -
    Encode the uncompressed source image associated with this compressor - instance into a YUV planar image and store it in the given - YUVImage instance. This method uses the accelerated color - conversion routines in TurboJPEG's underlying codec but does not execute - any of the other steps in the JPEG compression process. Encoding - CMYK source images to YUV is not supported.
    -
    Parameters:
    dstImage - YUVImage instance that will receive the YUV planar +
    Encode the packed-pixel source image associated with this compressor + instance into a planar YUV image and store it in the given + YUVImage instance. This method performs color conversion (which + is accelerated in the libjpeg-turbo implementation) but does not execute + any of the other steps in the JPEG compression process. Encoding CMYK + source images into YUV images is not supported.
    +
    Parameters:
    dstImage - YUVImage instance that will receive the planar YUV image
    flags - the bitwise OR of one or more of TJ.FLAG_*
    Throws:
    @@ -716,20 +717,21 @@

    encodeYUV

    • encodeYUV

      -
      public YUVImage encodeYUV(int pad,
      +
      public YUVImage encodeYUV(int align,
                        int flags)
                          throws TJException
      -
      Encode the uncompressed source image associated with this compressor - instance into a unified YUV planar image buffer and return a - YUVImage instance containing the encoded image. This method - uses the accelerated color conversion routines in TurboJPEG's underlying - codec but does not execute any of the other steps in the JPEG compression - process. Encoding CMYK source images to YUV is not supported.
      -
      Parameters:
      pad - the width of each line in each plane of the YUV image will be - padded to the nearest multiple of this number of bytes (must be a power of - 2.)
      flags - the bitwise OR of one or more of +
      Encode the packed-pixel source image associated with this compressor + instance into a unified planar YUV image and return a YUVImage + instance containing the encoded image. This method performs color + conversion (which is accelerated in the libjpeg-turbo implementation) but + does not execute any of the other steps in the JPEG compression process. + Encoding CMYK source images into YUV images is not supported.
      +
      Parameters:
      align - row alignment (in bytes) of the YUV image (must be a power of + 2.) Setting this parameter to n will cause each row in each plane of the + YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.)
      flags - the bitwise OR of one or more of TJ.FLAG_*
      -
      Returns:
      a YUV planar image.
      +
      Returns:
      a YUVImage instance containing the unified planar YUV + encoded image
      Throws:
      TJException
    • @@ -743,21 +745,22 @@

      encodeYUV

      public YUVImage encodeYUV(int[] strides,
                        int flags)
                          throws TJException
      -
      Encode the uncompressed source image associated with this compressor +
      Encode the packed-pixel source image associated with this compressor instance into separate Y, U (Cb), and V (Cr) image planes and return a - YUVImage instance containing the encoded image planes. This - method uses the accelerated color conversion routines in TurboJPEG's - underlying codec but does not execute any of the other steps in the JPEG - compression process. Encoding CMYK source images to YUV is not supported.
      + YUVImage instance containing the encoded image planes. This + method performs color conversion (which is accelerated in the + libjpeg-turbo implementation) but does not execute any of the other steps + in the JPEG compression process. Encoding CMYK source images into YUV + images is not supported.
      Parameters:
      strides - an array of integers, each specifying the number of bytes - per line in the corresponding plane of the output image. Setting the - stride for any plane to 0 is the same as setting it to the component width - of the plane. If strides is null, then the strides for all - planes will be set to their respective component widths. You can adjust - the strides in order to add an arbitrary amount of line padding to each - plane.
      flags - the bitwise OR of one or more of + per row in the corresponding plane of the YUV source image. Setting the + stride for any plane to 0 is the same as setting it to the plane width + (see YUVImage.) If strides is null, then the strides + for all planes will be set to their respective plane widths. You can + adjust the strides in order to add an arbitrary amount of row padding to + each plane.
      flags - the bitwise OR of one or more of TJ.FLAG_*
      -
      Returns:
      a YUV planar image.
      +
      Returns:
      a YUVImage instance containing the encoded image planes
      Throws:
      TJException
      diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJCustomFilter.html b/java/doc/org/libjpegturbo/turbojpeg/TJCustomFilter.html index 412dcd467..982079c47 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJCustomFilter.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJCustomFilter.html @@ -163,7 +163,7 @@

      customFilter

      into multiple DCT coefficient buffers and call the callback function once for each buffer.
    planeRegion - rectangle containing the width and height of the component plane to which coeffBuffer belongs
    componentID - ID number of the component plane to which - coeffBuffer belongs (Y, Cb, and Cr have, respectively, ID's + coeffBuffer belongs. (Y, Cb, and Cr have, respectively, ID's of 0, 1, and 2 in typical JPEG images.)
    transformID - ID number of the transformed image to which coeffBuffer belongs. This is the same as the index of the transform in the transforms array that was passed to TJTransformer.transform().
    transform - a TJTransform instance that specifies the diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html b/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html index 6666e4e17..cc7eb628b 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html @@ -180,20 +180,22 @@

    Constructor Summary

    TJDecompressor(byte[] jpegImage)
    Create a TurboJPEG decompressor instance and associate the JPEG source - image stored in jpegImage with the newly created instance.
    + image or "abbreviated table specification" (AKA "tables-only") datastream + stored in jpegImage with the newly created instance.
    TJDecompressor(byte[] jpegImage, int imageSize)
    Create a TurboJPEG decompressor instance and associate the JPEG source - image of length imageSize bytes stored in - jpegImage with the newly created instance.
    + image or "abbreviated table specification" (AKA "tables-only") datastream + of length imageSize bytes stored in jpegImage + with the newly created instance.
    TJDecompressor(YUVImage yuvImage) -
    Create a TurboJPEG decompressor instance and associate the YUV planar +
    Create a TurboJPEG decompressor instance and associate the planar YUV source image stored in yuvImage with the newly created instance.
    @@ -223,9 +225,10 @@

    Method Summary

    void decompress(java.awt.image.BufferedImage dstImage, int flags) -
    Decompress the JPEG source image or decode the YUV source image associated - with this decompressor instance and output a decompressed/decoded image to - the given BufferedImage instance.
    +
    Decompress the JPEG source image or decode the planar YUV source image + associated with this decompressor instance and output a packed-pixel + decompressed/decoded image to the given BufferedImage + instance.
    @@ -252,9 +255,9 @@

    Method Summary

    int desiredHeight, int pixelFormat, int flags)
    -
    Decompress the JPEG source image or decode the YUV source image associated - with this decompressor instance and output a grayscale, RGB, or CMYK image - to the given destination buffer.
    +
    Decompress the JPEG source image or decode the planar YUV source image + associated with this decompressor instance and output a packed-pixel + grayscale, RGB, or CMYK image to the given destination buffer.
    @@ -267,9 +270,9 @@

    Method Summary

    int desiredHeight, int pixelFormat, int flags) -
    Decompress the JPEG source image or decode the YUV source image associated - with this decompressor instance and output a grayscale, RGB, or CMYK image - to the given destination buffer.
    +
    Decompress the JPEG source image or decode the planar YUV source image + associated with this decompressor instance and output a packed-pixel + grayscale, RGB, or CMYK image to the given destination buffer.
    @@ -280,7 +283,7 @@

    Method Summary

    int flags)
    Decompress the JPEG source image or decode the YUV source image associated with this decompressor instance and return a BufferedImage - instance containing the decompressed/decoded image.
    + instance containing the packed-pixel decompressed/decoded image.
    @@ -290,8 +293,9 @@

    Method Summary

    int desiredHeight, int pixelFormat, int flags) -
    Decompress the JPEG source image associated with this decompressor - instance and return a buffer containing the decompressed image.
    +
    Decompress the JPEG source image or decode the planar YUV source image + associated with this decompressor instance and return a buffer containing + the packed-pixel decompressed image.
    @@ -319,18 +323,18 @@

    Method Summary

    int flags)
    Decompress the JPEG source image associated with this decompressor instance into a set of Y, U (Cb), and V (Cr) image planes and return a - YUVImage instance containing the decompressed image planes.
    + YUVImage instance containing the decompressed image planes.
    YUVImage decompressToYUV(int desiredWidth, - int pad, + int align, int desiredHeight, int flags)
    Decompress the JPEG source image associated with this decompressor - instance into a unified YUV planar image buffer and return a - YUVImage instance containing the decompressed image.
    + instance into a unified planar YUV image and return a YUVImage + instance containing the decompressed image.
    @@ -338,8 +342,8 @@

    Method Summary

    decompressToYUV(YUVImage dstImage, int flags)
    Decompress the JPEG source image associated with this decompressor - instance into a YUV planar image and store it in the given - YUVImage instance.
    + instance into a planar YUV image and store it in the given + YUVImage instance.
    @@ -363,7 +367,7 @@

    Method Summary

    byte[] getJPEGBuf() -
    Returns the JPEG image buffer associated with this decompressor instance.
    +
    Returns the JPEG buffer associated with this decompressor instance.
    @@ -426,7 +430,7 @@

    Method Summary

    void setSourceImage(YUVImage srcImage) -
    Associate the specified YUV planar source image with this decompressor +
    Associate the specified planar YUV source image with this decompressor instance.
    @@ -554,9 +558,11 @@

    TJDecompressor

    public TJDecompressor(byte[] jpegImage)
                    throws TJException
    Create a TurboJPEG decompressor instance and associate the JPEG source - image stored in jpegImage with the newly created instance.
    -
    Parameters:
    jpegImage - JPEG image buffer (size of the JPEG image is assumed to - be the length of the array.) This buffer is not modified.
    + image or "abbreviated table specification" (AKA "tables-only") datastream + stored in jpegImage with the newly created instance.
    +
    Parameters:
    jpegImage - buffer containing a JPEG source image or tables-only + datastream. (The size of the JPEG image or datastream is assumed to be + the length of the array.) This buffer is not modified.
    Throws:
    TJException
    @@ -571,9 +577,12 @@

    TJDecompressor

    int imageSize) throws TJException
    Create a TurboJPEG decompressor instance and associate the JPEG source - image of length imageSize bytes stored in - jpegImage with the newly created instance.
    -
    Parameters:
    jpegImage - JPEG image buffer. This buffer is not modified.
    imageSize - size of the JPEG image (in bytes)
    + image or "abbreviated table specification" (AKA "tables-only") datastream + of length imageSize bytes stored in jpegImage + with the newly created instance.
    +
    Parameters:
    jpegImage - buffer containing a JPEG source image or tables-only + datastream. This buffer is not modified.
    imageSize - size of the JPEG source image or tables-only datastream + (in bytes)
    Throws:
    TJException
    @@ -586,10 +595,10 @@

    TJDecompressor

    TJDecompressor

    public TJDecompressor(YUVImage yuvImage)
                    throws TJException
    -
    Create a TurboJPEG decompressor instance and associate the YUV planar +
    Create a TurboJPEG decompressor instance and associate the planar YUV source image stored in yuvImage with the newly created instance.
    -
    Parameters:
    yuvImage - YUVImage instance containing a YUV planar +
    Parameters:
    yuvImage - YUVImage instance containing a planar YUV source image to be decoded. This image is not modified.
    Throws:
    TJException
    @@ -622,9 +631,9 @@

    setSourceImage

    subsequent "abbreviated image" datastreams. This is useful, for instance, when decompressing video streams in which all frames share the same quantization and Huffman tables.
    -
    Parameters:
    jpegImage - buffer containing a JPEG image or an "abbreviated table - specification" (AKA "tables-only") datastream. This buffer is not - modified.
    imageSize - size of the JPEG image (in bytes)
    +
    Parameters:
    jpegImage - buffer containing a JPEG source image or tables-only + datastream. This buffer is not modified.
    imageSize - size of the JPEG source image or tables-only datastream + (in bytes)
    Throws:
    TJException
    @@ -651,11 +660,11 @@

    setJPEGImage

  • setSourceImage

    public void setSourceImage(YUVImage srcImage)
    -
    Associate the specified YUV planar source image with this decompressor - instance. Subsequent decompress operations will decode this image into an - RGB or grayscale destination image.
    -
    Parameters:
    srcImage - YUVImage instance containing a YUV planar image to - be decoded. This image is not modified.
    +
    Associate the specified planar YUV source image with this decompressor + instance. Subsequent decompress operations will decode this image into a + packed-pixel RGB or grayscale destination image.
    +
    Parameters:
    srcImage - YUVImage instance containing a planar YUV source + image to be decoded. This image is not modified.
  • @@ -719,8 +728,8 @@

    getColorspace

  • getJPEGBuf

    public byte[] getJPEGBuf()
    -
    Returns the JPEG image buffer associated with this decompressor instance.
    -
    Returns:
    the JPEG image buffer associated with this decompressor instance.
    +
    Returns the JPEG buffer associated with this decompressor instance.
    +
    Returns:
    the JPEG buffer associated with this decompressor instance.
  • @@ -748,12 +757,12 @@

    getScaledWidth

    decompressor can generate without exceeding the desired image width and height.
    Parameters:
    desiredWidth - desired width (in pixels) of the decompressed image. - Setting this to 0 is the same as setting it to the width of the JPEG image - (in other words, the width will not be considered when determining the - scaled image size.)
    desiredHeight - desired height (in pixels) of the decompressed image. + Setting this to 0 is the same as setting it to the width of the JPEG + image. (In other words, the width will not be considered when determining + the scaled image size.)
    desiredHeight - desired height (in pixels) of the decompressed image. Setting this to 0 is the same as setting it to the height of the JPEG - image (in other words, the height will not be considered when determining - the scaled image size.)
    + image. (In other words, the height will not be considered when + determining the scaled image size.)
    Returns:
    the width of the largest scaled-down image that the TurboJPEG decompressor can generate without exceeding the desired image width and height.
    @@ -771,12 +780,12 @@

    getScaledHeight

    decompressor can generate without exceeding the desired image width and height.
    Parameters:
    desiredWidth - desired width (in pixels) of the decompressed image. - Setting this to 0 is the same as setting it to the width of the JPEG image - (in other words, the width will not be considered when determining the - scaled image size.)
    desiredHeight - desired height (in pixels) of the decompressed image. + Setting this to 0 is the same as setting it to the width of the JPEG + image. (In other words, the width will not be considered when determining + the scaled image size.)
    desiredHeight - desired height (in pixels) of the decompressed image. Setting this to 0 is the same as setting it to the height of the JPEG - image (in other words, the height will not be considered when determining - the scaled image size.)
    + image. (In other words, the height will not be considered when + determining the scaled image size.)
    Returns:
    the height of the largest scaled-down image that the TurboJPEG decompressor can generate without exceeding the desired image width and height.
    @@ -797,25 +806,26 @@

    decompress

    int pixelFormat, int flags) throws
    TJException -
    Decompress the JPEG source image or decode the YUV source image associated - with this decompressor instance and output a grayscale, RGB, or CMYK image - to the given destination buffer. +
    Decompress the JPEG source image or decode the planar YUV source image + associated with this decompressor instance and output a packed-pixel + grayscale, RGB, or CMYK image to the given destination buffer.

    - NOTE: The output image is fully recoverable if this method throws a + NOTE: The destination image is fully recoverable if this method throws a non-fatal TJException (unless TJ.FLAG_STOPONWARNING is specified.)

    -
    Parameters:
    dstBuf - buffer that will receive the decompressed/decoded image. - If the source image is a JPEG image, then this buffer should normally be - pitch * scaledHeight bytes in size, where - scaledHeight can be determined by calling - scalingFactor.getScaled(jpegHeight) - with one of the scaling factors returned from TJ.getScalingFactors() or by calling getScaledHeight(int, int). If the - source image is a YUV image, then this buffer should normally be - pitch * height bytes in size, where height is - the height of the YUV image. However, the buffer may also be larger than - the dimensions of the source image, in which case the x, - y, and pitch parameters can be used to specify - the region into which the source image should be decompressed/decoded.
    x - x offset (in pixels) of the region in the destination image into +
    Parameters:
    dstBuf - buffer that will receive the packed-pixel + decompressed/decoded image. If the source image is a JPEG image, then + this buffer should normally be pitch * scaledHeight bytes in + size, where scaledHeight can be determined by calling + scalingFactor.getScaled(jpegHeight) + with one of the scaling factors returned from TJ.getScalingFactors() + or by calling getScaledHeight(int, int). If the source image is a YUV + image, then this buffer should normally be pitch * height + bytes in size, where height is the height of the YUV image. + However, the buffer may also be larger than the dimensions of the source + image, in which case the x, y, and + pitch parameters can be used to specify the region into which + the source image should be decompressed/decoded.
    x - x offset (in pixels) of the region in the destination image into which the source image should be decompressed/decoded
    y - y offset (in pixels) of the region in the destination image into which the source image should be decompressed/decoded
    desiredWidth - If the source image is a JPEG image, then this specifies the desired width (in pixels) of the decompressed image (or @@ -823,18 +833,18 @@

    decompress

    than the source image dimensions, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired dimensions. Setting this to 0 is the same as setting - it to the width of the JPEG image (in other words, the width will not be + it to the width of the JPEG image. (In other words, the width will not be considered when determining the scaled image size.) This parameter is - ignored if the source image is a YUV image.
    pitch - bytes per line of the destination image. Normally, this - should be set to scaledWidth * TJ.pixelSize(pixelFormat) if - the destination image is unpadded, but you can use this to, for instance, - pad each line of the destination image to a 4-byte boundary or to - decompress/decode the source image into a region of a larger image. NOTE: - if the source image is a JPEG image, then scaledWidth can be - determined by calling - scalingFactor.getScaled(jpegWidth) - or by calling getScaledWidth(int, int). If the source image is a - YUV image, then scaledWidth is the width of the YUV image. + ignored if the source image is a YUV image.
    pitch - bytes per row in the destination image. Normally this should + be set to scaledWidth * TJ.pixelSize(pixelFormat), if the + destination image will be unpadded. However, you can use this to, for + instance, pad each row of the destination image to the nearest multiple of + 4 bytes or to decompress/decode the source image into a region of a larger + image. NOTE: if the source image is a JPEG image, then + scaledWidth can be determined by calling + scalingFactor.getScaled(jpegWidth) + or by calling getScaledWidth(int, int). If the source image is a YUV + image, then scaledWidth is the width of the YUV image. Setting this parameter to 0 is the equivalent of setting it to scaledWidth * TJ.pixelSize(pixelFormat).
    desiredHeight - If the source image is a JPEG image, then this specifies the desired height (in pixels) of the decompressed image (or @@ -842,8 +852,8 @@

    decompress

    than the source image dimensions, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired dimensions. Setting this to 0 is the same as setting - it to the height of the JPEG image (in other words, the height will not be - considered when determining the scaled image size.) This parameter is + it to the height of the JPEG image. (In other words, the height will not + be considered when determining the scaled image size.) This parameter is ignored if the source image is a YUV image.
    pixelFormat - pixel format of the decompressed/decoded image (one of TJ.PF_*)
    flags - the bitwise OR of one or more of TJ.FLAG_*
    @@ -883,8 +893,9 @@

    decompress

    int pixelFormat, int flags) throws TJException -
    Decompress the JPEG source image associated with this decompressor - instance and return a buffer containing the decompressed image.
    +
    Decompress the JPEG source image or decode the planar YUV source image + associated with this decompressor instance and return a buffer containing + the packed-pixel decompressed image.
    Parameters:
    desiredWidth - see decompress(byte[], int, int, int, int, int, int, int) for description
    pitch - see @@ -894,7 +905,7 @@

    decompress

    for description
    pixelFormat - pixel format of the decompressed image (one of TJ.PF_*)
    flags - the bitwise OR of one or more of TJ.FLAG_*
    -
    Returns:
    a buffer containing the decompressed image.
    +
    Returns:
    a buffer containing the packed-pixel decompressed image.
    Throws:
    TJException
    @@ -909,21 +920,21 @@

    decompressToYUV

    int flags) throws TJException
    Decompress the JPEG source image associated with this decompressor - instance into a YUV planar image and store it in the given - YUVImage instance. This method performs JPEG decompression - but leaves out the color conversion step, so a planar YUV image is - generated instead of an RGB or grayscale image. This method cannot be - used to decompress JPEG source images with the CMYK or YCCK colorspace. + instance into a planar YUV image and store it in the given + YUVImage instance. This method performs JPEG decompression but + leaves out the color conversion step, so a planar YUV image is generated + instead of a packed-pixel image. This method cannot be used to decompress + JPEG source images with the CMYK or YCCK colorspace.

    - NOTE: The YUV planar output image is fully recoverable if this method + NOTE: The planar YUV destination image is fully recoverable if this method throws a non-fatal TJException (unless TJ.FLAG_STOPONWARNING is specified.)

    -
    Parameters:
    dstImage - YUVImage instance that will receive the YUV planar - image. The level of subsampling specified in this YUVImage - instance must match that of the JPEG image, and the width and height - specified in the YUVImage instance must match one of the - scaled image sizes that TurboJPEG is capable of generating from the JPEG - source image.
    flags - the bitwise OR of one or more of +
    Parameters:
    dstImage - YUVImage instance that will receive the planar YUV + decompressed image. The level of subsampling specified in this + YUVImage instance must match that of the JPEG image, and the width + and height specified in the YUVImage instance must match one of + the scaled image sizes that the decompressor is capable of generating from + the JPEG source image.
    flags - the bitwise OR of one or more of TJ.FLAG_*
    Throws:
    TJException
    @@ -957,32 +968,33 @@

    decompressToYUV

    throws TJException
    Decompress the JPEG source image associated with this decompressor instance into a set of Y, U (Cb), and V (Cr) image planes and return a - YUVImage instance containing the decompressed image planes. - This method performs JPEG decompression but leaves out the color - conversion step, so a planar YUV image is generated instead of an RGB or - grayscale image. This method cannot be used to decompress JPEG source - images with the CMYK or YCCK colorspace.
    + YUVImage instance containing the decompressed image planes. This + method performs JPEG decompression but leaves out the color conversion + step, so a planar YUV image is generated instead of a packed-pixel image. + This method cannot be used to decompress JPEG source images with the CMYK + or YCCK colorspace.
    Parameters:
    desiredWidth - desired width (in pixels) of the YUV image. If the desired image dimensions are different than the dimensions of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired dimensions. Setting this to 0 is the same as setting it to - the width of the JPEG image (in other words, the width will not be + the width of the JPEG image. (In other words, the width will not be considered when determining the scaled image size.)
    strides - an array of integers, each specifying the number of bytes - per line in the corresponding plane of the output image. Setting the - stride for any plane to 0 is the same as setting it to the scaled - component width of the plane. If strides is NULL, then the - strides for all planes will be set to their respective scaled component - widths. You can adjust the strides in order to add an arbitrary amount of - line padding to each plane.
    desiredHeight - desired height (in pixels) of the YUV image. If the + per row in the corresponding plane of the YUV image. Setting the stride + for any plane to 0 is the same as setting it to the scaled plane width + (see YUVImage.) If strides is null, then the strides + for all planes will be set to their respective scaled plane widths. You + can adjust the strides in order to add an arbitrary amount of row padding + to each plane.
    desiredHeight - desired height (in pixels) of the YUV image. If the desired image dimensions are different than the dimensions of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired dimensions. Setting this to 0 is the same as setting it to - the height of the JPEG image (in other words, the height will not be + the height of the JPEG image. (In other words, the height will not be considered when determining the scaled image size.)
    flags - the bitwise OR of one or more of TJ.FLAG_*
    -
    Returns:
    a YUV planar image.
    +
    Returns:
    a YUVImage instance containing the decompressed image + planes
    Throws:
    TJException
    @@ -994,34 +1006,34 @@

    decompressToYUV

  • decompressToYUV

    public YUVImage decompressToYUV(int desiredWidth,
    -                       int pad,
    +                       int align,
                            int desiredHeight,
                            int flags)
                              throws TJException
    Decompress the JPEG source image associated with this decompressor - instance into a unified YUV planar image buffer and return a - YUVImage instance containing the decompressed image. This - method performs JPEG decompression but leaves out the color conversion - step, so a planar YUV image is generated instead of an RGB or grayscale - image. This method cannot be used to decompress JPEG source images with - the CMYK or YCCK colorspace.
    + instance into a unified planar YUV image and return a YUVImage + instance containing the decompressed image. This method performs JPEG + decompression but leaves out the color conversion step, so a planar YUV + image is generated instead of a packed-pixel image. This method cannot be + used to decompress JPEG source images with the CMYK or YCCK colorspace.
    Parameters:
    desiredWidth - desired width (in pixels) of the YUV image. If the desired image dimensions are different than the dimensions of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired dimensions. Setting this to 0 is the same as setting it to - the width of the JPEG image (in other words, the width will not be - considered when determining the scaled image size.)
    pad - the width of each line in each plane of the YUV image will be - padded to the nearest multiple of this number of bytes (must be a power of - 2.)
    desiredHeight - desired height (in pixels) of the YUV image. If the + the width of the JPEG image. (In other words, the width will not be + considered when determining the scaled image size.)
    align - row alignment (in bytes) of the YUV image (must be a power of + 2.) Setting this parameter to n will cause each row in each plane of the + YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.)
    desiredHeight - desired height (in pixels) of the YUV image. If the desired image dimensions are different than the dimensions of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired dimensions. Setting this to 0 is the same as setting it to - the height of the JPEG image (in other words, the height will not be + the height of the JPEG image. (In other words, the height will not be considered when determining the scaled image size.)
    flags - the bitwise OR of one or more of TJ.FLAG_*
    -
    Returns:
    a YUV planar image.
    +
    Returns:
    a YUVImage instance containing the unified planar YUV + decompressed image
    Throws:
    TJException
  • @@ -1055,25 +1067,26 @@

    decompress

    int pixelFormat, int flags) throws TJException -
    Decompress the JPEG source image or decode the YUV source image associated - with this decompressor instance and output a grayscale, RGB, or CMYK image - to the given destination buffer. +
    Decompress the JPEG source image or decode the planar YUV source image + associated with this decompressor instance and output a packed-pixel + grayscale, RGB, or CMYK image to the given destination buffer.

    - NOTE: The output image is fully recoverable if this method throws a + NOTE: The destination image is fully recoverable if this method throws a non-fatal TJException (unless TJ.FLAG_STOPONWARNING is specified.)

    -
    Parameters:
    dstBuf - buffer that will receive the decompressed/decoded image. - If the source image is a JPEG image, then this buffer should normally be - stride * scaledHeight pixels in size, where - scaledHeight can be determined by calling - scalingFactor.getScaled(jpegHeight) - with one of the scaling factors returned from TJ.getScalingFactors() or by calling getScaledHeight(int, int). If the - source image is a YUV image, then this buffer should normally be - stride * height pixels in size, where height is - the height of the YUV image. However, the buffer may also be larger than - the dimensions of the JPEG image, in which case the x, - y, and stride parameters can be used to specify - the region into which the source image should be decompressed.
    x - x offset (in pixels) of the region in the destination image into +
    Parameters:
    dstBuf - buffer that will receive the packed-pixel + decompressed/decoded image. If the source image is a JPEG image, then + this buffer should normally be stride * scaledHeight pixels + in size, where scaledHeight can be determined by calling + scalingFactor.getScaled(jpegHeight) + with one of the scaling factors returned from TJ.getScalingFactors() + or by calling getScaledHeight(int, int). If the source image is a YUV + image, then this buffer should normally be stride * height + pixels in size, where height is the height of the YUV image. + However, the buffer may also be larger than the dimensions of the JPEG + image, in which case the x, y, and + stride parameters can be used to specify the region into + which the source image should be decompressed.
    x - x offset (in pixels) of the region in the destination image into which the source image should be decompressed/decoded
    y - y offset (in pixels) of the region in the destination image into which the source image should be decompressed/decoded
    desiredWidth - If the source image is a JPEG image, then this specifies the desired width (in pixels) of the decompressed image (or @@ -1081,16 +1094,16 @@

    decompress

    than the source image dimensions, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired dimensions. Setting this to 0 is the same as setting - it to the width of the JPEG image (in other words, the width will not be + it to the width of the JPEG image. (In other words, the width will not be considered when determining the scaled image size.) This parameter is - ignored if the source image is a YUV image.
    stride - pixels per line of the destination image. Normally, this + ignored if the source image is a YUV image.
    stride - pixels per row in the destination image. Normally this should be set to scaledWidth, but you can use this to, for instance, decompress the JPEG image into a region of a larger image. NOTE: if the source image is a JPEG image, then scaledWidth - can be determined by calling - scalingFactor.getScaled(jpegWidth) - or by calling getScaledWidth(int, int). If the source image is a - YUV image, then scaledWidth is the width of the YUV image. + can be determined by calling + scalingFactor.getScaled(jpegWidth) + or by calling getScaledWidth(int, int). If the source image is a YUV + image, then scaledWidth is the width of the YUV image. Setting this parameter to 0 is the equivalent of setting it to scaledWidth.
    desiredHeight - If the source image is a JPEG image, then this specifies the desired height (in pixels) of the decompressed image (or @@ -1098,8 +1111,8 @@

    decompress

    than the source image dimensions, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired dimensions. Setting this to 0 is the same as setting - it to the height of the JPEG image (in other words, the height will not be - considered when determining the scaled image size.) This parameter is + it to the height of the JPEG image. (In other words, the height will not + be considered when determining the scaled image size.) This parameter is ignored if the source image is a YUV image.
    pixelFormat - pixel format of the decompressed image (one of TJ.PF_*)
    flags - the bitwise OR of one or more of TJ.FLAG_*
    @@ -1116,20 +1129,21 @@

    decompress

    public void decompress(java.awt.image.BufferedImage dstImage,
                   int flags)
                     throws TJException
    -
    Decompress the JPEG source image or decode the YUV source image associated - with this decompressor instance and output a decompressed/decoded image to - the given BufferedImage instance. +
    Decompress the JPEG source image or decode the planar YUV source image + associated with this decompressor instance and output a packed-pixel + decompressed/decoded image to the given BufferedImage + instance.

    - NOTE: The output image is fully recoverable if this method throws a + NOTE: The destination image is fully recoverable if this method throws a non-fatal TJException (unless TJ.FLAG_STOPONWARNING is specified.)

    Parameters:
    dstImage - a BufferedImage instance that will receive - the decompressed/decoded image. If the source image is a JPEG image, then - the width and height of the BufferedImage instance must match - one of the scaled image sizes that TurboJPEG is capable of generating from - the JPEG image. If the source image is a YUV image, then the width and - height of the BufferedImage instance must match the width and - height of the YUV image.
    flags - the bitwise OR of one or more of + the packed-pixel decompressed/decoded image. If the source image is a + JPEG image, then the width and height of the BufferedImage + instance must match one of the scaled image sizes that the decompressor is + capable of generating from the JPEG image. If the source image is a YUV + image, then the width and height of the BufferedImage + instance must match the width and height of the YUV image.
    flags - the bitwise OR of one or more of TJ.FLAG_*
    Throws:
    TJException
    @@ -1148,7 +1162,7 @@

    decompress

    throws TJException
    Decompress the JPEG source image or decode the YUV source image associated with this decompressor instance and return a BufferedImage - instance containing the decompressed/decoded image.
    + instance containing the packed-pixel decompressed/decoded image.
    Parameters:
    desiredWidth - see decompress(byte[], int, int, int, int, int, int, int) for description
    desiredHeight - see @@ -1157,7 +1171,7 @@

    decompress

    instance that will be created (for instance, BufferedImage.TYPE_INT_RGB)
    flags - the bitwise OR of one or more of TJ.FLAG_*
    -
    Returns:
    a BufferedImage instance containing the +
    Returns:
    a BufferedImage instance containing the packed-pixel decompressed/decoded image.
    Throws:
    TJException
    diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJTransform.html b/java/doc/org/libjpegturbo/turbojpeg/TJTransform.html index 5f22691ef..b0141cbda 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJTransform.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJTransform.html @@ -167,7 +167,7 @@

    Field Summary

    int op -
    Transform operation (one of OP_*)
    +
    Transform operation (one of OP_*)
    @@ -223,7 +223,7 @@

    Field Summary

    static int OPT_COPYNONE
    This option will prevent TJTransformer.transform() from copying any extra markers (including EXIF - and ICC profile data) from the source image to the output image.
    + and ICC profile data) from the source image to the destination image.
    @@ -235,8 +235,8 @@

    Field Summary

    static int OPT_GRAY -
    This option will discard the color data in the input image and produce - a grayscale output image.
    +
    This option will discard the color data in the source image and produce a + grayscale destination image.
    @@ -256,7 +256,7 @@

    Field Summary

    static int OPT_PROGRESSIVE -
    This option will enable progressive entropy coding in the output image +
    This option will enable progressive entropy coding in the JPEG image generated by this particular transform.
    @@ -270,7 +270,8 @@

    Field Summary

    int options -
    Transform options (bitwise OR of one or more of OPT_*)
    +
    Transform options (bitwise OR of one or more of + OPT_*)
    @@ -509,7 +510,7 @@

    OPT_PERFECT

    the level of chrominance subsampling used. If the image's width or height is not evenly divisible by the MCU block size (see TJ.getMCUWidth(int) and TJ.getMCUHeight(int)), then there will be partial MCU blocks on the - right and/or bottom edges. It is not possible to move these partial MCU + right and/or bottom edges. It is not possible to move these partial MCU blocks to the top or left of the image, so any transform that would require that is "imperfect." If this option is not specified, then any partial MCU blocks that cannot be transformed will be left in place, which @@ -547,8 +548,8 @@

    OPT_CROP

  • OPT_GRAY

    public static final int OPT_GRAY
    -
    This option will discard the color data in the input image and produce - a grayscale output image.
    +
    This option will discard the color data in the source image and produce a + grayscale destination image.
    See Also:
    Constant Field Values
  • @@ -573,7 +574,7 @@

    OPT_NOOUTPUT

  • OPT_PROGRESSIVE

    public static final int OPT_PROGRESSIVE
    -
    This option will enable progressive entropy coding in the output image +
    This option will enable progressive entropy coding in the JPEG image generated by this particular transform. Progressive entropy coding will generally improve compression relative to baseline entropy coding (the default), but it will reduce compression and decompression performance @@ -589,7 +590,7 @@

    OPT_PROGRESSIVE

    OPT_COPYNONE

    public static final int OPT_COPYNONE
    This option will prevent TJTransformer.transform() from copying any extra markers (including EXIF - and ICC profile data) from the source image to the output image.
    + and ICC profile data) from the source image to the destination image.
    See Also:
    Constant Field Values
  • @@ -600,7 +601,7 @@

    OPT_COPYNONE

  • op

    public int op
    -
    Transform operation (one of OP_*)
    +
    Transform operation (one of OP_*)
  • @@ -610,7 +611,8 @@

    op

  • options

    public int options
    -
    Transform options (bitwise OR of one or more of OPT_*)
    +
  • @@ -661,8 +663,8 @@

    TJTransform

    equivalent of setting it to (width of the source JPEG image - x).
    h - the height of the cropping region. Setting this to 0 is the equivalent of setting it to (height of the source JPEG image - - y).
    op - one of the transform operations (OP_*)
    options - the bitwise OR of one or more of the transform options - (OPT_*)
    cf - an instance of an object that implements the TJCustomFilter interface, or null if no custom filter is needed
    + y).
    op - one of the transform operations (OP_*)
    options - the bitwise OR of one or more of the transform options + (OPT_*)
    cf - an instance of an object that implements the TJCustomFilter interface, or null if no custom filter is needed
    @@ -678,8 +680,8 @@

    TJTransform

    Create a new lossless transform instance with the given parameters.
    Parameters:
    r - a Rectangle instance that specifies the cropping region. See TJTransform(int, int, int, int, int, int, TJCustomFilter) for more - detail.
    op - one of the transform operations (OP_*)
    options - the bitwise OR of one or more of the transform options - (OPT_*)
    cf - an instance of an object that implements the TJCustomFilter interface, or null if no custom filter is needed
    + detail.
    op - one of the transform operations (OP_*)
    options - the bitwise OR of one or more of the transform options + (OPT_*)
    cf - an instance of an object that implements the TJCustomFilter interface, or null if no custom filter is needed
    diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJTransformer.html b/java/doc/org/libjpegturbo/turbojpeg/TJTransformer.html index a30fe30ca..6436b7fb9 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJTransformer.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJTransformer.html @@ -148,14 +148,15 @@

    Constructor Summary

    TJTransformer(byte[] jpegImage)
    Create a TurboJPEG lossless transformer instance and associate the JPEG - image stored in jpegImage with the newly created instance.
    + source image stored in jpegImage with the newly created + instance. TJTransformer(byte[] jpegImage, int imageSize)
    Create a TurboJPEG lossless transformer instance and associate the JPEG - image of length imageSize bytes stored in + source image of length imageSize bytes stored in jpegImage with the newly created instance.
    @@ -178,7 +179,7 @@

    Method Summary

    int[] getTransformedSizes()
    Returns an array containing the sizes of the transformed JPEG images - generated by the most recent transform operation.
    + (in bytes) generated by the most recent transform operation. @@ -186,18 +187,18 @@

    Method Summary

    transform(byte[][] dstBufs, TJTransform[] transforms, int flags) -
    Losslessly transform the JPEG image associated with this transformer - instance into one or more JPEG images stored in the given destination - buffers.
    +
    Losslessly transform the JPEG source image associated with this + transformer instance into one or more JPEG images stored in the given + destination buffers.
    TJDecompressor[] transform(TJTransform[] transforms, int flags) -
    Losslessly transform the JPEG image associated with this transformer - instance and return an array of TJDecompressor instances, each of - which has a transformed JPEG image associated with it.
    +
    Losslessly transform the JPEG source image associated with this + transformer instance and return an array of TJDecompressor + instances, each of which has a transformed JPEG image associated with it.
    @@ -251,9 +252,11 @@

    TJTransformer

    public TJTransformer(byte[] jpegImage)
                   throws TJException
    Create a TurboJPEG lossless transformer instance and associate the JPEG - image stored in jpegImage with the newly created instance.
    -
    Parameters:
    jpegImage - JPEG image buffer (size of the JPEG image is assumed to - be the length of the array.) This buffer is not modified.
    + source image stored in jpegImage with the newly created + instance. +
    Parameters:
    jpegImage - buffer containing the JPEG source image to transform. + (The size of the JPEG image is assumed to be the length of the array.) + This buffer is not modified.
    Throws:
    TJException
    @@ -268,9 +271,10 @@

    TJTransformer

    int imageSize) throws TJException
    Create a TurboJPEG lossless transformer instance and associate the JPEG - image of length imageSize bytes stored in + source image of length imageSize bytes stored in jpegImage with the newly created instance.
    -
    Parameters:
    jpegImage - JPEG image buffer. This buffer is not modified.
    imageSize - size of the JPEG image (in bytes)
    +
    Parameters:
    jpegImage - buffer containing the JPEG source image to transform. + This buffer is not modified.
    imageSize - size of the JPEG source image (in bytes)
    Throws:
    TJException
    @@ -293,25 +297,26 @@

    transform

    TJTransform[] transforms, int flags) throws TJException -
    Losslessly transform the JPEG image associated with this transformer - instance into one or more JPEG images stored in the given destination - buffers. Lossless transforms work by moving the raw coefficients from one - JPEG image structure to another without altering the values of the - coefficients. While this is typically faster than decompressing the - image, transforming it, and re-compressing it, lossless transforms are not - free. Each lossless transform requires reading and performing Huffman - decoding on all of the coefficients in the source image, regardless of the - size of the destination image. Thus, this method provides a means of - generating multiple transformed images from the same source or of applying - multiple transformations simultaneously, in order to eliminate the need to - read the source coefficients multiple times.
    -
    Parameters:
    dstBufs - an array of image buffers. dstbufs[i] will - receive a JPEG image that has been transformed using the parameters in - transforms[i]. Use TJ.bufSize(int, int, int) to determine the - maximum size for each buffer based on the transformed or cropped width and - height and the level of subsampling used in the source image.
    transforms - an array of TJTransform instances, each of +
    Losslessly transform the JPEG source image associated with this + transformer instance into one or more JPEG images stored in the given + destination buffers. Lossless transforms work by moving the raw + coefficients from one JPEG image structure to another without altering the + values of the coefficients. While this is typically faster than + decompressing the image, transforming it, and re-compressing it, lossless + transforms are not free. Each lossless transform requires reading and + performing Huffman decoding on all of the coefficients in the source + image, regardless of the size of the destination image. Thus, this method + provides a means of generating multiple transformed images from the same + source or of applying multiple transformations simultaneously, in order to + eliminate the need to read the source coefficients multiple times.
    +
    Parameters:
    dstBufs - an array of JPEG destination buffers. + dstbufs[i] will receive a JPEG image that has been + transformed using the parameters in transforms[i]. Use + TJ.bufSize(int, int, int) to determine the maximum size for each buffer based on + the transformed or cropped width and height and the level of subsampling + used in the source image.
    transforms - an array of TJTransform instances, each of which specifies the transform parameters and/or cropping region for the - corresponding transformed output image
    flags - the bitwise OR of one or more of + corresponding transformed JPEG image
    flags - the bitwise OR of one or more of TJ.FLAG_*
    Throws:
    TJException
    @@ -326,12 +331,12 @@

    transform

    public TJDecompressor[] transform(TJTransform[] transforms,
                              int flags)
                                throws TJException
    -
    Losslessly transform the JPEG image associated with this transformer - instance and return an array of TJDecompressor instances, each of - which has a transformed JPEG image associated with it.
    +
    Losslessly transform the JPEG source image associated with this + transformer instance and return an array of TJDecompressor + instances, each of which has a transformed JPEG image associated with it.
    Parameters:
    transforms - an array of TJTransform instances, each of which specifies the transform parameters and/or cropping region for the - corresponding transformed output image
    flags - the bitwise OR of one or more of + corresponding transformed JPEG image
    flags - the bitwise OR of one or more of TJ.FLAG_*
    Returns:
    an array of TJDecompressor instances, each of which has a transformed JPEG image associated with it.
    @@ -347,9 +352,9 @@

    transform

    getTransformedSizes

    public int[] getTransformedSizes()
    Returns an array containing the sizes of the transformed JPEG images - generated by the most recent transform operation.
    + (in bytes) generated by the most recent transform operation.
    Returns:
    an array containing the sizes of the transformed JPEG images - generated by the most recent transform operation.
    + (in bytes) generated by the most recent transform operation.
    diff --git a/java/doc/org/libjpegturbo/turbojpeg/YUVImage.html b/java/doc/org/libjpegturbo/turbojpeg/YUVImage.html index d4485ed6b..b08fcb3d6 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/YUVImage.html +++ b/java/doc/org/libjpegturbo/turbojpeg/YUVImage.html @@ -98,7 +98,7 @@

    Class YUVImage


    public class YUVImage
     extends java.lang.Object
    -
    This class encapsulates a YUV planar image and the metadata +
    This class encapsulates a planar YUV image and the metadata associated with it. The TurboJPEG API allows both the JPEG compression and decompression pipelines to be split into stages: YUV encode, compress from YUV, decompress to YUV, and YUV decode. A YUVImage instance @@ -106,30 +106,32 @@

    Class YUVImage

    operations and as the source image for compress-from-YUV and YUV decode operations.

    - Technically, the JPEG format uses the YCbCr colorspace (which technically is - not a "colorspace" but rather a "color transform"), but per the convention - of the digital video community, the TurboJPEG API uses "YUV" to refer to an - image format consisting of Y, Cb, and Cr image planes. + Technically, the JPEG format uses the YCbCr colorspace (which is technically + not a colorspace but a color transform), but per the convention of the + digital video community, the TurboJPEG API uses "YUV" to refer to an image + format consisting of Y, Cb, and Cr image planes.

    Each plane is simply a 2D array of bytes, each byte representing the value of one of the components (Y, Cb, or Cr) at a particular location in the image. The width and height of each plane are determined by the image width, height, and level of chrominance subsampling. The luminance plane width is the image width padded to the nearest multiple of the horizontal - subsampling factor (2 in the case of 4:2:0 and 4:2:2, 4 in the case of - 4:1:1, 1 in the case of 4:4:4 or grayscale.) Similarly, the luminance plane - height is the image height padded to the nearest multiple of the vertical - subsampling factor (2 in the case of 4:2:0 or 4:4:0, 1 in the case of 4:4:4 - or grayscale.) The chrominance plane width is equal to the luminance plane - width divided by the horizontal subsampling factor, and the chrominance - plane height is equal to the luminance plane height divided by the vertical - subsampling factor. + subsampling factor (1 in the case of 4:4:4, grayscale, or 4:4:0; 2 in the + case of 4:2:2 or 4:2:0; 4 in the case of 4:1:1.) Similarly, the luminance + plane height is the image height padded to the nearest multiple of the + vertical subsampling factor (1 in the case of 4:4:4, 4:2:2, grayscale, or + 4:1:1; 2 in the case of 4:2:0 or 4:4:0.) This is irrespective of any + additional padding that may be specified as an argument to the various + YUVImage methods. The chrominance plane width is equal to the luminance + plane width divided by the horizontal subsampling factor, and the + chrominance plane height is equal to the luminance plane height divided by + the vertical subsampling factor.

    For example, if the source image is 35 x 35 pixels and 4:2:2 subsampling is used, then the luminance plane would be 36 x 35 bytes, and each of the - chrominance planes would be 18 x 35 bytes. If you specify a line padding of - 4 bytes on top of this, then the luminance plane would be 36 x 35 bytes, and - each of the chrominance planes would be 20 x 35 bytes.

    + chrominance planes would be 18 x 35 bytes. If you specify a row alignment + of 4 bytes on top of this, then the luminance plane would be 36 x 35 bytes, + and each of the chrominance planes would be 20 x 35 bytes.
    @@ -154,15 +156,15 @@

    Field Summary

    protected int -yuvHeight  +yuvAlign  -protected int[] -yuvOffsets  +protected int +yuvHeight  -protected int -yuvPad  +protected int[] +yuvOffsets  protected byte[][] @@ -208,10 +210,10 @@

    Constructor Summary

    YUVImage(byte[] yuvImage, int width, - int pad, + int align, int height, int subsamp) -
    Create a new YUVImage instance from an existing unified image +
    Create a new YUVImage instance from an existing unified buffer.
    @@ -226,11 +228,11 @@

    Constructor Summary

    YUVImage(int width, - int pad, + int align, int height, int subsamp) -
    Create a new YUVImage instance backed by a unified image - buffer, and allocate memory for the image buffer.
    +
    Create a new YUVImage instance backed by a unified buffer, + and allocate memory for the buffer.
    @@ -251,8 +253,8 @@

    Method Summary

    byte[] getBuf() -
    Returns the YUV image buffer (if this image is stored in a unified - buffer rather than separate image planes.)
    +
    Returns the YUV buffer (if this image is stored in a unified buffer rather + than separate image planes.)
    @@ -271,7 +273,7 @@

    Method Summary

    int getPad() -
    Returns the line padding used in the YUV image buffer (if this image is +
    Returns the row alignment (in bytes) of the YUV buffer (if this image is stored in a unified buffer rather than separate image planes.)
    @@ -284,14 +286,14 @@

    Method Summary

    int getSize() -
    Returns the size (in bytes) of the YUV image buffer (if this image is - stored in a unified buffer rather than separate image planes.)
    +
    Returns the size (in bytes) of the YUV buffer (if this image is stored in + a unified buffer rather than separate image planes.)
    int[] getStrides() -
    Returns the number of bytes per line of each plane in the YUV image.
    +
    Returns the number of bytes per row of each plane in the YUV image.
    @@ -321,10 +323,10 @@

    Method Summary

    void setBuf(byte[] yuvImage, int width, - int pad, + int align, int height, int subsamp) -
    Assign a unified image buffer to this YUVImage instance.
    +
    Assign a unified buffer to this YUVImage instance.
    @@ -385,13 +387,13 @@

    yuvStrides

    protected int[] yuvStrides
    - +
    • -

      yuvPad

      -
      protected int yuvPad
      +

      yuvAlign

      +
      protected int yuvAlign
    @@ -442,7 +444,7 @@

    YUVImage

    Create a new YUVImage instance backed by separate image planes, and allocate memory for the image planes.
    Parameters:
    width - width (in pixels) of the YUV image
    strides - an array of integers, each specifying the number of bytes - per line in the corresponding plane of the YUV image. Setting the stride + per row in the corresponding plane of the YUV image. Setting the stride for any plane to 0 is the same as setting it to the plane width (see above.) If strides is null, then the strides for all planes will be set to their respective plane widths. When @@ -458,13 +460,15 @@

    YUVImage

  • YUVImage

    public YUVImage(int width,
    -        int pad,
    +        int align,
             int height,
             int subsamp)
    -
    Create a new YUVImage instance backed by a unified image - buffer, and allocate memory for the image buffer.
    -
    Parameters:
    width - width (in pixels) of the YUV image
    pad - Each line of each plane in the YUV image buffer will be padded - to this number of bytes (must be a power of 2.)
    height - height (in pixels) of the YUV image
    subsamp - the level of chrominance subsampling to be used in the YUV +
    Create a new YUVImage instance backed by a unified buffer, + and allocate memory for the buffer.
    +
    Parameters:
    width - width (in pixels) of the YUV image
    align - row alignment (in bytes) of the YUV image (must be a power of + 2.) Setting this parameter to n specifies that each row in each plane of + the YUV image will be padded to the nearest multiple of n bytes + (1 = unpadded.)
    height - height (in pixels) of the YUV image
    subsamp - the level of chrominance subsampling to be used in the YUV image (one of TJ.SAMP_*)
  • @@ -485,18 +489,18 @@

    YUVImage

    Parameters:
    planes - an array of buffers representing the Y, U (Cb), and V (Cr) image planes (or just the Y plane, if the image is grayscale.) These planes can be contiguous or non-contiguous in memory. Plane - i should be at least offsets[i] + - TJ.planeSizeYUV(i, width, strides[i], height, subsamp) + i should be at least offsets[i] + + TJ.planeSizeYUV(i, width, strides[i], height, subsamp) bytes in size.
    offsets - If this YUVImage instance represents a subregion of a larger image, then offsets[i] specifies the offset (in bytes) of the subregion within plane i of the larger image. Setting this to null is the same as setting the offsets for all planes to 0.
    width - width (in pixels) of the new YUV image (or subregion)
    strides - an array of integers, each specifying the number of bytes - per line in the corresponding plane of the YUV image. Setting the stride + per row in the corresponding plane of the YUV image. Setting the stride for any plane to 0 is the same as setting it to the plane width (see above.) If strides is null, then the strides for all planes will be set to their respective plane widths. You - can adjust the strides in order to add an arbitrary amount of line padding + can adjust the strides in order to add an arbitrary amount of row padding to each plane or to specify that this YUVImage instance is a subregion of a larger image (in which case, strides[i] should be set to the plane width of plane i in the larger image.)
    height - height (in pixels) of the new YUV image (or subregion)
    subsamp - the level of chrominance subsampling used in the YUV @@ -511,18 +515,19 @@

    YUVImage

    YUVImage

    public YUVImage(byte[] yuvImage,
             int width,
    -        int pad,
    +        int align,
             int height,
             int subsamp)
    -
    Create a new YUVImage instance from an existing unified image +
    Create a new YUVImage instance from an existing unified buffer.
    -
    Parameters:
    yuvImage - image buffer that contains or will contain YUV planar - image data. Use TJ.bufSizeYUV(int, int, int, int) to determine the minimum size for - this buffer. The Y, U (Cb), and V (Cr) image planes are stored - sequentially in the buffer (see above for a description - of the image format.)
    width - width (in pixels) of the YUV image
    pad - the line padding used in the YUV image buffer. For - instance, if each line in each plane of the buffer is padded to the - nearest multiple of 4 bytes, then pad should be set to 4.
    height - height (in pixels) of the YUV image
    subsamp - the level of chrominance subsampling used in the YUV +
    Parameters:
    yuvImage - buffer that contains or will receive a unified planar YUV + image. Use TJ.bufSizeYUV(int, int, int, int) to determine the minimum size for this + buffer. The Y, U (Cb), and V (Cr) image planes are stored sequentially in + the buffer. (See above for a description of the image + format.)
    width - width (in pixels) of the YUV image
    align - row alignment (in bytes) of the YUV image (must be a power of + 2.) Setting this parameter to n specifies that each row in each plane of + the YUV image will be padded to the nearest multiple of n bytes + (1 = unpadded.)
    height - height (in pixels) of the YUV image
    subsamp - the level of chrominance subsampling used in the YUV image (one of TJ.SAMP_*)
    @@ -550,19 +555,19 @@

    setBuf

    Parameters:
    planes - an array of buffers representing the Y, U (Cb), and V (Cr) image planes (or just the Y plane, if the image is grayscale.) These planes can be contiguous or non-contiguous in memory. Plane - i should be at least offsets[i] + - TJ.planeSizeYUV(i, width, strides[i], height, subsamp) + i should be at least offsets[i] + + TJ.planeSizeYUV(i, width, strides[i], height, subsamp) bytes in size.
    offsets - If this YUVImage instance represents a subregion of a larger image, then offsets[i] specifies the offset (in bytes) of the subregion within plane i of the larger image. Setting this to null is the same as setting the offsets for all planes to 0.
    width - width (in pixels) of the YUV image (or subregion)
    strides - an array of integers, each specifying the number of bytes - per line in the corresponding plane of the YUV image. Setting the stride + per row in the corresponding plane of the YUV image. Setting the stride for any plane to 0 is the same as setting it to the plane width (see above.) If strides is null, then the strides for all planes will be set to their respective plane widths. You - can adjust the strides in order to add an arbitrary amount of line padding - to each plane or to specify that this YUVImage image is a + can adjust the strides in order to add an arbitrary amount of row padding + to each plane or to specify that this YUVImage instance is a subregion of a larger image (in which case, strides[i] should be set to the plane width of plane i in the larger image.)
    height - height (in pixels) of the YUV image (or subregion)
    subsamp - the level of chrominance subsampling used in the YUV image (one of TJ.SAMP_*)
    @@ -576,17 +581,18 @@

    setBuf

    setBuf

    public void setBuf(byte[] yuvImage,
               int width,
    -          int pad,
    +          int align,
               int height,
               int subsamp)
    -
    Assign a unified image buffer to this YUVImage instance.
    -
    Parameters:
    yuvImage - image buffer that contains or will contain YUV planar - image data. Use TJ.bufSizeYUV(int, int, int, int) to determine the minimum size for - this buffer. The Y, U (Cb), and V (Cr) image planes are stored - sequentially in the buffer (see above for a description - of the image format.)
    width - width (in pixels) of the YUV image
    pad - the line padding used in the YUV image buffer. For - instance, if each line in each plane of the buffer is padded to the - nearest multiple of 4 bytes, then pad should be set to 4.
    height - height (in pixels) of the YUV image
    subsamp - the level of chrominance subsampling used in the YUV +
    Assign a unified buffer to this YUVImage instance.
    +
    Parameters:
    yuvImage - buffer that contains or will receive a unified planar YUV + image. Use TJ.bufSizeYUV(int, int, int, int) to determine the minimum size for this + buffer. The Y, U (Cb), and V (Cr) image planes are stored sequentially in + the buffer. (See above for a description of the image + format.)
    width - width (in pixels) of the YUV image
    align - row alignment (in bytes) of the YUV image (must be a power of + 2.) Setting this parameter to n specifies that each row in each plane of + the YUV image will be padded to the nearest multiple of n bytes + (1 = unpadded.)
    height - height (in pixels) of the YUV image
    subsamp - the level of chrominance subsampling used in the YUV image (one of TJ.SAMP_*)
    @@ -619,9 +625,9 @@

    getHeight

  • getPad

    public int getPad()
    -
    Returns the line padding used in the YUV image buffer (if this image is +
    Returns the row alignment (in bytes) of the YUV buffer (if this image is stored in a unified buffer rather than separate image planes.)
    -
    Returns:
    the line padding used in the YUV image buffer
    +
    Returns:
    the row alignment of the YUV buffer
  • @@ -631,8 +637,8 @@

    getPad

  • getStrides

    public int[] getStrides()
    -
    Returns the number of bytes per line of each plane in the YUV image.
    -
    Returns:
    the number of bytes per line of each plane in the YUV image
    +
    Returns the number of bytes per row of each plane in the YUV image.
    +
    Returns:
    the number of bytes per row of each plane in the YUV image
  • @@ -679,9 +685,9 @@

    getPlanes

  • getBuf

    public byte[] getBuf()
    -
    Returns the YUV image buffer (if this image is stored in a unified - buffer rather than separate image planes.)
    -
    Returns:
    the YUV image buffer
    +
    Returns the YUV buffer (if this image is stored in a unified buffer rather + than separate image planes.)
    +
    Returns:
    the YUV buffer
  • @@ -691,9 +697,9 @@

    getBuf

  • getSize

    public int getSize()
    -
    Returns the size (in bytes) of the YUV image buffer (if this image is - stored in a unified buffer rather than separate image planes.)
    -
    Returns:
    the size (in bytes) of the YUV image buffer
    +
    Returns the size (in bytes) of the YUV buffer (if this image is stored in + a unified buffer rather than separate image planes.)
    +
    Returns:
    the size (in bytes) of the YUV buffer
  • diff --git a/java/doc/org/libjpegturbo/turbojpeg/package-summary.html b/java/doc/org/libjpegturbo/turbojpeg/package-summary.html index dedcce5c2..89dbe05b1 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/package-summary.html +++ b/java/doc/org/libjpegturbo/turbojpeg/package-summary.html @@ -131,7 +131,7 @@

    Package org.libjpegturbo.turbojpeg

    YUVImage -
    This class encapsulates a YUV planar image and the metadata +
    This class encapsulates a planar YUV image and the metadata associated with it.
    diff --git a/java/doc/serialized-form.html b/java/doc/serialized-form.html index 45bbc8625..e123f3189 100644 --- a/java/doc/serialized-form.html +++ b/java/doc/serialized-form.html @@ -109,12 +109,13 @@

    Serialized Fields

  • op

    int op
    -
    Transform operation (one of OP_*)
    +
    Transform operation (one of OP_*)
  • options

    int options
    -
    Transform options (bitwise OR of one or more of OPT_*)
    +
    Transform options (bitwise OR of one or more of + OPT_*)
  • cf

    diff --git a/java/org/libjpegturbo/turbojpeg/TJ.java b/java/org/libjpegturbo/turbojpeg/TJ.java index d791e001d..3857087e3 100644 --- a/java/org/libjpegturbo/turbojpeg/TJ.java +++ b/java/org/libjpegturbo/turbojpeg/TJ.java @@ -1,6 +1,6 @@ /* - * Copyright (C)2011-2013, 2017-2018, 2020-2021 D. R. Commander. - * All Rights Reserved. + * Copyright (C)2011-2013, 2017-2018, 2020-2021, 2023 D. R. Commander. + * All Rights Reserved. * Copyright (C)2015 Viktor Szathmáry. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without @@ -85,7 +85,7 @@ private TJ() {} * subsampling. * * @param subsamp the level of chrominance subsampling (one of - * SAMP_*) + * {@link #SAMP_444 SAMP_*}) * * @return the MCU block width for the given level of chrominance * subsampling. @@ -105,7 +105,7 @@ public static int getMCUWidth(int subsamp) { * subsampling. * * @param subsamp the level of chrominance subsampling (one of - * SAMP_*) + * {@link #SAMP_444 SAMP_*}) * * @return the MCU block height for the given level of chrominance * subsampling. @@ -205,8 +205,8 @@ public static int getMCUHeight(int subsamp) { * vice versa, but the mapping is typically not 1:1 or reversible, nor can it * be defined with a simple formula. Thus, such a conversion is out of scope * for a codec library. However, the TurboJPEG API allows for compressing - * CMYK pixels into a YCCK JPEG image (see {@link #CS_YCCK}) and - * decompressing YCCK JPEG images into CMYK pixels. + * packed-pixel CMYK images into YCCK JPEG images (see {@link #CS_YCCK}) and + * decompressing YCCK JPEG images into packed-pixel CMYK images. */ public static final int PF_CMYK = 11; @@ -214,7 +214,7 @@ public static int getMCUHeight(int subsamp) { /** * Returns the pixel size (in bytes) for the given pixel format. * - * @param pixelFormat the pixel format (one of PF_*) + * @param pixelFormat the pixel format (one of {@link #PF_RGB PF_*}) * * @return the pixel size (in bytes) for the given pixel format. */ @@ -235,7 +235,7 @@ public static int getPixelSize(int pixelFormat) { * then the red component will be * pixel[TJ.getRedOffset(TJ.PF_BGRX)]. * - * @param pixelFormat the pixel format (one of PF_*) + * @param pixelFormat the pixel format (one of {@link #PF_RGB PF_*}) * * @return the red offset for the given pixel format, or -1 if the pixel * format does not have a red component. @@ -257,7 +257,7 @@ public static int getRedOffset(int pixelFormat) { * then the green component will be * pixel[TJ.getGreenOffset(TJ.PF_BGRX)]. * - * @param pixelFormat the pixel format (one of PF_*) + * @param pixelFormat the pixel format (one of {@link #PF_RGB PF_*}) * * @return the green offset for the given pixel format, or -1 if the pixel * format does not have a green component. @@ -279,7 +279,7 @@ public static int getGreenOffset(int pixelFormat) { * then the blue component will be * pixel[TJ.getBlueOffset(TJ.PF_BGRX)]. * - * @param pixelFormat the pixel format (one of PF_*) + * @param pixelFormat the pixel format (one of {@link #PF_RGB PF_*}) * * @return the blue offset for the given pixel format, or -1 if the pixel * format does not have a blue component. @@ -301,7 +301,7 @@ public static int getBlueOffset(int pixelFormat) { * then the alpha component will be * pixel[TJ.getAlphaOffset(TJ.PF_BGRA)]. * - * @param pixelFormat the pixel format (one of PF_*) + * @param pixelFormat the pixel format (one of {@link #PF_RGB PF_*}) * * @return the alpha offset for the given pixel format, or -1 if the pixel * format does not have a alpha component. @@ -324,8 +324,9 @@ public static int getAlphaOffset(int pixelFormat) { * RGB colorspace. When compressing the JPEG image, the R, G, and B * components in the source image are reordered into image planes, but no * colorspace conversion or subsampling is performed. RGB JPEG images can be - * decompressed to any of the extended RGB pixel formats or grayscale, but - * they cannot be decompressed to YUV images. + * decompressed to packed-pixel images with any of the extended RGB or + * grayscale pixel formats, but they cannot be decompressed to planar YUV + * images. */ public static final int CS_RGB = 0; /** @@ -339,26 +340,28 @@ public static int getAlphaOffset(int pixelFormat) { * transformation allowed the same signal to drive both black & white and * color televisions, but JPEG images use YCbCr primarily because it allows * the color data to be optionally subsampled for the purposes of reducing - * bandwidth or disk space. YCbCr is the most common JPEG colorspace, and - * YCbCr JPEG images can be compressed from and decompressed to any of the - * extended RGB pixel formats or grayscale, or they can be decompressed to - * YUV planar images. + * network or disk usage. YCbCr is the most common JPEG colorspace, and + * YCbCr JPEG images can be compressed from and decompressed to packed-pixel + * images with any of the extended RGB or grayscale pixel formats. YCbCr + * JPEG images can also be compressed from and decompressed to planar YUV + * images. */ @SuppressWarnings("checkstyle:ConstantName") public static final int CS_YCbCr = 1; /** * Grayscale colorspace. The JPEG image retains only the luminance data (Y * component), and any color data from the source image is discarded. - * Grayscale JPEG images can be compressed from and decompressed to any of - * the extended RGB pixel formats or grayscale, or they can be decompressed - * to YUV planar images. + * Grayscale JPEG images can be compressed from and decompressed to + * packed-pixel images with any of the extended RGB or grayscale pixel + * formats, or they can be compressed from and decompressed to planar YUV + * images. */ public static final int CS_GRAY = 2; /** * CMYK colorspace. When compressing the JPEG image, the C, M, Y, and K * components in the source image are reordered into image planes, but no * colorspace conversion or subsampling is performed. CMYK JPEG images can - * only be decompressed to CMYK pixels. + * only be decompressed to packed-pixel images with the CMYK pixel format. */ public static final int CS_CMYK = 3; /** @@ -368,14 +371,14 @@ public static int getAlphaOffset(int pixelFormat) { * reversibly transformed into YCCK, and as with YCbCr, the chrominance * components in the YCCK pixels can be subsampled without incurring major * perceptual loss. YCCK JPEG images can only be compressed from and - * decompressed to CMYK pixels. + * decompressed to packed-pixel images with the CMYK pixel format. */ public static final int CS_YCCK = 4; /** - * The uncompressed source/destination image is stored in bottom-up (Windows, - * OpenGL) order, not top-down (X11) order. + * Rows in the packed-pixel source/destination image are stored in bottom-up + * (Windows, OpenGL) order rather than in top-down (X11) order. */ public static final int FLAG_BOTTOMUP = 2; @@ -394,41 +397,39 @@ public static int getAlphaOffset(int pixelFormat) { /** * When decompressing an image that was compressed using chrominance - * subsampling, use the fastest chrominance upsampling algorithm available in - * the underlying codec. The default is to use smooth upsampling, which - * creates a smooth transition between neighboring chrominance components in - * order to reduce upsampling artifacts in the decompressed image. + * subsampling, use the fastest chrominance upsampling algorithm available. + * The default is to use smooth upsampling, which creates a smooth transition + * between neighboring chrominance components in order to reduce upsampling + * artifacts in the decompressed image. */ public static final int FLAG_FASTUPSAMPLE = 256; /** - * Use the fastest DCT/IDCT algorithm available in the underlying codec. The - * default if this flag is not specified is implementation-specific. For - * example, the implementation of TurboJPEG for libjpeg[-turbo] uses the fast - * algorithm by default when compressing, because this has been shown to have - * only a very slight effect on accuracy, but it uses the accurate algorithm - * when decompressing, because this has been shown to have a larger effect. + * Use the fastest DCT/IDCT algorithm available. The default if this flag is + * not specified is implementation-specific. For example, the implementation + * of the TurboJPEG API in libjpeg-turbo uses the fast algorithm by default + * when compressing, because this has been shown to have only a very slight + * effect on accuracy, but it uses the accurate algorithm when decompressing, + * because this has been shown to have a larger effect. */ public static final int FLAG_FASTDCT = 2048; /** - * Use the most accurate DCT/IDCT algorithm available in the underlying - * codec. The default if this flag is not specified is - * implementation-specific. For example, the implementation of TurboJPEG for - * libjpeg[-turbo] uses the fast algorithm by default when compressing, - * because this has been shown to have only a very slight effect on accuracy, - * but it uses the accurate algorithm when decompressing, because this has - * been shown to have a larger effect. + * Use the most accurate DCT/IDCT algorithm available. The default if this + * flag is not specified is implementation-specific. For example, the + * implementation of the TurboJPEG API in libjpeg-turbo uses the fast + * algorithm by default when compressing, because this has been shown to have + * only a very slight effect on accuracy, but it uses the accurate algorithm + * when decompressing, because this has been shown to have a larger effect. */ public static final int FLAG_ACCURATEDCT = 4096; /** * Immediately discontinue the current compression/decompression/transform - * operation if the underlying codec throws a warning (non-fatal error). The - * default behavior is to allow the operation to complete unless a fatal - * error is encountered. + * operation if a warning (non-fatal error) occurs. The default behavior is + * to allow the operation to complete unless a fatal error is encountered. *

    * NOTE: due to the design of the TurboJPEG Java API, only certain methods * (specifically, {@link TJDecompressor TJDecompressor.decompress*()} methods - * with a void return type) will complete and leave the output image in a - * fully recoverable state after a non-fatal error occurs. + * with a void return type) will complete and leave the destination image in + * a fully recoverable state after a non-fatal error occurs. */ public static final int FLAG_STOPONWARNING = 8192; /** @@ -455,13 +456,13 @@ public static int getAlphaOffset(int pixelFormat) { */ public static final int NUMERR = 2; /** - * The error was non-fatal and recoverable, but the image may still be - * corrupt. + * The error was non-fatal and recoverable, but the destination image may + * still be corrupt. *

    * NOTE: due to the design of the TurboJPEG Java API, only certain methods * (specifically, {@link TJDecompressor TJDecompressor.decompress*()} methods - * with a void return type) will complete and leave the output image in a - * fully recoverable state after a non-fatal error occurs. + * with a void return type) will complete and leave the destination image in + * a fully recoverable state after a non-fatal error occurs. */ public static final int ERR_WARNING = 0; /** @@ -479,7 +480,7 @@ public static int getAlphaOffset(int pixelFormat) { * @param height the height (in pixels) of the JPEG image * * @param jpegSubsamp the level of chrominance subsampling to be used when - * generating the JPEG image (one of {@link TJ TJ.SAMP_*}) + * generating the JPEG image (one of {@link #SAMP_444 TJ.SAMP_*}) * * @return the maximum size of the buffer (in bytes) required to hold a JPEG * image with the given width, height, and level of chrominance subsampling. @@ -487,23 +488,27 @@ public static int getAlphaOffset(int pixelFormat) { public static native int bufSize(int width, int height, int jpegSubsamp); /** - * Returns the size of the buffer (in bytes) required to hold a YUV planar - * image with the given width, height, and level of chrominance subsampling. + * Returns the size of the buffer (in bytes) required to hold a unified + * planar YUV image with the given width, height, and level of chrominance + * subsampling. * * @param width the width (in pixels) of the YUV image * - * @param pad the width of each line in each plane of the image is padded to - * the nearest multiple of this number of bytes (must be a power of 2.) + * @param align row alignment (in bytes) of the YUV image (must be a power of + * 2.) Setting this parameter to n specifies that each row in each plane of + * the YUV image will be padded to the nearest multiple of n bytes + * (1 = unpadded.) * * @param height the height (in pixels) of the YUV image * * @param subsamp the level of chrominance subsampling used in the YUV - * image (one of {@link TJ TJ.SAMP_*}) + * image (one of {@link #SAMP_444 TJ.SAMP_*}) * - * @return the size of the buffer (in bytes) required to hold a YUV planar - * image with the given width, height, and level of chrominance subsampling. + * @return the size of the buffer (in bytes) required to hold a unified + * planar YUV image with the given width, height, and level of chrominance + * subsampling. */ - public static native int bufSizeYUV(int width, int pad, int height, + public static native int bufSizeYUV(int width, int align, int height, int subsamp); /** @@ -523,16 +528,16 @@ public static native int bufSizeYUV(int width, int pad, int height, * @param width width (in pixels) of the YUV image. NOTE: this is the width * of the whole image, not the plane width. * - * @param stride bytes per line in the image plane. + * @param stride bytes per row in the image plane. * * @param height height (in pixels) of the YUV image. NOTE: this is the * height of the whole image, not the plane height. * * @param subsamp the level of chrominance subsampling used in the YUV - * image (one of {@link TJ TJ.SAMP_*}) + * image (one of {@link #SAMP_444 TJ.SAMP_*}) * - * @return the size of the buffer (in bytes) required to hold a YUV planar - * image with the given parameters. + * @return the size of the buffer (in bytes) required to hold a YUV image + * plane with the given parameters. */ public static native int planeSizeYUV(int componentID, int width, int stride, int height, int subsamp); @@ -547,7 +552,7 @@ public static native int planeSizeYUV(int componentID, int width, int stride, * @param width width (in pixels) of the YUV image * * @param subsamp the level of chrominance subsampling used in the YUV image - * (one of {@link TJ TJ.SAMP_*}) + * (one of {@link #SAMP_444 TJ.SAMP_*}) * * @return the plane width of a YUV image plane with the given parameters. */ @@ -563,7 +568,7 @@ public static native int planeSizeYUV(int componentID, int width, int stride, * @param height height (in pixels) of the YUV image * * @param subsamp the level of chrominance subsampling used in the YUV image - * (one of {@link TJ TJ.SAMP_*}) + * (one of {@link #SAMP_444 TJ.SAMP_*}) * * @return the plane height of a YUV image plane with the given parameters. */ @@ -571,11 +576,11 @@ public static native int planeHeight(int componentID, int height, int subsamp); /** - * Returns a list of fractional scaling factors that the JPEG decompressor in - * this implementation of TurboJPEG supports. + * Returns a list of fractional scaling factors that the JPEG decompressor + * supports. * - * @return a list of fractional scaling factors that the JPEG decompressor in - * this implementation of TurboJPEG supports. + * @return a list of fractional scaling factors that the JPEG decompressor + * supports. */ public static native TJScalingFactor[] getScalingFactors(); diff --git a/java/org/libjpegturbo/turbojpeg/TJCompressor.java b/java/org/libjpegturbo/turbojpeg/TJCompressor.java index 6d4830f5f..c29d3dc74 100644 --- a/java/org/libjpegturbo/turbojpeg/TJCompressor.java +++ b/java/org/libjpegturbo/turbojpeg/TJCompressor.java @@ -1,5 +1,6 @@ /* - * Copyright (C)2011-2015, 2018, 2020 D. R. Commander. All Rights Reserved. + * Copyright (C)2011-2015, 2018, 2020, 2023 D. R. Commander. + * All Rights Reserved. * Copyright (C)2015 Viktor Szathmáry. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without @@ -49,7 +50,7 @@ public TJCompressor() throws TJException { } /** - * Create a TurboJPEG compressor instance and associate the uncompressed + * Create a TurboJPEG compressor instance and associate the packed-pixel * source image stored in srcImage with the newly created * instance. * @@ -85,7 +86,7 @@ public TJCompressor(byte[] srcImage, int width, int pitch, int height, } /** - * Create a TurboJPEG compressor instance and associate the uncompressed + * Create a TurboJPEG compressor instance and associate the packed-pixel * source image stored in srcImage with the newly created * instance. * @@ -110,11 +111,11 @@ public TJCompressor(BufferedImage srcImage, int x, int y, int width, } /** - * Associate an uncompressed RGB, grayscale, or CMYK source image with this + * Associate a packed-pixel RGB, grayscale, or CMYK source image with this * compressor instance. * - * @param srcImage image buffer containing RGB, grayscale, or CMYK pixels to - * be compressed or encoded. This buffer is not modified. + * @param srcImage buffer containing a packed-pixel RGB, grayscale, or CMYK + * source image to be compressed or encoded. This buffer is not modified. * * @param x x offset (in pixels) of the region in the source image from which * the JPEG or YUV image should be compressed/encoded @@ -125,13 +126,13 @@ public TJCompressor(BufferedImage srcImage, int x, int y, int width, * @param width width (in pixels) of the region in the source image from * which the JPEG or YUV image should be compressed/encoded * - * @param pitch bytes per line of the source image. Normally, this should be - * width * TJ.pixelSize(pixelFormat) if the source image is - * unpadded, but you can use this parameter to, for instance, specify that - * the scanlines in the source image are padded to a 4-byte boundary or to - * compress/encode a JPEG or YUV image from a region of a larger source - * image. You can also be clever and use this parameter to skip lines, etc. - * Setting this parameter to 0 is the equivalent of setting it to + * @param pitch bytes per row in the source image. Normally this should be + * width * TJ.pixelSize(pixelFormat), if the source image is + * unpadded. However, you can use this parameter to, for instance, specify + * that the rows in the source image are padded to the nearest multiple of 4 + * bytes or to compress/encode a JPEG or YUV image from a region of a larger + * source image. You can also be clever and use this parameter to skip rows, + * etc. Setting this parameter to 0 is the equivalent of setting it to * width * TJ.pixelSize(pixelFormat). * * @param height height (in pixels) of the region in the source image from @@ -174,11 +175,12 @@ public void setSourceImage(byte[] srcImage, int width, int pitch, } /** - * Associate an uncompressed RGB or grayscale source image with this + * Associate a packed-pixel RGB or grayscale source image with this * compressor instance. * - * @param srcImage a BufferedImage instance containing RGB or - * grayscale pixels to be compressed or encoded. This image is not modified. + * @param srcImage a BufferedImage instance containing a + * packed-pixel RGB or grayscale source image to be compressed or encoded. + * This image is not modified. * * @param x x offset (in pixels) of the region in the source image from which * the JPEG or YUV image should be compressed/encoded @@ -260,11 +262,10 @@ public void setSourceImage(BufferedImage srcImage, int x, int y, int width, } /** - * Associate an uncompressed YUV planar source image with this compressor - * instance. + * Associate a planar YUV source image with this compressor instance. * - * @param srcImage YUV planar image to be compressed. This image is not - * modified. + * @param srcImage planar YUV source image to be compressed. This image is + * not modified. */ public void setSourceImage(YUVImage srcImage) throws TJException { if (handle == 0) init(); @@ -281,16 +282,16 @@ public void setSourceImage(YUVImage srcImage) throws TJException { * {@link TJ#CS_YCbCr}) or from CMYK to YCCK (see {@link TJ#CS_YCCK}) as part * of the JPEG compression process, some of the Cb and Cr (chrominance) * components can be discarded or averaged together to produce a smaller - * image with little perceptible loss of image clarity (the human eye is more - * sensitive to small changes in brightness than to small changes in color.) - * This is called "chrominance subsampling". + * image with little perceptible loss of image clarity. (The human eye is + * more sensitive to small changes in brightness than to small changes in + * color.) This is called "chrominance subsampling". *

    - * NOTE: This method has no effect when compressing a JPEG image from a YUV - * planar source. In that case, the level of chrominance subsampling in - * the JPEG image is determined by the source. Furthermore, this method has - * no effect when encoding to a pre-allocated {@link YUVImage} instance. In - * that case, the level of chrominance subsampling is determined by the - * destination. + * NOTE: This method has no effect when compressing a JPEG image from a + * planar YUV source image. In that case, the level of chrominance + * subsampling in the JPEG image is determined by the source image. + * Furthermore, this method has no effect when encoding to a pre-allocated + * {@link YUVImage} instance. In that case, the level of chrominance + * subsampling is determined by the destination image. * * @param newSubsamp the level of chrominance subsampling to use in * subsequent compress/encode oeprations (one of @@ -315,8 +316,9 @@ public void setJPEGQuality(int quality) { } /** - * Compress the uncompressed source image associated with this compressor - * instance and output a JPEG image to the given destination buffer. + * Compress the packed-pixel or planar YUV source image associated with this + * compressor instance and output a JPEG image to the given destination + * buffer. * * @param dstBuf buffer that will receive the JPEG image. Use * {@link TJ#bufSize} to determine the maximum size for this buffer based on @@ -366,8 +368,8 @@ else if (srcBuf != null) { } /** - * Compress the uncompressed source image associated with this compressor - * instance and return a buffer containing a JPEG image. + * Compress the packed-pixel or planar YUV source image associated with this + * compressor instance and return a buffer containing a JPEG image. * * @param flags the bitwise OR of one or more of * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} @@ -417,14 +419,14 @@ public byte[] compress(BufferedImage srcImage, int flags) } /** - * Encode the uncompressed source image associated with this compressor - * instance into a YUV planar image and store it in the given - * YUVImage instance. This method uses the accelerated color - * conversion routines in TurboJPEG's underlying codec but does not execute - * any of the other steps in the JPEG compression process. Encoding - * CMYK source images to YUV is not supported. - * - * @param dstImage {@link YUVImage} instance that will receive the YUV planar + * Encode the packed-pixel source image associated with this compressor + * instance into a planar YUV image and store it in the given + * {@link YUVImage} instance. This method performs color conversion (which + * is accelerated in the libjpeg-turbo implementation) but does not execute + * any of the other steps in the JPEG compression process. Encoding CMYK + * source images into YUV images is not supported. + * + * @param dstImage {@link YUVImage} instance that will receive the planar YUV * image * * @param flags the bitwise OR of one or more of @@ -469,52 +471,54 @@ public void encodeYUV(byte[] dstBuf, int flags) throws TJException { } /** - * Encode the uncompressed source image associated with this compressor - * instance into a unified YUV planar image buffer and return a - * YUVImage instance containing the encoded image. This method - * uses the accelerated color conversion routines in TurboJPEG's underlying - * codec but does not execute any of the other steps in the JPEG compression - * process. Encoding CMYK source images to YUV is not supported. + * Encode the packed-pixel source image associated with this compressor + * instance into a unified planar YUV image and return a {@link YUVImage} + * instance containing the encoded image. This method performs color + * conversion (which is accelerated in the libjpeg-turbo implementation) but + * does not execute any of the other steps in the JPEG compression process. + * Encoding CMYK source images into YUV images is not supported. * - * @param pad the width of each line in each plane of the YUV image will be - * padded to the nearest multiple of this number of bytes (must be a power of - * 2.) + * @param align row alignment (in bytes) of the YUV image (must be a power of + * 2.) Setting this parameter to n will cause each row in each plane of the + * YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.) * * @param flags the bitwise OR of one or more of * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} * - * @return a YUV planar image. + * @return a {@link YUVImage} instance containing the unified planar YUV + * encoded image */ - public YUVImage encodeYUV(int pad, int flags) throws TJException { + public YUVImage encodeYUV(int align, int flags) throws TJException { checkSourceImage(); checkSubsampling(); - if (pad < 1 || ((pad & (pad - 1)) != 0)) + if (align < 1 || ((align & (align - 1)) != 0)) throw new IllegalStateException("Invalid argument in encodeYUV()"); - YUVImage dstYUVImage = new YUVImage(srcWidth, pad, srcHeight, subsamp); + YUVImage dstYUVImage = new YUVImage(srcWidth, align, srcHeight, subsamp); encodeYUV(dstYUVImage, flags); return dstYUVImage; } /** - * Encode the uncompressed source image associated with this compressor + * Encode the packed-pixel source image associated with this compressor * instance into separate Y, U (Cb), and V (Cr) image planes and return a - * YUVImage instance containing the encoded image planes. This - * method uses the accelerated color conversion routines in TurboJPEG's - * underlying codec but does not execute any of the other steps in the JPEG - * compression process. Encoding CMYK source images to YUV is not supported. + * {@link YUVImage} instance containing the encoded image planes. This + * method performs color conversion (which is accelerated in the + * libjpeg-turbo implementation) but does not execute any of the other steps + * in the JPEG compression process. Encoding CMYK source images into YUV + * images is not supported. * * @param strides an array of integers, each specifying the number of bytes - * per line in the corresponding plane of the output image. Setting the - * stride for any plane to 0 is the same as setting it to the component width - * of the plane. If strides is null, then the strides for all - * planes will be set to their respective component widths. You can adjust - * the strides in order to add an arbitrary amount of line padding to each - * plane. + * per row in the corresponding plane of the YUV source image. Setting the + * stride for any plane to 0 is the same as setting it to the plane width + * (see {@link YUVImage}.) If strides is null, then the strides + * for all planes will be set to their respective plane widths. You can + * adjust the strides in order to add an arbitrary amount of row padding to + * each plane. * * @param flags the bitwise OR of one or more of * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} * - * @return a YUV planar image. + * @return a {@link YUVImage} instance containing the encoded image planes */ public YUVImage encodeYUV(int[] strides, int flags) throws TJException { checkSourceImage(); @@ -679,6 +683,5 @@ private void checkSubsampling() { private int subsamp = -1; private int jpegQuality = -1; private int compressedSize = 0; - private int yuvPad = 4; private ByteOrder byteOrder = null; } diff --git a/java/org/libjpegturbo/turbojpeg/TJCustomFilter.java b/java/org/libjpegturbo/turbojpeg/TJCustomFilter.java index 9a34587a1..3a66fd9ed 100644 --- a/java/org/libjpegturbo/turbojpeg/TJCustomFilter.java +++ b/java/org/libjpegturbo/turbojpeg/TJCustomFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (C)2011, 2013 D. R. Commander. All Rights Reserved. + * Copyright (C)2011, 2013, 2023 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -58,7 +58,7 @@ public interface TJCustomFilter { * component plane to which coeffBuffer belongs * * @param componentID ID number of the component plane to which - * coeffBuffer belongs (Y, Cb, and Cr have, respectively, ID's + * coeffBuffer belongs. (Y, Cb, and Cr have, respectively, ID's * of 0, 1, and 2 in typical JPEG images.) * * @param transformID ID number of the transformed image to which diff --git a/java/org/libjpegturbo/turbojpeg/TJDecompressor.java b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java index aba390b1f..e5ee9cf28 100644 --- a/java/org/libjpegturbo/turbojpeg/TJDecompressor.java +++ b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java @@ -1,5 +1,6 @@ /* - * Copyright (C)2011-2015, 2018, 2022 D. R. Commander. All Rights Reserved. + * Copyright (C)2011-2015, 2018, 2022-2023 D. R. Commander. + * All Rights Reserved. * Copyright (C)2015 Viktor Szathmáry. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without @@ -50,10 +51,12 @@ public TJDecompressor() throws TJException { /** * Create a TurboJPEG decompressor instance and associate the JPEG source - * image stored in jpegImage with the newly created instance. + * image or "abbreviated table specification" (AKA "tables-only") datastream + * stored in jpegImage with the newly created instance. * - * @param jpegImage JPEG image buffer (size of the JPEG image is assumed to - * be the length of the array.) This buffer is not modified. + * @param jpegImage buffer containing a JPEG source image or tables-only + * datastream. (The size of the JPEG image or datastream is assumed to be + * the length of the array.) This buffer is not modified. */ public TJDecompressor(byte[] jpegImage) throws TJException { init(); @@ -62,12 +65,15 @@ public TJDecompressor(byte[] jpegImage) throws TJException { /** * Create a TurboJPEG decompressor instance and associate the JPEG source - * image of length imageSize bytes stored in - * jpegImage with the newly created instance. + * image or "abbreviated table specification" (AKA "tables-only") datastream + * of length imageSize bytes stored in jpegImage + * with the newly created instance. * - * @param jpegImage JPEG image buffer. This buffer is not modified. + * @param jpegImage buffer containing a JPEG source image or tables-only + * datastream. This buffer is not modified. * - * @param imageSize size of the JPEG image (in bytes) + * @param imageSize size of the JPEG source image or tables-only datastream + * (in bytes) */ public TJDecompressor(byte[] jpegImage, int imageSize) throws TJException { init(); @@ -75,11 +81,11 @@ public TJDecompressor(byte[] jpegImage, int imageSize) throws TJException { } /** - * Create a TurboJPEG decompressor instance and associate the YUV planar + * Create a TurboJPEG decompressor instance and associate the planar YUV * source image stored in yuvImage with the newly created * instance. * - * @param yuvImage {@link YUVImage} instance containing a YUV planar + * @param yuvImage {@link YUVImage} instance containing a planar YUV source * image to be decoded. This image is not modified. */ @SuppressWarnings("checkstyle:HiddenField") @@ -100,11 +106,11 @@ public TJDecompressor(YUVImage yuvImage) throws TJException { * when decompressing video streams in which all frames share the same * quantization and Huffman tables. * - * @param jpegImage buffer containing a JPEG image or an "abbreviated table - * specification" (AKA "tables-only") datastream. This buffer is not - * modified. + * @param jpegImage buffer containing a JPEG source image or tables-only + * datastream. This buffer is not modified. * - * @param imageSize size of the JPEG image (in bytes) + * @param imageSize size of the JPEG source image or tables-only datastream + * (in bytes) */ public void setSourceImage(byte[] jpegImage, int imageSize) throws TJException { @@ -127,12 +133,12 @@ public void setJPEGImage(byte[] jpegImage, int imageSize) } /** - * Associate the specified YUV planar source image with this decompressor - * instance. Subsequent decompress operations will decode this image into an - * RGB or grayscale destination image. + * Associate the specified planar YUV source image with this decompressor + * instance. Subsequent decompress operations will decode this image into a + * packed-pixel RGB or grayscale destination image. * - * @param srcImage {@link YUVImage} instance containing a YUV planar image to - * be decoded. This image is not modified. + * @param srcImage {@link YUVImage} instance containing a planar YUV source + * image to be decoded. This image is not modified. */ public void setSourceImage(YUVImage srcImage) { if (srcImage == null) @@ -210,9 +216,9 @@ public int getColorspace() { } /** - * Returns the JPEG image buffer associated with this decompressor instance. + * Returns the JPEG buffer associated with this decompressor instance. * - * @return the JPEG image buffer associated with this decompressor instance. + * @return the JPEG buffer associated with this decompressor instance. */ public byte[] getJPEGBuf() { if (jpegBuf == null) @@ -239,14 +245,14 @@ public int getJPEGSize() { * height. * * @param desiredWidth desired width (in pixels) of the decompressed image. - * Setting this to 0 is the same as setting it to the width of the JPEG image - * (in other words, the width will not be considered when determining the - * scaled image size.) + * Setting this to 0 is the same as setting it to the width of the JPEG + * image. (In other words, the width will not be considered when determining + * the scaled image size.) * * @param desiredHeight desired height (in pixels) of the decompressed image. * Setting this to 0 is the same as setting it to the height of the JPEG - * image (in other words, the height will not be considered when determining - * the scaled image size.) + * image. (In other words, the height will not be considered when + * determining the scaled image size.) * * @return the width of the largest scaled-down image that the TurboJPEG * decompressor can generate without exceeding the desired image width and @@ -280,14 +286,14 @@ public int getScaledWidth(int desiredWidth, int desiredHeight) { * height. * * @param desiredWidth desired width (in pixels) of the decompressed image. - * Setting this to 0 is the same as setting it to the width of the JPEG image - * (in other words, the width will not be considered when determining the - * scaled image size.) + * Setting this to 0 is the same as setting it to the width of the JPEG + * image. (In other words, the width will not be considered when determining + * the scaled image size.) * * @param desiredHeight desired height (in pixels) of the decompressed image. * Setting this to 0 is the same as setting it to the height of the JPEG - * image (in other words, the height will not be considered when determining - * the scaled image size.) + * image. (In other words, the height will not be considered when + * determining the scaled image size.) * * @return the height of the largest scaled-down image that the TurboJPEG * decompressor can generate without exceeding the desired image width and @@ -316,27 +322,27 @@ public int getScaledHeight(int desiredWidth, int desiredHeight) { } /** - * Decompress the JPEG source image or decode the YUV source image associated - * with this decompressor instance and output a grayscale, RGB, or CMYK image - * to the given destination buffer. + * Decompress the JPEG source image or decode the planar YUV source image + * associated with this decompressor instance and output a packed-pixel + * grayscale, RGB, or CMYK image to the given destination buffer. *

    - * NOTE: The output image is fully recoverable if this method throws a + * NOTE: The destination image is fully recoverable if this method throws a * non-fatal {@link TJException} (unless * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.) * - * @param dstBuf buffer that will receive the decompressed/decoded image. - * If the source image is a JPEG image, then this buffer should normally be - * pitch * scaledHeight bytes in size, where - * scaledHeight can be determined by calling - * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegHeight) - * with one of the scaling factors returned from {@link - * TJ#getScalingFactors} or by calling {@link #getScaledHeight}. If the - * source image is a YUV image, then this buffer should normally be - * pitch * height bytes in size, where height is - * the height of the YUV image. However, the buffer may also be larger than - * the dimensions of the source image, in which case the x, - * y, and pitch parameters can be used to specify - * the region into which the source image should be decompressed/decoded. + * @param dstBuf buffer that will receive the packed-pixel + * decompressed/decoded image. If the source image is a JPEG image, then + * this buffer should normally be pitch * scaledHeight bytes in + * size, where scaledHeight can be determined by calling + * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegHeight) + * with one of the scaling factors returned from {@link TJ#getScalingFactors} + * or by calling {@link #getScaledHeight}. If the source image is a YUV + * image, then this buffer should normally be pitch * height + * bytes in size, where height is the height of the YUV image. + * However, the buffer may also be larger than the dimensions of the source + * image, in which case the x, y, and + * pitch parameters can be used to specify the region into which + * the source image should be decompressed/decoded. * * @param x x offset (in pixels) of the region in the destination image into * which the source image should be decompressed/decoded @@ -350,20 +356,20 @@ public int getScaledHeight(int desiredWidth, int desiredHeight) { * than the source image dimensions, then TurboJPEG will use scaling in the * JPEG decompressor to generate the largest possible image that will fit * within the desired dimensions. Setting this to 0 is the same as setting - * it to the width of the JPEG image (in other words, the width will not be + * it to the width of the JPEG image. (In other words, the width will not be * considered when determining the scaled image size.) This parameter is * ignored if the source image is a YUV image. * - * @param pitch bytes per line of the destination image. Normally, this - * should be set to scaledWidth * TJ.pixelSize(pixelFormat) if - * the destination image is unpadded, but you can use this to, for instance, - * pad each line of the destination image to a 4-byte boundary or to - * decompress/decode the source image into a region of a larger image. NOTE: - * if the source image is a JPEG image, then scaledWidth can be - * determined by calling - * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegWidth) - * or by calling {@link #getScaledWidth}. If the source image is a - * YUV image, then scaledWidth is the width of the YUV image. + * @param pitch bytes per row in the destination image. Normally this should + * be set to scaledWidth * TJ.pixelSize(pixelFormat), if the + * destination image will be unpadded. However, you can use this to, for + * instance, pad each row of the destination image to the nearest multiple of + * 4 bytes or to decompress/decode the source image into a region of a larger + * image. NOTE: if the source image is a JPEG image, then + * scaledWidth can be determined by calling + * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegWidth) + * or by calling {@link #getScaledWidth}. If the source image is a YUV + * image, then scaledWidth is the width of the YUV image. * Setting this parameter to 0 is the equivalent of setting it to * scaledWidth * TJ.pixelSize(pixelFormat). * @@ -373,8 +379,8 @@ public int getScaledHeight(int desiredWidth, int desiredHeight) { * than the source image dimensions, then TurboJPEG will use scaling in the * JPEG decompressor to generate the largest possible image that will fit * within the desired dimensions. Setting this to 0 is the same as setting - * it to the height of the JPEG image (in other words, the height will not be - * considered when determining the scaled image size.) This parameter is + * it to the height of the JPEG image. (In other words, the height will not + * be considered when determining the scaled image size.) This parameter is * ignored if the source image is a YUV image. * * @param pixelFormat pixel format of the decompressed/decoded image (one of @@ -421,8 +427,9 @@ public void decompress(byte[] dstBuf, int desiredWidth, int pitch, } /** - * Decompress the JPEG source image associated with this decompressor - * instance and return a buffer containing the decompressed image. + * Decompress the JPEG source image or decode the planar YUV source image + * associated with this decompressor instance and return a buffer containing + * the packed-pixel decompressed image. * * @param desiredWidth see * {@link #decompress(byte[], int, int, int, int, int, int, int)} @@ -442,7 +449,7 @@ public void decompress(byte[] dstBuf, int desiredWidth, int pitch, * @param flags the bitwise OR of one or more of * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} * - * @return a buffer containing the decompressed image. + * @return a buffer containing the packed-pixel decompressed image. */ public byte[] decompress(int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags) throws TJException { @@ -462,22 +469,22 @@ public byte[] decompress(int desiredWidth, int pitch, int desiredHeight, /** * Decompress the JPEG source image associated with this decompressor - * instance into a YUV planar image and store it in the given - * YUVImage instance. This method performs JPEG decompression - * but leaves out the color conversion step, so a planar YUV image is - * generated instead of an RGB or grayscale image. This method cannot be - * used to decompress JPEG source images with the CMYK or YCCK colorspace. + * instance into a planar YUV image and store it in the given + * {@link YUVImage} instance. This method performs JPEG decompression but + * leaves out the color conversion step, so a planar YUV image is generated + * instead of a packed-pixel image. This method cannot be used to decompress + * JPEG source images with the CMYK or YCCK colorspace. *

    - * NOTE: The YUV planar output image is fully recoverable if this method + * NOTE: The planar YUV destination image is fully recoverable if this method * throws a non-fatal {@link TJException} (unless * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.) * - * @param dstImage {@link YUVImage} instance that will receive the YUV planar - * image. The level of subsampling specified in this YUVImage - * instance must match that of the JPEG image, and the width and height - * specified in the YUVImage instance must match one of the - * scaled image sizes that TurboJPEG is capable of generating from the JPEG - * source image. + * @param dstImage {@link YUVImage} instance that will receive the planar YUV + * decompressed image. The level of subsampling specified in this + * {@link YUVImage} instance must match that of the JPEG image, and the width + * and height specified in the {@link YUVImage} instance must match one of + * the scaled image sizes that the decompressor is capable of generating from + * the JPEG source image. * * @param flags the bitwise OR of one or more of * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} @@ -494,7 +501,7 @@ public void decompressToYUV(YUVImage dstImage, int flags) dstImage.getHeight()); if (scaledWidth != dstImage.getWidth() || scaledHeight != dstImage.getHeight()) - throw new IllegalArgumentException("YUVImage dimensions do not match one of the scaled image sizes that TurboJPEG is capable of generating."); + throw new IllegalArgumentException("YUVImage dimensions do not match one of the scaled image sizes that the decompressor is capable of generating."); if (jpegSubsamp != dstImage.getSubsamp()) throw new IllegalArgumentException("YUVImage subsampling level does not match that of the JPEG image"); @@ -517,40 +524,41 @@ public void decompressToYUV(byte[] dstBuf, int flags) throws TJException { /** * Decompress the JPEG source image associated with this decompressor * instance into a set of Y, U (Cb), and V (Cr) image planes and return a - * YUVImage instance containing the decompressed image planes. - * This method performs JPEG decompression but leaves out the color - * conversion step, so a planar YUV image is generated instead of an RGB or - * grayscale image. This method cannot be used to decompress JPEG source - * images with the CMYK or YCCK colorspace. + * {@link YUVImage} instance containing the decompressed image planes. This + * method performs JPEG decompression but leaves out the color conversion + * step, so a planar YUV image is generated instead of a packed-pixel image. + * This method cannot be used to decompress JPEG source images with the CMYK + * or YCCK colorspace. * * @param desiredWidth desired width (in pixels) of the YUV image. If the * desired image dimensions are different than the dimensions of the JPEG * image being decompressed, then TurboJPEG will use scaling in the JPEG * decompressor to generate the largest possible image that will fit within * the desired dimensions. Setting this to 0 is the same as setting it to - * the width of the JPEG image (in other words, the width will not be + * the width of the JPEG image. (In other words, the width will not be * considered when determining the scaled image size.) * * @param strides an array of integers, each specifying the number of bytes - * per line in the corresponding plane of the output image. Setting the - * stride for any plane to 0 is the same as setting it to the scaled - * component width of the plane. If strides is NULL, then the - * strides for all planes will be set to their respective scaled component - * widths. You can adjust the strides in order to add an arbitrary amount of - * line padding to each plane. + * per row in the corresponding plane of the YUV image. Setting the stride + * for any plane to 0 is the same as setting it to the scaled plane width + * (see {@link YUVImage}.) If strides is null, then the strides + * for all planes will be set to their respective scaled plane widths. You + * can adjust the strides in order to add an arbitrary amount of row padding + * to each plane. * * @param desiredHeight desired height (in pixels) of the YUV image. If the * desired image dimensions are different than the dimensions of the JPEG * image being decompressed, then TurboJPEG will use scaling in the JPEG * decompressor to generate the largest possible image that will fit within * the desired dimensions. Setting this to 0 is the same as setting it to - * the height of the JPEG image (in other words, the height will not be + * the height of the JPEG image. (In other words, the height will not be * considered when determining the scaled image size.) * * @param flags the bitwise OR of one or more of * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} * - * @return a YUV planar image. + * @return a {@link YUVImage} instance containing the decompressed image + * planes */ public YUVImage decompressToYUV(int desiredWidth, int[] strides, int desiredHeight, @@ -574,40 +582,41 @@ public YUVImage decompressToYUV(int desiredWidth, int[] strides, /** * Decompress the JPEG source image associated with this decompressor - * instance into a unified YUV planar image buffer and return a - * YUVImage instance containing the decompressed image. This - * method performs JPEG decompression but leaves out the color conversion - * step, so a planar YUV image is generated instead of an RGB or grayscale - * image. This method cannot be used to decompress JPEG source images with - * the CMYK or YCCK colorspace. + * instance into a unified planar YUV image and return a {@link YUVImage} + * instance containing the decompressed image. This method performs JPEG + * decompression but leaves out the color conversion step, so a planar YUV + * image is generated instead of a packed-pixel image. This method cannot be + * used to decompress JPEG source images with the CMYK or YCCK colorspace. * * @param desiredWidth desired width (in pixels) of the YUV image. If the * desired image dimensions are different than the dimensions of the JPEG * image being decompressed, then TurboJPEG will use scaling in the JPEG * decompressor to generate the largest possible image that will fit within * the desired dimensions. Setting this to 0 is the same as setting it to - * the width of the JPEG image (in other words, the width will not be + * the width of the JPEG image. (In other words, the width will not be * considered when determining the scaled image size.) * - * @param pad the width of each line in each plane of the YUV image will be - * padded to the nearest multiple of this number of bytes (must be a power of - * 2.) + * @param align row alignment (in bytes) of the YUV image (must be a power of + * 2.) Setting this parameter to n will cause each row in each plane of the + * YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.) * * @param desiredHeight desired height (in pixels) of the YUV image. If the * desired image dimensions are different than the dimensions of the JPEG * image being decompressed, then TurboJPEG will use scaling in the JPEG * decompressor to generate the largest possible image that will fit within * the desired dimensions. Setting this to 0 is the same as setting it to - * the height of the JPEG image (in other words, the height will not be + * the height of the JPEG image. (In other words, the height will not be * considered when determining the scaled image size.) * * @param flags the bitwise OR of one or more of * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} * - * @return a YUV planar image. + * @return a {@link YUVImage} instance containing the unified planar YUV + * decompressed image */ - public YUVImage decompressToYUV(int desiredWidth, int pad, int desiredHeight, - int flags) throws TJException { + public YUVImage decompressToYUV(int desiredWidth, int align, + int desiredHeight, int flags) + throws TJException { if (flags < 0) throw new IllegalArgumentException("Invalid argument in decompressToYUV()"); if (jpegWidth < 1 || jpegHeight < 1 || jpegSubsamp < 0) @@ -619,7 +628,7 @@ public YUVImage decompressToYUV(int desiredWidth, int pad, int desiredHeight, int scaledWidth = getScaledWidth(desiredWidth, desiredHeight); int scaledHeight = getScaledHeight(desiredWidth, desiredHeight); - YUVImage dstYUVImage = new YUVImage(scaledWidth, pad, scaledHeight, + YUVImage dstYUVImage = new YUVImage(scaledWidth, align, scaledHeight, jpegSubsamp); decompressToYUV(dstYUVImage, flags); return dstYUVImage; @@ -637,27 +646,27 @@ public byte[] decompressToYUV(int flags) throws TJException { } /** - * Decompress the JPEG source image or decode the YUV source image associated - * with this decompressor instance and output a grayscale, RGB, or CMYK image - * to the given destination buffer. + * Decompress the JPEG source image or decode the planar YUV source image + * associated with this decompressor instance and output a packed-pixel + * grayscale, RGB, or CMYK image to the given destination buffer. *

    - * NOTE: The output image is fully recoverable if this method throws a + * NOTE: The destination image is fully recoverable if this method throws a * non-fatal {@link TJException} (unless * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.) * - * @param dstBuf buffer that will receive the decompressed/decoded image. - * If the source image is a JPEG image, then this buffer should normally be - * stride * scaledHeight pixels in size, where - * scaledHeight can be determined by calling - * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegHeight) - * with one of the scaling factors returned from {@link - * TJ#getScalingFactors} or by calling {@link #getScaledHeight}. If the - * source image is a YUV image, then this buffer should normally be - * stride * height pixels in size, where height is - * the height of the YUV image. However, the buffer may also be larger than - * the dimensions of the JPEG image, in which case the x, - * y, and stride parameters can be used to specify - * the region into which the source image should be decompressed. + * @param dstBuf buffer that will receive the packed-pixel + * decompressed/decoded image. If the source image is a JPEG image, then + * this buffer should normally be stride * scaledHeight pixels + * in size, where scaledHeight can be determined by calling + * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegHeight) + * with one of the scaling factors returned from {@link TJ#getScalingFactors} + * or by calling {@link #getScaledHeight}. If the source image is a YUV + * image, then this buffer should normally be stride * height + * pixels in size, where height is the height of the YUV image. + * However, the buffer may also be larger than the dimensions of the JPEG + * image, in which case the x, y, and + * stride parameters can be used to specify the region into + * which the source image should be decompressed. * * @param x x offset (in pixels) of the region in the destination image into * which the source image should be decompressed/decoded @@ -671,18 +680,18 @@ public byte[] decompressToYUV(int flags) throws TJException { * than the source image dimensions, then TurboJPEG will use scaling in the * JPEG decompressor to generate the largest possible image that will fit * within the desired dimensions. Setting this to 0 is the same as setting - * it to the width of the JPEG image (in other words, the width will not be + * it to the width of the JPEG image. (In other words, the width will not be * considered when determining the scaled image size.) This parameter is * ignored if the source image is a YUV image. * - * @param stride pixels per line of the destination image. Normally, this + * @param stride pixels per row in the destination image. Normally this * should be set to scaledWidth, but you can use this to, for * instance, decompress the JPEG image into a region of a larger image. * NOTE: if the source image is a JPEG image, then scaledWidth - * can be determined by calling - * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegWidth) - * or by calling {@link #getScaledWidth}. If the source image is a - * YUV image, then scaledWidth is the width of the YUV image. + * can be determined by calling + * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegWidth) + * or by calling {@link #getScaledWidth}. If the source image is a YUV + * image, then scaledWidth is the width of the YUV image. * Setting this parameter to 0 is the equivalent of setting it to * scaledWidth. * @@ -692,8 +701,8 @@ public byte[] decompressToYUV(int flags) throws TJException { * than the source image dimensions, then TurboJPEG will use scaling in the * JPEG decompressor to generate the largest possible image that will fit * within the desired dimensions. Setting this to 0 is the same as setting - * it to the height of the JPEG image (in other words, the height will not be - * considered when determining the scaled image size.) This parameter is + * it to the height of the JPEG image. (In other words, the height will not + * be considered when determining the scaled image size.) This parameter is * ignored if the source image is a YUV image. * * @param pixelFormat pixel format of the decompressed image (one of @@ -722,21 +731,22 @@ public void decompress(int[] dstBuf, int x, int y, int desiredWidth, } /** - * Decompress the JPEG source image or decode the YUV source image associated - * with this decompressor instance and output a decompressed/decoded image to - * the given BufferedImage instance. + * Decompress the JPEG source image or decode the planar YUV source image + * associated with this decompressor instance and output a packed-pixel + * decompressed/decoded image to the given BufferedImage + * instance. *

    - * NOTE: The output image is fully recoverable if this method throws a + * NOTE: The destination image is fully recoverable if this method throws a * non-fatal {@link TJException} (unless * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.) * * @param dstImage a BufferedImage instance that will receive - * the decompressed/decoded image. If the source image is a JPEG image, then - * the width and height of the BufferedImage instance must match - * one of the scaled image sizes that TurboJPEG is capable of generating from - * the JPEG image. If the source image is a YUV image, then the width and - * height of the BufferedImage instance must match the width and - * height of the YUV image. + * the packed-pixel decompressed/decoded image. If the source image is a + * JPEG image, then the width and height of the BufferedImage + * instance must match one of the scaled image sizes that the decompressor is + * capable of generating from the JPEG image. If the source image is a YUV + * image, then the width and height of the BufferedImage + * instance must match the width and height of the YUV image. * * @param flags the bitwise OR of one or more of * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} @@ -759,7 +769,7 @@ public void decompress(BufferedImage dstImage, int flags) scaledWidth = getScaledWidth(desiredWidth, desiredHeight); scaledHeight = getScaledHeight(desiredWidth, desiredHeight); if (scaledWidth != desiredWidth || scaledHeight != desiredHeight) - throw new IllegalArgumentException("BufferedImage dimensions do not match one of the scaled image sizes that TurboJPEG is capable of generating."); + throw new IllegalArgumentException("BufferedImage dimensions do not match one of the scaled image sizes that the decompressor is capable of generating."); } int pixelFormat; boolean intPixels = false; if (byteOrder == null) @@ -829,7 +839,7 @@ public void decompress(BufferedImage dstImage, int flags) /** * Decompress the JPEG source image or decode the YUV source image associated * with this decompressor instance and return a BufferedImage - * instance containing the decompressed/decoded image. + * instance containing the packed-pixel decompressed/decoded image. * * @param desiredWidth see * {@link #decompress(byte[], int, int, int, int, int, int, int)} for @@ -846,7 +856,7 @@ public void decompress(BufferedImage dstImage, int flags) * @param flags the bitwise OR of one or more of * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} * - * @return a BufferedImage instance containing the + * @return a BufferedImage instance containing the packed-pixel * decompressed/decoded image. */ public BufferedImage decompress(int desiredWidth, int desiredHeight, diff --git a/java/org/libjpegturbo/turbojpeg/TJTransform.java b/java/org/libjpegturbo/turbojpeg/TJTransform.java index 41c4b45ed..c51683469 100644 --- a/java/org/libjpegturbo/turbojpeg/TJTransform.java +++ b/java/org/libjpegturbo/turbojpeg/TJTransform.java @@ -1,5 +1,5 @@ /* - * Copyright (C)2011, 2013, 2018 D. R. Commander. All Rights Reserved. + * Copyright (C)2011, 2013, 2018, 2023 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -97,7 +97,7 @@ public class TJTransform extends Rectangle { * the level of chrominance subsampling used. If the image's width or height * is not evenly divisible by the MCU block size (see {@link TJ#getMCUWidth} * and {@link TJ#getMCUHeight}), then there will be partial MCU blocks on the - * right and/or bottom edges. It is not possible to move these partial MCU + * right and/or bottom edges. It is not possible to move these partial MCU * blocks to the top or left of the image, so any transform that would * require that is "imperfect." If this option is not specified, then any * partial MCU blocks that cannot be transformed will be left in place, which @@ -114,8 +114,8 @@ public class TJTransform extends Rectangle { */ public static final int OPT_CROP = 4; /** - * This option will discard the color data in the input image and produce - * a grayscale output image. + * This option will discard the color data in the source image and produce a + * grayscale destination image. */ public static final int OPT_GRAY = 8; /** @@ -127,7 +127,7 @@ public class TJTransform extends Rectangle { */ public static final int OPT_NOOUTPUT = 16; /** - * This option will enable progressive entropy coding in the output image + * This option will enable progressive entropy coding in the JPEG image * generated by this particular transform. Progressive entropy coding will * generally improve compression relative to baseline entropy coding (the * default), but it will reduce compression and decompression performance @@ -137,7 +137,7 @@ public class TJTransform extends Rectangle { /** * This option will prevent {@link TJTransformer#transform * TJTransformer.transform()} from copying any extra markers (including EXIF - * and ICC profile data) from the source image to the output image. + * and ICC profile data) from the source image to the destination image. */ public static final int OPT_COPYNONE = 64; @@ -165,10 +165,10 @@ public TJTransform() { * equivalent of setting it to (height of the source JPEG image - * y). * - * @param op one of the transform operations (OP_*) + * @param op one of the transform operations ({@link #OP_NONE OP_*}) * * @param options the bitwise OR of one or more of the transform options - * (OPT_*) + * ({@link #OPT_PERFECT OPT_*}) * * @param cf an instance of an object that implements the {@link * TJCustomFilter} interface, or null if no custom filter is needed @@ -190,10 +190,10 @@ public TJTransform(int x, int y, int w, int h, int op, int options, * #TJTransform(int, int, int, int, int, int, TJCustomFilter)} for more * detail. * - * @param op one of the transform operations (OP_*) + * @param op one of the transform operations ({@link #OP_NONE OP_*}) * * @param options the bitwise OR of one or more of the transform options - * (OPT_*) + * ({@link #OPT_PERFECT OPT_*}) * * @param cf an instance of an object that implements the {@link * TJCustomFilter} interface, or null if no custom filter is needed @@ -208,13 +208,14 @@ public TJTransform(Rectangle r, int op, int options, } /** - * Transform operation (one of OP_*) + * Transform operation (one of {@link #OP_NONE OP_*}) */ @SuppressWarnings("checkstyle:VisibilityModifier") public int op = 0; /** - * Transform options (bitwise OR of one or more of OPT_*) + * Transform options (bitwise OR of one or more of + * {@link #OPT_PERFECT OPT_*}) */ @SuppressWarnings("checkstyle:VisibilityModifier") public int options = 0; diff --git a/java/org/libjpegturbo/turbojpeg/TJTransformer.java b/java/org/libjpegturbo/turbojpeg/TJTransformer.java index d7a56f35a..2cbf0bfb9 100644 --- a/java/org/libjpegturbo/turbojpeg/TJTransformer.java +++ b/java/org/libjpegturbo/turbojpeg/TJTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright (C)2011, 2013-2015 D. R. Commander. All Rights Reserved. + * Copyright (C)2011, 2013-2015, 2023 D. R. Commander. All Rights Reserved. * Copyright (C)2015 Viktor Szathmáry. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,10 +43,12 @@ public TJTransformer() throws TJException { /** * Create a TurboJPEG lossless transformer instance and associate the JPEG - * image stored in jpegImage with the newly created instance. + * source image stored in jpegImage with the newly created + * instance. * - * @param jpegImage JPEG image buffer (size of the JPEG image is assumed to - * be the length of the array.) This buffer is not modified. + * @param jpegImage buffer containing the JPEG source image to transform. + * (The size of the JPEG image is assumed to be the length of the array.) + * This buffer is not modified. */ public TJTransformer(byte[] jpegImage) throws TJException { init(); @@ -55,12 +57,13 @@ public TJTransformer(byte[] jpegImage) throws TJException { /** * Create a TurboJPEG lossless transformer instance and associate the JPEG - * image of length imageSize bytes stored in + * source image of length imageSize bytes stored in * jpegImage with the newly created instance. * - * @param jpegImage JPEG image buffer. This buffer is not modified. + * @param jpegImage buffer containing the JPEG source image to transform. + * This buffer is not modified. * - * @param imageSize size of the JPEG image (in bytes) + * @param imageSize size of the JPEG source image (in bytes) */ public TJTransformer(byte[] jpegImage, int imageSize) throws TJException { init(); @@ -68,28 +71,29 @@ public TJTransformer(byte[] jpegImage, int imageSize) throws TJException { } /** - * Losslessly transform the JPEG image associated with this transformer - * instance into one or more JPEG images stored in the given destination - * buffers. Lossless transforms work by moving the raw coefficients from one - * JPEG image structure to another without altering the values of the - * coefficients. While this is typically faster than decompressing the - * image, transforming it, and re-compressing it, lossless transforms are not - * free. Each lossless transform requires reading and performing Huffman - * decoding on all of the coefficients in the source image, regardless of the - * size of the destination image. Thus, this method provides a means of - * generating multiple transformed images from the same source or of applying - * multiple transformations simultaneously, in order to eliminate the need to - * read the source coefficients multiple times. + * Losslessly transform the JPEG source image associated with this + * transformer instance into one or more JPEG images stored in the given + * destination buffers. Lossless transforms work by moving the raw + * coefficients from one JPEG image structure to another without altering the + * values of the coefficients. While this is typically faster than + * decompressing the image, transforming it, and re-compressing it, lossless + * transforms are not free. Each lossless transform requires reading and + * performing Huffman decoding on all of the coefficients in the source + * image, regardless of the size of the destination image. Thus, this method + * provides a means of generating multiple transformed images from the same + * source or of applying multiple transformations simultaneously, in order to + * eliminate the need to read the source coefficients multiple times. * - * @param dstBufs an array of image buffers. dstbufs[i] will - * receive a JPEG image that has been transformed using the parameters in - * transforms[i]. Use {@link TJ#bufSize} to determine the - * maximum size for each buffer based on the transformed or cropped width and - * height and the level of subsampling used in the source image. + * @param dstBufs an array of JPEG destination buffers. + * dstbufs[i] will receive a JPEG image that has been + * transformed using the parameters in transforms[i]. Use + * {@link TJ#bufSize} to determine the maximum size for each buffer based on + * the transformed or cropped width and height and the level of subsampling + * used in the source image. * * @param transforms an array of {@link TJTransform} instances, each of * which specifies the transform parameters and/or cropping region for the - * corresponding transformed output image + * corresponding transformed JPEG image * * @param flags the bitwise OR of one or more of * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} @@ -103,13 +107,13 @@ public void transform(byte[][] dstBufs, TJTransform[] transforms, } /** - * Losslessly transform the JPEG image associated with this transformer - * instance and return an array of {@link TJDecompressor} instances, each of - * which has a transformed JPEG image associated with it. + * Losslessly transform the JPEG source image associated with this + * transformer instance and return an array of {@link TJDecompressor} + * instances, each of which has a transformed JPEG image associated with it. * * @param transforms an array of {@link TJTransform} instances, each of * which specifies the transform parameters and/or cropping region for the - * corresponding transformed output image + * corresponding transformed JPEG image * * @param flags the bitwise OR of one or more of * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} @@ -139,10 +143,10 @@ public TJDecompressor[] transform(TJTransform[] transforms, int flags) /** * Returns an array containing the sizes of the transformed JPEG images - * generated by the most recent transform operation. + * (in bytes) generated by the most recent transform operation. * * @return an array containing the sizes of the transformed JPEG images - * generated by the most recent transform operation. + * (in bytes) generated by the most recent transform operation. */ public int[] getTransformedSizes() { if (transformedSizes == null) diff --git a/java/org/libjpegturbo/turbojpeg/YUVImage.java b/java/org/libjpegturbo/turbojpeg/YUVImage.java index 4da9843a8..948304647 100644 --- a/java/org/libjpegturbo/turbojpeg/YUVImage.java +++ b/java/org/libjpegturbo/turbojpeg/YUVImage.java @@ -1,5 +1,5 @@ /* - * Copyright (C)2014, 2017 D. R. Commander. All Rights Reserved. + * Copyright (C)2014, 2017, 2023 D. R. Commander. All Rights Reserved. * Copyright (C)2015 Viktor Szathmáry. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,7 +30,7 @@ package org.libjpegturbo.turbojpeg; /** - * This class encapsulates a YUV planar image and the metadata + * This class encapsulates a planar YUV image and the metadata * associated with it. The TurboJPEG API allows both the JPEG compression and * decompression pipelines to be split into stages: YUV encode, compress from * YUV, decompress to YUV, and YUV decode. A YUVImage instance @@ -38,30 +38,32 @@ * operations and as the source image for compress-from-YUV and YUV decode * operations. *

    - * Technically, the JPEG format uses the YCbCr colorspace (which technically is - * not a "colorspace" but rather a "color transform"), but per the convention - * of the digital video community, the TurboJPEG API uses "YUV" to refer to an - * image format consisting of Y, Cb, and Cr image planes. + * Technically, the JPEG format uses the YCbCr colorspace (which is technically + * not a colorspace but a color transform), but per the convention of the + * digital video community, the TurboJPEG API uses "YUV" to refer to an image + * format consisting of Y, Cb, and Cr image planes. *

    * Each plane is simply a 2D array of bytes, each byte representing the value * of one of the components (Y, Cb, or Cr) at a particular location in the * image. The width and height of each plane are determined by the image * width, height, and level of chrominance subsampling. The luminance plane * width is the image width padded to the nearest multiple of the horizontal - * subsampling factor (2 in the case of 4:2:0 and 4:2:2, 4 in the case of - * 4:1:1, 1 in the case of 4:4:4 or grayscale.) Similarly, the luminance plane - * height is the image height padded to the nearest multiple of the vertical - * subsampling factor (2 in the case of 4:2:0 or 4:4:0, 1 in the case of 4:4:4 - * or grayscale.) The chrominance plane width is equal to the luminance plane - * width divided by the horizontal subsampling factor, and the chrominance - * plane height is equal to the luminance plane height divided by the vertical - * subsampling factor. + * subsampling factor (1 in the case of 4:4:4, grayscale, or 4:4:0; 2 in the + * case of 4:2:2 or 4:2:0; 4 in the case of 4:1:1.) Similarly, the luminance + * plane height is the image height padded to the nearest multiple of the + * vertical subsampling factor (1 in the case of 4:4:4, 4:2:2, grayscale, or + * 4:1:1; 2 in the case of 4:2:0 or 4:4:0.) This is irrespective of any + * additional padding that may be specified as an argument to the various + * YUVImage methods. The chrominance plane width is equal to the luminance + * plane width divided by the horizontal subsampling factor, and the + * chrominance plane height is equal to the luminance plane height divided by + * the vertical subsampling factor. *

    * For example, if the source image is 35 x 35 pixels and 4:2:2 subsampling is * used, then the luminance plane would be 36 x 35 bytes, and each of the - * chrominance planes would be 18 x 35 bytes. If you specify a line padding of - * 4 bytes on top of this, then the luminance plane would be 36 x 35 bytes, and - * each of the chrominance planes would be 20 x 35 bytes. + * chrominance planes would be 18 x 35 bytes. If you specify a row alignment + * of 4 bytes on top of this, then the luminance plane would be 36 x 35 bytes, + * and each of the chrominance planes would be 20 x 35 bytes. */ public class YUVImage { @@ -75,7 +77,7 @@ public class YUVImage { * @param width width (in pixels) of the YUV image * * @param strides an array of integers, each specifying the number of bytes - * per line in the corresponding plane of the YUV image. Setting the stride + * per row in the corresponding plane of the YUV image. Setting the stride * for any plane to 0 is the same as setting it to the plane width (see * {@link YUVImage above}.) If strides is null, then the * strides for all planes will be set to their respective plane widths. When @@ -92,22 +94,24 @@ public YUVImage(int width, int[] strides, int height, int subsamp) { } /** - * Create a new YUVImage instance backed by a unified image - * buffer, and allocate memory for the image buffer. + * Create a new YUVImage instance backed by a unified buffer, + * and allocate memory for the buffer. * * @param width width (in pixels) of the YUV image * - * @param pad Each line of each plane in the YUV image buffer will be padded - * to this number of bytes (must be a power of 2.) + * @param align row alignment (in bytes) of the YUV image (must be a power of + * 2.) Setting this parameter to n specifies that each row in each plane of + * the YUV image will be padded to the nearest multiple of n bytes + * (1 = unpadded.) * * @param height height (in pixels) of the YUV image * * @param subsamp the level of chrominance subsampling to be used in the YUV * image (one of {@link TJ#SAMP_444 TJ.SAMP_*}) */ - public YUVImage(int width, int pad, int height, int subsamp) { - setBuf(new byte[TJ.bufSizeYUV(width, pad, height, subsamp)], width, pad, - height, subsamp); + public YUVImage(int width, int align, int height, int subsamp) { + setBuf(new byte[TJ.bufSizeYUV(width, align, height, subsamp)], width, + align, height, subsamp); } /** @@ -117,8 +121,8 @@ public YUVImage(int width, int pad, int height, int subsamp) { * @param planes an array of buffers representing the Y, U (Cb), and V (Cr) * image planes (or just the Y plane, if the image is grayscale.) These * planes can be contiguous or non-contiguous in memory. Plane - * i should be at least offsets[i] + - * {@link TJ#planeSizeYUV TJ.planeSizeYUV}(i, width, strides[i], height, subsamp) + * i should be at least offsets[i] + + * {@link TJ#planeSizeYUV TJ.planeSizeYUV}(i, width, strides[i], height, subsamp) * bytes in size. * * @param offsets If this YUVImage instance represents a @@ -130,11 +134,11 @@ public YUVImage(int width, int pad, int height, int subsamp) { * @param width width (in pixels) of the new YUV image (or subregion) * * @param strides an array of integers, each specifying the number of bytes - * per line in the corresponding plane of the YUV image. Setting the stride + * per row in the corresponding plane of the YUV image. Setting the stride * for any plane to 0 is the same as setting it to the plane width (see * {@link YUVImage above}.) If strides is null, then the * strides for all planes will be set to their respective plane widths. You - * can adjust the strides in order to add an arbitrary amount of line padding + * can adjust the strides in order to add an arbitrary amount of row padding * to each plane or to specify that this YUVImage instance is a * subregion of a larger image (in which case, strides[i] should * be set to the plane width of plane i in the larger image.) @@ -150,29 +154,30 @@ public YUVImage(byte[][] planes, int[] offsets, int width, int[] strides, } /** - * Create a new YUVImage instance from an existing unified image + * Create a new YUVImage instance from an existing unified * buffer. * - * @param yuvImage image buffer that contains or will contain YUV planar - * image data. Use {@link TJ#bufSizeYUV} to determine the minimum size for - * this buffer. The Y, U (Cb), and V (Cr) image planes are stored - * sequentially in the buffer (see {@link YUVImage above} for a description - * of the image format.) + * @param yuvImage buffer that contains or will receive a unified planar YUV + * image. Use {@link TJ#bufSizeYUV} to determine the minimum size for this + * buffer. The Y, U (Cb), and V (Cr) image planes are stored sequentially in + * the buffer. (See {@link YUVImage above} for a description of the image + * format.) * * @param width width (in pixels) of the YUV image * - * @param pad the line padding used in the YUV image buffer. For - * instance, if each line in each plane of the buffer is padded to the - * nearest multiple of 4 bytes, then pad should be set to 4. + * @param align row alignment (in bytes) of the YUV image (must be a power of + * 2.) Setting this parameter to n specifies that each row in each plane of + * the YUV image will be padded to the nearest multiple of n bytes + * (1 = unpadded.) * * @param height height (in pixels) of the YUV image * * @param subsamp the level of chrominance subsampling used in the YUV * image (one of {@link TJ#SAMP_444 TJ.SAMP_*}) */ - public YUVImage(byte[] yuvImage, int width, int pad, int height, + public YUVImage(byte[] yuvImage, int width, int align, int height, int subsamp) { - setBuf(yuvImage, width, pad, height, subsamp); + setBuf(yuvImage, width, align, height, subsamp); } /** @@ -181,8 +186,8 @@ public YUVImage(byte[] yuvImage, int width, int pad, int height, * @param planes an array of buffers representing the Y, U (Cb), and V (Cr) * image planes (or just the Y plane, if the image is grayscale.) These * planes can be contiguous or non-contiguous in memory. Plane - * i should be at least offsets[i] + - * {@link TJ#planeSizeYUV TJ.planeSizeYUV}(i, width, strides[i], height, subsamp) + * i should be at least offsets[i] + + * {@link TJ#planeSizeYUV TJ.planeSizeYUV}(i, width, strides[i], height, subsamp) * bytes in size. * * @param offsets If this YUVImage instance represents a @@ -194,12 +199,12 @@ public YUVImage(byte[] yuvImage, int width, int pad, int height, * @param width width (in pixels) of the YUV image (or subregion) * * @param strides an array of integers, each specifying the number of bytes - * per line in the corresponding plane of the YUV image. Setting the stride + * per row in the corresponding plane of the YUV image. Setting the stride * for any plane to 0 is the same as setting it to the plane width (see * {@link YUVImage above}.) If strides is null, then the * strides for all planes will be set to their respective plane widths. You - * can adjust the strides in order to add an arbitrary amount of line padding - * to each plane or to specify that this YUVImage image is a + * can adjust the strides in order to add an arbitrary amount of row padding + * to each plane or to specify that this YUVImage instance is a * subregion of a larger image (in which case, strides[i] should * be set to the plane width of plane i in the larger image.) * @@ -263,32 +268,34 @@ private void setBuf(byte[][] planes, int[] offsets, int width, int[] strides, } /** - * Assign a unified image buffer to this YUVImage instance. + * Assign a unified buffer to this YUVImage instance. * - * @param yuvImage image buffer that contains or will contain YUV planar - * image data. Use {@link TJ#bufSizeYUV} to determine the minimum size for - * this buffer. The Y, U (Cb), and V (Cr) image planes are stored - * sequentially in the buffer (see {@link YUVImage above} for a description - * of the image format.) + * @param yuvImage buffer that contains or will receive a unified planar YUV + * image. Use {@link TJ#bufSizeYUV} to determine the minimum size for this + * buffer. The Y, U (Cb), and V (Cr) image planes are stored sequentially in + * the buffer. (See {@link YUVImage above} for a description of the image + * format.) * * @param width width (in pixels) of the YUV image * - * @param pad the line padding used in the YUV image buffer. For - * instance, if each line in each plane of the buffer is padded to the - * nearest multiple of 4 bytes, then pad should be set to 4. + * @param align row alignment (in bytes) of the YUV image (must be a power of + * 2.) Setting this parameter to n specifies that each row in each plane of + * the YUV image will be padded to the nearest multiple of n bytes + * (1 = unpadded.) * * @param height height (in pixels) of the YUV image * * @param subsamp the level of chrominance subsampling used in the YUV * image (one of {@link TJ#SAMP_444 TJ.SAMP_*}) */ - public void setBuf(byte[] yuvImage, int width, int pad, int height, + public void setBuf(byte[] yuvImage, int width, int align, int height, int subsamp) { - if (yuvImage == null || width < 1 || pad < 1 || ((pad & (pad - 1)) != 0) || - height < 1 || subsamp < 0 || subsamp >= TJ.NUMSAMP) + if (yuvImage == null || width < 1 || align < 1 || + ((align & (align - 1)) != 0) || height < 1 || subsamp < 0 || + subsamp >= TJ.NUMSAMP) throw new IllegalArgumentException("Invalid argument in YUVImage::setBuf()"); - if (yuvImage.length < TJ.bufSizeYUV(width, pad, height, subsamp)) - throw new IllegalArgumentException("YUV image buffer is not large enough"); + if (yuvImage.length < TJ.bufSizeYUV(width, align, height, subsamp)) + throw new IllegalArgumentException("YUV buffer is not large enough"); int nc = (subsamp == TJ.SAMP_GRAY ? 1 : 3); byte[][] planes = new byte[nc][]; @@ -296,9 +303,9 @@ public void setBuf(byte[] yuvImage, int width, int pad, int height, int[] offsets = new int[nc]; planes[0] = yuvImage; - strides[0] = pad(TJ.planeWidth(0, width, subsamp), pad); + strides[0] = pad(TJ.planeWidth(0, width, subsamp), align); if (subsamp != TJ.SAMP_GRAY) { - strides[1] = strides[2] = pad(TJ.planeWidth(1, width, subsamp), pad); + strides[1] = strides[2] = pad(TJ.planeWidth(1, width, subsamp), align); planes[1] = planes[2] = yuvImage; offsets[1] = offsets[0] + strides[0] * TJ.planeHeight(0, height, subsamp); @@ -306,7 +313,7 @@ public void setBuf(byte[] yuvImage, int width, int pad, int height, strides[1] * TJ.planeHeight(1, height, subsamp); } - yuvPad = pad; + yuvAlign = align; setBuf(planes, offsets, width, strides, height, subsamp); } @@ -333,23 +340,23 @@ public int getHeight() { } /** - * Returns the line padding used in the YUV image buffer (if this image is + * Returns the row alignment (in bytes) of the YUV buffer (if this image is * stored in a unified buffer rather than separate image planes.) * - * @return the line padding used in the YUV image buffer + * @return the row alignment of the YUV buffer */ public int getPad() { if (yuvPlanes == null) throw new IllegalStateException(NO_ASSOC_ERROR); - if (yuvPad < 1 || ((yuvPad & (yuvPad - 1)) != 0)) + if (yuvAlign < 1 || ((yuvAlign & (yuvAlign - 1)) != 0)) throw new IllegalStateException("Image is not stored in a unified buffer"); - return yuvPad; + return yuvAlign; } /** - * Returns the number of bytes per line of each plane in the YUV image. + * Returns the number of bytes per row of each plane in the YUV image. * - * @return the number of bytes per line of each plane in the YUV image + * @return the number of bytes per row of each plane in the YUV image */ public int[] getStrides() { if (yuvStrides == null) @@ -395,10 +402,10 @@ public byte[][] getPlanes() { } /** - * Returns the YUV image buffer (if this image is stored in a unified - * buffer rather than separate image planes.) + * Returns the YUV buffer (if this image is stored in a unified buffer rather + * than separate image planes.) * - * @return the YUV image buffer + * @return the YUV buffer */ public byte[] getBuf() { if (yuvPlanes == null || yuvSubsamp < 0 || yuvSubsamp >= TJ.NUMSAMP) @@ -412,22 +419,22 @@ public byte[] getBuf() { } /** - * Returns the size (in bytes) of the YUV image buffer (if this image is - * stored in a unified buffer rather than separate image planes.) + * Returns the size (in bytes) of the YUV buffer (if this image is stored in + * a unified buffer rather than separate image planes.) * - * @return the size (in bytes) of the YUV image buffer + * @return the size (in bytes) of the YUV buffer */ public int getSize() { if (yuvPlanes == null || yuvSubsamp < 0 || yuvSubsamp >= TJ.NUMSAMP) throw new IllegalStateException(NO_ASSOC_ERROR); int nc = (yuvSubsamp == TJ.SAMP_GRAY ? 1 : 3); - if (yuvPad < 1) + if (yuvAlign < 1) throw new IllegalStateException("Image is not stored in a unified buffer"); for (int i = 1; i < nc; i++) { if (yuvPlanes[i] != yuvPlanes[0]) throw new IllegalStateException("Image is not stored in a unified buffer"); } - return TJ.bufSizeYUV(yuvWidth, yuvPad, yuvHeight, yuvSubsamp); + return TJ.bufSizeYUV(yuvWidth, yuvAlign, yuvHeight, yuvSubsamp); } private static int pad(int v, int p) { @@ -438,7 +445,7 @@ private static int pad(int v, int p) { protected byte[][] yuvPlanes = null; protected int[] yuvOffsets = null; protected int[] yuvStrides = null; - protected int yuvPad = 0; + protected int yuvAlign = 1; protected int yuvWidth = 0; protected int yuvHeight = 0; protected int yuvSubsamp = -1; diff --git a/tjbench.c b/tjbench.c index 354f8f373..d3ed67d23 100644 --- a/tjbench.c +++ b/tjbench.c @@ -82,7 +82,7 @@ int tjErrorLine = -1, tjErrorCode = -1; } int flags = TJFLAG_NOREALLOC, compOnly = 0, decompOnly = 0, doYUV = 0, - quiet = 0, doTile = 0, pf = TJPF_BGR, yuvPad = 1, doWrite = 1; + quiet = 0, doTile = 0, pf = TJPF_BGR, yuvAlign = 1, doWrite = 1; char *ext = "ppm"; const char *pixFormatStr[TJ_NUMPF] = { "RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "GRAY", "", "", "", "", "CMYK" @@ -182,7 +182,7 @@ static int decomp(unsigned char *srcBuf, unsigned char **jpegBuf, if (doYUV) { int width = doTile ? tilew : scaledw; int height = doTile ? tileh : scaledh; - unsigned long yuvSize = tjBufSizeYUV2(width, yuvPad, height, subsamp); + unsigned long yuvSize = tjBufSizeYUV2(width, yuvAlign, height, subsamp); if (yuvSize == (unsigned long)-1) THROW_TJ("allocating YUV buffer"); @@ -209,10 +209,10 @@ static int decomp(unsigned char *srcBuf, unsigned char **jpegBuf, double startDecode; if (tjDecompressToYUV2(handle, jpegBuf[tile], jpegSize[tile], yuvBuf, - width, yuvPad, height, flags) == -1) + width, yuvAlign, height, flags) == -1) THROW_TJ("executing tjDecompressToYUV2()"); startDecode = getTime(); - if (tjDecodeYUV(handle, yuvBuf, yuvPad, subsamp, dstPtr2, width, + if (tjDecodeYUV(handle, yuvBuf, yuvAlign, subsamp, dstPtr2, width, pitch, height, pf, flags) == -1) THROW_TJ("executing tjDecodeYUV()"); if (iter >= 0) elapsedDecode += getTime() - startDecode; @@ -380,7 +380,7 @@ static int fullTest(unsigned char *srcBuf, int w, int h, int subsamp, THROW_TJ("executing tjInitCompress()"); if (doYUV) { - yuvSize = tjBufSizeYUV2(tilew, yuvPad, tileh, subsamp); + yuvSize = tjBufSizeYUV2(tilew, yuvAlign, tileh, subsamp); if (yuvSize == (unsigned long)-1) THROW_TJ("allocating YUV buffer"); if ((yuvBuf = (unsigned char *)malloc(yuvSize)) == NULL) @@ -407,10 +407,10 @@ static int fullTest(unsigned char *srcBuf, int w, int h, int subsamp, double startEncode = getTime(); if (tjEncodeYUV3(handle, srcPtr2, width, pitch, height, pf, yuvBuf, - yuvPad, subsamp, flags) == -1) + yuvAlign, subsamp, flags) == -1) THROW_TJ("executing tjEncodeYUV3()"); if (iter >= 0) elapsedEncode += getTime() - startEncode; - if (tjCompressFromYUV(handle, yuvBuf, width, yuvPad, height, + if (tjCompressFromYUV(handle, yuvBuf, width, yuvAlign, height, subsamp, &jpegBuf[tile], &jpegSize[tile], jpegQual, flags) == -1) THROW_TJ("executing tjCompressFromYUV()"); @@ -779,8 +779,8 @@ static void usage(char *progName) printf("-quiet = Output results in tabular rather than verbose format\n"); printf("-yuv = Test YUV encoding/decoding functions\n"); printf("-yuvpad

    = If testing YUV encoding/decoding, this specifies the number of\n"); - printf(" bytes to which each row of each plane in the intermediate YUV image is\n"); - printf(" padded (default = 1)\n"); + printf(" bytes by which each row of each plane in the intermediate YUV image is\n"); + printf(" evenly divisible (default = 1)\n"); printf("-scale M/N = Scale down the width/height of the decompressed JPEG image by a\n"); printf(" factor of M/N (M/N = "); for (i = 0; i < nsf; i++) { @@ -940,12 +940,12 @@ int main(int argc, char *argv[]) else if (!strcasecmp(argv[i], "-bmp")) ext = "bmp"; else if (!strcasecmp(argv[i], "-yuv")) { - printf("Testing YUV planar encoding/decoding\n\n"); + printf("Testing planar YUV encoding/decoding\n\n"); doYUV = 1; } else if (!strcasecmp(argv[i], "-yuvpad") && i < argc - 1) { int tempi = atoi(argv[++i]); - if (tempi >= 1) yuvPad = tempi; + if (tempi >= 1) yuvAlign = tempi; } else if (!strcasecmp(argv[i], "-subsamp") && i < argc - 1) { i++; if (toupper(argv[i][0]) == 'G') subsamp = TJSAMP_GRAY; diff --git a/tjunittest.c b/tjunittest.c index ccd4a7043..dcf1eb244 100644 --- a/tjunittest.c +++ b/tjunittest.c @@ -57,7 +57,7 @@ static void usage(char *progName) printf("Options:\n"); printf("-yuv = test YUV encoding/decoding support\n"); printf("-noyuvpad = do not pad each line of each Y, U, and V plane to the nearest\n"); - printf(" 4-byte boundary\n"); + printf(" multiple of 4 bytes\n"); printf("-alloc = test automatic buffer allocation\n"); printf("-bmp = tjLoadImage()/tjSaveImage() unit test\n\n"); exit(1); @@ -95,7 +95,7 @@ const int _4byteFormats[] = { const int _onlyGray[] = { TJPF_GRAY }; const int _onlyRGB[] = { TJPF_RGB }; -int doYUV = 0, alloc = 0, pad = 4; +int doYUV = 0, alloc = 0, yuvAlign = 4; int exitStatus = 0; #define BAILOUT() { exitStatus = -1; goto bailout; } @@ -282,7 +282,7 @@ static int checkBufYUV(unsigned char *buf, int w, int h, int subsamp, int hsf = tjMCUWidth[subsamp] / 8, vsf = tjMCUHeight[subsamp] / 8; int pw = PAD(w, hsf), ph = PAD(h, vsf); int cw = pw / hsf, ch = ph / vsf; - int ypitch = PAD(pw, pad), uvpitch = PAD(cw, pad); + int ypitch = PAD(pw, yuvAlign), uvpitch = PAD(cw, yuvAlign); int retval = 1; int halfway = 16 * sf.num / sf.denom; int blocksize = 8 * sf.num / sf.denom; @@ -381,7 +381,7 @@ static void compTest(tjhandle handle, unsigned char **dstBuf, if (!alloc) flags |= TJFLAG_NOREALLOC; if (doYUV) { - unsigned long yuvSize = tjBufSizeYUV2(w, pad, h, subsamp); + unsigned long yuvSize = tjBufSizeYUV2(w, yuvAlign, h, subsamp); tjscalingfactor sf = { 1, 1 }; tjhandle handle2 = tjInitCompress(); @@ -392,15 +392,15 @@ static void compTest(tjhandle handle, unsigned char **dstBuf, memset(yuvBuf, 0, yuvSize); printf("%s %s -> YUV %s ... ", pfStr, buStrLong, subNameLong[subsamp]); - TRY_TJ(tjEncodeYUV3(handle2, srcBuf, w, 0, h, pf, yuvBuf, pad, subsamp, - flags)); + TRY_TJ(tjEncodeYUV3(handle2, srcBuf, w, 0, h, pf, yuvBuf, yuvAlign, + subsamp, flags)); tjDestroy(handle2); if (checkBufYUV(yuvBuf, w, h, subsamp, sf)) printf("Passed.\n"); else printf("FAILED!\n"); printf("YUV %s %s -> JPEG Q%d ... ", subNameLong[subsamp], buStrLong, jpegQual); - TRY_TJ(tjCompressFromYUV(handle, yuvBuf, w, pad, h, subsamp, dstBuf, + TRY_TJ(tjCompressFromYUV(handle, yuvBuf, w, yuvAlign, h, subsamp, dstBuf, dstSize, jpegQual, flags)); } else { printf("%s %s -> %s Q%d ... ", pfStr, buStrLong, subNameLong[subsamp], @@ -442,7 +442,7 @@ static void _decompTest(tjhandle handle, unsigned char *jpegBuf, memset(dstBuf, 0, dstSize); if (doYUV) { - unsigned long yuvSize = tjBufSizeYUV2(scaledWidth, pad, scaledHeight, + unsigned long yuvSize = tjBufSizeYUV2(scaledWidth, yuvAlign, scaledHeight, subsamp); tjhandle handle2 = tjInitDecompress(); @@ -457,15 +457,15 @@ static void _decompTest(tjhandle handle, unsigned char *jpegBuf, printf("%d/%d ... ", sf.num, sf.denom); else printf("... "); TRY_TJ(tjDecompressToYUV2(handle, jpegBuf, jpegSize, yuvBuf, scaledWidth, - pad, scaledHeight, flags)); + yuvAlign, scaledHeight, flags)); if (checkBufYUV(yuvBuf, scaledWidth, scaledHeight, subsamp, sf)) printf("Passed.\n"); else printf("FAILED!\n"); printf("YUV %s -> %s %s ... ", subNameLong[subsamp], pixFormatStr[pf], (flags & TJFLAG_BOTTOMUP) ? "Bottom-Up" : "Top-Down "); - TRY_TJ(tjDecodeYUV(handle2, yuvBuf, pad, subsamp, dstBuf, scaledWidth, 0, - scaledHeight, pf, flags)); + TRY_TJ(tjDecodeYUV(handle2, yuvBuf, yuvAlign, subsamp, dstBuf, scaledWidth, + 0, scaledHeight, pf, flags)); tjDestroy(handle2); } else { printf("JPEG -> %s %s ", pixFormatStr[pf], @@ -618,7 +618,7 @@ static void bufSizeTest(void) if ((srcBuf = (unsigned char *)malloc(w * h * 4)) == NULL) THROW("Memory allocation failure"); if (!alloc || doYUV) { - if (doYUV) dstSize = tjBufSizeYUV2(w, pad, h, subsamp); + if (doYUV) dstSize = tjBufSizeYUV2(w, yuvAlign, h, subsamp); else dstSize = tjBufSize(w, h, subsamp); if ((dstBuf = (unsigned char *)tjAlloc(dstSize)) == NULL) THROW("Memory allocation failure"); @@ -630,8 +630,8 @@ static void bufSizeTest(void) } if (doYUV) { - TRY_TJ(tjEncodeYUV3(handle, srcBuf, w, 0, h, TJPF_BGRX, dstBuf, pad, - subsamp, 0)); + TRY_TJ(tjEncodeYUV3(handle, srcBuf, w, 0, h, TJPF_BGRX, dstBuf, + yuvAlign, subsamp, 0)); } else { TRY_TJ(tjCompress2(handle, srcBuf, w, 0, h, TJPF_BGRX, &dstBuf, &dstSize, subsamp, 100, @@ -645,7 +645,7 @@ static void bufSizeTest(void) if ((srcBuf = (unsigned char *)malloc(h * w * 4)) == NULL) THROW("Memory allocation failure"); if (!alloc || doYUV) { - if (doYUV) dstSize = tjBufSizeYUV2(h, pad, w, subsamp); + if (doYUV) dstSize = tjBufSizeYUV2(h, yuvAlign, w, subsamp); else dstSize = tjBufSize(h, w, subsamp); if ((dstBuf = (unsigned char *)tjAlloc(dstSize)) == NULL) THROW("Memory allocation failure"); @@ -657,8 +657,8 @@ static void bufSizeTest(void) } if (doYUV) { - TRY_TJ(tjEncodeYUV3(handle, srcBuf, h, 0, w, TJPF_BGRX, dstBuf, pad, - subsamp, 0)); + TRY_TJ(tjEncodeYUV3(handle, srcBuf, h, 0, w, TJPF_BGRX, dstBuf, + yuvAlign, subsamp, 0)); } else { TRY_TJ(tjCompress2(handle, srcBuf, h, 0, w, TJPF_BGRX, &dstBuf, &dstSize, subsamp, 100, @@ -902,7 +902,7 @@ int main(int argc, char *argv[]) if (argc > 1) { for (i = 1; i < argc; i++) { if (!strcasecmp(argv[i], "-yuv")) doYUV = 1; - else if (!strcasecmp(argv[i], "-noyuvpad")) pad = 1; + else if (!strcasecmp(argv[i], "-noyuvpad")) yuvAlign = 1; else if (!strcasecmp(argv[i], "-alloc")) alloc = 1; else if (!strcasecmp(argv[i], "-bmp")) return bmpTest(); else usage(argv[0]); diff --git a/turbojpeg-jni.c b/turbojpeg-jni.c index 0cf5f702c..15e57bb6c 100644 --- a/turbojpeg-jni.c +++ b/turbojpeg-jni.c @@ -1,5 +1,5 @@ /* - * Copyright (C)2011-2022 D. R. Commander. All Rights Reserved. + * Copyright (C)2011-2023 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -142,9 +142,9 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSize /* TurboJPEG 1.4.x: TJ::bufSizeYUV() */ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSizeYUV__IIII - (JNIEnv *env, jclass cls, jint width, jint pad, jint height, jint subsamp) + (JNIEnv *env, jclass cls, jint width, jint align, jint height, jint subsamp) { - jint retval = (jint)tjBufSizeYUV2(width, pad, height, subsamp); + jint retval = (jint)tjBufSizeYUV2(width, align, height, subsamp); if (retval == -1) THROW_ARG(tjGetErrorStr()); diff --git a/turbojpeg-mapfile b/turbojpeg-mapfile index 5477fed2c..07a429be9 100644 --- a/turbojpeg-mapfile +++ b/turbojpeg-mapfile @@ -1,14 +1,14 @@ TURBOJPEG_1.0 { global: - tjInitCompress; - tjCompress; TJBUFSIZE; - tjInitDecompress; - tjDecompressHeader; + tjCompress; tjDecompress; + tjDecompressHeader; tjDestroy; tjGetErrorStr; + tjInitCompress; + tjInitDecompress; local: *; }; diff --git a/turbojpeg-mapfile.jni b/turbojpeg-mapfile.jni index 44327912b..4ae25aad9 100644 --- a/turbojpeg-mapfile.jni +++ b/turbojpeg-mapfile.jni @@ -1,14 +1,14 @@ TURBOJPEG_1.0 { global: - tjInitCompress; - tjCompress; TJBUFSIZE; - tjInitDecompress; - tjDecompressHeader; + tjCompress; tjDecompress; + tjDecompressHeader; tjDestroy; tjGetErrorStr; + tjInitCompress; + tjInitDecompress; local: *; }; diff --git a/turbojpeg.c b/turbojpeg.c index 826636a8b..b4672ce22 100644 --- a/turbojpeg.c +++ b/turbojpeg.c @@ -97,7 +97,7 @@ static void my_emit_message(j_common_ptr cinfo, int msg_level) } -/* Global structures, macros, etc. */ +/********************** Global structures, macros, etc. **********************/ enum { COMPRESS = 1, DECOMPRESS = 2 }; @@ -425,8 +425,9 @@ static int getSubsamp(j_decompress_ptr dinfo) } -/* General API functions */ +/*************************** General API functions ***************************/ +/* TurboJPEG 2.0+ */ DLLEXPORT char *tjGetErrorStr2(tjhandle handle) { tjinstance *this = (tjinstance *)handle; @@ -439,12 +440,14 @@ DLLEXPORT char *tjGetErrorStr2(tjhandle handle) } +/* TurboJPEG 1.0+ */ DLLEXPORT char *tjGetErrorStr(void) { return errStr; } +/* TurboJPEG 2.0+ */ DLLEXPORT int tjGetErrorCode(tjhandle handle) { tjinstance *this = (tjinstance *)handle; @@ -454,6 +457,7 @@ DLLEXPORT int tjGetErrorCode(tjhandle handle) } +/* TurboJPEG 1.0+ */ DLLEXPORT int tjDestroy(tjhandle handle) { GET_INSTANCE(handle); @@ -471,19 +475,21 @@ DLLEXPORT int tjDestroy(tjhandle handle) with turbojpeg.dll for compatibility reasons. However, these functions can potentially be used for other purposes by different implementations. */ +/* TurboJPEG 1.2+ */ DLLEXPORT void tjFree(unsigned char *buf) { free(buf); } +/* TurboJPEG 1.2+ */ DLLEXPORT unsigned char *tjAlloc(int bytes) { return (unsigned char *)malloc(bytes); } -/* Compressor */ +/******************************** Compressor *********************************/ static tjhandle _tjInitCompress(tjinstance *this) { @@ -515,6 +521,7 @@ static tjhandle _tjInitCompress(tjinstance *this) return (tjhandle)this; } +/* TurboJPEG 1.0+ */ DLLEXPORT tjhandle tjInitCompress(void) { tjinstance *this = NULL; @@ -530,6 +537,7 @@ DLLEXPORT tjhandle tjInitCompress(void) } +/* TurboJPEG 1.2+ */ DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp) { unsigned long long retval = 0; @@ -552,6 +560,7 @@ DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp) return (unsigned long)retval; } +/* TurboJPEG 1.0+ */ DLLEXPORT unsigned long TJBUFSIZE(int width, int height) { unsigned long long retval = 0; @@ -571,19 +580,20 @@ DLLEXPORT unsigned long TJBUFSIZE(int width, int height) } -DLLEXPORT unsigned long tjBufSizeYUV2(int width, int pad, int height, +/* TurboJPEG 1.4+ */ +DLLEXPORT unsigned long tjBufSizeYUV2(int width, int align, int height, int subsamp) { unsigned long long retval = 0; int nc, i; - if (pad < 1 || !IS_POW2(pad) || subsamp < 0 || subsamp >= TJ_NUMSAMP) + if (align < 1 || !IS_POW2(align) || subsamp < 0 || subsamp >= TJ_NUMSAMP) THROWG("tjBufSizeYUV2(): Invalid argument"); nc = (subsamp == TJSAMP_GRAY ? 1 : 3); for (i = 0; i < nc; i++) { int pw = tjPlaneWidth(i, width, subsamp); - int stride = PAD(pw, pad); + int stride = PAD(pw, align); int ph = tjPlaneHeight(i, height, subsamp); if (pw < 0 || ph < 0) return -1; @@ -596,17 +606,20 @@ DLLEXPORT unsigned long tjBufSizeYUV2(int width, int pad, int height, return (unsigned long)retval; } +/* TurboJPEG 1.2+ */ DLLEXPORT unsigned long tjBufSizeYUV(int width, int height, int subsamp) { return tjBufSizeYUV2(width, 4, height, subsamp); } +/* TurboJPEG 1.1+ */ DLLEXPORT unsigned long TJBUFSIZEYUV(int width, int height, int subsamp) { return tjBufSizeYUV(width, height, subsamp); } +/* TurboJPEG 1.4+ */ DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp) { int pw, nc, retval = 0; @@ -628,6 +641,7 @@ DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp) } +/* TurboJPEG 1.4+ */ DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp) { int ph, nc, retval = 0; @@ -649,6 +663,7 @@ DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp) } +/* TurboJPEG 1.4+ */ DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride, int height, int subsamp) { @@ -674,6 +689,7 @@ DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride, } +/* TurboJPEG 1.2+ */ DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf, int width, int pitch, int height, int pixelFormat, unsigned char **jpegBuf, unsigned long *jpegSize, @@ -742,6 +758,7 @@ DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf, return retval; } +/* TurboJPEG 1.0+ */ DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width, int pitch, int height, int pixelSize, unsigned char *jpegBuf, unsigned long *jpegSize, @@ -765,6 +782,7 @@ DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width, } +/* TurboJPEG 1.4+ */ DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf, int width, int pitch, int height, int pixelFormat, unsigned char **dstPlanes, @@ -797,7 +815,7 @@ DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf, THROW("tjEncodeYUVPlanes(): Invalid argument"); if (pixelFormat == TJPF_CMYK) - THROW("tjEncodeYUVPlanes(): Cannot generate YUV images from CMYK pixels"); + THROW("tjEncodeYUVPlanes(): Cannot generate YUV images from packed-pixel CMYK images"); if (pitch == 0) pitch = width * tjPixelSize[pixelFormat]; @@ -923,9 +941,10 @@ DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf, return retval; } +/* TurboJPEG 1.4+ */ DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf, int width, int pitch, int height, int pixelFormat, - unsigned char *dstBuf, int pad, int subsamp, + unsigned char *dstBuf, int align, int subsamp, int flags) { unsigned char *dstPlanes[3]; @@ -935,14 +954,14 @@ DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf, if (!this) THROWG("tjEncodeYUV3(): Invalid handle"); this->isInstanceError = FALSE; - if (width <= 0 || height <= 0 || dstBuf == NULL || pad < 1 || - !IS_POW2(pad) || subsamp < 0 || subsamp >= TJ_NUMSAMP) + if (width <= 0 || height <= 0 || dstBuf == NULL || align < 1 || + !IS_POW2(align) || subsamp < 0 || subsamp >= TJ_NUMSAMP) THROW("tjEncodeYUV3(): Invalid argument"); pw0 = tjPlaneWidth(0, width, subsamp); ph0 = tjPlaneHeight(0, height, subsamp); dstPlanes[0] = dstBuf; - strides[0] = PAD(pw0, pad); + strides[0] = PAD(pw0, align); if (subsamp == TJSAMP_GRAY) { strides[1] = strides[2] = 0; dstPlanes[1] = dstPlanes[2] = NULL; @@ -950,7 +969,7 @@ DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf, int pw1 = tjPlaneWidth(1, width, subsamp); int ph1 = tjPlaneHeight(1, height, subsamp); - strides[1] = strides[2] = PAD(pw1, pad); + strides[1] = strides[2] = PAD(pw1, align); dstPlanes[1] = dstPlanes[0] + strides[0] * ph0; dstPlanes[2] = dstPlanes[1] + strides[1] * ph1; } @@ -962,6 +981,7 @@ DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf, return retval; } +/* TurboJPEG 1.2+ */ DLLEXPORT int tjEncodeYUV2(tjhandle handle, unsigned char *srcBuf, int width, int pitch, int height, int pixelFormat, unsigned char *dstBuf, int subsamp, int flags) @@ -970,6 +990,7 @@ DLLEXPORT int tjEncodeYUV2(tjhandle handle, unsigned char *srcBuf, int width, dstBuf, 4, subsamp, flags); } +/* TurboJPEG 1.1+ */ DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width, int pitch, int height, int pixelSize, unsigned char *dstBuf, int subsamp, int flags) @@ -980,6 +1001,7 @@ DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width, } +/* TurboJPEG 1.4+ */ DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle, const unsigned char **srcPlanes, int width, const int *strides, @@ -1118,8 +1140,9 @@ DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle, return retval; } +/* TurboJPEG 1.4+ */ DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf, - int width, int pad, int height, int subsamp, + int width, int align, int height, int subsamp, unsigned char **jpegBuf, unsigned long *jpegSize, int jpegQual, int flags) @@ -1131,14 +1154,14 @@ DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf, if (!this) THROWG("tjCompressFromYUV(): Invalid handle"); this->isInstanceError = FALSE; - if (srcBuf == NULL || width <= 0 || pad < 1 || !IS_POW2(pad) || + if (srcBuf == NULL || width <= 0 || align < 1 || !IS_POW2(align) || height <= 0 || subsamp < 0 || subsamp >= TJ_NUMSAMP) THROW("tjCompressFromYUV(): Invalid argument"); pw0 = tjPlaneWidth(0, width, subsamp); ph0 = tjPlaneHeight(0, height, subsamp); srcPlanes[0] = srcBuf; - strides[0] = PAD(pw0, pad); + strides[0] = PAD(pw0, align); if (subsamp == TJSAMP_GRAY) { strides[1] = strides[2] = 0; srcPlanes[1] = srcPlanes[2] = NULL; @@ -1146,7 +1169,7 @@ DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf, int pw1 = tjPlaneWidth(1, width, subsamp); int ph1 = tjPlaneHeight(1, height, subsamp); - strides[1] = strides[2] = PAD(pw1, pad); + strides[1] = strides[2] = PAD(pw1, align); srcPlanes[1] = srcPlanes[0] + strides[0] * ph0; srcPlanes[2] = srcPlanes[1] + strides[1] * ph1; } @@ -1159,7 +1182,7 @@ DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf, } -/* Decompressor */ +/******************************* Decompressor ********************************/ static tjhandle _tjInitDecompress(tjinstance *this) { @@ -1189,6 +1212,7 @@ static tjhandle _tjInitDecompress(tjinstance *this) return (tjhandle)this; } +/* TurboJPEG 1.0+ */ DLLEXPORT tjhandle tjInitDecompress(void) { tjinstance *this; @@ -1204,6 +1228,7 @@ DLLEXPORT tjhandle tjInitDecompress(void) } +/* TurboJPEG 1.4+ */ DLLEXPORT int tjDecompressHeader3(tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, int *width, @@ -1260,6 +1285,7 @@ DLLEXPORT int tjDecompressHeader3(tjhandle handle, return retval; } +/* TurboJPEG 1.1+ */ DLLEXPORT int tjDecompressHeader2(tjhandle handle, unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height, int *jpegSubsamp) @@ -1270,6 +1296,7 @@ DLLEXPORT int tjDecompressHeader2(tjhandle handle, unsigned char *jpegBuf, jpegSubsamp, &jpegColorspace); } +/* TurboJPEG 1.0+ */ DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height) @@ -1281,19 +1308,21 @@ DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf, } -DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numscalingfactors) +/* TurboJPEG 1.2+ */ +DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numScalingFactors) { - if (numscalingfactors == NULL) { + if (numScalingFactors == NULL) { SNPRINTF(errStr, JMSG_LENGTH_MAX, "tjGetScalingFactors(): Invalid argument"); return NULL; } - *numscalingfactors = NUMSF; + *numScalingFactors = NUMSF; return (tjscalingfactor *)sf; } +/* TurboJPEG 1.2+ */ DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf, int width, int pitch, int height, int pixelFormat, @@ -1381,6 +1410,7 @@ DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf, return retval; } +/* TurboJPEG 1.0+ */ DLLEXPORT int tjDecompress(tjhandle handle, unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf, int width, int pitch, int height, int pixelSize, @@ -1442,6 +1472,7 @@ static void my_reset_marker_reader(j_decompress_ptr dinfo) { } +/* TurboJPEG 1.4+ */ DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle, const unsigned char **srcPlanes, const int *strides, int subsamp, @@ -1480,7 +1511,7 @@ DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle, } if (pixelFormat == TJPF_CMYK) - THROW("tjDecodeYUVPlanes(): Cannot decode YUV images into CMYK pixels."); + THROW("tjDecodeYUVPlanes(): Cannot decode YUV images into packed-pixel CMYK images."); if (pitch == 0) pitch = width * tjPixelSize[pixelFormat]; dinfo->image_width = width; @@ -1588,8 +1619,9 @@ DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle, return retval; } +/* TurboJPEG 1.4+ */ DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf, - int pad, int subsamp, unsigned char *dstBuf, + int align, int subsamp, unsigned char *dstBuf, int width, int pitch, int height, int pixelFormat, int flags) { @@ -1600,14 +1632,14 @@ DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf, if (!this) THROWG("tjDecodeYUV(): Invalid handle"); this->isInstanceError = FALSE; - if (srcBuf == NULL || pad < 1 || !IS_POW2(pad) || subsamp < 0 || + if (srcBuf == NULL || align < 1 || !IS_POW2(align) || subsamp < 0 || subsamp >= TJ_NUMSAMP || width <= 0 || height <= 0) THROW("tjDecodeYUV(): Invalid argument"); pw0 = tjPlaneWidth(0, width, subsamp); ph0 = tjPlaneHeight(0, height, subsamp); srcPlanes[0] = srcBuf; - strides[0] = PAD(pw0, pad); + strides[0] = PAD(pw0, align); if (subsamp == TJSAMP_GRAY) { strides[1] = strides[2] = 0; srcPlanes[1] = srcPlanes[2] = NULL; @@ -1615,7 +1647,7 @@ DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf, int pw1 = tjPlaneWidth(1, width, subsamp); int ph1 = tjPlaneHeight(1, height, subsamp); - strides[1] = strides[2] = PAD(pw1, pad); + strides[1] = strides[2] = PAD(pw1, align); srcPlanes[1] = srcPlanes[0] + strides[0] * ph0; srcPlanes[2] = srcPlanes[1] + strides[1] * ph1; } @@ -1627,6 +1659,7 @@ DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf, return retval; } +/* TurboJPEG 1.4+ */ DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, @@ -1807,9 +1840,10 @@ DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle, return retval; } +/* TurboJPEG 1.4+ */ DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf, - int width, int pad, int height, int flags) + int width, int align, int height, int flags) { unsigned char *dstPlanes[3]; int pw0, ph0, strides[3], retval = -1, jpegSubsamp = -1; @@ -1819,7 +1853,7 @@ DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf, this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE; if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 || - pad < 1 || !IS_POW2(pad) || height < 0) + align < 1 || !IS_POW2(align) || height < 0) THROW("tjDecompressToYUV2(): Invalid argument"); if (setjmp(this->jerr.setjmp_buffer)) { @@ -1836,7 +1870,6 @@ DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf, jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height; if (width == 0) width = jpegwidth; if (height == 0) height = jpegheight; - for (i = 0; i < NUMSF; i++) { scaledw = TJSCALED(jpegwidth, sf[i]); scaledh = TJSCALED(jpegheight, sf[i]); @@ -1849,7 +1882,7 @@ DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf, pw0 = tjPlaneWidth(0, width, jpegSubsamp); ph0 = tjPlaneHeight(0, height, jpegSubsamp); dstPlanes[0] = dstBuf; - strides[0] = PAD(pw0, pad); + strides[0] = PAD(pw0, align); if (jpegSubsamp == TJSAMP_GRAY) { strides[1] = strides[2] = 0; dstPlanes[1] = dstPlanes[2] = NULL; @@ -1857,7 +1890,7 @@ DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf, int pw1 = tjPlaneWidth(1, width, jpegSubsamp); int ph1 = tjPlaneHeight(1, height, jpegSubsamp); - strides[1] = strides[2] = PAD(pw1, pad); + strides[1] = strides[2] = PAD(pw1, align); dstPlanes[1] = dstPlanes[0] + strides[0] * ph0; dstPlanes[2] = dstPlanes[1] + strides[1] * ph1; } @@ -1871,6 +1904,7 @@ DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf, return retval; } +/* TurboJPEG 1.1+ */ DLLEXPORT int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf, int flags) @@ -1879,8 +1913,9 @@ DLLEXPORT int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf, } -/* Transformer */ +/******************************** Transformer ********************************/ +/* TurboJPEG 1.2+ */ DLLEXPORT tjhandle tjInitTransform(void) { tjinstance *this = NULL; @@ -1900,6 +1935,7 @@ DLLEXPORT tjhandle tjInitTransform(void) } +/* TurboJPEG 1.2+ */ DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, int n, unsigned char **dstBufs, unsigned long *dstSizes, @@ -2068,6 +2104,9 @@ DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf, } +/*************************** Packed-Pixel Image I/O **************************/ + +/* TurboJPEG 2.0+ */ DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width, int align, int *height, int *pixelFormat, int flags) @@ -2170,6 +2209,7 @@ DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width, } +/* TurboJPEG 2.0+ */ DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer, int width, int pitch, int height, int pixelFormat, int flags) diff --git a/turbojpeg.h b/turbojpeg.h index ac9ad4546..e6bdf5fb4 100644 --- a/turbojpeg.h +++ b/turbojpeg.h @@ -54,23 +54,24 @@ * Each plane is simply a 2D array of bytes, each byte representing the value * of one of the components (Y, Cb, or Cr) at a particular location in the * image. The width and height of each plane are determined by the image - * width, height, and level of chrominance subsampling. The luminance plane + * width, height, and level of chrominance subsampling. The luminance plane * width is the image width padded to the nearest multiple of the horizontal - * subsampling factor (2 in the case of 4:2:0 and 4:2:2, 4 in the case of - * 4:1:1, 1 in the case of 4:4:4 or grayscale.) Similarly, the luminance plane - * height is the image height padded to the nearest multiple of the vertical - * subsampling factor (2 in the case of 4:2:0 or 4:4:0, 1 in the case of 4:4:4 - * or grayscale.) This is irrespective of any additional padding that may be - * specified as an argument to the various YUV functions. The chrominance - * plane width is equal to the luminance plane width divided by the horizontal - * subsampling factor, and the chrominance plane height is equal to the - * luminance plane height divided by the vertical subsampling factor. + * subsampling factor (1 in the case of 4:4:4, grayscale, or 4:4:0; 2 in the + * case of 4:2:2 or 4:2:0; 4 in the case of 4:1:1.) Similarly, the luminance + * plane height is the image height padded to the nearest multiple of the + * vertical subsampling factor (1 in the case of 4:4:4, 4:2:2, grayscale, or + * 4:1:1; 2 in the case of 4:2:0 or 4:4:0.) This is irrespective of any + * additional padding that may be specified as an argument to the various YUV + * functions. The chrominance plane width is equal to the luminance plane + * width divided by the horizontal subsampling factor, and the chrominance + * plane height is equal to the luminance plane height divided by the vertical + * subsampling factor. * * For example, if the source image is 35 x 35 pixels and 4:2:2 subsampling is * used, then the luminance plane would be 36 x 35 bytes, and each of the - * chrominance planes would be 18 x 35 bytes. If you specify a line padding of - * 4 bytes on top of this, then the luminance plane would be 36 x 35 bytes, and - * each of the chrominance planes would be 20 x 35 bytes. + * chrominance planes would be 18 x 35 bytes. If you specify a row alignment + * of 4 bytes on top of this, then the luminance plane would be 36 x 35 bytes, + * and each of the chrominance planes would be 20 x 35 bytes. * * @{ */ @@ -86,8 +87,8 @@ * When pixels are converted from RGB to YCbCr (see #TJCS_YCbCr) or from CMYK * to YCCK (see #TJCS_YCCK) as part of the JPEG compression process, some of * the Cb and Cr (chrominance) components can be discarded or averaged together - * to produce a smaller image with little perceptible loss of image clarity - * (the human eye is more sensitive to small changes in brightness than to + * to produce a smaller image with little perceptible loss of image clarity. + * (The human eye is more sensitive to small changes in brightness than to * small changes in color.) This is called "chrominance subsampling". */ enum TJSAMP { @@ -245,8 +246,8 @@ enum TJPF { * vice versa, but the mapping is typically not 1:1 or reversible, nor can it * be defined with a simple formula. Thus, such a conversion is out of scope * for a codec library. However, the TurboJPEG API allows for compressing - * CMYK pixels into a YCCK JPEG image (see #TJCS_YCCK) and decompressing YCCK - * JPEG images into CMYK pixels. + * packed-pixel CMYK images into YCCK JPEG images (see #TJCS_YCCK) and + * decompressing YCCK JPEG images into packed-pixel CMYK images. */ TJPF_CMYK, /** @@ -258,9 +259,10 @@ enum TJPF { /** * Red offset (in bytes) for a given pixel format. This specifies the number * of bytes that the red component is offset from the start of the pixel. For - * instance, if a pixel of format TJ_BGRX is stored in char pixel[], - * then the red component will be pixel[tjRedOffset[TJ_BGRX]]. This - * will be -1 if the pixel format does not have a red component. + * instance, if a pixel of format TJPF_BGRX is stored in + * `unsigned char pixel[]`, then the red component will be + *`pixel[tjRedOffset[TJPF_BGRX]]`. This will be -1 if the pixel format does + * not have a red component. */ static const int tjRedOffset[TJ_NUMPF] = { 0, 2, 0, 2, 3, 1, -1, 0, 2, 3, 1, -1 @@ -268,31 +270,32 @@ static const int tjRedOffset[TJ_NUMPF] = { /** * Green offset (in bytes) for a given pixel format. This specifies the number * of bytes that the green component is offset from the start of the pixel. - * For instance, if a pixel of format TJ_BGRX is stored in - * char pixel[], then the green component will be - * pixel[tjGreenOffset[TJ_BGRX]]. This will be -1 if the pixel format - * does not have a green component. + * For instance, if a pixel of format TJPF_BGRX is stored in + * `unsigned char pixel[]`, then the green component will be + * `pixel[tjGreenOffset[TJPF_BGRX]]`. This will be -1 if the pixel format does + * not have a green component. */ static const int tjGreenOffset[TJ_NUMPF] = { 1, 1, 1, 1, 2, 2, -1, 1, 1, 2, 2, -1 }; /** * Blue offset (in bytes) for a given pixel format. This specifies the number - * of bytes that the Blue component is offset from the start of the pixel. For - * instance, if a pixel of format TJ_BGRX is stored in char pixel[], - * then the blue component will be pixel[tjBlueOffset[TJ_BGRX]]. This - * will be -1 if the pixel format does not have a blue component. + * of bytes that the blue component is offset from the start of the pixel. For + * instance, if a pixel of format TJPF_BGRX is stored in + * `unsigned char pixel[]`, then the blue component will be + * `pixel[tjBlueOffset[TJPF_BGRX]]`. This will be -1 if the pixel format does + * not have a blue component. */ static const int tjBlueOffset[TJ_NUMPF] = { 2, 0, 2, 0, 1, 3, -1, 2, 0, 1, 3, -1 }; /** * Alpha offset (in bytes) for a given pixel format. This specifies the number - * of bytes that the Alpha component is offset from the start of the pixel. - * For instance, if a pixel of format TJ_BGRA is stored in - * char pixel[], then the alpha component will be - * pixel[tjAlphaOffset[TJ_BGRA]]. This will be -1 if the pixel format - * does not have an alpha component. + * of bytes that the alpha component is offset from the start of the pixel. + * For instance, if a pixel of format TJPF_BGRA is stored in + * `unsigned char pixel[]`, then the alpha component will be + * `pixel[tjAlphaOffset[TJPF_BGRA]]`. This will be -1 if the pixel format does + * not have an alpha component. */ static const int tjAlphaOffset[TJ_NUMPF] = { -1, -1, -1, -1, -1, -1, -1, 3, 3, 0, 0, -1 @@ -318,8 +321,9 @@ enum TJCS { * RGB colorspace. When compressing the JPEG image, the R, G, and B * components in the source image are reordered into image planes, but no * colorspace conversion or subsampling is performed. RGB JPEG images can be - * decompressed to any of the extended RGB pixel formats or grayscale, but - * they cannot be decompressed to YUV images. + * decompressed to packed-pixel images with any of the extended RGB or + * grayscale pixel formats, but they cannot be decompressed to planar YUV + * images. */ TJCS_RGB = 0, /** @@ -332,25 +336,27 @@ enum TJCS { * original image. Originally, the analog equivalent of this transformation * allowed the same signal to drive both black & white and color televisions, * but JPEG images use YCbCr primarily because it allows the color data to be - * optionally subsampled for the purposes of reducing bandwidth or disk - * space. YCbCr is the most common JPEG colorspace, and YCbCr JPEG images - * can be compressed from and decompressed to any of the extended RGB pixel - * formats or grayscale, or they can be decompressed to YUV planar images. + * optionally subsampled for the purposes of reducing network or disk usage. + * YCbCr is the most common JPEG colorspace, and YCbCr JPEG images can be + * compressed from and decompressed to packed-pixel images with any of the + * extended RGB or grayscale pixel formats. YCbCr JPEG images can also be + * compressed from and decompressed to planar YUV images. */ TJCS_YCbCr, /** * Grayscale colorspace. The JPEG image retains only the luminance data (Y * component), and any color data from the source image is discarded. - * Grayscale JPEG images can be compressed from and decompressed to any of - * the extended RGB pixel formats or grayscale, or they can be decompressed - * to YUV planar images. + * Grayscale JPEG images can be compressed from and decompressed to + * packed-pixel images with any of the extended RGB or grayscale pixel + * formats, or they can be compressed from and decompressed to planar YUV + * images. */ TJCS_GRAY, /** * CMYK colorspace. When compressing the JPEG image, the C, M, Y, and K * components in the source image are reordered into image planes, but no * colorspace conversion or subsampling is performed. CMYK JPEG images can - * only be decompressed to CMYK pixels. + * only be decompressed to packed-pixel images with the CMYK pixel format. */ TJCS_CMYK, /** @@ -360,56 +366,54 @@ enum TJCS { * reversibly transformed into YCCK, and as with YCbCr, the chrominance * components in the YCCK pixels can be subsampled without incurring major * perceptual loss. YCCK JPEG images can only be compressed from and - * decompressed to CMYK pixels. + * decompressed to packed-pixel images with the CMYK pixel format. */ TJCS_YCCK }; /** - * The uncompressed source/destination image is stored in bottom-up (Windows, - * OpenGL) order, not top-down (X11) order. + * Rows in the packed-pixel source/destination image are stored in bottom-up + * (Windows, OpenGL) order rather than in top-down (X11) order. */ #define TJFLAG_BOTTOMUP 2 /** * When decompressing an image that was compressed using chrominance - * subsampling, use the fastest chrominance upsampling algorithm available in - * the underlying codec. The default is to use smooth upsampling, which - * creates a smooth transition between neighboring chrominance components in - * order to reduce upsampling artifacts in the decompressed image. + * subsampling, use the fastest chrominance upsampling algorithm available. + * The default is to use smooth upsampling, which creates a smooth transition + * between neighboring chrominance components in order to reduce upsampling + * artifacts in the decompressed image. */ #define TJFLAG_FASTUPSAMPLE 256 /** - * Disable buffer (re)allocation. If passed to one of the JPEG compression or - * transform functions, this flag will cause those functions to generate an - * error if the JPEG image buffer is invalid or too small rather than - * attempting to allocate or reallocate that buffer. This reproduces the - * behavior of earlier versions of TurboJPEG. + * Disable JPEG buffer (re)allocation. If passed to one of the JPEG + * compression or transform functions, this flag will cause those functions to + * generate an error if the JPEG destination buffer is invalid or too small, + * rather than attempt to allocate or reallocate that buffer. */ #define TJFLAG_NOREALLOC 1024 /** - * Use the fastest DCT/IDCT algorithm available in the underlying codec. The - * default if this flag is not specified is implementation-specific. For - * example, the implementation of TurboJPEG for libjpeg[-turbo] uses the fast - * algorithm by default when compressing, because this has been shown to have - * only a very slight effect on accuracy, but it uses the accurate algorithm - * when decompressing, because this has been shown to have a larger effect. + * Use the fastest DCT/IDCT algorithm available. The default if this flag is + * not specified is implementation-specific. For example, the implementation + * of the TurboJPEG API in libjpeg-turbo uses the fast algorithm by default + * when compressing, because this has been shown to have only a very slight + * effect on accuracy, but it uses the accurate algorithm when decompressing, + * because this has been shown to have a larger effect. */ #define TJFLAG_FASTDCT 2048 /** - * Use the most accurate DCT/IDCT algorithm available in the underlying codec. - * The default if this flag is not specified is implementation-specific. For - * example, the implementation of TurboJPEG for libjpeg[-turbo] uses the fast - * algorithm by default when compressing, because this has been shown to have - * only a very slight effect on accuracy, but it uses the accurate algorithm - * when decompressing, because this has been shown to have a larger effect. + * Use the most accurate DCT/IDCT algorithm available. The default if this + * flag is not specified is implementation-specific. For example, the + * implementation of the TurboJPEG API in libjpeg-turbo uses the fast algorithm + * by default when compressing, because this has been shown to have only a very + * slight effect on accuracy, but it uses the accurate algorithm when + * decompressing, because this has been shown to have a larger effect. */ #define TJFLAG_ACCURATEDCT 4096 /** * Immediately discontinue the current compression/decompression/transform - * operation if the underlying codec throws a warning (non-fatal error). The - * default behavior is to allow the operation to complete unless a fatal error - * is encountered. + * operation if a warning (non-fatal error) occurs. The default behavior is to + * allow the operation to complete unless a fatal error is encountered. */ #define TJFLAG_STOPONWARNING 8192 /** @@ -441,8 +445,8 @@ enum TJCS { */ enum TJERR { /** - * The error was non-fatal and recoverable, but the image may still be - * corrupt. + * The error was non-fatal and recoverable, but the destination image may + * still be corrupt. */ TJERR_WARNING = 0, /** @@ -509,9 +513,9 @@ enum TJXOP { /** * This option will cause #tjTransform() to return an error if the transform is * not perfect. Lossless transforms operate on MCU blocks, whose size depends - * on the level of chrominance subsampling used (see #tjMCUWidth - * and #tjMCUHeight.) If the image's width or height is not evenly divisible - * by the MCU block size, then there will be partial MCU blocks on the right + * on the level of chrominance subsampling used (see #tjMCUWidth and + * #tjMCUHeight.) If the image's width or height is not evenly divisible by + * the MCU block size, then there will be partial MCU blocks on the right * and/or bottom edges. It is not possible to move these partial MCU blocks to * the top or left of the image, so any transform that would require that is * "imperfect." If this option is not specified, then any partial MCU blocks @@ -530,19 +534,19 @@ enum TJXOP { */ #define TJXOPT_CROP 4 /** - * This option will discard the color data in the input image and produce - * a grayscale output image. + * This option will discard the color data in the source image and produce a + * grayscale destination image. */ #define TJXOPT_GRAY 8 /** * This option will prevent #tjTransform() from outputting a JPEG image for - * this particular transform (this can be used in conjunction with a custom + * this particular transform. (This can be used in conjunction with a custom * filter to capture the transformed DCT coefficients without transcoding * them.) */ #define TJXOPT_NOOUTPUT 16 /** - * This option will enable progressive entropy coding in the output image + * This option will enable progressive entropy coding in the JPEG image * generated by this particular transform. Progressive entropy coding will * generally improve compression relative to baseline entropy coding (the * default), but it will reduce compression and decompression performance @@ -551,8 +555,8 @@ enum TJXOP { #define TJXOPT_PROGRESSIVE 32 /** * This option will prevent #tjTransform() from copying any extra markers - * (including EXIF and ICC profile data) from the source image to the output - * image. + * (including EXIF and ICC profile data) from the source image to the + * destination image. */ #define TJXOPT_COPYNONE 64 @@ -586,12 +590,12 @@ typedef struct { */ int y; /** - * The width of the cropping region. Setting this to 0 is the equivalent of + * The width of the cropping region. Setting this to 0 is the equivalent of * setting it to the width of the source JPEG image - x. */ int w; /** - * The height of the cropping region. Setting this to 0 is the equivalent of + * The height of the cropping region. Setting this to 0 is the equivalent of * setting it to the height of the source JPEG image - y. */ int h; @@ -610,7 +614,8 @@ typedef struct tjtransform { */ int op; /** - * The bitwise OR of one of more of the @ref TJXOPT_CROP "transform options" + * The bitwise OR of one of more of the @ref TJXOPT_COPYNONE + * "transform options" */ int options; /** @@ -619,10 +624,10 @@ typedef struct tjtransform { */ void *data; /** - * A callback function that can be used to modify the DCT coefficients - * after they are losslessly transformed but before they are transcoded to a - * new JPEG image. This allows for custom filters or other transformations - * to be applied in the frequency domain. + * A callback function that can be used to modify the DCT coefficients after + * they are losslessly transformed but before they are transcoded to a new + * JPEG image. This allows for custom filters or other transformations to be + * applied in the frequency domain. * * @param coeffs pointer to an array of transformed DCT coefficients. (NOTE: * this pointer is not guaranteed to be valid once the callback returns, so @@ -630,21 +635,21 @@ typedef struct tjtransform { * or library should make a copy of them within the body of the callback.) * * @param arrayRegion #tjregion structure containing the width and height of - * the array pointed to by coeffs as well as its offset relative to - * the component plane. TurboJPEG implementations may choose to split each + * the array pointed to by `coeffs` as well as its offset relative to the + * component plane. TurboJPEG implementations may choose to split each * component plane into multiple DCT coefficient arrays and call the callback * function once for each array. * * @param planeRegion #tjregion structure containing the width and height of - * the component plane to which coeffs belongs + * the component plane to which `coeffs` belongs * - * @param componentID ID number of the component plane to which - * coeffs belongs (Y, Cb, and Cr have, respectively, ID's of 0, 1, - * and 2 in typical JPEG images.) + * @param componentID ID number of the component plane to which `coeffs` + * belongs. (Y, Cb, and Cr have, respectively, ID's of 0, 1, and 2 in + * typical JPEG images.) * - * @param transformID ID number of the transformed image to which - * coeffs belongs. This is the same as the index of the transform - * in the transforms array that was passed to #tjTransform(). + * @param transformID ID number of the transformed image to which `coeffs` + * belongs. This is the same as the index of the transform in the + * `transforms` array that was passed to #tjTransform(). * * @param transform a pointer to a #tjtransform structure that specifies the * parameters and/or cropping region for this transform @@ -663,14 +668,14 @@ typedef void *tjhandle; /** - * Pad the given width to the nearest 32-bit boundary + * Pad the given width to the nearest multiple of 4 */ #define TJPAD(width) (((width) + 3) & (~3)) /** - * Compute the scaled value of dimension using the given scaling - * factor. This macro performs the integer equivalent of ceil(dimension * - * scalingFactor). + * Compute the scaled value of `dimension` using the given scaling factor. + * This macro performs the integer equivalent of `ceil(dimension * + * scalingFactor)`. */ #define TJSCALED(dimension, scalingFactor) \ (((dimension) * scalingFactor.num + scalingFactor.denom - 1) / \ @@ -685,27 +690,27 @@ extern "C" { /** * Create a TurboJPEG compressor instance. * - * @return a handle to the newly-created instance, or NULL if an error - * occurred (see #tjGetErrorStr2().) + * @return a handle to the newly-created instance, or NULL if an error occurred + * (see #tjGetErrorStr2().) */ DLLEXPORT tjhandle tjInitCompress(void); /** - * Compress an RGB, grayscale, or CMYK image into a JPEG image. + * Compress a packed-pixel RGB, grayscale, or CMYK image into a JPEG image. * * @param handle a handle to a TurboJPEG compressor or transformer instance * - * @param srcBuf pointer to an image buffer containing RGB, grayscale, or - * CMYK pixels to be compressed + * @param srcBuf pointer to a buffer containing a packed-pixel RGB, grayscale, + * or CMYK source image to be compressed * * @param width width (in pixels) of the source image * - * @param pitch bytes per line in the source image. Normally, this should be - * width * #tjPixelSize[pixelFormat] if the image is unpadded, or - * #TJPAD(width * #tjPixelSize[pixelFormat]) if each line of the image - * is padded to the nearest 32-bit boundary, as is the case for Windows - * bitmaps. You can also be clever and use this parameter to skip lines, etc. + * @param pitch bytes per row in the source image. Normally this should be + * width * #tjPixelSize[pixelFormat], if the image is unpadded, or + * #TJPAD(width * #tjPixelSize[pixelFormat]) if each row of the image + * is padded to the nearest multiple of 4 bytes, as is the case for Windows + * bitmaps. You can also be clever and use this parameter to skip rows, etc. * Setting this parameter to 0 is the equivalent of setting it to * width * #tjPixelSize[pixelFormat]. * @@ -714,29 +719,28 @@ DLLEXPORT tjhandle tjInitCompress(void); * @param pixelFormat pixel format of the source image (see @ref TJPF * "Pixel formats".) * - * @param jpegBuf address of a pointer to an image buffer that will receive the - * JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer - * to accommodate the size of the JPEG image. Thus, you can choose to: + * @param jpegBuf address of a pointer to a byte buffer that will receive the + * JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to + * accommodate the size of the JPEG image. Thus, you can choose to: * -# pre-allocate the JPEG buffer with an arbitrary size using #tjAlloc() and * let TurboJPEG grow the buffer as needed, - * -# set *jpegBuf to NULL to tell TurboJPEG to allocate the buffer - * for you, or + * -# set `*jpegBuf` to NULL to tell TurboJPEG to allocate the buffer for you, + * or * -# pre-allocate the buffer to a "worst case" size determined by calling * #tjBufSize(). This should ensure that the buffer never has to be - * re-allocated (setting #TJFLAG_NOREALLOC guarantees that it won't be.) + * re-allocated. (Setting #TJFLAG_NOREALLOC guarantees that it won't be.) * . - * If you choose option 1, *jpegSize should be set to the size of your + * If you choose option 1, then `*jpegSize` should be set to the size of your * pre-allocated buffer. In any case, unless you have set #TJFLAG_NOREALLOC, - * you should always check *jpegBuf upon return from this function, as - * it may have changed. + * you should always check `*jpegBuf` upon return from this function, as it may + * have changed. * * @param jpegSize pointer to an unsigned long variable that holds the size of - * the JPEG image buffer. If *jpegBuf points to a pre-allocated - * buffer, then *jpegSize should be set to the size of the buffer. - * Upon return, *jpegSize will contain the size of the JPEG image (in - * bytes.) If *jpegBuf points to a JPEG image buffer that is being - * reused from a previous call to one of the JPEG compression functions, then - * *jpegSize is ignored. + * the JPEG buffer. If `*jpegBuf` points to a pre-allocated buffer, then + * `*jpegSize` should be set to the size of the buffer. Upon return, + * `*jpegSize` will contain the size of the JPEG image (in bytes.) If + * `*jpegBuf` points to a JPEG buffer that is being reused from a previous call + * to one of the JPEG compression functions, then `*jpegSize` is ignored. * * @param jpegSubsamp the level of chrominance subsampling to be used when * generating the JPEG image (see @ref TJSAMP @@ -750,7 +754,7 @@ DLLEXPORT tjhandle tjInitCompress(void); * * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() * and #tjGetErrorCode().) -*/ + */ DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf, int width, int pitch, int height, int pixelFormat, unsigned char **jpegBuf, unsigned long *jpegSize, @@ -758,55 +762,55 @@ DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf, /** - * Compress a YUV planar image into a JPEG image. + * Compress a unified planar YUV image into a JPEG image. * * @param handle a handle to a TurboJPEG compressor or transformer instance * - * @param srcBuf pointer to an image buffer containing a YUV planar image to be - * compressed. The size of this buffer should match the value returned by - * #tjBufSizeYUV2() for the given image width, height, padding, and level of - * chrominance subsampling. The Y, U (Cb), and V (Cr) image planes should be - * stored sequentially in the source buffer (refer to @ref YUVnotes - * "YUV Image Format Notes".) + * @param srcBuf pointer to a buffer containing a unified planar YUV source + * image to be compressed. The size of this buffer should match the value + * returned by #tjBufSizeYUV2() for the given image width, height, row + * alignment, and level of chrominance subsampling. The Y, U (Cb), and V (Cr) + * image planes should be stored sequentially in the buffer. (Refer to + * @ref YUVnotes "YUV Image Format Notes".) * * @param width width (in pixels) of the source image. If the width is not an * even multiple of the MCU block width (see #tjMCUWidth), then an intermediate - * buffer copy will be performed within TurboJPEG. + * buffer copy will be performed. * - * @param pad the line padding used in the source image (must be a power of 2.) - * For instance, if each line in each plane of the YUV image is padded to the - * nearest multiple of 4 bytes, then pad should be set to 4. + * @param align row alignment (in bytes) of the source image (must be a power + * of 2.) Setting this parameter to n indicates that each row in each plane of + * the source image is padded to the nearest multiple of n bytes + * (1 = unpadded.) * * @param height height (in pixels) of the source image. If the height is not * an even multiple of the MCU block height (see #tjMCUHeight), then an - * intermediate buffer copy will be performed within TurboJPEG. + * intermediate buffer copy will be performed. * - * @param subsamp the level of chrominance subsampling used in the source - * image (see @ref TJSAMP "Chrominance subsampling options".) + * @param subsamp the level of chrominance subsampling used in the source image + * (see @ref TJSAMP "Chrominance subsampling options".) * - * @param jpegBuf address of a pointer to an image buffer that will receive the + * @param jpegBuf address of a pointer to a byte buffer that will receive the * JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to * accommodate the size of the JPEG image. Thus, you can choose to: * -# pre-allocate the JPEG buffer with an arbitrary size using #tjAlloc() and * let TurboJPEG grow the buffer as needed, - * -# set *jpegBuf to NULL to tell TurboJPEG to allocate the buffer - * for you, or + * -# set `*jpegBuf` to NULL to tell TurboJPEG to allocate the buffer for you, + * or * -# pre-allocate the buffer to a "worst case" size determined by calling * #tjBufSize(). This should ensure that the buffer never has to be - * re-allocated (setting #TJFLAG_NOREALLOC guarantees that it won't be.) + * re-allocated. (Setting #TJFLAG_NOREALLOC guarantees that it won't be.) * . - * If you choose option 1, *jpegSize should be set to the size of your + * If you choose option 1, then `*jpegSize` should be set to the size of your * pre-allocated buffer. In any case, unless you have set #TJFLAG_NOREALLOC, - * you should always check *jpegBuf upon return from this function, as - * it may have changed. + * you should always check `*jpegBuf` upon return from this function, as it may + * have changed. * * @param jpegSize pointer to an unsigned long variable that holds the size of - * the JPEG image buffer. If *jpegBuf points to a pre-allocated - * buffer, then *jpegSize should be set to the size of the buffer. - * Upon return, *jpegSize will contain the size of the JPEG image (in - * bytes.) If *jpegBuf points to a JPEG image buffer that is being - * reused from a previous call to one of the JPEG compression functions, then - * *jpegSize is ignored. + * the JPEG buffer. If `*jpegBuf` points to a pre-allocated buffer, then + * `*jpegSize` should be set to the size of the buffer. Upon return, + * `*jpegSize` will contain the size of the JPEG image (in bytes.) If + * `*jpegBuf` points to a JPEG buffer that is being reused from a previous call + * to one of the JPEG compression functions, then `*jpegSize` is ignored. * * @param jpegQual the image quality of the generated JPEG image (1 = worst, * 100 = best) @@ -816,9 +820,9 @@ DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf, * * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() * and #tjGetErrorCode().) -*/ + */ DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf, - int width, int pad, int height, int subsamp, + int width, int align, int height, int subsamp, unsigned char **jpegBuf, unsigned long *jpegSize, int jpegQual, int flags); @@ -831,55 +835,54 @@ DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf, * * @param srcPlanes an array of pointers to Y, U (Cb), and V (Cr) image planes * (or just a Y plane, if compressing a grayscale image) that contain a YUV - * image to be compressed. These planes can be contiguous or non-contiguous in - * memory. The size of each plane should match the value returned by - * #tjPlaneSizeYUV() for the given image width, height, strides, and level of - * chrominance subsampling. Refer to @ref YUVnotes "YUV Image Format Notes" - * for more details. + * source image to be compressed. These planes can be contiguous or + * non-contiguous in memory. The size of each plane should match the value + * returned by #tjPlaneSizeYUV() for the given image width, height, strides, + * and level of chrominance subsampling. Refer to @ref YUVnotes + * "YUV Image Format Notes" for more details. * * @param width width (in pixels) of the source image. If the width is not an * even multiple of the MCU block width (see #tjMCUWidth), then an intermediate - * buffer copy will be performed within TurboJPEG. + * buffer copy will be performed. * * @param strides an array of integers, each specifying the number of bytes per - * line in the corresponding plane of the YUV source image. Setting the stride + * row in the corresponding plane of the YUV source image. Setting the stride * for any plane to 0 is the same as setting it to the plane width (see - * @ref YUVnotes "YUV Image Format Notes".) If strides is NULL, then - * the strides for all planes will be set to their respective plane widths. - * You can adjust the strides in order to specify an arbitrary amount of line + * @ref YUVnotes "YUV Image Format Notes".) If `strides` is NULL, then the + * strides for all planes will be set to their respective plane widths. You + * can adjust the strides in order to specify an arbitrary amount of row * padding in each plane or to create a JPEG image from a subregion of a larger - * YUV planar image. + * planar YUV image. * * @param height height (in pixels) of the source image. If the height is not * an even multiple of the MCU block height (see #tjMCUHeight), then an - * intermediate buffer copy will be performed within TurboJPEG. + * intermediate buffer copy will be performed. * - * @param subsamp the level of chrominance subsampling used in the source - * image (see @ref TJSAMP "Chrominance subsampling options".) + * @param subsamp the level of chrominance subsampling used in the source image + * (see @ref TJSAMP "Chrominance subsampling options".) * - * @param jpegBuf address of a pointer to an image buffer that will receive the + * @param jpegBuf address of a pointer to a byte buffer that will receive the * JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to * accommodate the size of the JPEG image. Thus, you can choose to: * -# pre-allocate the JPEG buffer with an arbitrary size using #tjAlloc() and * let TurboJPEG grow the buffer as needed, - * -# set *jpegBuf to NULL to tell TurboJPEG to allocate the buffer - * for you, or + * -# set `*jpegBuf` to NULL to tell TurboJPEG to allocate the buffer for you, + * or * -# pre-allocate the buffer to a "worst case" size determined by calling * #tjBufSize(). This should ensure that the buffer never has to be - * re-allocated (setting #TJFLAG_NOREALLOC guarantees that it won't be.) + * re-allocated. (Setting #TJFLAG_NOREALLOC guarantees that it won't be.) * . - * If you choose option 1, *jpegSize should be set to the size of your + * If you choose option 1, then `*jpegSize` should be set to the size of your * pre-allocated buffer. In any case, unless you have set #TJFLAG_NOREALLOC, - * you should always check *jpegBuf upon return from this function, as - * it may have changed. + * you should always check `*jpegBuf` upon return from this function, as it may + * have changed. * * @param jpegSize pointer to an unsigned long variable that holds the size of - * the JPEG image buffer. If *jpegBuf points to a pre-allocated - * buffer, then *jpegSize should be set to the size of the buffer. - * Upon return, *jpegSize will contain the size of the JPEG image (in - * bytes.) If *jpegBuf points to a JPEG image buffer that is being - * reused from a previous call to one of the JPEG compression functions, then - * *jpegSize is ignored. + * the JPEG buffer. If `*jpegBuf` points to a pre-allocated buffer, then + * `*jpegSize` should be set to the size of the buffer. Upon return, + * `*jpegSize` will contain the size of the JPEG image (in bytes.) If + * `*jpegBuf` points to a JPEG buffer that is being reused from a previous call + * to one of the JPEG compression functions, then `*jpegSize` is ignored. * * @param jpegQual the image quality of the generated JPEG image (1 = worst, * 100 = best) @@ -889,7 +892,7 @@ DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf, * * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() * and #tjGetErrorCode().) -*/ + */ DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle, const unsigned char **srcPlanes, int width, const int *strides, @@ -903,11 +906,11 @@ DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle, * The maximum size of the buffer (in bytes) required to hold a JPEG image with * the given parameters. The number of bytes returned by this function is * larger than the size of the uncompressed source image. The reason for this - * is that the JPEG format uses 16-bit coefficients, and it is thus possible - * for a very high-quality JPEG image with very high-frequency content to - * expand rather than compress when converted to the JPEG format. Such images - * represent a very rare corner case, but since there is no way to predict the - * size of a JPEG image prior to compression, the corner case has to be + * is that the JPEG format uses 16-bit coefficients, so it is possible for a + * very high-quality source image with very high-frequency content to expand + * rather than compress when converted to the JPEG format. Such images + * represent very rare corner cases, but since there is no way to predict the + * size of a JPEG image prior to compression, the corner cases have to be * handled. * * @param width width (in pixels) of the image @@ -925,23 +928,24 @@ DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp); /** - * The size of the buffer (in bytes) required to hold a YUV planar image with - * the given parameters. + * The size of the buffer (in bytes) required to hold a unified planar YUV + * image with the given parameters. * * @param width width (in pixels) of the image * - * @param pad the width of each line in each plane of the image is padded to - * the nearest multiple of this number of bytes (must be a power of 2.) + * @param align row alignment (in bytes) of the image (must be a power of 2.) + * Setting this parameter to n specifies that each row in each plane of the + * image will be padded to the nearest multiple of n bytes (1 = unpadded.) * * @param height height (in pixels) of the image * * @param subsamp level of chrominance subsampling in the image (see * @ref TJSAMP "Chrominance subsampling options".) * - * @return the size of the buffer (in bytes) required to hold the image, or - * -1 if the arguments are out of bounds. + * @return the size of the buffer (in bytes) required to hold the image, or -1 + * if the arguments are out of bounds. */ -DLLEXPORT unsigned long tjBufSizeYUV2(int width, int pad, int height, +DLLEXPORT unsigned long tjBufSizeYUV2(int width, int align, int height, int subsamp); @@ -954,7 +958,7 @@ DLLEXPORT unsigned long tjBufSizeYUV2(int width, int pad, int height, * @param width width (in pixels) of the YUV image. NOTE: this is the width of * the whole image, not the plane width. * - * @param stride bytes per line in the image plane. Setting this to 0 is the + * @param stride bytes per row in the image plane. Setting this to 0 is the * equivalent of setting it to the plane width. * * @param height height (in pixels) of the YUV image. NOTE: this is the height @@ -1005,23 +1009,23 @@ DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp); /** - * Encode an RGB or grayscale image into a YUV planar image. This function - * uses the accelerated color conversion routines in the underlying - * codec but does not execute any of the other steps in the JPEG compression - * process. + * Encode a packed-pixel RGB or grayscale image into a unified planar YUV + * image. This function performs color conversion (which is accelerated in the + * libjpeg-turbo implementation) but does not execute any of the other steps in + * the JPEG compression process. * * @param handle a handle to a TurboJPEG compressor or transformer instance * - * @param srcBuf pointer to an image buffer containing RGB or grayscale pixels - * to be encoded + * @param srcBuf pointer to a buffer containing a packed-pixel RGB or grayscale + * source image to be encoded * * @param width width (in pixels) of the source image * - * @param pitch bytes per line in the source image. Normally, this should be - * width * #tjPixelSize[pixelFormat] if the image is unpadded, or - * #TJPAD(width * #tjPixelSize[pixelFormat]) if each line of the image - * is padded to the nearest 32-bit boundary, as is the case for Windows - * bitmaps. You can also be clever and use this parameter to skip lines, etc. + * @param pitch bytes per row in the source image. Normally this should be + * width * #tjPixelSize[pixelFormat], if the image is unpadded, or + * #TJPAD(width * #tjPixelSize[pixelFormat]) if each row of the image + * is padded to the nearest multiple of 4 bytes, as is the case for Windows + * bitmaps. You can also be clever and use this parameter to skip rows, etc. * Setting this parameter to 0 is the equivalent of setting it to * width * #tjPixelSize[pixelFormat]. * @@ -1030,53 +1034,54 @@ DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp); * @param pixelFormat pixel format of the source image (see @ref TJPF * "Pixel formats".) * - * @param dstBuf pointer to an image buffer that will receive the YUV image. - * Use #tjBufSizeYUV2() to determine the appropriate size for this buffer based - * on the image width, height, padding, and level of chrominance subsampling. - * The Y, U (Cb), and V (Cr) image planes will be stored sequentially in the - * buffer (refer to @ref YUVnotes "YUV Image Format Notes".) + * @param dstBuf pointer to a buffer that will receive the unified planar YUV + * image. Use #tjBufSizeYUV2() to determine the appropriate size for this + * buffer based on the image width, height, row alignment, and level of + * chrominance subsampling. The Y, U (Cb), and V (Cr) image planes will be + * stored sequentially in the buffer. (Refer to @ref YUVnotes + * "YUV Image Format Notes".) * - * @param pad the width of each line in each plane of the YUV image will be - * padded to the nearest multiple of this number of bytes (must be a power of - * 2.) To generate images suitable for X Video, pad should be set to - * 4. + * @param align row alignment (in bytes) of the YUV image (must be a power of + * 2.) Setting this parameter to n will cause each row in each plane of the + * YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.) + * To generate images suitable for X Video, `align` should be set to 4. * * @param subsamp the level of chrominance subsampling to be used when * generating the YUV image (see @ref TJSAMP * "Chrominance subsampling options".) To generate images suitable for X - * Video, subsamp should be set to @ref TJSAMP_420. This produces an - * image compatible with the I420 (AKA "YUV420P") format. + * Video, `subsamp` should be set to @ref TJSAMP_420. This produces an image + * compatible with the I420 (AKA "YUV420P") format. * * @param flags the bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT * "flags" * * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() * and #tjGetErrorCode().) -*/ + */ DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf, int width, int pitch, int height, int pixelFormat, - unsigned char *dstBuf, int pad, int subsamp, + unsigned char *dstBuf, int align, int subsamp, int flags); /** - * Encode an RGB or grayscale image into separate Y, U (Cb), and V (Cr) image - * planes. This function uses the accelerated color conversion routines in the - * underlying codec but does not execute any of the other steps in the JPEG - * compression process. + * Encode a packed-pixel RGB or grayscale image into separate Y, U (Cb), and + * V (Cr) image planes. This function performs color conversion (which is + * accelerated in the libjpeg-turbo implementation) but does not execute any of + * the other steps in the JPEG compression process. * * @param handle a handle to a TurboJPEG compressor or transformer instance * - * @param srcBuf pointer to an image buffer containing RGB or grayscale pixels - * to be encoded + * @param srcBuf pointer to a buffer containing a packed-pixel RGB or grayscale + * source image to be encoded * * @param width width (in pixels) of the source image * - * @param pitch bytes per line in the source image. Normally, this should be - * width * #tjPixelSize[pixelFormat] if the image is unpadded, or - * #TJPAD(width * #tjPixelSize[pixelFormat]) if each line of the image - * is padded to the nearest 32-bit boundary, as is the case for Windows - * bitmaps. You can also be clever and use this parameter to skip lines, etc. + * @param pitch bytes per row in the source image. Normally this should be + * width * #tjPixelSize[pixelFormat], if the image is unpadded, or + * #TJPAD(width * #tjPixelSize[pixelFormat]) if each row of the image + * is padded to the nearest multiple of 4 bytes, as is the case for Windows + * bitmaps. You can also be clever and use this parameter to skip rows, etc. * Setting this parameter to 0 is the equivalent of setting it to * width * #tjPixelSize[pixelFormat]. * @@ -1093,26 +1098,26 @@ DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf, * Refer to @ref YUVnotes "YUV Image Format Notes" for more details. * * @param strides an array of integers, each specifying the number of bytes per - * line in the corresponding plane of the output image. Setting the stride for - * any plane to 0 is the same as setting it to the plane width (see - * @ref YUVnotes "YUV Image Format Notes".) If strides is NULL, then - * the strides for all planes will be set to their respective plane widths. - * You can adjust the strides in order to add an arbitrary amount of line - * padding to each plane or to encode an RGB or grayscale image into a - * subregion of a larger YUV planar image. + * row in the corresponding plane of the YUV image. Setting the stride for any + * plane to 0 is the same as setting it to the plane width (see @ref YUVnotes + * "YUV Image Format Notes".) If `strides` is NULL, then the strides for all + * planes will be set to their respective plane widths. You can adjust the + * strides in order to add an arbitrary amount of row padding to each plane or + * to encode an RGB or grayscale image into a subregion of a larger planar YUV + * image. * * @param subsamp the level of chrominance subsampling to be used when * generating the YUV image (see @ref TJSAMP * "Chrominance subsampling options".) To generate images suitable for X - * Video, subsamp should be set to @ref TJSAMP_420. This produces an - * image compatible with the I420 (AKA "YUV420P") format. + * Video, `subsamp` should be set to @ref TJSAMP_420. This produces an image + * compatible with the I420 (AKA "YUV420P") format. * * @param flags the bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT * "flags" * * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() * and #tjGetErrorCode().) -*/ + */ DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf, int width, int pitch, int height, int pixelFormat, unsigned char **dstPlanes, @@ -1122,9 +1127,9 @@ DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf, /** * Create a TurboJPEG decompressor instance. * - * @return a handle to the newly-created instance, or NULL if an error - * occurred (see #tjGetErrorStr2().) -*/ + * @return a handle to the newly-created instance, or NULL if an error occurred + * (see #tjGetErrorStr2().) + */ DLLEXPORT tjhandle tjInitDecompress(void); @@ -1134,7 +1139,7 @@ DLLEXPORT tjhandle tjInitDecompress(void); * * @param handle a handle to a TurboJPEG decompressor or transformer instance * - * @param jpegBuf pointer to a buffer containing a JPEG image or an + * @param jpegBuf pointer to a byte buffer containing a JPEG image or an * "abbreviated table specification" (AKA "tables-only") datastream. Passing a * tables-only datastream to this function primes the decompressor with * quantization and Huffman tables that can be used when decompressing @@ -1145,26 +1150,26 @@ DLLEXPORT tjhandle tjInitDecompress(void); * @param jpegSize size of the JPEG image or tables-only datastream (in bytes) * * @param width pointer to an integer variable that will receive the width (in - * pixels) of the JPEG image. If jpegBuf points to a tables-only - * datastream, then width is ignored. + * pixels) of the JPEG image. If `jpegBuf` points to a tables-only datastream, + * then `width` is ignored. * * @param height pointer to an integer variable that will receive the height - * (in pixels) of the JPEG image. If jpegBuf points to a tables-only - * datastream, then height is ignored. + * (in pixels) of the JPEG image. If `jpegBuf` points to a tables-only + * datastream, then `height` is ignored. * * @param jpegSubsamp pointer to an integer variable that will receive the * level of chrominance subsampling used when the JPEG image was compressed - * (see @ref TJSAMP "Chrominance subsampling options".) If jpegBuf - * points to a tables-only datastream, then jpegSubsamp is ignored. + * (see @ref TJSAMP "Chrominance subsampling options".) If `jpegBuf` points to + * a tables-only datastream, then `jpegSubsamp` is ignored. * * @param jpegColorspace pointer to an integer variable that will receive one * of the JPEG colorspace constants, indicating the colorspace of the JPEG - * image (see @ref TJCS "JPEG colorspaces".) If jpegBuf - * points to a tables-only datastream, then jpegColorspace is ignored. + * image (see @ref TJCS "JPEG colorspaces".) If `jpegBuf` points to a + * tables-only datastream, then `jpegColorspace` is ignored. * * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() * and #tjGetErrorCode().) -*/ + */ DLLEXPORT int tjDecompressHeader3(tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, int *width, @@ -1173,58 +1178,60 @@ DLLEXPORT int tjDecompressHeader3(tjhandle handle, /** - * Returns a list of fractional scaling factors that the JPEG decompressor in - * this implementation of TurboJPEG supports. + * Returns a list of fractional scaling factors that the JPEG decompressor + * supports. * - * @param numscalingfactors pointer to an integer variable that will receive + * @param numScalingFactors pointer to an integer variable that will receive * the number of elements in the list * * @return a pointer to a list of fractional scaling factors, or NULL if an * error is encountered (see #tjGetErrorStr2().) -*/ -DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numscalingfactors); + */ +DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numScalingFactors); /** - * Decompress a JPEG image to an RGB, grayscale, or CMYK image. + * Decompress a JPEG image into a packed-pixel RGB, grayscale, or CMYK image. * * @param handle a handle to a TurboJPEG decompressor or transformer instance * - * @param jpegBuf pointer to a buffer containing the JPEG image to decompress + * @param jpegBuf pointer to a byte buffer containing the JPEG image to + * decompress * * @param jpegSize size of the JPEG image (in bytes) * - * @param dstBuf pointer to an image buffer that will receive the decompressed - * image. This buffer should normally be pitch * scaledHeight bytes - * in size, where scaledHeight can be determined by calling - * #TJSCALED() with the JPEG image height and one of the scaling factors - * returned by #tjGetScalingFactors(). The dstBuf pointer may also be - * used to decompress into a specific region of a larger buffer. + * @param dstBuf pointer to a buffer that will receive the packed-pixel + * decompressed image. This buffer should normally be `pitch * scaledHeight` + * bytes in size, where `scaledHeight` can be determined by calling #TJSCALED() + * with the JPEG image height and one of the scaling factors returned by + * #tjGetScalingFactors(). The `dstBuf` pointer may also be used to decompress + * into a specific region of a larger buffer. * * @param width desired width (in pixels) of the destination image. If this is * different than the width of the JPEG image being decompressed, then * TurboJPEG will use scaling in the JPEG decompressor to generate the largest - * possible image that will fit within the desired width. If width is - * set to 0, then only the height will be considered when determining the - * scaled image size. - * - * @param pitch bytes per line in the destination image. Normally, this is - * scaledWidth * #tjPixelSize[pixelFormat] if the decompressed image - * is unpadded, else #TJPAD(scaledWidth * #tjPixelSize[pixelFormat]) - * if each line of the decompressed image is padded to the nearest 32-bit - * boundary, as is the case for Windows bitmaps. (NOTE: scaledWidth - * can be determined by calling #TJSCALED() with the JPEG image width and one - * of the scaling factors returned by #tjGetScalingFactors().) You can also be - * clever and use the pitch parameter to skip lines, etc. Setting this - * parameter to 0 is the equivalent of setting it to + * possible image that will fit within the desired width. If `width` is set to + * 0, then only the height will be considered when determining the scaled image + * size. + * + * @param pitch bytes per row in the destination image. Normally this should + * be set to scaledWidth * #tjPixelSize[pixelFormat], if the + * destination image should be unpadded, or + * #TJPAD(scaledWidth * #tjPixelSize[pixelFormat]) if each row of the + * destination image should be padded to the nearest multiple of 4 bytes, as is + * the case for Windows bitmaps. (NOTE: `scaledWidth` can be determined by + * calling #TJSCALED() with the JPEG image width and one of the scaling factors + * returned by #tjGetScalingFactors().) You can also be clever and use the + * pitch parameter to skip rows, etc. Setting this parameter to 0 is the + * equivalent of setting it to * scaledWidth * #tjPixelSize[pixelFormat]. * * @param height desired height (in pixels) of the destination image. If this * is different than the height of the JPEG image being decompressed, then * TurboJPEG will use scaling in the JPEG decompressor to generate the largest - * possible image that will fit within the desired height. If height - * is set to 0, then only the width will be considered when determining the - * scaled image size. + * possible image that will fit within the desired height. If `height` is set + * to 0, then only the width will be considered when determining the scaled + * image size. * * @param pixelFormat pixel format of the destination image (see @ref * TJPF "Pixel formats".) @@ -1242,44 +1249,45 @@ DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf, /** - * Decompress a JPEG image to a YUV planar image. This function performs JPEG - * decompression but leaves out the color conversion step, so a planar YUV - * image is generated instead of an RGB image. + * Decompress a JPEG image into a unified planar YUV image. This function + * performs JPEG decompression but leaves out the color conversion step, so a + * planar YUV image is generated instead of a packed-pixel image. * * @param handle a handle to a TurboJPEG decompressor or transformer instance * - * @param jpegBuf pointer to a buffer containing the JPEG image to decompress + * @param jpegBuf pointer to a byte buffer containing the JPEG image to + * decompress * * @param jpegSize size of the JPEG image (in bytes) * - * @param dstBuf pointer to an image buffer that will receive the YUV image. - * Use #tjBufSizeYUV2() to determine the appropriate size for this buffer based - * on the image width, height, padding, and level of subsampling. The Y, - * U (Cb), and V (Cr) image planes will be stored sequentially in the buffer - * (refer to @ref YUVnotes "YUV Image Format Notes".) + * @param dstBuf pointer to a buffer that will receive the unified planar YUV + * decompressed image. Use #tjBufSizeYUV2() to determine the appropriate size + * for this buffer based on the scaled image width, scaled image height, row + * alignment, and level of chrominance subsampling. The Y, U (Cb), and V (Cr) + * image planes will be stored sequentially in the buffer. (Refer to + * @ref YUVnotes "YUV Image Format Notes".) * * @param width desired width (in pixels) of the YUV image. If this is * different than the width of the JPEG image being decompressed, then * TurboJPEG will use scaling in the JPEG decompressor to generate the largest - * possible image that will fit within the desired width. If width is - * set to 0, then only the height will be considered when determining the - * scaled image size. If the scaled width is not an even multiple of the MCU - * block width (see #tjMCUWidth), then an intermediate buffer copy will be - * performed within TurboJPEG. + * possible image that will fit within the desired width. If `width` is set to + * 0, then only the height will be considered when determining the scaled image + * size. If the scaled width is not an even multiple of the MCU block width + * (see #tjMCUWidth), then an intermediate buffer copy will be performed. * - * @param pad the width of each line in each plane of the YUV image will be - * padded to the nearest multiple of this number of bytes (must be a power of - * 2.) To generate images suitable for X Video, pad should be set to - * 4. + * @param align row alignment (in bytes) of the YUV image (must be a power of + * 2.) Setting this parameter to n will cause each row in each plane of the + * YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.) + * To generate images suitable for X Video, `align` should be set to 4. * * @param height desired height (in pixels) of the YUV image. If this is * different than the height of the JPEG image being decompressed, then * TurboJPEG will use scaling in the JPEG decompressor to generate the largest - * possible image that will fit within the desired height. If height - * is set to 0, then only the width will be considered when determining the - * scaled image size. If the scaled height is not an even multiple of the MCU - * block height (see #tjMCUHeight), then an intermediate buffer copy will be - * performed within TurboJPEG. + * possible image that will fit within the desired height. If `height` is set + * to 0, then only the width will be considered when determining the scaled + * image size. If the scaled height is not an even multiple of the MCU block + * height (see #tjMCUHeight), then an intermediate buffer copy will be + * performed. * * @param flags the bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT * "flags" @@ -1289,54 +1297,55 @@ DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf, */ DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf, - int width, int pad, int height, int flags); + int width, int align, int height, int flags); /** * Decompress a JPEG image into separate Y, U (Cb), and V (Cr) image * planes. This function performs JPEG decompression but leaves out the color - * conversion step, so a planar YUV image is generated instead of an RGB image. + * conversion step, so a planar YUV image is generated instead of a + * packed-pixel image. * * @param handle a handle to a TurboJPEG decompressor or transformer instance * - * @param jpegBuf pointer to a buffer containing the JPEG image to decompress + * @param jpegBuf pointer to a byte buffer containing the JPEG image to + * decompress * * @param jpegSize size of the JPEG image (in bytes) * * @param dstPlanes an array of pointers to Y, U (Cb), and V (Cr) image planes * (or just a Y plane, if decompressing a grayscale image) that will receive - * the YUV image. These planes can be contiguous or non-contiguous in memory. - * Use #tjPlaneSizeYUV() to determine the appropriate size for each plane based - * on the scaled image width, scaled image height, strides, and level of - * chrominance subsampling. Refer to @ref YUVnotes "YUV Image Format Notes" - * for more details. + * the decompressed image. These planes can be contiguous or non-contiguous in + * memory. Use #tjPlaneSizeYUV() to determine the appropriate size for each + * plane based on the scaled image width, scaled image height, strides, and + * level of chrominance subsampling. Refer to @ref YUVnotes + * "YUV Image Format Notes" for more details. * * @param width desired width (in pixels) of the YUV image. If this is * different than the width of the JPEG image being decompressed, then * TurboJPEG will use scaling in the JPEG decompressor to generate the largest - * possible image that will fit within the desired width. If width is - * set to 0, then only the height will be considered when determining the - * scaled image size. If the scaled width is not an even multiple of the MCU - * block width (see #tjMCUWidth), then an intermediate buffer copy will be - * performed within TurboJPEG. + * possible image that will fit within the desired width. If `width` is set to + * 0, then only the height will be considered when determining the scaled image + * size. If the scaled width is not an even multiple of the MCU block width + * (see #tjMCUWidth), then an intermediate buffer copy will be performed. * * @param strides an array of integers, each specifying the number of bytes per - * line in the corresponding plane of the output image. Setting the stride for - * any plane to 0 is the same as setting it to the scaled plane width (see - * @ref YUVnotes "YUV Image Format Notes".) If strides is NULL, then - * the strides for all planes will be set to their respective scaled plane - * widths. You can adjust the strides in order to add an arbitrary amount of - * line padding to each plane or to decompress the JPEG image into a subregion - * of a larger YUV planar image. + * row in the corresponding plane of the YUV image. Setting the stride for any + * plane to 0 is the same as setting it to the scaled plane width (see + * @ref YUVnotes "YUV Image Format Notes".) If `strides` is NULL, then the + * strides for all planes will be set to their respective scaled plane widths. + * You can adjust the strides in order to add an arbitrary amount of row + * padding to each plane or to decompress the JPEG image into a subregion of a + * larger planar YUV image. * * @param height desired height (in pixels) of the YUV image. If this is * different than the height of the JPEG image being decompressed, then * TurboJPEG will use scaling in the JPEG decompressor to generate the largest - * possible image that will fit within the desired height. If height - * is set to 0, then only the width will be considered when determining the - * scaled image size. If the scaled height is not an even multiple of the MCU - * block height (see #tjMCUHeight), then an intermediate buffer copy will be - * performed within TurboJPEG. + * possible image that will fit within the desired height. If `height` is set + * to 0, then only the width will be considered when determining the scaled + * image size. If the scaled height is not an even multiple of the MCU block + * height (see #tjMCUHeight), then an intermediate buffer copy will be + * performed. * * @param flags the bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT * "flags" @@ -1352,40 +1361,42 @@ DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle, /** - * Decode a YUV planar image into an RGB or grayscale image. This function - * uses the accelerated color conversion routines in the underlying - * codec but does not execute any of the other steps in the JPEG decompression - * process. + * Decode a unified planar YUV image into a packed-pixel RGB or grayscale + * image. This function performs color conversion (which is accelerated in the + * libjpeg-turbo implementation) but does not execute any of the other steps in + * the JPEG decompression process. * * @param handle a handle to a TurboJPEG decompressor or transformer instance * - * @param srcBuf pointer to an image buffer containing a YUV planar image to be - * decoded. The size of this buffer should match the value returned by - * #tjBufSizeYUV2() for the given image width, height, padding, and level of - * chrominance subsampling. The Y, U (Cb), and V (Cr) image planes should be - * stored sequentially in the source buffer (refer to @ref YUVnotes - * "YUV Image Format Notes".) + * @param srcBuf pointer to a buffer containing a unified planar YUV source + * image to be decoded. The size of this buffer should match the value + * returned by #tjBufSizeYUV2() for the given image width, height, row + * alignment, and level of chrominance subsampling. The Y, U (Cb), and V (Cr) + * image planes should be stored sequentially in the source buffer. (Refer to + * @ref YUVnotes "YUV Image Format Notes".) * - * @param pad Use this parameter to specify that the width of each line in each - * plane of the YUV source image is padded to the nearest multiple of this - * number of bytes (must be a power of 2.) + * @param align row alignment (in bytes) of the YUV source image (must be a + * power of 2.) Setting this parameter to n indicates that each row in each + * plane of the YUV source image is padded to the nearest multiple of n bytes + * (1 = unpadded.) * * @param subsamp the level of chrominance subsampling used in the YUV source * image (see @ref TJSAMP "Chrominance subsampling options".) * - * @param dstBuf pointer to an image buffer that will receive the decoded - * image. This buffer should normally be pitch * height bytes in - * size, but the dstBuf pointer can also be used to decode into a - * specific region of a larger buffer. + * @param dstBuf pointer to a buffer that will receive the packed-pixel decoded + * image. This buffer should normally be `pitch * height` bytes in size, but + * the `dstBuf` pointer can also be used to decode into a specific region of a + * larger buffer. * * @param width width (in pixels) of the source and destination images * - * @param pitch bytes per line in the destination image. Normally, this should - * be width * #tjPixelSize[pixelFormat] if the destination image is - * unpadded, or #TJPAD(width * #tjPixelSize[pixelFormat]) if each line - * of the destination image should be padded to the nearest 32-bit boundary, as - * is the case for Windows bitmaps. You can also be clever and use the pitch - * parameter to skip lines, etc. Setting this parameter to 0 is the equivalent + * @param pitch bytes per row in the destination image. Normally this should + * be set to width * #tjPixelSize[pixelFormat], if the destination + * image should be unpadded, or + * #TJPAD(width * #tjPixelSize[pixelFormat]) if each row of the + * destination image should be padded to the nearest multiple of 4 bytes, as is + * the case for Windows bitmaps. You can also be clever and use the pitch + * parameter to skip rows, etc. Setting this parameter to 0 is the equivalent * of setting it to width * #tjPixelSize[pixelFormat]. * * @param height height (in pixels) of the source and destination images @@ -1400,16 +1411,16 @@ DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle, * and #tjGetErrorCode().) */ DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf, - int pad, int subsamp, unsigned char *dstBuf, + int align, int subsamp, unsigned char *dstBuf, int width, int pitch, int height, int pixelFormat, int flags); /** - * Decode a set of Y, U (Cb), and V (Cr) image planes into an RGB or grayscale - * image. This function uses the accelerated color conversion routines in the - * underlying codec but does not execute any of the other steps in the JPEG - * decompression process. + * Decode a set of Y, U (Cb), and V (Cr) image planes into a packed-pixel RGB + * or grayscale image. This function performs color conversion (which is + * accelerated in the libjpeg-turbo implementation) but does not execute any of + * the other steps in the JPEG decompression process. * * @param handle a handle to a TurboJPEG decompressor or transformer instance * @@ -1422,29 +1433,30 @@ DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf, * details. * * @param strides an array of integers, each specifying the number of bytes per - * line in the corresponding plane of the YUV source image. Setting the stride + * row in the corresponding plane of the YUV source image. Setting the stride * for any plane to 0 is the same as setting it to the plane width (see - * @ref YUVnotes "YUV Image Format Notes".) If strides is NULL, then - * the strides for all planes will be set to their respective plane widths. - * You can adjust the strides in order to specify an arbitrary amount of line - * padding in each plane or to decode a subregion of a larger YUV planar image. + * @ref YUVnotes "YUV Image Format Notes".) If `strides` is NULL, then the + * strides for all planes will be set to their respective plane widths. You + * can adjust the strides in order to specify an arbitrary amount of row + * padding in each plane or to decode a subregion of a larger planar YUV image. * * @param subsamp the level of chrominance subsampling used in the YUV source * image (see @ref TJSAMP "Chrominance subsampling options".) * - * @param dstBuf pointer to an image buffer that will receive the decoded - * image. This buffer should normally be pitch * height bytes in - * size, but the dstBuf pointer can also be used to decode into a - * specific region of a larger buffer. + * @param dstBuf pointer to a buffer that will receive the packed-pixel decoded + * image. This buffer should normally be `pitch * height` bytes in size, but + * the `dstBuf` pointer can also be used to decode into a specific region of a + * larger buffer. * * @param width width (in pixels) of the source and destination images * - * @param pitch bytes per line in the destination image. Normally, this should - * be width * #tjPixelSize[pixelFormat] if the destination image is - * unpadded, or #TJPAD(width * #tjPixelSize[pixelFormat]) if each line - * of the destination image should be padded to the nearest 32-bit boundary, as - * is the case for Windows bitmaps. You can also be clever and use the pitch - * parameter to skip lines, etc. Setting this parameter to 0 is the equivalent + * @param pitch bytes per row in the destination image. Normally this should + * be set to width * #tjPixelSize[pixelFormat], if the destination + * image should be unpadded, or + * #TJPAD(width * #tjPixelSize[pixelFormat]) if each row of the + * destination image should be padded to the nearest multiple of 4 bytes, as is + * the case for Windows bitmaps. You can also be clever and use the pitch + * parameter to skip rows, etc. Setting this parameter to 0 is the equivalent * of setting it to width * #tjPixelSize[pixelFormat]. * * @param height height (in pixels) of the source and destination images @@ -1483,50 +1495,51 @@ DLLEXPORT tjhandle tjInitTransform(void); * transform requires reading and performing Huffman decoding on all of the * coefficients in the source image, regardless of the size of the destination * image. Thus, this function provides a means of generating multiple - * transformed images from the same source or applying multiple - * transformations simultaneously, in order to eliminate the need to read the - * source coefficients multiple times. + * transformed images from the same source or applying multiple transformations + * simultaneously, in order to eliminate the need to read the source + * coefficients multiple times. * * @param handle a handle to a TurboJPEG transformer instance * - * @param jpegBuf pointer to a buffer containing the JPEG source image to + * @param jpegBuf pointer to a byte buffer containing the JPEG source image to * transform * * @param jpegSize size of the JPEG source image (in bytes) * * @param n the number of transformed JPEG images to generate * - * @param dstBufs pointer to an array of n image buffers. dstBufs[i] - * will receive a JPEG image that has been transformed using the parameters in - * transforms[i]. TurboJPEG has the ability to reallocate the JPEG - * buffer to accommodate the size of the JPEG image. Thus, you can choose to: - * -# pre-allocate the JPEG buffer with an arbitrary size using #tjAlloc() and - * let TurboJPEG grow the buffer as needed, - * -# set dstBufs[i] to NULL to tell TurboJPEG to allocate the buffer - * for you, or + * @param dstBufs pointer to an array of n byte buffers. `dstBufs[i]` will + * receive a JPEG image that has been transformed using the parameters in + * `transforms[i]`. TurboJPEG has the ability to reallocate the JPEG + * destination buffer to accommodate the size of the transformed JPEG image. + * Thus, you can choose to: + * -# pre-allocate the JPEG destination buffer with an arbitrary size using + * #tjAlloc() and let TurboJPEG grow the buffer as needed, + * -# set `dstBufs[i]` to NULL to tell TurboJPEG to allocate the buffer for + * you, or * -# pre-allocate the buffer to a "worst case" size determined by calling * #tjBufSize() with the transformed or cropped width and height. Under normal * circumstances, this should ensure that the buffer never has to be - * re-allocated (setting #TJFLAG_NOREALLOC guarantees that it won't be.) Note, - * however, that there are some rare cases (such as transforming images with a - * large amount of embedded EXIF or ICC profile data) in which the output image - * will be larger than the worst-case size, and #TJFLAG_NOREALLOC cannot be - * used in those cases. + * re-allocated. (Setting #TJFLAG_NOREALLOC guarantees that it won't be.) + * Note, however, that there are some rare cases (such as transforming images + * with a large amount of embedded EXIF or ICC profile data) in which the + * transformed JPEG image will be larger than the worst-case size, and + * #TJFLAG_NOREALLOC cannot be used in those cases. * . - * If you choose option 1, dstSizes[i] should be set to the size of - * your pre-allocated buffer. In any case, unless you have set - * #TJFLAG_NOREALLOC, you should always check dstBufs[i] upon return - * from this function, as it may have changed. + * If you choose option 1, then `dstSizes[i]` should be set to the size of your + * pre-allocated buffer. In any case, unless you have set #TJFLAG_NOREALLOC, + * you should always check `dstBufs[i]` upon return from this function, as it + * may have changed. * * @param dstSizes pointer to an array of n unsigned long variables that will * receive the actual sizes (in bytes) of each transformed JPEG image. If - * dstBufs[i] points to a pre-allocated buffer, then - * dstSizes[i] should be set to the size of the buffer. Upon return, - * dstSizes[i] will contain the size of the JPEG image (in bytes.) + * `dstBufs[i]` points to a pre-allocated buffer, then `dstSizes[i]` should be + * set to the size of the buffer. Upon return, `dstSizes[i]` will contain the + * size of the transformed JPEG image (in bytes.) * * @param transforms pointer to an array of n #tjtransform structures, each of * which specifies the transform parameters and/or cropping region for the - * corresponding transformed output image. + * corresponding transformed JPEG image. * * @param flags the bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT * "flags" @@ -1552,10 +1565,10 @@ DLLEXPORT int tjDestroy(tjhandle handle); /** - * Allocate an image buffer for use with TurboJPEG. You should always use - * this function to allocate the JPEG destination buffer(s) for the compression - * and transform functions unless you are disabling automatic buffer - * (re)allocation (by setting #TJFLAG_NOREALLOC.) + * Allocate a byte buffer for use with TurboJPEG. You should always use this + * function to allocate the JPEG destination buffer(s) for the compression and + * transform functions unless you are disabling automatic buffer (re)allocation + * (by setting #TJFLAG_NOREALLOC.) * * @param bytes the number of bytes to allocate * @@ -1568,44 +1581,43 @@ DLLEXPORT unsigned char *tjAlloc(int bytes); /** - * Load an uncompressed image from disk into memory. + * Load a packed-pixel image from disk into memory. * - * @param filename name of a file containing an uncompressed image in Windows + * @param filename name of a file containing a packed-pixel image in Windows * BMP or PBMPLUS (PPM/PGM) format * * @param width pointer to an integer variable that will receive the width (in - * pixels) of the uncompressed image + * pixels) of the packed-pixel image * - * @param align row alignment of the image buffer to be returned (must be a - * power of 2.) For instance, setting this parameter to 4 will cause all rows - * in the image buffer to be padded to the nearest 32-bit boundary, and setting - * this parameter to 1 will cause all rows in the image buffer to be unpadded. + * @param align row alignment of the packed-pixel buffer to be returned (must + * be a power of 2.) Setting this parameter to n will cause all rows in the + * buffer to be padded to the nearest multiple of n bytes (1 = unpadded.) * * @param height pointer to an integer variable that will receive the height - * (in pixels) of the uncompressed image + * (in pixels) of the packed-pixel image * * @param pixelFormat pointer to an integer variable that specifies or will - * receive the pixel format of the uncompressed image buffer. The behavior of - * #tjLoadImage() will vary depending on the value of *pixelFormat - * passed to the function: - * - @ref TJPF_UNKNOWN : The uncompressed image buffer returned by the function - * will use the most optimal pixel format for the file type, and - * *pixelFormat will contain the ID of this pixel format upon - * successful return from the function. - * - @ref TJPF_GRAY : Only PGM files and 8-bit BMP files with a grayscale - * colormap can be loaded. + * receive the pixel format of the packed-pixel buffer. The behavior of + * #tjLoadImage() will vary depending on the value of `*pixelFormat` passed to + * the function: + * - @ref TJPF_UNKNOWN : The packed-pixel buffer returned by this function will + * use the most optimal pixel format for the file type, and `*pixelFormat` will + * contain the ID of that pixel format upon successful return from this + * function. + * - @ref TJPF_GRAY : Only PGM files and 8-bit-per-pixel BMP files with a + * grayscale colormap can be loaded. * - @ref TJPF_CMYK : The RGB or grayscale pixels stored in the file will be * converted using a quick & dirty algorithm that is suitable only for testing - * purposes (proper conversion between CMYK and other formats requires a color - * management system.) - * - Other @ref TJPF "pixel formats" : The uncompressed image buffer will use - * the specified pixel format, and pixel format conversion will be performed if + * purposes. (Proper conversion between CMYK and other formats requires a + * color management system.) + * - Other @ref TJPF "pixel formats" : The packed-pixel buffer will use the + * specified pixel format, and pixel format conversion will be performed if * necessary. * * @param flags the bitwise OR of one or more of the @ref TJFLAG_BOTTOMUP * "flags". * - * @return a pointer to a newly-allocated buffer containing the uncompressed + * @return a pointer to a newly-allocated buffer containing the packed-pixel * image, converted to the chosen pixel format and with the chosen row * alignment, or NULL if an error occurred (see #tjGetErrorStr2().) This * buffer should be freed using #tjFree(). @@ -1616,31 +1628,31 @@ DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width, /** - * Save an uncompressed image from memory to disk. + * Save a packed-pixel image from memory to disk. * - * @param filename name of a file to which to save the uncompressed image. - * The image will be stored in Windows BMP or PBMPLUS (PPM/PGM) format, - * depending on the file extension. + * @param filename name of a file to which to save the packed-pixel image. The + * image will be stored in Windows BMP or PBMPLUS (PPM/PGM) format, depending + * on the file extension. * - * @param buffer pointer to an image buffer containing RGB, grayscale, or - * CMYK pixels to be saved + * @param buffer pointer to a buffer containing a packed-pixel RGB, grayscale, + * or CMYK image to be saved * - * @param width width (in pixels) of the uncompressed image + * @param width width (in pixels) of the packed-pixel image * - * @param pitch bytes per line in the image buffer. Setting this parameter to - * 0 is the equivalent of setting it to + * @param pitch bytes per row in the packed-pixel image. Setting this + * parameter to 0 is the equivalent of setting it to * width * #tjPixelSize[pixelFormat]. * - * @param height height (in pixels) of the uncompressed image + * @param height height (in pixels) of the packed-pixel image * - * @param pixelFormat pixel format of the image buffer (see @ref TJPF + * @param pixelFormat pixel format of the packed-pixel image (see @ref TJPF * "Pixel formats".) If this parameter is set to @ref TJPF_GRAY, then the - * image will be stored in PGM or 8-bit (indexed color) BMP format. Otherwise, - * the image will be stored in PPM or 24-bit BMP format. If this parameter - * is set to @ref TJPF_CMYK, then the CMYK pixels will be converted to RGB - * using a quick & dirty algorithm that is suitable only for testing (proper - * conversion between CMYK and other formats requires a color management - * system.) + * image will be stored in PGM or 8-bit-per-pixel (indexed color) BMP format. + * Otherwise, the image will be stored in PPM or 24-bit-per-pixel BMP format. + * If this parameter is set to @ref TJPF_CMYK, then the CMYK pixels will be + * converted to RGB using a quick & dirty algorithm that is suitable only for + * testing purposes. (Proper conversion between CMYK and other formats + * requires a color management system.) * * @param flags the bitwise OR of one or more of the @ref TJFLAG_BOTTOMUP * "flags". @@ -1653,8 +1665,8 @@ DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer, /** - * Free an image buffer previously allocated by TurboJPEG. You should always - * use this function to free JPEG destination buffer(s) that were automatically + * Free a byte buffer previously allocated by TurboJPEG. You should always use + * this function to free JPEG destination buffer(s) that were automatically * (re)allocated by the compression and transform functions or that were * manually allocated using #tjAlloc(). * @@ -1692,14 +1704,10 @@ DLLEXPORT char *tjGetErrorStr2(tjhandle handle); DLLEXPORT int tjGetErrorCode(tjhandle handle); -/* Deprecated functions and macros */ -#define TJFLAG_FORCEMMX 8 -#define TJFLAG_FORCESSE 16 -#define TJFLAG_FORCESSE2 32 -#define TJFLAG_FORCESSE3 128 +/* Backward compatibility functions and macros (nothing to see here) */ +/* TurboJPEG 1.0+ */ -/* Backward compatibility functions and macros (nothing to see here) */ #define NUMSUBOPT TJ_NUMSAMP #define TJ_444 TJSAMP_444 #define TJ_422 TJSAMP_422 @@ -1715,46 +1723,55 @@ DLLEXPORT int tjGetErrorCode(tjhandle handle); #define TJ_ALPHAFIRST 64 #define TJ_FORCESSE3 TJFLAG_FORCESSE3 #define TJ_FASTUPSAMPLE TJFLAG_FASTUPSAMPLE -#define TJ_YUV 512 DLLEXPORT unsigned long TJBUFSIZE(int width, int height); -DLLEXPORT unsigned long TJBUFSIZEYUV(int width, int height, int jpegSubsamp); - -DLLEXPORT unsigned long tjBufSizeYUV(int width, int height, int subsamp); - DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width, int pitch, int height, int pixelSize, unsigned char *dstBuf, unsigned long *compressedSize, int jpegSubsamp, int jpegQual, int flags); -DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width, - int pitch, int height, int pixelSize, - unsigned char *dstBuf, int subsamp, int flags); - -DLLEXPORT int tjEncodeYUV2(tjhandle handle, unsigned char *srcBuf, int width, - int pitch, int height, int pixelFormat, - unsigned char *dstBuf, int subsamp, int flags); +DLLEXPORT int tjDecompress(tjhandle handle, unsigned char *jpegBuf, + unsigned long jpegSize, unsigned char *dstBuf, + int width, int pitch, int height, int pixelSize, + int flags); DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height); +DLLEXPORT char *tjGetErrorStr(void); + +/* TurboJPEG 1.1+ */ + +#define TJ_YUV 512 + +DLLEXPORT unsigned long TJBUFSIZEYUV(int width, int height, int jpegSubsamp); + DLLEXPORT int tjDecompressHeader2(tjhandle handle, unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height, int *jpegSubsamp); -DLLEXPORT int tjDecompress(tjhandle handle, unsigned char *jpegBuf, - unsigned long jpegSize, unsigned char *dstBuf, - int width, int pitch, int height, int pixelSize, - int flags); - DLLEXPORT int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf, int flags); -DLLEXPORT char *tjGetErrorStr(void); +DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width, + int pitch, int height, int pixelSize, + unsigned char *dstBuf, int subsamp, int flags); +/* TurboJPEG 1.2+ */ + +#define TJFLAG_FORCEMMX 8 +#define TJFLAG_FORCESSE 16 +#define TJFLAG_FORCESSE2 32 +#define TJFLAG_FORCESSE3 128 + +DLLEXPORT unsigned long tjBufSizeYUV(int width, int height, int subsamp); + +DLLEXPORT int tjEncodeYUV2(tjhandle handle, unsigned char *srcBuf, int width, + int pitch, int height, int pixelFormat, + unsigned char *dstBuf, int subsamp, int flags); /** * @} From 94a2b953421124a425027ee97d8c858f414fbf7e Mon Sep 17 00:00:00 2001 From: DRC Date: Wed, 11 Jan 2023 15:01:35 -0600 Subject: [PATCH 068/162] tjDecompressToYUV2: Use scaled dims for plane calc The documented behavior of the function is to use decompression scaling to generate the largest possible image that will fit within the desired image dimensions. Thus, if the desired image dimensions are larger than the scaled image dimensions, then tjDecompressToYUV2() should use the scaled image dimensions when computing the plane pointers and strides to pass to tjDecompressToYUVPlanes(). Note that this bug was not previously detected, because tjunittest and tjbench always passed the scaled image dimensions to tjDecompressToYUV2(). --- ChangeLog.md | 7 +++++++ tjunittest.c | 15 +++++++++++---- turbojpeg.c | 2 ++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index e5bfd3dc9..a8b03e8a7 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -34,6 +34,13 @@ Fixed a similar issue in `tjCompressFromYUV()` whereby it generated a corrupt JPEG image in certain cases, rather than throwing an error, if the `align` parameter was not a power of 2. +6. Fixed an issue whereby `tjDecompressToYUV2()`, which is a wrapper for +`tjDecompressToYUVPlanes()`, used the desired YUV image dimensions rather than +the actual scaled image dimensions when computing the plane pointers and +strides to pass to `tjDecompressToYUVPlanes()`. This caused a buffer overrun +and subsequent segfault if the desired image dimensions exceeded the scaled +image dimensions. + 2.1.4 ===== diff --git a/tjunittest.c b/tjunittest.c index dcf1eb244..a9f8483e0 100644 --- a/tjunittest.c +++ b/tjunittest.c @@ -456,8 +456,12 @@ static void _decompTest(tjhandle handle, unsigned char *jpegBuf, if (sf.num != 1 || sf.denom != 1) printf("%d/%d ... ", sf.num, sf.denom); else printf("... "); - TRY_TJ(tjDecompressToYUV2(handle, jpegBuf, jpegSize, yuvBuf, scaledWidth, - yuvAlign, scaledHeight, flags)); + /* We pass scaledWidth + 1 and scaledHeight + 1 to validate that + tjDecompressToYUV2() generates the largest possible scaled image that + fits within the desired dimensions, as documented. */ + TRY_TJ(tjDecompressToYUV2(handle, jpegBuf, jpegSize, yuvBuf, + scaledWidth + 1, yuvAlign, scaledHeight + 1, + flags)); if (checkBufYUV(yuvBuf, scaledWidth, scaledHeight, subsamp, sf)) printf("Passed.\n"); else printf("FAILED!\n"); @@ -473,8 +477,11 @@ static void _decompTest(tjhandle handle, unsigned char *jpegBuf, if (sf.num != 1 || sf.denom != 1) printf("%d/%d ... ", sf.num, sf.denom); else printf("... "); - TRY_TJ(tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, scaledWidth, 0, - scaledHeight, pf, flags)); + /* We pass scaledWidth + 1 and scaledHeight + 1 to validate that + tjDecompress2() generates the largest possible scaled image that fits + within the desired dimensions, as documented. */ + TRY_TJ(tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, scaledWidth + 1, 0, + scaledHeight + 1, pf, flags)); } if (checkBuf(dstBuf, scaledWidth, scaledHeight, pf, subsamp, sf, flags)) diff --git a/turbojpeg.c b/turbojpeg.c index b4672ce22..05cdf4915 100644 --- a/turbojpeg.c +++ b/turbojpeg.c @@ -1879,6 +1879,8 @@ DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf, if (i >= NUMSF) THROW("tjDecompressToYUV2(): Could not scale down to desired image dimensions"); + width = scaledw; height = scaledh; + pw0 = tjPlaneWidth(0, width, jpegSubsamp); ph0 = tjPlaneHeight(0, height, jpegSubsamp); dstPlanes[0] = dstBuf; From d859232da39b7869380731f2392d5f803e08e429 Mon Sep 17 00:00:00 2001 From: DRC Date: Sat, 14 Jan 2023 18:27:37 -0600 Subject: [PATCH 069/162] TurboJPEG: Use 4:4:4 for lossless JPEG buf calcs --- java/TJUnitTest.java | 3 +++ tjunittest.c | 3 +++ turbojpeg-jni.c | 2 ++ turbojpeg.c | 2 ++ 4 files changed, 10 insertions(+) diff --git a/java/TJUnitTest.java b/java/TJUnitTest.java index 2f34c2d57..afba17c01 100644 --- a/java/TJUnitTest.java +++ b/java/TJUnitTest.java @@ -814,6 +814,9 @@ static void doTest(int w, int h, int[] formats, int subsamp, String baseName) int size, quality = 100; byte[] dstBuf; + if (lossless && subsamp != TJ.SAMP_GRAY) + subsamp = TJ.SAMP_444; + dstBuf = new byte[TJ.bufSize(w, h, subsamp)]; try { diff --git a/tjunittest.c b/tjunittest.c index d903b4fd6..8e26edbef 100644 --- a/tjunittest.c +++ b/tjunittest.c @@ -537,6 +537,9 @@ static void doTest(int w, int h, const int *formats, int nformats, int subsamp, unsigned long size = 0; int pfi, pf, i, quality = 100; + if (lossless && subsamp != TJSAMP_GRAY) + subsamp = TJSAMP_444; + if (!alloc) size = tjBufSize(w, h, subsamp); if (size != 0) diff --git a/turbojpeg-jni.c b/turbojpeg-jni.c index f886a8ece..e533c8f79 100644 --- a/turbojpeg-jni.c +++ b/turbojpeg-jni.c @@ -231,6 +231,8 @@ static jint TJCompressor_compress arraySize = (y + height - 1) * actualPitch + (x + width) * tjPixelSize[pf]; if ((*env)->GetArrayLength(env, src) * srcElementSize < arraySize) THROW_ARG("Source buffer is not large enough"); + if (flags & TJFLAG_LOSSLESS && jpegSubsamp != TJSAMP_GRAY) + jpegSubsamp = TJSAMP_444; jpegSize = tjBufSize(width, height, jpegSubsamp); if ((*env)->GetArrayLength(env, dst) < (jsize)jpegSize) THROW_ARG("Destination buffer is not large enough"); diff --git a/turbojpeg.c b/turbojpeg.c index 6d32edcaa..2617869bc 100644 --- a/turbojpeg.c +++ b/turbojpeg.c @@ -746,6 +746,8 @@ DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf, else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1"); else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1"); #endif + if (flags & TJFLAG_LOSSLESS && jpegSubsamp != TJSAMP_GRAY) + jpegSubsamp = TJSAMP_444; if (flags & TJFLAG_NOREALLOC) { alloc = FALSE; *jpegSize = tjBufSize(width, height, jpegSubsamp); From 22a66368521c0a1bc8a33c75cf7f653427faa29c Mon Sep 17 00:00:00 2001 From: DRC Date: Mon, 16 Jan 2023 15:32:49 -0600 Subject: [PATCH 070/162] Java: Don't allow int overflow in buf size methods This is similar to the fix that 2a9e3bd7430cfda1bc812d139e0609c6aca0b884 applied to the C API. We have to apply it separately at the JNI level because the Java API always stores buffer sizes in 32-bit integers, and the C buffer size functions could overflow an int when using 64-bit code. (NOTE: The Java API stores buffer sizes in 32-bit integers because Java itself always uses 32-bit integers for array sizes.) Since Java don't allow no buffer overruns 'round here, this commit doesn't change the ultimate outcome. It just makes the inevitable exception easier to diagnose. --- java/TJUnitTest.java | 38 ++++++++++++++++++++++++++++++++++++++ turbojpeg-jni.c | 26 ++++++++++++++++---------- 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/java/TJUnitTest.java b/java/TJUnitTest.java index 6443c0621..2d509caa1 100644 --- a/java/TJUnitTest.java +++ b/java/TJUnitTest.java @@ -837,6 +837,43 @@ static void doTest(int w, int h, int[] formats, int subsamp, String baseName) if (tjd != null) tjd.close(); } + static void overflowTest() throws Exception { + /* Ensure that the various buffer size methods don't overflow */ + int size = 0; + boolean exception = false; + + try { + exception = false; + size = TJ.bufSize(26755, 26755, TJ.SAMP_444); + } catch (Exception e) { exception = true; } + if (!exception || size != 0) + throw new Exception("TJ.bufSize() overflow"); + try { + exception = false; + size = TJ.bufSizeYUV(37838, 1, 37838, TJ.SAMP_444); + } catch (Exception e) { exception = true; } + if (!exception || size != 0) + throw new Exception("TJ.bufSizeYUV() overflow"); + try { + exception = false; + size = TJ.bufSizeYUV(37837, 3, 37837, TJ.SAMP_444); + } catch (Exception e) { exception = true; } + if (!exception || size != 0) + throw new Exception("TJ.bufSizeYUV() overflow"); + try { + exception = false; + size = TJ.bufSizeYUV(37837, -1, 37837, TJ.SAMP_444); + } catch (Exception e) { exception = true; } + if (!exception || size != 0) + throw new Exception("TJ.bufSizeYUV() overflow"); + try { + exception = false; + size = TJ.planeSizeYUV(0, 65536, 0, 65536, TJ.SAMP_444); + } catch (Exception e) { exception = true; } + if (!exception || size != 0) + throw new Exception("TJ.planeSizeYUV() overflow"); + } + static void bufSizeTest() throws Exception { int w, h, i, subsamp; byte[] srcBuf, dstBuf = null; @@ -912,6 +949,7 @@ else if (argv[i].equalsIgnoreCase("-bi")) { } if (doYUV) FORMATS_4BYTE[4] = -1; + overflowTest(); doTest(35, 39, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_444, testName); doTest(39, 41, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_444, diff --git a/turbojpeg-jni.c b/turbojpeg-jni.c index 15e57bb6c..00f56c3bd 100644 --- a/turbojpeg-jni.c +++ b/turbojpeg-jni.c @@ -132,24 +132,28 @@ static int ProcessSystemProperties(JNIEnv *env) JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSize (JNIEnv *env, jclass cls, jint width, jint height, jint jpegSubsamp) { - jint retval = (jint)tjBufSize(width, height, jpegSubsamp); + unsigned long retval = tjBufSize(width, height, jpegSubsamp); - if (retval == -1) THROW_ARG(tjGetErrorStr()); + if (retval == (unsigned long)-1) THROW_ARG(tjGetErrorStr()); + if (retval > (unsigned long)((unsigned int)-1)) + THROW_ARG("Image is too large"); bailout: - return retval; + return (jint)retval; } /* TurboJPEG 1.4.x: TJ::bufSizeYUV() */ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSizeYUV__IIII (JNIEnv *env, jclass cls, jint width, jint align, jint height, jint subsamp) { - jint retval = (jint)tjBufSizeYUV2(width, align, height, subsamp); + unsigned long retval = tjBufSizeYUV2(width, align, height, subsamp); - if (retval == -1) THROW_ARG(tjGetErrorStr()); + if (retval == (unsigned long)-1) THROW_ARG(tjGetErrorStr()); + if (retval > (unsigned long)((unsigned int)-1)) + THROW_ARG("Image is too large"); bailout: - return retval; + return (jint)retval; } /* TurboJPEG 1.2.x: TJ::bufSizeYUV() */ @@ -166,13 +170,15 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_planeSizeYUV__IIIII (JNIEnv *env, jclass cls, jint componentID, jint width, jint stride, jint height, jint subsamp) { - jint retval = (jint)tjPlaneSizeYUV(componentID, width, stride, height, - subsamp); + unsigned long retval = tjPlaneSizeYUV(componentID, width, stride, height, + subsamp); - if (retval == -1) THROW_ARG(tjGetErrorStr()); + if (retval == (unsigned long)-1) THROW_ARG(tjGetErrorStr()); + if (retval > (unsigned long)((unsigned int)-1)) + THROW_ARG("Image is too large"); bailout: - return retval; + return (jint)retval; } /* TurboJPEG 1.4.x: TJ::planeWidth() */ From 0c0df2d0c7cc20d79cd1fc77e43f36bce86e382f Mon Sep 17 00:00:00 2001 From: DRC Date: Mon, 16 Jan 2023 16:52:46 -0600 Subject: [PATCH 071/162] TJDecompressor.java: "YUV" = "planar YUV" (Oversight from 9a146f0f23b01869e1bf7c478e12b43f83d59c32) --- java/doc/index-all.html | 7 ++++--- .../org/libjpegturbo/turbojpeg/TJDecompressor.html | 14 ++++++++------ .../org/libjpegturbo/turbojpeg/TJDecompressor.java | 7 ++++--- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/java/doc/index-all.html b/java/doc/index-all.html index df8e653f5..02245366a 100644 --- a/java/doc/index-all.html +++ b/java/doc/index-all.html @@ -195,9 +195,10 @@

    D

  • decompress(int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
    Decompress the JPEG source image or decode the YUV source image associated - with this decompressor instance and return a BufferedImage - instance containing the packed-pixel decompressed/decoded image.
    +
    Decompress the JPEG source image or decode the planar YUV source image + associated with this decompressor instance and return a + BufferedImage instance containing the packed-pixel + decompressed/decoded image.
    decompressToYUV(YUVImage, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html b/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html index cc7eb628b..3f0baffb4 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html @@ -281,9 +281,10 @@

    Method Summary

    int desiredHeight, int bufferedImageType, int flags) -
    Decompress the JPEG source image or decode the YUV source image associated - with this decompressor instance and return a BufferedImage - instance containing the packed-pixel decompressed/decoded image.
    +
    Decompress the JPEG source image or decode the planar YUV source image + associated with this decompressor instance and return a + BufferedImage instance containing the packed-pixel + decompressed/decoded image.
    @@ -1160,9 +1161,10 @@

    decompress

    int bufferedImageType, int flags) throws TJException -
    Decompress the JPEG source image or decode the YUV source image associated - with this decompressor instance and return a BufferedImage - instance containing the packed-pixel decompressed/decoded image.
    +
    Decompress the JPEG source image or decode the planar YUV source image + associated with this decompressor instance and return a + BufferedImage instance containing the packed-pixel + decompressed/decoded image.
    Parameters:
    desiredWidth - see decompress(byte[], int, int, int, int, int, int, int) for description
    desiredHeight - see diff --git a/java/org/libjpegturbo/turbojpeg/TJDecompressor.java b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java index e5ee9cf28..c1ec6a7ef 100644 --- a/java/org/libjpegturbo/turbojpeg/TJDecompressor.java +++ b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java @@ -837,9 +837,10 @@ public void decompress(BufferedImage dstImage, int flags) } /** - * Decompress the JPEG source image or decode the YUV source image associated - * with this decompressor instance and return a BufferedImage - * instance containing the packed-pixel decompressed/decoded image. + * Decompress the JPEG source image or decode the planar YUV source image + * associated with this decompressor instance and return a + * BufferedImage instance containing the packed-pixel + * decompressed/decoded image. * * @param desiredWidth see * {@link #decompress(byte[], int, int, int, int, int, int, int)} for From 08cbc233342de72cd6f10885ead2d86071a7185c Mon Sep 17 00:00:00 2001 From: DRC Date: Tue, 17 Jan 2023 11:04:38 -0600 Subject: [PATCH 072/162] 12-bit: Set alpha channel to 4095 rather than 255 --- ChangeLog.md | 4 ++++ jdcolext.c | 14 +++++++------- jdmrgext.c | 20 ++++++++++---------- rdppm.c | 20 ++++++++++---------- 4 files changed, 31 insertions(+), 27 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index a8b03e8a7..0acfc7376 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -41,6 +41,10 @@ strides to pass to `tjDecompressToYUVPlanes()`. This caused a buffer overrun and subsequent segfault if the desired image dimensions exceeded the scaled image dimensions. +7. Fixed an issue whereby, when decompressing a 12-bit-per-component JPEG image +(`-DWITH_12BIT=1`) using an alpha-enabled output color space such as +`JCS_EXT_RGBA`, the alpha channel was set to 255 rather than 4095. + 2.1.4 ===== diff --git a/jdcolext.c b/jdcolext.c index 863c7a2fb..fc7e7b8f0 100644 --- a/jdcolext.c +++ b/jdcolext.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2009, 2011, 2015, D. R. Commander. + * Copyright (C) 2009, 2011, 2015, 2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -62,10 +62,10 @@ ycc_rgb_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, ((int)RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS))]; outptr[RGB_BLUE] = range_limit[y + Cbbtab[cb]]; - /* Set unused byte to 0xFF so it can be interpreted as an opaque */ + /* Set unused byte to MAXJSAMPLE so it can be interpreted as an opaque */ /* alpha channel value */ #ifdef RGB_ALPHA - outptr[RGB_ALPHA] = 0xFF; + outptr[RGB_ALPHA] = MAXJSAMPLE; #endif outptr += RGB_PIXELSIZE; } @@ -94,10 +94,10 @@ gray_rgb_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, outptr = *output_buf++; for (col = 0; col < num_cols; col++) { outptr[RGB_RED] = outptr[RGB_GREEN] = outptr[RGB_BLUE] = inptr[col]; - /* Set unused byte to 0xFF so it can be interpreted as an opaque */ + /* Set unused byte to MAXJSAMPLE so it can be interpreted as an opaque */ /* alpha channel value */ #ifdef RGB_ALPHA - outptr[RGB_ALPHA] = 0xFF; + outptr[RGB_ALPHA] = MAXJSAMPLE; #endif outptr += RGB_PIXELSIZE; } @@ -130,10 +130,10 @@ rgb_rgb_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, outptr[RGB_RED] = inptr0[col]; outptr[RGB_GREEN] = inptr1[col]; outptr[RGB_BLUE] = inptr2[col]; - /* Set unused byte to 0xFF so it can be interpreted as an opaque */ + /* Set unused byte to MAXJSAMPLE so it can be interpreted as an opaque */ /* alpha channel value */ #ifdef RGB_ALPHA - outptr[RGB_ALPHA] = 0xFF; + outptr[RGB_ALPHA] = MAXJSAMPLE; #endif outptr += RGB_PIXELSIZE; } diff --git a/jdmrgext.c b/jdmrgext.c index 9bf4f1a30..038abc75d 100644 --- a/jdmrgext.c +++ b/jdmrgext.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2011, 2015, 2020, D. R. Commander. + * Copyright (C) 2011, 2015, 2020, 2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -57,7 +57,7 @@ h2v1_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, outptr[RGB_GREEN] = range_limit[y + cgreen]; outptr[RGB_BLUE] = range_limit[y + cblue]; #ifdef RGB_ALPHA - outptr[RGB_ALPHA] = 0xFF; + outptr[RGB_ALPHA] = MAXJSAMPLE; #endif outptr += RGB_PIXELSIZE; y = *inptr0++; @@ -65,7 +65,7 @@ h2v1_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, outptr[RGB_GREEN] = range_limit[y + cgreen]; outptr[RGB_BLUE] = range_limit[y + cblue]; #ifdef RGB_ALPHA - outptr[RGB_ALPHA] = 0xFF; + outptr[RGB_ALPHA] = MAXJSAMPLE; #endif outptr += RGB_PIXELSIZE; } @@ -81,7 +81,7 @@ h2v1_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, outptr[RGB_GREEN] = range_limit[y + cgreen]; outptr[RGB_BLUE] = range_limit[y + cblue]; #ifdef RGB_ALPHA - outptr[RGB_ALPHA] = 0xFF; + outptr[RGB_ALPHA] = MAXJSAMPLE; #endif } } @@ -131,7 +131,7 @@ h2v2_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, outptr0[RGB_GREEN] = range_limit[y + cgreen]; outptr0[RGB_BLUE] = range_limit[y + cblue]; #ifdef RGB_ALPHA - outptr0[RGB_ALPHA] = 0xFF; + outptr0[RGB_ALPHA] = MAXJSAMPLE; #endif outptr0 += RGB_PIXELSIZE; y = *inptr00++; @@ -139,7 +139,7 @@ h2v2_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, outptr0[RGB_GREEN] = range_limit[y + cgreen]; outptr0[RGB_BLUE] = range_limit[y + cblue]; #ifdef RGB_ALPHA - outptr0[RGB_ALPHA] = 0xFF; + outptr0[RGB_ALPHA] = MAXJSAMPLE; #endif outptr0 += RGB_PIXELSIZE; y = *inptr01++; @@ -147,7 +147,7 @@ h2v2_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, outptr1[RGB_GREEN] = range_limit[y + cgreen]; outptr1[RGB_BLUE] = range_limit[y + cblue]; #ifdef RGB_ALPHA - outptr1[RGB_ALPHA] = 0xFF; + outptr1[RGB_ALPHA] = MAXJSAMPLE; #endif outptr1 += RGB_PIXELSIZE; y = *inptr01++; @@ -155,7 +155,7 @@ h2v2_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, outptr1[RGB_GREEN] = range_limit[y + cgreen]; outptr1[RGB_BLUE] = range_limit[y + cblue]; #ifdef RGB_ALPHA - outptr1[RGB_ALPHA] = 0xFF; + outptr1[RGB_ALPHA] = MAXJSAMPLE; #endif outptr1 += RGB_PIXELSIZE; } @@ -171,14 +171,14 @@ h2v2_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, outptr0[RGB_GREEN] = range_limit[y + cgreen]; outptr0[RGB_BLUE] = range_limit[y + cblue]; #ifdef RGB_ALPHA - outptr0[RGB_ALPHA] = 0xFF; + outptr0[RGB_ALPHA] = MAXJSAMPLE; #endif y = *inptr01; outptr1[RGB_RED] = range_limit[y + cred]; outptr1[RGB_GREEN] = range_limit[y + cgreen]; outptr1[RGB_BLUE] = range_limit[y + cblue]; #ifdef RGB_ALPHA - outptr1[RGB_ALPHA] = 0xFF; + outptr1[RGB_ALPHA] = MAXJSAMPLE; #endif } } diff --git a/rdppm.c b/rdppm.c index 294749a41..883641d89 100644 --- a/rdppm.c +++ b/rdppm.c @@ -5,7 +5,7 @@ * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 2009 by Bill Allombert, Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2015-2017, 2020-2022, D. R. Commander. + * Copyright (C) 2015-2017, 2020-2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -179,13 +179,13 @@ get_text_gray_rgb_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) if (maxval == MAXJSAMPLE) { if (aindex >= 0) GRAY_RGB_READ_LOOP((JSAMPLE)read_pbm_integer(cinfo, infile, maxval), - ptr[aindex] = 0xFF;) + ptr[aindex] = MAXJSAMPLE;) else GRAY_RGB_READ_LOOP((JSAMPLE)read_pbm_integer(cinfo, infile, maxval), {}) } else { if (aindex >= 0) GRAY_RGB_READ_LOOP(rescale[read_pbm_integer(cinfo, infile, maxval)], - ptr[aindex] = 0xFF;) + ptr[aindex] = MAXJSAMPLE;) else GRAY_RGB_READ_LOOP(rescale[read_pbm_integer(cinfo, infile, maxval)], {}) } @@ -253,13 +253,13 @@ get_text_rgb_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) if (maxval == MAXJSAMPLE) { if (aindex >= 0) RGB_READ_LOOP((JSAMPLE)read_pbm_integer(cinfo, infile, maxval), - ptr[aindex] = 0xFF;) + ptr[aindex] = MAXJSAMPLE;) else RGB_READ_LOOP((JSAMPLE)read_pbm_integer(cinfo, infile, maxval), {}) } else { if (aindex >= 0) RGB_READ_LOOP(rescale[read_pbm_integer(cinfo, infile, maxval)], - ptr[aindex] = 0xFF;) + ptr[aindex] = MAXJSAMPLE;) else RGB_READ_LOOP(rescale[read_pbm_integer(cinfo, infile, maxval)], {}) } @@ -345,12 +345,12 @@ get_gray_rgb_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) bufferptr = source->iobuffer; if (maxval == MAXJSAMPLE) { if (aindex >= 0) - GRAY_RGB_READ_LOOP(*bufferptr++, ptr[aindex] = 0xFF;) + GRAY_RGB_READ_LOOP(*bufferptr++, ptr[aindex] = MAXJSAMPLE;) else GRAY_RGB_READ_LOOP(*bufferptr++, {}) } else { if (aindex >= 0) - GRAY_RGB_READ_LOOP(rescale[UCH(*bufferptr++)], ptr[aindex] = 0xFF;) + GRAY_RGB_READ_LOOP(rescale[UCH(*bufferptr++)], ptr[aindex] = MAXJSAMPLE;) else GRAY_RGB_READ_LOOP(rescale[UCH(*bufferptr++)], {}) } @@ -413,12 +413,12 @@ get_rgb_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) bufferptr = source->iobuffer; if (maxval == MAXJSAMPLE) { if (aindex >= 0) - RGB_READ_LOOP(*bufferptr++, ptr[aindex] = 0xFF;) + RGB_READ_LOOP(*bufferptr++, ptr[aindex] = MAXJSAMPLE;) else RGB_READ_LOOP(*bufferptr++, {}) } else { if (aindex >= 0) - RGB_READ_LOOP(rescale[UCH(*bufferptr++)], ptr[aindex] = 0xFF;) + RGB_READ_LOOP(rescale[UCH(*bufferptr++)], ptr[aindex] = MAXJSAMPLE;) else RGB_READ_LOOP(rescale[UCH(*bufferptr++)], {}) } @@ -543,7 +543,7 @@ get_word_rgb_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) ERREXIT(cinfo, JERR_PPM_OUTOFRANGE); ptr[bindex] = rescale[temp]; if (aindex >= 0) - ptr[aindex] = 0xFF; + ptr[aindex] = MAXJSAMPLE; ptr += ps; } return 1; From 7ed186ed79a9cec68a14e53940bcfcca4229e40d Mon Sep 17 00:00:00 2001 From: DRC Date: Tue, 17 Jan 2023 18:18:27 -0600 Subject: [PATCH 073/162] TJDecompressor.java: Exception message tweak NO_ASSOC_ERROR is specific to JPEG source images, but the decompress() methods can handle YUV source images as well. --- java/org/libjpegturbo/turbojpeg/TJDecompressor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/org/libjpegturbo/turbojpeg/TJDecompressor.java b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java index c1ec6a7ef..1b44f4b58 100644 --- a/java/org/libjpegturbo/turbojpeg/TJDecompressor.java +++ b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java @@ -393,7 +393,7 @@ public void decompress(byte[] dstBuf, int x, int y, int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags) throws TJException { if (jpegBuf == null && yuvImage == null) - throw new IllegalStateException(NO_ASSOC_ERROR); + throw new IllegalStateException("No source image is associated with this instance"); if (dstBuf == null || x < 0 || y < 0 || pitch < 0 || (yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) || pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0) @@ -715,7 +715,7 @@ public void decompress(int[] dstBuf, int x, int y, int desiredWidth, int stride, int desiredHeight, int pixelFormat, int flags) throws TJException { if (jpegBuf == null && yuvImage == null) - throw new IllegalStateException(NO_ASSOC_ERROR); + throw new IllegalStateException("No source image is associated with this instance"); if (dstBuf == null || x < 0 || y < 0 || stride < 0 || (yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) || pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0) From 6a060d8c95f4f5bb53bdf6d18e080e4140403236 Mon Sep 17 00:00:00 2001 From: DRC Date: Tue, 17 Jan 2023 18:32:15 -0600 Subject: [PATCH 074/162] TJCompressor.java: (C) header formatting tweak (apparently borked by one of the previous merge commits) --- java/org/libjpegturbo/turbojpeg/TJCompressor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/org/libjpegturbo/turbojpeg/TJCompressor.java b/java/org/libjpegturbo/turbojpeg/TJCompressor.java index 305a9d13d..1c98a98ad 100644 --- a/java/org/libjpegturbo/turbojpeg/TJCompressor.java +++ b/java/org/libjpegturbo/turbojpeg/TJCompressor.java @@ -1,6 +1,6 @@ /* * Copyright (C)2011-2015, 2018, 2020, 2022-2023 D. R. Commander. - * All Rights Reserved. + * All Rights Reserved. * Copyright (C)2015 Viktor Szathmáry. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without From fb15efe94fe2337b4454944e191b74722a52c0bf Mon Sep 17 00:00:00 2001 From: DRC Date: Wed, 18 Jan 2023 06:44:46 -0600 Subject: [PATCH 075/162] TurboJPEG: More documentation improvements - TJBench/TJUnitTest: Wordsmith command-line output - Java: "decompress operations"="decompression operations" - tjLoadImage(): Error message tweak - Don't mention compression performance in the description of TJXOPT_PROGRESSIVE/TJTransform.OPT_PROGRESSIVE, because the image has already been compressed at that point. (Oversights from 9a146f0f23b01869e1bf7c478e12b43f83d59c32) --- doc/html/group___turbo_j_p_e_g.html | 2 +- java/TJBench.java | 71 ++++++++-------- java/TJUnitTest.java | 6 +- .../turbojpeg/TJDecompressor.html | 6 +- .../libjpegturbo/turbojpeg/TJTransform.html | 3 +- .../turbojpeg/TJDecompressor.java | 6 +- .../libjpegturbo/turbojpeg/TJTransform.java | 3 +- tjbench.c | 83 +++++++++---------- tjunittest.c | 8 +- turbojpeg.c | 2 +- turbojpeg.h | 3 +- 11 files changed, 90 insertions(+), 103 deletions(-) diff --git a/doc/html/group___turbo_j_p_e_g.html b/doc/html/group___turbo_j_p_e_g.html index 73cfcee7c..39fe4d796 100644 --- a/doc/html/group___turbo_j_p_e_g.html +++ b/doc/html/group___turbo_j_p_e_g.html @@ -717,7 +717,7 @@

    This option will enable progressive entropy coding in the JPEG image generated by this particular transform.

    -

    Progressive entropy coding will generally improve compression relative to baseline entropy coding (the default), but it will reduce compression and decompression performance considerably.

    +

    Progressive entropy coding will generally improve compression relative to baseline entropy coding (the default), but it will reduce decompression performance considerably.

    diff --git a/java/TJBench.java b/java/TJBench.java index b3b1a6525..d84053c43 100644 --- a/java/TJBench.java +++ b/java/TJBench.java @@ -525,7 +525,7 @@ static void decompTest(String fileName) throws Exception { if (quiet == 1) { System.out.println("All performance values in Mpixels/sec\n"); - System.out.format("Bitmap JPEG JPEG %s %s Xform Comp Decomp ", + System.out.format("Pixel JPEG JPEG %s %s Xform Comp Decomp ", (doTile ? "Tile " : "Image"), (doTile ? "Tile " : "Image")); if (doYUV) @@ -696,34 +696,29 @@ static void usage() throws Exception { String className = new TJBench().getClass().getName(); System.out.println("\nUSAGE: java " + className); - System.out.println(" [options]\n"); + System.out.println(" [options]\n"); System.out.println(" java " + className); - System.out.println(" [options]\n"); + System.out.println(" [options]\n"); System.out.println("Options:\n"); - System.out.println("-alloc = Dynamically allocate JPEG image buffers"); - System.out.println("-bottomup = Test bottom-up compression/decompression"); - System.out.println("-tile = Test performance of the codec when the image is encoded as separate"); - System.out.println(" tiles of varying sizes."); + System.out.println("-bottomup = Use bottom-up row order for packed-pixel source/destination buffers"); + System.out.println("-tile = Compress/transform the input image into separate JPEG tiles of varying"); + System.out.println(" sizes (useful for measuring JPEG overhead)"); System.out.println("-rgb, -bgr, -rgbx, -bgrx, -xbgr, -xrgb ="); - System.out.println(" Test the specified color conversion path in the codec (default = BGR)"); - System.out.println("-fastupsample = Use the fastest chrominance upsampling algorithm available in"); - System.out.println(" the underlying codec"); - System.out.println("-fastdct = Use the fastest DCT/IDCT algorithms available in the underlying"); - System.out.println(" codec"); - System.out.println("-accuratedct = Use the most accurate DCT/IDCT algorithms available in the"); - System.out.println(" underlying codec"); + System.out.println(" Use the specified pixel format for packed-pixel source/destination buffers"); + System.out.println(" [default = BGR]"); + System.out.println("-fastupsample = Use the fastest chrominance upsampling algorithm available"); + System.out.println("-fastdct = Use the fastest DCT/IDCT algorithm available"); + System.out.println("-accuratedct = Use the most accurate DCT/IDCT algorithm available"); System.out.println("-progressive = Use progressive entropy coding in JPEG images generated by"); - System.out.println(" compression and transform operations."); - System.out.println("-subsamp = When testing JPEG compression, this option specifies the level"); - System.out.println(" of chrominance subsampling to use ( = 444, 422, 440, 420, 411, or"); - System.out.println(" GRAY). The default is to test Grayscale, 4:2:0, 4:2:2, and 4:4:4 in"); - System.out.println(" sequence."); + System.out.println(" compression and transform operations"); + System.out.println("-subsamp = When compressing, use the specified level of chrominance"); + System.out.println(" subsampling ( = 444, 422, 440, 420, 411, or GRAY) [default = test"); + System.out.println(" Grayscale, 4:2:0, 4:2:2, and 4:4:4 in sequence]"); System.out.println("-quiet = Output results in tabular rather than verbose format"); - System.out.println("-yuv = Test YUV encoding/decoding functions"); - System.out.println("-yuvpad

    = If testing YUV encoding/decoding, this specifies the number of"); - System.out.println(" bytes by which each row of each plane in the intermediate YUV image is"); - System.out.println(" evenly divisible (default = 1)"); - System.out.println("-scale M/N = Scale down the width/height of the decompressed JPEG image by a"); + System.out.println("-yuv = Compress from/decompress to intermediate planar YUV images"); + System.out.println("-yuvpad

    = The number of bytes by which each row in each plane of an"); + System.out.println(" intermediate YUV image is evenly divisible [default = 1]"); + System.out.println("-scale M/N = When decompressing, scale the width/height of the JPEG image by a"); System.out.print(" factor of M/N (M/N = "); for (i = 0; i < nsf; i++) { System.out.format("%d/%d", scalingFactors[i].getNum(), @@ -741,24 +736,24 @@ else if (nsf > 2) { } System.out.println(")"); System.out.println("-hflip, -vflip, -transpose, -transverse, -rot90, -rot180, -rot270 ="); - System.out.println(" Perform the corresponding lossless transform prior to"); - System.out.println(" decompression (these options are mutually exclusive)"); - System.out.println("-grayscale = Perform lossless grayscale conversion prior to decompression"); - System.out.println(" test (can be combined with the other transforms above)"); + System.out.println(" Perform the specified lossless transform operation on the input image"); + System.out.println(" prior to decompression (these operations are mutually exclusive)"); + System.out.println("-grayscale = Transform the input image into a grayscale JPEG image prior to"); + System.out.println(" decompression (can be combined with the other transform operations above)"); System.out.println("-copynone = Do not copy any extra markers (including EXIF and ICC profile data)"); - System.out.println(" when transforming the image."); - System.out.println("-benchtime = Run each benchmark for at least seconds (default = 5.0)"); - System.out.println("-warmup = Run each benchmark for seconds (default = 1.0) prior to"); + System.out.println(" when transforming the input image"); + System.out.println("-benchtime = Run each benchmark for at least seconds [default = 5.0]"); + System.out.println("-warmup = Run each benchmark for seconds [default = 1.0] prior to"); System.out.println(" starting the timer, in order to prime the caches and thus improve the"); - System.out.println(" consistency of the results."); + System.out.println(" consistency of the benchmark results"); System.out.println("-componly = Stop after running compression tests. Do not test decompression."); - System.out.println("-nowrite = Do not write reference or output images (improves consistency"); - System.out.println(" of performance measurements.)"); + System.out.println("-nowrite = Do not write reference or output images (improves consistency of"); + System.out.println(" benchmark results)"); System.out.println("-limitscans = Refuse to decompress or transform progressive JPEG images that"); System.out.println(" have an unreasonably large number of scans"); System.out.println("-stoponwarning = Immediately discontinue the current"); - System.out.println(" compression/decompression/transform operation if the underlying codec"); - System.out.println(" throws a warning (non-fatal error)\n"); + System.out.println(" compression/decompression/transform operation if a warning (non-fatal"); + System.out.println(" error) occurs\n"); System.out.println("NOTE: If the quality is specified as a range (e.g. 90-100), a separate"); System.out.println("test will be performed for all quality values in the range.\n"); System.exit(1); @@ -806,7 +801,7 @@ public static void main(String[] argv) { if (argv[i].equalsIgnoreCase("-tile")) { doTile = true; xformOpt |= TJTransform.OPT_CROP; } else if (argv[i].equalsIgnoreCase("-fastupsample")) { - System.out.println("Using fast upsampling code\n"); + System.out.println("Using fastest upsampling algorithm\n"); flags |= TJ.FLAG_FASTUPSAMPLE; } else if (argv[i].equalsIgnoreCase("-fastdct")) { System.out.println("Using fastest DCT/IDCT algorithm\n"); @@ -962,7 +957,7 @@ else if (argv[i].equalsIgnoreCase("-stoponwarning")) if (quiet == 1 && !decompOnly) { System.out.println("All performance values in Mpixels/sec\n"); - System.out.format("Bitmap JPEG JPEG %s %s ", + System.out.format("Pixel JPEG JPEG %s %s ", (doTile ? "Tile " : "Image"), (doTile ? "Tile " : "Image")); if (doYUV) diff --git a/java/TJUnitTest.java b/java/TJUnitTest.java index 2d509caa1..eb342eecd 100644 --- a/java/TJUnitTest.java +++ b/java/TJUnitTest.java @@ -48,10 +48,10 @@ private TJUnitTest() {} static void usage() { System.out.println("\nUSAGE: java " + CLASS_NAME + " [options]\n"); System.out.println("Options:"); - System.out.println("-yuv = test YUV encoding/decoding support"); - System.out.println("-noyuvpad = do not pad each line of each Y, U, and V plane to the nearest"); + System.out.println("-yuv = test YUV encoding/compression/decompression/decoding"); + System.out.println("-noyuvpad = do not pad each row in each Y, U, and V plane to the nearest"); System.out.println(" multiple of 4 bytes"); - System.out.println("-bi = test BufferedImage support\n"); + System.out.println("-bi = test BufferedImage I/O\n"); System.exit(1); } diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html b/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html index 3f0baffb4..22517080a 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html @@ -626,7 +626,7 @@

    setSourceImage

    "tables-only") datastream of length imageSize bytes stored in jpegImage with this decompressor instance. If jpegImage contains a JPEG image, then this image will be used - as the source image for subsequent decompress operations. Passing a + as the source image for subsequent decompression operations. Passing a tables-only datastream to this method primes the decompressor with quantization and Huffman tables that can be used when decompressing subsequent "abbreviated image" datastreams. This is useful, for instance, @@ -662,8 +662,8 @@

    setJPEGImage

    setSourceImage

    public void setSourceImage(YUVImage srcImage)
    Associate the specified planar YUV source image with this decompressor - instance. Subsequent decompress operations will decode this image into a - packed-pixel RGB or grayscale destination image.
    + instance. Subsequent decompression operations will decode this image into + a packed-pixel RGB or grayscale destination image.
    Parameters:
    srcImage - YUVImage instance containing a planar YUV source image to be decoded. This image is not modified.
    diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJTransform.html b/java/doc/org/libjpegturbo/turbojpeg/TJTransform.html index b0141cbda..e528d791d 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJTransform.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJTransform.html @@ -577,8 +577,7 @@

    OPT_PROGRESSIVE

    This option will enable progressive entropy coding in the JPEG image generated by this particular transform. Progressive entropy coding will generally improve compression relative to baseline entropy coding (the - default), but it will reduce compression and decompression performance - considerably.
    + default), but it will reduce decompression performance considerably.
    See Also:
    Constant Field Values
    diff --git a/java/org/libjpegturbo/turbojpeg/TJDecompressor.java b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java index 1b44f4b58..5da72c024 100644 --- a/java/org/libjpegturbo/turbojpeg/TJDecompressor.java +++ b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java @@ -99,7 +99,7 @@ public TJDecompressor(YUVImage yuvImage) throws TJException { * "tables-only") datastream of length imageSize bytes stored in * jpegImage with this decompressor instance. If * jpegImage contains a JPEG image, then this image will be used - * as the source image for subsequent decompress operations. Passing a + * as the source image for subsequent decompression operations. Passing a * tables-only datastream to this method primes the decompressor with * quantization and Huffman tables that can be used when decompressing * subsequent "abbreviated image" datastreams. This is useful, for instance, @@ -134,8 +134,8 @@ public void setJPEGImage(byte[] jpegImage, int imageSize) /** * Associate the specified planar YUV source image with this decompressor - * instance. Subsequent decompress operations will decode this image into a - * packed-pixel RGB or grayscale destination image. + * instance. Subsequent decompression operations will decode this image into + * a packed-pixel RGB or grayscale destination image. * * @param srcImage {@link YUVImage} instance containing a planar YUV source * image to be decoded. This image is not modified. diff --git a/java/org/libjpegturbo/turbojpeg/TJTransform.java b/java/org/libjpegturbo/turbojpeg/TJTransform.java index c51683469..91bcc6bd3 100644 --- a/java/org/libjpegturbo/turbojpeg/TJTransform.java +++ b/java/org/libjpegturbo/turbojpeg/TJTransform.java @@ -130,8 +130,7 @@ public class TJTransform extends Rectangle { * This option will enable progressive entropy coding in the JPEG image * generated by this particular transform. Progressive entropy coding will * generally improve compression relative to baseline entropy coding (the - * default), but it will reduce compression and decompression performance - * considerably. + * default), but it will reduce decompression performance considerably. */ public static final int OPT_PROGRESSIVE = 32; /** diff --git a/tjbench.c b/tjbench.c index d3ed67d23..adb6ef3e6 100644 --- a/tjbench.c +++ b/tjbench.c @@ -273,7 +273,7 @@ static int decomp(unsigned char *srcBuf, unsigned char **jpegBuf, qualStr, sizeStr, ext); if (tjSaveImage(tempStr, dstBuf, scaledw, 0, scaledh, pf, flags) == -1) - THROW_TJG("saving bitmap"); + THROW_TJG("saving output image"); ptr = strrchr(tempStr, '.'); SNPRINTF(ptr, 1024 - (ptr - tempStr), "-err.%s", ext); if (srcBuf && sf.num == 1 && sf.denom == 1) { @@ -305,7 +305,7 @@ static int decomp(unsigned char *srcBuf, unsigned char **jpegBuf, srcBuf[pitch * row + col]); } if (tjSaveImage(tempStr, dstBuf, w, 0, h, pf, flags) == -1) - THROW_TJG("saving bitmap"); + THROW_TJG("saving output image"); } bailout: @@ -568,7 +568,7 @@ static int decompTest(char *fileName) if (quiet == 1) { printf("All performance values in Mpixels/sec\n\n"); - printf("Bitmap JPEG JPEG %s %s Xform Comp Decomp ", + printf("Pixel JPEG JPEG %s %s Xform Comp Decomp ", doTile ? "Tile " : "Image", doTile ? "Tile " : "Image"); if (doYUV) printf("Decode"); printf("\n"); @@ -750,38 +750,33 @@ static void usage(char *progName) int i; printf("USAGE: %s\n", progName); - printf(" [options]\n\n"); + printf(" [options]\n\n"); printf(" %s\n", progName); - printf(" [options]\n\n"); + printf(" [options]\n\n"); printf("Options:\n\n"); - printf("-alloc = Dynamically allocate JPEG image buffers\n"); - printf("-bmp = Generate output images in Windows Bitmap format (default = PPM)\n"); - printf("-bottomup = Test bottom-up compression/decompression\n"); - printf("-tile = Test performance of the codec when the image is encoded as separate\n"); - printf(" tiles of varying sizes.\n"); + printf("-alloc = Dynamically allocate JPEG buffers\n"); + printf("-bmp = Use Windows Bitmap format for output images [default = PPM]\n"); + printf("-bottomup = Use bottom-up row order for packed-pixel source/destination buffers\n"); + printf("-tile = Compress/transform the input image into separate JPEG tiles of varying\n"); + printf(" sizes (useful for measuring JPEG overhead)\n"); printf("-rgb, -bgr, -rgbx, -bgrx, -xbgr, -xrgb =\n"); - printf(" Test the specified color conversion path in the codec (default = BGR)\n"); - printf("-cmyk = Indirectly test YCCK JPEG compression/decompression (the source\n"); - printf(" and destination bitmaps are still RGB. The conversion is done\n"); - printf(" internally prior to compression or after decompression.)\n"); - printf("-fastupsample = Use the fastest chrominance upsampling algorithm available in\n"); - printf(" the underlying codec\n"); - printf("-fastdct = Use the fastest DCT/IDCT algorithms available in the underlying\n"); - printf(" codec\n"); - printf("-accuratedct = Use the most accurate DCT/IDCT algorithms available in the\n"); - printf(" underlying codec\n"); + printf(" Use the specified pixel format for packed-pixel source/destination buffers\n"); + printf(" [default = BGR]\n"); + printf("-cmyk = Indirectly test YCCK JPEG compression/decompression\n"); + printf(" (use the CMYK pixel format for packed-pixel source/destination buffers)\n"); + printf("-fastupsample = Use the fastest chrominance upsampling algorithm available\n"); + printf("-fastdct = Use the fastest DCT/IDCT algorithm available\n"); + printf("-accuratedct = Use the most accurate DCT/IDCT algorithm available\n"); printf("-progressive = Use progressive entropy coding in JPEG images generated by\n"); - printf(" compression and transform operations.\n"); - printf("-subsamp = When testing JPEG compression, this option specifies the level\n"); - printf(" of chrominance subsampling to use ( = 444, 422, 440, 420, 411, or\n"); - printf(" GRAY). The default is to test Grayscale, 4:2:0, 4:2:2, and 4:4:4 in\n"); - printf(" sequence.\n"); + printf(" compression and transform operations\n"); + printf("-subsamp = When compressing, use the specified level of chrominance\n"); + printf(" subsampling ( = 444, 422, 440, 420, 411, or GRAY) [default = test\n"); + printf(" Grayscale, 4:2:0, 4:2:2, and 4:4:4 in sequence]\n"); printf("-quiet = Output results in tabular rather than verbose format\n"); - printf("-yuv = Test YUV encoding/decoding functions\n"); - printf("-yuvpad

    = If testing YUV encoding/decoding, this specifies the number of\n"); - printf(" bytes by which each row of each plane in the intermediate YUV image is\n"); - printf(" evenly divisible (default = 1)\n"); - printf("-scale M/N = Scale down the width/height of the decompressed JPEG image by a\n"); + printf("-yuv = Compress from/decompress to intermediate planar YUV images\n"); + printf("-yuvpad

    = The number of bytes by which each row in each plane of an\n"); + printf(" intermediate YUV image is evenly divisible [default = 1]\n"); + printf("-scale M/N = When decompressing, scale the width/height of the JPEG image by a\n"); printf(" factor of M/N (M/N = "); for (i = 0; i < nsf; i++) { printf("%d/%d", scalingFactors[i].num, scalingFactors[i].denom); @@ -794,24 +789,24 @@ static void usage(char *progName) } printf(")\n"); printf("-hflip, -vflip, -transpose, -transverse, -rot90, -rot180, -rot270 =\n"); - printf(" Perform the corresponding lossless transform prior to\n"); - printf(" decompression (these options are mutually exclusive)\n"); - printf("-grayscale = Perform lossless grayscale conversion prior to decompression\n"); - printf(" test (can be combined with the other transforms above)\n"); + printf(" Perform the specified lossless transform operation on the input image\n"); + printf(" prior to decompression (these operations are mutually exclusive)\n"); + printf("-grayscale = Transform the input image into a grayscale JPEG image prior to\n"); + printf(" decompression (can be combined with the other transform operations above)\n"); printf("-copynone = Do not copy any extra markers (including EXIF and ICC profile data)\n"); - printf(" when transforming the image.\n"); - printf("-benchtime = Run each benchmark for at least seconds (default = 5.0)\n"); - printf("-warmup = Run each benchmark for seconds (default = 1.0) prior to\n"); + printf(" when transforming the input image\n"); + printf("-benchtime = Run each benchmark for at least seconds [default = 5.0]\n"); + printf("-warmup = Run each benchmark for seconds [default = 1.0] prior to\n"); printf(" starting the timer, in order to prime the caches and thus improve the\n"); - printf(" consistency of the results.\n"); + printf(" consistency of the benchmark results\n"); printf("-componly = Stop after running compression tests. Do not test decompression.\n"); printf("-nowrite = Do not write reference or output images (improves consistency of\n"); - printf(" performance measurements.)\n"); + printf(" benchmark results)\n"); printf("-limitscans = Refuse to decompress or transform progressive JPEG images that\n"); printf(" have an unreasonably large number of scans\n"); printf("-stoponwarning = Immediately discontinue the current\n"); - printf(" compression/decompression/transform operation if the underlying codec\n"); - printf(" throws a warning (non-fatal error)\n\n"); + printf(" compression/decompression/transform operation if a warning (non-fatal\n"); + printf(" error) occurs\n\n"); printf("NOTE: If the quality is specified as a range (e.g. 90-100), a separate\n"); printf("test will be performed for all quality values in the range.\n\n"); exit(1); @@ -857,7 +852,7 @@ int main(int argc, char *argv[]) if (!strcasecmp(argv[i], "-tile")) { doTile = 1; xformOpt |= TJXOPT_CROP; } else if (!strcasecmp(argv[i], "-fastupsample")) { - printf("Using fast upsampling code\n\n"); + printf("Using fastest upsampling algorithm\n\n"); flags |= TJFLAG_FASTUPSAMPLE; } else if (!strcasecmp(argv[i], "-fastdct")) { printf("Using fastest DCT/IDCT algorithm\n\n"); @@ -986,14 +981,14 @@ int main(int argc, char *argv[]) if (!decompOnly) { if ((srcBuf = tjLoadImage(argv[1], &w, 1, &h, &pf, flags)) == NULL) - THROW_TJG("loading bitmap"); + THROW_TJG("loading input image"); temp = strrchr(argv[1], '.'); if (temp != NULL) *temp = '\0'; } if (quiet == 1 && !decompOnly) { printf("All performance values in Mpixels/sec\n\n"); - printf("Bitmap JPEG JPEG %s %s ", + printf("Pixel JPEG JPEG %s %s ", doTile ? "Tile " : "Image", doTile ? "Tile " : "Image"); if (doYUV) printf("Encode "); printf("Comp Comp Decomp "); diff --git a/tjunittest.c b/tjunittest.c index a9f8483e0..8801f32d9 100644 --- a/tjunittest.c +++ b/tjunittest.c @@ -55,11 +55,11 @@ static void usage(char *progName) { printf("\nUSAGE: %s [options]\n\n", progName); printf("Options:\n"); - printf("-yuv = test YUV encoding/decoding support\n"); - printf("-noyuvpad = do not pad each line of each Y, U, and V plane to the nearest\n"); + printf("-yuv = test YUV encoding/compression/decompression/decoding\n"); + printf("-noyuvpad = do not pad each row in each Y, U, and V plane to the nearest\n"); printf(" multiple of 4 bytes\n"); - printf("-alloc = test automatic buffer allocation\n"); - printf("-bmp = tjLoadImage()/tjSaveImage() unit test\n\n"); + printf("-alloc = test automatic JPEG buffer allocation\n"); + printf("-bmp = test packed-pixel image I/O\n"); exit(1); } diff --git a/turbojpeg.c b/turbojpeg.c index 05cdf4915..1a089d851 100644 --- a/turbojpeg.c +++ b/turbojpeg.c @@ -2158,7 +2158,7 @@ DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width, invert = (flags & TJFLAG_BOTTOMUP) == 0; } else if (tempc == 'P') { if ((src = jinit_read_ppm(cinfo)) == NULL) - THROWG("tjLoadImage(): Could not initialize bitmap loader"); + THROWG("tjLoadImage(): Could not initialize PPM loader"); invert = (flags & TJFLAG_BOTTOMUP) != 0; } else THROWG("tjLoadImage(): Unsupported file type"); diff --git a/turbojpeg.h b/turbojpeg.h index e6bdf5fb4..1f8756a61 100644 --- a/turbojpeg.h +++ b/turbojpeg.h @@ -549,8 +549,7 @@ enum TJXOP { * This option will enable progressive entropy coding in the JPEG image * generated by this particular transform. Progressive entropy coding will * generally improve compression relative to baseline entropy coding (the - * default), but it will reduce compression and decompression performance - * considerably. + * default), but it will reduce decompression performance considerably. */ #define TJXOPT_PROGRESSIVE 32 /** From 28c2e6077077723bbcd0b7f98cf6260d06b12f9a Mon Sep 17 00:00:00 2001 From: DRC Date: Wed, 18 Jan 2023 15:31:04 -0600 Subject: [PATCH 076/162] TJBench: Strictly check all non-boolean arguments + document that the value of -yuvpad must be a power of 2 (refer to d2608583955f060ae7efa950a94d921fdd387d58) --- java/TJBench.java | 9 +++++++-- tjbench.c | 7 +++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/java/TJBench.java b/java/TJBench.java index d84053c43..a039eaf18 100644 --- a/java/TJBench.java +++ b/java/TJBench.java @@ -717,7 +717,8 @@ static void usage() throws Exception { System.out.println("-quiet = Output results in tabular rather than verbose format"); System.out.println("-yuv = Compress from/decompress to intermediate planar YUV images"); System.out.println("-yuvpad

    = The number of bytes by which each row in each plane of an"); - System.out.println(" intermediate YUV image is evenly divisible [default = 1]"); + System.out.println(" intermediate YUV image is evenly divisible (must be a power of 2)"); + System.out.println(" [default = 1]"); System.out.println("-scale M/N = When decompressing, scale the width/height of the JPEG image by a"); System.out.print(" factor of M/N (M/N = "); for (i = 0; i < nsf; i++) { @@ -907,8 +908,10 @@ else if (argv[i].equalsIgnoreCase("-benchtime") && try { temp = Integer.parseInt(argv[++i]); } catch (NumberFormatException e) {} - if (temp >= 1) + if (temp >= 1 && (temp & (temp - 1)) == 0) yuvAlign = temp; + else + usage(); } else if (argv[i].equalsIgnoreCase("-subsamp") && i < argv.length - 1) { i++; @@ -924,6 +927,8 @@ else if (argv[i].equals("420")) subsamp = TJ.SAMP_420; else if (argv[i].equals("411")) subsamp = TJ.SAMP_411; + else + usage(); } else if (argv[i].equalsIgnoreCase("-componly")) compOnly = true; else if (argv[i].equalsIgnoreCase("-nowrite")) diff --git a/tjbench.c b/tjbench.c index adb6ef3e6..a4458b1fb 100644 --- a/tjbench.c +++ b/tjbench.c @@ -775,7 +775,8 @@ static void usage(char *progName) printf("-quiet = Output results in tabular rather than verbose format\n"); printf("-yuv = Compress from/decompress to intermediate planar YUV images\n"); printf("-yuvpad

    = The number of bytes by which each row in each plane of an\n"); - printf(" intermediate YUV image is evenly divisible [default = 1]\n"); + printf(" intermediate YUV image is evenly divisible (must be a power of 2)\n"); + printf(" [default = 1]\n"); printf("-scale M/N = When decompressing, scale the width/height of the JPEG image by a\n"); printf(" factor of M/N (M/N = "); for (i = 0; i < nsf; i++) { @@ -940,7 +941,8 @@ int main(int argc, char *argv[]) } else if (!strcasecmp(argv[i], "-yuvpad") && i < argc - 1) { int tempi = atoi(argv[++i]); - if (tempi >= 1) yuvAlign = tempi; + if (tempi >= 1 && (tempi & (tempi - 1)) == 0) yuvAlign = tempi; + else usage(argv[0]); } else if (!strcasecmp(argv[i], "-subsamp") && i < argc - 1) { i++; if (toupper(argv[i][0]) == 'G') subsamp = TJSAMP_GRAY; @@ -953,6 +955,7 @@ int main(int argc, char *argv[]) case 440: subsamp = TJSAMP_440; break; case 420: subsamp = TJSAMP_420; break; case 411: subsamp = TJSAMP_411; break; + default: usage(argv[0]); } } } else if (!strcasecmp(argv[i], "-componly")) From b99e7590b092246d7a53bbd1b63ba2efe2e6bbc2 Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 20 Jan 2023 10:50:21 -0600 Subject: [PATCH 077/162] TJBench/Java: Fix parsing of quality ranges --- ChangeLog.md | 3 +++ java/TJBench.java | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 0acfc7376..754226a02 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -45,6 +45,9 @@ image dimensions. (`-DWITH_12BIT=1`) using an alpha-enabled output color space such as `JCS_EXT_RGBA`, the alpha channel was set to 255 rather than 4095. +8. Fixed an issue whereby the Java version of TJBench did not accept a range of +quality values. + 2.1.4 ===== diff --git a/java/TJBench.java b/java/TJBench.java index a039eaf18..6a06d17c7 100644 --- a/java/TJBench.java +++ b/java/TJBench.java @@ -782,18 +782,18 @@ public static void main(String[] argv) { minArg = 2; if (argv.length < minArg) usage(); + String[] quals = argv[1].split("-", 2); try { - minQual = Integer.parseInt(argv[1]); + minQual = Integer.parseInt(quals[0]); } catch (NumberFormatException e) {} if (minQual < 1 || minQual > 100) throw new Exception("Quality must be between 1 and 100."); - int dashIndex = argv[1].indexOf('-'); - if (dashIndex > 0 && argv[1].length() > dashIndex + 1) { + if (quals.length > 1) { try { - maxQual = Integer.parseInt(argv[1].substring(dashIndex + 1)); + maxQual = Integer.parseInt(quals[1]); } catch (NumberFormatException e) {} } - if (maxQual < 1 || maxQual > 100) + if (maxQual < 1 || maxQual > 100 || maxQual < minQual) maxQual = minQual; } From 98a6455875c0ebc8507ea3c9553350a01145e95e Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 20 Jan 2023 13:14:11 -0600 Subject: [PATCH 078/162] TJBench: Set TJ*OPT_PROGRESSIVE with -progressive The documented behavior of the -progressive option is to use progressive entropy coding in JPEG images generated by compression and transform operations. However, setting TJFLAG_PROGRESSIVE was insufficient to accomplish that, because TJBench doesn't enable lossless transformation if xformOpt == 0. --- ChangeLog.md | 4 ++++ java/TJBench.java | 1 + tjbench.c | 1 + 3 files changed, 6 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 754226a02..a547522a1 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -48,6 +48,10 @@ image dimensions. 8. Fixed an issue whereby the Java version of TJBench did not accept a range of quality values. +9. Fixed an issue whereby, when `-progressive` was passed to TJBench, the JPEG +input image was not transformed into a progressive JPEG image prior to +decompression. + 2.1.4 ===== diff --git a/java/TJBench.java b/java/TJBench.java index 6a06d17c7..95e278845 100644 --- a/java/TJBench.java +++ b/java/TJBench.java @@ -813,6 +813,7 @@ public static void main(String[] argv) { } else if (argv[i].equalsIgnoreCase("-progressive")) { System.out.println("Using progressive entropy coding\n"); flags |= TJ.FLAG_PROGRESSIVE; + xformOpt |= TJTransform.OPT_PROGRESSIVE; } else if (argv[i].equalsIgnoreCase("-rgb")) pf = TJ.PF_RGB; else if (argv[i].equalsIgnoreCase("-rgbx")) diff --git a/tjbench.c b/tjbench.c index a4458b1fb..c9e9e852e 100644 --- a/tjbench.c +++ b/tjbench.c @@ -864,6 +864,7 @@ int main(int argc, char *argv[]) } else if (!strcasecmp(argv[i], "-progressive")) { printf("Using progressive entropy coding\n\n"); flags |= TJFLAG_PROGRESSIVE; + xformOpt |= TJXOPT_PROGRESSIVE; } else if (!strcasecmp(argv[i], "-rgb")) pf = TJPF_RGB; else if (!strcasecmp(argv[i], "-rgbx")) From 0738305ec5496ad8799ab572021625024edc3fb3 Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 20 Jan 2023 13:41:25 -0600 Subject: [PATCH 079/162] GitHub: Update to actions/checkout@v3 ... to silence deprecation warning regarding Node.js 12 actions. --- .github/workflows/build.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index da45cb5d9..3b3567d2b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ jobs: run: | echo "BRANCH=${GITHUB_BASE_REF:-${GITHUB_REF#refs/heads/}}" >$GITHUB_ENV - name: Check out code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up build run: | mkdir -p $HOME/src/ljt.nightly @@ -57,7 +57,7 @@ jobs: run: | echo "BRANCH=${GITHUB_BASE_REF:-${GITHUB_REF#refs/heads/}}" >$GITHUB_ENV - name: Check out code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up build run: | brew install yasm md5sha1sum @@ -90,7 +90,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up build run: | sudo apt install -y nasm @@ -113,7 +113,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Build env: CTEST_OUTPUT_ON_FAILURE: 1 @@ -131,7 +131,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up build run: | sudo apt update @@ -156,7 +156,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up build run: | sudo apt -y install nasm @@ -180,7 +180,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Build env: CTEST_OUTPUT_ON_FAILURE: 1 From 2aac545899a70db43b80e5019088e096a2f0b3fc Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 20 Jan 2023 16:02:30 -0600 Subject: [PATCH 080/162] TJExample: Remove "underlying codec" references (Oversight from 9a146f0f23b01869e1bf7c478e12b43f83d59c32) --- java/TJExample.java | 13 +++++-------- tjexample.c | 11 ++++------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/java/TJExample.java b/java/TJExample.java index 785988698..5ff1c522e 100644 --- a/java/TJExample.java +++ b/java/TJExample.java @@ -1,6 +1,6 @@ /* - * Copyright (C)2011-2012, 2014-2015, 2017-2018 D. R. Commander. - * All Rights Reserved. + * Copyright (C)2011-2012, 2014-2015, 2017-2018, 2023 D. R. Commander. + * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -136,14 +136,11 @@ else if (SCALING_FACTORS.length > 2) { System.out.println("-display = Display output image (Output filename need not be specified in this"); System.out.println(" case.)\n"); - System.out.println("-fastupsample = Use the fastest chrominance upsampling algorithm available in"); - System.out.println(" the underlying codec.\n"); + System.out.println("-fastupsample = Use the fastest chrominance upsampling algorithm available\n"); - System.out.println("-fastdct = Use the fastest DCT/IDCT algorithms available in the underlying"); - System.out.println(" codec.\n"); + System.out.println("-fastdct = Use the fastest DCT/IDCT algorithm available\n"); - System.out.println("-accuratedct = Use the most accurate DCT/IDCT algorithms available in the"); - System.out.println(" underlying codec.\n"); + System.out.println("-accuratedct = Use the most accurate DCT/IDCT algorithm available\n"); System.exit(1); } diff --git a/tjexample.c b/tjexample.c index 505c9dd4a..094372535 100644 --- a/tjexample.c +++ b/tjexample.c @@ -1,5 +1,5 @@ /* - * Copyright (C)2011-2012, 2014-2015, 2017, 2019, 2021-2022 + * Copyright (C)2011-2012, 2014-2015, 2017, 2019, 2021-2023 * D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without @@ -149,14 +149,11 @@ static void usage(char *programName) printf("General Options\n"); printf("---------------\n\n"); - printf("-fastupsample = Use the fastest chrominance upsampling algorithm available in\n"); - printf(" the underlying codec.\n\n"); + printf("-fastupsample = Use the fastest chrominance upsampling algorithm available\n\n"); - printf("-fastdct = Use the fastest DCT/IDCT algorithms available in the underlying\n"); - printf(" codec.\n\n"); + printf("-fastdct = Use the fastest DCT/IDCT algorithm available\n\n"); - printf("-accuratedct = Use the most accurate DCT/IDCT algorithms available in the\n"); - printf(" underlying codec.\n\n"); + printf("-accuratedct = Use the most accurate DCT/IDCT algorithm available\n\n"); exit(1); } From af1b4c8df47c02f53124178758dd56bc75fdf005 Mon Sep 17 00:00:00 2001 From: DRC Date: Sat, 21 Jan 2023 18:31:20 -0600 Subject: [PATCH 081/162] TJBench: Unset TJ*OPT_CROP when disabling tiling Otherwise, if the input image is a JPEG image, then an unnecessary lossless transformation will be performed. --- java/TJBench.java | 3 ++- tjbench.c | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/java/TJBench.java b/java/TJBench.java index 95e278845..e95541d5e 100644 --- a/java/TJBench.java +++ b/java/TJBench.java @@ -947,8 +947,9 @@ else if (argv[i].equalsIgnoreCase("-stoponwarning")) if ((sf.getNum() != 1 || sf.getDenom() != 1) && doTile) { System.out.println("Disabling tiled compression/decompression tests, because those tests do not"); - System.out.println("work when scaled decompression is enabled."); + System.out.println("work when scaled decompression is enabled.\n"); doTile = false; + xformOpt &= (~TJTransform.OPT_CROP); } if (!decompOnly) { diff --git a/tjbench.c b/tjbench.c index c9e9e852e..624127f8a 100644 --- a/tjbench.c +++ b/tjbench.c @@ -973,14 +973,14 @@ int main(int argc, char *argv[]) if ((sf.num != 1 || sf.denom != 1) && doTile) { printf("Disabling tiled compression/decompression tests, because those tests do not\n"); - printf("work when scaled decompression is enabled.\n"); - doTile = 0; + printf("work when scaled decompression is enabled.\n\n"); + doTile = 0; xformOpt &= (~TJXOPT_CROP); } if ((flags & TJFLAG_NOREALLOC) == 0 && doTile) { printf("Disabling tiled compression/decompression tests, because those tests do not\n"); printf("work when dynamic JPEG buffer allocation is enabled.\n\n"); - doTile = 0; + doTile = 0; xformOpt &= (~TJXOPT_CROP); } if (!decompOnly) { From edbb7e6d437250d0a249893f3a9d51349081938c Mon Sep 17 00:00:00 2001 From: DRC Date: Mon, 23 Jan 2023 09:32:57 -0600 Subject: [PATCH 082/162] Java doc: TJ.pixelSize --> TJ.getPixelSize() TJ.pixelSize isn't actually a thing. Oops. --- .../org/libjpegturbo/turbojpeg/TJCompressor.html | 16 +++++++++------- .../libjpegturbo/turbojpeg/TJDecompressor.html | 14 ++++++++------ .../org/libjpegturbo/turbojpeg/TJCompressor.java | 16 +++++++++------- .../libjpegturbo/turbojpeg/TJDecompressor.java | 14 ++++++++------ 4 files changed, 34 insertions(+), 26 deletions(-) diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html b/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html index 84242e3fb..440247b3c 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html @@ -487,13 +487,15 @@

    setSourceImage

    the JPEG or YUV image should be compressed/encoded
    y - y offset (in pixels) of the region in the source image from which the JPEG or YUV image should be compressed/encoded
    width - width (in pixels) of the region in the source image from which the JPEG or YUV image should be compressed/encoded
    pitch - bytes per row in the source image. Normally this should be - width * TJ.pixelSize(pixelFormat), if the source image is - unpadded. However, you can use this parameter to, for instance, specify - that the rows in the source image are padded to the nearest multiple of 4 - bytes or to compress/encode a JPEG or YUV image from a region of a larger - source image. You can also be clever and use this parameter to skip rows, - etc. Setting this parameter to 0 is the equivalent of setting it to - width * TJ.pixelSize(pixelFormat).
    height - height (in pixels) of the region in the source image from + width * + TJ.getPixelSize(pixelFormat), + if the source image is unpadded. However, you can use this parameter to, + for instance, specify that the rows in the source image are padded to the + nearest multiple of 4 bytes or to compress/encode a JPEG or YUV image from + a region of a larger source image. You can also be clever and use this + parameter to skip rows, etc. Setting this parameter to 0 is the + equivalent of setting it to width * + TJ.getPixelSize(pixelFormat).
    height - height (in pixels) of the region in the source image from which the JPEG or YUV image should be compressed/encoded
    pixelFormat - pixel format of the source image (one of TJ.PF_*)
    Throws:
    diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html b/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html index 22517080a..77a7ab696 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html @@ -837,17 +837,19 @@

    decompress

    it to the width of the JPEG image. (In other words, the width will not be considered when determining the scaled image size.) This parameter is ignored if the source image is a YUV image.
    pitch - bytes per row in the destination image. Normally this should - be set to scaledWidth * TJ.pixelSize(pixelFormat), if the - destination image will be unpadded. However, you can use this to, for - instance, pad each row of the destination image to the nearest multiple of - 4 bytes or to decompress/decode the source image into a region of a larger - image. NOTE: if the source image is a JPEG image, then + be set to scaledWidth * + TJ.getPixelSize(pixelFormat), + if the destination image will be unpadded. However, you can use this to, + for instance, pad each row of the destination image to the nearest + multiple of 4 bytes or to decompress/decode the source image into a region + of a larger image. NOTE: if the source image is a JPEG image, then scaledWidth can be determined by calling scalingFactor.getScaled(jpegWidth) or by calling getScaledWidth(int, int). If the source image is a YUV image, then scaledWidth is the width of the YUV image. Setting this parameter to 0 is the equivalent of setting it to - scaledWidth * TJ.pixelSize(pixelFormat).
    desiredHeight - If the source image is a JPEG image, then this + scaledWidth * + TJ.getPixelSize(pixelFormat).
    desiredHeight - If the source image is a JPEG image, then this specifies the desired height (in pixels) of the decompressed image (or image region.) If the desired destination image dimensions are different than the source image dimensions, then TurboJPEG will use scaling in the diff --git a/java/org/libjpegturbo/turbojpeg/TJCompressor.java b/java/org/libjpegturbo/turbojpeg/TJCompressor.java index c29d3dc74..d5bbd8263 100644 --- a/java/org/libjpegturbo/turbojpeg/TJCompressor.java +++ b/java/org/libjpegturbo/turbojpeg/TJCompressor.java @@ -127,13 +127,15 @@ public TJCompressor(BufferedImage srcImage, int x, int y, int width, * which the JPEG or YUV image should be compressed/encoded * * @param pitch bytes per row in the source image. Normally this should be - * width * TJ.pixelSize(pixelFormat), if the source image is - * unpadded. However, you can use this parameter to, for instance, specify - * that the rows in the source image are padded to the nearest multiple of 4 - * bytes or to compress/encode a JPEG or YUV image from a region of a larger - * source image. You can also be clever and use this parameter to skip rows, - * etc. Setting this parameter to 0 is the equivalent of setting it to - * width * TJ.pixelSize(pixelFormat). + * width * + * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat), + * if the source image is unpadded. However, you can use this parameter to, + * for instance, specify that the rows in the source image are padded to the + * nearest multiple of 4 bytes or to compress/encode a JPEG or YUV image from + * a region of a larger source image. You can also be clever and use this + * parameter to skip rows, etc. Setting this parameter to 0 is the + * equivalent of setting it to width * + * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat). * * @param height height (in pixels) of the region in the source image from * which the JPEG or YUV image should be compressed/encoded diff --git a/java/org/libjpegturbo/turbojpeg/TJDecompressor.java b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java index 5da72c024..e35f80a91 100644 --- a/java/org/libjpegturbo/turbojpeg/TJDecompressor.java +++ b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java @@ -361,17 +361,19 @@ public int getScaledHeight(int desiredWidth, int desiredHeight) { * ignored if the source image is a YUV image. * * @param pitch bytes per row in the destination image. Normally this should - * be set to scaledWidth * TJ.pixelSize(pixelFormat), if the - * destination image will be unpadded. However, you can use this to, for - * instance, pad each row of the destination image to the nearest multiple of - * 4 bytes or to decompress/decode the source image into a region of a larger - * image. NOTE: if the source image is a JPEG image, then + * be set to scaledWidth * + * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat), + * if the destination image will be unpadded. However, you can use this to, + * for instance, pad each row of the destination image to the nearest + * multiple of 4 bytes or to decompress/decode the source image into a region + * of a larger image. NOTE: if the source image is a JPEG image, then * scaledWidth can be determined by calling * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegWidth) * or by calling {@link #getScaledWidth}. If the source image is a YUV * image, then scaledWidth is the width of the YUV image. * Setting this parameter to 0 is the equivalent of setting it to - * scaledWidth * TJ.pixelSize(pixelFormat). + * scaledWidth * + * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat). * * @param desiredHeight If the source image is a JPEG image, then this * specifies the desired height (in pixels) of the decompressed image (or From 8a1526a442a9eca8813fcf878fa5cfdd4260be83 Mon Sep 17 00:00:00 2001 From: DRC Date: Wed, 25 Jan 2023 09:52:06 -0600 Subject: [PATCH 083/162] tjPlane*(): Guard against int overflow tjPlaneWidth() and tjPlaneHeight() could overflow a signed int and return a negative value if passed a width/height argument of INT_MAX and a subsampling type for which the MCU block size is larger than 8x8. --- tjunittest.c | 10 ++++++++++ turbojpeg.c | 17 +++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/tjunittest.c b/tjunittest.c index 8801f32d9..0082149a1 100644 --- a/tjunittest.c +++ b/tjunittest.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include "tjutil.h" #include "turbojpeg.h" @@ -578,11 +579,16 @@ static void doTest(int w, int h, const int *formats, int nformats, int subsamp, THROW(#function " overflow"); \ } #endif +#define CHECKSIZEINT(function) { \ + if (intsize != -1 || !strcmp(tjGetErrorStr2(NULL), "No error")) \ + THROW(#function " overflow"); \ +} static void overflowTest(void) { /* Ensure that the various buffer size functions don't overflow */ unsigned long size; + int intsize; size = tjBufSize(26755, 26755, TJSAMP_444); CHECKSIZE(tjBufSize()); @@ -600,6 +606,10 @@ static void overflowTest(void) CHECKSIZE(tjBufSizeYUV()); size = tjPlaneSizeYUV(0, 65536, 0, 65536, TJSAMP_444); CHECKSIZE(tjPlaneSizeYUV()); + intsize = tjPlaneWidth(0, INT_MAX, TJSAMP_420); + CHECKSIZEINT(tjPlaneWidth()); + intsize = tjPlaneHeight(0, INT_MAX, TJSAMP_420); + CHECKSIZEINT(tjPlaneHeight()); bailout: return; diff --git a/turbojpeg.c b/turbojpeg.c index 1a089d851..32cb3b560 100644 --- a/turbojpeg.c +++ b/turbojpeg.c @@ -31,6 +31,7 @@ libjpeg-turbo */ #include +#include #include #define JPEG_INTERNALS #include @@ -622,7 +623,8 @@ DLLEXPORT unsigned long TJBUFSIZEYUV(int width, int height, int subsamp) /* TurboJPEG 1.4+ */ DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp) { - int pw, nc, retval = 0; + unsigned long long pw, retval = 0; + int nc; if (width < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP) THROWG("tjPlaneWidth(): Invalid argument"); @@ -636,15 +638,19 @@ DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp) else retval = pw * 8 / tjMCUWidth[subsamp]; + if (retval > (unsigned long long)INT_MAX) + THROWG("tjPlaneWidth(): Width is too large"); + bailout: - return retval; + return (int)retval; } /* TurboJPEG 1.4+ */ DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp) { - int ph, nc, retval = 0; + unsigned long long ph, retval = 0; + int nc; if (height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP) THROWG("tjPlaneHeight(): Invalid argument"); @@ -658,8 +664,11 @@ DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp) else retval = ph * 8 / tjMCUHeight[subsamp]; + if (retval > (unsigned long long)INT_MAX) + THROWG("tjPlaneHeight(): Height is too large"); + bailout: - return retval; + return (int)retval; } From 1485beaa49d720708ae5b605eb6ccba2e284269a Mon Sep 17 00:00:00 2001 From: DRC Date: Wed, 25 Jan 2023 12:13:21 -0600 Subject: [PATCH 084/162] turbojpeg.c: Fix UBSan warning (introduced by previous commit) --- turbojpeg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/turbojpeg.c b/turbojpeg.c index 32cb3b560..b5498dc83 100644 --- a/turbojpeg.c +++ b/turbojpeg.c @@ -632,7 +632,7 @@ DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp) if (componentID < 0 || componentID >= nc) THROWG("tjPlaneWidth(): Invalid argument"); - pw = PAD(width, tjMCUWidth[subsamp] / 8); + pw = PAD((unsigned long long)width, tjMCUWidth[subsamp] / 8); if (componentID == 0) retval = pw; else @@ -658,7 +658,7 @@ DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp) if (componentID < 0 || componentID >= nc) THROWG("tjPlaneHeight(): Invalid argument"); - ph = PAD(height, tjMCUHeight[subsamp] / 8); + ph = PAD((unsigned long long)height, tjMCUHeight[subsamp] / 8); if (componentID == 0) retval = ph; else From 27f4ff80ceddfe5d186c3b1f4f42d2ffb30d0ece Mon Sep 17 00:00:00 2001 From: DRC Date: Wed, 25 Jan 2023 11:09:36 -0600 Subject: [PATCH 085/162] Java: Guard against int overflow in size methods Because Java array sizes are ints, the various size methods in the TJ class have int return values. Thus, we have to guard against signed int overflow at the JNI level, because the C functions can return sizes greater than INT_MAX. This also adds a test for TJ.planeWidth() and TJ.planeHeight(), in order to validate 8a1526a442a9eca8813fcf878fa5cfdd4260be83 in Java. --- java/TJUnitTest.java | 22 +++++++++++++++++----- turbojpeg-jni.c | 7 ++++--- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/java/TJUnitTest.java b/java/TJUnitTest.java index eb342eecd..20de6dfd4 100644 --- a/java/TJUnitTest.java +++ b/java/TJUnitTest.java @@ -844,34 +844,46 @@ static void overflowTest() throws Exception { try { exception = false; - size = TJ.bufSize(26755, 26755, TJ.SAMP_444); + size = TJ.bufSize(18919, 18919, TJ.SAMP_444); } catch (Exception e) { exception = true; } if (!exception || size != 0) throw new Exception("TJ.bufSize() overflow"); try { exception = false; - size = TJ.bufSizeYUV(37838, 1, 37838, TJ.SAMP_444); + size = TJ.bufSizeYUV(26755, 1, 26755, TJ.SAMP_444); } catch (Exception e) { exception = true; } if (!exception || size != 0) throw new Exception("TJ.bufSizeYUV() overflow"); try { exception = false; - size = TJ.bufSizeYUV(37837, 3, 37837, TJ.SAMP_444); + size = TJ.bufSizeYUV(26754, 3, 26754, TJ.SAMP_444); } catch (Exception e) { exception = true; } if (!exception || size != 0) throw new Exception("TJ.bufSizeYUV() overflow"); try { exception = false; - size = TJ.bufSizeYUV(37837, -1, 37837, TJ.SAMP_444); + size = TJ.bufSizeYUV(26754, -1, 26754, TJ.SAMP_444); } catch (Exception e) { exception = true; } if (!exception || size != 0) throw new Exception("TJ.bufSizeYUV() overflow"); try { exception = false; - size = TJ.planeSizeYUV(0, 65536, 0, 65536, TJ.SAMP_444); + size = TJ.planeSizeYUV(0, 46341, 0, 46341, TJ.SAMP_444); } catch (Exception e) { exception = true; } if (!exception || size != 0) throw new Exception("TJ.planeSizeYUV() overflow"); + try { + exception = false; + size = TJ.planeWidth(0, Integer.MAX_VALUE, TJ.SAMP_420); + } catch (Exception e) { exception = true; } + if (!exception || size != 0) + throw new Exception("TJ.planeWidth() overflow"); + try { + exception = false; + size = TJ.planeHeight(0, Integer.MAX_VALUE, TJ.SAMP_420); + } catch (Exception e) { exception = true; } + if (!exception || size != 0) + throw new Exception("TJ.planeHeight() overflow"); } static void bufSizeTest() throws Exception { diff --git a/turbojpeg-jni.c b/turbojpeg-jni.c index 00f56c3bd..446cbd2a6 100644 --- a/turbojpeg-jni.c +++ b/turbojpeg-jni.c @@ -26,6 +26,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include #include "turbojpeg.h" #include "jinclude.h" #include @@ -135,7 +136,7 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSize unsigned long retval = tjBufSize(width, height, jpegSubsamp); if (retval == (unsigned long)-1) THROW_ARG(tjGetErrorStr()); - if (retval > (unsigned long)((unsigned int)-1)) + if (retval > (unsigned long)INT_MAX) THROW_ARG("Image is too large"); bailout: @@ -149,7 +150,7 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSizeYUV__IIII unsigned long retval = tjBufSizeYUV2(width, align, height, subsamp); if (retval == (unsigned long)-1) THROW_ARG(tjGetErrorStr()); - if (retval > (unsigned long)((unsigned int)-1)) + if (retval > (unsigned long)INT_MAX) THROW_ARG("Image is too large"); bailout: @@ -174,7 +175,7 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_planeSizeYUV__IIIII subsamp); if (retval == (unsigned long)-1) THROW_ARG(tjGetErrorStr()); - if (retval > (unsigned long)((unsigned int)-1)) + if (retval > (unsigned long)INT_MAX) THROW_ARG("Image is too large"); bailout: From fc01f4673b71c0b833c59c21e8c4478a9c4bcf21 Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 5 Jan 2023 06:36:46 -0600 Subject: [PATCH 086/162] TurboJPEG 3 API overhaul (ChangeLog update forthcoming) - Prefix all function names with "tj3" and remove version suffixes from function names. (Future API overhauls will increment the prefix to "tj4", etc., thus retaining backward API/ABI compatibility without versioning each individual function.) - Replace stateless boolean flags (including TJ*FLAG_ARITHMETIC and TJ*FLAG_LOSSLESS, which were never released) with stateful integer parameters, the value of which persists between function calls. * Use parameters for the JPEG quality and subsampling as well, in order to eliminate the awkwardness of specifying function arguments that weren't relevant for lossless compression. * tj3DecompressHeader() now stores all relevant information about the JPEG image, including the width, height, subsampling type, entropy coding type, etc. in parameters rather than returning that information in its arguments. * TJ*FLAG_LIMITSCANS has been reimplemented as an integer parameter (TJ*PARAM_SCANLIMIT) that allows the number of scans to be specified. - Use the const keyword for all pointer arguments to unmodified buffers, as well as for both dimensions of 2D pointers. Addresses #395. - Use size_t rather than unsigned long to represent buffer sizes, since unsigned long is a 32-bit type on Windows. Addresses #24. - Return 0 from all buffer size functions if an error occurs, rather than awkwardly trying to return -1 in an unsigned data type. - Implement 12-bit and 16-bit data precision using dedicated compression, decompression, and image I/O functions/methods. * Suffix the names of all data-precision-specific functions with 8, 12, or 16. * Because the YUV functions are intended to be used for video, they are currently only implemented with 8-bit data precision, but they can be expanded to 12-bit data precision in the future, if necessary. * Extend TJUnitTest and TJBench to test 12-bit and 16-bit data precision, using a new -precision option. * Add appropriate regression tests for all of the above to the 'test' target. * Extend tjbenchtest to test 12-bit and 16-bit data precision, and add separate 'tjtest12' and 'tjtest16' targets. * BufferedImage I/O in the Java API is currently limited to 8-bit data precision, since the BufferedImage class does not straightforwardly support higher data precisions. * Extend the PPM reader to convert 12-bit and 16-bit PBMPLUS files to grayscale or CMYK pixels, as it already does for 8-bit files. - Properly accommodate lossless JPEG using dedicated parameters (TJ*PARAM_LOSSLESS, TJ*PARAM_LOSSLESSPSV, and TJ*PARAM_LOSSLESSPT), rather than using a flag and awkwardly repurposing the JPEG quality. Update TJBench to properly reflect whether a JPEG image is lossless. - Re-organize the TJBench usage screen. - Update the Java docs using Java 11, to improve the formatting and eliminate HTML frames. - Use the accurate integer DCT algorithm by default for both compression and decompression, since the "fast" algorithm is a legacy feature, it does not pass the ISO compliance tests, and it is not actually faster on modern x86 CPUs. * Remove the -accuratedct option from TJBench and TJExample. - Re-implement the 'tjtest' target using a CMake script that enables the appropriate tests, depending on the data precision and whether or not the Java API is part of the build. - Consolidate the C and Java versions of tjbenchtest into one script. - Consolidate the C and Java versions of tjexampletest into one script. - Combine all initialization functions into a single function (tj3Init()) that accepts an integer parameter specifying the subsystems to initialize. - Enable decompression scaling explicitly, using a new function/method (tj3SetScalingFactor()/TJDecompressor.setScalingFactor()), rather than implicitly using awkward "desired width"/"desired height" parameters. - Introduce a new macro/constant (TJUNSCALED/TJ.UNSCALED) that maps to a scaling factor of 1/1. - Implement partial image decompression, using a new function/method (tj3SetCroppingRegion()/TJDecompressor.setCroppingRegion()) and TJBench option (-crop). Extend tjbenchtest to test the new feature. Addresses #1. - Allow the JPEG colorspace to be specified explicitly when compressing, using a new parameter (TJ*PARAM_COLORSPACE). This allows JPEG images with the RGB and CMYK colorspaces to be created. - Remove the error/difference image feature from TJBench. Identical images to the ones that TJBench created can be generated using ImageMagick with 'magick composite -compose difference ' - Handle JPEG images with unknown subsampling types. TJ*PARAM_SUBSAMP is set to TJ*SAMP_UNKNOWN (== -1) for such images, but they can still be decompressed fully into packed-pixel images or losslessly transformed (with the exception of lossless cropping.) They cannot be partially decompressed or decompressed into planar YUV images. Note also that TJBench, due to its lack of support for imperfect transforms, requires that the subsampling type be known when rotating, flipping, or transversely transposing an image. Addresses #436 - The Java version of TJBench now has identical functionality to the C version. This was accomplished by (somewhat hackishly) calling the TurboJPEG C image I/O functions through JNI and copying the pixels between the C heap and the Java heap. - Add parameters (TJ*PARAM_RESTARTROWS and TJ*PARAM_RESTARTBLOCKS) and a TJBench option (-restart) to allow the restart marker interval to be specified when compressing. Eliminate the undocumented TJ_RESTART environment variable. - Add a parameter (TJ*PARAM_OPTIMIZE), a transform option (TJ*OPT_OPTIMIZE), and a TJBench option (-optimize) to allow optimized baseline Huffman coding to be specified when compressing. Eliminate the undocumented TJ_OPTIMIZE environment variable. - Add parameters (TJ*PARAM_XDENSITY, TJ*PARAM_DENSITY, and TJ*DENSITYUNITS) to allow the pixel density to be specified when compressing or saving a Windows BMP image and to be queried when decompressing or loading a Windows BMP image. Addresses #77. - Refactor the fuzz targets to use the new API. * Extend decompression coverage to 12-bit and 16-bit data precision. * Replace the awkward cjpeg12 and cjpeg16 targets with proper TurboJPEG-based compress12, compress12-lossless, and compress16-lossless targets - Fix innocuous UBSan warnings uncovered by the new fuzzers. - Implement previous versions of the TurboJPEG API by wrapping the new functions (tested by running the 2.1.x versions of TJBench, via tjbenchtest, and TJUnitTest against the new implementation.) * Remove all JNI functions for deprecated Java methods and implement the deprecated methods using pure Java wrappers. It should be understood that backward API compatibility in Java applies only to the Java classes and that one cannot mix and match a JAR file from one version of libjpeg-turbo with a JNI library from another version. - tj3Destroy() now silently accepts a NULL handle. - tj3Alloc() and tj3Free() now return/accept void pointers, as malloc() and free() do. - The image I/O functions now accept a TurboJPEG instance handle, which is used to transmit/receive parameters and to receive error information. Closes #517 --- CMakeLists.txt | 313 +- cmakescripts/testclean.cmake | 3 + cmakescripts/tjbenchtest.cmake | 67 + doc/html/annotated.html | 2 +- doc/html/classes.html | 2 +- doc/html/functions.html | 2 +- doc/html/functions_vars.html | 2 +- doc/html/group___turbo_j_p_e_g.html | 2351 ++-- doc/html/index.html | 2 +- doc/html/modules.html | 2 +- doc/html/search/all_6.js | 240 +- doc/html/search/all_7.js | 2 +- doc/html/search/all_8.js | 2 +- doc/html/search/all_9.js | 2 +- doc/html/search/classes_0.js | 6 +- doc/html/search/enums_0.js | 12 +- doc/html/search/enumvalues_0.js | 95 +- doc/html/search/functions_0.js | 66 +- doc/html/search/groups_0.js | 2 +- doc/html/search/typedefs_0.js | 4 +- doc/html/search/variables_0.js | 2 +- doc/html/search/variables_1.js | 4 +- doc/html/search/variables_2.js | 2 +- doc/html/search/variables_3.js | 2 +- doc/html/search/variables_4.js | 4 +- doc/html/search/variables_5.js | 2 +- doc/html/search/variables_6.js | 16 +- doc/html/search/variables_7.js | 2 +- doc/html/search/variables_8.js | 2 +- doc/html/search/variables_9.js | 2 +- doc/html/structtjregion.html | 4 +- doc/html/structtjscalingfactor.html | 2 +- doc/html/structtjtransform.html | 4 +- doxygen.config | 2 +- fuzz/CMakeLists.txt | 24 +- fuzz/build.sh | 5 +- fuzz/cjpeg12.cc | 93 - fuzz/cjpeg16.cc | 79 - fuzz/compress.cc | 51 +- fuzz/compress12.cc | 133 + fuzz/compress12_lossless.cc | 131 + fuzz/compress16_lossless.cc | 131 + fuzz/compress_lossless.cc | 47 +- fuzz/compress_yuv.cc | 64 +- fuzz/decompress.cc | 85 +- fuzz/decompress_yuv.cc | 44 +- fuzz/transform.cc | 43 +- java/TJBench.java | 665 +- java/TJExample.java | 40 +- java/TJUnitTest.java | 421 +- java/doc/allclasses-frame.html | 24 - java/doc/allclasses-index.html | 215 + ...llclasses-noframe.html => allclasses.html} | 22 +- java/doc/allpackages-index.html | 162 + java/doc/constant-values.html | 493 +- java/doc/deprecated-list.html | 238 +- java/doc/{package-list => element-list} | 0 java/doc/help-doc.html | 154 +- java/doc/index-all.html | 993 +- java/doc/index.html | 78 +- java/doc/jquery-ui.overrides.css | 35 + java/doc/jquery/external/jquery/jquery.js | 10872 +++++++++++++++ java/doc/jquery/jquery-3.6.0.min.js | 2 + java/doc/jquery/jquery-ui.min.css | 6 + java/doc/jquery/jquery-ui.min.js | 6 + .../jquery/jszip-utils/dist/jszip-utils-ie.js | 56 + .../jszip-utils/dist/jszip-utils-ie.min.js | 10 + .../jquery/jszip-utils/dist/jszip-utils.js | 118 + .../jszip-utils/dist/jszip-utils.min.js | 10 + java/doc/jquery/jszip/dist/jszip.js | 11370 ++++++++++++++++ java/doc/jquery/jszip/dist/jszip.min.js | 13 + java/doc/member-search-index.js | 1 + java/doc/member-search-index.zip | Bin 0 -> 1927 bytes java/doc/org/libjpegturbo/turbojpeg/TJ.html | 1886 ++- .../libjpegturbo/turbojpeg/TJCompressor.html | 1060 +- .../turbojpeg/TJCustomFilter.html | 169 +- .../turbojpeg/TJDecompressor.html | 1881 ++- .../libjpegturbo/turbojpeg/TJException.html | 214 +- .../turbojpeg/TJScalingFactor.html | 241 +- .../libjpegturbo/turbojpeg/TJTransform.html | 526 +- .../libjpegturbo/turbojpeg/TJTransformer.html | 381 +- .../org/libjpegturbo/turbojpeg/YUVImage.html | 631 +- .../libjpegturbo/turbojpeg/package-frame.html | 31 - .../turbojpeg/package-summary.html | 111 +- .../libjpegturbo/turbojpeg/package-tree.html | 123 +- java/doc/overview-tree.html | 125 +- java/doc/package-search-index.js | 1 + java/doc/package-search-index.zip | Bin 0 -> 237 bytes java/doc/resources/background.gif | Bin 2313 -> 0 bytes java/doc/resources/glass.png | Bin 0 -> 499 bytes java/doc/resources/tab.gif | Bin 291 -> 0 bytes java/doc/resources/titlebar.gif | Bin 10701 -> 0 bytes java/doc/resources/titlebar_end.gif | Bin 849 -> 0 bytes java/doc/resources/x.png | Bin 0 -> 394 bytes java/doc/script.js | 131 +- java/doc/search.js | 326 + java/doc/serialized-form.html | 109 +- java/doc/stylesheet.css | 752 +- java/doc/type-search-index.js | 1 + java/doc/type-search-index.zip | Bin 0 -> 311 bytes java/org/libjpegturbo/turbojpeg/TJ.java | 576 +- .../libjpegturbo/turbojpeg/TJCompressor.java | 529 +- .../turbojpeg/TJCustomFilter.java | 4 +- .../turbojpeg/TJDecompressor.java | 1158 +- .../libjpegturbo/turbojpeg/TJTransform.java | 48 +- .../libjpegturbo/turbojpeg/TJTransformer.java | 55 +- java/org/libjpegturbo/turbojpeg/YUVImage.java | 40 +- java/org_libjpegturbo_turbojpeg_TJ.h | 46 + .../org_libjpegturbo_turbojpeg_TJCompressor.h | 80 +- ...rg_libjpegturbo_turbojpeg_TJDecompressor.h | 88 +- ...org_libjpegturbo_turbojpeg_TJTransformer.h | 4 +- jcapimin.c | 5 +- jdatadst-tj.c | 10 +- jdatasrc-tj.c | 8 +- rdppm.c | 107 + testimages/big_building16.ppm | Bin 0 -> 202955 bytes testimages/big_tree8.bmp | Bin 0 -> 82998 bytes .../{nightshot_iso_100.txt => big_tree8.txt} | 4 +- testimages/nightshot_iso_100.bmp | Bin 82998 -> 0 bytes tjbench.c | 768 +- tjbenchtest.in | 206 +- tjbenchtest.java.in | 241 - tjexample.c | 101 +- tjexampletest.in | 57 +- tjexampletest.java.in | 151 - tjunittest.c | 853 +- turbojpeg-jni.c | 840 +- turbojpeg-mapfile | 41 +- turbojpeg-mapfile.jni | 87 +- turbojpeg-mp.c | 493 + turbojpeg.c | 2053 +-- turbojpeg.h | 1692 ++- 132 files changed, 40223 insertions(+), 8982 deletions(-) create mode 100644 cmakescripts/tjbenchtest.cmake delete mode 100644 fuzz/cjpeg12.cc delete mode 100644 fuzz/cjpeg16.cc create mode 100644 fuzz/compress12.cc create mode 100644 fuzz/compress12_lossless.cc create mode 100644 fuzz/compress16_lossless.cc delete mode 100644 java/doc/allclasses-frame.html create mode 100644 java/doc/allclasses-index.html rename java/doc/{allclasses-noframe.html => allclasses.html} (56%) create mode 100644 java/doc/allpackages-index.html rename java/doc/{package-list => element-list} (100%) create mode 100644 java/doc/jquery-ui.overrides.css create mode 100644 java/doc/jquery/external/jquery/jquery.js create mode 100644 java/doc/jquery/jquery-3.6.0.min.js create mode 100644 java/doc/jquery/jquery-ui.min.css create mode 100644 java/doc/jquery/jquery-ui.min.js create mode 100644 java/doc/jquery/jszip-utils/dist/jszip-utils-ie.js create mode 100644 java/doc/jquery/jszip-utils/dist/jszip-utils-ie.min.js create mode 100644 java/doc/jquery/jszip-utils/dist/jszip-utils.js create mode 100644 java/doc/jquery/jszip-utils/dist/jszip-utils.min.js create mode 100644 java/doc/jquery/jszip/dist/jszip.js create mode 100644 java/doc/jquery/jszip/dist/jszip.min.js create mode 100644 java/doc/member-search-index.js create mode 100644 java/doc/member-search-index.zip delete mode 100644 java/doc/org/libjpegturbo/turbojpeg/package-frame.html create mode 100644 java/doc/package-search-index.js create mode 100644 java/doc/package-search-index.zip delete mode 100644 java/doc/resources/background.gif create mode 100644 java/doc/resources/glass.png delete mode 100644 java/doc/resources/tab.gif delete mode 100644 java/doc/resources/titlebar.gif delete mode 100644 java/doc/resources/titlebar_end.gif create mode 100644 java/doc/resources/x.png create mode 100644 java/doc/search.js create mode 100644 java/doc/type-search-index.js create mode 100644 java/doc/type-search-index.zip create mode 100644 testimages/big_building16.ppm create mode 100644 testimages/big_tree8.bmp rename testimages/{nightshot_iso_100.txt => big_tree8.txt} (93%) delete mode 100644 testimages/nightshot_iso_100.bmp delete mode 100755 tjbenchtest.java.in delete mode 100755 tjexampletest.java.in create mode 100644 turbojpeg-mp.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 5920cb52d..9b7248ffb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -342,7 +342,8 @@ set(TURBOJPEG_SO_MAJOR_VERSION 0) # function so that it accepts "abbreviated table specification" (AKA # "tables-only") datastreams as well as JPEG images, but that did not affect # forward API/ABI compatibility. -set(TURBOJPEG_SO_AGE 2) +# 3: TurboJPEG 3 API +set(TURBOJPEG_SO_AGE 3) set(TURBOJPEG_SO_VERSION 0.${TURBOJPEG_SO_AGE}.0) @@ -608,6 +609,12 @@ if(ENABLE_STATIC) endif() if(WITH_TURBOJPEG) + add_library(turbojpeg12 OBJECT rdppm.c wrppm.c) + set_property(TARGET turbojpeg12 PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=12 -DPPM_SUPPORTED") + add_library(turbojpeg16 OBJECT rdppm.c wrppm.c) + set_property(TARGET turbojpeg16 PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=16 -DPPM_SUPPORTED") if(ENABLE_SHARED) set(TURBOJPEG_SOURCES ${JPEG_SOURCES} ${SIMD_TARGET_OBJECTS} ${SIMD_OBJS} turbojpeg.c transupp.c jdatadst-tj.c jdatasrc-tj.c rdbmp.c rdppm.c @@ -624,7 +631,8 @@ if(WITH_TURBOJPEG) set(TURBOJPEG_SOURCES ${TURBOJPEG_SOURCES} ${CMAKE_BINARY_DIR}/win/turbojpeg.rc) endif() - add_library(turbojpeg SHARED ${TURBOJPEG_SOURCES}) + add_library(turbojpeg SHARED ${TURBOJPEG_SOURCES} + $ $) set_property(TARGET turbojpeg PROPERTY COMPILE_FLAGS "-DBMP_SUPPORTED -DPPM_SUPPORTED") if(WIN32) @@ -664,7 +672,8 @@ if(WITH_TURBOJPEG) add_library(turbojpeg-static STATIC ${JPEG_SOURCES} ${SIMD_TARGET_OBJECTS} ${SIMD_OBJS} turbojpeg.c transupp.c jdatadst-tj.c jdatasrc-tj.c rdbmp.c rdppm.c wrbmp.c wrppm.c $ - $) + $ $ + $) set_property(TARGET turbojpeg-static PROPERTY COMPILE_FLAGS "-DBMP_SUPPORTED -DPPM_SUPPORTED") if(NOT MSVC) @@ -786,6 +795,18 @@ if(WITH_JAVA) ${Java_JAVA_EXECUTABLE} ${JAVAARGS} -cp java/turbojpeg.jar -Djava.library.path=${CMAKE_CURRENT_BINARY_DIR}/${OBJDIR} TJUnitTest -bi -lossless) + add_test(TJUnitTest12 + ${Java_JAVA_EXECUTABLE} ${JAVAARGS} -cp java/turbojpeg.jar + -Djava.library.path=${CMAKE_CURRENT_BINARY_DIR}/${OBJDIR} + TJUnitTest -precision 12) + add_test(TJUnitTest12-lossless + ${Java_JAVA_EXECUTABLE} ${JAVAARGS} -cp java/turbojpeg.jar + -Djava.library.path=${CMAKE_CURRENT_BINARY_DIR}/${OBJDIR} + TJUnitTest -precision 12 -lossless) + add_test(TJUnitTest16-lossless + ${Java_JAVA_EXECUTABLE} ${JAVAARGS} -cp java/turbojpeg.jar + -Djava.library.path=${CMAKE_CURRENT_BINARY_DIR}/${OBJDIR} + TJUnitTest -precision 16) endif() set(TEST_LIBTYPES "") @@ -917,83 +938,137 @@ foreach(libtype ${TEST_LIBTYPES}) ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -lossless -alloc) add_test(tjunittest-${libtype}-bmp ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -bmp) + add_test(tjunittest12-${libtype} + ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -precision 12) + add_test(tjunittest12-${libtype}-alloc + ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -precision 12 + -alloc) + add_test(tjunittest12-${libtype}-lossless + ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -precision 12 + -lossless) + add_test(tjunittest12-${libtype}-lossless-alloc + ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -precision 12 + -lossless -alloc) + add_test(tjunittest12-${libtype}-bmp + ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -precision 12 -bmp) + add_test(tjunittest16-${libtype}-lossless + ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -precision 16) + add_test(tjunittest16-${libtype}-lossless-alloc + ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -precision 16 + -alloc) + add_test(tjunittest16-${libtype}-bmp + ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -precision 16 -bmp) + + foreach(sample_bits 8 12) + + if(sample_bits EQUAL 12) + set(tjbench tjbench12) + set(testout testout12) + + set(MD5_PPM_GRAY_TILE 2f799249148b1a9d0e61fa4408f6c397) + set(MD5_PPM_420_8x8_TILE b25684e1af37be504ee3fd137757353f) + set(MD5_PPM_420_16x16_TILE 2c1af444a63d19167eb3f4c1fa7f1b67) + set(MD5_PPM_420_32x32_TILE cce091fe18688f39bc0b5ba29238e1e2) + set(MD5_PPM_420_64x64_TILE f770ec8f710a014606dee662bc88606d) + set(MD5_PPM_420_128x128_TILE a841bc82e9eda34cbdefe53f808b339c) + set(MD5_PPM_420M_8x8_TILE 9de845a8d805affb9ae3a7b2712eaa46) + set(MD5_PPM_420M_TILE 455d273be0e229b9c8aabb16481bce5b) + set(MD5_PPM_422_8x8_TILE 5e9f784a98a7eae2789ea1458ed43748) + set(MD5_PPM_422_16x16_TILE c8df65a792d371a30c8fb7352f320314) + set(MD5_PPM_422_32x32_TILE b523b630237e3305a5c4d353ff4ee19b) + set(MD5_PPM_422_64x64_TILE eb30bdd20337079745b039e24e613bfd) + set(MD5_PPM_422_128x128_TILE 7997458635973b004da46863e2da55ea) + set(MD5_PPM_422M_8x8_TILE f8443fffd32cce7681dd36010ce43c07) + set(MD5_PPM_422M_TILE a0d45368343a63ca2c8ee87cc4ef9ded) + set(MD5_PPM_444_TILE 2f571a032e4dbc8ef40f75219d336b0b) + else() + set(tjbench tjbench) + set(testout testout) + + set(MD5_PPM_GRAY_TILE 2c3b567086e6ca0c5e6d34ad8d6f6fe8) + set(MD5_PPM_420_8x8_TILE efca1bdf0226df01777137778cf986ec) + set(MD5_PPM_420_16x16_TILE 8c92c7453870d9e11c6d1dec3a8c9101) + set(MD5_PPM_420_32x32_TILE 3f7651872a95e469d1c7115f1b11ecef) + set(MD5_PPM_420_64x64_TILE f64c71af03fdea12363b62f1a3096aab) + set(MD5_PPM_420_128x128_TILE 5a5ef57517558c671bf5e75793588d69) + set(MD5_PPM_420M_8x8_TILE 66bd869b315a32a00fef1a025661ce72) + set(MD5_PPM_420M_TILE bf9ec2ab4875abb2efcce8f876fe2c2a) + set(MD5_PPM_422_8x8_TILE c300553ce1b3b90fd414ec96b62fe988) + set(MD5_PPM_422_16x16_TILE 6559ddb1191f5b2d3eb41081b254c4e0) + set(MD5_PPM_422_32x32_TILE 58691797f4584c4c5ed5965a6bb9aec0) + set(MD5_PPM_422_64x64_TILE 7f9e34942ae46af7b784f459ec133f5e) + set(MD5_PPM_422_128x128_TILE 6afcb77580d85dd3eacb04b3c2bc7710) + set(MD5_PPM_422M_8x8_TILE 55df1f96bcfb631aedeb940cf3f011f5) + set(MD5_PPM_422M_TILE 6502031018c2d2f69bc6353347f8df4d) + set(MD5_PPM_444_TILE 87bd58005eec73f0f313c8e38d0d793c) + endif() - set(MD5_PPM_GRAY_TILE 89d3ca21213d9d864b50b4e4e7de4ca6) - set(MD5_PPM_420_8x8_TILE 847fceab15c5b7b911cb986cf0f71de3) - set(MD5_PPM_420_16x16_TILE ca45552a93687e078f7137cc4126a7b0) - set(MD5_PPM_420_32x32_TILE d8676f1d6b68df358353bba9844f4a00) - set(MD5_PPM_420_64x64_TILE 4e4c1a3d7ea4bace4f868bcbe83b7050) - set(MD5_PPM_420_128x128_TILE f24c3429c52265832beab9df72a0ceae) - set(MD5_PPM_420M_8x8_TILE bc25320e1f4c31ce2e610e43e9fd173c) - set(MD5_PPM_420M_TILE 75ffdf14602258c5c189522af57fa605) - set(MD5_PPM_422_8x8_TILE d83dacd9fc73b0a6f10c09acad64eb1e) - set(MD5_PPM_422_16x16_TILE 35077fb610d72dd743b1eb0cbcfe10fb) - set(MD5_PPM_422_32x32_TILE e6902ed8a449ecc0f0d6f2bf945f65f7) - set(MD5_PPM_422_64x64_TILE 2b4502a8f316cedbde1da7bce3d2231e) - set(MD5_PPM_422_128x128_TILE f0b5617d578f5e13c8eee215d64d4877) - set(MD5_PPM_422M_8x8_TILE 828941d7f41cd6283abd6beffb7fd51d) - set(MD5_PPM_422M_TILE e877ae1324c4a280b95376f7f018172f) - set(MD5_PPM_444_TILE 7964e41e67cfb8d0a587c0aa4798f9c3) - - # Test compressing from/decompressing to an arbitrary subregion of a larger - # image buffer - add_test(tjbench-${libtype}-tile-cp - ${CMAKE_COMMAND} -E copy_if_different ${TESTIMAGES}/testorig.ppm - testout_tile.ppm) - add_test(tjbench-${libtype}-tile - ${CMAKE_CROSSCOMPILING_EMULATOR} tjbench${suffix} testout_tile.ppm 95 - -rgb -quiet -tile -benchtime 0.01 -warmup 0) - set_tests_properties(tjbench-${libtype}-tile - PROPERTIES DEPENDS tjbench-${libtype}-tile-cp) - - foreach(tile 8 16 32 64 128) - add_test(tjbench-${libtype}-tile-gray-${tile}x${tile}-cmp - ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} ${MD5_PPM_GRAY_TILE} - testout_tile_GRAY_Q95_${tile}x${tile}.ppm) - foreach(subsamp 420 422) - add_test(tjbench-${libtype}-tile-${subsamp}-${tile}x${tile}-cmp - ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} - ${MD5_PPM_${subsamp}_${tile}x${tile}_TILE} - testout_tile_${subsamp}_Q95_${tile}x${tile}.ppm) + # Test compressing from/decompressing to an arbitrary subregion of a larger + # image buffer + add_test(${tjbench}-${libtype}-tile-cp + ${CMAKE_COMMAND} -E copy_if_different ${TESTIMAGES}/testorig.ppm + ${testout}_tile.ppm) + add_test(${tjbench}-${libtype}-tile + ${CMAKE_CROSSCOMPILING_EMULATOR} tjbench${suffix} ${testout}_tile.ppm + 95 -precision ${sample_bits} -rgb -quiet -tile -benchtime 0.01 + -warmup 0) + set_tests_properties(${tjbench}-${libtype}-tile + PROPERTIES DEPENDS ${tjbench}-${libtype}-tile-cp) + + foreach(tile 8 16 32 64 128) + add_test(${tjbench}-${libtype}-tile-gray-${tile}x${tile}-cmp + ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} ${MD5_PPM_GRAY_TILE} + ${testout}_tile_GRAY_Q95_${tile}x${tile}.ppm) + foreach(subsamp 420 422) + add_test(${tjbench}-${libtype}-tile-${subsamp}-${tile}x${tile}-cmp + ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} + ${MD5_PPM_${subsamp}_${tile}x${tile}_TILE} + ${testout}_tile_${subsamp}_Q95_${tile}x${tile}.ppm) + endforeach() + add_test(${tjbench}-${libtype}-tile-444-${tile}x${tile}-cmp + ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} ${MD5_PPM_444_TILE} + ${testout}_tile_444_Q95_${tile}x${tile}.ppm) + foreach(subsamp gray 420 422 444) + set_tests_properties( + ${tjbench}-${libtype}-tile-${subsamp}-${tile}x${tile}-cmp + PROPERTIES DEPENDS ${tjbench}-${libtype}-tile) + endforeach() endforeach() - add_test(tjbench-${libtype}-tile-444-${tile}x${tile}-cmp - ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} ${MD5_PPM_444_TILE} - testout_tile_444_Q95_${tile}x${tile}.ppm) - foreach(subsamp gray 420 422 444) - set_tests_properties(tjbench-${libtype}-tile-${subsamp}-${tile}x${tile}-cmp - PROPERTIES DEPENDS tjbench-${libtype}-tile) - endforeach() - endforeach() - add_test(tjbench-${libtype}-tilem-cp - ${CMAKE_COMMAND} -E copy_if_different ${TESTIMAGES}/testorig.ppm - testout_tilem.ppm) - add_test(tjbench-${libtype}-tilem - ${CMAKE_CROSSCOMPILING_EMULATOR} tjbench${suffix} testout_tilem.ppm 95 - -rgb -fastupsample -quiet -tile -benchtime 0.01 -warmup 0) - set_tests_properties(tjbench-${libtype}-tilem - PROPERTIES DEPENDS tjbench-${libtype}-tilem-cp) - - add_test(tjbench-${libtype}-tile-420m-8x8-cmp - ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} ${MD5_PPM_420M_8x8_TILE} - testout_tilem_420_Q95_8x8.ppm) - add_test(tjbench-${libtype}-tile-422m-8x8-cmp - ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} ${MD5_PPM_422M_8x8_TILE} - testout_tilem_422_Q95_8x8.ppm) - foreach(tile 16 32 64 128) - foreach(subsamp 420 422) - add_test(tjbench-${libtype}-tile-${subsamp}m-${tile}x${tile}-cmp - ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} - ${MD5_PPM_${subsamp}M_TILE} - testout_tilem_${subsamp}_Q95_${tile}x${tile}.ppm) + add_test(${tjbench}-${libtype}-tilem-cp + ${CMAKE_COMMAND} -E copy_if_different ${TESTIMAGES}/testorig.ppm + ${testout}_tilem.ppm) + add_test(${tjbench}-${libtype}-tilem + ${CMAKE_CROSSCOMPILING_EMULATOR} tjbench${suffix} ${testout}_tilem.ppm + 95 -precision ${sample_bits} -rgb -fastupsample -quiet -tile + -benchtime 0.01 -warmup 0) + set_tests_properties(${tjbench}-${libtype}-tilem + PROPERTIES DEPENDS ${tjbench}-${libtype}-tilem-cp) + + add_test(${tjbench}-${libtype}-tile-420m-8x8-cmp + ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} ${MD5_PPM_420M_8x8_TILE} + ${testout}_tilem_420_Q95_8x8.ppm) + add_test(${tjbench}-${libtype}-tile-422m-8x8-cmp + ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} ${MD5_PPM_422M_8x8_TILE} + ${testout}_tilem_422_Q95_8x8.ppm) + foreach(tile 16 32 64 128) + foreach(subsamp 420 422) + add_test(${tjbench}-${libtype}-tile-${subsamp}m-${tile}x${tile}-cmp + ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} + ${MD5_PPM_${subsamp}M_TILE} + ${testout}_tilem_${subsamp}_Q95_${tile}x${tile}.ppm) + endforeach() endforeach() - endforeach() - foreach(tile 8 16 32 64 128) - foreach(subsamp 420 422) - set_tests_properties(tjbench-${libtype}-tile-${subsamp}m-${tile}x${tile}-cmp - PROPERTIES DEPENDS tjbench-${libtype}-tilem) + foreach(tile 8 16 32 64 128) + foreach(subsamp 420 422) + set_tests_properties( + ${tjbench}-${libtype}-tile-${subsamp}m-${tile}x${tile}-cmp + PROPERTIES DEPENDS ${tjbench}-${libtype}-tilem) + endforeach() endforeach() + endforeach() + endif() # These tests are carefully crafted to provide full coverage of as many of @@ -1540,81 +1615,21 @@ if(WITH_TURBOJPEG) if(WIN32) set(BASH bash) endif() - if(WITH_JAVA) - configure_file(tjbenchtest.java.in tjbenchtest.java @ONLY) - configure_file(tjexampletest.java.in tjexampletest.java @ONLY) - add_custom_target(tjtest - COMMAND echo tjbenchtest - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest - COMMAND echo tjbenchtest -alloc - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -alloc - COMMAND echo tjbenchtest -yuv - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -yuv - COMMAND echo tjbenchtest -yuv -alloc - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -yuv -alloc - COMMAND echo tjbenchtest -progressive - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -progressive - COMMAND echo tjbenchtest -progressive -yuv - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -progressive -yuv - COMMAND echo tjbenchtest -arithmetic - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -arithmetic - COMMAND echo tjbenchtest -arithmetic -yuv - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -arithmetic -yuv - COMMAND echo tjbenchtest -lossless - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -lossless - COMMAND echo tjbenchtest -lossless -alloc - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -lossless -alloc - COMMAND echo tjexampletest - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest - COMMAND echo tjbenchtest.java - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest.java - COMMAND echo tjbenchtest.java -yuv - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest.java -yuv - COMMAND echo tjbenchtest.java -progressive - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest.java -progressive - COMMAND echo tjbenchtest.java -progressive -yuv - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest.java - -progressive -yuv - COMMAND echo tjbenchtest.java -arithmetic - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest.java -arithmetic - COMMAND echo tjbenchtest.java -arithmetic -yuv - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest.java -arithmetic - -yuv - COMMAND echo tjbenchtest.java -lossless - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest.java -lossless - COMMAND echo tjexampletest.java - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest.java - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest - ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest.java - ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest - ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest.java) - else() - add_custom_target(tjtest - COMMAND echo tjbenchtest - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest - COMMAND echo tjbenchtest -alloc - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -alloc - COMMAND echo tjbenchtest -yuv - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -yuv - COMMAND echo tjbenchtest -yuv -alloc - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -yuv -alloc - COMMAND echo tjbenchtest -progressive - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -progressive - COMMAND echo tjbenchtest -progressive -yuv - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -progressive -yuv - COMMAND echo tjbenchtest -arithmetic - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -arithmetic - COMMAND echo tjbenchtest -arithmetic -yuv - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -arithmetic -yuv - COMMAND echo tjbenchtest -lossless - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -lossless - COMMAND echo tjbenchtest -lossless -alloc - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -lossless -alloc - COMMAND echo tjexampletest - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest - ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest) - endif() + add_custom_target(tjtest COMMAND ${CMAKE_COMMAND} -DWITH_JAVA=${WITH_JAVA} + -DPRECISION=8 -P ${CMAKE_SOURCE_DIR}/cmakescripts/tjbenchtest.cmake + DEPENDS ${CMAKE_SOURCE_DIR}/cmakescripts/tjbenchtest.cmake + ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest + ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest) + add_custom_target(tjtest12 COMMAND ${CMAKE_COMMAND} -DWITH_JAVA=${WITH_JAVA} + -DPRECISION=12 -P ${CMAKE_SOURCE_DIR}/cmakescripts/tjbenchtest.cmake + DEPENDS ${CMAKE_SOURCE_DIR}/cmakescripts/tjbenchtest.cmake + ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest + ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest) + add_custom_target(tjtest16 COMMAND ${CMAKE_COMMAND} -DWITH_JAVA=${WITH_JAVA} + -DPRECISION=16 -P ${CMAKE_SOURCE_DIR}/cmakescripts/tjbenchtest.cmake + DEPENDS ${CMAKE_SOURCE_DIR}/cmakescripts/tjbenchtest.cmake + ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest + ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest) endif() diff --git a/cmakescripts/testclean.cmake b/cmakescripts/testclean.cmake index fc3fc25e9..4d249dee6 100644 --- a/cmakescripts/testclean.cmake +++ b/cmakescripts/testclean.cmake @@ -30,6 +30,9 @@ file(GLOB FILES *_411_*.ppm *_411_*.jpg *_411.yuv + *_LOSSL*S_*.bmp + *_LOSSL*S_*.ppm + *_LOSSL*S_*.jpg tjbenchtest*.log tjexampletest*.log) diff --git a/cmakescripts/tjbenchtest.cmake b/cmakescripts/tjbenchtest.cmake new file mode 100644 index 000000000..1f28b8a66 --- /dev/null +++ b/cmakescripts/tjbenchtest.cmake @@ -0,0 +1,67 @@ +if(NOT DEFINED PRECISION) + message(FATAL_ERROR "PRECISION must be specified") +endif() + +if(NOT DEFINED WITH_JAVA) + message(FATAL_ERROR "WITH_JAVA must be specified") +endif() + +macro(check_error program) + if(NOT RESULT EQUAL 0) + message(FATAL_ERROR "${program} failed.") + endif() +endmacro() + +macro(run_test PROG ARGS) + string(REPLACE ";" " " SPACED_ARGS "${ARGS}") + message(STATUS "${PROG} ${SPACED_ARGS}") + execute_process(COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${PROG} ${ARGS} + RESULT_VARIABLE RESULT) + check_error("${PROG} ${SPACED_ARGS}") +endmacro() + +if(NOT PRECISION EQUAL 16) + run_test(tjbenchtest "-precision;${PRECISION}") + run_test(tjbenchtest "-precision;${PRECISION};-alloc") +endif() +if(PRECISION EQUAL 8) + run_test(tjbenchtest "-precision;${PRECISION};-yuv") + run_test(tjbenchtest "-precision;${PRECISION};-yuv;-alloc") + run_test(tjbenchtest "-precision;${PRECISION};-optimize") + run_test(tjbenchtest "-precision;${PRECISION};-optimize;-yuv") +endif() +if(NOT PRECISION EQUAL 16) + run_test(tjbenchtest "-precision;${PRECISION};-progressive") +endif() +if(PRECISION EQUAL 8) + run_test(tjbenchtest "-precision;${PRECISION};-progressive;-yuv") + run_test(tjbenchtest "-precision;${PRECISION};-arithmetic") + run_test(tjbenchtest "-precision;${PRECISION};-arithmetic;-yuv") +endif() +run_test(tjbenchtest "-precision;${PRECISION};-lossless") +run_test(tjbenchtest "-precision;${PRECISION};-lossless;-alloc") +if(PRECISION EQUAL 8) + run_test(tjexampletest "") +endif() +if(WITH_JAVA) + if(NOT PRECISION EQUAL 16) + run_test(tjbenchtest "-java;-precision;${PRECISION}") + endif() + if(PRECISION EQUAL 8) + run_test(tjbenchtest "-java;-precision;${PRECISION};-yuv") + run_test(tjbenchtest "-java;-precision;${PRECISION};-optimize") + run_test(tjbenchtest "-java;-precision;${PRECISION};-optimize;-yuv") + endif() + if(NOT PRECISION EQUAL 16) + run_test(tjbenchtest "-java;-precision;${PRECISION};-progressive") + endif() + if(PRECISION EQUAL 8) + run_test(tjbenchtest "-java;-precision;${PRECISION};-progressive;-yuv") + run_test(tjbenchtest "-java;-precision;${PRECISION};-arithmetic") + run_test(tjbenchtest "-java;-precision;${PRECISION};-arithmetic;-yuv") + endif() + run_test(tjbenchtest "-java;-precision;${PRECISION};-lossless") + if(PRECISION EQUAL 8) + run_test(tjexampletest "-java") + endif() +endif() diff --git a/doc/html/annotated.html b/doc/html/annotated.html index de0462ee3..d7e29877e 100644 --- a/doc/html/annotated.html +++ b/doc/html/annotated.html @@ -23,7 +23,7 @@
    TurboJPEG -  2.2 +  3
    diff --git a/doc/html/classes.html b/doc/html/classes.html index ac3c535b4..78730e6b3 100644 --- a/doc/html/classes.html +++ b/doc/html/classes.html @@ -23,7 +23,7 @@
    TurboJPEG -  2.2 +  3
    diff --git a/doc/html/functions.html b/doc/html/functions.html index 68fcde3f8..a3456d705 100644 --- a/doc/html/functions.html +++ b/doc/html/functions.html @@ -23,7 +23,7 @@
    TurboJPEG -  2.2 +  3
    diff --git a/doc/html/functions_vars.html b/doc/html/functions_vars.html index d918435dc..409cbd599 100644 --- a/doc/html/functions_vars.html +++ b/doc/html/functions_vars.html @@ -23,7 +23,7 @@
    TurboJPEG -  2.2 +  3
    diff --git a/doc/html/group___turbo_j_p_e_g.html b/doc/html/group___turbo_j_p_e_g.html index 4c39201f2..1e96c4744 100644 --- a/doc/html/group___turbo_j_p_e_g.html +++ b/doc/html/group___turbo_j_p_e_g.html @@ -23,7 +23,7 @@
    TurboJPEG -  2.2 +  3
    @@ -92,6 +92,9 @@ + + + @@ -101,36 +104,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + @@ -138,10 +114,10 @@ - + - + @@ -150,20 +126,20 @@ - + - + - - - + + + @@ -179,6 +155,12 @@

    Macros

    #define TJ_NUMINIT
     The number of initialization options. More...
     
    #define TJ_NUMSAMP
     The number of chrominance subsampling options. More...
     
    #define TJ_NUMCS
     The number of JPEG colorspaces. More...
     
    #define TJFLAG_BOTTOMUP
     Rows in the packed-pixel source/destination image are stored in bottom-up (Windows, OpenGL) order rather than in top-down (X11) order. More...
     
    #define TJFLAG_FASTUPSAMPLE
     When decompressing an image that was compressed using chrominance subsampling, use the fastest chrominance upsampling algorithm available. More...
     
    #define TJFLAG_NOREALLOC
     Disable JPEG buffer (re)allocation. More...
     
    #define TJFLAG_FASTDCT
     Use the fastest DCT/IDCT algorithm available. More...
     
    #define TJFLAG_ACCURATEDCT
     Use the most accurate DCT/IDCT algorithm available. More...
     
    #define TJFLAG_STOPONWARNING
     Immediately discontinue the current compression/decompression/transform operation if a warning (non-fatal error) occurs. More...
     
    #define TJFLAG_PROGRESSIVE
     Use progressive entropy coding in JPEG images generated by the compression and transform functions. More...
     
    #define TJFLAG_LIMITSCANS
     Limit the number of progressive JPEG scans that the decompression and transform functions will process. More...
     
    #define TJFLAG_ARITHMETIC
     Use arithmetic entropy coding in JPEG images generated by the compression and transform functions. More...
     
    #define TJFLAG_LOSSLESS
     Generate a lossless JPEG image when compressing. More...
     
    #define TJ_NUMPARAM
     The number of parameters. More...
     
    #define TJ_NUMERR
     The number of error codes. More...
     
     The number of transform operations. More...
     
    #define TJXOPT_PERFECT
     This option will cause tjTransform() to return an error if the transform is not perfect. More...
     This option will cause tj3Transform() to return an error if the transform is not perfect. More...
     
    #define TJXOPT_TRIM
     This option will cause tjTransform() to discard any partial MCU blocks that cannot be transformed. More...
     This option will cause tj3Transform() to discard any partial MCU blocks that cannot be transformed. More...
     
    #define TJXOPT_CROP
     This option will enable lossless cropping. More...
     This option will discard the color data in the source image and produce a grayscale destination image. More...
     
    #define TJXOPT_NOOUTPUT
     This option will prevent tjTransform() from outputting a JPEG image for this particular transform. More...
     This option will prevent tj3Transform() from outputting a JPEG image for this particular transform. More...
     
    #define TJXOPT_PROGRESSIVE
     This option will enable progressive entropy coding in the JPEG image generated by this particular transform. More...
     
    #define TJXOPT_COPYNONE
     This option will prevent tjTransform() from copying any extra markers (including EXIF and ICC profile data) from the source image to the destination image. More...
     This option will prevent tj3Transform() from copying any extra markers (including EXIF and ICC profile data) from the source image to the destination image. More...
     
    #define TJXOPT_ARITHMETIC
     This option will enable arithmetic entropy coding in the JPEG image generated by this particular transform. More...
     
    #define TJPAD(width)
     Pad the given width to the nearest multiple of 4. More...
     
    #define TJXOPT_OPTIMIZE
     This option will enable optimized baseline entropy coding in the JPEG image generated by this particular transform. More...
     
    #define TJSCALED(dimension, scalingFactor)
     Compute the scaled value of dimension using the given scaling factor. More...
     
    + + + @@ -223,6 +206,39 @@ } + + + @@ -240,95 +256,125 @@ TJXOP_ROT270
    } - +

    Enumerations

    enum  TJINIT { TJINIT_COMPRESS, +TJINIT_DECOMPRESS, +TJINIT_TRANSFORM + }
     Initialization options. More...
     
    enum  TJSAMP {
      TJSAMP_444, TJSAMP_422, @@ -186,7 +168,8 @@ TJSAMP_GRAY,
      TJSAMP_440, -TJSAMP_411 +TJSAMP_411, +TJSAMP_UNKNOWN
    }
     Chrominance subsampling options. More...
     JPEG colorspaces. More...
     
    enum  TJPARAM {
    +  TJPARAM_STOPONWARNING, +TJPARAM_BOTTOMUP, +TJPARAM_NOREALLOC, +TJPARAM_QUALITY, +
    +  TJPARAM_SUBSAMP, +TJPARAM_JPEGWIDTH, +TJPARAM_JPEGHEIGHT, +TJPARAM_PRECISION, +
    +  TJPARAM_COLORSPACE, +TJPARAM_FASTUPSAMPLE, +TJPARAM_FASTDCT, +TJPARAM_OPTIMIZE, +
    +  TJPARAM_PROGRESSIVE, +TJPARAM_SCANLIMIT, +TJPARAM_ARITHMETIC, +TJPARAM_LOSSLESS, +
    +  TJPARAM_LOSSLESSPSV, +TJPARAM_LOSSLESSPT, +TJPARAM_RESTARTBLOCKS, +TJPARAM_RESTARTROWS, +
    +  TJPARAM_XDENSITY, +TJPARAM_YDENSITY, +TJPARAM_DENSITYUNITS +
    + }
     Parameters. More...
     
    enum  TJERR { TJERR_WARNING, TJERR_FATAL }
     Transform operations for tjTransform() More...
     Transform operations for tj3Transform() More...
     
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    Functions

    DLLEXPORT tjhandle tjInitCompress (void)
     Create a TurboJPEG compressor instance. More...
     
    DLLEXPORT int tjCompress2 (tjhandle handle, const unsigned char *srcBuf, int width, int pitch, int height, int pixelFormat, unsigned char **jpegBuf, unsigned long *jpegSize, int jpegSubsamp, int jpegQual, int flags)
     Compress a packed-pixel RGB, grayscale, or CMYK image into a JPEG image. More...
     
    DLLEXPORT int tjCompressFromYUV (tjhandle handle, const unsigned char *srcBuf, int width, int align, int height, int subsamp, unsigned char **jpegBuf, unsigned long *jpegSize, int jpegQual, int flags)
     Compress a unified planar YUV image into a JPEG image. More...
     
    DLLEXPORT int tjCompressFromYUVPlanes (tjhandle handle, const unsigned char **srcPlanes, int width, const int *strides, int height, int subsamp, unsigned char **jpegBuf, unsigned long *jpegSize, int jpegQual, int flags)
     Compress a set of Y, U (Cb), and V (Cr) image planes into a JPEG image. More...
     
    DLLEXPORT unsigned long tjBufSize (int width, int height, int jpegSubsamp)
     The maximum size of the buffer (in bytes) required to hold a JPEG image with the given parameters. More...
     
    DLLEXPORT unsigned long tjBufSizeYUV2 (int width, int align, int height, int subsamp)
     The size of the buffer (in bytes) required to hold a unified planar YUV image with the given parameters. More...
     
    DLLEXPORT unsigned long tjPlaneSizeYUV (int componentID, int width, int stride, int height, int subsamp)
     The size of the buffer (in bytes) required to hold a YUV image plane with the given parameters. More...
     
    DLLEXPORT int tjPlaneWidth (int componentID, int width, int subsamp)
     The plane width of a YUV image plane with the given parameters. More...
     
    DLLEXPORT int tjPlaneHeight (int componentID, int height, int subsamp)
     The plane height of a YUV image plane with the given parameters. More...
     
    DLLEXPORT int tjEncodeYUV3 (tjhandle handle, const unsigned char *srcBuf, int width, int pitch, int height, int pixelFormat, unsigned char *dstBuf, int align, int subsamp, int flags)
     Encode a packed-pixel RGB or grayscale image into a unified planar YUV image. More...
     
    DLLEXPORT int tjEncodeYUVPlanes (tjhandle handle, const unsigned char *srcBuf, int width, int pitch, int height, int pixelFormat, unsigned char **dstPlanes, int *strides, int subsamp, int flags)
     Encode a packed-pixel RGB or grayscale image into separate Y, U (Cb), and V (Cr) image planes. More...
     
    DLLEXPORT tjhandle tjInitDecompress (void)
     Create a TurboJPEG decompressor instance. More...
     
    DLLEXPORT int tjDecompressHeader4 (tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height, int *jpegSubsamp, int *jpegColorspace, int *jpegFlags)
     Retrieve information about a JPEG image without decompressing it, or prime the decompressor with quantization and Huffman tables. More...
     
    DLLEXPORT tjscalingfactortjGetScalingFactors (int *numScalingFactors)
     Returns a list of fractional scaling factors that the JPEG decompressor supports. More...
     
    DLLEXPORT int tjDecompress2 (tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf, int width, int pitch, int height, int pixelFormat, int flags)
     Decompress a JPEG image into a packed-pixel RGB, grayscale, or CMYK image. More...
     
    DLLEXPORT int tjDecompressToYUV2 (tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf, int width, int align, int height, int flags)
     Decompress a JPEG image into a unified planar YUV image. More...
     
    DLLEXPORT int tjDecompressToYUVPlanes (tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, unsigned char **dstPlanes, int width, int *strides, int height, int flags)
     Decompress a JPEG image into separate Y, U (Cb), and V (Cr) image planes. More...
     
    DLLEXPORT int tjDecodeYUV (tjhandle handle, const unsigned char *srcBuf, int align, int subsamp, unsigned char *dstBuf, int width, int pitch, int height, int pixelFormat, int flags)
     Decode a unified planar YUV image into a packed-pixel RGB or grayscale image. More...
     
    DLLEXPORT int tjDecodeYUVPlanes (tjhandle handle, const unsigned char **srcPlanes, const int *strides, int subsamp, unsigned char *dstBuf, int width, int pitch, int height, int pixelFormat, int flags)
     Decode a set of Y, U (Cb), and V (Cr) image planes into a packed-pixel RGB or grayscale image. More...
     
    DLLEXPORT tjhandle tjInitTransform (void)
     Create a new TurboJPEG transformer instance. More...
     
    DLLEXPORT int tjTransform (tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, int n, unsigned char **dstBufs, unsigned long *dstSizes, tjtransform *transforms, int flags)
     Losslessly transform a JPEG image into another JPEG image. More...
     
    DLLEXPORT int tjDestroy (tjhandle handle)
     Destroy a TurboJPEG compressor, decompressor, or transformer instance. More...
     
    DLLEXPORT unsigned char * tjAlloc (int bytes)
     Allocate a byte buffer for use with TurboJPEG. More...
     
    DLLEXPORT unsigned char * tjLoadImage (const char *filename, int *width, int align, int *height, int *pixelFormat, int flags)
     Load a packed-pixel image from disk into memory. More...
     
    DLLEXPORT int tjSaveImage (const char *filename, unsigned char *buffer, int width, int pitch, int height, int pixelFormat, int flags)
     Save a packed-pixel image from memory to disk. More...
     
    DLLEXPORT void tjFree (unsigned char *buffer)
     Free a byte buffer previously allocated by TurboJPEG. More...
     
    DLLEXPORT char * tjGetErrorStr2 (tjhandle handle)
     Returns a descriptive error message explaining why the last command failed. More...
     
    DLLEXPORT int tjGetErrorCode (tjhandle handle)
     Returns a code indicating the severity of the last error. More...
     
    DLLEXPORT tjhandle tj3Init (int initType)
     Create a new TurboJPEG instance. More...
     
    DLLEXPORT int tj3Set (tjhandle handle, int param, int value)
     Set the value of a parameter. More...
     
    DLLEXPORT int tj3Get (tjhandle handle, int param)
     Get the value of a parameter. More...
     
    DLLEXPORT int tj3Compress8 (tjhandle handle, const unsigned char *srcBuf, int width, int pitch, int height, int pixelFormat, unsigned char **jpegBuf, size_t *jpegSize)
     Compress an 8-bit-per-sample packed-pixel RGB, grayscale, or CMYK image into an 8-bit-per-sample JPEG image. More...
     
    DLLEXPORT int tj3Compress12 (tjhandle handle, const short *srcBuf, int width, int pitch, int height, int pixelFormat, unsigned char **jpegBuf, size_t *jpegSize)
     Compress a 12-bit-per-sample packed-pixel RGB, grayscale, or CMYK image into a 12-bit-per-sample JPEG image. More...
     
    DLLEXPORT int tj3Compress16 (tjhandle handle, const unsigned short *srcBuf, int width, int pitch, int height, int pixelFormat, unsigned char **jpegBuf, size_t *jpegSize)
     Compress a 16-bit-per-sample packed-pixel RGB, grayscale, or CMYK image into a 16-bit-per-sample lossless JPEG image. More...
     
    DLLEXPORT int tj3CompressFromYUV8 (tjhandle handle, const unsigned char *srcBuf, int width, int align, int height, unsigned char **jpegBuf, size_t *jpegSize)
     Compress an 8-bit-per-sample unified planar YUV image into an 8-bit-per-sample JPEG image. More...
     
    DLLEXPORT int tj3CompressFromYUVPlanes8 (tjhandle handle, const unsigned char *const *srcPlanes, int width, const int *strides, int height, unsigned char **jpegBuf, size_t *jpegSize)
     Compress a set of 8-bit-per-sample Y, U (Cb), and V (Cr) image planes into an 8-bit-per-sample JPEG image. More...
     
    DLLEXPORT size_t tj3JPEGBufSize (int width, int height, int jpegSubsamp)
     The maximum size of the buffer (in bytes) required to hold a JPEG image with the given parameters. More...
     
    DLLEXPORT size_t tj3YUVBufSize (int width, int align, int height, int subsamp)
     The size of the buffer (in bytes) required to hold a unified planar YUV image with the given parameters. More...
     
    DLLEXPORT size_t tj3YUVPlaneSize (int componentID, int width, int stride, int height, int subsamp)
     The size of the buffer (in bytes) required to hold a YUV image plane with the given parameters. More...
     
    DLLEXPORT int tj3YUVPlaneWidth (int componentID, int width, int subsamp)
     The plane width of a YUV image plane with the given parameters. More...
     
    DLLEXPORT int tj3YUVPlaneHeight (int componentID, int height, int subsamp)
     The plane height of a YUV image plane with the given parameters. More...
     
    DLLEXPORT int tj3EncodeYUV8 (tjhandle handle, const unsigned char *srcBuf, int width, int pitch, int height, int pixelFormat, unsigned char *dstBuf, int align)
     Encode an 8-bit-per-sample packed-pixel RGB or grayscale image into an 8-bit-per-sample unified planar YUV image. More...
     
    DLLEXPORT int tj3EncodeYUVPlanes8 (tjhandle handle, const unsigned char *srcBuf, int width, int pitch, int height, int pixelFormat, unsigned char **dstPlanes, int *strides)
     Encode an 8-bit-per-sample packed-pixel RGB or grayscale image into separate 8-bit-per-sample Y, U (Cb), and V (Cr) image planes. More...
     
    DLLEXPORT int tj3DecompressHeader (tjhandle handle, const unsigned char *jpegBuf, size_t jpegSize)
     Retrieve information about a JPEG image without decompressing it, or prime the decompressor with quantization and Huffman tables. More...
     
    DLLEXPORT tjscalingfactortj3GetScalingFactors (int *numScalingFactors)
     Returns a list of fractional scaling factors that the JPEG decompressor supports. More...
     
    DLLEXPORT int tj3SetScalingFactor (tjhandle handle, tjscalingfactor scalingFactor)
     Set the scaling factor for subsequent lossy decompression operations. More...
     
    DLLEXPORT int tj3SetCroppingRegion (tjhandle handle, tjregion croppingRegion)
     Set the cropping region for partially decompressing a lossy JPEG image into a packed-pixel image. More...
     
    DLLEXPORT int tj3Decompress8 (tjhandle handle, const unsigned char *jpegBuf, size_t jpegSize, unsigned char *dstBuf, int pitch, int pixelFormat)
     Decompress an 8-bit-per-sample JPEG image into an 8-bit-per-sample packed-pixel RGB, grayscale, or CMYK image. More...
     
    DLLEXPORT int tj3Decompress12 (tjhandle handle, const unsigned char *jpegBuf, size_t jpegSize, short *dstBuf, int pitch, int pixelFormat)
     Decompress a 12-bit-per-sample JPEG image into a 12-bit-per-sample packed-pixel RGB, grayscale, or CMYK image. More...
     
    DLLEXPORT int tj3Decompress16 (tjhandle handle, const unsigned char *jpegBuf, size_t jpegSize, unsigned short *dstBuf, int pitch, int pixelFormat)
     Decompress a 16-bit-per-sample lossless JPEG image into a 16-bit-per-sample packed-pixel RGB, grayscale, or CMYK image. More...
     
    DLLEXPORT int tj3DecompressToYUV8 (tjhandle handle, const unsigned char *jpegBuf, size_t jpegSize, unsigned char *dstBuf, int align)
     Decompress an 8-bit-per-sample JPEG image into an 8-bit-per-sample unified planar YUV image. More...
     
    DLLEXPORT int tj3DecompressToYUVPlanes8 (tjhandle handle, const unsigned char *jpegBuf, size_t jpegSize, unsigned char **dstPlanes, int *strides)
     Decompress an 8-bit-per-sample JPEG image into separate 8-bit-per-sample Y, U (Cb), and V (Cr) image planes. More...
     
    DLLEXPORT int tj3DecodeYUV8 (tjhandle handle, const unsigned char *srcBuf, int align, unsigned char *dstBuf, int width, int pitch, int height, int pixelFormat)
     Decode an 8-bit-per-sample unified planar YUV image into an 8-bit-per-sample packed-pixel RGB or grayscale image. More...
     
    DLLEXPORT int tj3DecodeYUVPlanes8 (tjhandle handle, const unsigned char *const *srcPlanes, const int *strides, unsigned char *dstBuf, int width, int pitch, int height, int pixelFormat)
     Decode a set of 8-bit-per-sample Y, U (Cb), and V (Cr) image planes into an 8-bit-per-sample packed-pixel RGB or grayscale image. More...
     
    DLLEXPORT int tj3Transform (tjhandle handle, const unsigned char *jpegBuf, size_t jpegSize, int n, unsigned char **dstBufs, size_t *dstSizes, const tjtransform *transforms)
     Losslessly transform a JPEG image into another JPEG image. More...
     
    DLLEXPORT void tj3Destroy (tjhandle handle)
     Destroy a TurboJPEG instance. More...
     
    DLLEXPORT void * tj3Alloc (size_t bytes)
     Allocate a byte buffer for use with TurboJPEG. More...
     
    DLLEXPORT unsigned char * tj3LoadImage8 (tjhandle handle, const char *filename, int *width, int align, int *height, int *pixelFormat)
     Load an 8-bit-per-sample packed-pixel image from disk into memory. More...
     
    DLLEXPORT short * tj3LoadImage12 (tjhandle handle, const char *filename, int *width, int align, int *height, int *pixelFormat)
     Load a 12-bit-per-sample packed-pixel image from disk into memory. More...
     
    DLLEXPORT unsigned short * tj3LoadImage16 (tjhandle handle, const char *filename, int *width, int align, int *height, int *pixelFormat)
     Load a 16-bit-per-sample packed-pixel image from disk into memory. More...
     
    DLLEXPORT int tj3SaveImage8 (tjhandle handle, const char *filename, const unsigned char *buffer, int width, int pitch, int height, int pixelFormat)
     Save an 8-bit-per-sample packed-pixel image from memory to disk. More...
     
    DLLEXPORT int tj3SaveImage12 (tjhandle handle, const char *filename, const short *buffer, int width, int pitch, int height, int pixelFormat)
     Save a 12-bit-per-sample packed-pixel image from memory to disk. More...
     
    DLLEXPORT int tj3SaveImage16 (tjhandle handle, const char *filename, const unsigned short *buffer, int width, int pitch, int height, int pixelFormat)
     Save a 16-bit-per-sample packed-pixel image from memory to disk. More...
     
    DLLEXPORT void tj3Free (void *buffer)
     Free a byte buffer previously allocated by TurboJPEG. More...
     
    DLLEXPORT char * tj3GetErrorStr (tjhandle handle)
     Returns a descriptive error message explaining why the last command failed. More...
     
    DLLEXPORT int tj3GetErrorCode (tjhandle handle)
     Returns a code indicating the severity of the last error. More...
     
    @@ -339,20 +385,26 @@ - + - + - + - + - + + + + + + +

    Variables

     MCU block height (in pixels) for a given level of chrominance subsampling. More...
     
    static const int tjRedOffset [TJ_NUMPF]
     Red offset (in bytes) for a given pixel format. More...
     Red offset (in samples) for a given pixel format. More...
     
    static const int tjGreenOffset [TJ_NUMPF]
     Green offset (in bytes) for a given pixel format. More...
     Green offset (in samples) for a given pixel format. More...
     
    static const int tjBlueOffset [TJ_NUMPF]
     Blue offset (in bytes) for a given pixel format. More...
     Blue offset (in samples) for a given pixel format. More...
     
    static const int tjAlphaOffset [TJ_NUMPF]
     Alpha offset (in bytes) for a given pixel format. More...
     Alpha offset (in samples) for a given pixel format. More...
     
    static const int tjPixelSize [TJ_NUMPF]
     Pixel size (in bytes) for a given pixel format. More...
     Pixel size (in samples) for a given pixel format. More...
     
    static const tjregion TJUNCROPPED
     A tjregion structure that specifies no cropping. More...
     
    static const tjscalingfactor TJUNSCALED
     A tjscalingfactor structure that specifies a scaling factor of 1/1 (no scaling) More...
     

    Detailed Description

    TurboJPEG API.

    @@ -396,250 +448,83 @@

    -

    ◆ TJ_NUMPF

    - -
    -
    - - - - -
    #define TJ_NUMPF
    -
    - -

    The number of pixel formats.

    - -
    -
    - -

    ◆ TJ_NUMSAMP

    - -
    -
    - - - - -
    #define TJ_NUMSAMP
    -
    - -

    The number of chrominance subsampling options.

    - -
    -
    - -

    ◆ TJ_NUMXOP

    - -
    -
    - - - - -
    #define TJ_NUMXOP
    -
    - -

    The number of transform operations.

    - -
    -
    - -

    ◆ TJFLAG_ACCURATEDCT

    - -
    -
    - - - - -
    #define TJFLAG_ACCURATEDCT
    -
    - -

    Use the most accurate DCT/IDCT algorithm available.

    -

    The default if this flag is not specified is implementation-specific. For example, the implementation of the TurboJPEG API in libjpeg-turbo uses the fast algorithm by default when compressing, because this has been shown to have only a very slight effect on accuracy, but it uses the accurate algorithm when decompressing, because this has been shown to have a larger effect.

    - -
    -
    - -

    ◆ TJFLAG_ARITHMETIC

    - -
    -
    - - - - -
    #define TJFLAG_ARITHMETIC
    -
    - -

    Use arithmetic entropy coding in JPEG images generated by the compression and transform functions.

    -

    Arithmetic entropy coding will generally improve compression relative to Huffman entropy coding (the default), but it will reduce compression and decompression performance considerably. Can be combined with TJFLAG_PROGRESSIVE.

    - -
    -
    - -

    ◆ TJFLAG_BOTTOMUP

    - -
    -
    - - - - -
    #define TJFLAG_BOTTOMUP
    -
    - -

    Rows in the packed-pixel source/destination image are stored in bottom-up (Windows, OpenGL) order rather than in top-down (X11) order.

    - -
    -
    - -

    ◆ TJFLAG_FASTDCT

    - -
    -
    - - - - -
    #define TJFLAG_FASTDCT
    -
    - -

    Use the fastest DCT/IDCT algorithm available.

    -

    The default if this flag is not specified is implementation-specific. For example, the implementation of the TurboJPEG API in libjpeg-turbo uses the fast algorithm by default when compressing, because this has been shown to have only a very slight effect on accuracy, but it uses the accurate algorithm when decompressing, because this has been shown to have a larger effect.

    - -
    -
    - -

    ◆ TJFLAG_FASTUPSAMPLE

    - -
    -
    - - - - -
    #define TJFLAG_FASTUPSAMPLE
    -
    - -

    When decompressing an image that was compressed using chrominance subsampling, use the fastest chrominance upsampling algorithm available.

    -

    The default is to use smooth upsampling, which creates a smooth transition between neighboring chrominance components in order to reduce upsampling artifacts in the decompressed image.

    - -
    -
    - -

    ◆ TJFLAG_LIMITSCANS

    - -
    -
    - - - - -
    #define TJFLAG_LIMITSCANS
    -
    - -

    Limit the number of progressive JPEG scans that the decompression and transform functions will process.

    -

    If a progressive JPEG image contains an unreasonably large number of scans, then this flag will cause the decompression and transform functions to return an error. The primary purpose of this is to allow security-critical applications to guard against an exploit of the progressive JPEG format described in this report.

    - -
    -
    - -

    ◆ TJFLAG_LOSSLESS

    + +

    ◆ TJ_NUMINIT

    - +
    #define TJFLAG_LOSSLESS#define TJ_NUMINIT
    -

    Generate a lossless JPEG image when compressing.

    -

    In most cases, compressing and decompressing lossless JPEG images is considerably slower than compressing and decompressing lossy JPEG images. Also note that the following features are not available with lossless JPEG images:

      -
    • Colorspace conversion
    • -
    • Chrominance subsampling
    • -
    • JPEG quality selection
    • -
    • DCT/IDCT algorithm selection
    • -
    • Progressive entropy coding
    • -
    • Arithmetic entropy coding
    • -
    • Compression from/decompression to planar YUV images
    • -
    • Decompression scaling
    • -
    • Lossless transformations
    • -
    +

    The number of initialization options.

    - -

    ◆ TJFLAG_NOREALLOC

    + +

    ◆ TJ_NUMPARAM

    - +
    #define TJFLAG_NOREALLOC#define TJ_NUMPARAM
    -

    Disable JPEG buffer (re)allocation.

    -

    If passed to one of the JPEG compression or transform functions, this flag will cause those functions to generate an error if the JPEG destination buffer is invalid or too small, rather than attempt to allocate or reallocate that buffer.

    +

    The number of parameters.

    - -

    ◆ TJFLAG_PROGRESSIVE

    + +

    ◆ TJ_NUMPF

    - +
    #define TJFLAG_PROGRESSIVE#define TJ_NUMPF
    -

    Use progressive entropy coding in JPEG images generated by the compression and transform functions.

    -

    Progressive entropy coding will generally improve compression relative to baseline entropy coding (the default), but it will reduce compression and decompression performance considerably. Can be combined with TJFLAG_ARITHMETIC.

    +

    The number of pixel formats.

    - -

    ◆ TJFLAG_STOPONWARNING

    + +

    ◆ TJ_NUMSAMP

    - +
    #define TJFLAG_STOPONWARNING#define TJ_NUMSAMP
    -

    Immediately discontinue the current compression/decompression/transform operation if a warning (non-fatal error) occurs.

    -

    The default behavior is to allow the operation to complete unless a fatal error is encountered.

    +

    The number of chrominance subsampling options.

    - -

    ◆ TJPAD

    + +

    ◆ TJ_NUMXOP

    - - - - - +
    #define TJPAD( width)#define TJ_NUMXOP
    -

    Pad the given width to the nearest multiple of 4.

    +

    The number of transform operations.

    @@ -687,7 +572,7 @@

    This option will enable arithmetic entropy coding in the JPEG image generated by this particular transform.

    -

    Arithmetic entropy coding will generally improve compression relative to Huffman entropy coding (the default), but it will reduce decompression performance considerably. Can be combined with TJXOPT_PROGRESSIVE.

    +

    Arithmetic entropy coding will generally improve compression relative to Huffman entropy coding (the default), but it will reduce decompression performance considerably. Can be combined with TJXOPT_PROGRESSIVE. Arithmetic entropy coding is currently only implemented for 8-bit samples.

    @@ -703,7 +588,7 @@

    -

    This option will prevent tjTransform() from copying any extra markers (including EXIF and ICC profile data) from the source image to the destination image.

    +

    This option will prevent tj3Transform() from copying any extra markers (including EXIF and ICC profile data) from the source image to the destination image.

    @@ -720,7 +605,7 @@

    This option will enable lossless cropping.

    -

    See tjTransform() for more information.

    +

    See tj3Transform() for more information.

    @@ -752,9 +637,26 @@

    -

    This option will prevent tjTransform() from outputting a JPEG image for this particular transform.

    +

    This option will prevent tj3Transform() from outputting a JPEG image for this particular transform.

    (This can be used in conjunction with a custom filter to capture the transformed DCT coefficients without transcoding them.)

    + + + +

    ◆ TJXOPT_OPTIMIZE

    + +
    +
    + + + + +
    #define TJXOPT_OPTIMIZE
    +
    + +

    This option will enable optimized baseline entropy coding in the JPEG image generated by this particular transform.

    +

    Optimized baseline entropy coding will improve compression slightly (generally 5% or less.)

    +
    @@ -769,7 +671,7 @@

    -

    This option will cause tjTransform() to return an error if the transform is not perfect.

    +

    This option will cause tj3Transform() to return an error if the transform is not perfect.

    Lossless transforms operate on MCU blocks, whose size depends on the level of chrominance subsampling used (see tjMCUWidth and tjMCUHeight.) If the image's width or height is not evenly divisible by the MCU block size, then there will be partial MCU blocks on the right and/or bottom edges. It is not possible to move these partial MCU blocks to the top or left of the image, so any transform that would require that is "imperfect." If this option is not specified, then any partial MCU blocks that cannot be transformed will be left in place, which will create odd-looking strips on the right or bottom edge of the image.

    @@ -787,7 +689,7 @@

    This option will enable progressive entropy coding in the JPEG image generated by this particular transform.

    -

    Progressive entropy coding will generally improve compression relative to baseline entropy coding (the default), but it will reduce decompression performance considerably. Can be combined with TJXOPT_ARITHMETIC.

    +

    Progressive entropy coding will generally improve compression relative to baseline entropy coding (the default), but it will reduce decompression performance considerably. Implies TJXOPT_OPTIMIZE. Can be combined with TJXOPT_ARITHMETIC.

    @@ -803,7 +705,7 @@

    -

    This option will cause tjTransform() to discard any partial MCU blocks that cannot be transformed.

    +

    This option will cause tj3Transform() to discard any partial MCU blocks that cannot be transformed.

    @@ -856,7 +758,7 @@

    EnumeratorTJCS_RGB 

    RGB colorspace.

    -

    When compressing the JPEG image, the R, G, and B components in the source image are reordered into image planes, but no colorspace conversion or subsampling is performed. RGB JPEG images can be decompressed to packed-pixel images with any of the extended RGB or grayscale pixel formats, but they cannot be decompressed to planar YUV images.

    +

    When compressing the JPEG image, the R, G, and B components in the source image are reordered into image planes, but no colorspace conversion or subsampling is performed. RGB JPEG images can be compressed from and decompressed to packed-pixel images with any of the extended RGB or grayscale pixel formats, but they cannot be compressed from or decompressed to planar YUV images.

    TJCS_YCbCr 

    YCbCr colorspace.

    YCbCr is not an absolute colorspace but rather a mathematical transformation of RGB designed solely for storage and transmission. YCbCr images must be converted to RGB before they can actually be displayed. In the YCbCr colorspace, the Y (luminance) component represents the black & white portion of the original image, and the Cb and Cr (chrominance) components represent the color portion of the original image. Originally, the analog equivalent of this transformation allowed the same signal to drive both black & white and color televisions, but JPEG images use YCbCr primarily because it allows the color data to be optionally subsampled for the purposes of reducing network or disk usage. YCbCr is the most common JPEG colorspace, and YCbCr JPEG images can be compressed from and decompressed to packed-pixel images with any of the extended RGB or grayscale pixel formats. YCbCr JPEG images can also be compressed from and decompressed to planar YUV images.

    @@ -865,7 +767,7 @@

    TJCS_CMYK 

    CMYK colorspace.

    -

    When compressing the JPEG image, the C, M, Y, and K components in the source image are reordered into image planes, but no colorspace conversion or subsampling is performed. CMYK JPEG images can only be decompressed to packed-pixel images with the CMYK pixel format.

    +

    When compressing the JPEG image, the C, M, Y, and K components in the source image are reordered into image planes, but no colorspace conversion or subsampling is performed. CMYK JPEG images can only be compressed from and decompressed to packed-pixel images with the CMYK pixel format.

    TJCS_YCCK 

    YCCK colorspace.

    YCCK (AKA "YCbCrK") is not an absolute colorspace but rather a mathematical transformation of CMYK designed solely for storage and transmission. It is to CMYK as YCbCr is to RGB. CMYK pixels can be reversibly transformed into YCCK, and as with YCbCr, the chrominance components in the YCCK pixels can be subsampled without incurring major perceptual loss. YCCK JPEG images can only be compressed from and decompressed to packed-pixel images with the CMYK pixel format.

    @@ -894,6 +796,208 @@

    +

    ◆ TJINIT

    + +
    +
    + + + + +
    enum TJINIT
    +
    + +

    Initialization options.

    + + + + +
    Enumerator
    TJINIT_COMPRESS 

    Initialize the TurboJPEG instance for compression.

    +
    TJINIT_DECOMPRESS 

    Initialize the TurboJPEG instance for decompression.

    +
    TJINIT_TRANSFORM 

    Initialize the TurboJPEG instance for lossless transformation (both compression and decompression.)

    +
    + +
    +
    + +

    ◆ TJPARAM

    + +
    +
    + + + + +
    enum TJPARAM
    +
    + +

    Parameters.

    + + + + + + + + + + + + + + + + + + + + + + + + +
    Enumerator
    TJPARAM_STOPONWARNING 

    Error handling behavior.

    +

    Value

      +
    • 0 [default] Allow the current compression/decompression/transform operation to complete unless a fatal error is encountered.
    • +
    • 1 Immediately discontinue the current compression/decompression/transform operation if a warning (non-fatal error) occurs.
    • +
    +
    TJPARAM_BOTTOMUP 

    Row order in packed-pixel source/destination images.

    +

    Value

      +
    • 0 [default] top-down (X11) order
    • +
    • 1 bottom-up (Windows, OpenGL) order
    • +
    +
    TJPARAM_NOREALLOC 

    JPEG destination buffer (re)allocation [compression, lossless transformation].

    +

    Value

      +
    • 0 [default] Attempt to allocate or reallocate the JPEG destination buffer as needed.
    • +
    • 1 Generate an error if the JPEG destination buffer is invalid or too small.
    • +
    +
    TJPARAM_QUALITY 

    Perceptual quality of lossy JPEG images [compression only].

    +

    Value

      +
    • 1-100 (1 = worst quality but best compression, 100 = best quality but worst compression) [no default; must be explicitly specified]
    • +
    +
    TJPARAM_SUBSAMP 

    Chrominance subsampling level.

    +

    The JPEG or YUV image uses (decompression, decoding) or will use (lossy compression, encoding) the specified level of chrominance subsampling.

    +

    Value

    +
    TJPARAM_JPEGWIDTH 

    JPEG width (in pixels) [decompression only, read-only].

    +
    TJPARAM_JPEGHEIGHT 

    JPEG height (in pixels) [decompression only, read-only].

    +
    TJPARAM_PRECISION 

    JPEG data precision (bits per sample) [decompression only, read-only].

    +

    The JPEG image uses the specified number of bits per sample.

    +

    Value

      +
    • 8, 12, or 16
    • +
    +

    12-bit data precision implies TJPARAM_OPTIMIZE.

    +
    TJPARAM_COLORSPACE 

    JPEG colorspace.

    +

    The JPEG image uses (decompression) or will use (lossy compression) the specified colorspace.

    +

    Value

      +
    • One of the JPEG colorspaces [default for lossy compression: automatically selected based on the subsampling level and pixel format]
    • +
    +
    TJPARAM_FASTUPSAMPLE 

    Chrominance upsampling algorithm [lossy decompression only].

    +

    Value

      +
    • 0 [default] Use smooth upsampling when decompressing a JPEG image that was compressed using chrominance subsampling. This creates a smooth transition between neighboring chrominance components in order to reduce upsampling artifacts in the decompressed image.
    • +
    • 1 Use the fastest chrominance upsampling algorithm available, which may combine upsampling with color conversion.
    • +
    +
    TJPARAM_FASTDCT 

    DCT/IDCT algorithm [lossy compression and decompression].

    +

    Value

      +
    • 0 [default] Use the most accurate DCT/IDCT algorithm available.
    • +
    • 1 Use the fastest DCT/IDCT algorithm available.
    • +
    +

    This parameter is provided mainly for backward compatibility with libjpeg, which historically implemented several different DCT/IDCT algorithms because of performance limitations with 1990s CPUs. In the libjpeg-turbo implementation of the TurboJPEG API:

      +
    • The "fast" and "accurate" DCT/IDCT algorithms perform similarly on modern x86/x86-64 CPUs that support AVX2 instructions.
    • +
    • The "fast" algorithm is generally only about 5-15% faster than the "accurate" algorithm on other types of CPUs.
    • +
    • The difference in accuracy between the "fast" and "accurate" algorithms is the most pronounced at JPEG quality levels above 90 and tends to be more pronounced with decompression than with compression.
    • +
    • The "fast" algorithm degrades and is not fully accelerated for JPEG quality levels above 97, so it will be slower than the "accurate" algorithm.
    • +
    +
    TJPARAM_OPTIMIZE 

    Optimized baseline entropy coding [lossy compression only].

    +

    Value

      +
    • 0 [default] The JPEG image will use the default Huffman tables.
    • +
    • 1 Optimal Huffman tables will be computed for the JPEG image. For lossless transformation, this can also be specified using TJXOPT_OPTIMIZE.
    • +
    +

    Optimized baseline entropy coding will improve compression slightly (generally 5% or less), but it will reduce compression performance considerably.

    +
    TJPARAM_PROGRESSIVE 

    Progressive entropy coding.

    +

    Value

      +
    • 0 [default for compression, lossless transformation] The lossy JPEG image uses (decompression) or will use (compression, lossless transformation) baseline entropy coding.
    • +
    • 1 The lossy JPEG image uses (decompression) or will use (compression, lossless transformation) progressive entropy coding. For lossless transformation, this can also be specified using TJXOPT_PROGRESSIVE.
    • +
    +

    Progressive entropy coding will generally improve compression relative to baseline entropy coding, but it will reduce compression and decompression performance considerably. Implies TJPARAM_OPTIMIZE. Can be combined with TJPARAM_ARITHMETIC.

    +
    TJPARAM_SCANLIMIT 

    Progressive JPEG scan limit for lossy JPEG images [decompression, lossless transformation].

    +

    Setting this parameter will cause the decompression and transform functions to return an error if the number of scans in a progressive JPEG image exceeds the specified limit. The primary purpose of this is to allow security-critical applications to guard against an exploit of the progressive JPEG format described in this report.

    +

    Value

      +
    • maximum number of progressive JPEG scans that the decompression and transform functions will process [default: 0 (no limit)]
    • +
    +
    See also
    TJPARAM_PROGRESSIVE
    +
    TJPARAM_ARITHMETIC 

    Arithmetic entropy coding.

    +

    Value

      +
    • 0 [default for compression, lossless transformation] The lossy JPEG image uses (decompression) or will use (compression, lossless transformation) Huffman entropy coding.
    • +
    • 1 The lossy JPEG image uses (decompression) or will use (compression, lossless transformation) arithmetic entropy coding. For lossless transformation, this can also be specified using TJXOPT_ARITHMETIC.
    • +
    +

    Arithmetic entropy coding will generally improve compression relative to Huffman entropy coding, but it will reduce compression and decompression performance considerably. Can be combined with TJPARAM_PROGRESSIVE. Arithmetic entropy coding is currently only implemented for 8-bit samples.

    +
    TJPARAM_LOSSLESS 

    Lossless JPEG.

    +

    Value

      +
    • 0 [default for compression] The JPEG image is (decompression) or will be (compression) lossy/DCT-based.
    • +
    • 1 The JPEG image is (decompression) or will be (compression) lossless/predictive.
    • +
    +

    In most cases, compressing and decompressing lossless JPEG images is considerably slower than compressing and decompressing lossy JPEG images. Also note that the following features are not available with lossless JPEG images:

      +
    • Colorspace conversion (lossless JPEG images always use TJCS_RGB, TJCS_GRAY, or TJCS_CMYK, depending on the pixel format of the source image)
    • +
    • Chrominance subsampling (lossless JPEG images always use TJSAMP_444)
    • +
    • JPEG quality selection
    • +
    • DCT/IDCT algorithm selection
    • +
    • Progressive entropy coding
    • +
    • Arithmetic entropy coding
    • +
    • Compression from/decompression to planar YUV images
    • +
    • Decompression scaling
    • +
    • Lossless transformation
    • +
    +
    See also
    TJPARAM_LOSSLESSPSV, TJPARAM_LOSSLESSPT
    +
    TJPARAM_LOSSLESSPSV 

    Lossless JPEG predictor selection value (PSV)

    +

    Value

      +
    • 1-7 [default for compression: 1]
    • +
    +
    See also
    TJPARAM_LOSSLESS
    +
    TJPARAM_LOSSLESSPT 

    Lossless JPEG point transform (Pt)

    +

    Value

      +
    • 0 through precision - 1, where precision is the JPEG data precision in bits [default for compression: 0]
    • +
    +

    A point transform value of 0 is necessary in order to generate a fully lossless JPEG image. (A non-zero point transform value right-shifts the input samples by the specified number of bits, which is effectively a form of lossy color quantization.)

    +
    See also
    TJPARAM_LOSSLESS, TJPARAM_PRECISION
    +
    TJPARAM_RESTARTBLOCKS 

    JPEG restart marker interval in MCU blocks (lossy) or samples (lossless) [compression only].

    +

    The nature of entropy coding is such that a corrupt JPEG image cannot be decompressed beyond the point of corruption unless it contains restart markers. A restart marker stops and restarts the entropy coding algorithm so that, if a JPEG image is corrupted, decompression can resume at the next marker. Thus, adding more restart markers improves the fault tolerance of the JPEG image, but adding too many restart markers can adversely affect the compression ratio and performance.

    +

    Value

      +
    • the number of MCU blocks or samples between each restart marker [default: 0 (no restart markers)]
    • +
    +

    Setting this parameter to a non-zero value sets TJPARAM_RESTARTROWS to 0.

    +
    TJPARAM_RESTARTROWS 

    JPEG restart marker interval in MCU rows (lossy) or sample rows (lossless) [compression only].

    +

    See TJPARAM_RESTARTBLOCKS for a description of restart markers.

    +

    Value

      +
    • the number of MCU rows or sample rows between each restart marker [default: 0 (no restart markers)]
    • +
    +

    Setting this parameter to a non-zero value sets TJPARAM_RESTARTBLOCKS to 0.

    +
    TJPARAM_XDENSITY 

    JPEG horizontal pixel density.

    +

    Value

      +
    • The JPEG image has (decompression) or will have (compression) the specified horizontal pixel density [default for compression: 1].
    • +
    +

    This value is stored in or read from the JPEG header. It does not affect the contents of the JPEG image. Note that this parameter is set by tj3LoadImage8() when loading a Windows BMP file that contains pixel density information, and the value of this parameter is stored to a Windows BMP file by tj3SaveImage8() if the value of #TJPARAM_DENSITYUNIT is 2.

    +
    See also
    TJPARAM_DENSITYUNIT
    +
    TJPARAM_YDENSITY 

    JPEG vertical pixel density.

    +

    Value

      +
    • The JPEG image has (decompression) or will have (compression) the specified vertical pixel density [default for compression: 1].
    • +
    +

    This value is stored in or read from the JPEG header. It does not affect the contents of the JPEG image. Note that this parameter is set by tj3LoadImage8() when loading a Windows BMP file that contains pixel density information, and the value of this parameter is stored to a Windows BMP file by tj3SaveImage8() if the value of #TJPARAM_DENSITYUNIT is 2.

    +
    See also
    TJPARAM_DENSITYUNIT
    +
    TJPARAM_DENSITYUNITS 

    JPEG pixel density units.

    +

    Value

      +
    • 0 [default for compression] The pixel density of the JPEG image is expressed (decompression) or will be expressed (compression) in unknown units.
    • +
    • 1 The pixel density of the JPEG image is expressed (decompression) or will be expressed (compression) in units of pixels/inch.
    • +
    • 2 The pixel density of the JPEG image is expressed (decompression) or will be expressed (compression) in units of pixels/cm.
    • +
    +

    This value is stored in or read from the JPEG header. It does not affect the contents of the JPEG image. Note that this parameter is set by tj3LoadImage8() when loading a Windows BMP file that contains pixel density information, and the value of this parameter is stored to a Windows BMP file by tj3SaveImage8() if the value is 2.

    +
    See also
    TJPARAM_XDENSITY, TJPARAM_YDENSITY
    +
    +
    @@ -911,43 +1015,43 @@

    EnumeratorTJPF_RGB 

    RGB pixel format.

    -

    The red, green, and blue components in the image are stored in 3-byte pixels in the order R, G, B from lowest to highest byte address within each pixel.

    +

    The red, green, and blue components in the image are stored in 3-sample pixels in the order R, G, B from lowest to highest memory address within each pixel.

    TJPF_BGR 

    BGR pixel format.

    -

    The red, green, and blue components in the image are stored in 3-byte pixels in the order B, G, R from lowest to highest byte address within each pixel.

    +

    The red, green, and blue components in the image are stored in 3-sample pixels in the order B, G, R from lowest to highest memory address within each pixel.

    TJPF_RGBX 

    RGBX pixel format.

    -

    The red, green, and blue components in the image are stored in 4-byte pixels in the order R, G, B from lowest to highest byte address within each pixel. The X component is ignored when compressing and undefined when decompressing.

    +

    The red, green, and blue components in the image are stored in 4-sample pixels in the order R, G, B from lowest to highest memory address within each pixel. The X component is ignored when compressing and undefined when decompressing.

    TJPF_BGRX 

    BGRX pixel format.

    -

    The red, green, and blue components in the image are stored in 4-byte pixels in the order B, G, R from lowest to highest byte address within each pixel. The X component is ignored when compressing and undefined when decompressing.

    +

    The red, green, and blue components in the image are stored in 4-sample pixels in the order B, G, R from lowest to highest memory address within each pixel. The X component is ignored when compressing and undefined when decompressing.

    TJPF_XBGR 

    XBGR pixel format.

    -

    The red, green, and blue components in the image are stored in 4-byte pixels in the order R, G, B from highest to lowest byte address within each pixel. The X component is ignored when compressing and undefined when decompressing.

    +

    The red, green, and blue components in the image are stored in 4-sample pixels in the order R, G, B from highest to lowest memory address within each pixel. The X component is ignored when compressing and undefined when decompressing.

    TJPF_XRGB 

    XRGB pixel format.

    -

    The red, green, and blue components in the image are stored in 4-byte pixels in the order B, G, R from highest to lowest byte address within each pixel. The X component is ignored when compressing and undefined when decompressing.

    +

    The red, green, and blue components in the image are stored in 4-sample pixels in the order B, G, R from highest to lowest memory address within each pixel. The X component is ignored when compressing and undefined when decompressing.

    TJPF_GRAY 

    Grayscale pixel format.

    -

    Each 1-byte pixel represents a luminance (brightness) level from 0 to 255.

    +

    Each 1-sample pixel represents a luminance (brightness) level from 0 to the maximum sample value (255 for 8-bit samples, 4095 for 12-bit samples, and 65535 for 16-bit samples.)

    TJPF_RGBA 

    RGBA pixel format.

    -

    This is the same as TJPF_RGBX, except that when decompressing, the X component is guaranteed to be 0xFF, which can be interpreted as an opaque alpha channel.

    +

    This is the same as TJPF_RGBX, except that when decompressing, the X component is guaranteed to be equal to the maximum sample value, which can be interpreted as an opaque alpha channel.

    TJPF_BGRA 

    BGRA pixel format.

    -

    This is the same as TJPF_BGRX, except that when decompressing, the X component is guaranteed to be 0xFF, which can be interpreted as an opaque alpha channel.

    +

    This is the same as TJPF_BGRX, except that when decompressing, the X component is guaranteed to be equal to the maximum sample value, which can be interpreted as an opaque alpha channel.

    TJPF_ABGR 

    ABGR pixel format.

    -

    This is the same as TJPF_XBGR, except that when decompressing, the X component is guaranteed to be 0xFF, which can be interpreted as an opaque alpha channel.

    +

    This is the same as TJPF_XBGR, except that when decompressing, the X component is guaranteed to be equal to the maximum sample value, which can be interpreted as an opaque alpha channel.

    TJPF_ARGB 

    ARGB pixel format.

    -

    This is the same as TJPF_XRGB, except that when decompressing, the X component is guaranteed to be 0xFF, which can be interpreted as an opaque alpha channel.

    +

    This is the same as TJPF_XRGB, except that when decompressing, the X component is guaranteed to be equal to the maximum sample value, which can be interpreted as an opaque alpha channel.

    TJPF_CMYK 

    CMYK pixel format.

    Unlike RGB, which is an additive color model used primarily for display, CMYK (Cyan/Magenta/Yellow/Key) is a subtractive color model used primarily for printing. In the CMYK color model, the value of each color component typically corresponds to an amount of cyan, magenta, yellow, or black ink that is applied to a white background. In order to convert between CMYK and RGB, it is necessary to use a color management system (CMS.) A CMS will attempt to map colors within the printer's gamut to perceptually similar colors in the display's gamut and vice versa, but the mapping is typically not 1:1 or reversible, nor can it be defined with a simple formula. Thus, such a conversion is out of scope for a codec library. However, the TurboJPEG API allows for compressing packed-pixel CMYK images into YCCK JPEG images (see TJCS_YCCK) and decompressing YCCK JPEG images into packed-pixel CMYK images.

    TJPF_UNKNOWN 

    Unknown pixel format.

    -

    Currently this is only used by tjLoadImage().

    +

    Currently this is only used by tj3LoadImage8(), tj3LoadImage12(), and tj3LoadImage16().

    @@ -988,6 +1092,13 @@

    Note
    4:1:1 subsampling is not fully accelerated in libjpeg-turbo.

    +TJSAMP_UNKNOWN 

    Unknown subsampling.

    +

    The JPEG image uses an unusual type of chrominance subsampling. Such images can be decompressed into packed-pixel images, but they cannot be

      +
    • decompressed into planar YUV images,
    • +
    • losslessly transformed if TJXOPT_CROP is specified, or
    • +
    • partially decompressed using a cropping region.
    • +
    + @@ -1004,44 +1115,44 @@

    -

    Transform operations for tjTransform()

    +

    Transform operations for tj3Transform()

    -
    Enumerator
    TJXOP_NONE 

    Do not transform the position of the image pixels.

    TJXOP_HFLIP 

    Flip (mirror) image horizontally.

    -

    This transform is imperfect if there are any partial MCU blocks on the right edge (see TJXOPT_PERFECT.)

    +

    This transform is imperfect if there are any partial MCU blocks on the right edge (see TJXOPT_PERFECT.)

    TJXOP_VFLIP 

    Flip (mirror) image vertically.

    -

    This transform is imperfect if there are any partial MCU blocks on the bottom edge (see TJXOPT_PERFECT.)

    +

    This transform is imperfect if there are any partial MCU blocks on the bottom edge (see TJXOPT_PERFECT.)

    TJXOP_TRANSPOSE 

    Transpose image (flip/mirror along upper left to lower right axis.) This transform is always perfect.

    TJXOP_TRANSVERSE 

    Transverse transpose image (flip/mirror along upper right to lower left axis.) This transform is imperfect if there are any partial MCU blocks in the image (see TJXOPT_PERFECT.)

    +
    TJXOP_TRANSVERSE 

    Transverse transpose image (flip/mirror along upper right to lower left axis.) This transform is imperfect if there are any partial MCU blocks in the image (see TJXOPT_PERFECT.)

    TJXOP_ROT90 

    Rotate image clockwise by 90 degrees.

    -

    This transform is imperfect if there are any partial MCU blocks on the bottom edge (see TJXOPT_PERFECT.)

    +

    This transform is imperfect if there are any partial MCU blocks on the bottom edge (see TJXOPT_PERFECT.)

    TJXOP_ROT180 

    Rotate image 180 degrees.

    -

    This transform is imperfect if there are any partial MCU blocks in the image (see TJXOPT_PERFECT.)

    +

    This transform is imperfect if there are any partial MCU blocks in the image (see TJXOPT_PERFECT.)

    TJXOP_ROT270 

    Rotate image counter-clockwise by 90 degrees.

    -

    This transform is imperfect if there are any partial MCU blocks on the right edge (see TJXOPT_PERFECT.)

    +

    This transform is imperfect if there are any partial MCU blocks on the right edge (see TJXOPT_PERFECT.)

    Function Documentation

    - -

    ◆ tjAlloc()

    + +

    ◆ tj3Alloc()

    - + - + @@ -1049,7 +1160,7 @@

    Allocate a byte buffer for use with TurboJPEG.

    -

    You should always use this function to allocate the JPEG destination buffer(s) for the compression and transform functions unless you are disabling automatic buffer (re)allocation (by setting TJFLAG_NOREALLOC.)

    +

    You should always use this function to allocate the JPEG destination buffer(s) for the compression and transform functions unless you are disabling automatic buffer (re)allocation (by setting TJPARAM_NOREALLOC.)

    Parameters

    DLLEXPORT unsigned char* tjAlloc DLLEXPORT void* tj3Alloc (int size_t  bytes)
    @@ -1057,33 +1168,63 @@

    Returns
    a pointer to a newly-allocated buffer with the specified number of bytes.
    -
    See also
    tjFree()
    +
    See also
    tj3Free()
    - -

    ◆ tjBufSize()

    + +

    ◆ tj3Compress12()

    bytesthe number of bytes to allocate
    - + - - + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1093,29 +1234,50 @@

    -

    The maximum size of the buffer (in bytes) required to hold a JPEG image with the given parameters.

    -

    The number of bytes returned by this function is larger than the size of the uncompressed source image. The reason for this is that the JPEG format uses 16-bit coefficients, so it is possible for a very high-quality source image with very high-frequency content to expand rather than compress when converted to the JPEG format. Such images represent very rare corner cases, but since there is no way to predict the size of a JPEG image prior to compression, the corner cases have to be handled.

    +

    Compress a 12-bit-per-sample packed-pixel RGB, grayscale, or CMYK image into a 12-bit-per-sample JPEG image.

    Parameters

    DLLEXPORT unsigned long tjBufSize DLLEXPORT int tj3Compress12 (int width, tjhandle handle,
    int height, const short * srcBuf,
    int jpegSubsamp width,
    int pitch,
    int height,
    int pixelFormat,
    unsigned char ** jpegBuf,
    size_t * jpegSize 
    - - - + + + + + + + +
    widthwidth (in pixels) of the image
    heightheight (in pixels) of the image
    jpegSubsampthe level of chrominance subsampling to be used when generating the JPEG image (see Chrominance subsampling options.)
    handlehandle to a TurboJPEG instance that has been initialized for compression
    srcBufpointer to a buffer containing a packed-pixel RGB, grayscale, or CMYK source image to be compressed. This buffer should normally be pitch * height samples in size. However, you can also use this parameter to compress from a specific region of a larger buffer.
    widthwidth (in pixels) of the source image
    pitchsamples per row in the source image. Normally this should be width * tjPixelSize[pixelFormat], if the image is unpadded. (Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].) However, you can also use this parameter to specify the row alignment/padding of the source image, to skip rows, or to compress from a specific region of a larger buffer.
    heightheight (in pixels) of the source image
    pixelFormatpixel format of the source image (see Pixel formats.)
    jpegBufaddress of a pointer to a byte buffer that will receive the JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to accommodate the size of the JPEG image. Thus, you can choose to:
      +
    1. pre-allocate the JPEG buffer with an arbitrary size using tj3Alloc() and let TurboJPEG grow the buffer as needed,
    2. +
    3. set *jpegBuf to NULL to tell TurboJPEG to allocate the buffer for you, or
    4. +
    5. pre-allocate the buffer to a "worst case" size determined by calling tj3JPEGBufSize(). This should ensure that the buffer never has to be re-allocated. (Setting TJPARAM_NOREALLOC guarantees that it won't be.)
    6. +
    +If you choose option 1, then *jpegSize should be set to the size of your pre-allocated buffer. In any case, unless you have set TJPARAM_NOREALLOC, you should always check *jpegBuf upon return from this function, as it may have changed.
    jpegSizepointer to a size_t variable that holds the size of the JPEG buffer. If *jpegBuf points to a pre-allocated buffer, then *jpegSize should be set to the size of the buffer. Upon return, *jpegSize will contain the size of the JPEG image (in bytes.) If *jpegBuf points to a JPEG buffer that is being reused from a previous call to one of the JPEG compression functions, then *jpegSize is ignored.

    -
    Returns
    the maximum size of the buffer (in bytes) required to hold the image, or -1 if the arguments are out of bounds.
    +
    Returns
    0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
    - -

    ◆ tjBufSizeYUV2()

    + +

    ◆ tj3Compress16()

    - + + + + + + + + + + + + + @@ -1123,7 +1285,7 @@

    - + @@ -1135,7 +1297,19 @@

    - + + + + + + + + + + + + + @@ -1145,28 +1319,37 @@

    -

    The size of the buffer (in bytes) required to hold a unified planar YUV image with the given parameters.

    +

    Compress a 16-bit-per-sample packed-pixel RGB, grayscale, or CMYK image into a 16-bit-per-sample lossless JPEG image.

    Parameters

    DLLEXPORT unsigned long tjBufSizeYUV2 DLLEXPORT int tj3Compress16 (tjhandle handle,
    const unsigned short * srcBuf,
    int  width,
    int align, pitch,
    int subsamp pixelFormat,
    unsigned char ** jpegBuf,
    size_t * jpegSize 
    - - - - + + + + + + + +
    widthwidth (in pixels) of the image
    alignrow alignment (in bytes) of the image (must be a power of 2.) Setting this parameter to n specifies that each row in each plane of the image will be padded to the nearest multiple of n bytes (1 = unpadded.)
    heightheight (in pixels) of the image
    subsamplevel of chrominance subsampling in the image (see Chrominance subsampling options.)
    handlehandle to a TurboJPEG instance that has been initialized for compression
    srcBufpointer to a buffer containing a packed-pixel RGB, grayscale, or CMYK source image to be compressed. This buffer should normally be pitch * height samples in size. However, you can also use this parameter to compress from a specific region of a larger buffer.
    widthwidth (in pixels) of the source image
    pitchsamples per row in the source image. Normally this should be width * tjPixelSize[pixelFormat], if the image is unpadded. (Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].) However, you can also use this parameter to specify the row alignment/padding of the source image, to skip rows, or to compress from a specific region of a larger buffer.
    heightheight (in pixels) of the source image
    pixelFormatpixel format of the source image (see Pixel formats.)
    jpegBufaddress of a pointer to a byte buffer that will receive the JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to accommodate the size of the JPEG image. Thus, you can choose to:
      +
    1. pre-allocate the JPEG buffer with an arbitrary size using tj3Alloc() and let TurboJPEG grow the buffer as needed,
    2. +
    3. set *jpegBuf to NULL to tell TurboJPEG to allocate the buffer for you, or
    4. +
    5. pre-allocate the buffer to a "worst case" size determined by calling tj3JPEGBufSize(). This should ensure that the buffer never has to be re-allocated. (Setting TJPARAM_NOREALLOC guarantees that it won't be.)
    6. +
    +If you choose option 1, then *jpegSize should be set to the size of your pre-allocated buffer. In any case, unless you have set TJPARAM_NOREALLOC, you should always check *jpegBuf upon return from this function, as it may have changed.
    jpegSizepointer to a size_t variable that holds the size of the JPEG buffer. If *jpegBuf points to a pre-allocated buffer, then *jpegSize should be set to the size of the buffer. Upon return, *jpegSize will contain the size of the JPEG image (in bytes.) If *jpegBuf points to a JPEG buffer that is being reused from a previous call to one of the JPEG compression functions, then *jpegSize is ignored.
    -
    Returns
    the size of the buffer (in bytes) required to hold the image, or -1 if the arguments are out of bounds.
    +
    Returns
    0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
    - -

    ◆ tjCompress2()

    + +

    ◆ tj3Compress8()

    - + @@ -1210,26 +1393,8 @@

    - - - - - - - - - - - - - - - - - - - - + + @@ -1239,40 +1404,37 @@

    -

    Compress a packed-pixel RGB, grayscale, or CMYK image into a JPEG image.

    +

    Compress an 8-bit-per-sample packed-pixel RGB, grayscale, or CMYK image into an 8-bit-per-sample JPEG image.

    Parameters

    DLLEXPORT int tjCompress2 DLLEXPORT int tj3Compress8 ( tjhandle  handle, unsigned long * jpegSize,
    int jpegSubsamp,
    int jpegQual,
    int flags size_t * jpegSize 
    - - + + - + - - - - +If you choose option 1, then *jpegSize should be set to the size of your pre-allocated buffer. In any case, unless you have set TJPARAM_NOREALLOC, you should always check *jpegBuf upon return from this function, as it may have changed. +
    handlea handle to a TurboJPEG compressor or transformer instance
    srcBufpointer to a buffer containing a packed-pixel RGB, grayscale, or CMYK source image to be compressed
    handlehandle to a TurboJPEG instance that has been initialized for compression
    srcBufpointer to a buffer containing a packed-pixel RGB, grayscale, or CMYK source image to be compressed. This buffer should normally be pitch * height samples in size. However, you can also use this parameter to compress from a specific region of a larger buffer.
    widthwidth (in pixels) of the source image
    pitchbytes per row in the source image. Normally this should be width * tjPixelSize[pixelFormat], if the image is unpadded, or TJPAD(width * tjPixelSize[pixelFormat]) if each row of the image is padded to the nearest multiple of 4 bytes, as is the case for Windows bitmaps. You can also be clever and use this parameter to skip rows, etc. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
    pitchsamples per row in the source image. Normally this should be width * tjPixelSize[pixelFormat], if the image is unpadded. (Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].) However, you can also use this parameter to specify the row alignment/padding of the source image, to skip rows, or to compress from a specific region of a larger buffer.
    heightheight (in pixels) of the source image
    pixelFormatpixel format of the source image (see Pixel formats.)
    jpegBufaddress of a pointer to a byte buffer that will receive the JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to accommodate the size of the JPEG image. Thus, you can choose to:
      -
    1. pre-allocate the JPEG buffer with an arbitrary size using tjAlloc() and let TurboJPEG grow the buffer as needed,
    2. +
    3. pre-allocate the JPEG buffer with an arbitrary size using tj3Alloc() and let TurboJPEG grow the buffer as needed,
    4. set *jpegBuf to NULL to tell TurboJPEG to allocate the buffer for you, or
    5. -
    6. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize(). This should ensure that the buffer never has to be re-allocated. (Setting TJFLAG_NOREALLOC guarantees that it won't be.)
    7. +
    8. pre-allocate the buffer to a "worst case" size determined by calling tj3JPEGBufSize(). This should ensure that the buffer never has to be re-allocated. (Setting TJPARAM_NOREALLOC guarantees that it won't be.)
    -If you choose option 1, then *jpegSize should be set to the size of your pre-allocated buffer. In any case, unless you have set TJFLAG_NOREALLOC, you should always check *jpegBuf upon return from this function, as it may have changed.
    jpegSizepointer to an unsigned long variable that holds the size of the JPEG buffer. If *jpegBuf points to a pre-allocated buffer, then *jpegSize should be set to the size of the buffer. Upon return, *jpegSize will contain the size of the JPEG image (in bytes.) If *jpegBuf points to a JPEG buffer that is being reused from a previous call to one of the JPEG compression functions, then *jpegSize is ignored.
    jpegSubsampthe level of chrominance subsampling to be used when generating the JPEG image (see Chrominance subsampling options.)
    jpegQualthe image quality of the generated JPEG image (1 = worst, 100 = best.) When generating a lossless JPEG image (see TJFLAG_LOSSLESS), jpegQual is psv * 10 + Pt, where psv is the predictor selection value (1-7) and Pt is the point transform (0-7). A point transform value of 0 is necessary in order to create a fully lossless JPEG image. (A non-zero point transform value right-shifts the input samples by the specified number of bits, which is effectively a form of lossy color quantization.)
    flagsthe bitwise OR of one or more of the flags
    jpegSizepointer to a size_t variable that holds the size of the JPEG buffer. If *jpegBuf points to a pre-allocated buffer, then *jpegSize should be set to the size of the buffer. Upon return, *jpegSize will contain the size of the JPEG image (in bytes.) If *jpegBuf points to a JPEG buffer that is being reused from a previous call to one of the JPEG compression functions, then *jpegSize is ignored.
    -
    Returns
    0 if successful, or -1 if an error occurred (see tjGetErrorStr2() and tjGetErrorCode().)
    +
    Returns
    0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
    - -

    ◆ tjCompressFromYUV()

    + +

    ◆ tj3CompressFromYUV8()

    - + @@ -1301,12 +1463,6 @@

    int 

    - - - - - - @@ -1316,20 +1472,8 @@

    - - - - - - - - - - - - - - + + @@ -1339,39 +1483,36 @@

    -

    Compress a unified planar YUV image into a JPEG image.

    +

    Compress an 8-bit-per-sample unified planar YUV image into an 8-bit-per-sample JPEG image.

    Parameters

    DLLEXPORT int tjCompressFromYUV DLLEXPORT int tj3CompressFromYUV8 ( tjhandle  handle, height,
    int subsamp,
    unsigned long * jpegSize,
    int jpegQual,
    int flags size_t * jpegSize 
    - - + + - - - - +If you choose option 1, then *jpegSize should be set to the size of your pre-allocated buffer. In any case, unless you have set TJPARAM_NOREALLOC, you should always check *jpegBuf upon return from this function, as it may have changed. +
    handlea handle to a TurboJPEG compressor or transformer instance
    srcBufpointer to a buffer containing a unified planar YUV source image to be compressed. The size of this buffer should match the value returned by tjBufSizeYUV2() for the given image width, height, row alignment, and level of chrominance subsampling. The Y, U (Cb), and V (Cr) image planes should be stored sequentially in the buffer. (Refer to YUV Image Format Notes.)
    handlehandle to a TurboJPEG instance that has been initialized for compression
    srcBufpointer to a buffer containing a unified planar YUV source image to be compressed. The size of this buffer should match the value returned by tj3YUVBufSize() for the given image width, height, row alignment, and level of chrominance subsampling (see TJPARAM_SUBSAMP.) The Y, U (Cb), and V (Cr) image planes should be stored sequentially in the buffer. (Refer to YUV Image Format Notes.)
    widthwidth (in pixels) of the source image. If the width is not an even multiple of the MCU block width (see tjMCUWidth), then an intermediate buffer copy will be performed.
    alignrow alignment (in bytes) of the source image (must be a power of 2.) Setting this parameter to n indicates that each row in each plane of the source image is padded to the nearest multiple of n bytes (1 = unpadded.)
    heightheight (in pixels) of the source image. If the height is not an even multiple of the MCU block height (see tjMCUHeight), then an intermediate buffer copy will be performed.
    subsampthe level of chrominance subsampling used in the source image (see Chrominance subsampling options.)
    jpegBufaddress of a pointer to a byte buffer that will receive the JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to accommodate the size of the JPEG image. Thus, you can choose to:
      -
    1. pre-allocate the JPEG buffer with an arbitrary size using tjAlloc() and let TurboJPEG grow the buffer as needed,
    2. +
    3. pre-allocate the JPEG buffer with an arbitrary size using tj3Alloc() and let TurboJPEG grow the buffer as needed,
    4. set *jpegBuf to NULL to tell TurboJPEG to allocate the buffer for you, or
    5. -
    6. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize(). This should ensure that the buffer never has to be re-allocated. (Setting TJFLAG_NOREALLOC guarantees that it won't be.)
    7. +
    8. pre-allocate the buffer to a "worst case" size determined by calling tj3JPEGBufSize(). This should ensure that the buffer never has to be re-allocated. (Setting TJPARAM_NOREALLOC guarantees that it won't be.)
    -If you choose option 1, then *jpegSize should be set to the size of your pre-allocated buffer. In any case, unless you have set TJFLAG_NOREALLOC, you should always check *jpegBuf upon return from this function, as it may have changed.
    jpegSizepointer to an unsigned long variable that holds the size of the JPEG buffer. If *jpegBuf points to a pre-allocated buffer, then *jpegSize should be set to the size of the buffer. Upon return, *jpegSize will contain the size of the JPEG image (in bytes.) If *jpegBuf points to a JPEG buffer that is being reused from a previous call to one of the JPEG compression functions, then *jpegSize is ignored.
    jpegQualthe image quality of the generated JPEG image (1 = worst, 100 = best)
    flagsthe bitwise OR of one or more of the flags
    jpegSizepointer to a size_t variable that holds the size of the JPEG buffer. If *jpegBuf points to a pre-allocated buffer, then *jpegSize should be set to the size of the buffer. Upon return, *jpegSize will contain the size of the JPEG image (in bytes.) If *jpegBuf points to a JPEG buffer that is being reused from a previous call to one of the JPEG compression functions, then *jpegSize is ignored.
    -
    Returns
    0 if successful, or -1 if an error occurred (see tjGetErrorStr2() and tjGetErrorCode().)
    +
    Returns
    0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
    - -

    ◆ tjCompressFromYUVPlanes()

    + +

    ◆ tj3CompressFromYUVPlanes8()

    - + @@ -1379,7 +1520,7 @@

    - + @@ -1400,12 +1541,6 @@

    int 

    - - - - - - @@ -1415,20 +1550,8 @@

    - - - - - - - - - - - - - - + + @@ -1438,39 +1561,36 @@

    -

    Compress a set of Y, U (Cb), and V (Cr) image planes into a JPEG image.

    +

    Compress a set of 8-bit-per-sample Y, U (Cb), and V (Cr) image planes into an 8-bit-per-sample JPEG image.

    Parameters

    DLLEXPORT int tjCompressFromYUVPlanes DLLEXPORT int tj3CompressFromYUVPlanes8 ( tjhandle  handle, const unsigned char ** const unsigned char *const *  srcPlanes,
    height,
    int subsamp,
    unsigned long * jpegSize,
    int jpegQual,
    int flags size_t * jpegSize 
    - - + + - - - - +If you choose option 1, then *jpegSize should be set to the size of your pre-allocated buffer. In any case, unless you have set TJPARAM_NOREALLOC, you should always check *jpegBuf upon return from this function, as it may have changed. +
    handlea handle to a TurboJPEG compressor or transformer instance
    srcPlanesan array of pointers to Y, U (Cb), and V (Cr) image planes (or just a Y plane, if compressing a grayscale image) that contain a YUV source image to be compressed. These planes can be contiguous or non-contiguous in memory. The size of each plane should match the value returned by tjPlaneSizeYUV() for the given image width, height, strides, and level of chrominance subsampling. Refer to YUV Image Format Notes for more details.
    handlehandle to a TurboJPEG instance that has been initialized for compression
    srcPlanesan array of pointers to Y, U (Cb), and V (Cr) image planes (or just a Y plane, if compressing a grayscale image) that contain a YUV source image to be compressed. These planes can be contiguous or non-contiguous in memory. The size of each plane should match the value returned by tj3YUVPlaneSize() for the given image width, height, strides, and level of chrominance subsampling (see TJPARAM_SUBSAMP.) Refer to YUV Image Format Notes for more details.
    widthwidth (in pixels) of the source image. If the width is not an even multiple of the MCU block width (see tjMCUWidth), then an intermediate buffer copy will be performed.
    stridesan array of integers, each specifying the number of bytes per row in the corresponding plane of the YUV source image. Setting the stride for any plane to 0 is the same as setting it to the plane width (see YUV Image Format Notes.) If strides is NULL, then the strides for all planes will be set to their respective plane widths. You can adjust the strides in order to specify an arbitrary amount of row padding in each plane or to create a JPEG image from a subregion of a larger planar YUV image.
    heightheight (in pixels) of the source image. If the height is not an even multiple of the MCU block height (see tjMCUHeight), then an intermediate buffer copy will be performed.
    subsampthe level of chrominance subsampling used in the source image (see Chrominance subsampling options.)
    jpegBufaddress of a pointer to a byte buffer that will receive the JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to accommodate the size of the JPEG image. Thus, you can choose to:
      -
    1. pre-allocate the JPEG buffer with an arbitrary size using tjAlloc() and let TurboJPEG grow the buffer as needed,
    2. +
    3. pre-allocate the JPEG buffer with an arbitrary size using tj3Alloc() and let TurboJPEG grow the buffer as needed,
    4. set *jpegBuf to NULL to tell TurboJPEG to allocate the buffer for you, or
    5. -
    6. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize(). This should ensure that the buffer never has to be re-allocated. (Setting TJFLAG_NOREALLOC guarantees that it won't be.)
    7. +
    8. pre-allocate the buffer to a "worst case" size determined by calling tj3JPEGBufSize(). This should ensure that the buffer never has to be re-allocated. (Setting TJPARAM_NOREALLOC guarantees that it won't be.)
    -If you choose option 1, then *jpegSize should be set to the size of your pre-allocated buffer. In any case, unless you have set TJFLAG_NOREALLOC, you should always check *jpegBuf upon return from this function, as it may have changed.
    jpegSizepointer to an unsigned long variable that holds the size of the JPEG buffer. If *jpegBuf points to a pre-allocated buffer, then *jpegSize should be set to the size of the buffer. Upon return, *jpegSize will contain the size of the JPEG image (in bytes.) If *jpegBuf points to a JPEG buffer that is being reused from a previous call to one of the JPEG compression functions, then *jpegSize is ignored.
    jpegQualthe image quality of the generated JPEG image (1 = worst, 100 = best)
    flagsthe bitwise OR of one or more of the flags
    jpegSizepointer to a size_t variable that holds the size of the JPEG buffer. If *jpegBuf points to a pre-allocated buffer, then *jpegSize should be set to the size of the buffer. Upon return, *jpegSize will contain the size of the JPEG image (in bytes.) If *jpegBuf points to a JPEG buffer that is being reused from a previous call to one of the JPEG compression functions, then *jpegSize is ignored.

    -
    Returns
    0 if successful, or -1 if an error occurred (see tjGetErrorStr2() and tjGetErrorCode().)
    +
    Returns
    0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
    - -

    ◆ tjDecodeYUV()

    + +

    ◆ tj3DecodeYUV8()

    - + @@ -1487,12 +1607,6 @@

    int 

    - - - - - - @@ -1521,13 +1635,7 @@

    - - - - - - - + @@ -1537,35 +1645,33 @@

    -

    Decode a unified planar YUV image into a packed-pixel RGB or grayscale image.

    +

    Decode an 8-bit-per-sample unified planar YUV image into an 8-bit-per-sample packed-pixel RGB or grayscale image.

    This function performs color conversion (which is accelerated in the libjpeg-turbo implementation) but does not execute any of the other steps in the JPEG decompression process.

    Parameters

    DLLEXPORT int tjDecodeYUV DLLEXPORT int tj3DecodeYUV8 ( tjhandle  handle, align,
    int subsamp,
    int pixelFormat,
    int flags pixelFormat 
    - - + + - - + - + -
    handlea handle to a TurboJPEG decompressor or transformer instance
    srcBufpointer to a buffer containing a unified planar YUV source image to be decoded. The size of this buffer should match the value returned by tjBufSizeYUV2() for the given image width, height, row alignment, and level of chrominance subsampling. The Y, U (Cb), and V (Cr) image planes should be stored sequentially in the source buffer. (Refer to YUV Image Format Notes.)
    handlehandle to a TurboJPEG instance that has been initialized for decompression
    srcBufpointer to a buffer containing a unified planar YUV source image to be decoded. The size of this buffer should match the value returned by tj3YUVBufSize() for the given image width, height, row alignment, and level of chrominance subsampling (see TJPARAM_SUBSAMP.) The Y, U (Cb), and V (Cr) image planes should be stored sequentially in the source buffer. (Refer to YUV Image Format Notes.)
    alignrow alignment (in bytes) of the YUV source image (must be a power of 2.) Setting this parameter to n indicates that each row in each plane of the YUV source image is padded to the nearest multiple of n bytes (1 = unpadded.)
    subsampthe level of chrominance subsampling used in the YUV source image (see Chrominance subsampling options.)
    dstBufpointer to a buffer that will receive the packed-pixel decoded image. This buffer should normally be pitch * height bytes in size, but the dstBuf pointer can also be used to decode into a specific region of a larger buffer.
    dstBufpointer to a buffer that will receive the packed-pixel decoded image. This buffer should normally be pitch * height bytes in size. However, you can also use this parameter to decode into a specific region of a larger buffer.
    widthwidth (in pixels) of the source and destination images
    pitchbytes per row in the destination image. Normally this should be set to width * tjPixelSize[pixelFormat], if the destination image should be unpadded, or TJPAD(width * tjPixelSize[pixelFormat]) if each row of the destination image should be padded to the nearest multiple of 4 bytes, as is the case for Windows bitmaps. You can also be clever and use the pitch parameter to skip rows, etc. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
    pitchbytes per row in the destination image. Normally this should be set to width * tjPixelSize[pixelFormat], if the destination image should be unpadded. (Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].) However, you can also use this parameter to specify the row alignment/padding of the destination image, to skip rows, or to decode into a specific region of a larger buffer.
    heightheight (in pixels) of the source and destination images
    pixelFormatpixel format of the destination image (see Pixel formats.)
    flagsthe bitwise OR of one or more of the flags
    -
    Returns
    0 if successful, or -1 if an error occurred (see tjGetErrorStr2() and tjGetErrorCode().)
    +
    Returns
    0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
    - -

    ◆ tjDecodeYUVPlanes()

    + +

    ◆ tj3DecodeYUVPlanes8()

    - + @@ -1573,7 +1679,7 @@

    - + @@ -1582,12 +1688,6 @@

    const int * 

    - - - - - - @@ -1616,13 +1716,7 @@

    - - - - - - - + @@ -1632,35 +1726,33 @@

    -

    Decode a set of Y, U (Cb), and V (Cr) image planes into a packed-pixel RGB or grayscale image.

    +

    Decode a set of 8-bit-per-sample Y, U (Cb), and V (Cr) image planes into an 8-bit-per-sample packed-pixel RGB or grayscale image.

    This function performs color conversion (which is accelerated in the libjpeg-turbo implementation) but does not execute any of the other steps in the JPEG decompression process.

    Parameters

    DLLEXPORT int tjDecodeYUVPlanes DLLEXPORT int tj3DecodeYUVPlanes8 ( tjhandle  handle, const unsigned char ** const unsigned char *const *  srcPlanes,
    strides,
    int subsamp,
    int pixelFormat,
    int flags pixelFormat 
    - - + + - - + - + -
    handlea handle to a TurboJPEG decompressor or transformer instance
    srcPlanesan array of pointers to Y, U (Cb), and V (Cr) image planes (or just a Y plane, if decoding a grayscale image) that contain a YUV image to be decoded. These planes can be contiguous or non-contiguous in memory. The size of each plane should match the value returned by tjPlaneSizeYUV() for the given image width, height, strides, and level of chrominance subsampling. Refer to YUV Image Format Notes for more details.
    handlehandle to a TurboJPEG instance that has been initialized for decompression
    srcPlanesan array of pointers to Y, U (Cb), and V (Cr) image planes (or just a Y plane, if decoding a grayscale image) that contain a YUV image to be decoded. These planes can be contiguous or non-contiguous in memory. The size of each plane should match the value returned by tj3YUVPlaneSize() for the given image width, height, strides, and level of chrominance subsampling (see TJPARAM_SUBSAMP.) Refer to YUV Image Format Notes for more details.
    stridesan array of integers, each specifying the number of bytes per row in the corresponding plane of the YUV source image. Setting the stride for any plane to 0 is the same as setting it to the plane width (see YUV Image Format Notes.) If strides is NULL, then the strides for all planes will be set to their respective plane widths. You can adjust the strides in order to specify an arbitrary amount of row padding in each plane or to decode a subregion of a larger planar YUV image.
    subsampthe level of chrominance subsampling used in the YUV source image (see Chrominance subsampling options.)
    dstBufpointer to a buffer that will receive the packed-pixel decoded image. This buffer should normally be pitch * height bytes in size, but the dstBuf pointer can also be used to decode into a specific region of a larger buffer.
    dstBufpointer to a buffer that will receive the packed-pixel decoded image. This buffer should normally be pitch * height bytes in size. However, you can also use this parameter to decode into a specific region of a larger buffer.
    widthwidth (in pixels) of the source and destination images
    pitchbytes per row in the destination image. Normally this should be set to width * tjPixelSize[pixelFormat], if the destination image should be unpadded, or TJPAD(width * tjPixelSize[pixelFormat]) if each row of the destination image should be padded to the nearest multiple of 4 bytes, as is the case for Windows bitmaps. You can also be clever and use the pitch parameter to skip rows, etc. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
    pitchbytes per row in the destination image. Normally this should be set to width * tjPixelSize[pixelFormat], if the destination image should be unpadded. (Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].) However, you can also use this parameter to specify the row alignment/padding of the destination image, to skip rows, or to decode into a specific region of a larger buffer.
    heightheight (in pixels) of the source and destination images
    pixelFormatpixel format of the destination image (see Pixel formats.)
    flagsthe bitwise OR of one or more of the flags
    -
    Returns
    0 if successful, or -1 if an error occurred (see tjGetErrorStr2() and tjGetErrorCode().)
    +
    Returns
    0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
    - -

    ◆ tjDecompress2()

    + +

    ◆ tj3Decompress12()

    - + @@ -1674,21 +1766,15 @@

    - + - + - - - - - - @@ -1699,19 +1785,7 @@

    - - - - - - - - - - - - - + @@ -1721,33 +1795,31 @@

    -

    Decompress a JPEG image into a packed-pixel RGB, grayscale, or CMYK image.

    +

    Decompress a 12-bit-per-sample JPEG image into a 12-bit-per-sample packed-pixel RGB, grayscale, or CMYK image.

    +

    The parameters that describe the JPEG image will be set when this function returns.

    Parameters

    DLLEXPORT int tjDecompress2 DLLEXPORT int tj3Decompress12 ( tjhandle  handle, unsigned long size_t  jpegSize,
    unsigned char * short *  dstBuf,
    int width,
    int height,
    int pixelFormat,
    int flags pixelFormat 
    - + - - - - + + -
    handlea handle to a TurboJPEG decompressor or transformer instance
    handlehandle to a TurboJPEG instance that has been initialized for decompression
    jpegBufpointer to a byte buffer containing the JPEG image to decompress
    jpegSizesize of the JPEG image (in bytes)
    dstBufpointer to a buffer that will receive the packed-pixel decompressed image. This buffer should normally be pitch * scaledHeight bytes in size, where scaledHeight can be determined by calling TJSCALED() with the JPEG image height and one of the scaling factors returned by tjGetScalingFactors(). The dstBuf pointer may also be used to decompress into a specific region of a larger buffer.
    widthdesired width (in pixels) of the destination image. If this is different than the width of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired width. If width is set to 0, then only the height will be considered when determining the scaled image size.
    pitchbytes per row in the destination image. Normally this should be set to scaledWidth * tjPixelSize[pixelFormat], if the destination image should be unpadded, or TJPAD(scaledWidth * tjPixelSize[pixelFormat]) if each row of the destination image should be padded to the nearest multiple of 4 bytes, as is the case for Windows bitmaps. (NOTE: scaledWidth can be determined by calling TJSCALED() with the JPEG image width and one of the scaling factors returned by tjGetScalingFactors().) You can also be clever and use the pitch parameter to skip rows, etc. Setting this parameter to 0 is the equivalent of setting it to scaledWidth * tjPixelSize[pixelFormat].
    heightdesired height (in pixels) of the destination image. If this is different than the height of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired height. If height is set to 0, then only the width will be considered when determining the scaled image size.
    dstBufpointer to a buffer that will receive the packed-pixel decompressed image. This buffer should normally be pitch * destinationHeight samples in size. However, you can also use this parameter to decompress into a specific region of a larger buffer. NOTE: If the JPEG image is lossy, then destinationHeight is either the scaled JPEG height (see TJSCALED(), TJPARAM_JPEGHEIGHT, and tj3SetScalingFactor()) or the height of the cropping region (see tj3SetCroppingRegion().) If the JPEG image is lossless, then destinationHeight is the JPEG height.
    pitchsamples per row in the destination image. Normally this should be set to destinationWidth * tjPixelSize[pixelFormat], if the destination image should be unpadded. (Setting this parameter to 0 is the equivalent of setting it to destinationWidth * tjPixelSize[pixelFormat].) However, you can also use this parameter to specify the row alignment/padding of the destination image, to skip rows, or to decompress into a specific region of a larger buffer. NOTE: If the JPEG image is lossy, then destinationWidth is either the scaled JPEG width (see TJSCALED(), TJPARAM_JPEGWIDTH, and tj3SetScalingFactor()) or the width of the cropping region (see tj3SetCroppingRegion().) If the JPEG image is lossless, then destinationWidth is the JPEG width.
    pixelFormatpixel format of the destination image (see Pixel formats.)
    flagsthe bitwise OR of one or more of the flags
    -
    Returns
    0 if successful, or -1 if an error occurred (see tjGetErrorStr2() and tjGetErrorCode().)
    +
    Returns
    0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
    - -

    ◆ tjDecompressHeader4()

    + +

    ◆ tj3Decompress16()

    - + @@ -1761,38 +1833,26 @@

    - + - - - - - - - - - - - - - - + + - - + + - - + + @@ -1802,32 +1862,31 @@

    -

    Retrieve information about a JPEG image without decompressing it, or prime the decompressor with quantization and Huffman tables.

    +

    Decompress a 16-bit-per-sample lossless JPEG image into a 16-bit-per-sample packed-pixel RGB, grayscale, or CMYK image.

    +

    The parameters that describe the JPEG image will be set when this function returns.

    Parameters

    DLLEXPORT int tjDecompressHeader4 DLLEXPORT int tj3Decompress16 ( tjhandle  handle, unsigned long size_t  jpegSize,
    int * width,
    int * height,
    int * jpegSubsamp, unsigned short * dstBuf,
    int * jpegColorspace, int pitch,
    int * jpegFlags int pixelFormat 
    - - - - - - - - + + + + + +
    handlea handle to a TurboJPEG decompressor or transformer instance
    jpegBufpointer to a byte buffer containing a JPEG image or an "abbreviated table specification" (AKA "tables-only") datastream. Passing a tables-only datastream to this function primes the decompressor with quantization and Huffman tables that can be used when decompressing subsequent "abbreviated image" datastreams. This is useful, for instance, when decompressing video streams in which all frames share the same quantization and Huffman tables.
    jpegSizesize of the JPEG image or tables-only datastream (in bytes)
    widthpointer to an integer variable that will receive the width (in pixels) of the JPEG image. If jpegBuf points to a tables-only datastream, then width is ignored.
    heightpointer to an integer variable that will receive the height (in pixels) of the JPEG image. If jpegBuf points to a tables-only datastream, then height is ignored.
    jpegSubsamppointer to an integer variable that will receive the level of chrominance subsampling used when the JPEG image was compressed (see Chrominance subsampling options.) If jpegBuf points to a tables-only datastream, then jpegSubsamp is ignored.
    jpegColorspacepointer to an integer variable that will receive one of the JPEG colorspace constants, indicating the colorspace of the JPEG image (see JPEG colorspaces.) If jpegBuf points to a tables-only datastream, then jpegColorspace is ignored.
    jpegFlagspointer to an integer variable that will receive the bitwise OR of one or more of the flags, such as TJFLAG_PROGRESSIVE and TJFLAG_LOSSLESS, that describe the JPEG image. If jpegBuf points to a tables-only datastream, then jpegFlags is ignored.
    handlehandle to a TurboJPEG instance that has been initialized for decompression
    jpegBufpointer to a byte buffer containing the JPEG image to decompress
    jpegSizesize of the JPEG image (in bytes)
    dstBufpointer to a buffer that will receive the packed-pixel decompressed image. This buffer should normally be pitch * destinationHeight samples in size. However, you can also use this parameter to decompress into a specific region of a larger buffer. NOTE: If the JPEG image is lossy, then destinationHeight is either the scaled JPEG height (see TJSCALED(), TJPARAM_JPEGHEIGHT, and tj3SetScalingFactor()) or the height of the cropping region (see tj3SetCroppingRegion().) If the JPEG image is lossless, then destinationHeight is the JPEG height.
    pitchsamples per row in the destination image. Normally this should be set to destinationWidth * tjPixelSize[pixelFormat], if the destination image should be unpadded. (Setting this parameter to 0 is the equivalent of setting it to destinationWidth * tjPixelSize[pixelFormat].) However, you can also use this parameter to specify the row alignment/padding of the destination image, to skip rows, or to decompress into a specific region of a larger buffer. NOTE: If the JPEG image is lossy, then destinationWidth is either the scaled JPEG width (see TJSCALED(), TJPARAM_JPEGWIDTH, and tj3SetScalingFactor()) or the width of the cropping region (see tj3SetCroppingRegion().) If the JPEG image is lossless, then destinationWidth is the JPEG width.
    pixelFormatpixel format of the destination image (see Pixel formats.)
    -
    Returns
    0 if successful, or -1 if an error occurred (see tjGetErrorStr2() and tjGetErrorCode().)
    +
    Returns
    0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
    - -

    ◆ tjDecompressToYUV2()

    + +

    ◆ tj3Decompress8()

    - + @@ -1841,7 +1900,7 @@

    - + @@ -1854,25 +1913,13 @@

    - - - - - - - - - - - - - + - + @@ -1882,33 +1929,31 @@

    -

    Decompress a JPEG image into a unified planar YUV image.

    -

    This function performs JPEG decompression but leaves out the color conversion step, so a planar YUV image is generated instead of a packed-pixel image.

    +

    Decompress an 8-bit-per-sample JPEG image into an 8-bit-per-sample packed-pixel RGB, grayscale, or CMYK image.

    +

    The parameters that describe the JPEG image will be set when this function returns.

    Parameters

    DLLEXPORT int tjDecompressToYUV2 DLLEXPORT int tj3Decompress8 ( tjhandle  handle, unsigned long size_t  jpegSize,
    int width,
    int align,
    int height, pitch,
    int flags pixelFormat 
    - + - - - - - + + +
    handlea handle to a TurboJPEG decompressor or transformer instance
    handlehandle to a TurboJPEG instance that has been initialized for decompression
    jpegBufpointer to a byte buffer containing the JPEG image to decompress
    jpegSizesize of the JPEG image (in bytes)
    dstBufpointer to a buffer that will receive the unified planar YUV decompressed image. Use tjBufSizeYUV2() to determine the appropriate size for this buffer based on the scaled image width, scaled image height, row alignment, and level of chrominance subsampling. The Y, U (Cb), and V (Cr) image planes will be stored sequentially in the buffer. (Refer to YUV Image Format Notes.)
    widthdesired width (in pixels) of the YUV image. If this is different than the width of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired width. If width is set to 0, then only the height will be considered when determining the scaled image size. If the scaled width is not an even multiple of the MCU block width (see tjMCUWidth), then an intermediate buffer copy will be performed.
    alignrow alignment (in bytes) of the YUV image (must be a power of 2.) Setting this parameter to n will cause each row in each plane of the YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.) To generate images suitable for X Video, align should be set to 4.
    heightdesired height (in pixels) of the YUV image. If this is different than the height of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired height. If height is set to 0, then only the width will be considered when determining the scaled image size. If the scaled height is not an even multiple of the MCU block height (see tjMCUHeight), then an intermediate buffer copy will be performed.
    flagsthe bitwise OR of one or more of the flags
    dstBufpointer to a buffer that will receive the packed-pixel decompressed image. This buffer should normally be pitch * destinationHeight samples in size. However, you can also use this parameter to decompress into a specific region of a larger buffer. NOTE: If the JPEG image is lossy, then destinationHeight is either the scaled JPEG height (see TJSCALED(), TJPARAM_JPEGHEIGHT, and tj3SetScalingFactor()) or the height of the cropping region (see tj3SetCroppingRegion().) If the JPEG image is lossless, then destinationHeight is the JPEG height.
    pitchsamples per row in the destination image. Normally this should be set to destinationWidth * tjPixelSize[pixelFormat], if the destination image should be unpadded. (Setting this parameter to 0 is the equivalent of setting it to destinationWidth * tjPixelSize[pixelFormat].) However, you can also use this parameter to specify the row alignment/padding of the destination image, to skip rows, or to decompress into a specific region of a larger buffer. NOTE: If the JPEG image is lossy, then destinationWidth is either the scaled JPEG width (see TJSCALED(), TJPARAM_JPEGWIDTH, and tj3SetScalingFactor()) or the width of the cropping region (see tj3SetCroppingRegion().) If the JPEG image is lossless, then destinationWidth is the JPEG width.
    pixelFormatpixel format of the destination image (see Pixel formats.)
    -
    Returns
    0 if successful, or -1 if an error occurred (see tjGetErrorStr2() and tjGetErrorCode().)
    +
    Returns
    0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
    - -

    ◆ tjDecompressToYUVPlanes()

    + +

    ◆ tj3DecompressHeader()

    - + @@ -1922,38 +1967,66 @@

    - - + + - - - + + + +
    DLLEXPORT int tjDecompressToYUVPlanes DLLEXPORT int tj3DecompressHeader ( tjhandle  handle, unsigned long jpegSize, size_t jpegSize 
    unsigned char ** dstPlanes, )
    +
    + +

    Retrieve information about a JPEG image without decompressing it, or prime the decompressor with quantization and Huffman tables.

    +

    If a JPEG image is passed to this function, then the parameters that describe the JPEG image will be set when the function returns.

    +
    Parameters
    + + + + +
    handlehandle to a TurboJPEG instance that has been initialized for decompression
    jpegBufpointer to a byte buffer containing a JPEG image or an "abbreviated table specification" (AKA "tables-only") datastream. Passing a tables-only datastream to this function primes the decompressor with quantization and Huffman tables that can be used when decompressing subsequent "abbreviated image" datastreams. This is useful, for instance, when decompressing video streams in which all frames share the same quantization and Huffman tables.
    jpegSizesize of the JPEG image or tables-only datastream (in bytes)
    +
    +
    +
    Returns
    0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
    + +
    +
    + +

    ◆ tj3DecompressToYUV8()

    + +
    +
    + + + + + + - - + + - - + + - - + + - + @@ -1963,60 +2036,116 @@

    -

    Decompress a JPEG image into separate Y, U (Cb), and V (Cr) image planes.

    -

    This function performs JPEG decompression but leaves out the color conversion step, so a planar YUV image is generated instead of a packed-pixel image.

    +

    Decompress an 8-bit-per-sample JPEG image into an 8-bit-per-sample unified planar YUV image.

    +

    This function performs JPEG decompression but leaves out the color conversion step, so a planar YUV image is generated instead of a packed-pixel image. The parameters that describe the JPEG image will be set when this function returns.

    Parameters

    DLLEXPORT int tj3DecompressToYUV8 (tjhandle handle,
    int width, const unsigned char * jpegBuf,
    int * strides, size_t jpegSize,
    int height, unsigned char * dstBuf,
    int flags align 
    - + - - - - - + +
    handlea handle to a TurboJPEG decompressor or transformer instance
    handlehandle to a TurboJPEG instance that has been initialized for decompression
    jpegBufpointer to a byte buffer containing the JPEG image to decompress
    jpegSizesize of the JPEG image (in bytes)
    dstPlanesan array of pointers to Y, U (Cb), and V (Cr) image planes (or just a Y plane, if decompressing a grayscale image) that will receive the decompressed image. These planes can be contiguous or non-contiguous in memory. Use tjPlaneSizeYUV() to determine the appropriate size for each plane based on the scaled image width, scaled image height, strides, and level of chrominance subsampling. Refer to YUV Image Format Notes for more details.
    widthdesired width (in pixels) of the YUV image. If this is different than the width of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired width. If width is set to 0, then only the height will be considered when determining the scaled image size. If the scaled width is not an even multiple of the MCU block width (see tjMCUWidth), then an intermediate buffer copy will be performed.
    stridesan array of integers, each specifying the number of bytes per row in the corresponding plane of the YUV image. Setting the stride for any plane to 0 is the same as setting it to the scaled plane width (see YUV Image Format Notes.) If strides is NULL, then the strides for all planes will be set to their respective scaled plane widths. You can adjust the strides in order to add an arbitrary amount of row padding to each plane or to decompress the JPEG image into a subregion of a larger planar YUV image.
    heightdesired height (in pixels) of the YUV image. If this is different than the height of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired height. If height is set to 0, then only the width will be considered when determining the scaled image size. If the scaled height is not an even multiple of the MCU block height (see tjMCUHeight), then an intermediate buffer copy will be performed.
    flagsthe bitwise OR of one or more of the flags
    dstBufpointer to a buffer that will receive the unified planar YUV decompressed image. Use tj3YUVBufSize() to determine the appropriate size for this buffer based on the scaled JPEG width and height (see TJSCALED(), TJPARAM_JPEGWIDTH, TJPARAM_JPEGHEIGHT, and tj3SetScalingFactor()), row alignment, and level of chrominance subsampling (see TJPARAM_SUBSAMP.) The Y, U (Cb), and V (Cr) image planes will be stored sequentially in the buffer. (Refer to YUV Image Format Notes.)
    alignrow alignment (in bytes) of the YUV image (must be a power of 2.) Setting this parameter to n will cause each row in each plane of the YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.) To generate images suitable for X Video, align should be set to 4.
    -
    Returns
    0 if successful, or -1 if an error occurred (see tjGetErrorStr2() and tjGetErrorCode().)
    +
    Returns
    0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
    - -

    ◆ tjDestroy()

    + +

    ◆ tj3DecompressToYUVPlanes8()

    - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DLLEXPORT int tjDestroy DLLEXPORT int tj3DecompressToYUVPlanes8 ( tjhandle handle)handle,
    const unsigned char * jpegBuf,
    size_t jpegSize,
    unsigned char ** dstPlanes,
    int * strides 
    )
    +
    + +

    Decompress an 8-bit-per-sample JPEG image into separate 8-bit-per-sample Y, U (Cb), and V (Cr) image planes.

    +

    This function performs JPEG decompression but leaves out the color conversion step, so a planar YUV image is generated instead of a packed-pixel image. The parameters that describe the JPEG image will be set when this function returns.

    +
    Parameters
    + + + + + + +
    handlehandle to a TurboJPEG instance that has been initialized for decompression
    jpegBufpointer to a byte buffer containing the JPEG image to decompress
    jpegSizesize of the JPEG image (in bytes)
    dstPlanesan array of pointers to Y, U (Cb), and V (Cr) image planes (or just a Y plane, if decompressing a grayscale image) that will receive the decompressed image. These planes can be contiguous or non-contiguous in memory. Use tj3YUVPlaneSize() to determine the appropriate size for each plane based on the scaled JPEG width and height (see TJSCALED(), TJPARAM_JPEGWIDTH, TJPARAM_JPEGHEIGHT, and tj3SetScalingFactor()), strides, and level of chrominance subsampling (see TJPARAM_SUBSAMP.) Refer to YUV Image Format Notes for more details.
    stridesan array of integers, each specifying the number of bytes per row in the corresponding plane of the YUV image. Setting the stride for any plane to 0 is the same as setting it to the scaled plane width (see YUV Image Format Notes.) If strides is NULL, then the strides for all planes will be set to their respective scaled plane widths. You can adjust the strides in order to add an arbitrary amount of row padding to each plane or to decompress the JPEG image into a subregion of a larger planar YUV image.
    +
    +
    +
    Returns
    0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
    + +
    +
    + +

    ◆ tj3Destroy()

    + +
    +
    + + + + + +
    DLLEXPORT void tj3Destroy (tjhandle handle)
    -

    Destroy a TurboJPEG compressor, decompressor, or transformer instance.

    +

    Destroy a TurboJPEG instance.

    Parameters
    - +
    handlea handle to a TurboJPEG compressor, decompressor or transformer instance
    handlehandle to a TurboJPEG instance. If the handle is NULL, then this function has no effect.
    -
    Returns
    0 if successful, or -1 if an error occurred (see tjGetErrorStr2().)
    - -

    ◆ tjEncodeYUV3()

    + +

    ◆ tj3EncodeYUV8()

    - + @@ -2061,19 +2190,7 @@

    - - - - - - - - - - - - - + @@ -2083,35 +2200,33 @@

    -

    Encode a packed-pixel RGB or grayscale image into a unified planar YUV image.

    +

    Encode an 8-bit-per-sample packed-pixel RGB or grayscale image into an 8-bit-per-sample unified planar YUV image.

    This function performs color conversion (which is accelerated in the libjpeg-turbo implementation) but does not execute any of the other steps in the JPEG compression process.

    Parameters

    DLLEXPORT int tjEncodeYUV3 DLLEXPORT int tj3EncodeYUV8 ( tjhandle  handle, int align,
    int subsamp,
    int flags align 
    - - + + - + - + - -
    handlea handle to a TurboJPEG compressor or transformer instance
    srcBufpointer to a buffer containing a packed-pixel RGB or grayscale source image to be encoded
    handlehandle to a TurboJPEG instance that has been initialized for compression
    srcBufpointer to a buffer containing a packed-pixel RGB or grayscale source image to be encoded. This buffer should normally be pitch * height bytes in size. However, you can also use this parameter to encode from a specific region of a larger buffer.
    widthwidth (in pixels) of the source image
    pitchbytes per row in the source image. Normally this should be width * tjPixelSize[pixelFormat], if the image is unpadded, or TJPAD(width * tjPixelSize[pixelFormat]) if each row of the image is padded to the nearest multiple of 4 bytes, as is the case for Windows bitmaps. You can also be clever and use this parameter to skip rows, etc. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
    pitchbytes per row in the source image. Normally this should be width * tjPixelSize[pixelFormat], if the image is unpadded. (Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].) However, you can also use this parameter to specify the row alignment/padding of the source image, to skip rows, or to encode from a specific region of a larger packed-pixel image.
    heightheight (in pixels) of the source image
    pixelFormatpixel format of the source image (see Pixel formats.)
    dstBufpointer to a buffer that will receive the unified planar YUV image. Use tjBufSizeYUV2() to determine the appropriate size for this buffer based on the image width, height, row alignment, and level of chrominance subsampling. The Y, U (Cb), and V (Cr) image planes will be stored sequentially in the buffer. (Refer to YUV Image Format Notes.)
    dstBufpointer to a buffer that will receive the unified planar YUV image. Use tj3YUVBufSize() to determine the appropriate size for this buffer based on the image width, height, row alignment, and level of chrominance subsampling (see TJPARAM_SUBSAMP.) The Y, U (Cb), and V (Cr) image planes will be stored sequentially in the buffer. (Refer to YUV Image Format Notes.)
    alignrow alignment (in bytes) of the YUV image (must be a power of 2.) Setting this parameter to n will cause each row in each plane of the YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.) To generate images suitable for X Video, align should be set to 4.
    subsampthe level of chrominance subsampling to be used when generating the YUV image (see Chrominance subsampling options.) To generate images suitable for X Video, subsamp should be set to TJSAMP_420. This produces an image compatible with the I420 (AKA "YUV420P") format.
    flagsthe bitwise OR of one or more of the flags
    -
    Returns
    0 if successful, or -1 if an error occurred (see tjGetErrorStr2() and tjGetErrorCode().)
    +
    Returns
    0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
    - -

    ◆ tjEncodeYUVPlanes()

    + +

    ◆ tj3EncodeYUVPlanes8()

    - + @@ -2156,19 +2271,7 @@

    - - - - - - - - - - - - - + @@ -2178,37 +2281,35 @@

    -

    Encode a packed-pixel RGB or grayscale image into separate Y, U (Cb), and V (Cr) image planes.

    +

    Encode an 8-bit-per-sample packed-pixel RGB or grayscale image into separate 8-bit-per-sample Y, U (Cb), and V (Cr) image planes.

    This function performs color conversion (which is accelerated in the libjpeg-turbo implementation) but does not execute any of the other steps in the JPEG compression process.

    Parameters

    DLLEXPORT int tjEncodeYUVPlanes DLLEXPORT int tj3EncodeYUVPlanes8 ( tjhandle  handle, int * strides,
    int subsamp,
    int flags strides 
    - - + + - + - + - -
    handlea handle to a TurboJPEG compressor or transformer instance
    srcBufpointer to a buffer containing a packed-pixel RGB or grayscale source image to be encoded
    handlehandle to a TurboJPEG instance that has been initialized for compression
    srcBufpointer to a buffer containing a packed-pixel RGB or grayscale source image to be encoded. This buffer should normally be pitch * height bytes in size. However, you can also use this parameter to encode from a specific region of a larger buffer.
    widthwidth (in pixels) of the source image
    pitchbytes per row in the source image. Normally this should be width * tjPixelSize[pixelFormat], if the image is unpadded, or TJPAD(width * tjPixelSize[pixelFormat]) if each row of the image is padded to the nearest multiple of 4 bytes, as is the case for Windows bitmaps. You can also be clever and use this parameter to skip rows, etc. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
    pitchbytes per row in the source image. Normally this should be width * tjPixelSize[pixelFormat], if the image is unpadded. (Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].) However, you can also use this parameter to specify the row alignment/padding of the source image, to skip rows, or to encode from a specific region of a larger packed-pixel image.
    heightheight (in pixels) of the source image
    pixelFormatpixel format of the source image (see Pixel formats.)
    dstPlanesan array of pointers to Y, U (Cb), and V (Cr) image planes (or just a Y plane, if generating a grayscale image) that will receive the encoded image. These planes can be contiguous or non-contiguous in memory. Use tjPlaneSizeYUV() to determine the appropriate size for each plane based on the image width, height, strides, and level of chrominance subsampling. Refer to YUV Image Format Notes for more details.
    dstPlanesan array of pointers to Y, U (Cb), and V (Cr) image planes (or just a Y plane, if generating a grayscale image) that will receive the encoded image. These planes can be contiguous or non-contiguous in memory. Use tj3YUVPlaneSize() to determine the appropriate size for each plane based on the image width, height, strides, and level of chrominance subsampling (see TJPARAM_SUBSAMP.) Refer to YUV Image Format Notes for more details.
    stridesan array of integers, each specifying the number of bytes per row in the corresponding plane of the YUV image. Setting the stride for any plane to 0 is the same as setting it to the plane width (see YUV Image Format Notes.) If strides is NULL, then the strides for all planes will be set to their respective plane widths. You can adjust the strides in order to add an arbitrary amount of row padding to each plane or to encode an RGB or grayscale image into a subregion of a larger planar YUV image.
    subsampthe level of chrominance subsampling to be used when generating the YUV image (see Chrominance subsampling options.) To generate images suitable for X Video, subsamp should be set to TJSAMP_420. This produces an image compatible with the I420 (AKA "YUV420P") format.
    flagsthe bitwise OR of one or more of the flags
    -
    Returns
    0 if successful, or -1 if an error occurred (see tjGetErrorStr2() and tjGetErrorCode().)
    +
    Returns
    0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
    - -

    ◆ tjFree()

    + +

    ◆ tj3Free()

    - + - + @@ -2216,53 +2317,63 @@

    Free a byte buffer previously allocated by TurboJPEG.

    -

    You should always use this function to free JPEG destination buffer(s) that were automatically (re)allocated by the compression and transform functions or that were manually allocated using tjAlloc().

    +

    You should always use this function to free JPEG destination buffer(s) that were automatically (re)allocated by the compression and transform functions or that were manually allocated using tj3Alloc().

    Parameters

    DLLEXPORT void tjFree DLLEXPORT void tj3Free (unsigned char * void *  buffer)
    bufferaddress of the buffer to free. If the address is NULL, then this function has no effect.
    -
    See also
    tjAlloc()
    +
    See also
    tj3Alloc()
    - -

    ◆ tjGetErrorCode()

    + +

    ◆ tj3Get()

    - + - + + + + + + + + + + +
    DLLEXPORT int tjGetErrorCode DLLEXPORT int tj3Get ( tjhandle handle)handle,
    int param 
    )
    -

    Returns a code indicating the severity of the last error.

    -

    See Error codes.

    +

    Get the value of a parameter.

    Parameters
    - + +
    handlea handle to a TurboJPEG compressor, decompressor or transformer instance
    handlehandle to a TurboJPEG instance
    paramone of the parameters
    -
    Returns
    a code indicating the severity of the last error. See Error codes.
    +
    Returns
    the value of the specified parameter, or -1 if the value is unknown.
    - -

    ◆ tjGetErrorStr2()

    + +

    ◆ tj3GetErrorCode()

    - + @@ -2271,116 +2382,160 @@

    -

    Returns a descriptive error message explaining why the last command failed.

    +

    Returns a code indicating the severity of the last error.

    +

    See Error codes.

    Parameters

    DLLEXPORT char* tjGetErrorStr2 DLLEXPORT int tj3GetErrorCode ( tjhandle  handle)
    - +
    handlea handle to a TurboJPEG compressor, decompressor, or transformer instance, or NULL if the error was generated by a global function (but note that retrieving the error message for a global function is thread-safe only on platforms that support thread-local storage.)
    handlehandle to a TurboJPEG instance
    -
    Returns
    a descriptive error message explaining why the last command failed.
    +
    Returns
    a code indicating the severity of the last error. See Error codes.
    - -

    ◆ tjGetScalingFactors()

    + +

    ◆ tj3GetErrorStr()

    - + - - + +
    DLLEXPORT tjscalingfactor* tjGetScalingFactors DLLEXPORT char* tj3GetErrorStr (int * numScalingFactors)tjhandle handle)
    -

    Returns a list of fractional scaling factors that the JPEG decompressor supports.

    +

    Returns a descriptive error message explaining why the last command failed.

    Parameters
    - +
    numScalingFactorspointer to an integer variable that will receive the number of elements in the list
    handlehandle to a TurboJPEG instance, or NULL if the error was generated by a global function (but note that retrieving the error message for a global function is thread-safe only on platforms that support thread-local storage.)
    -
    Returns
    a pointer to a list of fractional scaling factors, or NULL if an error is encountered (see tjGetErrorStr2().)
    +
    Returns
    a descriptive error message explaining why the last command failed.
    - -

    ◆ tjInitCompress()

    + +

    ◆ tj3GetScalingFactors()

    - + - - + +
    DLLEXPORT tjhandle tjInitCompress DLLEXPORT tjscalingfactor* tj3GetScalingFactors (void )int * numScalingFactors)
    -

    Create a TurboJPEG compressor instance.

    -
    Returns
    a handle to the newly-created instance, or NULL if an error occurred (see tjGetErrorStr2().)
    +

    Returns a list of fractional scaling factors that the JPEG decompressor supports.

    +
    Parameters
    + + +
    numScalingFactorspointer to an integer variable that will receive the number of elements in the list
    +
    +
    +
    Returns
    a pointer to a list of fractional scaling factors, or NULL if an error is encountered (see tj3GetErrorStr().)
    - -

    ◆ tjInitDecompress()

    + +

    ◆ tj3Init()

    - + - - + +
    DLLEXPORT tjhandle tjInitDecompress DLLEXPORT tjhandle tj3Init (void )int initType)
    -

    Create a TurboJPEG decompressor instance.

    -
    Returns
    a handle to the newly-created instance, or NULL if an error occurred (see tjGetErrorStr2().)
    +

    Create a new TurboJPEG instance.

    +
    Parameters
    + + +
    initTypeone of the initialization options
    +
    +
    +
    Returns
    a handle to the newly-created instance, or NULL if an error occurred (see tj3GetErrorStr().)
    - -

    ◆ tjInitTransform()

    + +

    ◆ tj3JPEGBufSize()

    - + - - + + + + + + + + + + + + + + + + + +
    DLLEXPORT tjhandle tjInitTransform DLLEXPORT size_t tj3JPEGBufSize (void )int width,
    int height,
    int jpegSubsamp 
    )
    -

    Create a new TurboJPEG transformer instance.

    -
    Returns
    a handle to the newly-created instance, or NULL if an error occurred (see tjGetErrorStr2().)
    +

    The maximum size of the buffer (in bytes) required to hold a JPEG image with the given parameters.

    +

    The number of bytes returned by this function is larger than the size of the uncompressed source image. The reason for this is that the JPEG format uses 16-bit coefficients, so it is possible for a very high-quality source image with very high-frequency content to expand rather than compress when converted to the JPEG format. Such images represent very rare corner cases, but since there is no way to predict the size of a JPEG image prior to compression, the corner cases have to be handled.

    +
    Parameters
    + + + + +
    widthwidth (in pixels) of the image
    heightheight (in pixels) of the image
    jpegSubsampthe level of chrominance subsampling to be used when generating the JPEG image (see Chrominance subsampling options.) TJSAMP_UNKNOWN is treated like TJSAMP_444, since a buffer large enough to hold a JPEG image with no subsampling should also be large enough to hold a JPEG image with an arbitrary level of subsampling. Note that lossless JPEG images always use TJSAMP_444.
    +
    +
    +
    Returns
    the maximum size of the buffer (in bytes) required to hold the image, or 0 if the arguments are out of bounds.
    - -

    ◆ tjLoadImage()

    + +

    ◆ tj3LoadImage12()

    - + + + + + + + @@ -2406,13 +2561,7 @@

    - - - - - - - + @@ -2422,51 +2571,69 @@

    -

    Load a packed-pixel image from disk into memory.

    +

    Load a 12-bit-per-sample packed-pixel image from disk into memory.

    Parameters

    DLLEXPORT unsigned char* tjLoadImage DLLEXPORT short* tj3LoadImage12 (tjhandle handle,
    const char *  filename,
    int * pixelFormat,
    int flags pixelFormat 
    - + + - + - -
    filenamename of a file containing a packed-pixel image in Windows BMP or PBMPLUS (PPM/PGM) format
    handlehandle to a TurboJPEG instance
    filenamename of a file containing a packed-pixel image in Windows BMP or PBMPLUS (PPM/PGM) format. Windows BMP files require 8-bit-per-sample data precision. If the data precision of the PBMPLUS file does not match the target data precision, then upconverting or downconverting will be performed.
    widthpointer to an integer variable that will receive the width (in pixels) of the packed-pixel image
    alignrow alignment of the packed-pixel buffer to be returned (must be a power of 2.) Setting this parameter to n will cause all rows in the buffer to be padded to the nearest multiple of n bytes (1 = unpadded.)
    alignrow alignment (in samples) of the packed-pixel buffer to be returned (must be a power of 2.) Setting this parameter to n will cause all rows in the buffer to be padded to the nearest multiple of n samples (1 = unpadded.)
    heightpointer to an integer variable that will receive the height (in pixels) of the packed-pixel image
    pixelFormatpointer to an integer variable that specifies or will receive the pixel format of the packed-pixel buffer. The behavior of tjLoadImage() will vary depending on the value of *pixelFormat passed to the function:
      +
    pixelFormatpointer to an integer variable that specifies or will receive the pixel format of the packed-pixel buffer. The behavior of this function will vary depending on the value of *pixelFormat passed to the function:
    • TJPF_UNKNOWN : The packed-pixel buffer returned by this function will use the most optimal pixel format for the file type, and *pixelFormat will contain the ID of that pixel format upon successful return from this function.
    • TJPF_GRAY : Only PGM files and 8-bit-per-pixel BMP files with a grayscale colormap can be loaded.
    • TJPF_CMYK : The RGB or grayscale pixels stored in the file will be converted using a quick & dirty algorithm that is suitable only for testing purposes. (Proper conversion between CMYK and other formats requires a color management system.)
    • Other pixel formats : The packed-pixel buffer will use the specified pixel format, and pixel format conversion will be performed if necessary.
    flagsthe bitwise OR of one or more of the flags.
    -
    Returns
    a pointer to a newly-allocated buffer containing the packed-pixel image, converted to the chosen pixel format and with the chosen row alignment, or NULL if an error occurred (see tjGetErrorStr2().) This buffer should be freed using tjFree().
    +
    Returns
    a pointer to a newly-allocated buffer containing the packed-pixel image, converted to the chosen pixel format and with the chosen row alignment, or NULL if an error occurred (see tj3GetErrorStr().) This buffer should be freed using tj3Free().
    - -

    ◆ tjPlaneHeight()

    + +

    ◆ tj3LoadImage16()

    - + - - + + + + + + + + + + + + + + + + + + + + - - + + @@ -2476,55 +2643,69 @@

    -

    The plane height of a YUV image plane with the given parameters.

    -

    Refer to YUV Image Format Notes for a description of plane height.

    +

    Load a 16-bit-per-sample packed-pixel image from disk into memory.

    Parameters

    DLLEXPORT int tjPlaneHeight DLLEXPORT unsigned short* tj3LoadImage16 (int componentID, tjhandle handle,
    const char * filename,
    int * width,
    int align,
    int *  height,
    int subsamp int * pixelFormat 
    - - - + + + + + +
    componentIDID number of the image plane (0 = Y, 1 = U/Cb, 2 = V/Cr)
    heightheight (in pixels) of the YUV image
    subsamplevel of chrominance subsampling in the image (see Chrominance subsampling options.)
    handlehandle to a TurboJPEG instance
    filenamename of a file containing a packed-pixel image in Windows BMP or PBMPLUS (PPM/PGM) format. Windows BMP files require 8-bit-per-sample data precision. If the data precision of the PBMPLUS file does not match the target data precision, then upconverting or downconverting will be performed.
    widthpointer to an integer variable that will receive the width (in pixels) of the packed-pixel image
    alignrow alignment (in samples) of the packed-pixel buffer to be returned (must be a power of 2.) Setting this parameter to n will cause all rows in the buffer to be padded to the nearest multiple of n samples (1 = unpadded.)
    heightpointer to an integer variable that will receive the height (in pixels) of the packed-pixel image
    pixelFormatpointer to an integer variable that specifies or will receive the pixel format of the packed-pixel buffer. The behavior of this function will vary depending on the value of *pixelFormat passed to the function:
      +
    • TJPF_UNKNOWN : The packed-pixel buffer returned by this function will use the most optimal pixel format for the file type, and *pixelFormat will contain the ID of that pixel format upon successful return from this function.
    • +
    • TJPF_GRAY : Only PGM files and 8-bit-per-pixel BMP files with a grayscale colormap can be loaded.
    • +
    • TJPF_CMYK : The RGB or grayscale pixels stored in the file will be converted using a quick & dirty algorithm that is suitable only for testing purposes. (Proper conversion between CMYK and other formats requires a color management system.)
    • +
    • Other pixel formats : The packed-pixel buffer will use the specified pixel format, and pixel format conversion will be performed if necessary.
    • +
    +
    -
    Returns
    the plane height of a YUV image plane with the given parameters, or -1 if the arguments are out of bounds.
    +
    Returns
    a pointer to a newly-allocated buffer containing the packed-pixel image, converted to the chosen pixel format and with the chosen row alignment, or NULL if an error occurred (see tj3GetErrorStr().) This buffer should be freed using tj3Free().
    - -

    ◆ tjPlaneSizeYUV()

    + +

    ◆ tj3LoadImage8()

    - + - - + + - + + + + + + + - + - + - - + + @@ -2534,32 +2715,51 @@

    -

    The size of the buffer (in bytes) required to hold a YUV image plane with the given parameters.

    +

    Load an 8-bit-per-sample packed-pixel image from disk into memory.

    Parameters

    DLLEXPORT unsigned long tjPlaneSizeYUV DLLEXPORT unsigned char* tj3LoadImage8 (int componentID, tjhandle handle,
    int const char * filename,
    int *  width,
    int stride, align,
    int int *  height,
    int subsamp int * pixelFormat 
    - - - - - + + + + + +
    componentIDID number of the image plane (0 = Y, 1 = U/Cb, 2 = V/Cr)
    widthwidth (in pixels) of the YUV image. NOTE: this is the width of the whole image, not the plane width.
    stridebytes per row in the image plane. Setting this to 0 is the equivalent of setting it to the plane width.
    heightheight (in pixels) of the YUV image. NOTE: this is the height of the whole image, not the plane height.
    subsamplevel of chrominance subsampling in the image (see Chrominance subsampling options.)
    handlehandle to a TurboJPEG instance
    filenamename of a file containing a packed-pixel image in Windows BMP or PBMPLUS (PPM/PGM) format. Windows BMP files require 8-bit-per-sample data precision. If the data precision of the PBMPLUS file does not match the target data precision, then upconverting or downconverting will be performed.
    widthpointer to an integer variable that will receive the width (in pixels) of the packed-pixel image
    alignrow alignment (in samples) of the packed-pixel buffer to be returned (must be a power of 2.) Setting this parameter to n will cause all rows in the buffer to be padded to the nearest multiple of n samples (1 = unpadded.)
    heightpointer to an integer variable that will receive the height (in pixels) of the packed-pixel image
    pixelFormatpointer to an integer variable that specifies or will receive the pixel format of the packed-pixel buffer. The behavior of this function will vary depending on the value of *pixelFormat passed to the function:
      +
    • TJPF_UNKNOWN : The packed-pixel buffer returned by this function will use the most optimal pixel format for the file type, and *pixelFormat will contain the ID of that pixel format upon successful return from this function.
    • +
    • TJPF_GRAY : Only PGM files and 8-bit-per-pixel BMP files with a grayscale colormap can be loaded.
    • +
    • TJPF_CMYK : The RGB or grayscale pixels stored in the file will be converted using a quick & dirty algorithm that is suitable only for testing purposes. (Proper conversion between CMYK and other formats requires a color management system.)
    • +
    • Other pixel formats : The packed-pixel buffer will use the specified pixel format, and pixel format conversion will be performed if necessary.
    • +
    +
    -
    Returns
    the size of the buffer (in bytes) required to hold the YUV image plane, or -1 if the arguments are out of bounds.
    +
    Returns
    a pointer to a newly-allocated buffer containing the packed-pixel image, converted to the chosen pixel format and with the chosen row alignment, or NULL if an error occurred (see tj3GetErrorStr().) This buffer should be freed using tj3Free().
    - -

    ◆ tjPlaneWidth()

    + +

    ◆ tj3SaveImage12()

    - + - - + + + + + + + + + + + + + + @@ -2571,7 +2771,19 @@

    - + + + + + + + + + + + + + @@ -2581,36 +2793,45 @@

    -

    The plane width of a YUV image plane with the given parameters.

    -

    Refer to YUV Image Format Notes for a description of plane width.

    +

    Save a 12-bit-per-sample packed-pixel image from memory to disk.

    Parameters

    DLLEXPORT int tjPlaneWidth DLLEXPORT int tj3SaveImage12 (int componentID, tjhandle handle,
    const char * filename,
    const short * buffer,
    int subsamp pitch,
    int height,
    int pixelFormat 
    - - - -
    componentIDID number of the image plane (0 = Y, 1 = U/Cb, 2 = V/Cr)
    widthwidth (in pixels) of the YUV image
    subsamplevel of chrominance subsampling in the image (see Chrominance subsampling options.)
    - - -
    Returns
    the plane width of a YUV image plane with the given parameters, or -1 if the arguments are out of bounds.
    + handlehandle to a TurboJPEG instance + filenamename of a file to which to save the packed-pixel image. The image will be stored in Windows BMP or PBMPLUS (PPM/PGM) format, depending on the file extension. Windows BMP files require 8-bit-per-sample data precision. + bufferpointer to a buffer containing a packed-pixel RGB, grayscale, or CMYK image to be saved + widthwidth (in pixels) of the packed-pixel image + pitchsamples per row in the packed-pixel image. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat]. + heightheight (in pixels) of the packed-pixel image + pixelFormatpixel format of the packed-pixel image (see Pixel formats.) If this parameter is set to TJPF_GRAY, then the image will be stored in PGM or 8-bit-per-pixel (indexed color) BMP format. Otherwise, the image will be stored in PPM or 24-bit-per-pixel BMP format. If this parameter is set to TJPF_CMYK, then the CMYK pixels will be converted to RGB using a quick & dirty algorithm that is suitable only for testing purposes. (Proper conversion between CMYK and other formats requires a color management system.) + + + +
    Returns
    0 if successful, or -1 if an error occurred (see tj3GetErrorStr().)
    - -

    ◆ tjSaveImage()

    + +

    ◆ tj3SaveImage16()

    - + + + + + + + - + @@ -2635,13 +2856,80 @@

    - + + + + + + + +
    DLLEXPORT int tjSaveImage DLLEXPORT int tj3SaveImage16 (tjhandle handle,
    const char *  filename,
    unsigned char * const unsigned short *  buffer,
    int pixelFormat, pixelFormat 
    )
    +
    +
    + +

    ◆ tj3SaveImage8()

    + +
    +
    + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + @@ -2651,31 +2939,152 @@

    -

    Save a packed-pixel image from memory to disk.

    +

    Save an 8-bit-per-sample packed-pixel image from memory to disk.

    Parameters

    DLLEXPORT int tj3SaveImage8 (tjhandle handle,
    const char * filename,
    const unsigned char * buffer,
    int flags width,
    int pitch,
    int height,
    int pixelFormat 
    - + + - + -
    filenamename of a file to which to save the packed-pixel image. The image will be stored in Windows BMP or PBMPLUS (PPM/PGM) format, depending on the file extension.
    handlehandle to a TurboJPEG instance
    filenamename of a file to which to save the packed-pixel image. The image will be stored in Windows BMP or PBMPLUS (PPM/PGM) format, depending on the file extension. Windows BMP files require 8-bit-per-sample data precision.
    bufferpointer to a buffer containing a packed-pixel RGB, grayscale, or CMYK image to be saved
    widthwidth (in pixels) of the packed-pixel image
    pitchbytes per row in the packed-pixel image. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
    pitchsamples per row in the packed-pixel image. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
    heightheight (in pixels) of the packed-pixel image
    pixelFormatpixel format of the packed-pixel image (see Pixel formats.) If this parameter is set to TJPF_GRAY, then the image will be stored in PGM or 8-bit-per-pixel (indexed color) BMP format. Otherwise, the image will be stored in PPM or 24-bit-per-pixel BMP format. If this parameter is set to TJPF_CMYK, then the CMYK pixels will be converted to RGB using a quick & dirty algorithm that is suitable only for testing purposes. (Proper conversion between CMYK and other formats requires a color management system.)
    flagsthe bitwise OR of one or more of the flags.
    -
    Returns
    0 if successful, or -1 if an error occurred (see tjGetErrorStr2().)
    +
    Returns
    0 if successful, or -1 if an error occurred (see tj3GetErrorStr().)
    + +
    +
    + +

    ◆ tj3Set()

    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    DLLEXPORT int tj3Set (tjhandle handle,
    int param,
    int value 
    )
    +
    + +

    Set the value of a parameter.

    +
    Parameters
    + + + + +
    handlehandle to a TurboJPEG instance
    paramone of the parameters
    valuevalue of the parameter (refer to parameter documentation)
    +
    +
    +
    Returns
    0 if successful, or -1 if an error occurred (see tj3GetErrorStr().)
    + +
    +
    + +

    ◆ tj3SetCroppingRegion()

    + +
    +
    + + + + + + + + + + + + + + + + + + +
    DLLEXPORT int tj3SetCroppingRegion (tjhandle handle,
    tjregion croppingRegion 
    )
    +
    + +

    Set the cropping region for partially decompressing a lossy JPEG image into a packed-pixel image.

    +
    Parameters
    + + + +
    handlehandle to a TurboJPEG instance that has been initialized for decompression
    croppingRegiontjregion structure that specifies a subregion of the JPEG image to decompress, or TJUNCROPPED for no cropping. The left boundary of the cropping region must be evenly divisible by the scaled MCU block width (TJSCALED(tjMCUWidth[subsamp], scalingFactor), where subsamp is the level of chrominance subsampling in the JPEG image (see TJPARAM_SUBSAMP) and scalingFactor is the decompression scaling factor (see tj3SetScalingFactor().) The cropping region should be specified relative to the scaled image dimensions. Unless croppingRegion is TJUNCROPPED, the JPEG header must be read (see tj3DecompressHeader()) prior to calling this function.
    +
    +
    +
    Returns
    0 if successful, or -1 if an error occurred (see tj3GetErrorStr().)
    + +
    +
    + +

    ◆ tj3SetScalingFactor()

    + +
    +
    + + + + + + + + + + + + + + + + + + +
    DLLEXPORT int tj3SetScalingFactor (tjhandle handle,
    tjscalingfactor scalingFactor 
    )
    +
    + +

    Set the scaling factor for subsequent lossy decompression operations.

    +
    Parameters
    + + + +
    handlehandle to a TurboJPEG instance that has been initialized for decompression
    scalingFactortjscalingfactor structure that specifies a fractional scaling factor that the decompressor supports (see tj3GetScalingFactors()), or TJUNSCALED for no scaling. Decompression scaling is a function of the IDCT algorithm, so scaling factors are generally limited to multiples of 1/8. If the entire JPEG image will be decompressed, then the width and height of the scaled destination image can be determined by calling TJSCALED() with the JPEG width and height (see TJPARAM_JPEGWIDTH and TJPARAM_JPEGHEIGHT) and the specified scaling factor. When decompressing into a planar YUV image, an intermediate buffer copy will be performed if the width or height of the scaled destination image is not an even multiple of the MCU block size (see tjMCUWidth and tjMCUHeight.) Note that decompression scaling is not available (and the specified scaling factor is ignored) when decompressing lossless JPEG images (see TJPARAM_LOSSLESS), since the IDCT algorithm is not used with those images. Note also that TJPARAM_FASTDCT is ignored when decompression scaling is enabled.
    +
    +
    +
    Returns
    0 if successful, or -1 if an error occurred (see tj3GetErrorStr().)
    - -

    ◆ tjTransform()

    + +

    ◆ tj3Transform()

    - + @@ -2689,7 +3098,7 @@

    - + @@ -2707,20 +3116,14 @@

    - + - - - - - - - - + + @@ -2731,26 +3134,228 @@

    Losslessly transform a JPEG image into another JPEG image.

    -

    Lossless transforms work by moving the raw DCT coefficients from one JPEG image structure to another without altering the values of the coefficients. While this is typically faster than decompressing the image, transforming it, and re-compressing it, lossless transforms are not free. Each lossless transform requires reading and performing Huffman decoding on all of the coefficients in the source image, regardless of the size of the destination image. Thus, this function provides a means of generating multiple transformed images from the same source or applying multiple transformations simultaneously, in order to eliminate the need to read the source coefficients multiple times.

    +

    Lossless transforms work by moving the raw DCT coefficients from one JPEG image structure to another without altering the values of the coefficients. While this is typically faster than decompressing the image, transforming it, and re-compressing it, lossless transforms are not free. Each lossless transform requires reading and performing entropy decoding on all of the coefficients in the source image, regardless of the size of the destination image. Thus, this function provides a means of generating multiple transformed images from the same source or applying multiple transformations simultaneously, in order to eliminate the need to read the source coefficients multiple times.

    Parameters

    DLLEXPORT int tjTransform DLLEXPORT int tj3Transform ( tjhandle  handle, unsigned long size_t  jpegSize,
    unsigned long * size_t *  dstSizes,
    tjtransformtransforms,
    int flags const tjtransformtransforms 
    - + - +If you choose option 1, then dstSizes[i] should be set to the size of your pre-allocated buffer. In any case, unless you have set TJPARAM_NOREALLOC, you should always check dstBufs[i] upon return from this function, as it may have changed. + -
    handlea handle to a TurboJPEG transformer instance
    handlehandle to a TurboJPEG instance that has been initialized for lossless transformation
    jpegBufpointer to a byte buffer containing the JPEG source image to transform
    jpegSizesize of the JPEG source image (in bytes)
    nthe number of transformed JPEG images to generate
    dstBufspointer to an array of n byte buffers. dstBufs[i] will receive a JPEG image that has been transformed using the parameters in transforms[i]. TurboJPEG has the ability to reallocate the JPEG destination buffer to accommodate the size of the transformed JPEG image. Thus, you can choose to:
      -
    1. pre-allocate the JPEG destination buffer with an arbitrary size using tjAlloc() and let TurboJPEG grow the buffer as needed,
    2. +
    3. pre-allocate the JPEG destination buffer with an arbitrary size using tj3Alloc() and let TurboJPEG grow the buffer as needed,
    4. set dstBufs[i] to NULL to tell TurboJPEG to allocate the buffer for you, or
    5. -
    6. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize() with the transformed or cropped width and height. Under normal circumstances, this should ensure that the buffer never has to be re-allocated. (Setting TJFLAG_NOREALLOC guarantees that it won't be.) Note, however, that there are some rare cases (such as transforming images with a large amount of embedded EXIF or ICC profile data) in which the transformed JPEG image will be larger than the worst-case size, and TJFLAG_NOREALLOC cannot be used in those cases.
    7. +
    8. pre-allocate the buffer to a "worst case" size determined by calling tj3JPEGBufSize() with the transformed or cropped width and height. Under normal circumstances, this should ensure that the buffer never has to be re-allocated. (Setting TJPARAM_NOREALLOC guarantees that it won't be.) Note, however, that there are some rare cases (such as transforming images with a large amount of embedded EXIF or ICC profile data) in which the transformed JPEG image will be larger than the worst-case size, and TJPARAM_NOREALLOC cannot be used in those cases.
    -If you choose option 1, then dstSizes[i] should be set to the size of your pre-allocated buffer. In any case, unless you have set TJFLAG_NOREALLOC, you should always check dstBufs[i] upon return from this function, as it may have changed.
    dstSizespointer to an array of n unsigned long variables that will receive the actual sizes (in bytes) of each transformed JPEG image. If dstBufs[i] points to a pre-allocated buffer, then dstSizes[i] should be set to the size of the buffer. Upon return, dstSizes[i] will contain the size of the transformed JPEG image (in bytes.)
    dstSizespointer to an array of n size_t variables that will receive the actual sizes (in bytes) of each transformed JPEG image. If dstBufs[i] points to a pre-allocated buffer, then dstSizes[i] should be set to the size of the buffer. Upon return, dstSizes[i] will contain the size of the transformed JPEG image (in bytes.)
    transformspointer to an array of n tjtransform structures, each of which specifies the transform parameters and/or cropping region for the corresponding transformed JPEG image.
    flagsthe bitwise OR of one or more of the flags
    -
    Returns
    0 if successful, or -1 if an error occurred (see tjGetErrorStr2() and tjGetErrorCode().)
    +
    Returns
    0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
    + +
    +
    + +

    ◆ tj3YUVBufSize()

    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DLLEXPORT size_t tj3YUVBufSize (int width,
    int align,
    int height,
    int subsamp 
    )
    +
    + +

    The size of the buffer (in bytes) required to hold a unified planar YUV image with the given parameters.

    +
    Parameters
    + + + + + +
    widthwidth (in pixels) of the image
    alignrow alignment (in bytes) of the image (must be a power of 2.) Setting this parameter to n specifies that each row in each plane of the image will be padded to the nearest multiple of n bytes (1 = unpadded.)
    heightheight (in pixels) of the image
    subsamplevel of chrominance subsampling in the image (see Chrominance subsampling options.)
    +
    +
    +
    Returns
    the size of the buffer (in bytes) required to hold the image, or 0 if the arguments are out of bounds.
    + +
    +
    + +

    ◆ tj3YUVPlaneHeight()

    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    DLLEXPORT int tj3YUVPlaneHeight (int componentID,
    int height,
    int subsamp 
    )
    +
    + +

    The plane height of a YUV image plane with the given parameters.

    +

    Refer to YUV Image Format Notes for a description of plane height.

    +
    Parameters
    + + + + +
    componentIDID number of the image plane (0 = Y, 1 = U/Cb, 2 = V/Cr)
    heightheight (in pixels) of the YUV image
    subsamplevel of chrominance subsampling in the image (see Chrominance subsampling options.)
    +
    +
    +
    Returns
    the plane height of a YUV image plane with the given parameters, or 0 if the arguments are out of bounds.
    + +
    +
    + +

    ◆ tj3YUVPlaneSize()

    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DLLEXPORT size_t tj3YUVPlaneSize (int componentID,
    int width,
    int stride,
    int height,
    int subsamp 
    )
    +
    + +

    The size of the buffer (in bytes) required to hold a YUV image plane with the given parameters.

    +
    Parameters
    + + + + + + +
    componentIDID number of the image plane (0 = Y, 1 = U/Cb, 2 = V/Cr)
    widthwidth (in pixels) of the YUV image. NOTE: this is the width of the whole image, not the plane width.
    stridebytes per row in the image plane. Setting this to 0 is the equivalent of setting it to the plane width.
    heightheight (in pixels) of the YUV image. NOTE: this is the height of the whole image, not the plane height.
    subsamplevel of chrominance subsampling in the image (see Chrominance subsampling options.)
    +
    +
    +
    Returns
    the size of the buffer (in bytes) required to hold the YUV image plane, or 0 if the arguments are out of bounds.
    + +
    +
    + +

    ◆ tj3YUVPlaneWidth()

    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    DLLEXPORT int tj3YUVPlaneWidth (int componentID,
    int width,
    int subsamp 
    )
    +
    + +

    The plane width of a YUV image plane with the given parameters.

    +

    Refer to YUV Image Format Notes for a description of plane width.

    +
    Parameters
    + + + + +
    componentIDID number of the image plane (0 = Y, 1 = U/Cb, 2 = V/Cr)
    widthwidth (in pixels) of the YUV image
    subsamplevel of chrominance subsampling in the image (see Chrominance subsampling options.)
    +
    +
    +
    Returns
    the plane width of a YUV image plane with the given parameters, or 0 if the arguments are out of bounds.
    @@ -2775,8 +3380,8 @@

    -

    Alpha offset (in bytes) for a given pixel format.

    -

    This specifies the number of bytes that the alpha component is offset from the start of the pixel. For instance, if a pixel of format TJPF_BGRA is stored in unsigned char pixel[], then the alpha component will be pixel[tjAlphaOffset[TJPF_BGRA]]. This will be -1 if the pixel format does not have an alpha component.

    +

    Alpha offset (in samples) for a given pixel format.

    +

    This specifies the number of samples that the alpha component is offset from the start of the pixel. For instance, if an 8-bit-per-component pixel of format TJPF_BGRA is stored in unsigned char pixel[], then the alpha component will be pixel[tjAlphaOffset[TJPF_BGRA]]. This will be -1 if the pixel format does not have an alpha component.

    @@ -2800,8 +3405,8 @@

    -

    Blue offset (in bytes) for a given pixel format.

    -

    This specifies the number of bytes that the blue component is offset from the start of the pixel. For instance, if a pixel of format TJPF_BGRX is stored in unsigned char pixel[], then the blue component will be pixel[tjBlueOffset[TJPF_BGRX]]. This will be -1 if the pixel format does not have a blue component.

    +

    Blue offset (in samples) for a given pixel format.

    +

    This specifies the number of samples that the blue component is offset from the start of the pixel. For instance, if an 8-bit-per-component pixel of format TJPF_BGRX is stored in unsigned char pixel[], then the blue component will be pixel[tjBlueOffset[TJPF_BGRX]]. This will be -1 if the pixel format does not have a blue component.

    @@ -2825,8 +3430,8 @@

    -

    Green offset (in bytes) for a given pixel format.

    -

    This specifies the number of bytes that the green component is offset from the start of the pixel. For instance, if a pixel of format TJPF_BGRX is stored in unsigned char pixel[], then the green component will be pixel[tjGreenOffset[TJPF_BGRX]]. This will be -1 if the pixel format does not have a green component.

    +

    Green offset (in samples) for a given pixel format.

    +

    This specifies the number of samples that the green component is offset from the start of the pixel. For instance, if an 8-bit-per-component pixel of format TJPF_BGRX is stored in unsigned char pixel[], then the green component will be pixel[tjGreenOffset[TJPF_BGRX]]. This will be -1 if the pixel format does not have a green component.

    @@ -2912,7 +3517,7 @@

    -

    Pixel size (in bytes) for a given pixel format.

    +

    Pixel size (in samples) for a given pixel format.

    @@ -2936,8 +3541,56 @@

    -

    Red offset (in bytes) for a given pixel format.

    -

    This specifies the number of bytes that the red component is offset from the start of the pixel. For instance, if a pixel of format TJPF_BGRX is stored in unsigned char pixel[], then the red component will be pixel[tjRedOffset[TJPF_BGRX]]. This will be -1 if the pixel format does not have a red component.

    +

    Red offset (in samples) for a given pixel format.

    +

    This specifies the number of samples that the red component is offset from the start of the pixel. For instance, if an 8-bit-per-component pixel of format TJPF_BGRX is stored in unsigned char pixel[], then the red component will be pixel[tjRedOffset[TJPF_BGRX]]. This will be -1 if the pixel format does not have a red component.

    + + + +
    +

    ◆ TJUNCROPPED

    + +
    +
    + + + + + +
    + + + + +
    const tjregion TJUNCROPPED
    +
    +static
    +
    + +

    A tjregion structure that specifies no cropping.

    + +
    +
    + +

    ◆ TJUNSCALED

    + +
    +
    + + + + + +
    + + + + +
    const tjscalingfactor TJUNSCALED
    +
    +static
    +
    + +

    A tjscalingfactor structure that specifies a scaling factor of 1/1 (no scaling)

    diff --git a/doc/html/index.html b/doc/html/index.html index 8d06e0bcd..7c9ccd28f 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -23,7 +23,7 @@
    TurboJPEG -  2.2 +  3
    diff --git a/doc/html/modules.html b/doc/html/modules.html index f0d4eb56f..b6c30d89f 100644 --- a/doc/html/modules.html +++ b/doc/html/modules.html @@ -23,7 +23,7 @@
    TurboJPEG -  2.2 +  3
    diff --git a/doc/html/search/all_6.js b/doc/html/search/all_6.js index 7e2ed520b..553066484 100644 --- a/doc/html/search/all_6.js +++ b/doc/html/search/all_6.js @@ -1,106 +1,140 @@ var searchData= [ - ['tj_5fnumcs_8',['TJ_NUMCS',['../group___turbo_j_p_e_g.html#ga39f57a6fb02d9cf32e7b6890099b5a71',1,'turbojpeg.h']]], - ['tj_5fnumerr_9',['TJ_NUMERR',['../group___turbo_j_p_e_g.html#ga79bde1b4a3e2351e00887e47781b966e',1,'turbojpeg.h']]], - ['tj_5fnumpf_10',['TJ_NUMPF',['../group___turbo_j_p_e_g.html#ga7010a4402f54a45ba822ad8675a4655e',1,'turbojpeg.h']]], - ['tj_5fnumsamp_11',['TJ_NUMSAMP',['../group___turbo_j_p_e_g.html#ga5ef3d169162ce77ce348e292a0b7477c',1,'turbojpeg.h']]], - ['tj_5fnumxop_12',['TJ_NUMXOP',['../group___turbo_j_p_e_g.html#ga0f6dbd18adf38b7d46ac547f0f4d562c',1,'turbojpeg.h']]], - ['tjalloc_13',['tjAlloc',['../group___turbo_j_p_e_g.html#gaec627dd4c5f30b7a775a7aea3bec5d83',1,'turbojpeg.h']]], - ['tjalphaoffset_14',['tjAlphaOffset',['../group___turbo_j_p_e_g.html#ga5af0ab065feefd526debf1e20c43e837',1,'turbojpeg.h']]], - ['tjblueoffset_15',['tjBlueOffset',['../group___turbo_j_p_e_g.html#ga84e2e35d3f08025f976ec1ec53693dea',1,'turbojpeg.h']]], - ['tjbufsize_16',['tjBufSize',['../group___turbo_j_p_e_g.html#ga67ac12fee79073242cb216e07c9f1f90',1,'turbojpeg.h']]], - ['tjbufsizeyuv2_17',['tjBufSizeYUV2',['../group___turbo_j_p_e_g.html#ga5e5aac9e8bcf17049279301e2466474c',1,'turbojpeg.h']]], - ['tjcompress2_18',['tjCompress2',['../group___turbo_j_p_e_g.html#gafbdce0112fd78fd38efae841443a9bcf',1,'turbojpeg.h']]], - ['tjcompressfromyuv_19',['tjCompressFromYUV',['../group___turbo_j_p_e_g.html#gab40f5096a72fd7e5bda9d6b58fa37e2e',1,'turbojpeg.h']]], - ['tjcompressfromyuvplanes_20',['tjCompressFromYUVPlanes',['../group___turbo_j_p_e_g.html#ga29ec5dfbd2d84b8724e951d6fa0d5d9e',1,'turbojpeg.h']]], - ['tjcs_21',['TJCS',['../group___turbo_j_p_e_g.html#ga4f83ad3368e0e29d1957be0efa7c3720',1,'turbojpeg.h']]], - ['tjcs_5fcmyk_22',['TJCS_CMYK',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a6c8b636152ac8195b869587db315ee53',1,'turbojpeg.h']]], - ['tjcs_5fgray_23',['TJCS_GRAY',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720ab3e7d6a87f695e45b81c1b5262b5a50a',1,'turbojpeg.h']]], - ['tjcs_5frgb_24',['TJCS_RGB',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a677cb7ccb85c4038ac41964a2e09e555',1,'turbojpeg.h']]], - ['tjcs_5fycbcr_25',['TJCS_YCbCr',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a7389b8f65bb387ffedce3efd0d78ec75',1,'turbojpeg.h']]], - ['tjcs_5fycck_26',['TJCS_YCCK',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a53839e0fe867b76b58d16b0a1a7c598e',1,'turbojpeg.h']]], - ['tjdecodeyuv_27',['tjDecodeYUV',['../group___turbo_j_p_e_g.html#ga97c2cedc1e2bade15a84164c94e503c1',1,'turbojpeg.h']]], - ['tjdecodeyuvplanes_28',['tjDecodeYUVPlanes',['../group___turbo_j_p_e_g.html#ga10e837c07fa9d25770565b237d3898d9',1,'turbojpeg.h']]], - ['tjdecompress2_29',['tjDecompress2',['../group___turbo_j_p_e_g.html#gae9eccef8b682a48f43a9117c231ed013',1,'turbojpeg.h']]], - ['tjdecompressheader4_30',['tjDecompressHeader4',['../group___turbo_j_p_e_g.html#gac104e6e729f57f195009405949d198dc',1,'turbojpeg.h']]], - ['tjdecompresstoyuv2_31',['tjDecompressToYUV2',['../group___turbo_j_p_e_g.html#ga5a3093e325598c17a9f004323af6fafa',1,'turbojpeg.h']]], - ['tjdecompresstoyuvplanes_32',['tjDecompressToYUVPlanes',['../group___turbo_j_p_e_g.html#gaa59f901a5258ada5bd0185ad59368540',1,'turbojpeg.h']]], - ['tjdestroy_33',['tjDestroy',['../group___turbo_j_p_e_g.html#ga75f355fa27225ba1a4ee392c852394d2',1,'turbojpeg.h']]], - ['tjencodeyuv3_34',['tjEncodeYUV3',['../group___turbo_j_p_e_g.html#ga5d619e0a02b71e05a8dffb764f6d7a64',1,'turbojpeg.h']]], - ['tjencodeyuvplanes_35',['tjEncodeYUVPlanes',['../group___turbo_j_p_e_g.html#gae2d04c72457fe7f4d60cf78ab1b1feb1',1,'turbojpeg.h']]], - ['tjerr_36',['TJERR',['../group___turbo_j_p_e_g.html#gafbc17cfa57d0d5d11fea35ac025950fe',1,'turbojpeg.h']]], - ['tjerr_5ffatal_37',['TJERR_FATAL',['../group___turbo_j_p_e_g.html#ggafbc17cfa57d0d5d11fea35ac025950feafc9cceeada13122b09e4851e3788039a',1,'turbojpeg.h']]], - ['tjerr_5fwarning_38',['TJERR_WARNING',['../group___turbo_j_p_e_g.html#ggafbc17cfa57d0d5d11fea35ac025950fea342dd6e2aedb47bb257b4e7568329b59',1,'turbojpeg.h']]], - ['tjflag_5faccuratedct_39',['TJFLAG_ACCURATEDCT',['../group___turbo_j_p_e_g.html#gacb233cfd722d66d1ccbf48a7de81f0e0',1,'turbojpeg.h']]], - ['tjflag_5farithmetic_40',['TJFLAG_ARITHMETIC',['../group___turbo_j_p_e_g.html#ga91fb6ac6054a32375f1b90d48129f335',1,'turbojpeg.h']]], - ['tjflag_5fbottomup_41',['TJFLAG_BOTTOMUP',['../group___turbo_j_p_e_g.html#ga72ecf4ebe6eb702d3c6f5ca27455e1ec',1,'turbojpeg.h']]], - ['tjflag_5ffastdct_42',['TJFLAG_FASTDCT',['../group___turbo_j_p_e_g.html#gaabce235db80d3f698b27f36cbd453da2',1,'turbojpeg.h']]], - ['tjflag_5ffastupsample_43',['TJFLAG_FASTUPSAMPLE',['../group___turbo_j_p_e_g.html#ga4ee4506c81177a06f77e2504a22efd2d',1,'turbojpeg.h']]], - ['tjflag_5flimitscans_44',['TJFLAG_LIMITSCANS',['../group___turbo_j_p_e_g.html#ga163e6482dc5096831feef9c79ff3f805',1,'turbojpeg.h']]], - ['tjflag_5flossless_45',['TJFLAG_LOSSLESS',['../group___turbo_j_p_e_g.html#gaaf0e8b612bb5b981329db9f30e2115bd',1,'turbojpeg.h']]], - ['tjflag_5fnorealloc_46',['TJFLAG_NOREALLOC',['../group___turbo_j_p_e_g.html#ga8808d403c68b62aaa58a4c1e58e98963',1,'turbojpeg.h']]], - ['tjflag_5fprogressive_47',['TJFLAG_PROGRESSIVE',['../group___turbo_j_p_e_g.html#ga43b426750b46190a25d34a67ef76df1b',1,'turbojpeg.h']]], - ['tjflag_5fstoponwarning_48',['TJFLAG_STOPONWARNING',['../group___turbo_j_p_e_g.html#ga519cfa4ef6c18d9e5b455fdf59306a3a',1,'turbojpeg.h']]], - ['tjfree_49',['tjFree',['../group___turbo_j_p_e_g.html#gaea863d2da0cdb609563aabdf9196514b',1,'turbojpeg.h']]], - ['tjgeterrorcode_50',['tjGetErrorCode',['../group___turbo_j_p_e_g.html#ga414feeffbf860ebd31c745df203de410',1,'turbojpeg.h']]], - ['tjgeterrorstr2_51',['tjGetErrorStr2',['../group___turbo_j_p_e_g.html#ga1ead8574f9f39fbafc6b497124e7aafa',1,'turbojpeg.h']]], - ['tjgetscalingfactors_52',['tjGetScalingFactors',['../group___turbo_j_p_e_g.html#ga193d0977b3b9966d53a6c402e90899b1',1,'turbojpeg.h']]], - ['tjgreenoffset_53',['tjGreenOffset',['../group___turbo_j_p_e_g.html#ga82d6e35da441112a411da41923c0ba2f',1,'turbojpeg.h']]], - ['tjhandle_54',['tjhandle',['../group___turbo_j_p_e_g.html#ga758d2634ecb4949de7815cba621f5763',1,'turbojpeg.h']]], - ['tjinitcompress_55',['tjInitCompress',['../group___turbo_j_p_e_g.html#ga9d63a05fc6d813f4aae06107041a37e8',1,'turbojpeg.h']]], - ['tjinitdecompress_56',['tjInitDecompress',['../group___turbo_j_p_e_g.html#ga52300eac3f3d9ef4bab303bc244f62d3',1,'turbojpeg.h']]], - ['tjinittransform_57',['tjInitTransform',['../group___turbo_j_p_e_g.html#ga928beff6ac248ceadf01089fc6b41957',1,'turbojpeg.h']]], - ['tjloadimage_58',['tjLoadImage',['../group___turbo_j_p_e_g.html#gaffbd83c375e79f5db4b5c5d8ad4466e7',1,'turbojpeg.h']]], - ['tjmcuheight_59',['tjMCUHeight',['../group___turbo_j_p_e_g.html#gabd247bb9fecb393eca57366feb8327bf',1,'turbojpeg.h']]], - ['tjmcuwidth_60',['tjMCUWidth',['../group___turbo_j_p_e_g.html#ga9e61e7cd47a15a173283ba94e781308c',1,'turbojpeg.h']]], - ['tjpad_61',['TJPAD',['../group___turbo_j_p_e_g.html#ga0aba955473315e405295d978f0c16511',1,'turbojpeg.h']]], - ['tjpf_62',['TJPF',['../group___turbo_j_p_e_g.html#gac916144e26c3817ac514e64ae5d12e2a',1,'turbojpeg.h']]], - ['tjpf_5fabgr_63',['TJPF_ABGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa1ba1a7f1631dbeaa49a0a85fc4a40081',1,'turbojpeg.h']]], - ['tjpf_5fargb_64',['TJPF_ARGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aae8f846ed9d9de99b6e1dfe448848765c',1,'turbojpeg.h']]], - ['tjpf_5fbgr_65',['TJPF_BGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aab10624437fb8ef495a0b153e65749839',1,'turbojpeg.h']]], - ['tjpf_5fbgra_66',['TJPF_BGRA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aac037ff1845cf9b74bb81a3659c2b9fb4',1,'turbojpeg.h']]], - ['tjpf_5fbgrx_67',['TJPF_BGRX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa2a1fbf569ca79897eae886e3376ca4c8',1,'turbojpeg.h']]], - ['tjpf_5fcmyk_68',['TJPF_CMYK',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7f5100ec44c91994e243f1cf55553f8b',1,'turbojpeg.h']]], - ['tjpf_5fgray_69',['TJPF_GRAY',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa5431b54b015337705f13118073711a1a',1,'turbojpeg.h']]], - ['tjpf_5frgb_70',['TJPF_RGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7ce93230bff449518ce387c17e6ed37c',1,'turbojpeg.h']]], - ['tjpf_5frgba_71',['TJPF_RGBA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa88d2e88fab67f6503cf972e14851cc12',1,'turbojpeg.h']]], - ['tjpf_5frgbx_72',['TJPF_RGBX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa83973bebb7e2dc6fa8bae89ff3f42e01',1,'turbojpeg.h']]], - ['tjpf_5funknown_73',['TJPF_UNKNOWN',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa84c1a6cead7952998e2fb895844a21ed',1,'turbojpeg.h']]], - ['tjpf_5fxbgr_74',['TJPF_XBGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aaf6603b27147de47e212e75dac027b2af',1,'turbojpeg.h']]], - ['tjpf_5fxrgb_75',['TJPF_XRGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aadae996905efcfa3b42a0bb3bea7f9d84',1,'turbojpeg.h']]], - ['tjpixelsize_76',['tjPixelSize',['../group___turbo_j_p_e_g.html#gad77cf8fe5b2bfd3cb3f53098146abb4c',1,'turbojpeg.h']]], - ['tjplaneheight_77',['tjPlaneHeight',['../group___turbo_j_p_e_g.html#ga1a209696c6a80748f20e134b3c64789f',1,'turbojpeg.h']]], - ['tjplanesizeyuv_78',['tjPlaneSizeYUV',['../group___turbo_j_p_e_g.html#gab4ab7b24f6e797d79abaaa670373961d',1,'turbojpeg.h']]], - ['tjplanewidth_79',['tjPlaneWidth',['../group___turbo_j_p_e_g.html#ga63fb66bb1e36c74008c4634360becbb1',1,'turbojpeg.h']]], - ['tjredoffset_80',['tjRedOffset',['../group___turbo_j_p_e_g.html#gadd9b446742ac8a3923f7992c7988fea8',1,'turbojpeg.h']]], - ['tjregion_81',['tjregion',['../structtjregion.html',1,'']]], - ['tjsamp_82',['TJSAMP',['../group___turbo_j_p_e_g.html#ga1d047060ea80bb9820d540bb928e9074',1,'turbojpeg.h']]], - ['tjsamp_5f411_83',['TJSAMP_411',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a28ec62575e5ea295c3fde3001dc628e2',1,'turbojpeg.h']]], - ['tjsamp_5f420_84',['TJSAMP_420',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a63085dbf683cfe39e513cdb6343e3737',1,'turbojpeg.h']]], - ['tjsamp_5f422_85',['TJSAMP_422',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a136130902cc578f11f32429b59368404',1,'turbojpeg.h']]], - ['tjsamp_5f440_86',['TJSAMP_440',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074accf740e6f3aa6ba20ba922cad13cb974',1,'turbojpeg.h']]], - ['tjsamp_5f444_87',['TJSAMP_444',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074afb8da4f44197837bdec0a4f593dacae3',1,'turbojpeg.h']]], - ['tjsamp_5fgray_88',['TJSAMP_GRAY',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a3f1c9504842ddc7a48d0f690754b6248',1,'turbojpeg.h']]], - ['tjsaveimage_89',['tjSaveImage',['../group___turbo_j_p_e_g.html#ga6f445b22d8933ae4815b3370a538d879',1,'turbojpeg.h']]], - ['tjscaled_90',['TJSCALED',['../group___turbo_j_p_e_g.html#ga84878bb65404204743aa18cac02781df',1,'turbojpeg.h']]], - ['tjscalingfactor_91',['tjscalingfactor',['../structtjscalingfactor.html',1,'']]], - ['tjtransform_92',['tjtransform',['../structtjtransform.html',1,'tjtransform'],['../group___turbo_j_p_e_g.html#ga9cb8abf4cc91881e04a0329b2270be25',1,'tjTransform(tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, int n, unsigned char **dstBufs, unsigned long *dstSizes, tjtransform *transforms, int flags): turbojpeg.h'],['../group___turbo_j_p_e_g.html#ga504805ec0161f1b505397ca0118bf8fd',1,'tjtransform(): turbojpeg.h']]], - ['tjxop_93',['TJXOP',['../group___turbo_j_p_e_g.html#ga2de531af4e7e6c4f124908376b354866',1,'turbojpeg.h']]], - ['tjxop_5fhflip_94',['TJXOP_HFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aa0df69776caa30f0fa28e26332d311ce',1,'turbojpeg.h']]], - ['tjxop_5fnone_95',['TJXOP_NONE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aad88c0366cd3f7d0eac9d7a3fa1c2c27',1,'turbojpeg.h']]], - ['tjxop_5frot180_96',['TJXOP_ROT180',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a140952eb8dd0300accfcc22726d69692',1,'turbojpeg.h']]], - ['tjxop_5frot270_97',['TJXOP_ROT270',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a3064ee5dfb7f032df332818587567a08',1,'turbojpeg.h']]], - ['tjxop_5frot90_98',['TJXOP_ROT90',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a43b2bbb23bc4bd548422d43fbe9af128',1,'turbojpeg.h']]], - ['tjxop_5ftranspose_99',['TJXOP_TRANSPOSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a31060aed199f886afdd417f80499c32d',1,'turbojpeg.h']]], - ['tjxop_5ftransverse_100',['TJXOP_TRANSVERSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866af3b14d488aea6ece9e5b3df73a74d6a4',1,'turbojpeg.h']]], - ['tjxop_5fvflip_101',['TJXOP_VFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a324eddfbec53b7e691f61e56929d0d5d',1,'turbojpeg.h']]], - ['tjxopt_5farithmetic_102',['TJXOPT_ARITHMETIC',['../group___turbo_j_p_e_g.html#gaecaaa3b7e2af812592c015d83207f010',1,'turbojpeg.h']]], - ['tjxopt_5fcopynone_103',['TJXOPT_COPYNONE',['../group___turbo_j_p_e_g.html#ga153b468cfb905d0de61706c838986fe8',1,'turbojpeg.h']]], - ['tjxopt_5fcrop_104',['TJXOPT_CROP',['../group___turbo_j_p_e_g.html#ga9c771a757fc1294add611906b89ab2d2',1,'turbojpeg.h']]], - ['tjxopt_5fgray_105',['TJXOPT_GRAY',['../group___turbo_j_p_e_g.html#ga3acee7b48ade1b99e5588736007c2589',1,'turbojpeg.h']]], - ['tjxopt_5fnooutput_106',['TJXOPT_NOOUTPUT',['../group___turbo_j_p_e_g.html#gafbf992bbf6e006705886333703ffab31',1,'turbojpeg.h']]], - ['tjxopt_5fperfect_107',['TJXOPT_PERFECT',['../group___turbo_j_p_e_g.html#ga50e03cb5ed115330e212417429600b00',1,'turbojpeg.h']]], - ['tjxopt_5fprogressive_108',['TJXOPT_PROGRESSIVE',['../group___turbo_j_p_e_g.html#gad2371c80674584ecc1a7d75e564cf026',1,'turbojpeg.h']]], - ['tjxopt_5ftrim_109',['TJXOPT_TRIM',['../group___turbo_j_p_e_g.html#ga319826b7eb1583c0595bbe7b95428709',1,'turbojpeg.h']]], - ['turbojpeg_110',['TurboJPEG',['../group___turbo_j_p_e_g.html',1,'']]] + ['tj3alloc_8',['tj3Alloc',['../group___turbo_j_p_e_g.html#gab40a0b231122f536e503e3394569a68d',1,'turbojpeg.h']]], + ['tj3compress12_9',['tj3Compress12',['../group___turbo_j_p_e_g.html#ga9a1968c384ec7abb6122830253ebf570',1,'turbojpeg.h']]], + ['tj3compress16_10',['tj3Compress16',['../group___turbo_j_p_e_g.html#ga77901b71d0471784f318ada31ff4e7bd',1,'turbojpeg.h']]], + ['tj3compress8_11',['tj3Compress8',['../group___turbo_j_p_e_g.html#ga2cc418a2dab709ad7f30f5b25905f138',1,'turbojpeg.h']]], + ['tj3compressfromyuv8_12',['tj3CompressFromYUV8',['../group___turbo_j_p_e_g.html#ga041c870d9c669eb3f385c78f4346c43f',1,'turbojpeg.h']]], + ['tj3compressfromyuvplanes8_13',['tj3CompressFromYUVPlanes8',['../group___turbo_j_p_e_g.html#gac9f5ace3e73805b476c95dda9f8d0cd0',1,'turbojpeg.h']]], + ['tj3decodeyuv8_14',['tj3DecodeYUV8',['../group___turbo_j_p_e_g.html#gaa1eb574f38b1c1de43a6c7aafcf68d8c',1,'turbojpeg.h']]], + ['tj3decodeyuvplanes8_15',['tj3DecodeYUVPlanes8',['../group___turbo_j_p_e_g.html#gad366f1915f82c1ad4e7e37ebe073ca89',1,'turbojpeg.h']]], + ['tj3decompress12_16',['tj3Decompress12',['../group___turbo_j_p_e_g.html#ga39b848f01781ad74a5b3941c012b6199',1,'turbojpeg.h']]], + ['tj3decompress16_17',['tj3Decompress16',['../group___turbo_j_p_e_g.html#gaa074e63f9beb0b3ff42b833a4049df6e',1,'turbojpeg.h']]], + ['tj3decompress8_18',['tj3Decompress8',['../group___turbo_j_p_e_g.html#ga1169c7c1a26ec18c9e6122cb8ae64013',1,'turbojpeg.h']]], + ['tj3decompressheader_19',['tj3DecompressHeader',['../group___turbo_j_p_e_g.html#ga96d2c4b3432f9d88ad14758ae240b8d1',1,'turbojpeg.h']]], + ['tj3decompresstoyuv8_20',['tj3DecompressToYUV8',['../group___turbo_j_p_e_g.html#ga1e6bf6a19fec3f9fa7534348879d8320',1,'turbojpeg.h']]], + ['tj3decompresstoyuvplanes8_21',['tj3DecompressToYUVPlanes8',['../group___turbo_j_p_e_g.html#ga934373482dbbf257f2280505b6ff4fb5',1,'turbojpeg.h']]], + ['tj3destroy_22',['tj3Destroy',['../group___turbo_j_p_e_g.html#ga53fbadf4560e95a65b8f5ab81703fe82',1,'turbojpeg.h']]], + ['tj3encodeyuv8_23',['tj3EncodeYUV8',['../group___turbo_j_p_e_g.html#ga2a8d50f130bde10f0a04030f8cc59936',1,'turbojpeg.h']]], + ['tj3encodeyuvplanes8_24',['tj3EncodeYUVPlanes8',['../group___turbo_j_p_e_g.html#gae2e9df38790e9bddc249d04cb158a4cf',1,'turbojpeg.h']]], + ['tj3free_25',['tj3Free',['../group___turbo_j_p_e_g.html#gaddb84fb6c81769e9faa0f5a63b296606',1,'turbojpeg.h']]], + ['tj3get_26',['tj3Get',['../group___turbo_j_p_e_g.html#ga34af9ba3183bdf0ec7c8f47bb9a4c84f',1,'turbojpeg.h']]], + ['tj3geterrorcode_27',['tj3GetErrorCode',['../group___turbo_j_p_e_g.html#gab8c8279f1415fe425ff30dbbc56013bd',1,'turbojpeg.h']]], + ['tj3geterrorstr_28',['tj3GetErrorStr',['../group___turbo_j_p_e_g.html#gaf2aab0e6dbb3edc57646b0fec25e8bb2',1,'turbojpeg.h']]], + ['tj3getscalingfactors_29',['tj3GetScalingFactors',['../group___turbo_j_p_e_g.html#ga74397f8e0587d4233182c72f085aaf04',1,'turbojpeg.h']]], + ['tj3init_30',['tj3Init',['../group___turbo_j_p_e_g.html#ga69c09d39f97ec30250ad3605ace7e5df',1,'turbojpeg.h']]], + ['tj3jpegbufsize_31',['tj3JPEGBufSize',['../group___turbo_j_p_e_g.html#gac6285e58e35a35d871d7162ec5a929c4',1,'turbojpeg.h']]], + ['tj3loadimage12_32',['tj3LoadImage12',['../group___turbo_j_p_e_g.html#ga1f03c26892a26d4ce077ed6a4ac40e8f',1,'turbojpeg.h']]], + ['tj3loadimage16_33',['tj3LoadImage16',['../group___turbo_j_p_e_g.html#ga638aeba63e0ccb89d472fdbf34224cfc',1,'turbojpeg.h']]], + ['tj3loadimage8_34',['tj3LoadImage8',['../group___turbo_j_p_e_g.html#ga565aaae7be3f8ca9099b56655c893251',1,'turbojpeg.h']]], + ['tj3saveimage12_35',['tj3SaveImage12',['../group___turbo_j_p_e_g.html#ga7c64b5106d04267a46aad85f9714ad90',1,'turbojpeg.h']]], + ['tj3saveimage16_36',['tj3SaveImage16',['../group___turbo_j_p_e_g.html#ga0fd87851f4266aca24bf4594dd0c0e71',1,'turbojpeg.h']]], + ['tj3saveimage8_37',['tj3SaveImage8',['../group___turbo_j_p_e_g.html#gaa4ec838988e469cc15618e4690cc8722',1,'turbojpeg.h']]], + ['tj3set_38',['tj3Set',['../group___turbo_j_p_e_g.html#gaddf92640bfee3e8622218c713e77e7db',1,'turbojpeg.h']]], + ['tj3setcroppingregion_39',['tj3SetCroppingRegion',['../group___turbo_j_p_e_g.html#gaa49c7bd4c9431667a043cfc93388ba1c',1,'turbojpeg.h']]], + ['tj3setscalingfactor_40',['tj3SetScalingFactor',['../group___turbo_j_p_e_g.html#ga89da17ee1e43ff423382cbc145803c75',1,'turbojpeg.h']]], + ['tj3transform_41',['tj3Transform',['../group___turbo_j_p_e_g.html#gaff23ba1dcabed456794b844791613920',1,'turbojpeg.h']]], + ['tj3yuvbufsize_42',['tj3YUVBufSize',['../group___turbo_j_p_e_g.html#gaaebaa16973a0f550a66eca5765ed0546',1,'turbojpeg.h']]], + ['tj3yuvplaneheight_43',['tj3YUVPlaneHeight',['../group___turbo_j_p_e_g.html#ga969767ec8180cc3edd99cf507f87299b',1,'turbojpeg.h']]], + ['tj3yuvplanesize_44',['tj3YUVPlaneSize',['../group___turbo_j_p_e_g.html#gacc19d265edce76b46146f59579f9438d',1,'turbojpeg.h']]], + ['tj3yuvplanewidth_45',['tj3YUVPlaneWidth',['../group___turbo_j_p_e_g.html#gac99d1933ede1d59fcada9a826e88eb2d',1,'turbojpeg.h']]], + ['tj_5fnumcs_46',['TJ_NUMCS',['../group___turbo_j_p_e_g.html#ga39f57a6fb02d9cf32e7b6890099b5a71',1,'turbojpeg.h']]], + ['tj_5fnumerr_47',['TJ_NUMERR',['../group___turbo_j_p_e_g.html#ga79bde1b4a3e2351e00887e47781b966e',1,'turbojpeg.h']]], + ['tj_5fnuminit_48',['TJ_NUMINIT',['../group___turbo_j_p_e_g.html#ga5e0e8c784295c636f0bf8dab93c4bddf',1,'turbojpeg.h']]], + ['tj_5fnumparam_49',['TJ_NUMPARAM',['../group___turbo_j_p_e_g.html#gaa628be5db276fc3676dfba205d45d780',1,'turbojpeg.h']]], + ['tj_5fnumpf_50',['TJ_NUMPF',['../group___turbo_j_p_e_g.html#ga7010a4402f54a45ba822ad8675a4655e',1,'turbojpeg.h']]], + ['tj_5fnumsamp_51',['TJ_NUMSAMP',['../group___turbo_j_p_e_g.html#ga5ef3d169162ce77ce348e292a0b7477c',1,'turbojpeg.h']]], + ['tj_5fnumxop_52',['TJ_NUMXOP',['../group___turbo_j_p_e_g.html#ga0f6dbd18adf38b7d46ac547f0f4d562c',1,'turbojpeg.h']]], + ['tjalphaoffset_53',['tjAlphaOffset',['../group___turbo_j_p_e_g.html#ga5af0ab065feefd526debf1e20c43e837',1,'turbojpeg.h']]], + ['tjblueoffset_54',['tjBlueOffset',['../group___turbo_j_p_e_g.html#ga84e2e35d3f08025f976ec1ec53693dea',1,'turbojpeg.h']]], + ['tjcs_55',['TJCS',['../group___turbo_j_p_e_g.html#ga4f83ad3368e0e29d1957be0efa7c3720',1,'turbojpeg.h']]], + ['tjcs_5fcmyk_56',['TJCS_CMYK',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a6c8b636152ac8195b869587db315ee53',1,'turbojpeg.h']]], + ['tjcs_5fgray_57',['TJCS_GRAY',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720ab3e7d6a87f695e45b81c1b5262b5a50a',1,'turbojpeg.h']]], + ['tjcs_5frgb_58',['TJCS_RGB',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a677cb7ccb85c4038ac41964a2e09e555',1,'turbojpeg.h']]], + ['tjcs_5fycbcr_59',['TJCS_YCbCr',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a7389b8f65bb387ffedce3efd0d78ec75',1,'turbojpeg.h']]], + ['tjcs_5fycck_60',['TJCS_YCCK',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a53839e0fe867b76b58d16b0a1a7c598e',1,'turbojpeg.h']]], + ['tjerr_61',['TJERR',['../group___turbo_j_p_e_g.html#gafbc17cfa57d0d5d11fea35ac025950fe',1,'turbojpeg.h']]], + ['tjerr_5ffatal_62',['TJERR_FATAL',['../group___turbo_j_p_e_g.html#ggafbc17cfa57d0d5d11fea35ac025950feafc9cceeada13122b09e4851e3788039a',1,'turbojpeg.h']]], + ['tjerr_5fwarning_63',['TJERR_WARNING',['../group___turbo_j_p_e_g.html#ggafbc17cfa57d0d5d11fea35ac025950fea342dd6e2aedb47bb257b4e7568329b59',1,'turbojpeg.h']]], + ['tjgreenoffset_64',['tjGreenOffset',['../group___turbo_j_p_e_g.html#ga82d6e35da441112a411da41923c0ba2f',1,'turbojpeg.h']]], + ['tjhandle_65',['tjhandle',['../group___turbo_j_p_e_g.html#ga758d2634ecb4949de7815cba621f5763',1,'turbojpeg.h']]], + ['tjinit_66',['TJINIT',['../group___turbo_j_p_e_g.html#ga3850bbee1313e752e667b4eb08b1e086',1,'turbojpeg.h']]], + ['tjinit_5fcompress_67',['TJINIT_COMPRESS',['../group___turbo_j_p_e_g.html#gga3850bbee1313e752e667b4eb08b1e086aa45ac279e3dc6ffabc4b0f45864da796',1,'turbojpeg.h']]], + ['tjinit_5fdecompress_68',['TJINIT_DECOMPRESS',['../group___turbo_j_p_e_g.html#gga3850bbee1313e752e667b4eb08b1e086a4b8ca1ef700699b71350700bf95c2167',1,'turbojpeg.h']]], + ['tjinit_5ftransform_69',['TJINIT_TRANSFORM',['../group___turbo_j_p_e_g.html#gga3850bbee1313e752e667b4eb08b1e086a8d58a2a4c45b3e0cd349746544a6e0c2',1,'turbojpeg.h']]], + ['tjmcuheight_70',['tjMCUHeight',['../group___turbo_j_p_e_g.html#gabd247bb9fecb393eca57366feb8327bf',1,'turbojpeg.h']]], + ['tjmcuwidth_71',['tjMCUWidth',['../group___turbo_j_p_e_g.html#ga9e61e7cd47a15a173283ba94e781308c',1,'turbojpeg.h']]], + ['tjparam_72',['TJPARAM',['../group___turbo_j_p_e_g.html#gaa0f6be63ba78278299c9f5c12031fe82',1,'turbojpeg.h']]], + ['tjparam_5farithmetic_73',['TJPARAM_ARITHMETIC',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a1c756757384308145602c040524aebf7',1,'turbojpeg.h']]], + ['tjparam_5fbottomup_74',['TJPARAM_BOTTOMUP',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a924657172695ed6cb0b128219546fcce',1,'turbojpeg.h']]], + ['tjparam_5fcolorspace_75',['TJPARAM_COLORSPACE',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a46a10d46309514907d0c39fcd86c324c',1,'turbojpeg.h']]], + ['tjparam_5fdensityunits_76',['TJPARAM_DENSITYUNITS',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a4c045981bd8a303521a401dbbe1df208',1,'turbojpeg.h']]], + ['tjparam_5ffastdct_77',['TJPARAM_FASTDCT',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a6914692ac6ec5567787d592b7563f627',1,'turbojpeg.h']]], + ['tjparam_5ffastupsample_78',['TJPARAM_FASTUPSAMPLE',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a0e051ac106f7b7402b690a5daf4869c0',1,'turbojpeg.h']]], + ['tjparam_5fjpegheight_79',['TJPARAM_JPEGHEIGHT',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a8f76673be73f2b659440a9572a65a95f',1,'turbojpeg.h']]], + ['tjparam_5fjpegwidth_80',['TJPARAM_JPEGWIDTH',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a02ab77fb294a0c9061a78cd424c82dd8',1,'turbojpeg.h']]], + ['tjparam_5flossless_81',['TJPARAM_LOSSLESS',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a249f35f0770792b19f995e603bb17c6f',1,'turbojpeg.h']]], + ['tjparam_5flosslesspsv_82',['TJPARAM_LOSSLESSPSV',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82abcc997d40e5bec84817c12b76ef84159',1,'turbojpeg.h']]], + ['tjparam_5flosslesspt_83',['TJPARAM_LOSSLESSPT',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a4a6c6f25764ecaf4231a36bff844e46a',1,'turbojpeg.h']]], + ['tjparam_5fnorealloc_84',['TJPARAM_NOREALLOC',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82ae64ffb358bc7b194fd48e0f27750b29b',1,'turbojpeg.h']]], + ['tjparam_5foptimize_85',['TJPARAM_OPTIMIZE',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a8f0af9afc0b36443751f9ee82b760aa6',1,'turbojpeg.h']]], + ['tjparam_5fprecision_86',['TJPARAM_PRECISION',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a781db82741934e8cd008d308597c59d8',1,'turbojpeg.h']]], + ['tjparam_5fprogressive_87',['TJPARAM_PROGRESSIVE',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a1716f242b3859905b4a317dae8cfb75f',1,'turbojpeg.h']]], + ['tjparam_5fquality_88',['TJPARAM_QUALITY',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a0467e8792621f2d817dc2af563d3186c',1,'turbojpeg.h']]], + ['tjparam_5frestartblocks_89',['TJPARAM_RESTARTBLOCKS',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a343c72883b7160f23f3ef46fc548a0ec',1,'turbojpeg.h']]], + ['tjparam_5frestartrows_90',['TJPARAM_RESTARTROWS',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a714367585952fe5c863f0dba5bd37e5c',1,'turbojpeg.h']]], + ['tjparam_5fscanlimit_91',['TJPARAM_SCANLIMIT',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82ac478910e20ecf61b914f9824d80f8167',1,'turbojpeg.h']]], + ['tjparam_5fstoponwarning_92',['TJPARAM_STOPONWARNING',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a555e2212079fa49b30bcd2879c6c8ddb',1,'turbojpeg.h']]], + ['tjparam_5fsubsamp_93',['TJPARAM_SUBSAMP',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a2a3494a8215d3de4fdbaeb2ba6f6b03a',1,'turbojpeg.h']]], + ['tjparam_5fxdensity_94',['TJPARAM_XDENSITY',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a4de5c9d7cab5be806143a43c3b0e0877',1,'turbojpeg.h']]], + ['tjparam_5fydensity_95',['TJPARAM_YDENSITY',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82abda48f2df7eb9b88e2b7621efb017eba',1,'turbojpeg.h']]], + ['tjpf_96',['TJPF',['../group___turbo_j_p_e_g.html#gac916144e26c3817ac514e64ae5d12e2a',1,'turbojpeg.h']]], + ['tjpf_5fabgr_97',['TJPF_ABGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa1ba1a7f1631dbeaa49a0a85fc4a40081',1,'turbojpeg.h']]], + ['tjpf_5fargb_98',['TJPF_ARGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aae8f846ed9d9de99b6e1dfe448848765c',1,'turbojpeg.h']]], + ['tjpf_5fbgr_99',['TJPF_BGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aab10624437fb8ef495a0b153e65749839',1,'turbojpeg.h']]], + ['tjpf_5fbgra_100',['TJPF_BGRA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aac037ff1845cf9b74bb81a3659c2b9fb4',1,'turbojpeg.h']]], + ['tjpf_5fbgrx_101',['TJPF_BGRX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa2a1fbf569ca79897eae886e3376ca4c8',1,'turbojpeg.h']]], + ['tjpf_5fcmyk_102',['TJPF_CMYK',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7f5100ec44c91994e243f1cf55553f8b',1,'turbojpeg.h']]], + ['tjpf_5fgray_103',['TJPF_GRAY',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa5431b54b015337705f13118073711a1a',1,'turbojpeg.h']]], + ['tjpf_5frgb_104',['TJPF_RGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7ce93230bff449518ce387c17e6ed37c',1,'turbojpeg.h']]], + ['tjpf_5frgba_105',['TJPF_RGBA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa88d2e88fab67f6503cf972e14851cc12',1,'turbojpeg.h']]], + ['tjpf_5frgbx_106',['TJPF_RGBX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa83973bebb7e2dc6fa8bae89ff3f42e01',1,'turbojpeg.h']]], + ['tjpf_5funknown_107',['TJPF_UNKNOWN',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa84c1a6cead7952998e2fb895844a21ed',1,'turbojpeg.h']]], + ['tjpf_5fxbgr_108',['TJPF_XBGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aaf6603b27147de47e212e75dac027b2af',1,'turbojpeg.h']]], + ['tjpf_5fxrgb_109',['TJPF_XRGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aadae996905efcfa3b42a0bb3bea7f9d84',1,'turbojpeg.h']]], + ['tjpixelsize_110',['tjPixelSize',['../group___turbo_j_p_e_g.html#gad77cf8fe5b2bfd3cb3f53098146abb4c',1,'turbojpeg.h']]], + ['tjredoffset_111',['tjRedOffset',['../group___turbo_j_p_e_g.html#gadd9b446742ac8a3923f7992c7988fea8',1,'turbojpeg.h']]], + ['tjregion_112',['tjregion',['../structtjregion.html',1,'']]], + ['tjsamp_113',['TJSAMP',['../group___turbo_j_p_e_g.html#ga1d047060ea80bb9820d540bb928e9074',1,'turbojpeg.h']]], + ['tjsamp_5f411_114',['TJSAMP_411',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a28ec62575e5ea295c3fde3001dc628e2',1,'turbojpeg.h']]], + ['tjsamp_5f420_115',['TJSAMP_420',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a63085dbf683cfe39e513cdb6343e3737',1,'turbojpeg.h']]], + ['tjsamp_5f422_116',['TJSAMP_422',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a136130902cc578f11f32429b59368404',1,'turbojpeg.h']]], + ['tjsamp_5f440_117',['TJSAMP_440',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074accf740e6f3aa6ba20ba922cad13cb974',1,'turbojpeg.h']]], + ['tjsamp_5f444_118',['TJSAMP_444',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074afb8da4f44197837bdec0a4f593dacae3',1,'turbojpeg.h']]], + ['tjsamp_5fgray_119',['TJSAMP_GRAY',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a3f1c9504842ddc7a48d0f690754b6248',1,'turbojpeg.h']]], + ['tjsamp_5funknown_120',['TJSAMP_UNKNOWN',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074ac124fa8f6cb41147e3d670dfbdfb7173',1,'turbojpeg.h']]], + ['tjscaled_121',['TJSCALED',['../group___turbo_j_p_e_g.html#ga84878bb65404204743aa18cac02781df',1,'turbojpeg.h']]], + ['tjscalingfactor_122',['tjscalingfactor',['../structtjscalingfactor.html',1,'']]], + ['tjtransform_123',['tjtransform',['../structtjtransform.html',1,'tjtransform'],['../group___turbo_j_p_e_g.html#ga504805ec0161f1b505397ca0118bf8fd',1,'tjtransform(): turbojpeg.h']]], + ['tjuncropped_124',['TJUNCROPPED',['../group___turbo_j_p_e_g.html#ga6f192ad58a5a5802e145149d83c643bf',1,'turbojpeg.h']]], + ['tjunscaled_125',['TJUNSCALED',['../group___turbo_j_p_e_g.html#ga7880644a0849161ad20933536169ee19',1,'turbojpeg.h']]], + ['tjxop_126',['TJXOP',['../group___turbo_j_p_e_g.html#ga2de531af4e7e6c4f124908376b354866',1,'turbojpeg.h']]], + ['tjxop_5fhflip_127',['TJXOP_HFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aa0df69776caa30f0fa28e26332d311ce',1,'turbojpeg.h']]], + ['tjxop_5fnone_128',['TJXOP_NONE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aad88c0366cd3f7d0eac9d7a3fa1c2c27',1,'turbojpeg.h']]], + ['tjxop_5frot180_129',['TJXOP_ROT180',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a140952eb8dd0300accfcc22726d69692',1,'turbojpeg.h']]], + ['tjxop_5frot270_130',['TJXOP_ROT270',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a3064ee5dfb7f032df332818587567a08',1,'turbojpeg.h']]], + ['tjxop_5frot90_131',['TJXOP_ROT90',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a43b2bbb23bc4bd548422d43fbe9af128',1,'turbojpeg.h']]], + ['tjxop_5ftranspose_132',['TJXOP_TRANSPOSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a31060aed199f886afdd417f80499c32d',1,'turbojpeg.h']]], + ['tjxop_5ftransverse_133',['TJXOP_TRANSVERSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866af3b14d488aea6ece9e5b3df73a74d6a4',1,'turbojpeg.h']]], + ['tjxop_5fvflip_134',['TJXOP_VFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a324eddfbec53b7e691f61e56929d0d5d',1,'turbojpeg.h']]], + ['tjxopt_5farithmetic_135',['TJXOPT_ARITHMETIC',['../group___turbo_j_p_e_g.html#gaecaaa3b7e2af812592c015d83207f010',1,'turbojpeg.h']]], + ['tjxopt_5fcopynone_136',['TJXOPT_COPYNONE',['../group___turbo_j_p_e_g.html#ga153b468cfb905d0de61706c838986fe8',1,'turbojpeg.h']]], + ['tjxopt_5fcrop_137',['TJXOPT_CROP',['../group___turbo_j_p_e_g.html#ga9c771a757fc1294add611906b89ab2d2',1,'turbojpeg.h']]], + ['tjxopt_5fgray_138',['TJXOPT_GRAY',['../group___turbo_j_p_e_g.html#ga3acee7b48ade1b99e5588736007c2589',1,'turbojpeg.h']]], + ['tjxopt_5fnooutput_139',['TJXOPT_NOOUTPUT',['../group___turbo_j_p_e_g.html#gafbf992bbf6e006705886333703ffab31',1,'turbojpeg.h']]], + ['tjxopt_5foptimize_140',['TJXOPT_OPTIMIZE',['../group___turbo_j_p_e_g.html#ga6bedf37aa9e1122f3ec9f7302ca59117',1,'turbojpeg.h']]], + ['tjxopt_5fperfect_141',['TJXOPT_PERFECT',['../group___turbo_j_p_e_g.html#ga50e03cb5ed115330e212417429600b00',1,'turbojpeg.h']]], + ['tjxopt_5fprogressive_142',['TJXOPT_PROGRESSIVE',['../group___turbo_j_p_e_g.html#gad2371c80674584ecc1a7d75e564cf026',1,'turbojpeg.h']]], + ['tjxopt_5ftrim_143',['TJXOPT_TRIM',['../group___turbo_j_p_e_g.html#ga319826b7eb1583c0595bbe7b95428709',1,'turbojpeg.h']]], + ['turbojpeg_144',['TurboJPEG',['../group___turbo_j_p_e_g.html',1,'']]] ]; diff --git a/doc/html/search/all_7.js b/doc/html/search/all_7.js index 2f57008af..1093ee358 100644 --- a/doc/html/search/all_7.js +++ b/doc/html/search/all_7.js @@ -1,4 +1,4 @@ var searchData= [ - ['w_111',['w',['../structtjregion.html#ab6eb73ceef584fc23c8c8097926dce42',1,'tjregion']]] + ['w_145',['w',['../structtjregion.html#ab6eb73ceef584fc23c8c8097926dce42',1,'tjregion']]] ]; diff --git a/doc/html/search/all_8.js b/doc/html/search/all_8.js index 98e47c02b..fa87fc98b 100644 --- a/doc/html/search/all_8.js +++ b/doc/html/search/all_8.js @@ -1,4 +1,4 @@ var searchData= [ - ['x_112',['x',['../structtjregion.html#a4b6a37a93997091b26a75831fa291ad9',1,'tjregion']]] + ['x_146',['x',['../structtjregion.html#a4b6a37a93997091b26a75831fa291ad9',1,'tjregion']]] ]; diff --git a/doc/html/search/all_9.js b/doc/html/search/all_9.js index 8c5b0555a..192181cd0 100644 --- a/doc/html/search/all_9.js +++ b/doc/html/search/all_9.js @@ -1,4 +1,4 @@ var searchData= [ - ['y_113',['y',['../structtjregion.html#a7b3e0c24cfe87acc80e334cafdcf22c2',1,'tjregion']]] + ['y_147',['y',['../structtjregion.html#a7b3e0c24cfe87acc80e334cafdcf22c2',1,'tjregion']]] ]; diff --git a/doc/html/search/classes_0.js b/doc/html/search/classes_0.js index 39971e8e4..5460dc5d3 100644 --- a/doc/html/search/classes_0.js +++ b/doc/html/search/classes_0.js @@ -1,6 +1,6 @@ var searchData= [ - ['tjregion_114',['tjregion',['../structtjregion.html',1,'']]], - ['tjscalingfactor_115',['tjscalingfactor',['../structtjscalingfactor.html',1,'']]], - ['tjtransform_116',['tjtransform',['../structtjtransform.html',1,'']]] + ['tjregion_148',['tjregion',['../structtjregion.html',1,'']]], + ['tjscalingfactor_149',['tjscalingfactor',['../structtjscalingfactor.html',1,'']]], + ['tjtransform_150',['tjtransform',['../structtjtransform.html',1,'']]] ]; diff --git a/doc/html/search/enums_0.js b/doc/html/search/enums_0.js index 6d5b80ad7..df0c9f0a2 100644 --- a/doc/html/search/enums_0.js +++ b/doc/html/search/enums_0.js @@ -1,8 +1,10 @@ var searchData= [ - ['tjcs_165',['TJCS',['../group___turbo_j_p_e_g.html#ga4f83ad3368e0e29d1957be0efa7c3720',1,'turbojpeg.h']]], - ['tjerr_166',['TJERR',['../group___turbo_j_p_e_g.html#gafbc17cfa57d0d5d11fea35ac025950fe',1,'turbojpeg.h']]], - ['tjpf_167',['TJPF',['../group___turbo_j_p_e_g.html#gac916144e26c3817ac514e64ae5d12e2a',1,'turbojpeg.h']]], - ['tjsamp_168',['TJSAMP',['../group___turbo_j_p_e_g.html#ga1d047060ea80bb9820d540bb928e9074',1,'turbojpeg.h']]], - ['tjxop_169',['TJXOP',['../group___turbo_j_p_e_g.html#ga2de531af4e7e6c4f124908376b354866',1,'turbojpeg.h']]] + ['tjcs_211',['TJCS',['../group___turbo_j_p_e_g.html#ga4f83ad3368e0e29d1957be0efa7c3720',1,'turbojpeg.h']]], + ['tjerr_212',['TJERR',['../group___turbo_j_p_e_g.html#gafbc17cfa57d0d5d11fea35ac025950fe',1,'turbojpeg.h']]], + ['tjinit_213',['TJINIT',['../group___turbo_j_p_e_g.html#ga3850bbee1313e752e667b4eb08b1e086',1,'turbojpeg.h']]], + ['tjparam_214',['TJPARAM',['../group___turbo_j_p_e_g.html#gaa0f6be63ba78278299c9f5c12031fe82',1,'turbojpeg.h']]], + ['tjpf_215',['TJPF',['../group___turbo_j_p_e_g.html#gac916144e26c3817ac514e64ae5d12e2a',1,'turbojpeg.h']]], + ['tjsamp_216',['TJSAMP',['../group___turbo_j_p_e_g.html#ga1d047060ea80bb9820d540bb928e9074',1,'turbojpeg.h']]], + ['tjxop_217',['TJXOP',['../group___turbo_j_p_e_g.html#ga2de531af4e7e6c4f124908376b354866',1,'turbojpeg.h']]] ]; diff --git a/doc/html/search/enumvalues_0.js b/doc/html/search/enumvalues_0.js index ff1d8a7ca..f5b344d13 100644 --- a/doc/html/search/enumvalues_0.js +++ b/doc/html/search/enumvalues_0.js @@ -1,37 +1,64 @@ var searchData= [ - ['tjcs_5fcmyk_170',['TJCS_CMYK',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a6c8b636152ac8195b869587db315ee53',1,'turbojpeg.h']]], - ['tjcs_5fgray_171',['TJCS_GRAY',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720ab3e7d6a87f695e45b81c1b5262b5a50a',1,'turbojpeg.h']]], - ['tjcs_5frgb_172',['TJCS_RGB',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a677cb7ccb85c4038ac41964a2e09e555',1,'turbojpeg.h']]], - ['tjcs_5fycbcr_173',['TJCS_YCbCr',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a7389b8f65bb387ffedce3efd0d78ec75',1,'turbojpeg.h']]], - ['tjcs_5fycck_174',['TJCS_YCCK',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a53839e0fe867b76b58d16b0a1a7c598e',1,'turbojpeg.h']]], - ['tjerr_5ffatal_175',['TJERR_FATAL',['../group___turbo_j_p_e_g.html#ggafbc17cfa57d0d5d11fea35ac025950feafc9cceeada13122b09e4851e3788039a',1,'turbojpeg.h']]], - ['tjerr_5fwarning_176',['TJERR_WARNING',['../group___turbo_j_p_e_g.html#ggafbc17cfa57d0d5d11fea35ac025950fea342dd6e2aedb47bb257b4e7568329b59',1,'turbojpeg.h']]], - ['tjpf_5fabgr_177',['TJPF_ABGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa1ba1a7f1631dbeaa49a0a85fc4a40081',1,'turbojpeg.h']]], - ['tjpf_5fargb_178',['TJPF_ARGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aae8f846ed9d9de99b6e1dfe448848765c',1,'turbojpeg.h']]], - ['tjpf_5fbgr_179',['TJPF_BGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aab10624437fb8ef495a0b153e65749839',1,'turbojpeg.h']]], - ['tjpf_5fbgra_180',['TJPF_BGRA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aac037ff1845cf9b74bb81a3659c2b9fb4',1,'turbojpeg.h']]], - ['tjpf_5fbgrx_181',['TJPF_BGRX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa2a1fbf569ca79897eae886e3376ca4c8',1,'turbojpeg.h']]], - ['tjpf_5fcmyk_182',['TJPF_CMYK',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7f5100ec44c91994e243f1cf55553f8b',1,'turbojpeg.h']]], - ['tjpf_5fgray_183',['TJPF_GRAY',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa5431b54b015337705f13118073711a1a',1,'turbojpeg.h']]], - ['tjpf_5frgb_184',['TJPF_RGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7ce93230bff449518ce387c17e6ed37c',1,'turbojpeg.h']]], - ['tjpf_5frgba_185',['TJPF_RGBA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa88d2e88fab67f6503cf972e14851cc12',1,'turbojpeg.h']]], - ['tjpf_5frgbx_186',['TJPF_RGBX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa83973bebb7e2dc6fa8bae89ff3f42e01',1,'turbojpeg.h']]], - ['tjpf_5funknown_187',['TJPF_UNKNOWN',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa84c1a6cead7952998e2fb895844a21ed',1,'turbojpeg.h']]], - ['tjpf_5fxbgr_188',['TJPF_XBGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aaf6603b27147de47e212e75dac027b2af',1,'turbojpeg.h']]], - ['tjpf_5fxrgb_189',['TJPF_XRGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aadae996905efcfa3b42a0bb3bea7f9d84',1,'turbojpeg.h']]], - ['tjsamp_5f411_190',['TJSAMP_411',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a28ec62575e5ea295c3fde3001dc628e2',1,'turbojpeg.h']]], - ['tjsamp_5f420_191',['TJSAMP_420',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a63085dbf683cfe39e513cdb6343e3737',1,'turbojpeg.h']]], - ['tjsamp_5f422_192',['TJSAMP_422',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a136130902cc578f11f32429b59368404',1,'turbojpeg.h']]], - ['tjsamp_5f440_193',['TJSAMP_440',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074accf740e6f3aa6ba20ba922cad13cb974',1,'turbojpeg.h']]], - ['tjsamp_5f444_194',['TJSAMP_444',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074afb8da4f44197837bdec0a4f593dacae3',1,'turbojpeg.h']]], - ['tjsamp_5fgray_195',['TJSAMP_GRAY',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a3f1c9504842ddc7a48d0f690754b6248',1,'turbojpeg.h']]], - ['tjxop_5fhflip_196',['TJXOP_HFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aa0df69776caa30f0fa28e26332d311ce',1,'turbojpeg.h']]], - ['tjxop_5fnone_197',['TJXOP_NONE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aad88c0366cd3f7d0eac9d7a3fa1c2c27',1,'turbojpeg.h']]], - ['tjxop_5frot180_198',['TJXOP_ROT180',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a140952eb8dd0300accfcc22726d69692',1,'turbojpeg.h']]], - ['tjxop_5frot270_199',['TJXOP_ROT270',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a3064ee5dfb7f032df332818587567a08',1,'turbojpeg.h']]], - ['tjxop_5frot90_200',['TJXOP_ROT90',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a43b2bbb23bc4bd548422d43fbe9af128',1,'turbojpeg.h']]], - ['tjxop_5ftranspose_201',['TJXOP_TRANSPOSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a31060aed199f886afdd417f80499c32d',1,'turbojpeg.h']]], - ['tjxop_5ftransverse_202',['TJXOP_TRANSVERSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866af3b14d488aea6ece9e5b3df73a74d6a4',1,'turbojpeg.h']]], - ['tjxop_5fvflip_203',['TJXOP_VFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a324eddfbec53b7e691f61e56929d0d5d',1,'turbojpeg.h']]] + ['tjcs_5fcmyk_218',['TJCS_CMYK',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a6c8b636152ac8195b869587db315ee53',1,'turbojpeg.h']]], + ['tjcs_5fgray_219',['TJCS_GRAY',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720ab3e7d6a87f695e45b81c1b5262b5a50a',1,'turbojpeg.h']]], + ['tjcs_5frgb_220',['TJCS_RGB',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a677cb7ccb85c4038ac41964a2e09e555',1,'turbojpeg.h']]], + ['tjcs_5fycbcr_221',['TJCS_YCbCr',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a7389b8f65bb387ffedce3efd0d78ec75',1,'turbojpeg.h']]], + ['tjcs_5fycck_222',['TJCS_YCCK',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a53839e0fe867b76b58d16b0a1a7c598e',1,'turbojpeg.h']]], + ['tjerr_5ffatal_223',['TJERR_FATAL',['../group___turbo_j_p_e_g.html#ggafbc17cfa57d0d5d11fea35ac025950feafc9cceeada13122b09e4851e3788039a',1,'turbojpeg.h']]], + ['tjerr_5fwarning_224',['TJERR_WARNING',['../group___turbo_j_p_e_g.html#ggafbc17cfa57d0d5d11fea35ac025950fea342dd6e2aedb47bb257b4e7568329b59',1,'turbojpeg.h']]], + ['tjinit_5fcompress_225',['TJINIT_COMPRESS',['../group___turbo_j_p_e_g.html#gga3850bbee1313e752e667b4eb08b1e086aa45ac279e3dc6ffabc4b0f45864da796',1,'turbojpeg.h']]], + ['tjinit_5fdecompress_226',['TJINIT_DECOMPRESS',['../group___turbo_j_p_e_g.html#gga3850bbee1313e752e667b4eb08b1e086a4b8ca1ef700699b71350700bf95c2167',1,'turbojpeg.h']]], + ['tjinit_5ftransform_227',['TJINIT_TRANSFORM',['../group___turbo_j_p_e_g.html#gga3850bbee1313e752e667b4eb08b1e086a8d58a2a4c45b3e0cd349746544a6e0c2',1,'turbojpeg.h']]], + ['tjparam_5farithmetic_228',['TJPARAM_ARITHMETIC',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a1c756757384308145602c040524aebf7',1,'turbojpeg.h']]], + ['tjparam_5fbottomup_229',['TJPARAM_BOTTOMUP',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a924657172695ed6cb0b128219546fcce',1,'turbojpeg.h']]], + ['tjparam_5fcolorspace_230',['TJPARAM_COLORSPACE',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a46a10d46309514907d0c39fcd86c324c',1,'turbojpeg.h']]], + ['tjparam_5fdensityunits_231',['TJPARAM_DENSITYUNITS',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a4c045981bd8a303521a401dbbe1df208',1,'turbojpeg.h']]], + ['tjparam_5ffastdct_232',['TJPARAM_FASTDCT',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a6914692ac6ec5567787d592b7563f627',1,'turbojpeg.h']]], + ['tjparam_5ffastupsample_233',['TJPARAM_FASTUPSAMPLE',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a0e051ac106f7b7402b690a5daf4869c0',1,'turbojpeg.h']]], + ['tjparam_5fjpegheight_234',['TJPARAM_JPEGHEIGHT',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a8f76673be73f2b659440a9572a65a95f',1,'turbojpeg.h']]], + ['tjparam_5fjpegwidth_235',['TJPARAM_JPEGWIDTH',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a02ab77fb294a0c9061a78cd424c82dd8',1,'turbojpeg.h']]], + ['tjparam_5flossless_236',['TJPARAM_LOSSLESS',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a249f35f0770792b19f995e603bb17c6f',1,'turbojpeg.h']]], + ['tjparam_5flosslesspsv_237',['TJPARAM_LOSSLESSPSV',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82abcc997d40e5bec84817c12b76ef84159',1,'turbojpeg.h']]], + ['tjparam_5flosslesspt_238',['TJPARAM_LOSSLESSPT',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a4a6c6f25764ecaf4231a36bff844e46a',1,'turbojpeg.h']]], + ['tjparam_5fnorealloc_239',['TJPARAM_NOREALLOC',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82ae64ffb358bc7b194fd48e0f27750b29b',1,'turbojpeg.h']]], + ['tjparam_5foptimize_240',['TJPARAM_OPTIMIZE',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a8f0af9afc0b36443751f9ee82b760aa6',1,'turbojpeg.h']]], + ['tjparam_5fprecision_241',['TJPARAM_PRECISION',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a781db82741934e8cd008d308597c59d8',1,'turbojpeg.h']]], + ['tjparam_5fprogressive_242',['TJPARAM_PROGRESSIVE',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a1716f242b3859905b4a317dae8cfb75f',1,'turbojpeg.h']]], + ['tjparam_5fquality_243',['TJPARAM_QUALITY',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a0467e8792621f2d817dc2af563d3186c',1,'turbojpeg.h']]], + ['tjparam_5frestartblocks_244',['TJPARAM_RESTARTBLOCKS',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a343c72883b7160f23f3ef46fc548a0ec',1,'turbojpeg.h']]], + ['tjparam_5frestartrows_245',['TJPARAM_RESTARTROWS',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a714367585952fe5c863f0dba5bd37e5c',1,'turbojpeg.h']]], + ['tjparam_5fscanlimit_246',['TJPARAM_SCANLIMIT',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82ac478910e20ecf61b914f9824d80f8167',1,'turbojpeg.h']]], + ['tjparam_5fstoponwarning_247',['TJPARAM_STOPONWARNING',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a555e2212079fa49b30bcd2879c6c8ddb',1,'turbojpeg.h']]], + ['tjparam_5fsubsamp_248',['TJPARAM_SUBSAMP',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a2a3494a8215d3de4fdbaeb2ba6f6b03a',1,'turbojpeg.h']]], + ['tjparam_5fxdensity_249',['TJPARAM_XDENSITY',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a4de5c9d7cab5be806143a43c3b0e0877',1,'turbojpeg.h']]], + ['tjparam_5fydensity_250',['TJPARAM_YDENSITY',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82abda48f2df7eb9b88e2b7621efb017eba',1,'turbojpeg.h']]], + ['tjpf_5fabgr_251',['TJPF_ABGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa1ba1a7f1631dbeaa49a0a85fc4a40081',1,'turbojpeg.h']]], + ['tjpf_5fargb_252',['TJPF_ARGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aae8f846ed9d9de99b6e1dfe448848765c',1,'turbojpeg.h']]], + ['tjpf_5fbgr_253',['TJPF_BGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aab10624437fb8ef495a0b153e65749839',1,'turbojpeg.h']]], + ['tjpf_5fbgra_254',['TJPF_BGRA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aac037ff1845cf9b74bb81a3659c2b9fb4',1,'turbojpeg.h']]], + ['tjpf_5fbgrx_255',['TJPF_BGRX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa2a1fbf569ca79897eae886e3376ca4c8',1,'turbojpeg.h']]], + ['tjpf_5fcmyk_256',['TJPF_CMYK',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7f5100ec44c91994e243f1cf55553f8b',1,'turbojpeg.h']]], + ['tjpf_5fgray_257',['TJPF_GRAY',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa5431b54b015337705f13118073711a1a',1,'turbojpeg.h']]], + ['tjpf_5frgb_258',['TJPF_RGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7ce93230bff449518ce387c17e6ed37c',1,'turbojpeg.h']]], + ['tjpf_5frgba_259',['TJPF_RGBA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa88d2e88fab67f6503cf972e14851cc12',1,'turbojpeg.h']]], + ['tjpf_5frgbx_260',['TJPF_RGBX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa83973bebb7e2dc6fa8bae89ff3f42e01',1,'turbojpeg.h']]], + ['tjpf_5funknown_261',['TJPF_UNKNOWN',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa84c1a6cead7952998e2fb895844a21ed',1,'turbojpeg.h']]], + ['tjpf_5fxbgr_262',['TJPF_XBGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aaf6603b27147de47e212e75dac027b2af',1,'turbojpeg.h']]], + ['tjpf_5fxrgb_263',['TJPF_XRGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aadae996905efcfa3b42a0bb3bea7f9d84',1,'turbojpeg.h']]], + ['tjsamp_5f411_264',['TJSAMP_411',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a28ec62575e5ea295c3fde3001dc628e2',1,'turbojpeg.h']]], + ['tjsamp_5f420_265',['TJSAMP_420',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a63085dbf683cfe39e513cdb6343e3737',1,'turbojpeg.h']]], + ['tjsamp_5f422_266',['TJSAMP_422',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a136130902cc578f11f32429b59368404',1,'turbojpeg.h']]], + ['tjsamp_5f440_267',['TJSAMP_440',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074accf740e6f3aa6ba20ba922cad13cb974',1,'turbojpeg.h']]], + ['tjsamp_5f444_268',['TJSAMP_444',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074afb8da4f44197837bdec0a4f593dacae3',1,'turbojpeg.h']]], + ['tjsamp_5fgray_269',['TJSAMP_GRAY',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a3f1c9504842ddc7a48d0f690754b6248',1,'turbojpeg.h']]], + ['tjsamp_5funknown_270',['TJSAMP_UNKNOWN',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074ac124fa8f6cb41147e3d670dfbdfb7173',1,'turbojpeg.h']]], + ['tjxop_5fhflip_271',['TJXOP_HFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aa0df69776caa30f0fa28e26332d311ce',1,'turbojpeg.h']]], + ['tjxop_5fnone_272',['TJXOP_NONE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aad88c0366cd3f7d0eac9d7a3fa1c2c27',1,'turbojpeg.h']]], + ['tjxop_5frot180_273',['TJXOP_ROT180',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a140952eb8dd0300accfcc22726d69692',1,'turbojpeg.h']]], + ['tjxop_5frot270_274',['TJXOP_ROT270',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a3064ee5dfb7f032df332818587567a08',1,'turbojpeg.h']]], + ['tjxop_5frot90_275',['TJXOP_ROT90',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a43b2bbb23bc4bd548422d43fbe9af128',1,'turbojpeg.h']]], + ['tjxop_5ftranspose_276',['TJXOP_TRANSPOSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a31060aed199f886afdd417f80499c32d',1,'turbojpeg.h']]], + ['tjxop_5ftransverse_277',['TJXOP_TRANSVERSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866af3b14d488aea6ece9e5b3df73a74d6a4',1,'turbojpeg.h']]], + ['tjxop_5fvflip_278',['TJXOP_VFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a324eddfbec53b7e691f61e56929d0d5d',1,'turbojpeg.h']]] ]; diff --git a/doc/html/search/functions_0.js b/doc/html/search/functions_0.js index e134e6705..3a066fe66 100644 --- a/doc/html/search/functions_0.js +++ b/doc/html/search/functions_0.js @@ -1,31 +1,41 @@ var searchData= [ - ['tjalloc_117',['tjAlloc',['../group___turbo_j_p_e_g.html#gaec627dd4c5f30b7a775a7aea3bec5d83',1,'turbojpeg.h']]], - ['tjbufsize_118',['tjBufSize',['../group___turbo_j_p_e_g.html#ga67ac12fee79073242cb216e07c9f1f90',1,'turbojpeg.h']]], - ['tjbufsizeyuv2_119',['tjBufSizeYUV2',['../group___turbo_j_p_e_g.html#ga5e5aac9e8bcf17049279301e2466474c',1,'turbojpeg.h']]], - ['tjcompress2_120',['tjCompress2',['../group___turbo_j_p_e_g.html#gafbdce0112fd78fd38efae841443a9bcf',1,'turbojpeg.h']]], - ['tjcompressfromyuv_121',['tjCompressFromYUV',['../group___turbo_j_p_e_g.html#gab40f5096a72fd7e5bda9d6b58fa37e2e',1,'turbojpeg.h']]], - ['tjcompressfromyuvplanes_122',['tjCompressFromYUVPlanes',['../group___turbo_j_p_e_g.html#ga29ec5dfbd2d84b8724e951d6fa0d5d9e',1,'turbojpeg.h']]], - ['tjdecodeyuv_123',['tjDecodeYUV',['../group___turbo_j_p_e_g.html#ga97c2cedc1e2bade15a84164c94e503c1',1,'turbojpeg.h']]], - ['tjdecodeyuvplanes_124',['tjDecodeYUVPlanes',['../group___turbo_j_p_e_g.html#ga10e837c07fa9d25770565b237d3898d9',1,'turbojpeg.h']]], - ['tjdecompress2_125',['tjDecompress2',['../group___turbo_j_p_e_g.html#gae9eccef8b682a48f43a9117c231ed013',1,'turbojpeg.h']]], - ['tjdecompressheader4_126',['tjDecompressHeader4',['../group___turbo_j_p_e_g.html#gac104e6e729f57f195009405949d198dc',1,'turbojpeg.h']]], - ['tjdecompresstoyuv2_127',['tjDecompressToYUV2',['../group___turbo_j_p_e_g.html#ga5a3093e325598c17a9f004323af6fafa',1,'turbojpeg.h']]], - ['tjdecompresstoyuvplanes_128',['tjDecompressToYUVPlanes',['../group___turbo_j_p_e_g.html#gaa59f901a5258ada5bd0185ad59368540',1,'turbojpeg.h']]], - ['tjdestroy_129',['tjDestroy',['../group___turbo_j_p_e_g.html#ga75f355fa27225ba1a4ee392c852394d2',1,'turbojpeg.h']]], - ['tjencodeyuv3_130',['tjEncodeYUV3',['../group___turbo_j_p_e_g.html#ga5d619e0a02b71e05a8dffb764f6d7a64',1,'turbojpeg.h']]], - ['tjencodeyuvplanes_131',['tjEncodeYUVPlanes',['../group___turbo_j_p_e_g.html#gae2d04c72457fe7f4d60cf78ab1b1feb1',1,'turbojpeg.h']]], - ['tjfree_132',['tjFree',['../group___turbo_j_p_e_g.html#gaea863d2da0cdb609563aabdf9196514b',1,'turbojpeg.h']]], - ['tjgeterrorcode_133',['tjGetErrorCode',['../group___turbo_j_p_e_g.html#ga414feeffbf860ebd31c745df203de410',1,'turbojpeg.h']]], - ['tjgeterrorstr2_134',['tjGetErrorStr2',['../group___turbo_j_p_e_g.html#ga1ead8574f9f39fbafc6b497124e7aafa',1,'turbojpeg.h']]], - ['tjgetscalingfactors_135',['tjGetScalingFactors',['../group___turbo_j_p_e_g.html#ga193d0977b3b9966d53a6c402e90899b1',1,'turbojpeg.h']]], - ['tjinitcompress_136',['tjInitCompress',['../group___turbo_j_p_e_g.html#ga9d63a05fc6d813f4aae06107041a37e8',1,'turbojpeg.h']]], - ['tjinitdecompress_137',['tjInitDecompress',['../group___turbo_j_p_e_g.html#ga52300eac3f3d9ef4bab303bc244f62d3',1,'turbojpeg.h']]], - ['tjinittransform_138',['tjInitTransform',['../group___turbo_j_p_e_g.html#ga928beff6ac248ceadf01089fc6b41957',1,'turbojpeg.h']]], - ['tjloadimage_139',['tjLoadImage',['../group___turbo_j_p_e_g.html#gaffbd83c375e79f5db4b5c5d8ad4466e7',1,'turbojpeg.h']]], - ['tjplaneheight_140',['tjPlaneHeight',['../group___turbo_j_p_e_g.html#ga1a209696c6a80748f20e134b3c64789f',1,'turbojpeg.h']]], - ['tjplanesizeyuv_141',['tjPlaneSizeYUV',['../group___turbo_j_p_e_g.html#gab4ab7b24f6e797d79abaaa670373961d',1,'turbojpeg.h']]], - ['tjplanewidth_142',['tjPlaneWidth',['../group___turbo_j_p_e_g.html#ga63fb66bb1e36c74008c4634360becbb1',1,'turbojpeg.h']]], - ['tjsaveimage_143',['tjSaveImage',['../group___turbo_j_p_e_g.html#ga6f445b22d8933ae4815b3370a538d879',1,'turbojpeg.h']]], - ['tjtransform_144',['tjTransform',['../group___turbo_j_p_e_g.html#ga9cb8abf4cc91881e04a0329b2270be25',1,'turbojpeg.h']]] + ['tj3alloc_151',['tj3Alloc',['../group___turbo_j_p_e_g.html#gab40a0b231122f536e503e3394569a68d',1,'turbojpeg.h']]], + ['tj3compress12_152',['tj3Compress12',['../group___turbo_j_p_e_g.html#ga9a1968c384ec7abb6122830253ebf570',1,'turbojpeg.h']]], + ['tj3compress16_153',['tj3Compress16',['../group___turbo_j_p_e_g.html#ga77901b71d0471784f318ada31ff4e7bd',1,'turbojpeg.h']]], + ['tj3compress8_154',['tj3Compress8',['../group___turbo_j_p_e_g.html#ga2cc418a2dab709ad7f30f5b25905f138',1,'turbojpeg.h']]], + ['tj3compressfromyuv8_155',['tj3CompressFromYUV8',['../group___turbo_j_p_e_g.html#ga041c870d9c669eb3f385c78f4346c43f',1,'turbojpeg.h']]], + ['tj3compressfromyuvplanes8_156',['tj3CompressFromYUVPlanes8',['../group___turbo_j_p_e_g.html#gac9f5ace3e73805b476c95dda9f8d0cd0',1,'turbojpeg.h']]], + ['tj3decodeyuv8_157',['tj3DecodeYUV8',['../group___turbo_j_p_e_g.html#gaa1eb574f38b1c1de43a6c7aafcf68d8c',1,'turbojpeg.h']]], + ['tj3decodeyuvplanes8_158',['tj3DecodeYUVPlanes8',['../group___turbo_j_p_e_g.html#gad366f1915f82c1ad4e7e37ebe073ca89',1,'turbojpeg.h']]], + ['tj3decompress12_159',['tj3Decompress12',['../group___turbo_j_p_e_g.html#ga39b848f01781ad74a5b3941c012b6199',1,'turbojpeg.h']]], + ['tj3decompress16_160',['tj3Decompress16',['../group___turbo_j_p_e_g.html#gaa074e63f9beb0b3ff42b833a4049df6e',1,'turbojpeg.h']]], + ['tj3decompress8_161',['tj3Decompress8',['../group___turbo_j_p_e_g.html#ga1169c7c1a26ec18c9e6122cb8ae64013',1,'turbojpeg.h']]], + ['tj3decompressheader_162',['tj3DecompressHeader',['../group___turbo_j_p_e_g.html#ga96d2c4b3432f9d88ad14758ae240b8d1',1,'turbojpeg.h']]], + ['tj3decompresstoyuv8_163',['tj3DecompressToYUV8',['../group___turbo_j_p_e_g.html#ga1e6bf6a19fec3f9fa7534348879d8320',1,'turbojpeg.h']]], + ['tj3decompresstoyuvplanes8_164',['tj3DecompressToYUVPlanes8',['../group___turbo_j_p_e_g.html#ga934373482dbbf257f2280505b6ff4fb5',1,'turbojpeg.h']]], + ['tj3destroy_165',['tj3Destroy',['../group___turbo_j_p_e_g.html#ga53fbadf4560e95a65b8f5ab81703fe82',1,'turbojpeg.h']]], + ['tj3encodeyuv8_166',['tj3EncodeYUV8',['../group___turbo_j_p_e_g.html#ga2a8d50f130bde10f0a04030f8cc59936',1,'turbojpeg.h']]], + ['tj3encodeyuvplanes8_167',['tj3EncodeYUVPlanes8',['../group___turbo_j_p_e_g.html#gae2e9df38790e9bddc249d04cb158a4cf',1,'turbojpeg.h']]], + ['tj3free_168',['tj3Free',['../group___turbo_j_p_e_g.html#gaddb84fb6c81769e9faa0f5a63b296606',1,'turbojpeg.h']]], + ['tj3get_169',['tj3Get',['../group___turbo_j_p_e_g.html#ga34af9ba3183bdf0ec7c8f47bb9a4c84f',1,'turbojpeg.h']]], + ['tj3geterrorcode_170',['tj3GetErrorCode',['../group___turbo_j_p_e_g.html#gab8c8279f1415fe425ff30dbbc56013bd',1,'turbojpeg.h']]], + ['tj3geterrorstr_171',['tj3GetErrorStr',['../group___turbo_j_p_e_g.html#gaf2aab0e6dbb3edc57646b0fec25e8bb2',1,'turbojpeg.h']]], + ['tj3getscalingfactors_172',['tj3GetScalingFactors',['../group___turbo_j_p_e_g.html#ga74397f8e0587d4233182c72f085aaf04',1,'turbojpeg.h']]], + ['tj3init_173',['tj3Init',['../group___turbo_j_p_e_g.html#ga69c09d39f97ec30250ad3605ace7e5df',1,'turbojpeg.h']]], + ['tj3jpegbufsize_174',['tj3JPEGBufSize',['../group___turbo_j_p_e_g.html#gac6285e58e35a35d871d7162ec5a929c4',1,'turbojpeg.h']]], + ['tj3loadimage12_175',['tj3LoadImage12',['../group___turbo_j_p_e_g.html#ga1f03c26892a26d4ce077ed6a4ac40e8f',1,'turbojpeg.h']]], + ['tj3loadimage16_176',['tj3LoadImage16',['../group___turbo_j_p_e_g.html#ga638aeba63e0ccb89d472fdbf34224cfc',1,'turbojpeg.h']]], + ['tj3loadimage8_177',['tj3LoadImage8',['../group___turbo_j_p_e_g.html#ga565aaae7be3f8ca9099b56655c893251',1,'turbojpeg.h']]], + ['tj3saveimage12_178',['tj3SaveImage12',['../group___turbo_j_p_e_g.html#ga7c64b5106d04267a46aad85f9714ad90',1,'turbojpeg.h']]], + ['tj3saveimage16_179',['tj3SaveImage16',['../group___turbo_j_p_e_g.html#ga0fd87851f4266aca24bf4594dd0c0e71',1,'turbojpeg.h']]], + ['tj3saveimage8_180',['tj3SaveImage8',['../group___turbo_j_p_e_g.html#gaa4ec838988e469cc15618e4690cc8722',1,'turbojpeg.h']]], + ['tj3set_181',['tj3Set',['../group___turbo_j_p_e_g.html#gaddf92640bfee3e8622218c713e77e7db',1,'turbojpeg.h']]], + ['tj3setcroppingregion_182',['tj3SetCroppingRegion',['../group___turbo_j_p_e_g.html#gaa49c7bd4c9431667a043cfc93388ba1c',1,'turbojpeg.h']]], + ['tj3setscalingfactor_183',['tj3SetScalingFactor',['../group___turbo_j_p_e_g.html#ga89da17ee1e43ff423382cbc145803c75',1,'turbojpeg.h']]], + ['tj3transform_184',['tj3Transform',['../group___turbo_j_p_e_g.html#gaff23ba1dcabed456794b844791613920',1,'turbojpeg.h']]], + ['tj3yuvbufsize_185',['tj3YUVBufSize',['../group___turbo_j_p_e_g.html#gaaebaa16973a0f550a66eca5765ed0546',1,'turbojpeg.h']]], + ['tj3yuvplaneheight_186',['tj3YUVPlaneHeight',['../group___turbo_j_p_e_g.html#ga969767ec8180cc3edd99cf507f87299b',1,'turbojpeg.h']]], + ['tj3yuvplanesize_187',['tj3YUVPlaneSize',['../group___turbo_j_p_e_g.html#gacc19d265edce76b46146f59579f9438d',1,'turbojpeg.h']]], + ['tj3yuvplanewidth_188',['tj3YUVPlaneWidth',['../group___turbo_j_p_e_g.html#gac99d1933ede1d59fcada9a826e88eb2d',1,'turbojpeg.h']]] ]; diff --git a/doc/html/search/groups_0.js b/doc/html/search/groups_0.js index dc5404cec..46ff898c5 100644 --- a/doc/html/search/groups_0.js +++ b/doc/html/search/groups_0.js @@ -1,4 +1,4 @@ var searchData= [ - ['turbojpeg_204',['TurboJPEG',['../group___turbo_j_p_e_g.html',1,'']]] + ['turbojpeg_279',['TurboJPEG',['../group___turbo_j_p_e_g.html',1,'']]] ]; diff --git a/doc/html/search/typedefs_0.js b/doc/html/search/typedefs_0.js index 6b01cef3f..927d7d320 100644 --- a/doc/html/search/typedefs_0.js +++ b/doc/html/search/typedefs_0.js @@ -1,5 +1,5 @@ var searchData= [ - ['tjhandle_163',['tjhandle',['../group___turbo_j_p_e_g.html#ga758d2634ecb4949de7815cba621f5763',1,'turbojpeg.h']]], - ['tjtransform_164',['tjtransform',['../group___turbo_j_p_e_g.html#ga504805ec0161f1b505397ca0118bf8fd',1,'turbojpeg.h']]] + ['tjhandle_209',['tjhandle',['../group___turbo_j_p_e_g.html#ga758d2634ecb4949de7815cba621f5763',1,'turbojpeg.h']]], + ['tjtransform_210',['tjtransform',['../group___turbo_j_p_e_g.html#ga504805ec0161f1b505397ca0118bf8fd',1,'turbojpeg.h']]] ]; diff --git a/doc/html/search/variables_0.js b/doc/html/search/variables_0.js index eb7bc5af6..b438812c8 100644 --- a/doc/html/search/variables_0.js +++ b/doc/html/search/variables_0.js @@ -1,4 +1,4 @@ var searchData= [ - ['customfilter_145',['customFilter',['../structtjtransform.html#afd7fc262df33f741e120ef4183202ef5',1,'tjtransform']]] + ['customfilter_189',['customFilter',['../structtjtransform.html#afd7fc262df33f741e120ef4183202ef5',1,'tjtransform']]] ]; diff --git a/doc/html/search/variables_1.js b/doc/html/search/variables_1.js index cd6ee7e37..e9a35d299 100644 --- a/doc/html/search/variables_1.js +++ b/doc/html/search/variables_1.js @@ -1,5 +1,5 @@ var searchData= [ - ['data_146',['data',['../structtjtransform.html#a688fe8f1a8ecc12a538d9e561cf338e3',1,'tjtransform']]], - ['denom_147',['denom',['../structtjscalingfactor.html#aefbcdf3e9e62274b2d312c695f133ce3',1,'tjscalingfactor']]] + ['data_190',['data',['../structtjtransform.html#a688fe8f1a8ecc12a538d9e561cf338e3',1,'tjtransform']]], + ['denom_191',['denom',['../structtjscalingfactor.html#aefbcdf3e9e62274b2d312c695f133ce3',1,'tjscalingfactor']]] ]; diff --git a/doc/html/search/variables_2.js b/doc/html/search/variables_2.js index 5be414e83..80319f858 100644 --- a/doc/html/search/variables_2.js +++ b/doc/html/search/variables_2.js @@ -1,4 +1,4 @@ var searchData= [ - ['h_148',['h',['../structtjregion.html#aecefc45a26f4d8b60dd4d825c1710115',1,'tjregion']]] + ['h_192',['h',['../structtjregion.html#aecefc45a26f4d8b60dd4d825c1710115',1,'tjregion']]] ]; diff --git a/doc/html/search/variables_3.js b/doc/html/search/variables_3.js index 586307e74..e04ac9a04 100644 --- a/doc/html/search/variables_3.js +++ b/doc/html/search/variables_3.js @@ -1,4 +1,4 @@ var searchData= [ - ['num_149',['num',['../structtjscalingfactor.html#a9b011e57f981ee23083e2c1aa5e640ec',1,'tjscalingfactor']]] + ['num_193',['num',['../structtjscalingfactor.html#a9b011e57f981ee23083e2c1aa5e640ec',1,'tjscalingfactor']]] ]; diff --git a/doc/html/search/variables_4.js b/doc/html/search/variables_4.js index 7023c9ca5..7b86b6e46 100644 --- a/doc/html/search/variables_4.js +++ b/doc/html/search/variables_4.js @@ -1,5 +1,5 @@ var searchData= [ - ['op_150',['op',['../structtjtransform.html#a2525aab4ba6978a1c273f74fef50e498',1,'tjtransform']]], - ['options_151',['options',['../structtjtransform.html#ac0e74655baa4402209a21e1ae481c8f6',1,'tjtransform']]] + ['op_194',['op',['../structtjtransform.html#a2525aab4ba6978a1c273f74fef50e498',1,'tjtransform']]], + ['options_195',['options',['../structtjtransform.html#ac0e74655baa4402209a21e1ae481c8f6',1,'tjtransform']]] ]; diff --git a/doc/html/search/variables_5.js b/doc/html/search/variables_5.js index ca1e0cd26..7abd5f5e5 100644 --- a/doc/html/search/variables_5.js +++ b/doc/html/search/variables_5.js @@ -1,4 +1,4 @@ var searchData= [ - ['r_152',['r',['../structtjtransform.html#ac324e5e442abec8a961e5bf219db12cf',1,'tjtransform']]] + ['r_196',['r',['../structtjtransform.html#ac324e5e442abec8a961e5bf219db12cf',1,'tjtransform']]] ]; diff --git a/doc/html/search/variables_6.js b/doc/html/search/variables_6.js index f7a582c41..35ad31f53 100644 --- a/doc/html/search/variables_6.js +++ b/doc/html/search/variables_6.js @@ -1,10 +1,12 @@ var searchData= [ - ['tjalphaoffset_153',['tjAlphaOffset',['../group___turbo_j_p_e_g.html#ga5af0ab065feefd526debf1e20c43e837',1,'turbojpeg.h']]], - ['tjblueoffset_154',['tjBlueOffset',['../group___turbo_j_p_e_g.html#ga84e2e35d3f08025f976ec1ec53693dea',1,'turbojpeg.h']]], - ['tjgreenoffset_155',['tjGreenOffset',['../group___turbo_j_p_e_g.html#ga82d6e35da441112a411da41923c0ba2f',1,'turbojpeg.h']]], - ['tjmcuheight_156',['tjMCUHeight',['../group___turbo_j_p_e_g.html#gabd247bb9fecb393eca57366feb8327bf',1,'turbojpeg.h']]], - ['tjmcuwidth_157',['tjMCUWidth',['../group___turbo_j_p_e_g.html#ga9e61e7cd47a15a173283ba94e781308c',1,'turbojpeg.h']]], - ['tjpixelsize_158',['tjPixelSize',['../group___turbo_j_p_e_g.html#gad77cf8fe5b2bfd3cb3f53098146abb4c',1,'turbojpeg.h']]], - ['tjredoffset_159',['tjRedOffset',['../group___turbo_j_p_e_g.html#gadd9b446742ac8a3923f7992c7988fea8',1,'turbojpeg.h']]] + ['tjalphaoffset_197',['tjAlphaOffset',['../group___turbo_j_p_e_g.html#ga5af0ab065feefd526debf1e20c43e837',1,'turbojpeg.h']]], + ['tjblueoffset_198',['tjBlueOffset',['../group___turbo_j_p_e_g.html#ga84e2e35d3f08025f976ec1ec53693dea',1,'turbojpeg.h']]], + ['tjgreenoffset_199',['tjGreenOffset',['../group___turbo_j_p_e_g.html#ga82d6e35da441112a411da41923c0ba2f',1,'turbojpeg.h']]], + ['tjmcuheight_200',['tjMCUHeight',['../group___turbo_j_p_e_g.html#gabd247bb9fecb393eca57366feb8327bf',1,'turbojpeg.h']]], + ['tjmcuwidth_201',['tjMCUWidth',['../group___turbo_j_p_e_g.html#ga9e61e7cd47a15a173283ba94e781308c',1,'turbojpeg.h']]], + ['tjpixelsize_202',['tjPixelSize',['../group___turbo_j_p_e_g.html#gad77cf8fe5b2bfd3cb3f53098146abb4c',1,'turbojpeg.h']]], + ['tjredoffset_203',['tjRedOffset',['../group___turbo_j_p_e_g.html#gadd9b446742ac8a3923f7992c7988fea8',1,'turbojpeg.h']]], + ['tjuncropped_204',['TJUNCROPPED',['../group___turbo_j_p_e_g.html#ga6f192ad58a5a5802e145149d83c643bf',1,'turbojpeg.h']]], + ['tjunscaled_205',['TJUNSCALED',['../group___turbo_j_p_e_g.html#ga7880644a0849161ad20933536169ee19',1,'turbojpeg.h']]] ]; diff --git a/doc/html/search/variables_7.js b/doc/html/search/variables_7.js index fe5bba704..ae8d2cde9 100644 --- a/doc/html/search/variables_7.js +++ b/doc/html/search/variables_7.js @@ -1,4 +1,4 @@ var searchData= [ - ['w_160',['w',['../structtjregion.html#ab6eb73ceef584fc23c8c8097926dce42',1,'tjregion']]] + ['w_206',['w',['../structtjregion.html#ab6eb73ceef584fc23c8c8097926dce42',1,'tjregion']]] ]; diff --git a/doc/html/search/variables_8.js b/doc/html/search/variables_8.js index 5e1016018..9baab8716 100644 --- a/doc/html/search/variables_8.js +++ b/doc/html/search/variables_8.js @@ -1,4 +1,4 @@ var searchData= [ - ['x_161',['x',['../structtjregion.html#a4b6a37a93997091b26a75831fa291ad9',1,'tjregion']]] + ['x_207',['x',['../structtjregion.html#a4b6a37a93997091b26a75831fa291ad9',1,'tjregion']]] ]; diff --git a/doc/html/search/variables_9.js b/doc/html/search/variables_9.js index 1010f64aa..d52ead7e3 100644 --- a/doc/html/search/variables_9.js +++ b/doc/html/search/variables_9.js @@ -1,4 +1,4 @@ var searchData= [ - ['y_162',['y',['../structtjregion.html#a7b3e0c24cfe87acc80e334cafdcf22c2',1,'tjregion']]] + ['y_208',['y',['../structtjregion.html#a7b3e0c24cfe87acc80e334cafdcf22c2',1,'tjregion']]] ]; diff --git a/doc/html/structtjregion.html b/doc/html/structtjregion.html index fc3989008..e67858464 100644 --- a/doc/html/structtjregion.html +++ b/doc/html/structtjregion.html @@ -23,7 +23,7 @@
    TurboJPEG -  2.2 +  3
    @@ -157,7 +157,7 @@

    The upper boundary of the cropping region.

    -

    This must be evenly divisible by the MCU block height (see tjMCUHeight.)

    +

    For lossless transformation, this must be evenly divisible by the MCU block height (see tjMCUHeight.)

    diff --git a/doc/html/structtjscalingfactor.html b/doc/html/structtjscalingfactor.html index 6b8b1ffd2..818dfbd06 100644 --- a/doc/html/structtjscalingfactor.html +++ b/doc/html/structtjscalingfactor.html @@ -23,7 +23,7 @@
    TurboJPEG -  2.2 +  3
    diff --git a/doc/html/structtjtransform.html b/doc/html/structtjtransform.html index 7a2435202..bcb6a0ac1 100644 --- a/doc/html/structtjtransform.html +++ b/doc/html/structtjtransform.html @@ -23,7 +23,7 @@
    TurboJPEG -  2.2 +  3
    @@ -116,7 +116,7 @@

    arrayRegiontjregion structure containing the width and height of the array pointed to by coeffs as well as its offset relative to the component plane. TurboJPEG implementations may choose to split each component plane into multiple DCT coefficient arrays and call the callback function once for each array. planeRegiontjregion structure containing the width and height of the component plane to which coeffs belongs componentIDID number of the component plane to which coeffs belongs. (Y, Cb, and Cr have, respectively, ID's of 0, 1, and 2 in typical JPEG images.) - transformIDID number of the transformed image to which coeffs belongs. This is the same as the index of the transform in the transforms array that was passed to tjTransform(). + transformIDID number of the transformed image to which coeffs belongs. This is the same as the index of the transform in the transforms array that was passed to tj3Transform(). transforma pointer to a tjtransform structure that specifies the parameters and/or cropping region for this transform diff --git a/doxygen.config b/doxygen.config index b051aa4cf..1309a059b 100644 --- a/doxygen.config +++ b/doxygen.config @@ -1,5 +1,5 @@ PROJECT_NAME = TurboJPEG -PROJECT_NUMBER = 2.2 +PROJECT_NUMBER = 3 OUTPUT_DIRECTORY = doc/ USE_WINDOWS_ENCODING = NO OPTIMIZE_OUTPUT_FOR_C = YES diff --git a/fuzz/CMakeLists.txt b/fuzz/CMakeLists.txt index 1c7d68bff..a08cb46a3 100644 --- a/fuzz/CMakeLists.txt +++ b/fuzz/CMakeLists.txt @@ -33,24 +33,6 @@ target_link_libraries(cjpeg_fuzzer${FUZZER_SUFFIX} ${FUZZ_LIBRARY} jpeg-static) install(TARGETS cjpeg_fuzzer${FUZZER_SUFFIX} RUNTIME DESTINATION ${FUZZ_BINDIR}) -add_executable(cjpeg12_fuzzer${FUZZER_SUFFIX} cjpeg12.cc ../cdjpeg.c ../rdbmp.c - ../rdgif.c ../rdppm.c ../rdswitch.c ../rdtarga.c) -set_property(TARGET cjpeg12_fuzzer${FUZZER_SUFFIX} PROPERTY COMPILE_FLAGS - ${COMPILE_FLAGS}) -target_link_libraries(cjpeg12_fuzzer${FUZZER_SUFFIX} ${FUZZ_LIBRARY} - jpeg-static) -install(TARGETS cjpeg12_fuzzer${FUZZER_SUFFIX} RUNTIME DESTINATION - ${FUZZ_BINDIR}) - -add_executable(cjpeg16_fuzzer${FUZZER_SUFFIX} cjpeg16.cc ../cdjpeg.c ../rdbmp.c - ../rdgif.c ../rdppm.c ../rdswitch.c ../rdtarga.c) -set_property(TARGET cjpeg16_fuzzer${FUZZER_SUFFIX} PROPERTY COMPILE_FLAGS - ${COMPILE_FLAGS}) -target_link_libraries(cjpeg16_fuzzer${FUZZER_SUFFIX} ${FUZZ_LIBRARY} - jpeg-static) -install(TARGETS cjpeg16_fuzzer${FUZZER_SUFFIX} RUNTIME DESTINATION - ${FUZZ_BINDIR}) - macro(add_fuzz_target target source_file) add_executable(${target}_fuzzer${FUZZER_SUFFIX} ${source_file}) target_link_libraries(${target}_fuzzer${FUZZER_SUFFIX} ${FUZZ_LIBRARY} @@ -65,6 +47,12 @@ add_fuzz_target(compress_yuv compress_yuv.cc) add_fuzz_target(compress_lossless compress_lossless.cc) +add_fuzz_target(compress12 compress12.cc) + +add_fuzz_target(compress12_lossless compress12.cc) + +add_fuzz_target(compress16_lossless compress16_lossless.cc) + # NOTE: This target is named libjpeg_turbo_fuzzer instead of decompress_fuzzer # in order to preserve the corpora from Google's OSS-Fuzz target for # libjpeg-turbo, which this target replaces. diff --git a/fuzz/build.sh b/fuzz/build.sh index 8e88b4d0a..d87cbdffa 100644 --- a/fuzz/build.sh +++ b/fuzz/build.sh @@ -18,11 +18,12 @@ make "-j$(nproc)" "--load-average=$(nproc)" make install cp $SRC/compress_fuzzer_seed_corpus.zip $OUT/cjpeg_fuzzer${FUZZER_SUFFIX}_seed_corpus.zip -cp $SRC/compress_fuzzer_seed_corpus.zip $OUT/cjpeg12_fuzzer${FUZZER_SUFFIX}_seed_corpus.zip -cp $SRC/compress_fuzzer_seed_corpus.zip $OUT/cjpeg16_fuzzer${FUZZER_SUFFIX}_seed_corpus.zip cp $SRC/compress_fuzzer_seed_corpus.zip $OUT/compress_fuzzer${FUZZER_SUFFIX}_seed_corpus.zip cp $SRC/compress_fuzzer_seed_corpus.zip $OUT/compress_yuv_fuzzer${FUZZER_SUFFIX}_seed_corpus.zip cp $SRC/compress_fuzzer_seed_corpus.zip $OUT/compress_lossless_fuzzer${FUZZER_SUFFIX}_seed_corpus.zip +cp $SRC/compress_fuzzer_seed_corpus.zip $OUT/compress12_fuzzer${FUZZER_SUFFIX}_seed_corpus.zip +cp $SRC/compress_fuzzer_seed_corpus.zip $OUT/compress12_lossless_fuzzer${FUZZER_SUFFIX}_seed_corpus.zip +cp $SRC/compress_fuzzer_seed_corpus.zip $OUT/compress16_lossless_fuzzer${FUZZER_SUFFIX}_seed_corpus.zip cp $SRC/decompress_fuzzer_seed_corpus.zip $OUT/libjpeg_turbo_fuzzer${FUZZER_SUFFIX}_seed_corpus.zip cp $SRC/decompress_fuzzer_seed_corpus.zip $OUT/decompress_yuv_fuzzer${FUZZER_SUFFIX}_seed_corpus.zip cp $SRC/decompress_fuzzer_seed_corpus.zip $OUT/transform_fuzzer${FUZZER_SUFFIX}_seed_corpus.zip diff --git a/fuzz/cjpeg12.cc b/fuzz/cjpeg12.cc deleted file mode 100644 index ca3ec7c1d..000000000 --- a/fuzz/cjpeg12.cc +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C)2021-2022 D. R. Commander. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - Neither the name of the libjpeg-turbo Project nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/* This fuzz target wraps cjpeg in order to test esoteric compression options - as well as the GIF and Targa readers. */ - -#define main cjpeg_main -#define CJPEG_FUZZER -extern "C" { -#include "../cjpeg.c" -} -#undef main -#undef CJPEG_FUZZER - -#include -#include - - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) -{ - char filename[FILENAME_MAX] = { 0 }; - char *argv1[] = { - (char *)"cjpeg", (char *)"-precision", (char *)"12", - (char *)"-dct", (char *)"int", (char *)"-memdst", - (char *)"-quality", (char *)"100,99,98", (char *)"-restart", (char *)"2", - (char *)"-sample", (char *)"4x1,2x2,1x2", NULL - }; - char *argv2[] = { - (char *)"cjpeg", (char *)"-precision", (char *)"12", - (char *)"-dct", (char *)"fast", (char *)"-memdst", - (char *)"-quality", (char *)"90,80,70", (char *)"-rgb", - (char *)"-sample", (char *)"2x2", (char *)"-smooth", (char *)"50", NULL - }; - char *argv3[] = { - (char *)"cjpeg", (char *)"-precision", (char *)"12", - (char *)"-lossless", (char *)"1,4", NULL - }; - char *argv4[] = { - (char *)"cjpeg", (char *)"-precision", (char *)"12", - (char *)"-lossless", (char *)"4,0", NULL - }; - int fd = -1; -#if defined(__has_feature) && __has_feature(memory_sanitizer) - char env[18] = "JSIMD_FORCENONE=1"; - - /* The libjpeg-turbo SIMD extensions produce false positives with - MemorySanitizer. */ - putenv(env); -#endif - - snprintf(filename, FILENAME_MAX, "/tmp/libjpeg-turbo_cjpeg12_fuzz.XXXXXX"); - if ((fd = mkstemp(filename)) < 0 || write(fd, data, size) < 0) - goto bailout; - - argv1[12] = argv2[13] = argv3[5] = argv4[5] = filename; - - cjpeg_main(13, argv1); - cjpeg_main(14, argv2); - cjpeg_main(6, argv3); - cjpeg_main(6, argv4); - -bailout: - if (fd >= 0) { - close(fd); - if (strlen(filename) > 0) unlink(filename); - } - return 0; -} diff --git a/fuzz/cjpeg16.cc b/fuzz/cjpeg16.cc deleted file mode 100644 index 6f1d20c3f..000000000 --- a/fuzz/cjpeg16.cc +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C)2021-2022 D. R. Commander. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - Neither the name of the libjpeg-turbo Project nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/* This fuzz target wraps cjpeg in order to test esoteric compression options - as well as the GIF and Targa readers. */ - -#define main cjpeg_main -#define CJPEG_FUZZER -extern "C" { -#include "../cjpeg.c" -} -#undef main -#undef CJPEG_FUZZER - -#include -#include - - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) -{ - char filename[FILENAME_MAX] = { 0 }; - char *argv1[] = { - (char *)"cjpeg", (char *)"-precision", (char *)"16", - (char *)"-lossless", (char *)"1,4", NULL - }; - char *argv2[] = { - (char *)"cjpeg", (char *)"-precision", (char *)"16", - (char *)"-lossless", (char *)"4,0", NULL - }; - int fd = -1; -#if defined(__has_feature) && __has_feature(memory_sanitizer) - char env[18] = "JSIMD_FORCENONE=1"; - - /* The libjpeg-turbo SIMD extensions produce false positives with - MemorySanitizer. */ - putenv(env); -#endif - - snprintf(filename, FILENAME_MAX, "/tmp/libjpeg-turbo_cjpeg16_fuzz.XXXXXX"); - if ((fd = mkstemp(filename)) < 0 || write(fd, data, size) < 0) - goto bailout; - - argv1[5] = argv2[5] = filename; - - cjpeg_main(6, argv1); - cjpeg_main(6, argv2); - -bailout: - if (fd >= 0) { - close(fd); - if (strlen(filename) > 0) unlink(filename); - } - return 0; -} diff --git a/fuzz/compress.cc b/fuzz/compress.cc index 539932f13..f59f66d10 100644 --- a/fuzz/compress.cc +++ b/fuzz/compress.cc @@ -1,5 +1,5 @@ /* - * Copyright (C)2021 D. R. Commander. All Rights Reserved. + * Copyright (C)2021, 2023 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -35,8 +35,6 @@ #define NUMTESTS 7 -/* Private flag that triggers different TurboJPEG API behavior when fuzzing */ -#define TJFLAG_FUZZING (1 << 30) struct test { @@ -73,37 +71,40 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) if ((fd = mkstemp(filename)) < 0 || write(fd, data, size) < 0) goto bailout; - if ((handle = tjInitCompress()) == NULL) + if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL) goto bailout; for (ti = 0; ti < NUMTESTS; ti++) { - int flags = TJFLAG_FUZZING, sum = 0, pf = tests[ti].pf; - unsigned long dstSize = 0, maxBufSize; + int sum = 0, pf = tests[ti].pf; + size_t dstSize = 0, maxBufSize; /* Test non-default compression options on specific iterations. */ - if (ti == 0) - flags |= TJFLAG_BOTTOMUP | TJFLAG_ACCURATEDCT; - else if (ti == 1) - flags |= TJFLAG_PROGRESSIVE; - if (ti != 2) - flags |= TJFLAG_NOREALLOC; - - /* tjLoadImage() refuses to load images larger than 1 Megapixel when - FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION is defined (yes, that's a dirty - hack), so we don't need to check the width and height here. */ - if ((srcBuf = tjLoadImage(filename, &width, 1, &height, &pf, - flags)) == NULL) + tj3Set(handle, TJPARAM_BOTTOMUP, ti == 0); + tj3Set(handle, TJPARAM_FASTDCT, ti == 1); + tj3Set(handle, TJPARAM_OPTIMIZE, ti == 6); + tj3Set(handle, TJPARAM_PROGRESSIVE, ti == 1 || ti == 3); + tj3Set(handle, TJPARAM_ARITHMETIC, ti == 2 || ti == 3); + tj3Set(handle, TJPARAM_NOREALLOC, ti != 2); + tj3Set(handle, TJPARAM_RESTARTROWS, ti == 1 || ti == 2 ? 2 : 0); + + tj3Set(handle, TJPARAM_MAXPIXELS, 1048576); + /* tj3LoadImage8() will refuse to load images larger than 1 Megapixel, so + we don't need to check the width and height here. */ + if ((srcBuf = tj3LoadImage8(handle, filename, &width, 1, &height, + &pf)) == NULL) continue; - maxBufSize = tjBufSize(width, height, tests[ti].subsamp); - if (flags & TJFLAG_NOREALLOC) { + maxBufSize = tj3JPEGBufSize(width, height, tests[ti].subsamp); + if (tj3Get(handle, TJPARAM_NOREALLOC)) { if ((dstBuf = (unsigned char *)malloc(maxBufSize)) == NULL) goto bailout; } else dstBuf = NULL; - if (tjCompress2(handle, srcBuf, width, 0, height, pf, &dstBuf, &dstSize, - tests[ti].subsamp, tests[ti].quality, flags) == 0) { + tj3Set(handle, TJPARAM_SUBSAMP, tests[ti].subsamp); + tj3Set(handle, TJPARAM_QUALITY, tests[ti].quality); + if (tj3Compress8(handle, srcBuf, width, 0, height, pf, &dstBuf, + &dstSize) == 0) { /* Touch all of the output pixels in order to catch uninitialized reads when using MemorySanitizer. */ for (i = 0; i < dstSize; i++) @@ -112,7 +113,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) free(dstBuf); dstBuf = NULL; - tjFree(srcBuf); + tj3Free(srcBuf); srcBuf = NULL; /* Prevent the code above from being optimized out. This test should never @@ -123,11 +124,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) bailout: free(dstBuf); - tjFree(srcBuf); + tj3Free(srcBuf); if (fd >= 0) { close(fd); if (strlen(filename) > 0) unlink(filename); } - if (handle) tjDestroy(handle); + tj3Destroy(handle); return 0; } diff --git a/fuzz/compress12.cc b/fuzz/compress12.cc new file mode 100644 index 000000000..ef0813172 --- /dev/null +++ b/fuzz/compress12.cc @@ -0,0 +1,133 @@ +/* + * Copyright (C)2021, 2023 D. R. Commander. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the libjpeg-turbo Project nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + + +#define NUMTESTS 7 + + +struct test { + enum TJPF pf; + enum TJSAMP subsamp; + int quality; +}; + + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + tjhandle handle = NULL; + short *srcBuf = NULL; + unsigned char *dstBuf = NULL; + int width = 0, height = 0, fd = -1, i, ti; + char filename[FILENAME_MAX] = { 0 }; + struct test tests[NUMTESTS] = { + { TJPF_RGB, TJSAMP_444, 100 }, + { TJPF_BGR, TJSAMP_422, 90 }, + { TJPF_RGBX, TJSAMP_420, 80 }, + { TJPF_BGRA, TJSAMP_411, 70 }, + { TJPF_XRGB, TJSAMP_GRAY, 60 }, + { TJPF_GRAY, TJSAMP_GRAY, 50 }, + { TJPF_CMYK, TJSAMP_440, 40 } + }; +#if defined(__has_feature) && __has_feature(memory_sanitizer) + char env[18] = "JSIMD_FORCENONE=1"; + + /* The libjpeg-turbo SIMD extensions produce false positives with + MemorySanitizer. */ + putenv(env); +#endif + + snprintf(filename, FILENAME_MAX, "/tmp/libjpeg-turbo_compress12_fuzz.XXXXXX"); + if ((fd = mkstemp(filename)) < 0 || write(fd, data, size) < 0) + goto bailout; + + if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL) + goto bailout; + + for (ti = 0; ti < NUMTESTS; ti++) { + int sum = 0, pf = tests[ti].pf; + size_t dstSize = 0, maxBufSize; + + /* Test non-default compression options on specific iterations. */ + tj3Set(handle, TJPARAM_BOTTOMUP, ti == 0); + tj3Set(handle, TJPARAM_FASTDCT, ti == 0); + tj3Set(handle, TJPARAM_PROGRESSIVE, ti == 1 || ti == 3); + tj3Set(handle, TJPARAM_NOREALLOC, ti != 2); + tj3Set(handle, TJPARAM_RESTARTROWS, ti == 1 || ti == 2 ? 2 : 0); + + tj3Set(handle, TJPARAM_MAXPIXELS, 1048576); + /* tj3LoadImage12() will refuse to load images larger than 1 Megapixel, so + we don't need to check the width and height here. */ + if ((srcBuf = tj3LoadImage12(handle, filename, &width, 1, &height, + &pf)) == NULL) + continue; + + maxBufSize = tj3JPEGBufSize(width, height, tests[ti].subsamp); + if (tj3Get(handle, TJPARAM_NOREALLOC)) { + if ((dstBuf = (unsigned char *)malloc(maxBufSize)) == NULL) + goto bailout; + } else + dstBuf = NULL; + + tj3Set(handle, TJPARAM_SUBSAMP, tests[ti].subsamp); + tj3Set(handle, TJPARAM_QUALITY, tests[ti].quality); + if (tj3Compress12(handle, srcBuf, width, 0, height, pf, &dstBuf, + &dstSize) == 0) { + /* Touch all of the output pixels in order to catch uninitialized reads + when using MemorySanitizer. */ + for (i = 0; i < dstSize; i++) + sum += dstBuf[i]; + } + + free(dstBuf); + dstBuf = NULL; + tj3Free(srcBuf); + srcBuf = NULL; + + /* Prevent the code above from being optimized out. This test should never + be true, but the compiler doesn't know that. */ + if (sum > 255 * maxBufSize) + goto bailout; + } + +bailout: + free(dstBuf); + tj3Free(srcBuf); + if (fd >= 0) { + close(fd); + if (strlen(filename) > 0) unlink(filename); + } + tj3Destroy(handle); + return 0; +} diff --git a/fuzz/compress12_lossless.cc b/fuzz/compress12_lossless.cc new file mode 100644 index 000000000..76a0a0d92 --- /dev/null +++ b/fuzz/compress12_lossless.cc @@ -0,0 +1,131 @@ +/* + * Copyright (C)2021-2023 D. R. Commander. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the libjpeg-turbo Project nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + + +#define NUMTESTS 7 + + +struct test { + enum TJPF pf; + int psv, pt; +}; + + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + tjhandle handle = NULL; + short *srcBuf = NULL; + unsigned char *dstBuf = NULL; + int width = 0, height = 0, fd = -1, i, ti; + char filename[FILENAME_MAX] = { 0 }; + struct test tests[NUMTESTS] = { + { TJPF_RGB, 1, 0 }, + { TJPF_BGR, 2, 2 }, + { TJPF_RGBX, 3, 4 }, + { TJPF_BGRA, 4, 7 }, + { TJPF_XRGB, 5, 5 }, + { TJPF_GRAY, 6, 3 }, + { TJPF_CMYK, 7, 0 } + }; +#if defined(__has_feature) && __has_feature(memory_sanitizer) + char env[18] = "JSIMD_FORCENONE=1"; + + /* The libjpeg-turbo SIMD extensions produce false positives with + MemorySanitizer. */ + putenv(env); +#endif + + snprintf(filename, FILENAME_MAX, "/tmp/libjpeg-turbo_compress_fuzz.XXXXXX"); + if ((fd = mkstemp(filename)) < 0 || write(fd, data, size) < 0) + goto bailout; + + if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL) + goto bailout; + + for (ti = 0; ti < NUMTESTS; ti++) { + int sum = 0, pf = tests[ti].pf; + size_t dstSize = 0, maxBufSize; + + /* Test non-default compression options on specific iterations. */ + tj3Set(handle, TJPARAM_BOTTOMUP, ti == 0); + tj3Set(handle, TJPARAM_NOREALLOC, ti != 2); + tj3Set(handle, TJPARAM_RESTARTROWS, ti == 0 || ti == 6 ? 1 : 0); + + tj3Set(handle, TJPARAM_MAXPIXELS, 1048576); + /* tj3LoadImage12() will refuse to load images larger than 1 Megapixel, so + we don't need to check the width and height here. */ + if ((srcBuf = tj3LoadImage12(handle, filename, &width, 1, &height, + &pf)) == NULL) + continue; + + maxBufSize = tj3JPEGBufSize(width, height, TJSAMP_444); + if (tj3Get(handle, TJPARAM_NOREALLOC)) { + if ((dstBuf = (unsigned char *)malloc(maxBufSize)) == NULL) + goto bailout; + } else + dstBuf = NULL; + + tj3Set(handle, TJPARAM_LOSSLESS, 1); + tj3Set(handle, TJPARAM_LOSSLESSPSV, tests[ti].psv); + tj3Set(handle, TJPARAM_LOSSLESSPT, tests[ti].pt); + if (tj3Compress12(handle, srcBuf, width, 0, height, pf, &dstBuf, + &dstSize) == 0) { + /* Touch all of the output pixels in order to catch uninitialized reads + when using MemorySanitizer. */ + for (i = 0; i < dstSize; i++) + sum += dstBuf[i]; + } + + free(dstBuf); + dstBuf = NULL; + tj3Free(srcBuf); + srcBuf = NULL; + + /* Prevent the code above from being optimized out. This test should never + be true, but the compiler doesn't know that. */ + if (sum > 255 * maxBufSize) + goto bailout; + } + +bailout: + free(dstBuf); + tj3Free(srcBuf); + if (fd >= 0) { + close(fd); + if (strlen(filename) > 0) unlink(filename); + } + tj3Destroy(handle); + return 0; +} diff --git a/fuzz/compress16_lossless.cc b/fuzz/compress16_lossless.cc new file mode 100644 index 000000000..aa6037ec7 --- /dev/null +++ b/fuzz/compress16_lossless.cc @@ -0,0 +1,131 @@ +/* + * Copyright (C)2021-2023 D. R. Commander. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the libjpeg-turbo Project nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + + +#define NUMTESTS 7 + + +struct test { + enum TJPF pf; + int psv, pt; +}; + + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + tjhandle handle = NULL; + unsigned short *srcBuf = NULL; + unsigned char *dstBuf = NULL; + int width = 0, height = 0, fd = -1, i, ti; + char filename[FILENAME_MAX] = { 0 }; + struct test tests[NUMTESTS] = { + { TJPF_RGB, 1, 0 }, + { TJPF_BGR, 2, 2 }, + { TJPF_RGBX, 3, 4 }, + { TJPF_BGRA, 4, 7 }, + { TJPF_XRGB, 5, 5 }, + { TJPF_GRAY, 6, 3 }, + { TJPF_CMYK, 7, 0 } + }; +#if defined(__has_feature) && __has_feature(memory_sanitizer) + char env[18] = "JSIMD_FORCENONE=1"; + + /* The libjpeg-turbo SIMD extensions produce false positives with + MemorySanitizer. */ + putenv(env); +#endif + + snprintf(filename, FILENAME_MAX, "/tmp/libjpeg-turbo_compress_fuzz.XXXXXX"); + if ((fd = mkstemp(filename)) < 0 || write(fd, data, size) < 0) + goto bailout; + + if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL) + goto bailout; + + for (ti = 0; ti < NUMTESTS; ti++) { + int sum = 0, pf = tests[ti].pf; + size_t dstSize = 0, maxBufSize; + + /* Test non-default compression options on specific iterations. */ + tj3Set(handle, TJPARAM_BOTTOMUP, ti == 0); + tj3Set(handle, TJPARAM_NOREALLOC, ti != 2); + tj3Set(handle, TJPARAM_RESTARTROWS, ti == 0 || ti == 6 ? 1 : 0); + + tj3Set(handle, TJPARAM_MAXPIXELS, 1048576); + /* tj3LoadImage16() will refuse to load images larger than 1 Megapixel, so + we don't need to check the width and height here. */ + if ((srcBuf = tj3LoadImage16(handle, filename, &width, 1, &height, + &pf)) == NULL) + continue; + + maxBufSize = tj3JPEGBufSize(width, height, TJSAMP_444); + if (tj3Get(handle, TJPARAM_NOREALLOC)) { + if ((dstBuf = (unsigned char *)malloc(maxBufSize)) == NULL) + goto bailout; + } else + dstBuf = NULL; + + tj3Set(handle, TJPARAM_LOSSLESS, 1); + tj3Set(handle, TJPARAM_LOSSLESSPSV, tests[ti].psv); + tj3Set(handle, TJPARAM_LOSSLESSPT, tests[ti].pt); + if (tj3Compress16(handle, srcBuf, width, 0, height, pf, &dstBuf, + &dstSize) == 0) { + /* Touch all of the output pixels in order to catch uninitialized reads + when using MemorySanitizer. */ + for (i = 0; i < dstSize; i++) + sum += dstBuf[i]; + } + + free(dstBuf); + dstBuf = NULL; + tj3Free(srcBuf); + srcBuf = NULL; + + /* Prevent the code above from being optimized out. This test should never + be true, but the compiler doesn't know that. */ + if (sum > 255 * maxBufSize) + goto bailout; + } + +bailout: + free(dstBuf); + tj3Free(srcBuf); + if (fd >= 0) { + close(fd); + if (strlen(filename) > 0) unlink(filename); + } + tj3Destroy(handle); + return 0; +} diff --git a/fuzz/compress_lossless.cc b/fuzz/compress_lossless.cc index 4ba156691..7c2bb7903 100644 --- a/fuzz/compress_lossless.cc +++ b/fuzz/compress_lossless.cc @@ -1,5 +1,5 @@ /* - * Copyright (C)2021-2022 D. R. Commander. All Rights Reserved. + * Copyright (C)2021-2023 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -35,8 +35,6 @@ #define NUMTESTS 7 -/* Private flag that triggers different TurboJPEG API behavior when fuzzing */ -#define TJFLAG_FUZZING (1 << 30) struct test { @@ -72,36 +70,37 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) if ((fd = mkstemp(filename)) < 0 || write(fd, data, size) < 0) goto bailout; - if ((handle = tjInitCompress()) == NULL) + if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL) goto bailout; for (ti = 0; ti < NUMTESTS; ti++) { - int flags = TJFLAG_FUZZING | TJFLAG_LOSSLESS, sum = 0, pf = tests[ti].pf; - unsigned long dstSize = 0, maxBufSize; + int sum = 0, pf = tests[ti].pf; + size_t dstSize = 0, maxBufSize; /* Test non-default compression options on specific iterations. */ - if (ti == 0) - flags |= TJFLAG_BOTTOMUP; - if (ti != 2) - flags |= TJFLAG_NOREALLOC; - - /* tjLoadImage() refuses to load images larger than 1 Megapixel when - FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION is defined (yes, that's a dirty - hack), so we don't need to check the width and height here. */ - if ((srcBuf = tjLoadImage(filename, &width, 1, &height, &pf, - flags)) == NULL) + tj3Set(handle, TJPARAM_BOTTOMUP, ti == 0); + tj3Set(handle, TJPARAM_NOREALLOC, ti != 2); + tj3Set(handle, TJPARAM_RESTARTROWS, ti == 0 || ti == 6 ? 1 : 0); + + tj3Set(handle, TJPARAM_MAXPIXELS, 1048576); + /* tj3LoadImage8() will refuse to load images larger than 1 Megapixel, so + we don't need to check the width and height here. */ + if ((srcBuf = tj3LoadImage8(handle, filename, &width, 1, &height, + &pf)) == NULL) continue; - maxBufSize = tjBufSize(width, height, TJSAMP_444); - if (flags & TJFLAG_NOREALLOC) { + maxBufSize = tj3JPEGBufSize(width, height, TJSAMP_444); + if (tj3Get(handle, TJPARAM_NOREALLOC)) { if ((dstBuf = (unsigned char *)malloc(maxBufSize)) == NULL) goto bailout; } else dstBuf = NULL; - if (tjCompress2(handle, srcBuf, width, 0, height, pf, &dstBuf, &dstSize, - TJSAMP_444, tests[ti].psv * 10 + tests[ti].pt, - flags) == 0) { + tj3Set(handle, TJPARAM_LOSSLESS, 1); + tj3Set(handle, TJPARAM_LOSSLESSPSV, tests[ti].psv); + tj3Set(handle, TJPARAM_LOSSLESSPT, tests[ti].pt); + if (tj3Compress8(handle, srcBuf, width, 0, height, pf, &dstBuf, + &dstSize) == 0) { /* Touch all of the output pixels in order to catch uninitialized reads when using MemorySanitizer. */ for (i = 0; i < dstSize; i++) @@ -110,7 +109,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) free(dstBuf); dstBuf = NULL; - tjFree(srcBuf); + tj3Free(srcBuf); srcBuf = NULL; /* Prevent the code above from being optimized out. This test should never @@ -121,11 +120,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) bailout: free(dstBuf); - tjFree(srcBuf); + tj3Free(srcBuf); if (fd >= 0) { close(fd); if (strlen(filename) > 0) unlink(filename); } - if (handle) tjDestroy(handle); + tj3Destroy(handle); return 0; } diff --git a/fuzz/compress_yuv.cc b/fuzz/compress_yuv.cc index ef41fc2d4..0b12e0ceb 100644 --- a/fuzz/compress_yuv.cc +++ b/fuzz/compress_yuv.cc @@ -1,5 +1,5 @@ /* - * Copyright (C)2021-2022 D. R. Commander. All Rights Reserved. + * Copyright (C)2021-2023 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -35,8 +35,6 @@ #define NUMTESTS 6 -/* Private flag that triggers different TurboJPEG API behavior when fuzzing */ -#define TJFLAG_FUZZING (1 << 30) struct test { @@ -60,58 +58,54 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { TJPF_BGR, TJSAMP_GRAY, 60 }, { TJPF_GRAY, TJSAMP_GRAY, 50 } }; - char restartEnv[13] = "TJ_RESTART=0"; #if defined(__has_feature) && __has_feature(memory_sanitizer) - char simdEnv[18] = "JSIMD_FORCENONE=1"; + char env[18] = "JSIMD_FORCENONE=1"; /* The libjpeg-turbo SIMD extensions produce false positives with MemorySanitizer. */ - putenv(simdEnv); + putenv(env); #endif - putenv(restartEnv); snprintf(filename, FILENAME_MAX, "/tmp/libjpeg-turbo_compress_yuv_fuzz.XXXXXX"); if ((fd = mkstemp(filename)) < 0 || write(fd, data, size) < 0) goto bailout; - if ((handle = tjInitCompress()) == NULL) + if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL) goto bailout; for (ti = 0; ti < NUMTESTS; ti++) { - int flags = TJFLAG_FUZZING | TJFLAG_NOREALLOC, sum = 0, pf = tests[ti].pf; - unsigned long dstSize = 0, maxBufSize; + int sum = 0, pf = tests[ti].pf; + size_t dstSize = 0, maxBufSize; /* Test non-default compression options on specific iterations. */ - if (ti == 0) - flags |= TJFLAG_BOTTOMUP | TJFLAG_ACCURATEDCT; - else if (ti == 1 || ti == 3) - flags |= TJFLAG_PROGRESSIVE; - if (ti == 2 || ti == 3) - flags |= TJFLAG_ARITHMETIC; - if (ti == 1 || ti == 2) - restartEnv[11] = '2'; - else - restartEnv[11] = '0'; - - /* tjLoadImage() refuses to load images larger than 1 Megapixel when - FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION is defined (yes, that's a dirty - hack), so we don't need to check the width and height here. */ - if ((srcBuf = tjLoadImage(filename, &width, 1, &height, &pf, - flags)) == NULL) + tj3Set(handle, TJPARAM_BOTTOMUP, ti == 0); + tj3Set(handle, TJPARAM_FASTDCT, ti == 1); + tj3Set(handle, TJPARAM_OPTIMIZE, ti == 4); + tj3Set(handle, TJPARAM_PROGRESSIVE, ti == 1 || ti == 3); + tj3Set(handle, TJPARAM_ARITHMETIC, ti == 2 || ti == 3); + tj3Set(handle, TJPARAM_NOREALLOC, 1); + tj3Set(handle, TJPARAM_RESTARTBLOCKS, ti == 3 || ti == 4 ? 4 : 0); + + tj3Set(handle, TJPARAM_MAXPIXELS, 1048576); + /* tj3LoadImage8() will refuse to load images larger than 1 Megapixel, so + we don't need to check the width and height here. */ + if ((srcBuf = tj3LoadImage8(handle, filename, &width, 1, &height, + &pf)) == NULL) continue; - maxBufSize = tjBufSize(width, height, tests[ti].subsamp); + maxBufSize = tj3JPEGBufSize(width, height, tests[ti].subsamp); if ((dstBuf = (unsigned char *)malloc(maxBufSize)) == NULL) goto bailout; if ((yuvBuf = - (unsigned char *)malloc(tjBufSizeYUV2(width, 1, height, + (unsigned char *)malloc(tj3YUVBufSize(width, 1, height, tests[ti].subsamp))) == NULL) goto bailout; - if (tjEncodeYUV3(handle, srcBuf, width, 0, height, pf, yuvBuf, 1, - tests[ti].subsamp, flags) == 0 && - tjCompressFromYUV(handle, yuvBuf, width, 1, height, tests[ti].subsamp, - &dstBuf, &dstSize, tests[ti].quality, flags) == 0) { + tj3Set(handle, TJPARAM_SUBSAMP, tests[ti].subsamp); + tj3Set(handle, TJPARAM_QUALITY, tests[ti].quality); + if (tj3EncodeYUV8(handle, srcBuf, width, 0, height, pf, yuvBuf, 1) == 0 && + tj3CompressFromYUV8(handle, yuvBuf, width, 1, height, &dstBuf, + &dstSize) == 0) { /* Touch all of the output pixels in order to catch uninitialized reads when using MemorySanitizer. */ for (i = 0; i < dstSize; i++) @@ -122,7 +116,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) dstBuf = NULL; free(yuvBuf); yuvBuf = NULL; - tjFree(srcBuf); + tj3Free(srcBuf); srcBuf = NULL; /* Prevent the code above from being optimized out. This test should never @@ -134,11 +128,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) bailout: free(dstBuf); free(yuvBuf); - tjFree(srcBuf); + tj3Free(srcBuf); if (fd >= 0) { close(fd); if (strlen(filename) > 0) unlink(filename); } - if (handle) tjDestroy(handle); + tj3Destroy(handle); return 0; } diff --git a/fuzz/decompress.cc b/fuzz/decompress.cc index b6604224b..2ef17ba04 100644 --- a/fuzz/decompress.cc +++ b/fuzz/decompress.cc @@ -1,5 +1,5 @@ /* - * Copyright (C)2021-2022 D. R. Commander. All Rights Reserved. + * Copyright (C)2021-2023 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -37,8 +37,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { tjhandle handle = NULL; - unsigned char *dstBuf = NULL; - int width = 0, height = 0, jpegSubsamp, jpegColorspace, jpegFlags, pfi; + void *dstBuf = NULL; + int width = 0, height = 0, precision, sampleSize, pfi; /* TJPF_RGB-TJPF_BGR share the same code paths, as do TJPF_RGBX-TJPF_XRGB and TJPF_RGBA-TJPF_ARGB. Thus, the pixel formats below should be the minimum necessary to achieve full coverage. */ @@ -52,14 +52,15 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) putenv(env); #endif - if ((handle = tjInitDecompress()) == NULL) + if ((handle = tj3Init(TJINIT_DECOMPRESS)) == NULL) goto bailout; - /* We ignore the return value of tjDecompressHeader3(), because some JPEG - images may have unusual subsampling configurations that the TurboJPEG API - cannot identify but can still decompress. */ - tjDecompressHeader4(handle, data, size, &width, &height, &jpegSubsamp, - &jpegColorspace, &jpegFlags); + if (tj3DecompressHeader(handle, data, size) < 0) + goto bailout; + width = tj3Get(handle, TJPARAM_JPEGWIDTH); + height = tj3Get(handle, TJPARAM_JPEGHEIGHT); + precision = tj3Get(handle, TJPARAM_PRECISION); + sampleSize = (precision > 8 ? 2 : 1); /* Ignore 0-pixel images and images larger than 1 Megapixel, as Google's OSS-Fuzz target for libjpeg-turbo did. Casting width to (uint64_t) @@ -67,27 +68,61 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) if (width < 1 || height < 1 || (uint64_t)width * height > 1048576) goto bailout; + tj3Set(handle, TJPARAM_SCANLIMIT, 500); + for (pfi = 0; pfi < NUMPF; pfi++) { - int pf = pixelFormats[pfi], flags = TJFLAG_LIMITSCANS, i, sum = 0; - int w = width, h = height; + int pf = pixelFormats[pfi], i; + int64_t sum = 0; /* Test non-default decompression options on the first iteration. */ - if (pfi == 0) - flags |= TJFLAG_BOTTOMUP | TJFLAG_FASTUPSAMPLE | TJFLAG_FASTDCT; - /* Test IDCT scaling on the second iteration. */ - else if (pfi == 1 && !(jpegFlags & TJFLAG_LOSSLESS)) { - w = (width + 1) / 2; - h = (height + 1) / 2; + if (!tj3Get(handle, TJPARAM_LOSSLESS)) { + tj3Set(handle, TJPARAM_BOTTOMUP, pfi == 0); + tj3Set(handle, TJPARAM_FASTUPSAMPLE, pfi == 0); + tj3Set(handle, TJPARAM_FASTDCT, pfi == 0); + + /* Test IDCT scaling on the second iteration. */ + if (pfi == 1) { + tjscalingfactor sf = { 1, 2 }; + tj3SetScalingFactor(handle, sf); + } else + tj3SetScalingFactor(handle, TJUNSCALED); + + /* Test partial image decompression on the fourth iteration, if the image + is large enough. */ + if (pfi == 3 && width >= 97 && height >= 75) { + tjregion cr = { 32, 16, 65, 59 }; + tj3SetCroppingRegion(handle, cr); + } else + tj3SetCroppingRegion(handle, TJUNCROPPED); } - if ((dstBuf = (unsigned char *)malloc(w * h * tjPixelSize[pf])) == NULL) + if ((dstBuf = malloc(width * height * tjPixelSize[pf] * + sampleSize)) == NULL) goto bailout; - if (tjDecompress2(handle, data, size, dstBuf, w, 0, h, pf, flags) == 0) { - /* Touch all of the output pixels in order to catch uninitialized reads - when using MemorySanitizer. */ - for (i = 0; i < w * h * tjPixelSize[pf]; i++) - sum += dstBuf[i]; + if (precision == 8) { + if (tj3Decompress8(handle, data, size, (unsigned char *)dstBuf, 0, + pf) == 0) { + /* Touch all of the output pixels in order to catch uninitialized reads + when using MemorySanitizer. */ + for (i = 0; i < width * height * tjPixelSize[pf]; i++) + sum += ((unsigned char *)dstBuf)[i]; + } + } else if (precision == 12) { + if (tj3Decompress12(handle, data, size, (short *)dstBuf, 0, pf) == 0) { + /* Touch all of the output pixels in order to catch uninitialized reads + when using MemorySanitizer. */ + for (i = 0; i < width * height * tjPixelSize[pf]; i++) + sum += ((short *)dstBuf)[i]; + } + } else { + if (tj3Decompress16(handle, data, size, (unsigned short *)dstBuf, 0, + pf) == 0) { + /* Touch all of the output pixels in order to catch uninitialized reads + when using MemorySanitizer. */ + for (i = 0; i < width * height * tjPixelSize[pf]; i++) + sum += ((unsigned short *)dstBuf)[i]; + } } free(dstBuf); @@ -95,12 +130,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) /* Prevent the code above from being optimized out. This test should never be true, but the compiler doesn't know that. */ - if (sum > 255 * 1048576 * tjPixelSize[pf]) + if (sum > ((1LL << precision) - 1LL) * 1048576LL * tjPixelSize[pf]) goto bailout; } bailout: free(dstBuf); - if (handle) tjDestroy(handle); + tj3Destroy(handle); return 0; } diff --git a/fuzz/decompress_yuv.cc b/fuzz/decompress_yuv.cc index 4b2c89de6..125b16322 100644 --- a/fuzz/decompress_yuv.cc +++ b/fuzz/decompress_yuv.cc @@ -1,5 +1,5 @@ /* - * Copyright (C)2021-2022 D. R. Commander. All Rights Reserved. + * Copyright (C)2021-2023 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -38,7 +38,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { tjhandle handle = NULL; unsigned char *dstBuf = NULL, *yuvBuf = NULL; - int width = 0, height = 0, jpegSubsamp, jpegColorspace, jpegFlags, pfi; + int width = 0, height = 0, jpegSubsamp, pfi; /* TJPF_RGB-TJPF_BGR share the same code paths, as do TJPF_RGBX-TJPF_XRGB and TJPF_RGBA-TJPF_ARGB. Thus, the pixel formats below should be the minimum necessary to achieve full coverage. */ @@ -52,40 +52,50 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) putenv(env); #endif - if ((handle = tjInitDecompress()) == NULL) + if ((handle = tj3Init(TJINIT_DECOMPRESS)) == NULL) goto bailout; - if (tjDecompressHeader4(handle, data, size, &width, &height, &jpegSubsamp, - &jpegColorspace, &jpegFlags) < 0) + if (tj3DecompressHeader(handle, data, size) < 0) goto bailout; + width = tj3Get(handle, TJPARAM_JPEGWIDTH); + height = tj3Get(handle, TJPARAM_JPEGHEIGHT); + jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP); /* Ignore 0-pixel images and images larger than 1 Megapixel. Casting width to (uint64_t) prevents integer overflow if width * height > INT_MAX. */ if (width < 1 || height < 1 || (uint64_t)width * height > 1048576) goto bailout; + tj3Set(handle, TJPARAM_SCANLIMIT, 500); + for (pfi = 0; pfi < NUMPF; pfi++) { - int pf = pixelFormats[pfi], flags = TJFLAG_LIMITSCANS, i, sum = 0; int w = width, h = height; + int pf = pixelFormats[pfi], i, sum = 0; /* Test non-default decompression options on the first iteration. */ - if (pfi == 0) - flags |= TJFLAG_BOTTOMUP | TJFLAG_FASTUPSAMPLE | TJFLAG_FASTDCT; - /* Test IDCT scaling on the second iteration. */ - else if (pfi == 1 && !(jpegFlags & TJFLAG_LOSSLESS)) { - w = (width + 3) / 4; - h = (height + 3) / 4; + if (!tj3Get(handle, TJPARAM_LOSSLESS)) { + tj3Set(handle, TJPARAM_BOTTOMUP, pfi == 0); + tj3Set(handle, TJPARAM_FASTUPSAMPLE, pfi == 0); + tj3Set(handle, TJPARAM_FASTDCT, pfi == 0); + + /* Test IDCT scaling on the second iteration. */ + if (pfi == 1) { + tjscalingfactor sf = { 3, 4 }; + tj3SetScalingFactor(handle, sf); + w = TJSCALED(width, sf); + h = TJSCALED(height, sf); + } else + tj3SetScalingFactor(handle, TJUNSCALED); } if ((dstBuf = (unsigned char *)malloc(w * h * tjPixelSize[pf])) == NULL) goto bailout; if ((yuvBuf = - (unsigned char *)malloc(tjBufSizeYUV2(w, 1, h, jpegSubsamp))) == NULL) + (unsigned char *)malloc(tj3YUVBufSize(w, 1, h, jpegSubsamp))) == NULL) goto bailout; - if (tjDecompressToYUV2(handle, data, size, yuvBuf, w, 1, h, flags) == 0 && - tjDecodeYUV(handle, yuvBuf, 1, jpegSubsamp, dstBuf, w, 0, h, pf, - flags) == 0) { + if (tj3DecompressToYUV8(handle, data, size, yuvBuf, 1) == 0 && + tj3DecodeYUV8(handle, yuvBuf, 1, dstBuf, w, 0, h, pf) == 0) { /* Touch all of the output pixels in order to catch uninitialized reads when using MemorySanitizer. */ for (i = 0; i < w * h * tjPixelSize[pf]; i++) @@ -106,6 +116,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) bailout: free(dstBuf); free(yuvBuf); - if (handle) tjDestroy(handle); + tj3Destroy(handle); return 0; } diff --git a/fuzz/transform.cc b/fuzz/transform.cc index 4511102bd..f45521254 100644 --- a/fuzz/transform.cc +++ b/fuzz/transform.cc @@ -1,5 +1,5 @@ /* - * Copyright (C)2021-2022 D. R. Commander. All Rights Reserved. + * Copyright (C)2021-2023 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -40,7 +40,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) tjhandle handle = NULL; unsigned char *dstBufs[NUMXFORMS] = { NULL, NULL, NULL }; unsigned long dstSizes[NUMXFORMS] = { 0, 0, 0 }, maxBufSize; - int width = 0, height = 0, jpegSubsamp, jpegColorspace, i, t; + int width = 0, height = 0, jpegSubsamp, i, t; tjtransform transforms[NUMXFORMS]; #if defined(__has_feature) && __has_feature(memory_sanitizer) char env[18] = "JSIMD_FORCENONE=1"; @@ -50,20 +50,22 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) putenv(env); #endif - if ((handle = tjInitTransform()) == NULL) + if ((handle = tj3Init(TJINIT_TRANSFORM)) == NULL) goto bailout; - /* We ignore the return value of tjDecompressHeader3(), because some JPEG - images may have unusual subsampling configurations that the TurboJPEG API - cannot identify but can still transform. */ - tjDecompressHeader3(handle, data, size, &width, &height, &jpegSubsamp, - &jpegColorspace); + if (tj3DecompressHeader(handle, data, size) < 0) + goto bailout; + width = tj3Get(handle, TJPARAM_JPEGWIDTH); + height = tj3Get(handle, TJPARAM_JPEGHEIGHT); + jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP); /* Ignore 0-pixel images and images larger than 1 Megapixel. Casting width to (uint64_t) prevents integer overflow if width * height > INT_MAX. */ if (width < 1 || height < 1 || (uint64_t)width * height > 1048576) goto bailout; + tj3Set(handle, TJPARAM_SCANLIMIT, 500); + if (jpegSubsamp < 0 || jpegSubsamp >= TJ_NUMSAMP) jpegSubsamp = TJSAMP_444; @@ -72,7 +74,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) transforms[0].op = TJXOP_NONE; transforms[0].options = TJXOPT_PROGRESSIVE | TJXOPT_COPYNONE; - dstBufs[0] = (unsigned char *)malloc(tjBufSize(width, height, jpegSubsamp)); + dstBufs[0] = + (unsigned char *)malloc(tj3JPEGBufSize(width, height, jpegSubsamp)); if (!dstBufs[0]) goto bailout; @@ -81,21 +84,23 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) transforms[1].op = TJXOP_TRANSPOSE; transforms[1].options = TJXOPT_GRAY | TJXOPT_CROP | TJXOPT_COPYNONE; dstBufs[1] = - (unsigned char *)malloc(tjBufSize((width + 1) / 2, (height + 1) / 2, - TJSAMP_GRAY)); + (unsigned char *)malloc(tj3JPEGBufSize((width + 1) / 2, (height + 1) / 2, + TJSAMP_GRAY)); if (!dstBufs[1]) goto bailout; transforms[2].op = TJXOP_ROT90; transforms[2].options = TJXOPT_TRIM | TJXOPT_COPYNONE | TJXOPT_ARITHMETIC; - dstBufs[2] = (unsigned char *)malloc(tjBufSize(height, width, jpegSubsamp)); + dstBufs[2] = + (unsigned char *)malloc(tj3JPEGBufSize(height, width, jpegSubsamp)); if (!dstBufs[2]) goto bailout; - maxBufSize = tjBufSize(width, height, jpegSubsamp); + maxBufSize = tj3JPEGBufSize(width, height, jpegSubsamp); - if (tjTransform(handle, data, size, NUMXFORMS, dstBufs, dstSizes, transforms, - TJFLAG_LIMITSCANS | TJFLAG_NOREALLOC) == 0) { + tj3Set(handle, TJPARAM_NOREALLOC, 1); + if (tj3Transform(handle, data, size, NUMXFORMS, dstBufs, dstSizes, + transforms) == 0) { /* Touch all of the output pixels in order to catch uninitialized reads when using MemorySanitizer. */ for (t = 0; t < NUMXFORMS; t++) { @@ -112,12 +117,14 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) } transforms[0].options &= ~TJXOPT_COPYNONE; + transforms[0].options |= TJXOPT_OPTIMIZE; free(dstBufs[0]); dstBufs[0] = NULL; dstSizes[0] = 0; - if (tjTransform(handle, data, size, 1, dstBufs, dstSizes, transforms, - TJFLAG_LIMITSCANS) == 0) { + tj3Set(handle, TJPARAM_NOREALLOC, 0); + if (tj3Transform(handle, data, size, 1, dstBufs, dstSizes, + transforms) == 0) { int sum = 0; for (i = 0; i < dstSizes[0]; i++) @@ -130,6 +137,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) bailout: for (t = 0; t < NUMXFORMS; t++) free(dstBufs[t]); - if (handle) tjDestroy(handle); + tj3Destroy(handle); return 0; } diff --git a/java/TJBench.java b/java/TJBench.java index f1f3854b7..39260feb9 100644 --- a/java/TJBench.java +++ b/java/TJBench.java @@ -28,8 +28,10 @@ */ import java.io.*; +import java.awt.*; import java.awt.image.*; import javax.imageio.*; +import java.nio.*; import java.util.*; import org.libjpegturbo.turbojpeg.*; @@ -37,11 +39,16 @@ final class TJBench { private TJBench() {} - private static int flags = 0, quiet = 0, pf = TJ.PF_BGR, yuvAlign = 1; - private static boolean compOnly, decompOnly, doTile, doYUV, write = true; + private static boolean stopOnWarning, bottomUp, fastUpsample, fastDCT, + optimize, progressive, limitScans, arithmetic, lossless; + private static int precision = 8, quiet = 0, pf = TJ.PF_BGR, yuvAlign = 1, + restartIntervalBlocks, restartIntervalRows = 0; + private static boolean compOnly, decompOnly, doTile, doYUV, write = true, + bmp = false; static final String[] PIXFORMATSTR = { - "RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "GRAY" + "RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "GRAY", "", "", "", "", + "CMYK" }; static final String[] SUBNAME_LONG = { @@ -56,11 +63,44 @@ private TJBench() {} "RGB", "YCbCr", "GRAY", "CMYK", "YCCK" }; - private static TJScalingFactor sf; + private static TJScalingFactor sf = TJ.UNSCALED; + private static java.awt.Rectangle cr = TJ.UNCROPPED; private static int xformOp = TJTransform.OP_NONE, xformOpt = 0; private static double benchTime = 5.0, warmup = 1.0; + private static class DummyDCTFilter implements TJCustomFilter { + public void customFilter(ShortBuffer coeffBuffer, Rectangle bufferRegion, + Rectangle planeRegion, int componentID, + int transformID, TJTransform transform) { + for (int i = 0; i < bufferRegion.width * bufferRegion.height; i++) + coeffBuffer.put(i, (short)(-coeffBuffer.get(i))); + } + } + + private static DummyDCTFilter customFilter; + + + @SuppressWarnings("checkstyle:HiddenField") + private static boolean isCropped(java.awt.Rectangle cr) { + return (cr.x != 0 || cr.y != 0 || cr.width != 0 || cr.height != 0); + } + + private static int getCroppedWidth(int width) { + if (isCropped(cr)) + return (cr.width != 0 ? cr.width : sf.getScaled(width) - cr.x); + else + return sf.getScaled(width); + } + + private static int getCroppedHeight(int height) { + if (isCropped(cr)) + return (cr.height != 0 ? cr.height : sf.getScaled(height) - cr.y); + else + return sf.getScaled(height); + } + + static double getTime() { return (double)System.nanoTime() / 1.0e9; } @@ -73,8 +113,7 @@ static void handleTJException(TJException e) throws TJException { String errorMsg = e.getMessage(); int errorCode = e.getErrorCode(); - if ((flags & TJ.FLAG_STOPONWARNING) == 0 && - errorCode == TJ.ERR_WARNING) { + if (!stopOnWarning && errorCode == TJ.ERR_WARNING) { if (tjErrorMsg == null || !tjErrorMsg.equals(errorMsg) || tjErrorCode != errorCode) { tjErrorMsg = errorMsg; @@ -87,12 +126,22 @@ static void handleTJException(TJException e) throws TJException { static String formatName(int subsamp, int cs) { - if (cs == TJ.CS_YCbCr) - return SUBNAME_LONG[subsamp]; - else if (cs == TJ.CS_YCCK) - return CSNAME[cs] + " " + SUBNAME_LONG[subsamp]; - else - return CSNAME[cs]; + if (quiet != 0) { + if (lossless) + return String.format("%-2d/LOSSLESS ", precision); + else if (subsamp == TJ.SAMP_UNKNOWN) + return String.format("%-2d/%-5s ", precision, CSNAME[cs]); + else + return String.format("%-2d/%-5s/%-5s", precision, CSNAME[cs], + SUBNAME_LONG[subsamp]); + } else { + if (lossless) + return "Lossless"; + else if (subsamp == TJ.SAMP_UNKNOWN) + return CSNAME[cs]; + else + return CSNAME[cs] + " " + SUBNAME_LONG[subsamp]; + } } @@ -108,62 +157,10 @@ static String sigFig(double val, int figs) { } - static byte[] loadImage(String fileName, int[] w, int[] h, int pixelFormat) - throws Exception { - BufferedImage img = ImageIO.read(new File(fileName)); - - if (img == null) - throw new Exception("Could not read " + fileName); - w[0] = img.getWidth(); - h[0] = img.getHeight(); - - int[] rgb = img.getRGB(0, 0, w[0], h[0], null, 0, w[0]); - int ps = TJ.getPixelSize(pixelFormat); - int rindex = TJ.getRedOffset(pixelFormat); - int gindex = TJ.getGreenOffset(pixelFormat); - int bindex = TJ.getBlueOffset(pixelFormat); - if ((long)w[0] * (long)h[0] * (long)ps > (long)Integer.MAX_VALUE) - throw new Exception("Image is too large"); - byte[] dstBuf = new byte[w[0] * h[0] * ps]; - int pixels = w[0] * h[0], dstPtr = 0, rgbPtr = 0; - - while (pixels-- > 0) { - dstBuf[dstPtr + rindex] = (byte)((rgb[rgbPtr] >> 16) & 0xff); - dstBuf[dstPtr + gindex] = (byte)((rgb[rgbPtr] >> 8) & 0xff); - dstBuf[dstPtr + bindex] = (byte)(rgb[rgbPtr] & 0xff); - dstPtr += ps; - rgbPtr++; - } - return dstBuf; - } - - - static void saveImage(String fileName, byte[] srcBuf, int w, int h, - int pixelFormat) throws Exception { - BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); - int pixels = w * h, srcPtr = 0; - int ps = TJ.getPixelSize(pixelFormat); - int rindex = TJ.getRedOffset(pixelFormat); - int gindex = TJ.getGreenOffset(pixelFormat); - int bindex = TJ.getBlueOffset(pixelFormat); - - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++, srcPtr += ps) { - int pixel = (srcBuf[srcPtr + rindex] & 0xff) << 16 | - (srcBuf[srcPtr + gindex] & 0xff) << 8 | - (srcBuf[srcPtr + bindex] & 0xff); - - img.setRGB(x, y, pixel); - } - } - ImageIO.write(img, "bmp", new File(fileName)); - } - - /* Decompression test */ - static void decomp(byte[] srcBuf, byte[][] jpegBuf, int[] jpegSize, - byte[] dstBuf, int w, int h, int subsamp, int jpegQual, - String fileName, int tilew, int tileh) throws Exception { + static void decomp(byte[][] jpegBufs, int[] jpegSizes, Object dstBuf, int w, + int h, int subsamp, int jpegQual, String fileName, + int tilew, int tileh) throws Exception { String qualStr = new String(""), sizeStr, tempStr; TJDecompressor tjd; double elapsed, elapsedDecode; @@ -171,27 +168,52 @@ static void decomp(byte[] srcBuf, byte[][] jpegBuf, int[] jpegSize, int scaledw, scaledh, pitch; YUVImage yuvImage = null; - if ((flags & TJ.FLAG_LOSSLESS) != 0) - sf = new TJScalingFactor(1, 1); + if (lossless) + sf = TJ.UNSCALED; scaledw = sf.getScaled(w); scaledh = sf.getScaled(h); - pitch = scaledw * ps; if (jpegQual > 0) - qualStr = new String("_Q" + jpegQual); + qualStr = new String((lossless ? "_PSV" : "_Q") + jpegQual); tjd = new TJDecompressor(); + tjd.set(TJ.PARAM_STOPONWARNING, stopOnWarning ? 1 : 0); + tjd.set(TJ.PARAM_BOTTOMUP, bottomUp ? 1 : 0); + tjd.set(TJ.PARAM_FASTUPSAMPLE, fastUpsample ? 1 : 0); + tjd.set(TJ.PARAM_FASTDCT, fastDCT ? 1 : 0); + tjd.set(TJ.PARAM_SCANLIMIT, limitScans ? 500 : 0); + + if (isCropped(cr)) { + try { + tjd.setSourceImage(jpegBufs[0], jpegSizes[0]); + } catch (TJException e) { handleTJException(e); } + } + tjd.setScalingFactor(sf); + tjd.setCroppingRegion(cr); + if (isCropped(cr)) { + scaledw = cr.width != 0 ? cr.width : scaledw - cr.x; + scaledh = cr.height != 0 ? cr.height : scaledh - cr.y; + } + pitch = scaledw * ps; if (dstBuf == null) { if ((long)pitch * (long)scaledh > (long)Integer.MAX_VALUE) throw new Exception("Image is too large"); - dstBuf = new byte[pitch * scaledh]; + if (precision == 8) + dstBuf = new byte[pitch * scaledh]; + else + dstBuf = new short[pitch * scaledh]; } /* Set the destination buffer to gray so we know whether the decompressor attempted to write to it */ - Arrays.fill(dstBuf, (byte)127); + if (precision == 8) + Arrays.fill((byte[])dstBuf, (byte)127); + else if (precision == 12) + Arrays.fill((short[])dstBuf, (short)2047); + else + Arrays.fill((short[])dstBuf, (short)32767); if (doYUV) { int width = doTile ? tilew : scaledw; @@ -214,24 +236,29 @@ static void decomp(byte[] srcBuf, byte[][] jpegBuf, int[] jpegSize, int height = doTile ? Math.min(tileh, h - y) : scaledh; try { - tjd.setSourceImage(jpegBuf[tile], jpegSize[tile]); + tjd.setSourceImage(jpegBufs[tile], jpegSizes[tile]); } catch (TJException e) { handleTJException(e); } if (doYUV) { yuvImage.setBuf(yuvImage.getBuf(), width, yuvAlign, height, subsamp); try { - tjd.decompressToYUV(yuvImage, flags); + tjd.decompressToYUV(yuvImage); } catch (TJException e) { handleTJException(e); } double startDecode = getTime(); tjd.setSourceImage(yuvImage); try { - tjd.decompress(dstBuf, x, y, width, pitch, height, pf, flags); + tjd.decompress8((byte[])dstBuf, x, y, pitch, pf); } catch (TJException e) { handleTJException(e); } if (iter >= 0) elapsedDecode += getTime() - startDecode; } else { try { - tjd.decompress(dstBuf, x, y, width, pitch, height, pf, flags); + if (precision == 8) + tjd.decompress8((byte[])dstBuf, x, y, pitch, pf); + else if (precision == 12) + tjd.decompress12((short[])dstBuf, x, y, pitch, pf); + else + tjd.decompress16((short[])dstBuf, x, y, pitch, pf); } catch (TJException e) { handleTJException(e); } } } @@ -249,10 +276,9 @@ static void decomp(byte[] srcBuf, byte[][] jpegBuf, int[] jpegSize, if (doYUV) elapsed -= elapsedDecode; - tjd = null; - for (i = 0; i < jpegBuf.length; i++) - jpegBuf[i] = null; - jpegBuf = null; jpegSize = null; + for (i = 0; i < jpegBufs.length; i++) + jpegBufs[i] = null; + jpegBufs = null; jpegSizes = null; System.gc(); if (quiet != 0) { @@ -290,52 +316,22 @@ else if (tilew != w || tileh != h) else sizeStr = new String("full"); if (decompOnly) - tempStr = new String(fileName + "_" + sizeStr + ".bmp"); + tempStr = new String(fileName + "_" + sizeStr + (bmp ? ".bmp" : ".ppm")); else - tempStr = new String(fileName + "_" + SUBNAME[subsamp] + qualStr + - "_" + sizeStr + ".bmp"); - - saveImage(tempStr, dstBuf, scaledw, scaledh, pf); - int ndx = tempStr.lastIndexOf('.'); - tempStr = new String(tempStr.substring(0, ndx) + "-err.bmp"); - if (srcBuf != null && sf.getNum() == 1 && sf.getDenom() == 1) { - if (quiet == 0) - System.out.println("Compression error written to " + tempStr + "."); - if (subsamp == TJ.SAMP_GRAY) { - for (int y = 0, index = 0; y < h; y++, index += pitch) { - for (int x = 0, index2 = index; x < w; x++, index2 += ps) { - int rindex = index2 + TJ.getRedOffset(pf); - int gindex = index2 + TJ.getGreenOffset(pf); - int bindex = index2 + TJ.getBlueOffset(pf); - int lum = (int)((double)(srcBuf[rindex] & 0xff) * 0.299 + - (double)(srcBuf[gindex] & 0xff) * 0.587 + - (double)(srcBuf[bindex] & 0xff) * 0.114 + 0.5); - - if (lum > 255) lum = 255; - if (lum < 0) lum = 0; - dstBuf[rindex] = (byte)Math.abs((dstBuf[rindex] & 0xff) - lum); - dstBuf[gindex] = (byte)Math.abs((dstBuf[gindex] & 0xff) - lum); - dstBuf[bindex] = (byte)Math.abs((dstBuf[bindex] & 0xff) - lum); - } - } - } else { - for (int y = 0; y < h; y++) - for (int x = 0; x < w * ps; x++) - dstBuf[pitch * y + x] = - (byte)Math.abs((dstBuf[pitch * y + x] & 0xff) - - (srcBuf[pitch * y + x] & 0xff)); - } - saveImage(tempStr, dstBuf, w, h, pf); - } + tempStr = new String(fileName + "_" + + (lossless ? "LOSSLS" : SUBNAME[subsamp]) + qualStr + + "_" + sizeStr + (bmp ? ".bmp" : ".ppm")); + + tjd.saveImage(precision, tempStr, dstBuf, scaledw, 0, scaledh, pf); } - static void fullTest(byte[] srcBuf, int w, int h, int subsamp, int jpegQual, - String fileName) throws Exception { - TJCompressor tjc; - byte[] tmpBuf; - byte[][] jpegBuf; - int[] jpegSize; + static void fullTest(TJCompressor tjc, Object srcBuf, int w, int h, + int subsamp, int jpegQual, String fileName) + throws Exception { + Object tmpBuf; + byte[][] jpegBufs; + int[] jpegSizes; double start, elapsed, elapsedEncode; int totalJpegSize = 0, tilew, tileh, i, iter; int ps = TJ.getPixelSize(pf); @@ -345,15 +341,29 @@ static void fullTest(byte[] srcBuf, int w, int h, int subsamp, int jpegQual, if ((long)pitch * (long)h > (long)Integer.MAX_VALUE) throw new Exception("Image is too large"); - tmpBuf = new byte[pitch * h]; + if (precision == 8) + tmpBuf = new byte[pitch * h]; + else + tmpBuf = new short[pitch * h]; if (quiet == 0) - System.out.format(">>>>> %s (%s) <--> JPEG %s Q%d <<<<<\n", pfStr, - (flags & TJ.FLAG_BOTTOMUP) != 0 ? - "Bottom-up" : "Top-down", - SUBNAME_LONG[subsamp], jpegQual); - - tjc = new TJCompressor(); + System.out.format(">>>>> %s (%s) <--> %d-bit JPEG (%s %s%d) <<<<<\n", + pfStr, bottomUp ? "Bottom-up" : "Top-down", precision, + lossless ? "Lossless" : SUBNAME_LONG[subsamp], + lossless ? "PSV" : "Q", jpegQual); + + tjc.set(TJ.PARAM_SUBSAMP, subsamp); + tjc.set(TJ.PARAM_FASTDCT, fastDCT ? 1 : 0); + tjc.set(TJ.PARAM_OPTIMIZE, optimize ? 1 : 0); + tjc.set(TJ.PARAM_PROGRESSIVE, progressive ? 1 : 0); + tjc.set(TJ.PARAM_ARITHMETIC, arithmetic ? 1 : 0); + tjc.set(TJ.PARAM_LOSSLESS, lossless ? 1 : 0); + if (lossless) + tjc.set(TJ.PARAM_LOSSLESSPSV, jpegQual); + else + tjc.set(TJ.PARAM_QUALITY, jpegQual); + tjc.set(TJ.PARAM_RESTARTBLOCKS, restartIntervalBlocks); + tjc.set(TJ.PARAM_RESTARTROWS, restartIntervalRows); for (tilew = doTile ? 8 : w, tileh = doTile ? 8 : h; ; tilew *= 2, tileh *= 2) { @@ -364,18 +374,25 @@ static void fullTest(byte[] srcBuf, int w, int h, int subsamp, int jpegQual, ntilesw = (w + tilew - 1) / tilew; ntilesh = (h + tileh - 1) / tileh; - jpegBuf = new byte[ntilesw * ntilesh][TJ.bufSize(tilew, tileh, subsamp)]; - jpegSize = new int[ntilesw * ntilesh]; + jpegBufs = + new byte[ntilesw * ntilesh][TJ.bufSize(tilew, tileh, subsamp)]; + jpegSizes = new int[ntilesw * ntilesh]; /* Compression test */ if (quiet == 1) - System.out.format("%-4s (%s) %-5s %-3d ", pfStr, - (flags & TJ.FLAG_BOTTOMUP) != 0 ? "BU" : "TD", - SUBNAME_LONG[subsamp], jpegQual); - for (i = 0; i < h; i++) - System.arraycopy(srcBuf, w * ps * i, tmpBuf, pitch * i, w * ps); - tjc.setJPEGQuality(jpegQual); - tjc.setSubsamp(subsamp); + System.out.format("%-4s(%s) %-2d/%-6s %-3d ", pfStr, + bottomUp ? "BU" : "TD", precision, + lossless ? "LOSSLS" : SUBNAME_LONG[subsamp], + jpegQual); + if (precision == 8) { + for (i = 0; i < h; i++) + System.arraycopy((byte[])srcBuf, w * ps * i, (byte[])tmpBuf, + pitch * i, w * ps); + } else { + for (i = 0; i < h; i++) + System.arraycopy((short[])srcBuf, w * ps * i, (short[])tmpBuf, + pitch * i, w * ps); + } if (doYUV) { yuvImage = new YUVImage(tilew, yuvAlign, tileh, subsamp); @@ -395,20 +412,28 @@ static void fullTest(byte[] srcBuf, int w, int h, int subsamp, int jpegQual, int width = Math.min(tilew, w - x); int height = Math.min(tileh, h - y); - tjc.setSourceImage(srcBuf, x, y, width, pitch, height, pf); + if (precision == 8) + tjc.setSourceImage((byte[])srcBuf, x, y, width, pitch, height, + pf); + else if (precision == 12) + tjc.setSourceImage12((short[])srcBuf, x, y, width, pitch, height, + pf); + else + tjc.setSourceImage16((short[])srcBuf, x, y, width, pitch, height, + pf); if (doYUV) { double startEncode = getTime(); yuvImage.setBuf(yuvImage.getBuf(), width, yuvAlign, height, subsamp); - tjc.encodeYUV(yuvImage, flags); + tjc.encodeYUV(yuvImage); if (iter >= 0) elapsedEncode += getTime() - startEncode; tjc.setSourceImage(yuvImage); } - tjc.compress(jpegBuf[tile], flags); - jpegSize[tile] = tjc.getCompressedSize(); - totalJpegSize += jpegSize[tile]; + tjc.compress(jpegBufs[tile]); + jpegSizes[tile] = tjc.getCompressedSize(); + totalJpegSize += jpegSizes[tile]; } } elapsed += getTime() - start; @@ -471,11 +496,12 @@ static void fullTest(byte[] srcBuf, int w, int h, int subsamp, int jpegQual, (double)iter / elapsed); } if (tilew == w && tileh == h && write) { - String tempStr = fileName + "_" + SUBNAME[subsamp] + "_" + "Q" + - jpegQual + ".jpg"; + String tempStr = fileName + "_" + + (lossless ? "LOSSLS" : SUBNAME[subsamp]) + "_" + + (lossless ? "PSV" : "Q") + jpegQual + ".jpg"; FileOutputStream fos = new FileOutputStream(tempStr); - fos.write(jpegBuf[0], 0, jpegSize[0]); + fos.write(jpegBufs[0], 0, jpegSizes[0]); fos.close(); if (quiet == 0) System.out.println("Reference image written to " + tempStr); @@ -483,8 +509,8 @@ static void fullTest(byte[] srcBuf, int w, int h, int subsamp, int jpegQual, /* Decompression test */ if (!compOnly) - decomp(srcBuf, jpegBuf, jpegSize, tmpBuf, w, h, subsamp, jpegQual, - fileName, tilew, tileh); + decomp(jpegBufs, jpegSizes, tmpBuf, w, h, subsamp, jpegQual, fileName, + tilew, tileh); else if (quiet == 1) System.out.println("N/A"); @@ -495,16 +521,16 @@ else if (quiet == 1) static void decompTest(String fileName) throws Exception { TJTransformer tjt; - byte[][] jpegBuf = null; + byte[][] jpegBufs = null; byte[] srcBuf; - int[] jpegSize = null; + int[] jpegSizes = null; int totalJpegSize; double start, elapsed; int ps = TJ.getPixelSize(pf), tile, x, y, iter; // Original image int w = 0, h = 0, ntilesw = 1, ntilesh = 1, subsamp = -1, cs = -1; // Transformed image - int tw, th, ttilew, ttileh, tntilesw, tntilesh, tsubsamp, jpegFlags; + int tw, th, ttilew, ttileh, tntilesw, tntilesh, tsubsamp; FileInputStream fis = new FileInputStream(fileName); if (fis.getChannel().size() > (long)Integer.MAX_VALUE) @@ -519,36 +545,53 @@ static void decompTest(String fileName) throws Exception { fileName = new String(fileName.substring(0, index)); tjt = new TJTransformer(); + tjt.set(TJ.PARAM_STOPONWARNING, stopOnWarning ? 1 : 0); + tjt.set(TJ.PARAM_BOTTOMUP, bottomUp ? 1 : 0); + tjt.set(TJ.PARAM_FASTUPSAMPLE, fastUpsample ? 1 : 0); + tjt.set(TJ.PARAM_FASTDCT, fastDCT ? 1 : 0); + tjt.set(TJ.PARAM_SCANLIMIT, limitScans ? 500 : 0); try { tjt.setSourceImage(srcBuf, srcSize); } catch (TJException e) { handleTJException(e); } w = tjt.getWidth(); h = tjt.getHeight(); - subsamp = tjt.getSubsamp(); - cs = tjt.getColorspace(); - jpegFlags = tjt.getFlags(); + subsamp = tjt.get(TJ.PARAM_SUBSAMP); + precision = tjt.get(TJ.PARAM_PRECISION); + cs = tjt.get(TJ.PARAM_COLORSPACE); + if (tjt.get(TJ.PARAM_PROGRESSIVE) == 1) + System.out.println("JPEG image uses progressive entropy coding\n"); + if (tjt.get(TJ.PARAM_ARITHMETIC) == 1) + System.out.println("JPEG image uses arithmetic entropy coding\n"); + tjt.set(TJ.PARAM_PROGRESSIVE, progressive ? 1 : 0); + tjt.set(TJ.PARAM_ARITHMETIC, arithmetic ? 1 : 0); + + if (cs == TJ.CS_YCCK || cs == TJ.CS_CMYK) { + pf = TJ.PF_CMYK; ps = TJ.getPixelSize(pf); + } - if ((jpegFlags & TJ.FLAG_LOSSLESS) != 0) - sf = new TJScalingFactor(1, 1); + if (tjt.get(TJ.PARAM_LOSSLESS) != 0) + sf = TJ.UNSCALED; + + tjt.setScalingFactor(sf); + tjt.setCroppingRegion(cr); if (quiet == 1) { System.out.println("All performance values in Mpixels/sec\n"); - System.out.format("Pixel JPEG JPEG %s %s Xform Comp Decomp ", + System.out.format("Pixel JPEG %s %s Xform Comp Decomp ", (doTile ? "Tile " : "Image"), (doTile ? "Tile " : "Image")); if (doYUV) System.out.print("Decode"); System.out.print("\n"); - System.out.print("Format CS Subsamp Width Height Perf Ratio Perf "); + System.out.print("Format Format Width Height Perf Ratio Perf "); if (doYUV) System.out.print("Perf"); System.out.println("\n"); } else if (quiet == 0) - System.out.format(">>>>> JPEG %s --> %s (%s) <<<<<\n", - formatName(subsamp, cs), PIXFORMATSTR[pf], - (flags & TJ.FLAG_BOTTOMUP) != 0 ? - "Bottom-up" : "Top-down"); + System.out.format(">>>>> %d-bit JPEG (%s) --> %s (%s) <<<<<\n", + precision, formatName(subsamp, cs), PIXFORMATSTR[pf], + bottomUp ? "Bottom-up" : "Top-down"); for (int tilew = doTile ? 16 : w, tileh = doTile ? 16 : h; ; tilew *= 2, tileh *= 2) { @@ -563,19 +606,20 @@ static void decompTest(String fileName) throws Exception { if (quiet == 0) { System.out.format("\n%s size: %d x %d", (doTile ? "Tile" : "Image"), ttilew, ttileh); - if (sf.getNum() != 1 || sf.getDenom() != 1) - System.out.format(" --> %d x %d", sf.getScaled(tw), - sf.getScaled(th)); + if (sf.getNum() != 1 || sf.getDenom() != 1 || isCropped(cr)) + System.out.format(" --> %d x %d", getCroppedWidth(tw), + getCroppedHeight(th)); System.out.println(""); } else if (quiet == 1) { - System.out.format("%-4s (%s) %-5s %-5s ", PIXFORMATSTR[pf], - (flags & TJ.FLAG_BOTTOMUP) != 0 ? "BU" : "TD", - CSNAME[cs], SUBNAME_LONG[subsamp]); - System.out.format("%-5d %-5d ", tilew, tileh); + System.out.format("%-4s(%s) %-14s ", PIXFORMATSTR[pf], + bottomUp ? "BU" : "TD", formatName(subsamp, cs)); + System.out.format("%-5d %-5d ", getCroppedWidth(tilew), + getCroppedHeight(tileh)); } tsubsamp = subsamp; - if (doTile || xformOp != TJTransform.OP_NONE || xformOpt != 0) { + if (doTile || xformOp != TJTransform.OP_NONE || xformOpt != 0 || + customFilter != null) { if (xformOp == TJTransform.OP_TRANSPOSE || xformOp == TJTransform.OP_TRANSVERSE || xformOp == TJTransform.OP_ROT90 || @@ -583,6 +627,9 @@ static void decompTest(String fileName) throws Exception { tw = h; th = w; ttilew = tileh; ttileh = tilew; } + if (xformOp != TJTransform.OP_NONE && + xformOp != TJTransform.OP_TRANSPOSE && subsamp == TJ.SAMP_UNKNOWN) + throw new Exception("Could not determine subsampling level of JPEG image"); if ((xformOpt & TJTransform.OPT_GRAY) != 0) tsubsamp = TJ.SAMP_GRAY; if (xformOp == TJTransform.OP_HFLIP || @@ -611,7 +658,7 @@ else if (tsubsamp == TJ.SAMP_440) } TJTransform[] t = new TJTransform[tntilesw * tntilesh]; - jpegBuf = + jpegBufs = new byte[tntilesw * tntilesh][TJ.bufSize(ttilew, ttileh, subsamp)]; for (y = 0, tile = 0; y < th; y += ttileh) { @@ -623,9 +670,10 @@ else if (tsubsamp == TJ.SAMP_440) t[tile].y = y; t[tile].op = xformOp; t[tile].options = xformOpt | TJTransform.OPT_TRIM; + t[tile].cf = customFilter; if ((t[tile].options & TJTransform.OPT_NOOUTPUT) != 0 && - jpegBuf[tile] != null) - jpegBuf[tile] = null; + jpegBufs[tile] != null) + jpegBufs[tile] = null; } } @@ -634,9 +682,9 @@ else if (tsubsamp == TJ.SAMP_440) while (true) { start = getTime(); try { - tjt.transform(jpegBuf, t, flags); + tjt.transform(jpegBufs, t); } catch (TJException e) { handleTJException(e); } - jpegSize = tjt.getTransformedSizes(); + jpegSizes = tjt.getTransformedSizes(); elapsed += getTime() - start; if (iter >= 0) { iter++; @@ -650,7 +698,7 @@ else if (tsubsamp == TJ.SAMP_440) t = null; for (tile = 0, totalJpegSize = 0; tile < tntilesw * tntilesh; tile++) - totalJpegSize += jpegSize[tile]; + totalJpegSize += jpegSizes[tile]; if (quiet != 0) { System.out.format("%-6s%s%-6s%s", @@ -674,10 +722,10 @@ else if (tsubsamp == TJ.SAMP_440) } else { if (quiet == 1) System.out.print("N/A N/A "); - jpegBuf = new byte[1][TJ.bufSize(ttilew, ttileh, subsamp)]; - jpegSize = new int[1]; - jpegBuf[0] = srcBuf; - jpegSize[0] = srcSize; + jpegBufs = new byte[1][TJ.bufSize(ttilew, ttileh, subsamp)]; + jpegSizes = new int[1]; + jpegBufs[0] = srcBuf; + jpegSizes[0] = srcSize; } if (w == tilew) @@ -685,13 +733,13 @@ else if (tsubsamp == TJ.SAMP_440) if (h == tileh) ttileh = th; if ((xformOpt & TJTransform.OPT_NOOUTPUT) == 0) - decomp(null, jpegBuf, jpegSize, null, tw, th, tsubsamp, 0, - fileName, ttilew, ttileh); + decomp(jpegBufs, jpegSizes, null, tw, th, tsubsamp, 0, fileName, + ttilew, ttileh); else if (quiet == 1) System.out.println("N/A"); - jpegBuf = null; - jpegSize = null; + jpegBufs = null; + jpegSizes = null; if (tilew == w && tileh == h) break; } @@ -705,36 +753,61 @@ static void usage() throws Exception { String className = new TJBench().getClass().getName(); System.out.println("\nUSAGE: java " + className); - System.out.println(" [options]\n"); + System.out.println(" [options]\n"); System.out.println(" java " + className); - System.out.println(" [options]\n"); - System.out.println("Options:\n"); + System.out.println(" [options]"); + + System.out.println("\nGENERAL OPTIONS"); + System.out.println("---------------"); + System.out.println("-benchtime T = Run each benchmark for at least T seconds [default = 5.0]"); + System.out.println("-bmp = Use Windows Bitmap format for output images [default = PPM]"); + System.out.println(" ** 8-bit data precision only **"); System.out.println("-bottomup = Use bottom-up row order for packed-pixel source/destination buffers"); - System.out.println("-tile = Compress/transform the input image into separate JPEG tiles of varying"); - System.out.println(" sizes (useful for measuring JPEG overhead)"); + System.out.println("-componly = Stop after running compression tests. Do not test decompression."); + System.out.println("-lossless = Generate lossless JPEG images when compressing (implies"); + System.out.println(" -subsamp 444). PSV is the predictor selection value (1-7)."); + System.out.println("-nowrite = Do not write reference or output images (improves consistency of"); + System.out.println(" benchmark results)"); System.out.println("-rgb, -bgr, -rgbx, -bgrx, -xbgr, -xrgb ="); System.out.println(" Use the specified pixel format for packed-pixel source/destination buffers"); System.out.println(" [default = BGR]"); - System.out.println("-fastupsample = Use the fastest chrominance upsampling algorithm available"); - System.out.println("-fastdct = Use the fastest DCT/IDCT algorithm available"); - System.out.println("-accuratedct = Use the most accurate DCT/IDCT algorithm available"); - System.out.println("-progressive = Use progressive entropy coding in JPEG images generated by"); - System.out.println(" compression and transform operations (can be combined with -arithmetic)"); + System.out.println("-cmyk = Indirectly test YCCK JPEG compression/decompression"); + System.out.println(" (use the CMYK pixel format for packed-pixel source/destination buffers)"); + System.out.println("-precision N = Use N-bit data precision when compressing [N is 8, 12, or 16;"); + System.out.println(" default = 8; if N is 16, then -lossless must also be specified]"); + System.out.println("-quiet = Output results in tabular rather than verbose format"); + System.out.println("-restart N = When compressing, add a restart marker every N MCU rows (lossy) or"); + System.out.println(" N sample rows (lossless) [default = 0 (no restart markers)]. Append 'B'"); + System.out.println(" to specify the restart marker interval in MCU blocks (lossy) or samples"); + System.out.println(" (lossless)."); + System.out.println("-stoponwarning = Immediately discontinue the current"); + System.out.println(" compression/decompression/transform operation if a warning (non-fatal"); + System.out.println(" error) occurs"); + System.out.println("-tile = Compress/transform the input image into separate JPEG tiles of varying"); + System.out.println(" sizes (useful for measuring JPEG overhead)"); + System.out.println("-warmup T = Run each benchmark for T seconds [default = 1.0] prior to starting"); + System.out.println(" the timer, in order to prime the caches and thus improve the consistency"); + System.out.println(" of the benchmark results"); + + System.out.println("\nLOSSY JPEG OPTIONS"); + System.out.println("------------------"); System.out.println("-arithmetic = Use arithmetic entropy coding in JPEG images generated by"); System.out.println(" compression and transform operations (can be combined with -progressive)"); - System.out.println("-lossless = Generate lossless JPEG images when compressing (implies"); - System.out.println(" -subsamp 444). When generating lossless JPEG images, Quality is"); - System.out.println(" psv * 10 + Pt, where psv is the predictor selection value (1-7) and Pt is"); - System.out.println(" the point transform (0-7). A point transform value of 0 is necessary in"); - System.out.println(" order to create a fully lossless JPEG image."); - System.out.println("-subsamp = When compressing, use the specified level of chrominance"); - System.out.println(" subsampling ( = 444, 422, 440, 420, 411, or GRAY) [default = test"); - System.out.println(" Grayscale, 4:2:0, 4:2:2, and 4:4:4 in sequence]"); - System.out.println("-quiet = Output results in tabular rather than verbose format"); - System.out.println("-yuv = Compress from/decompress to intermediate planar YUV images"); - System.out.println("-yuvpad

    = The number of bytes by which each row in each plane of an"); - System.out.println(" intermediate YUV image is evenly divisible (must be a power of 2)"); - System.out.println(" [default = 1]"); + System.out.println(" ** 8-bit data precision only **"); + System.out.println("-crop WxH+X+Y = Decompress only the specified region of the JPEG image, where W"); + System.out.println(" and H are the width and height of the region (0 = maximum possible width"); + System.out.println(" or height) and X and Y are the left and upper boundary of the region, all"); + System.out.println(" specified relative to the scaled image dimensions. X must be divible by"); + System.out.println(" the scaled MCU width."); + System.out.println("-fastdct = Use the fastest DCT/IDCT algorithm available"); + System.out.println("-fastupsample = Use the fastest chrominance upsampling algorithm available"); + System.out.println("-optimize = Use optimized baseline entropy coding in JPEG images generated by"); + System.out.println(" compession and transform operations"); + System.out.println("-progressive = Use progressive entropy coding in JPEG images generated by"); + System.out.println(" compression and transform operations (implies -optimize; can be combined"); + System.out.println(" with -arithmetic)"); + System.out.println("-limitscans = Refuse to decompress or transform progressive JPEG images that"); + System.out.println(" have an unreasonably large number of scans"); System.out.println("-scale M/N = When decompressing, scale the width/height of the JPEG image by a"); System.out.print(" factor of M/N (M/N = "); for (i = 0; i < nsf; i++) { @@ -752,6 +825,9 @@ else if (nsf > 2) { System.out.print("\n "); } System.out.println(")"); + System.out.println("-subsamp S = When compressing, use the specified level of chrominance"); + System.out.println(" subsampling (S = 444, 422, 440, 420, 411, or GRAY) [default = test"); + System.out.println(" Grayscale, 4:2:0, 4:2:2, and 4:4:4 in sequence]"); System.out.println("-hflip, -vflip, -transpose, -transverse, -rot90, -rot180, -rot270 ="); System.out.println(" Perform the specified lossless transform operation on the input image"); System.out.println(" prior to decompression (these operations are mutually exclusive)"); @@ -759,29 +835,24 @@ else if (nsf > 2) { System.out.println(" decompression (can be combined with the other transform operations above)"); System.out.println("-copynone = Do not copy any extra markers (including EXIF and ICC profile data)"); System.out.println(" when transforming the input image"); - System.out.println("-benchtime = Run each benchmark for at least seconds [default = 5.0]"); - System.out.println("-warmup = Run each benchmark for seconds [default = 1.0] prior to"); - System.out.println(" starting the timer, in order to prime the caches and thus improve the"); - System.out.println(" consistency of the benchmark results"); - System.out.println("-componly = Stop after running compression tests. Do not test decompression."); - System.out.println("-nowrite = Do not write reference or output images (improves consistency of"); - System.out.println(" benchmark results)"); - System.out.println("-limitscans = Refuse to decompress or transform progressive JPEG images that"); - System.out.println(" have an unreasonably large number of scans"); - System.out.println("-stoponwarning = Immediately discontinue the current"); - System.out.println(" compression/decompression/transform operation if a warning (non-fatal"); - System.out.println(" error) occurs\n"); - System.out.println("NOTE: If the quality is specified as a range (e.g. 90-100), a separate"); - System.out.println("test will be performed for all quality values in the range.\n"); + System.out.println("-yuv = Compress from/decompress to intermediate planar YUV images"); + System.out.println(" ** 8-bit data precision only **"); + System.out.println("-yuvpad N = The number of bytes by which each row in each plane of an"); + System.out.println(" intermediate YUV image is evenly divisible (N must be a power of 2)"); + System.out.println(" [default = 1]"); + + System.out.println("\nNOTE: If the quality/PSV is specified as a range (e.g. 90-100 or 1-4), a"); + System.out.println("separate test will be performed for all values in the range.\n"); System.exit(1); } public static void main(String[] argv) { - byte[] srcBuf = null; + Object srcBuf = null; int w = 0, h = 0, minQual = -1, maxQual = -1; int minArg = 1, retval = 0; int subsamp = -1; + TJCompressor tjc = null; try { @@ -791,6 +862,8 @@ public static void main(String[] argv) { String tempStr = argv[0].toLowerCase(); if (tempStr.endsWith(".jpg") || tempStr.endsWith(".jpeg")) decompOnly = true; + if (tempStr.endsWith(".bmp")) + bmp = true; System.out.println(""); @@ -802,14 +875,12 @@ public static void main(String[] argv) { try { minQual = Integer.parseInt(quals[0]); } catch (NumberFormatException e) {} - if (minQual < 1 || minQual > 100) - throw new Exception("Quality must be between 1 and 100."); if (quals.length > 1) { try { maxQual = Integer.parseInt(quals[1]); } catch (NumberFormatException e) {} } - if (maxQual < 1 || maxQual > 100 || maxQual < minQual) + if (maxQual < minQual) maxQual = minQual; } @@ -817,26 +888,37 @@ public static void main(String[] argv) { for (int i = minArg; i < argv.length; i++) { if (argv[i].equalsIgnoreCase("-tile")) { doTile = true; xformOpt |= TJTransform.OPT_CROP; + } else if (argv[i].equalsIgnoreCase("-precision") && + i < argv.length - 1) { + int temp = 0; + + try { + temp = Integer.parseInt(argv[++i]); + } catch (NumberFormatException e) {} + if (temp == 8 || temp == 12 || temp == 16) + precision = temp; + else + usage(); } else if (argv[i].equalsIgnoreCase("-fastupsample")) { System.out.println("Using fastest upsampling algorithm\n"); - flags |= TJ.FLAG_FASTUPSAMPLE; + fastUpsample = true; } else if (argv[i].equalsIgnoreCase("-fastdct")) { System.out.println("Using fastest DCT/IDCT algorithm\n"); - flags |= TJ.FLAG_FASTDCT; - } else if (argv[i].equalsIgnoreCase("-accuratedct")) { - System.out.println("Using most accurate DCT/IDCT algorithm\n"); - flags |= TJ.FLAG_ACCURATEDCT; + fastDCT = true; + } else if (argv[i].equalsIgnoreCase("-optimize")) { + System.out.println("Using optimized baseline entropy coding\n"); + optimize = true; + xformOpt |= TJTransform.OPT_OPTIMIZE; } else if (argv[i].equalsIgnoreCase("-progressive")) { System.out.println("Using progressive entropy coding\n"); - flags |= TJ.FLAG_PROGRESSIVE; + progressive = true; xformOpt |= TJTransform.OPT_PROGRESSIVE; } else if (argv[i].equalsIgnoreCase("-arithmetic")) { System.out.println("Using arithmetic entropy coding\n"); - flags |= TJ.FLAG_ARITHMETIC; + arithmetic = true; xformOpt |= TJTransform.OPT_ARITHMETIC; } else if (argv[i].equalsIgnoreCase("-lossless")) { - System.out.println("Using lossless JPEG\n\n"); - flags |= TJ.FLAG_LOSSLESS; + lossless = true; subsamp = TJ.SAMP_444; } else if (argv[i].equalsIgnoreCase("-rgb")) pf = TJ.PF_RGB; @@ -850,8 +932,10 @@ else if (argv[i].equalsIgnoreCase("-xbgr")) pf = TJ.PF_XBGR; else if (argv[i].equalsIgnoreCase("-xrgb")) pf = TJ.PF_XRGB; + else if (argv[i].equalsIgnoreCase("-cmyk")) + pf = TJ.PF_CMYK; else if (argv[i].equalsIgnoreCase("-bottomup")) - flags |= TJ.FLAG_BOTTOMUP; + bottomUp = true; else if (argv[i].equalsIgnoreCase("-quiet")) quiet = 1; else if (argv[i].equalsIgnoreCase("-qq")) @@ -880,6 +964,21 @@ else if (argv[i].equalsIgnoreCase("-scale") && i < argv.length - 1) { if (!match) usage(); } else usage(); + } else if (argv[i].equalsIgnoreCase("-crop") && + i < argv.length - 1) { + int temp1 = -1, temp2 = -1, temp3 = -1, temp4 = -1; + Scanner scanner = new Scanner(argv[++i]).useDelimiter("x|\\+"); + + try { + temp1 = scanner.nextInt(); + temp2 = scanner.nextInt(); + temp3 = scanner.nextInt(); + temp4 = scanner.nextInt(); + } catch (Exception e) {} + + if (temp1 < 0 || temp2 < 0 || temp3 < 0 || temp4 < 0) + usage(); + cr.width = temp1; cr.height = temp2; cr.x = temp3; cr.y = temp4; } else if (argv[i].equalsIgnoreCase("-hflip")) xformOp = TJTransform.OP_HFLIP; else if (argv[i].equalsIgnoreCase("-vflip")) @@ -896,6 +995,8 @@ else if (argv[i].equalsIgnoreCase("-rot270")) xformOp = TJTransform.OP_ROT270; else if (argv[i].equalsIgnoreCase("-grayscale")) xformOpt |= TJTransform.OPT_GRAY; + else if (argv[i].equalsIgnoreCase("-custom")) + customFilter = new DummyDCTFilter(); else if (argv[i].equalsIgnoreCase("-nooutput")) xformOpt |= TJTransform.OPT_NOOUTPUT; else if (argv[i].equalsIgnoreCase("-copynone")) @@ -923,7 +1024,9 @@ else if (argv[i].equalsIgnoreCase("-benchtime") && System.out.format("Warmup time = %.1f seconds\n\n", warmup); } else usage(); - } else if (argv[i].equalsIgnoreCase("-yuv")) { + } else if (argv[i].equalsIgnoreCase("-bmp")) + bmp = true; + else if (argv[i].equalsIgnoreCase("-yuv")) { System.out.println("Testing planar YUV encoding/decoding\n"); doYUV = true; } else if (argv[i].equalsIgnoreCase("-yuvpad") && @@ -959,15 +1062,37 @@ else if (argv[i].equals("411")) else if (argv[i].equalsIgnoreCase("-nowrite")) write = false; else if (argv[i].equalsIgnoreCase("-limitscans")) - flags |= TJ.FLAG_LIMITSCANS; - else if (argv[i].equalsIgnoreCase("-stoponwarning")) - flags |= TJ.FLAG_STOPONWARNING; + limitScans = true; + else if (argv[i].equalsIgnoreCase("-restart") && + i < argv.length - 1) { + int temp = -1; + String arg = argv[++i]; + Scanner scanner = new Scanner(arg).useDelimiter("b|B"); + + try { + temp = scanner.nextInt(); + } catch (Exception e) {} + + if (temp < 0 || temp > 65535 || scanner.hasNext()) + usage(); + if (arg.endsWith("B") || arg.endsWith("b")) + restartIntervalBlocks = temp; + else + restartIntervalRows = temp; + } else if (argv[i].equalsIgnoreCase("-stoponwarning")) + stopOnWarning = true; else usage(); } } - if (sf == null) - sf = new TJScalingFactor(1, 1); + if (precision == 16 && !lossless) + throw new Exception("-lossless must be specified along with -precision 16"); + if (precision != 8 && arithmetic) + throw new Exception("-arithmetic requires 8-bit data precision"); + if (precision != 8 && doYUV) + throw new Exception("-yuv requires 8-bit data precision"); + if (lossless && doYUV) + throw new Exception("ERROR: -lossless and -yuv are incompatible"); if ((sf.getNum() != 1 || sf.getDenom() != 1) && doTile) { System.out.println("Disabling tiled compression/decompression tests, because those tests do not"); @@ -976,11 +1101,31 @@ else if (argv[i].equalsIgnoreCase("-stoponwarning")) xformOpt &= (~TJTransform.OPT_CROP); } + if (isCropped(cr)) { + if (!decompOnly) + throw new Exception("ERROR: Partial image decompression can only be enabled for JPEG input images"); + if (doTile) { + System.out.println("Disabling tiled compression/decompression tests, because those tests do not"); + System.out.println("work when partial image decompression is enabled.\n"); + doTile = false; + xformOpt &= (~TJTransform.OPT_CROP); + } + if (doYUV) + throw new Exception("ERROR: -crop and -yuv are incompatible"); + } + if (!decompOnly) { - int[] width = new int[1], height = new int[1]; + int[] width = new int[1], height = new int[1], + pixelFormat = new int[1]; + + tjc = new TJCompressor(); + tjc.set(TJ.PARAM_STOPONWARNING, stopOnWarning ? 1 : 0); + tjc.set(TJ.PARAM_BOTTOMUP, bottomUp ? 1 : 0); - srcBuf = loadImage(argv[0], width, height, pf); - w = width[0]; h = height[0]; + pixelFormat[0] = pf; + srcBuf = tjc.loadImage(precision, argv[0], width, 1, height, + pixelFormat); + w = width[0]; h = height[0]; pf = pixelFormat[0]; int index = -1; if ((index = argv[0].lastIndexOf('.')) >= 0) argv[0] = argv[0].substring(0, index); @@ -988,7 +1133,7 @@ else if (argv[i].equalsIgnoreCase("-stoponwarning")) if (quiet == 1 && !decompOnly) { System.out.println("All performance values in Mpixels/sec\n"); - System.out.format("Pixel JPEG JPEG %s %s ", + System.out.format("Pixel JPEG JPEG %s %s ", (doTile ? "Tile " : "Image"), (doTile ? "Tile " : "Image")); if (doYUV) @@ -997,7 +1142,8 @@ else if (argv[i].equalsIgnoreCase("-stoponwarning")) if (doYUV) System.out.print("Decode"); System.out.print("\n"); - System.out.print("Format Subsamp Qual Width Height "); + System.out.format("Format Format %s Width Height ", + lossless ? "PSV " : "Qual"); if (doYUV) System.out.print("Perf "); System.out.print("Perf Ratio Perf "); @@ -1013,25 +1159,34 @@ else if (argv[i].equalsIgnoreCase("-stoponwarning")) } System.gc(); + if (lossless) { + if (minQual < 1 || minQual > 7 || maxQual < 1 || maxQual > 7) + throw new Exception("PSV must be between 1 and 7."); + } else { + if (minQual < 1 || minQual > 100 || maxQual < 1 || maxQual > 100) + throw new Exception("Quality must be between 1 and 100."); + } if (subsamp >= 0 && subsamp < TJ.NUMSAMP) { for (int i = maxQual; i >= minQual; i--) - fullTest(srcBuf, w, h, subsamp, i, argv[0]); + fullTest(tjc, srcBuf, w, h, subsamp, i, argv[0]); System.out.println(""); } else { + if (pf != TJ.PF_CMYK) { + for (int i = maxQual; i >= minQual; i--) + fullTest(tjc, srcBuf, w, h, TJ.SAMP_GRAY, i, argv[0]); + System.out.println(""); + System.gc(); + } for (int i = maxQual; i >= minQual; i--) - fullTest(srcBuf, w, h, TJ.SAMP_GRAY, i, argv[0]); - System.out.println(""); - System.gc(); - for (int i = maxQual; i >= minQual; i--) - fullTest(srcBuf, w, h, TJ.SAMP_420, i, argv[0]); + fullTest(tjc, srcBuf, w, h, TJ.SAMP_420, i, argv[0]); System.out.println(""); System.gc(); for (int i = maxQual; i >= minQual; i--) - fullTest(srcBuf, w, h, TJ.SAMP_422, i, argv[0]); + fullTest(tjc, srcBuf, w, h, TJ.SAMP_422, i, argv[0]); System.out.println(""); System.gc(); for (int i = maxQual; i >= minQual; i--) - fullTest(srcBuf, w, h, TJ.SAMP_444, i, argv[0]); + fullTest(tjc, srcBuf, w, h, TJ.SAMP_444, i, argv[0]); System.out.println(""); } diff --git a/java/TJExample.java b/java/TJExample.java index 64e7ad13d..cffe9c848 100644 --- a/java/TJExample.java +++ b/java/TJExample.java @@ -140,8 +140,6 @@ else if (SCALING_FACTORS.length > 2) { System.out.println("-fastdct = Use the fastest DCT/IDCT algorithm available\n"); - System.out.println("-accuratedct = Use the most accurate DCT/IDCT algorithm available\n"); - System.exit(1); } @@ -150,11 +148,10 @@ public static void main(String[] argv) { try { - TJScalingFactor scalingFactor = new TJScalingFactor(1, 1); + TJScalingFactor scalingFactor = TJ.UNSCALED; int outSubsamp = -1, outQual = -1; TJTransform xform = new TJTransform(); - boolean display = false; - int flags = 0; + boolean display = false, fastUpsample = false, fastDCT = false; int width, height; String inFormat = "jpg", outFormat = "jpg"; BufferedImage img = null; @@ -244,13 +241,10 @@ else if (argv[i].length() > 2 && display = true; else if (argv[i].equalsIgnoreCase("-fastupsample")) { System.out.println("Using fast upsampling code"); - flags |= TJ.FLAG_FASTUPSAMPLE; + fastUpsample = true; } else if (argv[i].equalsIgnoreCase("-fastdct")) { System.out.println("Using fastest DCT/IDCT algorithm"); - flags |= TJ.FLAG_FASTDCT; - } else if (argv[i].equalsIgnoreCase("-accuratedct")) { - System.out.println("Using most accurate DCT/IDCT algorithm"); - flags |= TJ.FLAG_ACCURATEDCT; + fastDCT = true; } else usage(); } @@ -291,20 +285,21 @@ else if (argv[i].equalsIgnoreCase("-fastupsample")) { TJTransform[] xforms = new TJTransform[1]; xforms[0] = xform; xforms[0].options |= TJTransform.OPT_TRIM; - TJDecompressor[] tjds = tjt.transform(xforms, 0); + TJDecompressor[] tjds = tjt.transform(xforms); tjd = tjds[0]; tjt.close(); } else tjd = new TJDecompressor(jpegBuf); + tjd.set(TJ.PARAM_FASTUPSAMPLE, fastUpsample ? 1 : 0); + tjd.set(TJ.PARAM_FASTDCT, fastDCT ? 1 : 0); width = tjd.getWidth(); height = tjd.getHeight(); - int inSubsamp = tjd.getSubsamp(); - int inColorspace = tjd.getColorspace(); - int inFlags = tjd.getFlags(); + int inSubsamp = tjd.get(TJ.PARAM_SUBSAMP); + int inColorspace = tjd.get(TJ.PARAM_COLORSPACE); - if ((inFlags & TJ.FLAG_LOSSLESS) != 0) - scalingFactor = new TJScalingFactor(1, 1); + if (tjd.get(TJ.PARAM_LOSSLESS) == 1) + scalingFactor = TJ.UNSCALED; System.out.println((doTransform ? "Transformed" : "Input") + " Image (jpg): " + width + " x " + height + @@ -326,16 +321,16 @@ else if (argv[i].equalsIgnoreCase("-fastupsample")) { /* Scaling and/or a non-JPEG output image format and/or compression options have been selected, so we need to decompress the input/transformed image. */ + tjd.setScalingFactor(scalingFactor); width = scalingFactor.getScaled(width); height = scalingFactor.getScaled(height); if (outSubsamp < 0) outSubsamp = inSubsamp; if (!outFormat.equalsIgnoreCase("jpg")) - img = tjd.decompress(width, height, BufferedImage.TYPE_INT_RGB, - flags); + img = tjd.decompress8(BufferedImage.TYPE_INT_RGB); else - imgBuf = tjd.decompress(width, 0, height, TJ.PF_BGRX, flags); + imgBuf = tjd.decompress8(0, TJ.PF_BGRX); tjd.close(); } else { /* Input image is not a JPEG image. Load it into memory. */ @@ -372,13 +367,14 @@ else if (argv[i].equalsIgnoreCase("-fastupsample")) { " subsampling, quality = " + outQual); TJCompressor tjc = new TJCompressor(); - tjc.setSubsamp(outSubsamp); - tjc.setJPEGQuality(outQual); + tjc.set(TJ.PARAM_SUBSAMP, outSubsamp); + tjc.set(TJ.PARAM_QUALITY, outQual); + tjc.set(TJ.PARAM_FASTDCT, fastDCT ? 1 : 0); if (img != null) tjc.setSourceImage(img, 0, 0, 0, 0); else tjc.setSourceImage(imgBuf, 0, 0, width, 0, height, TJ.PF_BGRX); - byte[] jpegBuf = tjc.compress(flags); + byte[] jpegBuf = tjc.compress(); int jpegSize = tjc.getCompressedSize(); tjc.close(); diff --git a/java/TJUnitTest.java b/java/TJUnitTest.java index c64c21f09..fff4a0610 100644 --- a/java/TJUnitTest.java +++ b/java/TJUnitTest.java @@ -49,10 +49,13 @@ static void usage() { System.out.println("\nUSAGE: java " + CLASS_NAME + " [options]\n"); System.out.println("Options:"); System.out.println("-yuv = test YUV encoding/compression/decompression/decoding"); + System.out.println(" (8-bit data precision only)"); System.out.println("-noyuvpad = do not pad each row in each Y, U, and V plane to the nearest"); System.out.println(" multiple of 4 bytes"); + System.out.println("-precision N = test N-bit data precision (N is 8, 12, or 16; default is 8; if N"); + System.out.println(" is 16, then -lossless is implied)"); System.out.println("-lossless = test lossless JPEG compression/decompression"); - System.out.println("-bi = test BufferedImage I/O\n"); + System.out.println("-bi = test BufferedImage I/O (8-bit data precision only)\n"); System.exit(1); } @@ -68,13 +71,13 @@ static void usage() { "RGBA", "BGRA", "ABGR", "ARGB", "CMYK" }; - static final int[] FORMATS_3BYTE = { + static final int[] FORMATS_3SAMPLE = { TJ.PF_RGB, TJ.PF_BGR }; static final int[] FORMATS_3BYTEBI = { BufferedImage.TYPE_3BYTE_BGR }; - static final int[] FORMATS_4BYTE = { + static final int[] FORMATS_4SAMPLE = { TJ.PF_RGBX, TJ.PF_BGRX, TJ.PF_XBGR, TJ.PF_XRGB, TJ.PF_CMYK }; static final int[] FORMATS_4BYTEBI = { @@ -96,6 +99,8 @@ static void usage() { private static boolean lossless = false; private static int psv = 1; private static int yuvAlign = 4; + private static int precision = 8; + private static int sampleSize, maxSample, tolerance, redToY, yellowToY; private static boolean bi = false; private static int exitStatus = 0; @@ -145,8 +150,22 @@ static String biTypeStr(int biType) { } } - static void initBuf(byte[] buf, int w, int pitch, int h, int pf, int flags) - throws Exception { + static void fillArray(Object buf, int val) { + if (precision == 8) + Arrays.fill((byte[])buf, (byte)val); + else + Arrays.fill((short[])buf, (short)val); + } + + static void setVal(Object buf, int index, int value) { + if (precision == 8) + ((byte[])buf)[index] = (byte)value; + else + ((short[])buf)[index] = (short)value; + } + + static void initBuf(Object buf, int w, int pitch, int h, int pf, + boolean bottomUp) throws Exception { int roffset = TJ.getRedOffset(pf); int goffset = TJ.getGreenOffset(pf); int boffset = TJ.getBlueOffset(pf); @@ -155,67 +174,67 @@ static void initBuf(byte[] buf, int w, int pitch, int h, int pf, int flags) int index, row, col, halfway = 16; if (pf == TJ.PF_GRAY) { - Arrays.fill(buf, (byte)0); + fillArray(buf, 0); for (row = 0; row < h; row++) { for (col = 0; col < w; col++) { - if ((flags & TJ.FLAG_BOTTOMUP) != 0) + if (bottomUp) index = pitch * (h - row - 1) + col; else index = pitch * row + col; if (((row / 8) + (col / 8)) % 2 == 0) - buf[index] = (row < halfway) ? (byte)255 : 0; + setVal(buf, index, (row < halfway) ? maxSample : 0); else - buf[index] = (row < halfway) ? 76 : (byte)226; + setVal(buf, index, (row < halfway) ? redToY : yellowToY); } } return; } if (pf == TJ.PF_CMYK) { - Arrays.fill(buf, (byte)255); + fillArray(buf, maxSample); for (row = 0; row < h; row++) { for (col = 0; col < w; col++) { - if ((flags & TJ.FLAG_BOTTOMUP) != 0) + if (bottomUp) index = (h - row - 1) * w + col; else index = row * w + col; if (((row / 8) + (col / 8)) % 2 == 0) { - if (row >= halfway) buf[index * ps + 3] = 0; + if (row >= halfway) setVal(buf, index * ps + 3, 0); } else { - buf[index * ps + 2] = 0; + setVal(buf, index * ps + 2, 0); if (row < halfway) - buf[index * ps + 1] = 0; + setVal(buf, index * ps + 1, 0); } } } return; } - Arrays.fill(buf, (byte)0); + fillArray(buf, 0); for (row = 0; row < h; row++) { for (col = 0; col < w; col++) { - if ((flags & TJ.FLAG_BOTTOMUP) != 0) + if (bottomUp) index = pitch * (h - row - 1) + col * ps; else index = pitch * row + col * ps; if (((row / 8) + (col / 8)) % 2 == 0) { if (row < halfway) { - buf[index + roffset] = (byte)255; - buf[index + goffset] = (byte)255; - buf[index + boffset] = (byte)255; + setVal(buf, index + roffset, maxSample); + setVal(buf, index + goffset, maxSample); + setVal(buf, index + boffset, maxSample); } } else { - buf[index + roffset] = (byte)255; + setVal(buf, index + roffset, maxSample); if (row >= halfway) - buf[index + goffset] = (byte)255; + setVal(buf, index + goffset, maxSample); } if (aoffset >= 0) - buf[index + aoffset] = (byte)255; + setVal(buf, index + aoffset, maxSample); } } } - static void initIntBuf(int[] buf, int w, int pitch, int h, int pf, int flags) - throws Exception { + static void initIntBuf(int[] buf, int w, int pitch, int h, int pf, + boolean bottomUp) throws Exception { int rshift = TJ.getRedOffset(pf) * 8; int gshift = TJ.getGreenOffset(pf) * 8; int bshift = TJ.getBlueOffset(pf) * 8; @@ -225,7 +244,7 @@ static void initIntBuf(int[] buf, int w, int pitch, int h, int pf, int flags) Arrays.fill(buf, 0); for (row = 0; row < h; row++) { for (col = 0; col < w; col++) { - if ((flags & TJ.FLAG_BOTTOMUP) != 0) + if (bottomUp) index = pitch * (h - row - 1) + col; else index = pitch * row + col; @@ -246,7 +265,8 @@ static void initIntBuf(int[] buf, int w, int pitch, int h, int pf, int flags) } } - static void initImg(BufferedImage img, int pf, int flags) throws Exception { + static void initImg(BufferedImage img, int pf, boolean bottomUp) + throws Exception { WritableRaster wr = img.getRaster(); int imgType = img.getType(); @@ -259,20 +279,20 @@ static void initImg(BufferedImage img, int pf, int flags) throws Exception { int pitch = sm.getScanlineStride(); DataBufferInt db = (DataBufferInt)wr.getDataBuffer(); int[] buf = db.getData(); - initIntBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, flags); + initIntBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, bottomUp); } else { ComponentSampleModel sm = (ComponentSampleModel)img.getSampleModel(); int pitch = sm.getScanlineStride(); DataBufferByte db = (DataBufferByte)wr.getDataBuffer(); byte[] buf = db.getData(); - initBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, flags); + initBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, bottomUp); } } static void checkVal(int row, int col, int v, String vname, int cv) throws Exception { v = (v < 0) ? v + 256 : v; - if (v < cv - (lossless ? 0 : 1) || v > cv + (lossless ? 0 : 1)) { + if (v < cv - tolerance || v > cv + tolerance) { throw new Exception("Comp. " + vname + " at " + row + "," + col + " should be " + cv + ", not " + v); } @@ -281,23 +301,34 @@ static void checkVal(int row, int col, int v, String vname, int cv) static void checkVal0(int row, int col, int v, String vname) throws Exception { v = (v < 0) ? v + 256 : v; - if (v > (lossless ? 0 : 1)) { + if (v > tolerance) { throw new Exception("Comp. " + vname + " at " + row + "," + col + " should be 0, not " + v); } } - static void checkVal255(int row, int col, int v, String vname) + static void checkValMax(int row, int col, int v, String vname) throws Exception { v = (v < 0) ? v + 256 : v; - if (v < 255 - (lossless ? 0 : 1)) { + if (v < maxSample - tolerance) { throw new Exception("Comp. " + vname + " at " + row + "," + col + - " should be 255, not " + v); + " should be " + maxSample + ", not " + v); } } - static int checkBuf(byte[] buf, int w, int pitch, int h, int pf, int subsamp, - TJScalingFactor sf, int flags) throws Exception { + static int getVal(Object buf, int index) { + int v; + if (precision == 8) + v = (int)(((byte[])buf)[index]); + else + v = (int)(((short[])buf)[index]); + if (v < 0) + v += maxSample + 1; + return v; + } + + static int checkBuf(Object buf, int w, int pitch, int h, int pf, int subsamp, + TJScalingFactor sf, boolean bottomUp) throws Exception { int roffset = TJ.getRedOffset(pf); int goffset = TJ.getGreenOffset(pf); int boffset = TJ.getBlueOffset(pf); @@ -315,29 +346,29 @@ static int checkBuf(byte[] buf, int w, int pitch, int h, int pf, int subsamp, if (pf == TJ.PF_CMYK) { for (row = 0; row < h; row++) { for (col = 0; col < w; col++) { - if ((flags & TJ.FLAG_BOTTOMUP) != 0) + if (bottomUp) index = (h - row - 1) * w + col; else index = row * w + col; - byte c = buf[index * ps]; - byte m = buf[index * ps + 1]; - byte y = buf[index * ps + 2]; - byte k = buf[index * ps + 3]; - checkVal255(row, col, c, "C"); + int c = getVal(buf, index * ps); + int m = getVal(buf, index * ps + 1); + int y = getVal(buf, index * ps + 2); + int k = getVal(buf, index * ps + 3); + checkValMax(row, col, c, "C"); if (((row / blockSize) + (col / blockSize)) % 2 == 0) { - checkVal255(row, col, m, "M"); - checkVal255(row, col, y, "Y"); + checkValMax(row, col, m, "M"); + checkValMax(row, col, y, "Y"); if (row < halfway) - checkVal255(row, col, k, "K"); + checkValMax(row, col, k, "K"); else checkVal0(row, col, k, "K"); } else { checkVal0(row, col, y, "Y"); - checkVal255(row, col, k, "K"); + checkValMax(row, col, k, "K"); if (row < halfway) checkVal0(row, col, m, "M"); else - checkVal255(row, col, m, "M"); + checkValMax(row, col, m, "M"); } } } @@ -346,19 +377,19 @@ static int checkBuf(byte[] buf, int w, int pitch, int h, int pf, int subsamp, for (row = 0; row < halfway; row++) { for (col = 0; col < w; col++) { - if ((flags & TJ.FLAG_BOTTOMUP) != 0) + if (bottomUp) index = pitch * (h - row - 1) + col * ps; else index = pitch * row + col * ps; - byte r = buf[index + roffset]; - byte g = buf[index + goffset]; - byte b = buf[index + boffset]; - byte a = aoffset >= 0 ? buf[index + aoffset] : (byte)255; + int r = getVal(buf, index + roffset); + int g = getVal(buf, index + goffset); + int b = getVal(buf, index + boffset); + int a = aoffset >= 0 ? getVal(buf, index + aoffset) : maxSample; if (((row / blockSize) + (col / blockSize)) % 2 == 0) { if (row < halfway) { - checkVal255(row, col, r, "R"); - checkVal255(row, col, g, "G"); - checkVal255(row, col, b, "B"); + checkValMax(row, col, r, "R"); + checkValMax(row, col, g, "G"); + checkValMax(row, col, b, "B"); } else { checkVal0(row, col, r, "R"); checkVal0(row, col, g, "G"); @@ -367,25 +398,25 @@ static int checkBuf(byte[] buf, int w, int pitch, int h, int pf, int subsamp, } else { if (subsamp == TJ.SAMP_GRAY) { if (row < halfway) { - checkVal(row, col, r, "R", 76); - checkVal(row, col, g, "G", 76); - checkVal(row, col, b, "B", 76); + checkVal(row, col, r, "R", redToY); + checkVal(row, col, g, "G", redToY); + checkVal(row, col, b, "B", redToY); } else { - checkVal(row, col, r, "R", 226); - checkVal(row, col, g, "G", 226); - checkVal(row, col, b, "B", 226); + checkVal(row, col, r, "R", yellowToY); + checkVal(row, col, g, "G", yellowToY); + checkVal(row, col, b, "B", yellowToY); } } else { - checkVal255(row, col, r, "R"); + checkValMax(row, col, r, "R"); if (row < halfway) { checkVal0(row, col, g, "G"); } else { - checkVal255(row, col, g, "G"); + checkValMax(row, col, g, "G"); } checkVal0(row, col, b, "B"); } } - checkVal255(row, col, a, "A"); + checkValMax(row, col, a, "A"); } } } catch (Exception e) { @@ -397,22 +428,15 @@ static int checkBuf(byte[] buf, int w, int pitch, int h, int pf, int subsamp, for (row = 0; row < h; row++) { for (col = 0; col < w; col++) { if (pf == TJ.PF_CMYK) { - int c = buf[pitch * row + col * ps]; - int m = buf[pitch * row + col * ps + 1]; - int y = buf[pitch * row + col * ps + 2]; - int k = buf[pitch * row + col * ps + 3]; - if (c < 0) c += 256; - if (m < 0) m += 256; - if (y < 0) y += 256; - if (k < 0) k += 256; + int c = getVal(buf, pitch * row + col * ps); + int m = getVal(buf, pitch * row + col * ps + 1); + int y = getVal(buf, pitch * row + col * ps + 2); + int k = getVal(buf, pitch * row + col * ps + 3); System.out.format("%3d/%3d/%3d/%3d ", c, m, y, k); } else { - int r = buf[pitch * row + col * ps + roffset]; - int g = buf[pitch * row + col * ps + goffset]; - int b = buf[pitch * row + col * ps + boffset]; - if (r < 0) r += 256; - if (g < 0) g += 256; - if (b < 0) b += 256; + int r = getVal(buf, pitch * row + col * ps + roffset); + int g = getVal(buf, pitch * row + col * ps + goffset); + int b = getVal(buf, pitch * row + col * ps + boffset); System.out.format("%3d/%3d/%3d ", r, g, b); } } @@ -423,7 +447,7 @@ static int checkBuf(byte[] buf, int w, int pitch, int h, int pf, int subsamp, } static int checkIntBuf(int[] buf, int w, int pitch, int h, int pf, - int subsamp, TJScalingFactor sf, int flags) + int subsamp, TJScalingFactor sf, boolean bottomUp) throws Exception { int rshift = TJ.getRedOffset(pf) * 8; int gshift = TJ.getGreenOffset(pf) * 8; @@ -436,7 +460,7 @@ static int checkIntBuf(int[] buf, int w, int pitch, int h, int pf, try { for (row = 0; row < halfway; row++) { for (col = 0; col < w; col++) { - if ((flags & TJ.FLAG_BOTTOMUP) != 0) + if (bottomUp) index = pitch * (h - row - 1) + col; else index = pitch * row + col; @@ -446,9 +470,9 @@ static int checkIntBuf(int[] buf, int w, int pitch, int h, int pf, int a = ashift >= 0 ? (buf[index] >> ashift) & 0xFF : 255; if (((row / blockSize) + (col / blockSize)) % 2 == 0) { if (row < halfway) { - checkVal255(row, col, r, "R"); - checkVal255(row, col, g, "G"); - checkVal255(row, col, b, "B"); + checkValMax(row, col, r, "R"); + checkValMax(row, col, g, "G"); + checkValMax(row, col, b, "B"); } else { checkVal0(row, col, r, "R"); checkVal0(row, col, g, "G"); @@ -466,16 +490,16 @@ static int checkIntBuf(int[] buf, int w, int pitch, int h, int pf, checkVal(row, col, b, "B", 226); } } else { - checkVal255(row, col, r, "R"); + checkValMax(row, col, r, "R"); if (row < halfway) { checkVal0(row, col, g, "G"); } else { - checkVal255(row, col, g, "G"); + checkValMax(row, col, g, "G"); } checkVal0(row, col, b, "B"); } } - checkVal255(row, col, a, "A"); + checkValMax(row, col, a, "A"); } } } catch (Exception e) { @@ -501,7 +525,7 @@ static int checkIntBuf(int[] buf, int w, int pitch, int h, int pf, } static int checkImg(BufferedImage img, int pf, int subsamp, - TJScalingFactor sf, int flags) throws Exception { + TJScalingFactor sf, boolean bottomUp) throws Exception { WritableRaster wr = img.getRaster(); int imgType = img.getType(); if (imgType == BufferedImage.TYPE_INT_RGB || @@ -514,14 +538,14 @@ static int checkImg(BufferedImage img, int pf, int subsamp, DataBufferInt db = (DataBufferInt)wr.getDataBuffer(); int[] buf = db.getData(); return checkIntBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, - subsamp, sf, flags); + subsamp, sf, bottomUp); } else { ComponentSampleModel sm = (ComponentSampleModel)img.getSampleModel(); int pitch = sm.getScanlineStride(); DataBufferByte db = (DataBufferByte)wr.getDataBuffer(); byte[] buf = db.getData(); return checkBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, subsamp, - sf, flags); + sf, bottomUp); } } @@ -552,7 +576,7 @@ static int checkBufYUV(byte[] buf, int size, int w, int h, int subsamp, byte y = buf[ypitch * row + col]; if (((row / blockSize) + (col / blockSize)) % 2 == 0) { if (row < halfway) - checkVal255(row, col, y, "Y"); + checkValMax(row, col, y, "Y"); else checkVal0(row, col, y, "Y"); } else { @@ -575,7 +599,7 @@ static int checkBufYUV(byte[] buf, int size, int w, int h, int subsamp, } else { if (row < halfway) { checkVal(row, col, u, "U", 85); - checkVal255(row, col, v, "V"); + checkValMax(row, col, v, "V"); } else { checkVal0(row, col, u, "U"); checkVal(row, col, v, "V", 149); @@ -630,15 +654,17 @@ static void writeJPEG(byte[] jpegBuf, int jpegBufSize, String filename) } static int compTest(TJCompressor tjc, byte[] dstBuf, int w, int h, int pf, - String baseName, int subsamp, int jpegQual, int flags) - throws Exception { + String baseName) throws Exception { String tempStr; - byte[] srcBuf = null; + Object srcBuf = null; BufferedImage img = null; String pfStr, pfStrLong; - String buStr = (flags & TJ.FLAG_BOTTOMUP) != 0 ? "BU" : "TD"; - String buStrLong = (flags & TJ.FLAG_BOTTOMUP) != 0 ? - "Bottom-Up" : "Top-Down "; + boolean bottomUp = (tjc.get(TJ.PARAM_BOTTOMUP) == 1); + int subsamp = tjc.get(TJ.PARAM_SUBSAMP); + int jpegQual = tjc.get(TJ.PARAM_QUALITY); + int jpegPSV = tjc.get(TJ.PARAM_LOSSLESSPSV); + String buStr = bottomUp ? "BU" : "TD"; + String buStrLong = bottomUp ? "Bottom-Up" : "Top-Down "; int size = 0, ps, imgType = pf; if (bi) { @@ -653,25 +679,31 @@ static int compTest(TJCompressor tjc, byte[] dstBuf, int w, int h, int pf, if (bi) { img = new BufferedImage(w, h, imgType); - initImg(img, pf, flags); - tempStr = baseName + "_enc_" + pfStr + "_" + buStr + "_" + - SUBNAME[subsamp] + "_Q" + jpegQual + ".png"; + initImg(img, pf, bottomUp); + tempStr = baseName + "_enc" + precision + "_" + pfStr + "_" + buStr + + "_" + SUBNAME[subsamp] + "_Q" + jpegQual + ".png"; File file = new File(tempStr); ImageIO.write(img, "png", file); tjc.setSourceImage(img, 0, 0, 0, 0); } else { - srcBuf = new byte[w * h * ps + 1]; - initBuf(srcBuf, w, w * ps, h, pf, flags); - tjc.setSourceImage(srcBuf, 0, 0, w, 0, h, pf); + if (precision == 8) + srcBuf = new byte[w * h * ps + 1]; + else + srcBuf = new short[w * h * ps + 1]; + initBuf(srcBuf, w, w * ps, h, pf, bottomUp); + if (precision == 8) + tjc.setSourceImage((byte[])srcBuf, 0, 0, w, 0, h, pf); + else if (precision == 12) + tjc.setSourceImage12((short[])srcBuf, 0, 0, w, 0, h, pf); + else + tjc.setSourceImage16((short[])srcBuf, 0, 0, w, 0, h, pf); } Arrays.fill(dstBuf, (byte)0); - tjc.setSubsamp(subsamp); - tjc.setJPEGQuality(jpegQual); if (doYUV) { System.out.format("%s %s -> YUV %s ... ", pfStrLong, buStrLong, SUBNAME_LONG[subsamp]); - YUVImage yuvImage = tjc.encodeYUV(yuvAlign, flags); + YUVImage yuvImage = tjc.encodeYUV(yuvAlign); if (checkBufYUV(yuvImage.getBuf(), yuvImage.getSize(), w, h, subsamp, new TJScalingFactor(1, 1)) == 1) System.out.print("Passed.\n"); @@ -684,14 +716,22 @@ static int compTest(TJCompressor tjc, byte[] dstBuf, int w, int h, int pf, buStrLong, jpegQual); tjc.setSourceImage(yuvImage); } else { - System.out.format("%s %s -> %s Q%d ... ", pfStrLong, buStrLong, - SUBNAME_LONG[subsamp], jpegQual); + if (lossless) + System.out.format("%s %s -> LOSSLESS PSV%d ... ", pfStrLong, buStrLong, + jpegPSV); + else + System.out.format("%s %s -> %s Q%d ... ", pfStrLong, buStrLong, + SUBNAME_LONG[subsamp], jpegQual); } - tjc.compress(dstBuf, flags); + tjc.compress(dstBuf); size = tjc.getCompressedSize(); - tempStr = baseName + "_enc_" + pfStr + "_" + buStr + "_" + - SUBNAME[subsamp] + "_Q" + jpegQual + ".jpg"; + if (lossless) + tempStr = baseName + "_enc" + precision + "_" + pfStr + "_" + buStr + + "_LOSSLESS_PSV" + jpegPSV + ".jpg"; + else + tempStr = baseName + "_enc" + precision + "_" + pfStr + "_" + buStr + + "_" + SUBNAME[subsamp] + "_Q" + jpegQual + ".jpg"; writeJPEG(dstBuf, size, tempStr); System.out.println("Done.\n Result in " + tempStr); @@ -700,15 +740,15 @@ static int compTest(TJCompressor tjc, byte[] dstBuf, int w, int h, int pf, static void decompTest(TJDecompressor tjd, byte[] jpegBuf, int jpegSize, int w, int h, int pf, String baseName, int subsamp, - int flags, TJScalingFactor sf) throws Exception { + TJScalingFactor sf) throws Exception { String pfStr, pfStrLong, tempStr; - String buStrLong = (flags & TJ.FLAG_BOTTOMUP) != 0 ? - "Bottom-Up" : "Top-Down "; + boolean bottomUp = (tjd.get(TJ.PARAM_BOTTOMUP) == 1); + String buStrLong = bottomUp ? "Bottom-Up" : "Top-Down "; int scaledWidth = sf.getScaled(w); int scaledHeight = sf.getScaled(h); int temp1, temp2, imgType = pf; BufferedImage img = null; - byte[] dstBuf = null; + Object dstBuf = null; if (bi) { pf = biTypePF(imgType); @@ -720,26 +760,19 @@ static void decompTest(TJDecompressor tjd, byte[] jpegBuf, int jpegSize, } tjd.setSourceImage(jpegBuf, jpegSize); + tjd.setScalingFactor(sf); if (lossless && subsamp != TJ.SAMP_444 && subsamp != TJ.SAMP_GRAY) subsamp = TJ.SAMP_444; if (tjd.getWidth() != w || tjd.getHeight() != h || - tjd.getSubsamp() != subsamp) + tjd.get(TJ.PARAM_SUBSAMP) != subsamp) throw new Exception("Incorrect JPEG header"); - temp1 = scaledWidth; - temp2 = scaledHeight; - temp1 = tjd.getScaledWidth(temp1, temp2); - temp2 = tjd.getScaledHeight(temp1, temp2); - if (temp1 != scaledWidth || temp2 != scaledHeight) - throw new Exception("Scaled size mismatch"); - if (doYUV) { System.out.format("JPEG -> YUV %s ", SUBNAME_LONG[subsamp]); if (!sf.isOne()) System.out.format("%d/%d ... ", sf.getNum(), sf.getDenom()); else System.out.print("... "); - YUVImage yuvImage = tjd.decompressToYUV(scaledWidth, yuvAlign, - scaledHeight, flags); + YUVImage yuvImage = tjd.decompressToYUV(yuvAlign); if (checkBufYUV(yuvImage.getBuf(), yuvImage.getSize(), scaledWidth, scaledHeight, subsamp, sf) == 1) System.out.print("Passed.\n"); @@ -757,23 +790,28 @@ static void decompTest(TJDecompressor tjd, byte[] jpegBuf, int jpegSize, else System.out.print("... "); } if (bi) - img = tjd.decompress(scaledWidth, scaledHeight, imgType, flags); - else - dstBuf = tjd.decompress(scaledWidth, 0, scaledHeight, pf, flags); + img = tjd.decompress8(imgType); + else { + if (precision == 8) + dstBuf = tjd.decompress8(0, pf); + else if (precision == 12) + dstBuf = tjd.decompress12(0, pf); + else + dstBuf = tjd.decompress16(0, pf); + } if (bi) { - tempStr = baseName + "_dec_" + pfStr + "_" + - (((flags & TJ.FLAG_BOTTOMUP) != 0) ? "BU" : "TD") + "_" + - SUBNAME[subsamp] + "_" + + tempStr = baseName + "_dec_" + pfStr + "_" + (bottomUp ? "BU" : "TD") + + "_" + SUBNAME[subsamp] + "_" + (double)sf.getNum() / (double)sf.getDenom() + "x" + ".png"; File file = new File(tempStr); ImageIO.write(img, "png", file); } - if ((bi && checkImg(img, pf, subsamp, sf, flags) == 1) || + if ((bi && checkImg(img, pf, subsamp, sf, bottomUp) == 1) || (!bi && checkBuf(dstBuf, scaledWidth, scaledWidth * TJ.getPixelSize(pf), scaledHeight, pf, - subsamp, sf, flags) == 1)) + subsamp, sf, bottomUp) == 1)) System.out.print("Passed.\n"); else { System.out.print("FAILED!\n"); @@ -782,14 +820,13 @@ static void decompTest(TJDecompressor tjd, byte[] jpegBuf, int jpegSize, } static void decompTest(TJDecompressor tjd, byte[] jpegBuf, int jpegSize, - int w, int h, int pf, String baseName, int subsamp, - int flags) throws Exception { + int w, int h, int pf, String baseName, int subsamp) + throws Exception { int i; - TJScalingFactor sf1 = new TJScalingFactor(1, 1); if (lossless) { - decompTest(tjd, jpegBuf, jpegSize, w, h, pf, baseName, subsamp, flags, - sf1); + decompTest(tjd, jpegBuf, jpegSize, w, h, pf, baseName, subsamp, + TJ.UNSCALED); return; } @@ -802,8 +839,7 @@ static void decompTest(TJDecompressor tjd, byte[] jpegBuf, int jpegSize, (denom == 2 || denom == 1)) || (subsamp != TJ.SAMP_411 && num == 1 && (denom == 4 || denom == 2 || denom == 1))) - decompTest(tjd, jpegBuf, jpegSize, w, h, pf, baseName, subsamp, - flags, sf[i]); + decompTest(tjd, jpegBuf, jpegSize, w, h, pf, baseName, subsamp, sf[i]); } } @@ -811,7 +847,7 @@ static void doTest(int w, int h, int[] formats, int subsamp, String baseName) throws Exception { TJCompressor tjc = null; TJDecompressor tjd = null; - int size, quality = 100; + int size; byte[] dstBuf; if (lossless && subsamp != TJ.SAMP_GRAY) @@ -823,26 +859,28 @@ static void doTest(int w, int h, int[] formats, int subsamp, String baseName) tjc = new TJCompressor(); tjd = new TJDecompressor(); + if (lossless) { + tjc.set(TJ.PARAM_LOSSLESS, 1); + tjc.set(TJ.PARAM_LOSSLESSPSV, ((psv++ - 1) % 7) + 1); + } else { + tjc.set(TJ.PARAM_QUALITY, 100); + if (subsamp == TJ.SAMP_422 || subsamp == TJ.SAMP_420 || + subsamp == TJ.SAMP_440 || subsamp == TJ.SAMP_411) + tjd.set(TJ.PARAM_FASTUPSAMPLE, 1); + } + tjc.set(TJ.PARAM_SUBSAMP, subsamp); + for (int pf : formats) { if (pf < 0) continue; for (int i = 0; i < 2; i++) { - int flags = 0; - if (lossless) { - flags |= TJ.FLAG_LOSSLESS; - quality = (((psv++ - 1) % 7) + 1) * 10; - } - if (subsamp == TJ.SAMP_422 || subsamp == TJ.SAMP_420 || - subsamp == TJ.SAMP_440 || subsamp == TJ.SAMP_411) - flags |= TJ.FLAG_FASTUPSAMPLE; - if (i == 1) - flags |= TJ.FLAG_BOTTOMUP; - size = compTest(tjc, dstBuf, w, h, pf, baseName, subsamp, quality, - flags); - decompTest(tjd, dstBuf, size, w, h, pf, baseName, subsamp, flags); + tjc.set(TJ.PARAM_BOTTOMUP, i == 1 ? 1 : 0); + tjd.set(TJ.PARAM_BOTTOMUP, i == 1 ? 1 : 0); + size = compTest(tjc, dstBuf, w, h, pf, baseName); + decompTest(tjd, dstBuf, size, w, h, pf, baseName, subsamp); if (pf >= TJ.PF_RGBX && pf <= TJ.PF_XRGB && !bi) { System.out.print("\n"); decompTest(tjd, dstBuf, size, w, h, pf + (TJ.PF_RGBA - TJ.PF_RGBX), - baseName, subsamp, flags); + baseName, subsamp); } System.out.print("\n"); } @@ -907,22 +945,25 @@ static void overflowTest() throws Exception { } static void bufSizeTest() throws Exception { - int w, h, i, subsamp, flags = 0, quality = 100, numSamp = TJ.NUMSAMP; + int w, h, i, subsamp, numSamp = TJ.NUMSAMP; byte[] srcBuf, dstBuf = null; YUVImage dstImage = null; TJCompressor tjc = null; Random r = new Random(); try { + tjc = new TJCompressor(); + if (lossless) { - flags |= TJ.FLAG_LOSSLESS; - quality = (((psv++ - 1) % 7) + 1) * 10; + tjc.set(TJ.PARAM_LOSSLESS, 1); + tjc.set(TJ.PARAM_LOSSLESSPSV, ((psv++ - 1) % 7) + 1); numSamp = 1; - } + } else + tjc.set(TJ.PARAM_QUALITY, 100); - tjc = new TJCompressor(); System.out.println("Buffer size regression test"); for (subsamp = 0; subsamp < numSamp; subsamp++) { + tjc.set(TJ.PARAM_SUBSAMP, subsamp); for (w = 1; w < 48; w++) { int maxh = (w == 1) ? 2048 : 48; for (h = 1; h < maxh; h++) { @@ -937,12 +978,10 @@ static void bufSizeTest() throws Exception { srcBuf[i] = (byte)(r.nextInt(2) * 255); } tjc.setSourceImage(srcBuf, 0, 0, w, 0, h, TJ.PF_BGRX); - tjc.setSubsamp(subsamp); - tjc.setJPEGQuality(quality); if (doYUV) - tjc.encodeYUV(dstImage, 0); + tjc.encodeYUV(dstImage); else - tjc.compress(dstBuf, flags); + tjc.compress(dstBuf); srcBuf = new byte[h * w * 4]; if (doYUV) @@ -954,9 +993,9 @@ static void bufSizeTest() throws Exception { } tjc.setSourceImage(srcBuf, 0, 0, h, 0, w, TJ.PF_BGRX); if (doYUV) - tjc.encodeYUV(dstImage, 0); + tjc.encodeYUV(dstImage); else - tjc.compress(dstBuf, flags); + tjc.compress(dstBuf); } dstImage = null; dstBuf = null; @@ -984,43 +1023,67 @@ else if (argv[i].equalsIgnoreCase("-lossless")) else if (argv[i].equalsIgnoreCase("-bi")) { bi = true; testName = "javabitest"; + } else if (argv[i].equalsIgnoreCase("-precision") && + i < argv.length - 1) { + int tempi = -1; + + try { + tempi = Integer.parseInt(argv[++i]); + } catch (NumberFormatException e) {} + if (tempi != 8 && tempi != 12 && tempi != 16) + usage(); + precision = tempi; + if (precision == 16) + lossless = true; } else usage(); } if (lossless && doYUV) throw new Exception("Lossless JPEG and YUV encoding/decoding are incompatible."); + if (precision != 8 && doYUV) + throw new Exception("YUV encoding/decoding requires 8-bit data precision."); + if (precision != 8 && bi) + throw new Exception("BufferedImage support requires 8-bit data precision."); + + System.out.format("Testing %d-bit precision\n", precision); + sampleSize = (precision == 8 ? 1 : 2); + maxSample = (1 << precision) - 1; + tolerance = (lossless ? 0 : (precision > 8 ? 2 : 1)); + redToY = (19595 * maxSample) >> 16; + yellowToY = (58065 * maxSample) >> 16; + if (doYUV) - FORMATS_4BYTE[4] = -1; + FORMATS_4SAMPLE[4] = -1; overflowTest(); - doTest(35, 39, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_444, + doTest(35, 39, bi ? FORMATS_3BYTEBI : FORMATS_3SAMPLE, TJ.SAMP_444, testName); - doTest(39, 41, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_444, + doTest(39, 41, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_444, testName); - doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_422, + doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3SAMPLE, TJ.SAMP_422, testName); if (!lossless) { - doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_422, + doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_422, testName); - doTest(39, 41, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_420, + doTest(39, 41, bi ? FORMATS_3BYTEBI : FORMATS_3SAMPLE, TJ.SAMP_420, testName); - doTest(41, 35, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_420, + doTest(41, 35, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_420, testName); - doTest(35, 39, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_440, + doTest(35, 39, bi ? FORMATS_3BYTEBI : FORMATS_3SAMPLE, TJ.SAMP_440, testName); - doTest(39, 41, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_440, + doTest(39, 41, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_440, testName); - doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_411, + doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3SAMPLE, TJ.SAMP_411, testName); - doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_411, + doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_411, testName); } doTest(39, 41, bi ? FORMATS_GRAYBI : FORMATS_GRAY, TJ.SAMP_GRAY, testName); if (!lossless) { - doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_GRAY, + doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3SAMPLE, TJ.SAMP_GRAY, testName); - FORMATS_4BYTE[4] = -1; - doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_GRAY, + FORMATS_4SAMPLE[4] = -1; + doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_GRAY, testName); } if (!bi) diff --git a/java/doc/allclasses-frame.html b/java/doc/allclasses-frame.html deleted file mode 100644 index fecac06d0..000000000 --- a/java/doc/allclasses-frame.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - -All Classes - - - -

    All Classes

    - - - diff --git a/java/doc/allclasses-index.html b/java/doc/allclasses-index.html new file mode 100644 index 000000000..f3179c5f3 --- /dev/null +++ b/java/doc/allclasses-index.html @@ -0,0 +1,215 @@ + + + + + +All Classes + + + + + + + + + + + + + + +
    + +
    +
    +
    +

    All Classes

    +
    +
    + +
    +
    +
    + +
    + + diff --git a/java/doc/allclasses-noframe.html b/java/doc/allclasses.html similarity index 56% rename from java/doc/allclasses-noframe.html rename to java/doc/allclasses.html index 1f7fd3c6b..2703a3c47 100644 --- a/java/doc/allclasses-noframe.html +++ b/java/doc/allclasses.html @@ -1,17 +1,29 @@ - + + All Classes + + + + + + + + + -

    All Classes

    - + diff --git a/java/doc/allpackages-index.html b/java/doc/allpackages-index.html new file mode 100644 index 000000000..8ad354d72 --- /dev/null +++ b/java/doc/allpackages-index.html @@ -0,0 +1,162 @@ + + + + + +All Packages + + + + + + + + + + + + + + +
    + +
    +
    +
    +

    All Packages

    +
    +
    + +
    +
    +
    + +
    + + diff --git a/java/doc/constant-values.html b/java/doc/constant-values.html index b4ddce952..5b1d2385d 100644 --- a/java/doc/constant-values.html +++ b/java/doc/constant-values.html @@ -1,9 +1,21 @@ - + + Constant Field Values + + + + + + + + + +var pathtoroot = "./"; +var useModuleDirectories = true; +loadScripts(document, 'script'); +
    + +
    +

    Constant Field Values

    +

    Contents

    +
    -
    +
    +

    org.libjpegturbo.*

    +
    +
    + diff --git a/java/doc/deprecated-list.html b/java/doc/deprecated-list.html index b8726bcb0..3f4f34a07 100644 --- a/java/doc/deprecated-list.html +++ b/java/doc/deprecated-list.html @@ -1,9 +1,21 @@ - + + Deprecated List + + + + + + + + + +var pathtoroot = "./"; +var useModuleDirectories = true; +loadScripts(document, 'script'); +
    + +
    +

    Deprecated API

    Contents

    +
    +
    + + + + + + + +
    +
    + diff --git a/java/doc/package-list b/java/doc/element-list similarity index 100% rename from java/doc/package-list rename to java/doc/element-list diff --git a/java/doc/help-doc.html b/java/doc/help-doc.html index 6645d9575..6d9362c9d 100644 --- a/java/doc/help-doc.html +++ b/java/doc/help-doc.html @@ -1,9 +1,21 @@ - + + API Help + + + + + + + + + +var pathtoroot = "./"; +var useModuleDirectories = true; +loadScripts(document, 'script'); +
    + +
    +

    How This API Document Is Organized

    This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
    @@ -69,104 +98,132 @@

    How This API Document Is Organized

    • +

      Package

      -

      Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain six categories:

      +

      Each package has a page that contains a list of its classes and interfaces, with a summary for each. These pages may contain six categories:

        -
      • Interfaces (italic)
      • +
      • Interfaces
      • Classes
      • Enums
      • Exceptions
      • Errors
      • Annotation Types
      +
    • -

      Class/Interface

      +
      +

      Class or Interface

      Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:

        -
      • Class inheritance diagram
      • +
      • Class Inheritance Diagram
      • Direct Subclasses
      • All Known Subinterfaces
      • All Known Implementing Classes
      • -
      • Class/interface declaration
      • -
      • Class/interface description
      • +
      • Class or Interface Declaration
      • +
      • Class or Interface Description
      +
      • Nested Class Summary
      • Field Summary
      • +
      • Property Summary
      • Constructor Summary
      • Method Summary
      +
      • Field Detail
      • +
      • Property Detail
      • Constructor Detail
      • Method Detail

      Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.

      +
    • +

      Annotation Type

      Each annotation type has its own separate page with the following sections:

        -
      • Annotation Type declaration
      • -
      • Annotation Type description
      • +
      • Annotation Type Declaration
      • +
      • Annotation Type Description
      • Required Element Summary
      • Optional Element Summary
      • Element Detail
      +
    • +

      Enum

      Each enum has its own separate page with the following sections:

        -
      • Enum declaration
      • -
      • Enum description
      • +
      • Enum Declaration
      • +
      • Enum Description
      • Enum Constant Summary
      • Enum Constant Detail
      +
    • +

      Tree (Class Hierarchy)

      -

      There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.

      +

      There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. Classes are organized by inheritance structure starting with java.lang.Object. Interfaces do not inherit from java.lang.Object.

      • When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
      • -
      • When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
      • +
      • When viewing a particular package, class or interface page, clicking on "Tree" displays the hierarchy for only that package.
      +
    • +

      Deprecated API

      The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.

      +
    • +

      Index

      -

      The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.

      +

      The Index contains an alphabetic index of all classes, interfaces, constructors, methods, and fields, as well as lists of all packages and all classes.

      +
    • -

      Prev/Next

      -

      These links take you to the next or previous class, interface, package, or related page.

      -
    • -
    • -

      Frames/No Frames

      -

      These links show and hide the HTML frames. All pages are available with or without frames.

      -
    • -
    • -

      All Classes

      -

      The All Classes link shows all classes and interfaces except non-static nested types.

      +
      +

      All Classes

      +

      The All Classes link shows all classes and interfaces except non-static nested types.

      +
    • +

      Serialized Form

      Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.

      +
    • +

      Constant Field Values

      The Constant Field Values page lists the static final fields and their values.

      +
      +
    • +
    • +
      +

      Search

      +

      You can search for definitions of modules, packages, types, fields, methods and other terms defined in the API, using some or all of the name. "Camel-case" abbreviations are supported: for example, "InpStr" will find "InputStream" and "InputStreamReader".

      +
    -This help file applies to API documentation generated using the standard doclet.
    +
    +This help file applies to API documentation generated by the standard doclet.
    +
    + diff --git a/java/doc/index-all.html b/java/doc/index-all.html index b770db6a7..22c930572 100644 --- a/java/doc/index-all.html +++ b/java/doc/index-all.html @@ -1,9 +1,21 @@ - + + Index - + + + + + + + + + + +var pathtoroot = "./"; +var useModuleDirectories = true; +loadScripts(document, 'script'); +
    + +
    +
    +
    B C D E F G I N O P S T U Y 
    All Classes All Packages

    B

    -
    bufSize(int, int, int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    +
    bufSize(int, int, int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    Returns the maximum size of the buffer (in bytes) required to hold a JPEG image with the given width, height, and level of chrominance subsampling.
    -
    bufSizeYUV(int, int, int, int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    +
    bufSizeYUV(int, int, int, int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    Returns the size of the buffer (in bytes) required to hold a unified planar YUV image with the given width, height, and level of chrominance subsampling.
    - +

    C

    -
    cf - Variable in class org.libjpegturbo.turbojpeg.TJTransform
    +
    cf - Variable in class org.libjpegturbo.turbojpeg.TJTransform
    Custom filter instance
    -
    close() - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    close() - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    Free the native structures associated with this compressor instance.
    -
    close() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    close() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    Free the native structures associated with this decompressor instance.
    -
    compress(byte[], int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    compress() - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    +
    Compress the packed-pixel or planar YUV source image associated with this + compressor instance and return a buffer containing a JPEG image.
    +
    +
    compress(byte[]) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    Compress the packed-pixel or planar YUV source image associated with this compressor instance and output a JPEG image to the given destination buffer.
    -
    compress(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    compress(byte[], int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    -
    Compress the packed-pixel or planar YUV source image associated with this - compressor instance and return a buffer containing a JPEG image.
    +
    Deprecated. + +
    +
    +
    compress(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    +
    Deprecated. + +
    -
    CS_CMYK - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    CS_CMYK - Static variable in class org.libjpegturbo.turbojpeg.TJ
    CMYK colorspace.
    -
    CS_GRAY - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    CS_GRAY - Static variable in class org.libjpegturbo.turbojpeg.TJ
    Grayscale colorspace.
    -
    CS_RGB - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    CS_RGB - Static variable in class org.libjpegturbo.turbojpeg.TJ
    RGB colorspace.
    -
    CS_YCbCr - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    CS_YCbCr - Static variable in class org.libjpegturbo.turbojpeg.TJ
    YCbCr colorspace.
    -
    CS_YCCK - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    CS_YCCK - Static variable in class org.libjpegturbo.turbojpeg.TJ
    YCCK colorspace.
    -
    customFilter(ShortBuffer, Rectangle, Rectangle, int, int, TJTransform) - Method in interface org.libjpegturbo.turbojpeg.TJCustomFilter
    +
    customFilter(ShortBuffer, Rectangle, Rectangle, int, int, TJTransform) - Method in interface org.libjpegturbo.turbojpeg.TJCustomFilter
    A callback function that can be used to modify the DCT coefficients after they are losslessly transformed but before they are transcoded to a new JPEG image.
    - +

    D

    -
    decompress(byte[], int, int, int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    decompress(byte[], int, int, int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
    Decompress the JPEG source image or decode the planar YUV source image - associated with this decompressor instance and output a packed-pixel - grayscale, RGB, or CMYK image to the given destination buffer.
    +
    -
    decompress(int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    decompress(int[], int, int, int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
    Decompress the JPEG source image or decode the planar YUV source image - associated with this decompressor instance and return a buffer containing - the packed-pixel decompressed image.
    + +
    +
    decompress(int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    +
    Deprecated. + +
    -
    decompress(int[], int, int, int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    decompress(int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
    Decompress the JPEG source image or decode the planar YUV source image - associated with this decompressor instance and output a packed-pixel +
    Deprecated. + +
    +
    +
    decompress(BufferedImage, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    + +
    +
    decompress12(int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    +
    Decompress the 12-bit-per-sample JPEG source image associated with this + decompressor instance and return a buffer containing a 12-bit-per-sample + packed-pixel decompressed image.
    +
    +
    decompress12(short[], int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    +
    Decompress the 12-bit-per-sample JPEG source image associated with this + decompressor instance and output a 12-bit-per-sample packed-pixel grayscale, RGB, or CMYK image to the given destination buffer.
    -
    decompress(BufferedImage, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    decompress16(int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
    Decompress the JPEG source image or decode the planar YUV source image - associated with this decompressor instance and output a packed-pixel - decompressed/decoded image to the given BufferedImage - instance.
    +
    Decompress the 16-bit-per-sample JPEG source image associated with this + decompressor instance and return a buffer containing a 16-bit-per-sample + packed-pixel decompressed image.
    +
    +
    decompress16(short[], int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    +
    Decompress the 16-bit-per-sample lossless JPEG source image associated + with this decompressor instance and output a 16-bit-per-sample + packed-pixel grayscale, RGB, or CMYK image to the given destination + buffer.
    +
    +
    decompress8(byte[], int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    +
    Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + source image associated with this decompressor instance and output an + 8-bit-per-sample packed-pixel grayscale, RGB, or CMYK image to the given + destination buffer.
    -
    decompress(int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    decompress8(int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
    Decompress the JPEG source image or decode the planar YUV source image - associated with this decompressor instance and return a - BufferedImage instance containing the packed-pixel - decompressed/decoded image.
    +
    Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + source image associated with this decompressor instance and return a + BufferedImage instance containing the 8-bit-per-sample + packed-pixel decompressed/decoded image.
    -
    decompressToYUV(YUVImage, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    decompress8(int[], int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
    Decompress the JPEG source image associated with this decompressor - instance into a planar YUV image and store it in the given - YUVImage instance.
    +
    Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + source image associated with this decompressor instance and output an + 8-bit-per-sample packed-pixel grayscale, RGB, or CMYK image to the given + destination buffer.
    -
    decompressToYUV(int, int[], int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    decompress8(int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
    Decompress the JPEG source image associated with this decompressor - instance into a set of Y, U (Cb), and V (Cr) image planes and return a - YUVImage instance containing the decompressed image planes.
    +
    Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + source image associated with this decompressor instance and return a + buffer containing an 8-bit-per-sample packed-pixel decompressed image.
    -
    decompressToYUV(int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    decompress8(BufferedImage) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
    Decompress the JPEG source image associated with this decompressor - instance into a unified planar YUV image and return a YUVImage - instance containing the decompressed image.
    +
    Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + source image associated with this decompressor instance and output an + 8-bit-per-sample packed-pixel decompressed/decoded image to the given + BufferedImage instance.
    +
    +
    decompressToYUV(int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    +
    Decompress the 8-bit-per-sample JPEG source image associated with this + decompressor instance into an 8-bit-per-sample unified planar YUV image + and return a YUVImage instance containing the decompressed image.
    +
    +
    decompressToYUV(int[]) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    +
    Decompress the 8-bit-per-sample JPEG source image associated with this + decompressor instance into a set of 8-bit-per-sample Y, U (Cb), and V (Cr) + image planes and return a YUVImage instance containing the + decompressed image planes.
    +
    +
    decompressToYUV(int, int[], int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    + +
    +
    decompressToYUV(int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    + +
    +
    decompressToYUV(YUVImage) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    +
    Decompress the 8-bit-per-sample JPEG source image associated with this + decompressor instance into an 8-bit-per-sample planar YUV image and store + it in the given YUVImage instance.
    +
    +
    decompressToYUV(YUVImage, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    +
    - +

    E

    -
    encodeYUV(YUVImage, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    encodeYUV(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    +
    Encode the 8-bit-per-sample packed-pixel source image associated with this + compressor instance into an 8-bit-per-sample unified planar YUV image and + return a YUVImage instance containing the encoded image.
    +
    +
    encodeYUV(int[]) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    +
    Encode the 8-bit-per-sample packed-pixel source image associated with this + compressor instance into separate 8-bit-per-sample Y, U (Cb), and V (Cr) + image planes and return a YUVImage instance containing the encoded + image planes.
    +
    +
    encodeYUV(int[], int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    +
    Deprecated. + +
    +
    +
    encodeYUV(int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    -
    Encode the packed-pixel source image associated with this compressor - instance into a planar YUV image and store it in the given - YUVImage instance.
    +
    Deprecated. + +
    -
    encodeYUV(int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    encodeYUV(YUVImage) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    -
    Encode the packed-pixel source image associated with this compressor - instance into a unified planar YUV image and return a YUVImage - instance containing the encoded image.
    +
    Encode the 8-bit-per-sample packed-pixel source image associated with this + compressor instance into an 8-bit-per-sample planar YUV image and store it + in the given YUVImage instance.
    -
    encodeYUV(int[], int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    encodeYUV(YUVImage, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    -
    Encode the packed-pixel source image associated with this compressor - instance into separate Y, U (Cb), and V (Cr) image planes and return a - YUVImage instance containing the encoded image planes.
    +
    Deprecated. + +
    -
    equals(TJScalingFactor) - Method in class org.libjpegturbo.turbojpeg.TJScalingFactor
    +
    equals(TJScalingFactor) - Method in class org.libjpegturbo.turbojpeg.TJScalingFactor
    Returns true or false, depending on whether this instance and other have the same numerator and denominator.
    -
    ERR_FATAL - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    ERR_FATAL - Static variable in class org.libjpegturbo.turbojpeg.TJ
    The error was fatal and non-recoverable.
    -
    ERR_WARNING - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    ERR_WARNING - Static variable in class org.libjpegturbo.turbojpeg.TJ
    The error was non-fatal and recoverable, but the destination image may still be corrupt.
    - +

    F

    -
    finalize() - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    finalize() - Method in class org.libjpegturbo.turbojpeg.TJCompressor
     
    -
    finalize() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    finalize() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
     
    -
    FLAG_ACCURATEDCT - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    FLAG_ACCURATEDCT - Static variable in class org.libjpegturbo.turbojpeg.TJ
    -
    Use the most accurate DCT/IDCT algorithm available.
    -
    -
    FLAG_ARITHMETIC - Static variable in class org.libjpegturbo.turbojpeg.TJ
    -
    -
    Use arithmetic entropy coding in JPEG images generated by compression and - transform operations.
    -
    -
    FLAG_BOTTOMUP - Static variable in class org.libjpegturbo.turbojpeg.TJ
    -
    -
    Rows in the packed-pixel source/destination image are stored in bottom-up - (Windows, OpenGL) order rather than in top-down (X11) order.
    +
    Deprecated. +
    Use TJ.PARAM_FASTDCT instead.
    +
    -
    FLAG_FASTDCT - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    FLAG_BOTTOMUP - Static variable in class org.libjpegturbo.turbojpeg.TJ
    -
    Use the fastest DCT/IDCT algorithm available.
    +
    Deprecated. +
    Use TJ.PARAM_BOTTOMUP instead.
    +
    -
    FLAG_FASTUPSAMPLE - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    FLAG_FASTDCT - Static variable in class org.libjpegturbo.turbojpeg.TJ
    -
    When decompressing an image that was compressed using chrominance - subsampling, use the fastest chrominance upsampling algorithm available.
    +
    Deprecated. +
    Use TJ.PARAM_FASTDCT instead.
    +
    -
    FLAG_LIMITSCANS - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    FLAG_FASTUPSAMPLE - Static variable in class org.libjpegturbo.turbojpeg.TJ
    -
    Limit the number of progressive JPEG scans that the decompression and - transform operations will process.
    +
    Deprecated. + +
    -
    FLAG_LOSSLESS - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    FLAG_LIMITSCANS - Static variable in class org.libjpegturbo.turbojpeg.TJ
    -
    Generate a lossless JPEG image when compressing.
    +
    Deprecated. +
    Use TJ.PARAM_SCANLIMIT instead.
    +
    -
    FLAG_PROGRESSIVE - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    FLAG_PROGRESSIVE - Static variable in class org.libjpegturbo.turbojpeg.TJ
    -
    Use progressive entropy coding in JPEG images generated by compression and - transform operations.
    +
    Deprecated. + +
    -
    FLAG_STOPONWARNING - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    FLAG_STOPONWARNING - Static variable in class org.libjpegturbo.turbojpeg.TJ
    -
    Immediately discontinue the current compression/decompression/transform - operation if a warning (non-fatal error) occurs.
    +
    Deprecated. + +
    - +

    G

    -
    getAlphaOffset(int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    +
    get(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    +
    Get the value of a compression parameter.
    +
    +
    get(int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
    For the given pixel format, returns the number of bytes that the alpha +
    Get the value of a decompression parameter.
    +
    +
    getAlphaOffset(int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    For the given pixel format, returns the number of samples that the alpha component is offset from the start of the pixel.
    -
    getBlueOffset(int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    +
    getBlueOffset(int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    -
    For the given pixel format, returns the number of bytes that the blue +
    For the given pixel format, returns the number of samples that the blue component is offset from the start of the pixel.
    -
    getBuf() - Method in class org.libjpegturbo.turbojpeg.YUVImage
    +
    getBuf() - Method in class org.libjpegturbo.turbojpeg.YUVImage
    Returns the YUV buffer (if this image is stored in a unified buffer rather than separate image planes.)
    -
    getColorspace() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    getColorspace() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
    Returns the colorspace used in the source image (JPEG or YUV) associated - with this decompressor instance.
    +
    Deprecated. +
    Use get(TJ.PARAM_COLORSPACE) + instead.
    +
    -
    getCompressedSize() - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    getCompressedSize() - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    Returns the size of the image (in bytes) generated by the most recent compress operation.
    -
    getDenom() - Method in class org.libjpegturbo.turbojpeg.TJScalingFactor
    +
    getDenom() - Method in class org.libjpegturbo.turbojpeg.TJScalingFactor
    Returns denominator
    -
    getErrorCode() - Method in exception org.libjpegturbo.turbojpeg.TJException
    +
    getErrorCode() - Method in exception org.libjpegturbo.turbojpeg.TJException
    -
    Returns a code (one of TJ.ERR_*) indicating the severity of the +
    Returns a code (one of TJ.ERR_*) indicating the severity of the last error.
    -
    getFlags() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    getGreenOffset(int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    -
    Returns the bitwise OR of one or more of the - flags, such as - TJ.FLAG_PROGRESSIVE and - TJ.FLAG_LOSSLESS, that describe the JPEG image.
    -
    -
    getGreenOffset(int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    -
    -
    For the given pixel format, returns the number of bytes that the green +
    For the given pixel format, returns the number of samples that the green component is offset from the start of the pixel.
    -
    getHeight() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    getHeight() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    Returns the height of the source image (JPEG or YUV) associated with this decompressor instance.
    -
    getHeight() - Method in class org.libjpegturbo.turbojpeg.YUVImage
    +
    getHeight() - Method in class org.libjpegturbo.turbojpeg.YUVImage
    Returns the height of the YUV image (or subregion.)
    -
    getJPEGBuf() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    getJPEGBuf() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    Returns the JPEG buffer associated with this decompressor instance.
    -
    getJPEGSize() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    getJPEGSize() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    Returns the size of the JPEG image (in bytes) associated with this decompressor instance.
    -
    getMCUHeight(int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    +
    getMCUHeight(int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    Returns the MCU block height for the given level of chrominance subsampling.
    -
    getMCUWidth(int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    +
    getMCUWidth(int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    Returns the MCU block width for the given level of chrominance subsampling.
    -
    getNum() - Method in class org.libjpegturbo.turbojpeg.TJScalingFactor
    +
    getNum() - Method in class org.libjpegturbo.turbojpeg.TJScalingFactor
    Returns numerator
    -
    getOffsets() - Method in class org.libjpegturbo.turbojpeg.YUVImage
    +
    getOffsets() - Method in class org.libjpegturbo.turbojpeg.YUVImage
    Returns the offsets (in bytes) of each plane within the planes of a larger YUV image.
    -
    getPad() - Method in class org.libjpegturbo.turbojpeg.YUVImage
    +
    getPad() - Method in class org.libjpegturbo.turbojpeg.YUVImage
    Returns the row alignment (in bytes) of the YUV buffer (if this image is stored in a unified buffer rather than separate image planes.)
    -
    getPixelSize(int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    +
    getPixelSize(int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    -
    Returns the pixel size (in bytes) for the given pixel format.
    +
    Returns the pixel size (in samples) for the given pixel format.
    -
    getPlanes() - Method in class org.libjpegturbo.turbojpeg.YUVImage
    +
    getPlanes() - Method in class org.libjpegturbo.turbojpeg.YUVImage
    Returns the YUV image planes.
    -
    getRedOffset(int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    +
    getRedOffset(int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    -
    For the given pixel format, returns the number of bytes that the red +
    For the given pixel format, returns the number of samples that the red component is offset from the start of the pixel.
    -
    getScaled(int) - Method in class org.libjpegturbo.turbojpeg.TJScalingFactor
    +
    getScaled(int) - Method in class org.libjpegturbo.turbojpeg.TJScalingFactor
    Returns the scaled value of dimension.
    -
    getScaledHeight(int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    getScaledHeight(int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
    Returns the height of the largest scaled-down image that the TurboJPEG - decompressor can generate without exceeding the desired image width and - height.
    +
    Deprecated. + +
    -
    getScaledWidth(int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    getScaledWidth(int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
    Returns the width of the largest scaled-down image that the TurboJPEG - decompressor can generate without exceeding the desired image width and - height.
    +
    Deprecated. + +
    -
    getScalingFactors() - Static method in class org.libjpegturbo.turbojpeg.TJ
    +
    getScalingFactors() - Static method in class org.libjpegturbo.turbojpeg.TJ
    Returns a list of fractional scaling factors that the JPEG decompressor supports.
    -
    getSize() - Method in class org.libjpegturbo.turbojpeg.YUVImage
    +
    getSize() - Method in class org.libjpegturbo.turbojpeg.YUVImage
    Returns the size (in bytes) of the YUV buffer (if this image is stored in a unified buffer rather than separate image planes.)
    -
    getStrides() - Method in class org.libjpegturbo.turbojpeg.YUVImage
    +
    getStrides() - Method in class org.libjpegturbo.turbojpeg.YUVImage
    Returns the number of bytes per row of each plane in the YUV image.
    -
    getSubsamp() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    getSubsamp() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
    Returns the level of chrominance subsampling used in the source image - (JPEG or YUV) associated with this decompressor instance.
    +
    Deprecated. +
    Use get(TJ.PARAM_SUBSAMP) + instead.
    +
    -
    getSubsamp() - Method in class org.libjpegturbo.turbojpeg.YUVImage
    +
    getSubsamp() - Method in class org.libjpegturbo.turbojpeg.YUVImage
    Returns the level of chrominance subsampling used in the YUV image.
    -
    getTransformedSizes() - Method in class org.libjpegturbo.turbojpeg.TJTransformer
    +
    getTransformedSizes() - Method in class org.libjpegturbo.turbojpeg.TJTransformer
    Returns an array containing the sizes of the transformed JPEG images (in bytes) generated by the most recent transform operation.
    -
    getWidth() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    getWidth() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    Returns the width of the source image (JPEG or YUV) associated with this decompressor instance.
    -
    getWidth() - Method in class org.libjpegturbo.turbojpeg.YUVImage
    +
    getWidth() - Method in class org.libjpegturbo.turbojpeg.YUVImage
    Returns the width of the YUV image (or subregion.)
    - - - -

    H

    -
    -
    handle - Variable in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
     
    -
    handle - Variable in class org.libjpegturbo.turbojpeg.YUVImage
    -
     
    -
    - +

    I

    -
    isOne() - Method in class org.libjpegturbo.turbojpeg.TJScalingFactor
    +
    isOne() - Method in class org.libjpegturbo.turbojpeg.TJScalingFactor
    Returns true or false, depending on whether this instance is equal to 1/1.
    - - - -

    J

    -
    -
    jpegBuf - Variable in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
     
    -
    jpegBufSize - Variable in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
     
    -
    jpegColorspace - Variable in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
     
    -
    jpegFlags - Variable in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
     
    -
    jpegHeight - Variable in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
     
    -
    jpegSubsamp - Variable in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
     
    -
    jpegWidth - Variable in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
     
    -
    - +

    N

    -
    NUMCS - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    NUMCS - Static variable in class org.libjpegturbo.turbojpeg.TJ
    The number of JPEG colorspaces
    -
    NUMERR - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    NUMERR - Static variable in class org.libjpegturbo.turbojpeg.TJ
    The number of error codes
    -
    NUMOP - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    +
    NUMOP - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    The number of lossless transform operations
    -
    NUMPF - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    NUMPF - Static variable in class org.libjpegturbo.turbojpeg.TJ
    The number of pixel formats
    -
    NUMSAMP - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    NUMSAMP - Static variable in class org.libjpegturbo.turbojpeg.TJ
    The number of chrominance subsampling options
    - +

    O

    -
    op - Variable in class org.libjpegturbo.turbojpeg.TJTransform
    +
    op - Variable in class org.libjpegturbo.turbojpeg.TJTransform
    -
    Transform operation (one of OP_*)
    +
    Transform operation (one of OP_*)
    -
    OP_HFLIP - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    +
    OP_HFLIP - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    Flip (mirror) image horizontally.
    -
    OP_NONE - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    +
    OP_NONE - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    Do not transform the position of the image pixels.
    -
    OP_ROT180 - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    +
    OP_ROT180 - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    Rotate image 180 degrees.
    -
    OP_ROT270 - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    +
    OP_ROT270 - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    Rotate image counter-clockwise by 90 degrees.
    -
    OP_ROT90 - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    +
    OP_ROT90 - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    Rotate image clockwise by 90 degrees.
    -
    OP_TRANSPOSE - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    +
    OP_TRANSPOSE - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    Transpose image (flip/mirror along upper left to lower right axis).
    -
    OP_TRANSVERSE - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    +
    OP_TRANSVERSE - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    Transverse transpose image (flip/mirror along upper right to lower left axis).
    -
    OP_VFLIP - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    +
    OP_VFLIP - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    Flip (mirror) image vertically.
    -
    OPT_ARITHMETIC - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    +
    OPT_ARITHMETIC - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    This option will enable arithmetic entropy coding in the JPEG image generated by this particular transform.
    -
    OPT_COPYNONE - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    +
    OPT_COPYNONE - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    -
    This option will prevent TJTransformer.transform() from copying any extra markers (including EXIF +
    This option will prevent TJTransformer.transform() from copying any extra markers (including EXIF and ICC profile data) from the source image to the destination image.
    -
    OPT_CROP - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    +
    OPT_CROP - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    This option will enable lossless cropping.
    -
    OPT_GRAY - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    +
    OPT_GRAY - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    This option will discard the color data in the source image and produce a grayscale destination image.
    -
    OPT_NOOUTPUT - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    +
    OPT_NOOUTPUT - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    -
    This option will prevent TJTransformer.transform() from outputting a JPEG image for this +
    This option will prevent TJTransformer.transform() from outputting a JPEG image for this particular transform.
    -
    OPT_PERFECT - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    +
    OPT_OPTIMIZE - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    +
    +
    This option will enable optimized baseline entropy coding in the JPEG + image generated by this particular transform.
    +
    +
    OPT_PERFECT - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    -
    This option will cause TJTransformer.transform() to throw an exception if the transform is not +
    This option will cause TJTransformer.transform() to throw an exception if the transform is not perfect.
    -
    OPT_PROGRESSIVE - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    +
    OPT_PROGRESSIVE - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    This option will enable progressive entropy coding in the JPEG image generated by this particular transform.
    -
    OPT_TRIM - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    +
    OPT_TRIM - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
    This option will discard any partial MCU blocks that cannot be transformed.
    -
    options - Variable in class org.libjpegturbo.turbojpeg.TJTransform
    +
    options - Variable in class org.libjpegturbo.turbojpeg.TJTransform
    Transform options (bitwise OR of one or more of - OPT_*)
    + OPT_*)
    -
    org.libjpegturbo.turbojpeg - package org.libjpegturbo.turbojpeg
    +
    org.libjpegturbo.turbojpeg - package org.libjpegturbo.turbojpeg
     
    - +

    P

    -
    PF_ABGR - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    PARAM_ARITHMETIC - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    Arithmetic entropy coding
    +
    +
    PARAM_BOTTOMUP - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    Row order in packed-pixel source/destination images
    +
    +
    PARAM_COLORSPACE - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    JPEG colorspace
    +
    +
    PARAM_DENSITYUNITS - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    JPEG pixel density units
    +
    +
    PARAM_FASTDCT - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    DCT/IDCT algorithm [lossy compression and decompression]
    +
    +
    PARAM_FASTUPSAMPLE - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    Chrominance upsampling algorithm [lossy decompression only]
    +
    +
    PARAM_JPEGHEIGHT - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    JPEG height (in pixels) [decompression only, read-only]
    +
    +
    PARAM_JPEGWIDTH - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    JPEG width (in pixels) [decompression only, read-only]
    +
    +
    PARAM_LOSSLESS - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    Lossless JPEG
    +
    +
    PARAM_LOSSLESSPSV - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    Lossless JPEG predictor selection value (PSV)
    +
    +
    PARAM_LOSSLESSPT - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    Lossless JPEG point transform (Pt)
    +
    +
    PARAM_OPTIMIZE - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    Optimized baseline entropy coding [lossy compression only]
    +
    +
    PARAM_PRECISION - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    JPEG data precision (bits per sample) [decompression only, read-only]
    +
    +
    PARAM_PROGRESSIVE - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    Progressive entropy coding
    +
    +
    PARAM_QUALITY - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    Perceptual quality of lossy JPEG images [compression only]
    +
    +
    PARAM_RESTARTBLOCKS - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    JPEG restart marker interval in MCU blocks (lossy) or samples (lossless) + [compression only]
    +
    +
    PARAM_RESTARTROWS - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    JPEG restart marker interval in MCU rows (lossy) or sample rows (lossless) + [compression only]
    +
    +
    PARAM_SCANLIMIT - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    Progressive JPEG scan limit for lossy JPEG images [decompression, lossless + transformation]
    +
    +
    PARAM_STOPONWARNING - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    Error handling behavior
    +
    +
    PARAM_SUBSAMP - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    Chrominance subsampling level
    +
    +
    PARAM_XDENSITY - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    JPEG horizontal pixel density
    +
    +
    PARAM_YDENSITY - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    +
    JPEG vertical pixel density
    +
    +
    PF_ABGR - Static variable in class org.libjpegturbo.turbojpeg.TJ
    ABGR pixel format.
    -
    PF_ARGB - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    PF_ARGB - Static variable in class org.libjpegturbo.turbojpeg.TJ
    ARGB pixel format.
    -
    PF_BGR - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    PF_BGR - Static variable in class org.libjpegturbo.turbojpeg.TJ
    BGR pixel format.
    -
    PF_BGRA - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    PF_BGRA - Static variable in class org.libjpegturbo.turbojpeg.TJ
    BGRA pixel format.
    -
    PF_BGRX - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    PF_BGRX - Static variable in class org.libjpegturbo.turbojpeg.TJ
    BGRX pixel format.
    -
    PF_CMYK - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    PF_CMYK - Static variable in class org.libjpegturbo.turbojpeg.TJ
    CMYK pixel format.
    -
    PF_GRAY - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    PF_GRAY - Static variable in class org.libjpegturbo.turbojpeg.TJ
    Grayscale pixel format.
    -
    PF_RGB - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    PF_RGB - Static variable in class org.libjpegturbo.turbojpeg.TJ
    RGB pixel format.
    -
    PF_RGBA - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    PF_RGBA - Static variable in class org.libjpegturbo.turbojpeg.TJ
    RGBA pixel format.
    -
    PF_RGBX - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    PF_RGBX - Static variable in class org.libjpegturbo.turbojpeg.TJ
    RGBX pixel format.
    -
    PF_XBGR - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    PF_XBGR - Static variable in class org.libjpegturbo.turbojpeg.TJ
    XBGR pixel format.
    -
    PF_XRGB - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    PF_XRGB - Static variable in class org.libjpegturbo.turbojpeg.TJ
    XRGB pixel format.
    -
    planeHeight(int, int, int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    +
    planeHeight(int, int, int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    Returns the plane height of a YUV image plane with the given parameters.
    -
    planeSizeYUV(int, int, int, int, int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    +
    planeSizeYUV(int, int, int, int, int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    Returns the size of the buffer (in bytes) required to hold a YUV image plane with the given parameters.
    -
    planeWidth(int, int, int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    +
    planeWidth(int, int, int) - Static method in class org.libjpegturbo.turbojpeg.TJ
    Returns the plane width of a YUV image plane with the given parameters.
    - +

    S

    -
    SAMP_411 - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    SAMP_411 - Static variable in class org.libjpegturbo.turbojpeg.TJ
    4:1:1 chrominance subsampling.
    -
    SAMP_420 - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    SAMP_420 - Static variable in class org.libjpegturbo.turbojpeg.TJ
    4:2:0 chrominance subsampling.
    -
    SAMP_422 - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    SAMP_422 - Static variable in class org.libjpegturbo.turbojpeg.TJ
    4:2:2 chrominance subsampling.
    -
    SAMP_440 - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    SAMP_440 - Static variable in class org.libjpegturbo.turbojpeg.TJ
    4:4:0 chrominance subsampling.
    -
    SAMP_444 - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    SAMP_444 - Static variable in class org.libjpegturbo.turbojpeg.TJ
    4:4:4 chrominance subsampling (no chrominance subsampling).
    -
    SAMP_GRAY - Static variable in class org.libjpegturbo.turbojpeg.TJ
    +
    SAMP_GRAY - Static variable in class org.libjpegturbo.turbojpeg.TJ
    Grayscale.
    -
    setBuf(byte[][], int[], int, int[], int, int) - Method in class org.libjpegturbo.turbojpeg.YUVImage
    +
    SAMP_UNKNOWN - Static variable in class org.libjpegturbo.turbojpeg.TJ
    -
    Assign a set of image planes to this YUVImage instance.
    +
    Unknown subsampling.
    -
    setBuf(byte[], int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.YUVImage
    +
    set(int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    -
    Assign a unified buffer to this YUVImage instance.
    +
    Set the value of a compression parameter.
    -
    setJPEGQuality(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    set(int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
    Set the JPEG image quality level for subsequent compress operations.
    +
    Set the value of a decompression parameter.
    -
    setSourceImage(byte[], int, int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    setBuf(byte[][], int[], int, int[], int, int) - Method in class org.libjpegturbo.turbojpeg.YUVImage
    -
    Associate a packed-pixel RGB, grayscale, or CMYK source image with this - compressor instance.
    +
    Assign a set of image planes to this YUVImage instance.
    -
    setSourceImage(BufferedImage, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    setBuf(byte[], int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.YUVImage
    -
    Associate a packed-pixel RGB or grayscale source image with this - compressor instance.
    +
    Assign a unified buffer to this YUVImage instance.
    -
    setSourceImage(YUVImage) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    setCroppingRegion(Rectangle) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
    Associate a planar YUV source image with this compressor instance.
    +
    Set the cropping region for partially decompressing a lossy JPEG image + into a packed-pixel image.
    +
    +
    setJPEGQuality(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    +
    Deprecated. +
    Use + set(TJ.PARAM_QUALITY, ...) instead.
    +
    -
    setSourceImage(byte[], int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    setScalingFactor(TJScalingFactor) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    +
    Set the scaling factor for subsequent lossy decompression operations.
    +
    +
    setSourceImage(byte[], int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    Associate the JPEG image or "abbreviated table specification" (AKA "tables-only") datastream of length imageSize bytes stored in jpegImage with this decompressor instance.
    -
    setSourceImage(YUVImage) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    setSourceImage(byte[], int, int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    +
    Associate an 8-bit-per-sample packed-pixel RGB, grayscale, or CMYK source + image with this compressor instance.
    +
    +
    setSourceImage(BufferedImage, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    +
    Associate an 8-bit-per-pixel packed-pixel RGB or grayscale source image + with this compressor instance.
    +
    +
    setSourceImage(YUVImage) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    +
    Associate an 8-bit-per-sample planar YUV source image with this compressor + instance.
    +
    +
    setSourceImage(YUVImage) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
    Associate the specified planar YUV source image with this decompressor instance.
    -
    setSubsamp(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    setSourceImage12(short[], int, int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    +
    Associate a 12-bit-per-sample packed-pixel RGB, grayscale, or CMYK source + image with this compressor instance.
    +
    +
    setSourceImage16(short[], int, int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    +
    +
    Associate a 16-bit-per-sample packed-pixel RGB, grayscale, or CMYK source + image with this compressor instance.
    +
    +
    setSubsamp(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
    -
    Set the level of chrominance subsampling for subsequent compress/encode - operations.
    +
    Deprecated. +
    Use + set(TJ.PARAM_SUBSAMP, ...) instead.
    +
    - +

    T

    -
    TJ - Class in org.libjpegturbo.turbojpeg
    +
    TJ - Class in org.libjpegturbo.turbojpeg
    TurboJPEG utility class (cannot be instantiated)
    -
    TJCompressor - Class in org.libjpegturbo.turbojpeg
    +
    TJCompressor - Class in org.libjpegturbo.turbojpeg
    TurboJPEG compressor
    -
    TJCompressor() - Constructor for class org.libjpegturbo.turbojpeg.TJCompressor
    +
    TJCompressor() - Constructor for class org.libjpegturbo.turbojpeg.TJCompressor
    Create a TurboJPEG compressor instance.
    -
    TJCompressor(byte[], int, int, int, int, int, int) - Constructor for class org.libjpegturbo.turbojpeg.TJCompressor
    +
    TJCompressor(byte[], int, int, int, int, int, int) - Constructor for class org.libjpegturbo.turbojpeg.TJCompressor
    -
    Create a TurboJPEG compressor instance and associate the packed-pixel - source image stored in srcImage with the newly created - instance.
    +
    Create a TurboJPEG compressor instance and associate the 8-bit-per-sample + packed-pixel source image stored in srcImage with the newly + created instance.
    -
    TJCompressor(BufferedImage, int, int, int, int) - Constructor for class org.libjpegturbo.turbojpeg.TJCompressor
    +
    TJCompressor(BufferedImage, int, int, int, int) - Constructor for class org.libjpegturbo.turbojpeg.TJCompressor
    -
    Create a TurboJPEG compressor instance and associate the packed-pixel - source image stored in srcImage with the newly created - instance.
    +
    Create a TurboJPEG compressor instance and associate the 8-bit-per-sample + packed-pixel source image stored in srcImage with the newly + created instance.
    -
    TJCustomFilter - Interface in org.libjpegturbo.turbojpeg
    +
    TJCustomFilter - Interface in org.libjpegturbo.turbojpeg
    Custom filter callback interface
    -
    TJDecompressor - Class in org.libjpegturbo.turbojpeg
    +
    TJDecompressor - Class in org.libjpegturbo.turbojpeg
    TurboJPEG decompressor
    -
    TJDecompressor() - Constructor for class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    TJDecompressor() - Constructor for class org.libjpegturbo.turbojpeg.TJDecompressor
    Create a TurboJPEG decompresssor instance.
    -
    TJDecompressor(byte[]) - Constructor for class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    TJDecompressor(byte[]) - Constructor for class org.libjpegturbo.turbojpeg.TJDecompressor
    Create a TurboJPEG decompressor instance and associate the JPEG source image or "abbreviated table specification" (AKA "tables-only") datastream stored in jpegImage with the newly created instance.
    -
    TJDecompressor(byte[], int) - Constructor for class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    TJDecompressor(byte[], int) - Constructor for class org.libjpegturbo.turbojpeg.TJDecompressor
    Create a TurboJPEG decompressor instance and associate the JPEG source image or "abbreviated table specification" (AKA "tables-only") datastream of length imageSize bytes stored in jpegImage with the newly created instance.
    -
    TJDecompressor(YUVImage) - Constructor for class org.libjpegturbo.turbojpeg.TJDecompressor
    +
    TJDecompressor(YUVImage) - Constructor for class org.libjpegturbo.turbojpeg.TJDecompressor
    -
    Create a TurboJPEG decompressor instance and associate the planar YUV - source image stored in yuvImage with the newly created - instance.
    +
    Create a TurboJPEG decompressor instance and associate the + 8-bit-per-sample planar YUV source image stored in yuvImage + with the newly created instance.
    -
    TJException - Exception in org.libjpegturbo.turbojpeg
    +
    TJException - Exception in org.libjpegturbo.turbojpeg
     
    -
    TJException() - Constructor for exception org.libjpegturbo.turbojpeg.TJException
    +
    TJException() - Constructor for exception org.libjpegturbo.turbojpeg.TJException
     
    -
    TJException(String, Throwable) - Constructor for exception org.libjpegturbo.turbojpeg.TJException
    +
    TJException(String) - Constructor for exception org.libjpegturbo.turbojpeg.TJException
     
    -
    TJException(String) - Constructor for exception org.libjpegturbo.turbojpeg.TJException
    +
    TJException(String, int) - Constructor for exception org.libjpegturbo.turbojpeg.TJException
     
    -
    TJException(String, int) - Constructor for exception org.libjpegturbo.turbojpeg.TJException
    +
    TJException(String, Throwable) - Constructor for exception org.libjpegturbo.turbojpeg.TJException
     
    -
    TJException(Throwable) - Constructor for exception org.libjpegturbo.turbojpeg.TJException
    +
    TJException(Throwable) - Constructor for exception org.libjpegturbo.turbojpeg.TJException
     
    -
    TJScalingFactor - Class in org.libjpegturbo.turbojpeg
    +
    TJScalingFactor - Class in org.libjpegturbo.turbojpeg
    Fractional scaling factor
    -
    TJScalingFactor(int, int) - Constructor for class org.libjpegturbo.turbojpeg.TJScalingFactor
    +
    TJScalingFactor(int, int) - Constructor for class org.libjpegturbo.turbojpeg.TJScalingFactor
    Create a TurboJPEG scaling factor instance.
    -
    TJTransform - Class in org.libjpegturbo.turbojpeg
    +
    TJTransform - Class in org.libjpegturbo.turbojpeg
    Lossless transform parameters
    -
    TJTransform() - Constructor for class org.libjpegturbo.turbojpeg.TJTransform
    +
    TJTransform() - Constructor for class org.libjpegturbo.turbojpeg.TJTransform
    Create a new lossless transform instance.
    -
    TJTransform(int, int, int, int, int, int, TJCustomFilter) - Constructor for class org.libjpegturbo.turbojpeg.TJTransform
    +
    TJTransform(int, int, int, int, int, int, TJCustomFilter) - Constructor for class org.libjpegturbo.turbojpeg.TJTransform
    Create a new lossless transform instance with the given parameters.
    -
    TJTransform(Rectangle, int, int, TJCustomFilter) - Constructor for class org.libjpegturbo.turbojpeg.TJTransform
    +
    TJTransform(Rectangle, int, int, TJCustomFilter) - Constructor for class org.libjpegturbo.turbojpeg.TJTransform
    Create a new lossless transform instance with the given parameters.
    -
    TJTransformer - Class in org.libjpegturbo.turbojpeg
    +
    TJTransformer - Class in org.libjpegturbo.turbojpeg
    TurboJPEG lossless transformer
    -
    TJTransformer() - Constructor for class org.libjpegturbo.turbojpeg.TJTransformer
    +
    TJTransformer() - Constructor for class org.libjpegturbo.turbojpeg.TJTransformer
    Create a TurboJPEG lossless transformer instance.
    -
    TJTransformer(byte[]) - Constructor for class org.libjpegturbo.turbojpeg.TJTransformer
    +
    TJTransformer(byte[]) - Constructor for class org.libjpegturbo.turbojpeg.TJTransformer
    Create a TurboJPEG lossless transformer instance and associate the JPEG source image stored in jpegImage with the newly created instance.
    -
    TJTransformer(byte[], int) - Constructor for class org.libjpegturbo.turbojpeg.TJTransformer
    +
    TJTransformer(byte[], int) - Constructor for class org.libjpegturbo.turbojpeg.TJTransformer
    Create a TurboJPEG lossless transformer instance and associate the JPEG source image of length imageSize bytes stored in jpegImage with the newly created instance.
    -
    transform(byte[][], TJTransform[], int) - Method in class org.libjpegturbo.turbojpeg.TJTransformer
    +
    transform(byte[][], TJTransform[]) - Method in class org.libjpegturbo.turbojpeg.TJTransformer
    Losslessly transform the JPEG source image associated with this transformer instance into one or more JPEG images stored in the given destination buffers.
    -
    transform(TJTransform[], int) - Method in class org.libjpegturbo.turbojpeg.TJTransformer
    +
    transform(byte[][], TJTransform[], int) - Method in class org.libjpegturbo.turbojpeg.TJTransformer
    +
    + +
    +
    transform(TJTransform[]) - Method in class org.libjpegturbo.turbojpeg.TJTransformer
    Losslessly transform the JPEG source image associated with this - transformer instance and return an array of TJDecompressor + transformer instance and return an array of TJDecompressor instances, each of which has a transformed JPEG image associated with it.
    +
    transform(TJTransform[], int) - Method in class org.libjpegturbo.turbojpeg.TJTransformer
    +
    + +
    - + -

    Y

    +

    U

    -
    yuvAlign - Variable in class org.libjpegturbo.turbojpeg.YUVImage
    -
     
    -
    yuvHeight - Variable in class org.libjpegturbo.turbojpeg.YUVImage
    -
     
    -
    yuvImage - Variable in class org.libjpegturbo.turbojpeg.TJDecompressor
    -
     
    -
    YUVImage - Class in org.libjpegturbo.turbojpeg
    +
    UNCROPPED - Static variable in class org.libjpegturbo.turbojpeg.TJ
    -
    This class encapsulates a planar YUV image and the metadata - associated with it.
    +
    A java.awt.Rectangle instance that specifies no cropping
    -
    YUVImage(int, int[], int, int) - Constructor for class org.libjpegturbo.turbojpeg.YUVImage
    +
    UNSCALED - Static variable in class org.libjpegturbo.turbojpeg.TJ
    -
    Create a new YUVImage instance backed by separate image - planes, and allocate memory for the image planes.
    +
    A TJScalingFactor instance that specifies a scaling factor of 1/1 + (no scaling)
    -
    YUVImage(int, int, int, int) - Constructor for class org.libjpegturbo.turbojpeg.YUVImage
    +
    + + + +

    Y

    +
    +
    YUVImage - Class in org.libjpegturbo.turbojpeg
    -
    Create a new YUVImage instance backed by a unified buffer, - and allocate memory for the buffer.
    +
    This class encapsulates a planar YUV image and the metadata + associated with it.
    -
    YUVImage(byte[][], int[], int, int[], int, int) - Constructor for class org.libjpegturbo.turbojpeg.YUVImage
    +
    YUVImage(byte[][], int[], int, int[], int, int) - Constructor for class org.libjpegturbo.turbojpeg.YUVImage
    Create a new YUVImage instance from a set of existing image planes.
    -
    YUVImage(byte[], int, int, int, int) - Constructor for class org.libjpegturbo.turbojpeg.YUVImage
    +
    YUVImage(byte[], int, int, int, int) - Constructor for class org.libjpegturbo.turbojpeg.YUVImage
    Create a new YUVImage instance from an existing unified buffer.
    -
    yuvOffsets - Variable in class org.libjpegturbo.turbojpeg.YUVImage
    -
     
    -
    yuvPlanes - Variable in class org.libjpegturbo.turbojpeg.YUVImage
    -
     
    -
    yuvStrides - Variable in class org.libjpegturbo.turbojpeg.YUVImage
    -
     
    -
    yuvSubsamp - Variable in class org.libjpegturbo.turbojpeg.YUVImage
    -
     
    -
    yuvWidth - Variable in class org.libjpegturbo.turbojpeg.YUVImage
    -
     
    +
    YUVImage(int, int[], int, int) - Constructor for class org.libjpegturbo.turbojpeg.YUVImage
    +
    +
    Create a new YUVImage instance backed by separate image + planes, and allocate memory for the image planes.
    +
    +
    YUVImage(int, int, int, int) - Constructor for class org.libjpegturbo.turbojpeg.YUVImage
    +
    +
    Create a new YUVImage instance backed by a unified buffer, + and allocate memory for the buffer.
    +
    -B C D E F G H I J N O P S T Y  +B C D E F G I N O P S T U Y 
    All Classes All Packages +
    + diff --git a/java/doc/index.html b/java/doc/index.html index 4e2107549..cae2fdd85 100644 --- a/java/doc/index.html +++ b/java/doc/index.html @@ -1,71 +1,23 @@ - + + Generated Documentation (Untitled) - + + + + + - - - - +<body> +<main role="main"> <noscript> -<div>JavaScript is disabled on your browser.</div> +<p>JavaScript is disabled on your browser.</p> </noscript> -<h2>Frame Alert</h2> -<p>This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client. Link to <a href="org/libjpegturbo/turbojpeg/package-summary.html">Non-frame version</a>.</p> - - +

    org/libjpegturbo/turbojpeg/package-summary.html

    + + diff --git a/java/doc/jquery-ui.overrides.css b/java/doc/jquery-ui.overrides.css new file mode 100644 index 000000000..facf852c2 --- /dev/null +++ b/java/doc/jquery-ui.overrides.css @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +.ui-state-active, +.ui-widget-content .ui-state-active, +.ui-widget-header .ui-state-active, +a.ui-button:active, +.ui-button:active, +.ui-button.ui-state-active:hover { + /* Overrides the color of selection used in jQuery UI */ + background: #F8981D; + border: 1px solid #F8981D; +} diff --git a/java/doc/jquery/external/jquery/jquery.js b/java/doc/jquery/external/jquery/jquery.js new file mode 100644 index 000000000..50937333b --- /dev/null +++ b/java/doc/jquery/external/jquery/jquery.js @@ -0,0 +1,10872 @@ +/*! + * jQuery JavaScript Library v3.5.1 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2020-05-04T22:49Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var flat = arr.flat ? function( array ) { + return arr.flat.call( array ); +} : function( array ) { + return arr.concat.apply( [], array ); +}; + + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + return typeof obj === "function" && typeof obj.nodeType !== "number"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + +var document = window.document; + + + + var preservedScriptAttributes = { + type: true, + src: true, + nonce: true, + noModule: true + }; + + function DOMEval( code, node, doc ) { + doc = doc || document; + + var i, val, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.5.1", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + even: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return ( i + 1 ) % 2; + } ) ); + }, + + odd: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return i % 2; + } ) ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + copy = options[ name ]; + + // Prevent Object.prototype pollution + // Prevent never-ending loop + if ( name === "__proto__" || target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + src = target[ name ]; + + // Ensure proper type for the source value + if ( copyIsArray && !Array.isArray( src ) ) { + clone = []; + } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { + clone = {}; + } else { + clone = src; + } + copyIsArray = false; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a provided context; falls back to the global one + // if not specified. + globalEval: function( code, options, doc ) { + DOMEval( code, { nonce: options && options.nonce }, doc ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return flat( ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( _i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.5 + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://js.foundation/ + * + * Date: 2020-03-14 + */ +( function( window ) { +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + nonnativeSelectorCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ( {} ).hasOwnProperty, + arr = [], + pop = arr.pop, + pushNative = arr.push, + push = arr.push, + slice = arr.slice, + + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[ i ] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" + + "ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram + identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + + "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + + // "Attribute values must be CSS identifiers [capture 5] + // or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + + whitespace + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + + "*" ), + rdescend = new RegExp( whitespace + "|>" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rhtml = /HTML$/i, + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ), + funescape = function( escape, nonHex ) { + var high = "0x" + escape.slice( 1 ) - 0x10000; + + return nonHex ? + + // Strip the backslash prefix from a non-hex escape sequence + nonHex : + + // Replace a hexadecimal escape sequence with the encoded Unicode code point + // Support: IE <=11+ + // For values outside the Basic Multilingual Plane (BMP), manually construct a + // surrogate pair + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + inDisabledFieldset = addCombinator( + function( elem ) { + return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + ( arr = slice.call( preferredDoc.childNodes ) ), + preferredDoc.childNodes + ); + + // Support: Android<4.0 + // Detect silently failing push.apply + // eslint-disable-next-line no-unused-expressions + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + pushNative.apply( target, slice.call( els ) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + + // Can't trust NodeList.length + while ( ( target[ j++ ] = els[ i++ ] ) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + setDocument( context ); + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) { + + // ID selector + if ( ( m = match[ 1 ] ) ) { + + // Document context + if ( nodeType === 9 ) { + if ( ( elem = context.getElementById( m ) ) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && ( elem = newContext.getElementById( m ) ) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[ 2 ] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !nonnativeSelectorCache[ selector + " " ] && + ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) && + + // Support: IE 8 only + // Exclude object elements + ( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) { + + newSelector = selector; + newContext = context; + + // qSA considers elements outside a scoping root when evaluating child or + // descendant combinators, which is not what we want. + // In such cases, we work around the behavior by prefixing every selector in the + // list with an ID selector referencing the scope context. + // The technique has to be used as well when a leading combinator is used + // as such selectors are not recognized by querySelectorAll. + // Thanks to Andrew Dupont for this technique. + if ( nodeType === 1 && + ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) { + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + + // We can use :scope instead of the ID hack if the browser + // supports it & if we're not changing the context. + if ( newContext !== context || !support.scope ) { + + // Capture the context ID, setting it first if necessary + if ( ( nid = context.getAttribute( "id" ) ) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", ( nid = expando ) ); + } + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + + toSelector( groups[ i ] ); + } + newSelector = groups.join( "," ); + } + + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + nonnativeSelectorCache( selector, true ); + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return ( cache[ key + " " ] = value ); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement( "fieldset" ); + + try { + return !!fn( el ); + } catch ( e ) { + return false; + } finally { + + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split( "|" ), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[ i ] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( ( cur = cur.nextSibling ) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return ( name === "input" || name === "button" ) && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + inDisabledFieldset( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction( function( argument ) { + argument = +argument; + return markFunction( function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ ( j = matchIndexes[ i ] ) ] ) { + seed[ j ] = !( matches[ j ] = seed[ j ] ); + } + } + } ); + } ); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + var namespace = elem.namespaceURI, + docElem = ( elem.ownerDocument || elem ).documentElement; + + // Support: IE <=8 + // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes + // https://bugs.jquery.com/ticket/4833 + return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9 - 11+, Edge 12 - 18+ + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( preferredDoc != document && + ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + // Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only, + // Safari 4 - 5 only, Opera <=11.6 - 12.x only + // IE/Edge & older browsers don't support the :scope pseudo-class. + // Support: Safari 6.0 only + // Safari 6.0 supports :scope but it's an alias of :root there. + support.scope = assert( function( el ) { + docElem.appendChild( el ).appendChild( document.createElement( "div" ) ); + return typeof el.querySelectorAll !== "undefined" && + !el.querySelectorAll( ":scope fieldset div" ).length; + } ); + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert( function( el ) { + el.className = "i"; + return !el.getAttribute( "className" ); + } ); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert( function( el ) { + el.appendChild( document.createComment( "" ) ); + return !el.getElementsByTagName( "*" ).length; + } ); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert( function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + } ); + + // ID filter and find + if ( support.getById ) { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute( "id" ) === attrId; + }; + }; + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode( "id" ); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( ( elem = elems[ i++ ] ) ) { + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find[ "TAG" ] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) { + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert( function( el ) { + + var input; + + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll( "[selected]" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push( "~=" ); + } + + // Support: IE 11+, Edge 15 - 18+ + // IE 11/Edge don't find elements on a `[name='']` query in some cases. + // Adding a temporary attribute to the document before the selection works + // around the issue. + // Interestingly, IE 10 & older don't seem to have the issue. + input = document.createElement( "input" ); + input.setAttribute( "name", "" ); + el.appendChild( input ); + if ( !el.querySelectorAll( "[name='']" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + + whitespace + "*(?:''|\"\")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll( ":checked" ).length ) { + rbuggyQSA.push( ":checked" ); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push( ".#.+[+~]" ); + } + + // Support: Firefox <=3.6 - 5 only + // Old Firefox doesn't throw on a badly-escaped identifier. + el.querySelectorAll( "\\\f" ); + rbuggyQSA.push( "[\\r\\n\\f]" ); + } ); + + assert( function( el ) { + el.innerHTML = "" + + ""; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement( "input" ); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll( "[name=d]" ).length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll( ":enabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: Opera 10 - 11 only + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll( "*,:x" ); + rbuggyQSA.push( ",.*:" ); + } ); + } + + if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector ) ) ) ) { + + assert( function( el ) { + + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + } ); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + ) ); + } : + function( a, b ) { + if ( b ) { + while ( ( b = b.parentNode ) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) { + + // Choose the first element that is related to our preferred document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( a == document || a.ownerDocument == preferredDoc && + contains( preferredDoc, a ) ) { + return -1; + } + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( b == document || b.ownerDocument == preferredDoc && + contains( preferredDoc, b ) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + return a == document ? -1 : + b == document ? 1 : + /* eslint-enable eqeqeq */ + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( ( cur = cur.parentNode ) ) { + ap.unshift( cur ); + } + cur = b; + while ( ( cur = cur.parentNode ) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[ i ] === bp[ i ] ) { + i++; + } + + return i ? + + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[ i ], bp[ i ] ) : + + // Otherwise nodes in our document sort first + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + ap[ i ] == preferredDoc ? -1 : + bp[ i ] == preferredDoc ? 1 : + /* eslint-enable eqeqeq */ + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + setDocument( elem ); + + if ( support.matchesSelector && documentIsHTML && + !nonnativeSelectorCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch ( e ) { + nonnativeSelectorCache( expr, true ); + } + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( context.ownerDocument || context ) != document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( elem.ownerDocument || elem ) != document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return ( sel + "" ).replace( rcssescape, fcssescape ); +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + + // If no nodeType, this is expected to be an array + while ( ( node = elem[ i++ ] ) ) { + + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[ 1 ] = match[ 1 ].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[ 3 ] = ( match[ 3 ] || match[ 4 ] || + match[ 5 ] || "" ).replace( runescape, funescape ); + + if ( match[ 2 ] === "~=" ) { + match[ 3 ] = " " + match[ 3 ] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[ 1 ] = match[ 1 ].toLowerCase(); + + if ( match[ 1 ].slice( 0, 3 ) === "nth" ) { + + // nth-* requires argument + if ( !match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[ 4 ] = +( match[ 4 ] ? + match[ 5 ] + ( match[ 6 ] || 1 ) : + 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) ); + match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); + + // other types prohibit arguments + } else if ( match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[ 6 ] && match[ 2 ]; + + if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[ 3 ] ) { + match[ 2 ] = match[ 4 ] || match[ 5 ] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + + // Get excess from tokenize (recursively) + ( excess = tokenize( unquoted, true ) ) && + + // advance to the next closing parenthesis + ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) { + + // excess is a negative index + match[ 0 ] = match[ 0 ].slice( 0, excess ); + match[ 2 ] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { + return true; + } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + ( pattern = new RegExp( "(^|" + whitespace + + ")" + className + "(" + whitespace + "|$)" ) ) && classCache( + className, function( elem ) { + return pattern.test( + typeof elem.className === "string" && elem.className || + typeof elem.getAttribute !== "undefined" && + elem.getAttribute( "class" ) || + "" + ); + } ); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + /* eslint-disable max-len */ + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + /* eslint-enable max-len */ + + }; + }, + + "CHILD": function( type, what, _argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, _context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( ( node = node[ dir ] ) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( ( node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + + // Use previously-cached element index if available + if ( useCache ) { + + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + + // Use the same loop as above to seek `elem` from the start + while ( ( node = ++nodeIndex && node && node[ dir ] || + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || + ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction( function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[ i ] ); + seed[ idx ] = !( matches[ idx ] = matched[ i ] ); + } + } ) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + + // Potentially complex pseudos + "not": markFunction( function( selector ) { + + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction( function( seed, matches, _context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( ( elem = unmatched[ i ] ) ) { + seed[ i ] = !( matches[ i ] = elem ); + } + } + } ) : + function( elem, _context, xml ) { + input[ 0 ] = elem; + matcher( input, null, xml, results ); + + // Don't keep the element (issue #299) + input[ 0 ] = null; + return !results.pop(); + }; + } ), + + "has": markFunction( function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + } ), + + "contains": markFunction( function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; + }; + } ), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + + // lang value must be a valid identifier + if ( !ridentifier.test( lang || "" ) ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( ( elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 ); + return false; + }; + } ), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && + ( !document.hasFocus || document.hasFocus() ) && + !!( elem.type || elem.href || ~elem.tabIndex ); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return ( nodeName === "input" && !!elem.checked ) || + ( nodeName === "option" && !!elem.selected ); + }, + + "selected": function( elem ) { + + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + // eslint-disable-next-line no-unused-expressions + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos[ "empty" ]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( ( attr = elem.getAttribute( "type" ) ) == null || + attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo( function() { + return [ 0 ]; + } ), + + "last": createPositionalPseudo( function( _matchIndexes, length ) { + return [ length - 1 ]; + } ), + + "eq": createPositionalPseudo( function( _matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + } ), + + "even": createPositionalPseudo( function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "odd": createPositionalPseudo( function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "lt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? + argument + length : + argument > length ? + length : + argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "gt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ) + } +}; + +Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || ( match = rcomma.exec( soFar ) ) ) { + if ( match ) { + + // Don't consume trailing commas as valid + soFar = soFar.slice( match[ 0 ].length ) || soFar; + } + groups.push( ( tokens = [] ) ); + } + + matched = false; + + // Combinators + if ( ( match = rcombinators.exec( soFar ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + + // Cast descendant combinators to space + type: match[ 0 ].replace( rtrim, " " ) + } ); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] || + ( match = preFilters[ type ]( match ) ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + type: type, + matches: match + } ); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[ i ].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || ( elem[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || + ( outerCache[ elem.uniqueID ] = {} ); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( ( oldCache = uniqueCache[ key ] ) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return ( newCache[ 2 ] = oldCache[ 2 ] ); + } else { + + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { + return true; + } + } + } + } + } + return false; + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[ i ]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[ 0 ]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[ i ], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( ( elem = unmatched[ i ] ) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction( function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( + selector || "*", + context.nodeType ? [ context ] : context, + [] + ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( ( elem = temp[ i ] ) ) { + matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem ); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) ) { + + // Restore matcherIn since elem is not yet a final match + temp.push( ( matcherIn[ i ] = elem ) ); + } + } + postFinder( null, ( matcherOut = [] ), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) && + ( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) { + + seed[ temp ] = !( results[ temp ] = elem ); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + } ); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[ 0 ].type ], + implicitRelative = leadingRelative || Expr.relative[ " " ], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + ( checkContext = context ).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[ j ].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens + .slice( 0, i - 1 ) + .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ), + + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ), + len = elems.length; + + if ( outermost ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + outermostContext = context == document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( !context && elem.ownerDocument != document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( ( matcher = elementMatchers[ j++ ] ) ) { + if ( matcher( elem, context || document, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + + // They will have gone through all possible matchers + if ( ( elem = !matcher && elem ) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( ( matcher = setMatchers[ j++ ] ) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !( unmatched[ i ] || setMatched[ i ] ) ) { + setMatched[ i ] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[ i ] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( + selector, + matcherFromGroupMatchers( elementMatchers, setMatchers ) + ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( ( selector = compiled.selector || selector ) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[ 0 ] = match[ 0 ].slice( 0 ); + if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { + + context = ( Expr.find[ "ID" ]( token.matches[ 0 ] + .replace( runescape, funescape ), context ) || [] )[ 0 ]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[ i ]; + + // Abort if we hit a combinator + if ( Expr.relative[ ( type = token.type ) ] ) { + break; + } + if ( ( find = Expr.find[ type ] ) ) { + + // Search, expanding context for leading sibling combinators + if ( ( seed = find( + token.matches[ 0 ].replace( runescape, funescape ), + rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) || + context + ) ) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert( function( el ) { + + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1; +} ); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert( function( el ) { + el.innerHTML = ""; + return el.firstChild.getAttribute( "href" ) === "#"; +} ) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + } ); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert( function( el ) { + el.innerHTML = ""; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; +} ) ) { + addHandle( "value", function( elem, _name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + } ); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert( function( el ) { + return el.getAttribute( "disabled" ) == null; +} ) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; + } + } ); +} + +return Sizzle; + +} )( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +}; +var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + + + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, _i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, _i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, _i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( elem.contentDocument != null && + + // Support: IE 11+ + // elements with no `data` attribute has an object + // `contentDocument` with a `null` prototype. + getProto( elem.contentDocument ) ) { + + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( _i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the master Deferred + master = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + master.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( master.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return master.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); + } + + return master.promise(); + } +} ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + + + + +// The deferred used on DOM ready +var readyList = jQuery.Deferred(); + +jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); + +jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, _key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + + +// Matches dashed string for camelizing +var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() +function fcamelCase( _all, letter ) { + return letter.toUpperCase(); +} + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var documentElement = document.documentElement; + + + + var isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ); + }, + composed = { composed: true }; + + // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only + // Check attachment across shadow DOM boundaries when possible (gh-3504) + // Support: iOS 10.0-10.2 only + // Early iOS 10 versions support `attachShadow` but not `getRootNode`, + // leading to errors. We need to check for `getRootNode`. + if ( documentElement.getRootNode ) { + isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ) || + elem.getRootNode( composed ) === elem.ownerDocument; + }; + } +var isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + isAttached( elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = elem.nodeType && + ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +var defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); + +var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + + // Support: IE <=9 only + // IE <=9 replaces "; + support.option = !!div.lastChild; +} )(); + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "
    " ], + col: [ 2, "", "
    " ], + tr: [ 2, "", "
    " ], + td: [ 3, "", "
    " ], + + _default: [ 0, "", "" ] +}; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// Support: IE <=9 only +if ( !support.option ) { + wrapMap.optgroup = wrapMap.option = [ 1, "" ]; +} + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, attached, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + attached = isAttached( elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( attached ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 - 11+ +// focus() and blur() are asynchronous, except when they are no-op. +// So expect focus to be synchronous when the element is already active, +// and blur to be synchronous when the element is not already active. +// (focus and blur are always synchronous in other supported browsers, +// this just defines when we can count on it). +function expectSync( elem, type ) { + return ( elem === safeActiveElement() ) === ( type === "focus" ); +} + +// Support: IE <=9 only +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Only attach events to objects that accept data + if ( !acceptData( elem ) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = Object.create( null ); + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( nativeEvent ), + + handlers = ( + dataPriv.get( this, "events" ) || Object.create( null ) + )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // If the event is namespaced, then each handler is only invoked if it is + // specially universal or its namespaces are a superset of the event's. + if ( !event.rnamespace || handleObj.namespace === false || + event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { + + // Utilize native event to ensure correct state for checkable inputs + setup: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Claim the first handler + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + // dataPriv.set( el, "click", ... ) + leverageNative( el, "click", returnTrue ); + } + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Force setup before triggering a click + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + leverageNative( el, "click" ); + } + + // Return non-false to allow normal event-path propagation + return true; + }, + + // For cross-browser consistency, suppress native .click() on links + // Also prevent it if we're currently inside a leveraged native-event stack + _default: function( event ) { + var target = event.target; + return rcheckableType.test( target.type ) && + target.click && nodeName( target, "input" ) && + dataPriv.get( target, "click" ) || + nodeName( target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +// Ensure the presence of an event listener that handles manually-triggered +// synthetic events by interrupting progress until reinvoked in response to +// *native* events that it fires directly, ensuring that state changes have +// already occurred before other listeners are invoked. +function leverageNative( el, type, expectSync ) { + + // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add + if ( !expectSync ) { + if ( dataPriv.get( el, type ) === undefined ) { + jQuery.event.add( el, type, returnTrue ); + } + return; + } + + // Register the controller as a special universal handler for all event namespaces + dataPriv.set( el, type, false ); + jQuery.event.add( el, type, { + namespace: false, + handler: function( event ) { + var notAsync, result, + saved = dataPriv.get( this, type ); + + if ( ( event.isTrigger & 1 ) && this[ type ] ) { + + // Interrupt processing of the outer synthetic .trigger()ed event + // Saved data should be false in such cases, but might be a leftover capture object + // from an async native handler (gh-4350) + if ( !saved.length ) { + + // Store arguments for use when handling the inner native event + // There will always be at least one argument (an event object), so this array + // will not be confused with a leftover capture object. + saved = slice.call( arguments ); + dataPriv.set( this, type, saved ); + + // Trigger the native event and capture its result + // Support: IE <=9 - 11+ + // focus() and blur() are asynchronous + notAsync = expectSync( this, type ); + this[ type ](); + result = dataPriv.get( this, type ); + if ( saved !== result || notAsync ) { + dataPriv.set( this, type, false ); + } else { + result = {}; + } + if ( saved !== result ) { + + // Cancel the outer synthetic event + event.stopImmediatePropagation(); + event.preventDefault(); + return result.value; + } + + // If this is an inner synthetic event for an event with a bubbling surrogate + // (focus or blur), assume that the surrogate already propagated from triggering the + // native event and prevent that from happening again here. + // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the + // bubbling surrogate propagates *after* the non-bubbling base), but that seems + // less bad than duplication. + } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { + event.stopPropagation(); + } + + // If this is a native event triggered above, everything is now in order + // Fire an inner synthetic event with the original arguments + } else if ( saved.length ) { + + // ...and capture the result + dataPriv.set( this, type, { + value: jQuery.event.trigger( + + // Support: IE <=9 - 11+ + // Extend with the prototype to reset the above stopImmediatePropagation() + jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), + saved.slice( 1 ), + this + ) + } ); + + // Abort handling of the native event + event.stopImmediatePropagation(); + } + } + } ); +} + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + code: true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + + which: function( event ) { + var button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + if ( button & 1 ) { + return 1; + } + + if ( button & 2 ) { + return 3; + } + + if ( button & 4 ) { + return 2; + } + + return 0; + } + + return event.which; + } +}, jQuery.event.addProp ); + +jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + jQuery.event.special[ type ] = { + + // Utilize native event if possible so blur/focus sequence is correct + setup: function() { + + // Claim the first handler + // dataPriv.set( this, "focus", ... ) + // dataPriv.set( this, "blur", ... ) + leverageNative( this, type, expectSync ); + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function() { + + // Force setup before trigger + leverageNative( this, type ); + + // Return non-false to allow normal event-path propagation + return true; + }, + + delegateType: delegateType + }; +} ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; + +// Prefer a tbody over its parent table for containing new rows +function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.get( src ); + events = pdataOld.events; + + if ( events ) { + dataPriv.remove( dest, "handle events" ); + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = flat( args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl && !node.noModule ) { + jQuery._evalUrl( node.src, { + nonce: node.nonce || node.getAttribute( "nonce" ) + }, doc ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && isAttached( node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html; + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = isAttached( elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + +var swap = function( elem, options, callback ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + +var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); + + + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; + div.style.cssText = + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + + "margin:auto;border:1px;padding:1px;" + + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; + + // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 + // Some styles come back with percentage values, even though they shouldn't + div.style.right = "60%"; + pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + // Support: Chrome <=64 + // Don't get tricked when zoom affects offsetWidth (gh-4029) + div.style.position = "absolute"; + scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + function roundPixelMeasures( measure ) { + return Math.round( parseFloat( measure ) ); + } + + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, + reliableTrDimensionsVal, reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + jQuery.extend( support, { + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function() { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; + }, + + // Support: IE 9 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Behavior in IE 9 is more subtle than in newer versions & it passes + // some versions of this test; make sure not to make it pass there! + reliableTrDimensions: function() { + var table, tr, trChild, trStyle; + if ( reliableTrDimensionsVal == null ) { + table = document.createElement( "table" ); + tr = document.createElement( "tr" ); + trChild = document.createElement( "div" ); + + table.style.cssText = "position:absolute;left:-11111px"; + tr.style.height = "1px"; + trChild.style.height = "9px"; + + documentElement + .appendChild( table ) + .appendChild( tr ) + .appendChild( trChild ); + + trStyle = window.getComputedStyle( tr ); + reliableTrDimensionsVal = parseInt( trStyle.height ) > 3; + + documentElement.removeChild( table ); + } + return reliableTrDimensionsVal; + } + } ); +} )(); + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !isAttached( elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + + +var cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style, + vendorProps = {}; + +// Return a vendor-prefixed property or undefined +function vendorPropName( name ) { + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +// Return a potentially-mapped jQuery.cssProps or vendor prefixed property +function finalPropName( name ) { + var final = jQuery.cssProps[ name ] || vendorProps[ name ]; + + if ( final ) { + return final; + } + if ( name in emptyStyle ) { + return name; + } + return vendorProps[ name ] = vendorPropName( name ) || name; +} + + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }; + +function setPositiveNumber( _elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { + var i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0; + + // Adjustment may not be necessary + if ( box === ( isBorderBox ? "border" : "content" ) ) { + return 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin + if ( box === "margin" ) { + delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + } + + // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + if ( !isBorderBox ) { + + // Add padding + delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // For "border" or "margin", add border + if ( box !== "padding" ) { + delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + + // But still keep track of it otherwise + } else { + extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + + // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + } else { + + // For "content", subtract padding + if ( box === "content" ) { + delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // For "content" or "padding", subtract border + if ( box !== "margin" ) { + delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + // Account for positive content-box scroll gutter when requested by providing computedVal + if ( !isBorderBox && computedVal >= 0 ) { + + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max( 0, Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + computedVal - + delta - + extra - + 0.5 + + // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter + // Use an explicit zero to avoid NaN (gh-3964) + ) ) || 0; + } + + return delta; +} + +function getWidthOrHeight( elem, dimension, extra ) { + + // Start with computed style + var styles = getStyles( elem ), + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). + // Fake content-box until we know it's needed to know the true value. + boxSizingNeeded = !support.boxSizingReliable() || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox, + + val = curCSS( elem, dimension, styles ), + offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); + + // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + if ( rnumnonpx.test( val ) ) { + if ( !extra ) { + return val; + } + val = "auto"; + } + + + // Support: IE 9 - 11 only + // Use offsetWidth/offsetHeight for when box sizing is unreliable. + // In those cases, the computed value can be trusted to be border-box. + if ( ( !support.boxSizingReliable() && isBorderBox || + + // Support: IE 10 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Interestingly, in some cases IE 9 doesn't suffer from this issue. + !support.reliableTrDimensions() && nodeName( elem, "tr" ) || + + // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + val === "auto" || + + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && + + // Make sure the element is visible & connected + elem.getClientRects().length ) { + + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // Where available, offsetWidth/offsetHeight approximate border box dimensions. + // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the + // retrieved value as a content box dimension. + valueIsBorderBox = offsetProp in elem; + if ( valueIsBorderBox ) { + val = elem[ offsetProp ]; + } + } + + // Normalize "" and auto + val = parseFloat( val ) || 0; + + // Adjust for the element's box model + return ( val + + boxModelAdjustment( + elem, + dimension, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles, + + // Provide the current computed size to request scroll gutter calculation (gh-3589) + val + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "gridArea": true, + "gridColumn": true, + "gridColumnEnd": true, + "gridColumnStart": true, + "gridRow": true, + "gridRowEnd": true, + "gridRowStart": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append + // "px" to a few hardcoded values. + if ( type === "number" && !isCustomProp ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( _i, dimension ) { + jQuery.cssHooks[ dimension ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = getStyles( elem ), + + // Only read styles.position if the test has a chance to fail + // to avoid forcing a reflow. + scrollboxSizeBuggy = !support.scrollboxSize() && + styles.position === "absolute", + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) + boxSizingNeeded = scrollboxSizeBuggy || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra ? + boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ) : + 0; + + // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + if ( isBorderBox && scrollboxSizeBuggy ) { + subtract -= Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + parseFloat( styles[ dimension ] ) - + boxModelAdjustment( elem, dimension, "border", false, styles ) - + 0.5 + ); + } + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ dimension ] = value; + value = jQuery.css( elem, dimension ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( prefix !== "margin" ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && ( + jQuery.cssHooks[ tween.prop ] || + tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = Date.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 15 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY and Edge just mirrors + // the overflowX value there. + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + result.stop.bind( result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = Date.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); +}; + +jQuery.fx.stop = function() { + inProgress = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + + +( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + + +var boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + + + + + // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + function stripAndCollapse( value ) { + var tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +function classesToArray( value ) { + if ( Array.isArray( value ) ) { + return value; + } + if ( typeof value === "string" ) { + return value.match( rnothtmlwhite ) || []; + } + return []; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isValidValue = type === "string" || Array.isArray( value ); + + if ( typeof stateVal === "boolean" && isValidValue ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( isValidValue ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = classesToArray( value ); + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } +} ); + + + + +var rreturn = /\r/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, valueIsFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( valueIsFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + + + + +// Return jQuery for attributes-only inclusion + + +support.focusin = "onfocusin" in window; + + +var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function( e ) { + e.stopPropagation(); + }; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = lastElement = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + lastElement = cur; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( + dataPriv.get( cur, "events" ) || Object.create( null ) + )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + + if ( event.isPropagationStopped() ) { + lastElement.addEventListener( type, stopPropagationCallback ); + } + + elem[ type ](); + + if ( event.isPropagationStopped() ) { + lastElement.removeEventListener( type, stopPropagationCallback ); + } + + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + + // Handle: regular nodes (via `this.ownerDocument`), window + // (via `this.document`) & document (via `this`). + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} +var location = window.location; + +var nonce = { guid: Date.now() }; + +var rquery = ( /\?/ ); + + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) { + xml = undefined; + } + + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + + +var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && toType( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + if ( a == null ) { + return ""; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ) + .filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ) + .map( function( _i, elem ) { + var val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + + +var + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() + " " ] = + ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) + .concat( match[ 2 ] ); + } + } + match = responseHeaders[ key.toLowerCase() + " " ]; + } + return match == null ? null : match.join( ", " ); + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 15 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available and should be processed, append data to url + if ( s.data && ( s.processData || typeof s.data === "string" ) ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) + + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Use a noop converter for missing script + if ( !isSuccess && jQuery.inArray( "script", s.dataTypes ) > -1 ) { + s.converters[ "text script" ] = function() {}; + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( _i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + +jQuery.ajaxPrefilter( function( s ) { + var i; + for ( i in s.headers ) { + if ( i.toLowerCase() === "content-type" ) { + s.contentType = s.headers[ i ] || ""; + } + } +} ); + + +jQuery._evalUrl = function( url, options, doc ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + + // Only evaluate the response if it is successful (gh-4126) + // dataFilter is not invoked for failure responses, so using it instead + // of the default converter is kludgy but it works. + converters: { + "text script": function() {} + }, + dataFilter: function( response ) { + jQuery.globalEval( response, options, doc ); + } + } ); +}; + + +jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var htmlIsFunction = isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + + + + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.ontimeout = + xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain or forced-by-attrs requests + if ( s.crossDomain || s.scriptAttrs ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( "\r\n"; + +// inject VBScript +document.write(IEBinaryToArray_ByteStr_Script); + +global.JSZipUtils._getBinaryFromXHR = function (xhr) { + var binary = xhr.responseBody; + var byteMapping = {}; + for ( var i = 0; i < 256; i++ ) { + for ( var j = 0; j < 256; j++ ) { + byteMapping[ String.fromCharCode( i + (j << 8) ) ] = + String.fromCharCode(i) + String.fromCharCode(j); + } + } + var rawBytes = IEBinaryToArray_ByteStr(binary); + var lastChr = IEBinaryToArray_ByteStr_Last(binary); + return rawBytes.replace(/[\s\S]/g, function( match ) { + return byteMapping[match]; + }) + lastChr; +}; + +// enforcing Stuk's coding style +// vim: set shiftwidth=4 softtabstop=4: + +},{}]},{},[1]) +; diff --git a/java/doc/jquery/jszip-utils/dist/jszip-utils-ie.min.js b/java/doc/jquery/jszip-utils/dist/jszip-utils-ie.min.js new file mode 100644 index 000000000..93d8bc8ef --- /dev/null +++ b/java/doc/jquery/jszip-utils/dist/jszip-utils-ie.min.js @@ -0,0 +1,10 @@ +/*! + +JSZipUtils - A collection of cross-browser utilities to go along with JSZip. + + +(c) 2014 Stuart Knightley, David Duponchel +Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip-utils/master/LICENSE.markdown. + +*/ +!function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g\r\n";document.write(b),a.JSZipUtils._getBinaryFromXHR=function(a){for(var b=a.responseBody,c={},d=0;256>d;d++)for(var e=0;256>e;e++)c[String.fromCharCode(d+(e<<8))]=String.fromCharCode(d)+String.fromCharCode(e);var f=IEBinaryToArray_ByteStr(b),g=IEBinaryToArray_ByteStr_Last(b);return f.replace(/[\s\S]/g,function(a){return c[a]})+g}},{}]},{},[1]); diff --git a/java/doc/jquery/jszip-utils/dist/jszip-utils.js b/java/doc/jquery/jszip-utils/dist/jszip-utils.js new file mode 100644 index 000000000..775895ec9 --- /dev/null +++ b/java/doc/jquery/jszip-utils/dist/jszip-utils.js @@ -0,0 +1,118 @@ +/*! + +JSZipUtils - A collection of cross-browser utilities to go along with JSZip. + + +(c) 2014 Stuart Knightley, David Duponchel +Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip-utils/master/LICENSE.markdown. + +*/ +!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.JSZipUtils=e():"undefined"!=typeof global?global.JSZipUtils=e():"undefined"!=typeof self&&(self.JSZipUtils=e())}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o + +(c) 2014 Stuart Knightley, David Duponchel +Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip-utils/master/LICENSE.markdown. + +*/ +!function(a){"object"==typeof exports?module.exports=a():"function"==typeof define&&define.amd?define(a):"undefined"!=typeof window?window.JSZipUtils=a():"undefined"!=typeof global?global.JSZipUtils=a():"undefined"!=typeof self&&(self.JSZipUtils=a())}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g + +(c) 2009-2016 Stuart Knightley +Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/master/LICENSE.markdown. + +JSZip uses the library pako released under the MIT license : +https://github.com/nodeca/pako/blob/master/LICENSE +*/ + +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.JSZip = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o> 2; + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); + enc3 = remainingBytes > 1 ? (((chr2 & 15) << 2) | (chr3 >> 6)) : 64; + enc4 = remainingBytes > 2 ? (chr3 & 63) : 64; + + output.push(_keyStr.charAt(enc1) + _keyStr.charAt(enc2) + _keyStr.charAt(enc3) + _keyStr.charAt(enc4)); + + } + + return output.join(""); +}; + +// public method for decoding +exports.decode = function(input) { + var chr1, chr2, chr3; + var enc1, enc2, enc3, enc4; + var i = 0, resultIndex = 0; + + var dataUrlPrefix = "data:"; + + if (input.substr(0, dataUrlPrefix.length) === dataUrlPrefix) { + // This is a common error: people give a data url + // (data:image/png;base64,iVBOR...) with a {base64: true} and + // wonders why things don't work. + // We can detect that the string input looks like a data url but we + // *can't* be sure it is one: removing everything up to the comma would + // be too dangerous. + throw new Error("Invalid base64 input, it looks like a data url."); + } + + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + + var totalLength = input.length * 3 / 4; + if(input.charAt(input.length - 1) === _keyStr.charAt(64)) { + totalLength--; + } + if(input.charAt(input.length - 2) === _keyStr.charAt(64)) { + totalLength--; + } + if (totalLength % 1 !== 0) { + // totalLength is not an integer, the length does not match a valid + // base64 content. That can happen if: + // - the input is not a base64 content + // - the input is *almost* a base64 content, with a extra chars at the + // beginning or at the end + // - the input uses a base64 variant (base64url for example) + throw new Error("Invalid base64 input, bad content length."); + } + var output; + if (support.uint8array) { + output = new Uint8Array(totalLength|0); + } else { + output = new Array(totalLength|0); + } + + while (i < input.length) { + + enc1 = _keyStr.indexOf(input.charAt(i++)); + enc2 = _keyStr.indexOf(input.charAt(i++)); + enc3 = _keyStr.indexOf(input.charAt(i++)); + enc4 = _keyStr.indexOf(input.charAt(i++)); + + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + + output[resultIndex++] = chr1; + + if (enc3 !== 64) { + output[resultIndex++] = chr2; + } + if (enc4 !== 64) { + output[resultIndex++] = chr3; + } + + } + + return output; +}; + +},{"./support":30,"./utils":32}],2:[function(require,module,exports){ +'use strict'; + +var external = require("./external"); +var DataWorker = require('./stream/DataWorker'); +var Crc32Probe = require('./stream/Crc32Probe'); +var DataLengthProbe = require('./stream/DataLengthProbe'); + +/** + * Represent a compressed object, with everything needed to decompress it. + * @constructor + * @param {number} compressedSize the size of the data compressed. + * @param {number} uncompressedSize the size of the data after decompression. + * @param {number} crc32 the crc32 of the decompressed file. + * @param {object} compression the type of compression, see lib/compressions.js. + * @param {String|ArrayBuffer|Uint8Array|Buffer} data the compressed data. + */ +function CompressedObject(compressedSize, uncompressedSize, crc32, compression, data) { + this.compressedSize = compressedSize; + this.uncompressedSize = uncompressedSize; + this.crc32 = crc32; + this.compression = compression; + this.compressedContent = data; +} + +CompressedObject.prototype = { + /** + * Create a worker to get the uncompressed content. + * @return {GenericWorker} the worker. + */ + getContentWorker: function () { + var worker = new DataWorker(external.Promise.resolve(this.compressedContent)) + .pipe(this.compression.uncompressWorker()) + .pipe(new DataLengthProbe("data_length")); + + var that = this; + worker.on("end", function () { + if (this.streamInfo['data_length'] !== that.uncompressedSize) { + throw new Error("Bug : uncompressed data size mismatch"); + } + }); + return worker; + }, + /** + * Create a worker to get the compressed content. + * @return {GenericWorker} the worker. + */ + getCompressedWorker: function () { + return new DataWorker(external.Promise.resolve(this.compressedContent)) + .withStreamInfo("compressedSize", this.compressedSize) + .withStreamInfo("uncompressedSize", this.uncompressedSize) + .withStreamInfo("crc32", this.crc32) + .withStreamInfo("compression", this.compression) + ; + } +}; + +/** + * Chain the given worker with other workers to compress the content with the + * given compression. + * @param {GenericWorker} uncompressedWorker the worker to pipe. + * @param {Object} compression the compression object. + * @param {Object} compressionOptions the options to use when compressing. + * @return {GenericWorker} the new worker compressing the content. + */ +CompressedObject.createWorkerFrom = function (uncompressedWorker, compression, compressionOptions) { + return uncompressedWorker + .pipe(new Crc32Probe()) + .pipe(new DataLengthProbe("uncompressedSize")) + .pipe(compression.compressWorker(compressionOptions)) + .pipe(new DataLengthProbe("compressedSize")) + .withStreamInfo("compression", compression); +}; + +module.exports = CompressedObject; + +},{"./external":6,"./stream/Crc32Probe":25,"./stream/DataLengthProbe":26,"./stream/DataWorker":27}],3:[function(require,module,exports){ +'use strict'; + +var GenericWorker = require("./stream/GenericWorker"); + +exports.STORE = { + magic: "\x00\x00", + compressWorker : function (compressionOptions) { + return new GenericWorker("STORE compression"); + }, + uncompressWorker : function () { + return new GenericWorker("STORE decompression"); + } +}; +exports.DEFLATE = require('./flate'); + +},{"./flate":7,"./stream/GenericWorker":28}],4:[function(require,module,exports){ +'use strict'; + +var utils = require('./utils'); + +/** + * The following functions come from pako, from pako/lib/zlib/crc32.js + * released under the MIT license, see pako https://github.com/nodeca/pako/ + */ + +// Use ordinary array, since untyped makes no boost here +function makeTable() { + var c, table = []; + + for(var n =0; n < 256; n++){ + c = n; + for(var k =0; k < 8; k++){ + c = ((c&1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); + } + table[n] = c; + } + + return table; +} + +// Create table on load. Just 255 signed longs. Not a problem. +var crcTable = makeTable(); + + +function crc32(crc, buf, len, pos) { + var t = crcTable, end = pos + len; + + crc = crc ^ (-1); + + for (var i = pos; i < end; i++ ) { + crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF]; + } + + return (crc ^ (-1)); // >>> 0; +} + +// That's all for the pako functions. + +/** + * Compute the crc32 of a string. + * This is almost the same as the function crc32, but for strings. Using the + * same function for the two use cases leads to horrible performances. + * @param {Number} crc the starting value of the crc. + * @param {String} str the string to use. + * @param {Number} len the length of the string. + * @param {Number} pos the starting position for the crc32 computation. + * @return {Number} the computed crc32. + */ +function crc32str(crc, str, len, pos) { + var t = crcTable, end = pos + len; + + crc = crc ^ (-1); + + for (var i = pos; i < end; i++ ) { + crc = (crc >>> 8) ^ t[(crc ^ str.charCodeAt(i)) & 0xFF]; + } + + return (crc ^ (-1)); // >>> 0; +} + +module.exports = function crc32wrapper(input, crc) { + if (typeof input === "undefined" || !input.length) { + return 0; + } + + var isArray = utils.getTypeOf(input) !== "string"; + + if(isArray) { + return crc32(crc|0, input, input.length, 0); + } else { + return crc32str(crc|0, input, input.length, 0); + } +}; + +},{"./utils":32}],5:[function(require,module,exports){ +'use strict'; +exports.base64 = false; +exports.binary = false; +exports.dir = false; +exports.createFolders = true; +exports.date = null; +exports.compression = null; +exports.compressionOptions = null; +exports.comment = null; +exports.unixPermissions = null; +exports.dosPermissions = null; + +},{}],6:[function(require,module,exports){ +/* global Promise */ +'use strict'; + +// load the global object first: +// - it should be better integrated in the system (unhandledRejection in node) +// - the environment may have a custom Promise implementation (see zone.js) +var ES6Promise = null; +if (typeof Promise !== "undefined") { + ES6Promise = Promise; +} else { + ES6Promise = require("lie"); +} + +/** + * Let the user use/change some implementations. + */ +module.exports = { + Promise: ES6Promise +}; + +},{"lie":37}],7:[function(require,module,exports){ +'use strict'; +var USE_TYPEDARRAY = (typeof Uint8Array !== 'undefined') && (typeof Uint16Array !== 'undefined') && (typeof Uint32Array !== 'undefined'); + +var pako = require("pako"); +var utils = require("./utils"); +var GenericWorker = require("./stream/GenericWorker"); + +var ARRAY_TYPE = USE_TYPEDARRAY ? "uint8array" : "array"; + +exports.magic = "\x08\x00"; + +/** + * Create a worker that uses pako to inflate/deflate. + * @constructor + * @param {String} action the name of the pako function to call : either "Deflate" or "Inflate". + * @param {Object} options the options to use when (de)compressing. + */ +function FlateWorker(action, options) { + GenericWorker.call(this, "FlateWorker/" + action); + + this._pako = null; + this._pakoAction = action; + this._pakoOptions = options; + // the `meta` object from the last chunk received + // this allow this worker to pass around metadata + this.meta = {}; +} + +utils.inherits(FlateWorker, GenericWorker); + +/** + * @see GenericWorker.processChunk + */ +FlateWorker.prototype.processChunk = function (chunk) { + this.meta = chunk.meta; + if (this._pako === null) { + this._createPako(); + } + this._pako.push(utils.transformTo(ARRAY_TYPE, chunk.data), false); +}; + +/** + * @see GenericWorker.flush + */ +FlateWorker.prototype.flush = function () { + GenericWorker.prototype.flush.call(this); + if (this._pako === null) { + this._createPako(); + } + this._pako.push([], true); +}; +/** + * @see GenericWorker.cleanUp + */ +FlateWorker.prototype.cleanUp = function () { + GenericWorker.prototype.cleanUp.call(this); + this._pako = null; +}; + +/** + * Create the _pako object. + * TODO: lazy-loading this object isn't the best solution but it's the + * quickest. The best solution is to lazy-load the worker list. See also the + * issue #446. + */ +FlateWorker.prototype._createPako = function () { + this._pako = new pako[this._pakoAction]({ + raw: true, + level: this._pakoOptions.level || -1 // default compression + }); + var self = this; + this._pako.onData = function(data) { + self.push({ + data : data, + meta : self.meta + }); + }; +}; + +exports.compressWorker = function (compressionOptions) { + return new FlateWorker("Deflate", compressionOptions); +}; +exports.uncompressWorker = function () { + return new FlateWorker("Inflate", {}); +}; + +},{"./stream/GenericWorker":28,"./utils":32,"pako":38}],8:[function(require,module,exports){ +'use strict'; + +var utils = require('../utils'); +var GenericWorker = require('../stream/GenericWorker'); +var utf8 = require('../utf8'); +var crc32 = require('../crc32'); +var signature = require('../signature'); + +/** + * Transform an integer into a string in hexadecimal. + * @private + * @param {number} dec the number to convert. + * @param {number} bytes the number of bytes to generate. + * @returns {string} the result. + */ +var decToHex = function(dec, bytes) { + var hex = "", i; + for (i = 0; i < bytes; i++) { + hex += String.fromCharCode(dec & 0xff); + dec = dec >>> 8; + } + return hex; +}; + +/** + * Generate the UNIX part of the external file attributes. + * @param {Object} unixPermissions the unix permissions or null. + * @param {Boolean} isDir true if the entry is a directory, false otherwise. + * @return {Number} a 32 bit integer. + * + * adapted from http://unix.stackexchange.com/questions/14705/the-zip-formats-external-file-attribute : + * + * TTTTsstrwxrwxrwx0000000000ADVSHR + * ^^^^____________________________ file type, see zipinfo.c (UNX_*) + * ^^^_________________________ setuid, setgid, sticky + * ^^^^^^^^^________________ permissions + * ^^^^^^^^^^______ not used ? + * ^^^^^^ DOS attribute bits : Archive, Directory, Volume label, System file, Hidden, Read only + */ +var generateUnixExternalFileAttr = function (unixPermissions, isDir) { + + var result = unixPermissions; + if (!unixPermissions) { + // I can't use octal values in strict mode, hence the hexa. + // 040775 => 0x41fd + // 0100664 => 0x81b4 + result = isDir ? 0x41fd : 0x81b4; + } + return (result & 0xFFFF) << 16; +}; + +/** + * Generate the DOS part of the external file attributes. + * @param {Object} dosPermissions the dos permissions or null. + * @param {Boolean} isDir true if the entry is a directory, false otherwise. + * @return {Number} a 32 bit integer. + * + * Bit 0 Read-Only + * Bit 1 Hidden + * Bit 2 System + * Bit 3 Volume Label + * Bit 4 Directory + * Bit 5 Archive + */ +var generateDosExternalFileAttr = function (dosPermissions, isDir) { + + // the dir flag is already set for compatibility + return (dosPermissions || 0) & 0x3F; +}; + +/** + * Generate the various parts used in the construction of the final zip file. + * @param {Object} streamInfo the hash with information about the compressed file. + * @param {Boolean} streamedContent is the content streamed ? + * @param {Boolean} streamingEnded is the stream finished ? + * @param {number} offset the current offset from the start of the zip file. + * @param {String} platform let's pretend we are this platform (change platform dependents fields) + * @param {Function} encodeFileName the function to encode the file name / comment. + * @return {Object} the zip parts. + */ +var generateZipParts = function(streamInfo, streamedContent, streamingEnded, offset, platform, encodeFileName) { + var file = streamInfo['file'], + compression = streamInfo['compression'], + useCustomEncoding = encodeFileName !== utf8.utf8encode, + encodedFileName = utils.transformTo("string", encodeFileName(file.name)), + utfEncodedFileName = utils.transformTo("string", utf8.utf8encode(file.name)), + comment = file.comment, + encodedComment = utils.transformTo("string", encodeFileName(comment)), + utfEncodedComment = utils.transformTo("string", utf8.utf8encode(comment)), + useUTF8ForFileName = utfEncodedFileName.length !== file.name.length, + useUTF8ForComment = utfEncodedComment.length !== comment.length, + dosTime, + dosDate, + extraFields = "", + unicodePathExtraField = "", + unicodeCommentExtraField = "", + dir = file.dir, + date = file.date; + + + var dataInfo = { + crc32 : 0, + compressedSize : 0, + uncompressedSize : 0 + }; + + // if the content is streamed, the sizes/crc32 are only available AFTER + // the end of the stream. + if (!streamedContent || streamingEnded) { + dataInfo.crc32 = streamInfo['crc32']; + dataInfo.compressedSize = streamInfo['compressedSize']; + dataInfo.uncompressedSize = streamInfo['uncompressedSize']; + } + + var bitflag = 0; + if (streamedContent) { + // Bit 3: the sizes/crc32 are set to zero in the local header. + // The correct values are put in the data descriptor immediately + // following the compressed data. + bitflag |= 0x0008; + } + if (!useCustomEncoding && (useUTF8ForFileName || useUTF8ForComment)) { + // Bit 11: Language encoding flag (EFS). + bitflag |= 0x0800; + } + + + var extFileAttr = 0; + var versionMadeBy = 0; + if (dir) { + // dos or unix, we set the dos dir flag + extFileAttr |= 0x00010; + } + if(platform === "UNIX") { + versionMadeBy = 0x031E; // UNIX, version 3.0 + extFileAttr |= generateUnixExternalFileAttr(file.unixPermissions, dir); + } else { // DOS or other, fallback to DOS + versionMadeBy = 0x0014; // DOS, version 2.0 + extFileAttr |= generateDosExternalFileAttr(file.dosPermissions, dir); + } + + // date + // @see http://www.delorie.com/djgpp/doc/rbinter/it/52/13.html + // @see http://www.delorie.com/djgpp/doc/rbinter/it/65/16.html + // @see http://www.delorie.com/djgpp/doc/rbinter/it/66/16.html + + dosTime = date.getUTCHours(); + dosTime = dosTime << 6; + dosTime = dosTime | date.getUTCMinutes(); + dosTime = dosTime << 5; + dosTime = dosTime | date.getUTCSeconds() / 2; + + dosDate = date.getUTCFullYear() - 1980; + dosDate = dosDate << 4; + dosDate = dosDate | (date.getUTCMonth() + 1); + dosDate = dosDate << 5; + dosDate = dosDate | date.getUTCDate(); + + if (useUTF8ForFileName) { + // set the unicode path extra field. unzip needs at least one extra + // field to correctly handle unicode path, so using the path is as good + // as any other information. This could improve the situation with + // other archive managers too. + // This field is usually used without the utf8 flag, with a non + // unicode path in the header (winrar, winzip). This helps (a bit) + // with the messy Windows' default compressed folders feature but + // breaks on p7zip which doesn't seek the unicode path extra field. + // So for now, UTF-8 everywhere ! + unicodePathExtraField = + // Version + decToHex(1, 1) + + // NameCRC32 + decToHex(crc32(encodedFileName), 4) + + // UnicodeName + utfEncodedFileName; + + extraFields += + // Info-ZIP Unicode Path Extra Field + "\x75\x70" + + // size + decToHex(unicodePathExtraField.length, 2) + + // content + unicodePathExtraField; + } + + if(useUTF8ForComment) { + + unicodeCommentExtraField = + // Version + decToHex(1, 1) + + // CommentCRC32 + decToHex(crc32(encodedComment), 4) + + // UnicodeName + utfEncodedComment; + + extraFields += + // Info-ZIP Unicode Path Extra Field + "\x75\x63" + + // size + decToHex(unicodeCommentExtraField.length, 2) + + // content + unicodeCommentExtraField; + } + + var header = ""; + + // version needed to extract + header += "\x0A\x00"; + // general purpose bit flag + header += decToHex(bitflag, 2); + // compression method + header += compression.magic; + // last mod file time + header += decToHex(dosTime, 2); + // last mod file date + header += decToHex(dosDate, 2); + // crc-32 + header += decToHex(dataInfo.crc32, 4); + // compressed size + header += decToHex(dataInfo.compressedSize, 4); + // uncompressed size + header += decToHex(dataInfo.uncompressedSize, 4); + // file name length + header += decToHex(encodedFileName.length, 2); + // extra field length + header += decToHex(extraFields.length, 2); + + + var fileRecord = signature.LOCAL_FILE_HEADER + header + encodedFileName + extraFields; + + var dirRecord = signature.CENTRAL_FILE_HEADER + + // version made by (00: DOS) + decToHex(versionMadeBy, 2) + + // file header (common to file and central directory) + header + + // file comment length + decToHex(encodedComment.length, 2) + + // disk number start + "\x00\x00" + + // internal file attributes TODO + "\x00\x00" + + // external file attributes + decToHex(extFileAttr, 4) + + // relative offset of local header + decToHex(offset, 4) + + // file name + encodedFileName + + // extra field + extraFields + + // file comment + encodedComment; + + return { + fileRecord: fileRecord, + dirRecord: dirRecord + }; +}; + +/** + * Generate the EOCD record. + * @param {Number} entriesCount the number of entries in the zip file. + * @param {Number} centralDirLength the length (in bytes) of the central dir. + * @param {Number} localDirLength the length (in bytes) of the local dir. + * @param {String} comment the zip file comment as a binary string. + * @param {Function} encodeFileName the function to encode the comment. + * @return {String} the EOCD record. + */ +var generateCentralDirectoryEnd = function (entriesCount, centralDirLength, localDirLength, comment, encodeFileName) { + var dirEnd = ""; + var encodedComment = utils.transformTo("string", encodeFileName(comment)); + + // end of central dir signature + dirEnd = signature.CENTRAL_DIRECTORY_END + + // number of this disk + "\x00\x00" + + // number of the disk with the start of the central directory + "\x00\x00" + + // total number of entries in the central directory on this disk + decToHex(entriesCount, 2) + + // total number of entries in the central directory + decToHex(entriesCount, 2) + + // size of the central directory 4 bytes + decToHex(centralDirLength, 4) + + // offset of start of central directory with respect to the starting disk number + decToHex(localDirLength, 4) + + // .ZIP file comment length + decToHex(encodedComment.length, 2) + + // .ZIP file comment + encodedComment; + + return dirEnd; +}; + +/** + * Generate data descriptors for a file entry. + * @param {Object} streamInfo the hash generated by a worker, containing information + * on the file entry. + * @return {String} the data descriptors. + */ +var generateDataDescriptors = function (streamInfo) { + var descriptor = ""; + descriptor = signature.DATA_DESCRIPTOR + + // crc-32 4 bytes + decToHex(streamInfo['crc32'], 4) + + // compressed size 4 bytes + decToHex(streamInfo['compressedSize'], 4) + + // uncompressed size 4 bytes + decToHex(streamInfo['uncompressedSize'], 4); + + return descriptor; +}; + + +/** + * A worker to concatenate other workers to create a zip file. + * @param {Boolean} streamFiles `true` to stream the content of the files, + * `false` to accumulate it. + * @param {String} comment the comment to use. + * @param {String} platform the platform to use, "UNIX" or "DOS". + * @param {Function} encodeFileName the function to encode file names and comments. + */ +function ZipFileWorker(streamFiles, comment, platform, encodeFileName) { + GenericWorker.call(this, "ZipFileWorker"); + // The number of bytes written so far. This doesn't count accumulated chunks. + this.bytesWritten = 0; + // The comment of the zip file + this.zipComment = comment; + // The platform "generating" the zip file. + this.zipPlatform = platform; + // the function to encode file names and comments. + this.encodeFileName = encodeFileName; + // Should we stream the content of the files ? + this.streamFiles = streamFiles; + // If `streamFiles` is false, we will need to accumulate the content of the + // files to calculate sizes / crc32 (and write them *before* the content). + // This boolean indicates if we are accumulating chunks (it will change a lot + // during the lifetime of this worker). + this.accumulate = false; + // The buffer receiving chunks when accumulating content. + this.contentBuffer = []; + // The list of generated directory records. + this.dirRecords = []; + // The offset (in bytes) from the beginning of the zip file for the current source. + this.currentSourceOffset = 0; + // The total number of entries in this zip file. + this.entriesCount = 0; + // the name of the file currently being added, null when handling the end of the zip file. + // Used for the emitted metadata. + this.currentFile = null; + + + + this._sources = []; +} +utils.inherits(ZipFileWorker, GenericWorker); + +/** + * @see GenericWorker.push + */ +ZipFileWorker.prototype.push = function (chunk) { + + var currentFilePercent = chunk.meta.percent || 0; + var entriesCount = this.entriesCount; + var remainingFiles = this._sources.length; + + if(this.accumulate) { + this.contentBuffer.push(chunk); + } else { + this.bytesWritten += chunk.data.length; + + GenericWorker.prototype.push.call(this, { + data : chunk.data, + meta : { + currentFile : this.currentFile, + percent : entriesCount ? (currentFilePercent + 100 * (entriesCount - remainingFiles - 1)) / entriesCount : 100 + } + }); + } +}; + +/** + * The worker started a new source (an other worker). + * @param {Object} streamInfo the streamInfo object from the new source. + */ +ZipFileWorker.prototype.openedSource = function (streamInfo) { + this.currentSourceOffset = this.bytesWritten; + this.currentFile = streamInfo['file'].name; + + var streamedContent = this.streamFiles && !streamInfo['file'].dir; + + // don't stream folders (because they don't have any content) + if(streamedContent) { + var record = generateZipParts(streamInfo, streamedContent, false, this.currentSourceOffset, this.zipPlatform, this.encodeFileName); + this.push({ + data : record.fileRecord, + meta : {percent:0} + }); + } else { + // we need to wait for the whole file before pushing anything + this.accumulate = true; + } +}; + +/** + * The worker finished a source (an other worker). + * @param {Object} streamInfo the streamInfo object from the finished source. + */ +ZipFileWorker.prototype.closedSource = function (streamInfo) { + this.accumulate = false; + var streamedContent = this.streamFiles && !streamInfo['file'].dir; + var record = generateZipParts(streamInfo, streamedContent, true, this.currentSourceOffset, this.zipPlatform, this.encodeFileName); + + this.dirRecords.push(record.dirRecord); + if(streamedContent) { + // after the streamed file, we put data descriptors + this.push({ + data : generateDataDescriptors(streamInfo), + meta : {percent:100} + }); + } else { + // the content wasn't streamed, we need to push everything now + // first the file record, then the content + this.push({ + data : record.fileRecord, + meta : {percent:0} + }); + while(this.contentBuffer.length) { + this.push(this.contentBuffer.shift()); + } + } + this.currentFile = null; +}; + +/** + * @see GenericWorker.flush + */ +ZipFileWorker.prototype.flush = function () { + + var localDirLength = this.bytesWritten; + for(var i = 0; i < this.dirRecords.length; i++) { + this.push({ + data : this.dirRecords[i], + meta : {percent:100} + }); + } + var centralDirLength = this.bytesWritten - localDirLength; + + var dirEnd = generateCentralDirectoryEnd(this.dirRecords.length, centralDirLength, localDirLength, this.zipComment, this.encodeFileName); + + this.push({ + data : dirEnd, + meta : {percent:100} + }); +}; + +/** + * Prepare the next source to be read. + */ +ZipFileWorker.prototype.prepareNextSource = function () { + this.previous = this._sources.shift(); + this.openedSource(this.previous.streamInfo); + if (this.isPaused) { + this.previous.pause(); + } else { + this.previous.resume(); + } +}; + +/** + * @see GenericWorker.registerPrevious + */ +ZipFileWorker.prototype.registerPrevious = function (previous) { + this._sources.push(previous); + var self = this; + + previous.on('data', function (chunk) { + self.processChunk(chunk); + }); + previous.on('end', function () { + self.closedSource(self.previous.streamInfo); + if(self._sources.length) { + self.prepareNextSource(); + } else { + self.end(); + } + }); + previous.on('error', function (e) { + self.error(e); + }); + return this; +}; + +/** + * @see GenericWorker.resume + */ +ZipFileWorker.prototype.resume = function () { + if(!GenericWorker.prototype.resume.call(this)) { + return false; + } + + if (!this.previous && this._sources.length) { + this.prepareNextSource(); + return true; + } + if (!this.previous && !this._sources.length && !this.generatedError) { + this.end(); + return true; + } +}; + +/** + * @see GenericWorker.error + */ +ZipFileWorker.prototype.error = function (e) { + var sources = this._sources; + if(!GenericWorker.prototype.error.call(this, e)) { + return false; + } + for(var i = 0; i < sources.length; i++) { + try { + sources[i].error(e); + } catch(e) { + // the `error` exploded, nothing to do + } + } + return true; +}; + +/** + * @see GenericWorker.lock + */ +ZipFileWorker.prototype.lock = function () { + GenericWorker.prototype.lock.call(this); + var sources = this._sources; + for(var i = 0; i < sources.length; i++) { + sources[i].lock(); + } +}; + +module.exports = ZipFileWorker; + +},{"../crc32":4,"../signature":23,"../stream/GenericWorker":28,"../utf8":31,"../utils":32}],9:[function(require,module,exports){ +'use strict'; + +var compressions = require('../compressions'); +var ZipFileWorker = require('./ZipFileWorker'); + +/** + * Find the compression to use. + * @param {String} fileCompression the compression defined at the file level, if any. + * @param {String} zipCompression the compression defined at the load() level. + * @return {Object} the compression object to use. + */ +var getCompression = function (fileCompression, zipCompression) { + + var compressionName = fileCompression || zipCompression; + var compression = compressions[compressionName]; + if (!compression) { + throw new Error(compressionName + " is not a valid compression method !"); + } + return compression; +}; + +/** + * Create a worker to generate a zip file. + * @param {JSZip} zip the JSZip instance at the right root level. + * @param {Object} options to generate the zip file. + * @param {String} comment the comment to use. + */ +exports.generateWorker = function (zip, options, comment) { + + var zipFileWorker = new ZipFileWorker(options.streamFiles, comment, options.platform, options.encodeFileName); + var entriesCount = 0; + try { + + zip.forEach(function (relativePath, file) { + entriesCount++; + var compression = getCompression(file.options.compression, options.compression); + var compressionOptions = file.options.compressionOptions || options.compressionOptions || {}; + var dir = file.dir, date = file.date; + + file._compressWorker(compression, compressionOptions) + .withStreamInfo("file", { + name : relativePath, + dir : dir, + date : date, + comment : file.comment || "", + unixPermissions : file.unixPermissions, + dosPermissions : file.dosPermissions + }) + .pipe(zipFileWorker); + }); + zipFileWorker.entriesCount = entriesCount; + } catch (e) { + zipFileWorker.error(e); + } + + return zipFileWorker; +}; + +},{"../compressions":3,"./ZipFileWorker":8}],10:[function(require,module,exports){ +'use strict'; + +/** + * Representation a of zip file in js + * @constructor + */ +function JSZip() { + // if this constructor is used without `new`, it adds `new` before itself: + if(!(this instanceof JSZip)) { + return new JSZip(); + } + + if(arguments.length) { + throw new Error("The constructor with parameters has been removed in JSZip 3.0, please check the upgrade guide."); + } + + // object containing the files : + // { + // "folder/" : {...}, + // "folder/data.txt" : {...} + // } + // NOTE: we use a null prototype because we do not + // want filenames like "toString" coming from a zip file + // to overwrite methods and attributes in a normal Object. + this.files = Object.create(null); + + this.comment = null; + + // Where we are in the hierarchy + this.root = ""; + this.clone = function() { + var newObj = new JSZip(); + for (var i in this) { + if (typeof this[i] !== "function") { + newObj[i] = this[i]; + } + } + return newObj; + }; +} +JSZip.prototype = require('./object'); +JSZip.prototype.loadAsync = require('./load'); +JSZip.support = require('./support'); +JSZip.defaults = require('./defaults'); + +// TODO find a better way to handle this version, +// a require('package.json').version doesn't work with webpack, see #327 +JSZip.version = "3.7.1"; + +JSZip.loadAsync = function (content, options) { + return new JSZip().loadAsync(content, options); +}; + +JSZip.external = require("./external"); +module.exports = JSZip; + +},{"./defaults":5,"./external":6,"./load":11,"./object":15,"./support":30}],11:[function(require,module,exports){ +'use strict'; +var utils = require('./utils'); +var external = require("./external"); +var utf8 = require('./utf8'); +var ZipEntries = require('./zipEntries'); +var Crc32Probe = require('./stream/Crc32Probe'); +var nodejsUtils = require("./nodejsUtils"); + +/** + * Check the CRC32 of an entry. + * @param {ZipEntry} zipEntry the zip entry to check. + * @return {Promise} the result. + */ +function checkEntryCRC32(zipEntry) { + return new external.Promise(function (resolve, reject) { + var worker = zipEntry.decompressed.getContentWorker().pipe(new Crc32Probe()); + worker.on("error", function (e) { + reject(e); + }) + .on("end", function () { + if (worker.streamInfo.crc32 !== zipEntry.decompressed.crc32) { + reject(new Error("Corrupted zip : CRC32 mismatch")); + } else { + resolve(); + } + }) + .resume(); + }); +} + +module.exports = function (data, options) { + var zip = this; + options = utils.extend(options || {}, { + base64: false, + checkCRC32: false, + optimizedBinaryString: false, + createFolders: false, + decodeFileName: utf8.utf8decode + }); + + if (nodejsUtils.isNode && nodejsUtils.isStream(data)) { + return external.Promise.reject(new Error("JSZip can't accept a stream when loading a zip file.")); + } + + return utils.prepareContent("the loaded zip file", data, true, options.optimizedBinaryString, options.base64) + .then(function (data) { + var zipEntries = new ZipEntries(options); + zipEntries.load(data); + return zipEntries; + }).then(function checkCRC32(zipEntries) { + var promises = [external.Promise.resolve(zipEntries)]; + var files = zipEntries.files; + if (options.checkCRC32) { + for (var i = 0; i < files.length; i++) { + promises.push(checkEntryCRC32(files[i])); + } + } + return external.Promise.all(promises); + }).then(function addFiles(results) { + var zipEntries = results.shift(); + var files = zipEntries.files; + for (var i = 0; i < files.length; i++) { + var input = files[i]; + zip.file(input.fileNameStr, input.decompressed, { + binary: true, + optimizedBinaryString: true, + date: input.date, + dir: input.dir, + comment: input.fileCommentStr.length ? input.fileCommentStr : null, + unixPermissions: input.unixPermissions, + dosPermissions: input.dosPermissions, + createFolders: options.createFolders + }); + } + if (zipEntries.zipComment.length) { + zip.comment = zipEntries.zipComment; + } + + return zip; + }); +}; + +},{"./external":6,"./nodejsUtils":14,"./stream/Crc32Probe":25,"./utf8":31,"./utils":32,"./zipEntries":33}],12:[function(require,module,exports){ +"use strict"; + +var utils = require('../utils'); +var GenericWorker = require('../stream/GenericWorker'); + +/** + * A worker that use a nodejs stream as source. + * @constructor + * @param {String} filename the name of the file entry for this stream. + * @param {Readable} stream the nodejs stream. + */ +function NodejsStreamInputAdapter(filename, stream) { + GenericWorker.call(this, "Nodejs stream input adapter for " + filename); + this._upstreamEnded = false; + this._bindStream(stream); +} + +utils.inherits(NodejsStreamInputAdapter, GenericWorker); + +/** + * Prepare the stream and bind the callbacks on it. + * Do this ASAP on node 0.10 ! A lazy binding doesn't always work. + * @param {Stream} stream the nodejs stream to use. + */ +NodejsStreamInputAdapter.prototype._bindStream = function (stream) { + var self = this; + this._stream = stream; + stream.pause(); + stream + .on("data", function (chunk) { + self.push({ + data: chunk, + meta : { + percent : 0 + } + }); + }) + .on("error", function (e) { + if(self.isPaused) { + this.generatedError = e; + } else { + self.error(e); + } + }) + .on("end", function () { + if(self.isPaused) { + self._upstreamEnded = true; + } else { + self.end(); + } + }); +}; +NodejsStreamInputAdapter.prototype.pause = function () { + if(!GenericWorker.prototype.pause.call(this)) { + return false; + } + this._stream.pause(); + return true; +}; +NodejsStreamInputAdapter.prototype.resume = function () { + if(!GenericWorker.prototype.resume.call(this)) { + return false; + } + + if(this._upstreamEnded) { + this.end(); + } else { + this._stream.resume(); + } + + return true; +}; + +module.exports = NodejsStreamInputAdapter; + +},{"../stream/GenericWorker":28,"../utils":32}],13:[function(require,module,exports){ +'use strict'; + +var Readable = require('readable-stream').Readable; + +var utils = require('../utils'); +utils.inherits(NodejsStreamOutputAdapter, Readable); + +/** +* A nodejs stream using a worker as source. +* @see the SourceWrapper in http://nodejs.org/api/stream.html +* @constructor +* @param {StreamHelper} helper the helper wrapping the worker +* @param {Object} options the nodejs stream options +* @param {Function} updateCb the update callback. +*/ +function NodejsStreamOutputAdapter(helper, options, updateCb) { + Readable.call(this, options); + this._helper = helper; + + var self = this; + helper.on("data", function (data, meta) { + if (!self.push(data)) { + self._helper.pause(); + } + if(updateCb) { + updateCb(meta); + } + }) + .on("error", function(e) { + self.emit('error', e); + }) + .on("end", function () { + self.push(null); + }); +} + + +NodejsStreamOutputAdapter.prototype._read = function() { + this._helper.resume(); +}; + +module.exports = NodejsStreamOutputAdapter; + +},{"../utils":32,"readable-stream":16}],14:[function(require,module,exports){ +'use strict'; + +module.exports = { + /** + * True if this is running in Nodejs, will be undefined in a browser. + * In a browser, browserify won't include this file and the whole module + * will be resolved an empty object. + */ + isNode : typeof Buffer !== "undefined", + /** + * Create a new nodejs Buffer from an existing content. + * @param {Object} data the data to pass to the constructor. + * @param {String} encoding the encoding to use. + * @return {Buffer} a new Buffer. + */ + newBufferFrom: function(data, encoding) { + if (Buffer.from && Buffer.from !== Uint8Array.from) { + return Buffer.from(data, encoding); + } else { + if (typeof data === "number") { + // Safeguard for old Node.js versions. On newer versions, + // Buffer.from(number) / Buffer(number, encoding) already throw. + throw new Error("The \"data\" argument must not be a number"); + } + return new Buffer(data, encoding); + } + }, + /** + * Create a new nodejs Buffer with the specified size. + * @param {Integer} size the size of the buffer. + * @return {Buffer} a new Buffer. + */ + allocBuffer: function (size) { + if (Buffer.alloc) { + return Buffer.alloc(size); + } else { + var buf = new Buffer(size); + buf.fill(0); + return buf; + } + }, + /** + * Find out if an object is a Buffer. + * @param {Object} b the object to test. + * @return {Boolean} true if the object is a Buffer, false otherwise. + */ + isBuffer : function(b){ + return Buffer.isBuffer(b); + }, + + isStream : function (obj) { + return obj && + typeof obj.on === "function" && + typeof obj.pause === "function" && + typeof obj.resume === "function"; + } +}; + +},{}],15:[function(require,module,exports){ +'use strict'; +var utf8 = require('./utf8'); +var utils = require('./utils'); +var GenericWorker = require('./stream/GenericWorker'); +var StreamHelper = require('./stream/StreamHelper'); +var defaults = require('./defaults'); +var CompressedObject = require('./compressedObject'); +var ZipObject = require('./zipObject'); +var generate = require("./generate"); +var nodejsUtils = require("./nodejsUtils"); +var NodejsStreamInputAdapter = require("./nodejs/NodejsStreamInputAdapter"); + + +/** + * Add a file in the current folder. + * @private + * @param {string} name the name of the file + * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data of the file + * @param {Object} originalOptions the options of the file + * @return {Object} the new file. + */ +var fileAdd = function(name, data, originalOptions) { + // be sure sub folders exist + var dataType = utils.getTypeOf(data), + parent; + + + /* + * Correct options. + */ + + var o = utils.extend(originalOptions || {}, defaults); + o.date = o.date || new Date(); + if (o.compression !== null) { + o.compression = o.compression.toUpperCase(); + } + + if (typeof o.unixPermissions === "string") { + o.unixPermissions = parseInt(o.unixPermissions, 8); + } + + // UNX_IFDIR 0040000 see zipinfo.c + if (o.unixPermissions && (o.unixPermissions & 0x4000)) { + o.dir = true; + } + // Bit 4 Directory + if (o.dosPermissions && (o.dosPermissions & 0x0010)) { + o.dir = true; + } + + if (o.dir) { + name = forceTrailingSlash(name); + } + if (o.createFolders && (parent = parentFolder(name))) { + folderAdd.call(this, parent, true); + } + + var isUnicodeString = dataType === "string" && o.binary === false && o.base64 === false; + if (!originalOptions || typeof originalOptions.binary === "undefined") { + o.binary = !isUnicodeString; + } + + + var isCompressedEmpty = (data instanceof CompressedObject) && data.uncompressedSize === 0; + + if (isCompressedEmpty || o.dir || !data || data.length === 0) { + o.base64 = false; + o.binary = true; + data = ""; + o.compression = "STORE"; + dataType = "string"; + } + + /* + * Convert content to fit. + */ + + var zipObjectContent = null; + if (data instanceof CompressedObject || data instanceof GenericWorker) { + zipObjectContent = data; + } else if (nodejsUtils.isNode && nodejsUtils.isStream(data)) { + zipObjectContent = new NodejsStreamInputAdapter(name, data); + } else { + zipObjectContent = utils.prepareContent(name, data, o.binary, o.optimizedBinaryString, o.base64); + } + + var object = new ZipObject(name, zipObjectContent, o); + this.files[name] = object; + /* + TODO: we can't throw an exception because we have async promises + (we can have a promise of a Date() for example) but returning a + promise is useless because file(name, data) returns the JSZip + object for chaining. Should we break that to allow the user + to catch the error ? + + return external.Promise.resolve(zipObjectContent) + .then(function () { + return object; + }); + */ +}; + +/** + * Find the parent folder of the path. + * @private + * @param {string} path the path to use + * @return {string} the parent folder, or "" + */ +var parentFolder = function (path) { + if (path.slice(-1) === '/') { + path = path.substring(0, path.length - 1); + } + var lastSlash = path.lastIndexOf('/'); + return (lastSlash > 0) ? path.substring(0, lastSlash) : ""; +}; + +/** + * Returns the path with a slash at the end. + * @private + * @param {String} path the path to check. + * @return {String} the path with a trailing slash. + */ +var forceTrailingSlash = function(path) { + // Check the name ends with a / + if (path.slice(-1) !== "/") { + path += "/"; // IE doesn't like substr(-1) + } + return path; +}; + +/** + * Add a (sub) folder in the current folder. + * @private + * @param {string} name the folder's name + * @param {boolean=} [createFolders] If true, automatically create sub + * folders. Defaults to false. + * @return {Object} the new folder. + */ +var folderAdd = function(name, createFolders) { + createFolders = (typeof createFolders !== 'undefined') ? createFolders : defaults.createFolders; + + name = forceTrailingSlash(name); + + // Does this folder already exist? + if (!this.files[name]) { + fileAdd.call(this, name, null, { + dir: true, + createFolders: createFolders + }); + } + return this.files[name]; +}; + +/** +* Cross-window, cross-Node-context regular expression detection +* @param {Object} object Anything +* @return {Boolean} true if the object is a regular expression, +* false otherwise +*/ +function isRegExp(object) { + return Object.prototype.toString.call(object) === "[object RegExp]"; +} + +// return the actual prototype of JSZip +var out = { + /** + * @see loadAsync + */ + load: function() { + throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide."); + }, + + + /** + * Call a callback function for each entry at this folder level. + * @param {Function} cb the callback function: + * function (relativePath, file) {...} + * It takes 2 arguments : the relative path and the file. + */ + forEach: function(cb) { + var filename, relativePath, file; + /* jshint ignore:start */ + // ignore warning about unwanted properties because this.files is a null prototype object + for (filename in this.files) { + file = this.files[filename]; + relativePath = filename.slice(this.root.length, filename.length); + if (relativePath && filename.slice(0, this.root.length) === this.root) { // the file is in the current root + cb(relativePath, file); // TODO reverse the parameters ? need to be clean AND consistent with the filter search fn... + } + } + /* jshint ignore:end */ + }, + + /** + * Filter nested files/folders with the specified function. + * @param {Function} search the predicate to use : + * function (relativePath, file) {...} + * It takes 2 arguments : the relative path and the file. + * @return {Array} An array of matching elements. + */ + filter: function(search) { + var result = []; + this.forEach(function (relativePath, entry) { + if (search(relativePath, entry)) { // the file matches the function + result.push(entry); + } + + }); + return result; + }, + + /** + * Add a file to the zip file, or search a file. + * @param {string|RegExp} name The name of the file to add (if data is defined), + * the name of the file to find (if no data) or a regex to match files. + * @param {String|ArrayBuffer|Uint8Array|Buffer} data The file data, either raw or base64 encoded + * @param {Object} o File options + * @return {JSZip|Object|Array} this JSZip object (when adding a file), + * a file (when searching by string) or an array of files (when searching by regex). + */ + file: function(name, data, o) { + if (arguments.length === 1) { + if (isRegExp(name)) { + var regexp = name; + return this.filter(function(relativePath, file) { + return !file.dir && regexp.test(relativePath); + }); + } + else { // text + var obj = this.files[this.root + name]; + if (obj && !obj.dir) { + return obj; + } else { + return null; + } + } + } + else { // more than one argument : we have data ! + name = this.root + name; + fileAdd.call(this, name, data, o); + } + return this; + }, + + /** + * Add a directory to the zip file, or search. + * @param {String|RegExp} arg The name of the directory to add, or a regex to search folders. + * @return {JSZip} an object with the new directory as the root, or an array containing matching folders. + */ + folder: function(arg) { + if (!arg) { + return this; + } + + if (isRegExp(arg)) { + return this.filter(function(relativePath, file) { + return file.dir && arg.test(relativePath); + }); + } + + // else, name is a new folder + var name = this.root + arg; + var newFolder = folderAdd.call(this, name); + + // Allow chaining by returning a new object with this folder as the root + var ret = this.clone(); + ret.root = newFolder.name; + return ret; + }, + + /** + * Delete a file, or a directory and all sub-files, from the zip + * @param {string} name the name of the file to delete + * @return {JSZip} this JSZip object + */ + remove: function(name) { + name = this.root + name; + var file = this.files[name]; + if (!file) { + // Look for any folders + if (name.slice(-1) !== "/") { + name += "/"; + } + file = this.files[name]; + } + + if (file && !file.dir) { + // file + delete this.files[name]; + } else { + // maybe a folder, delete recursively + var kids = this.filter(function(relativePath, file) { + return file.name.slice(0, name.length) === name; + }); + for (var i = 0; i < kids.length; i++) { + delete this.files[kids[i].name]; + } + } + + return this; + }, + + /** + * Generate the complete zip file + * @param {Object} options the options to generate the zip file : + * - compression, "STORE" by default. + * - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob. + * @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the zip file + */ + generate: function(options) { + throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide."); + }, + + /** + * Generate the complete zip file as an internal stream. + * @param {Object} options the options to generate the zip file : + * - compression, "STORE" by default. + * - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob. + * @return {StreamHelper} the streamed zip file. + */ + generateInternalStream: function(options) { + var worker, opts = {}; + try { + opts = utils.extend(options || {}, { + streamFiles: false, + compression: "STORE", + compressionOptions : null, + type: "", + platform: "DOS", + comment: null, + mimeType: 'application/zip', + encodeFileName: utf8.utf8encode + }); + + opts.type = opts.type.toLowerCase(); + opts.compression = opts.compression.toUpperCase(); + + // "binarystring" is preferred but the internals use "string". + if(opts.type === "binarystring") { + opts.type = "string"; + } + + if (!opts.type) { + throw new Error("No output type specified."); + } + + utils.checkSupport(opts.type); + + // accept nodejs `process.platform` + if( + opts.platform === 'darwin' || + opts.platform === 'freebsd' || + opts.platform === 'linux' || + opts.platform === 'sunos' + ) { + opts.platform = "UNIX"; + } + if (opts.platform === 'win32') { + opts.platform = "DOS"; + } + + var comment = opts.comment || this.comment || ""; + worker = generate.generateWorker(this, opts, comment); + } catch (e) { + worker = new GenericWorker("error"); + worker.error(e); + } + return new StreamHelper(worker, opts.type || "string", opts.mimeType); + }, + /** + * Generate the complete zip file asynchronously. + * @see generateInternalStream + */ + generateAsync: function(options, onUpdate) { + return this.generateInternalStream(options).accumulate(onUpdate); + }, + /** + * Generate the complete zip file asynchronously. + * @see generateInternalStream + */ + generateNodeStream: function(options, onUpdate) { + options = options || {}; + if (!options.type) { + options.type = "nodebuffer"; + } + return this.generateInternalStream(options).toNodejsStream(onUpdate); + } +}; +module.exports = out; + +},{"./compressedObject":2,"./defaults":5,"./generate":9,"./nodejs/NodejsStreamInputAdapter":12,"./nodejsUtils":14,"./stream/GenericWorker":28,"./stream/StreamHelper":29,"./utf8":31,"./utils":32,"./zipObject":35}],16:[function(require,module,exports){ +/* + * This file is used by module bundlers (browserify/webpack/etc) when + * including a stream implementation. We use "readable-stream" to get a + * consistent behavior between nodejs versions but bundlers often have a shim + * for "stream". Using this shim greatly improve the compatibility and greatly + * reduce the final size of the bundle (only one stream implementation, not + * two). + */ +module.exports = require("stream"); + +},{"stream":undefined}],17:[function(require,module,exports){ +'use strict'; +var DataReader = require('./DataReader'); +var utils = require('../utils'); + +function ArrayReader(data) { + DataReader.call(this, data); + for(var i = 0; i < this.data.length; i++) { + data[i] = data[i] & 0xFF; + } +} +utils.inherits(ArrayReader, DataReader); +/** + * @see DataReader.byteAt + */ +ArrayReader.prototype.byteAt = function(i) { + return this.data[this.zero + i]; +}; +/** + * @see DataReader.lastIndexOfSignature + */ +ArrayReader.prototype.lastIndexOfSignature = function(sig) { + var sig0 = sig.charCodeAt(0), + sig1 = sig.charCodeAt(1), + sig2 = sig.charCodeAt(2), + sig3 = sig.charCodeAt(3); + for (var i = this.length - 4; i >= 0; --i) { + if (this.data[i] === sig0 && this.data[i + 1] === sig1 && this.data[i + 2] === sig2 && this.data[i + 3] === sig3) { + return i - this.zero; + } + } + + return -1; +}; +/** + * @see DataReader.readAndCheckSignature + */ +ArrayReader.prototype.readAndCheckSignature = function (sig) { + var sig0 = sig.charCodeAt(0), + sig1 = sig.charCodeAt(1), + sig2 = sig.charCodeAt(2), + sig3 = sig.charCodeAt(3), + data = this.readData(4); + return sig0 === data[0] && sig1 === data[1] && sig2 === data[2] && sig3 === data[3]; +}; +/** + * @see DataReader.readData + */ +ArrayReader.prototype.readData = function(size) { + this.checkOffset(size); + if(size === 0) { + return []; + } + var result = this.data.slice(this.zero + this.index, this.zero + this.index + size); + this.index += size; + return result; +}; +module.exports = ArrayReader; + +},{"../utils":32,"./DataReader":18}],18:[function(require,module,exports){ +'use strict'; +var utils = require('../utils'); + +function DataReader(data) { + this.data = data; // type : see implementation + this.length = data.length; + this.index = 0; + this.zero = 0; +} +DataReader.prototype = { + /** + * Check that the offset will not go too far. + * @param {string} offset the additional offset to check. + * @throws {Error} an Error if the offset is out of bounds. + */ + checkOffset: function(offset) { + this.checkIndex(this.index + offset); + }, + /** + * Check that the specified index will not be too far. + * @param {string} newIndex the index to check. + * @throws {Error} an Error if the index is out of bounds. + */ + checkIndex: function(newIndex) { + if (this.length < this.zero + newIndex || newIndex < 0) { + throw new Error("End of data reached (data length = " + this.length + ", asked index = " + (newIndex) + "). Corrupted zip ?"); + } + }, + /** + * Change the index. + * @param {number} newIndex The new index. + * @throws {Error} if the new index is out of the data. + */ + setIndex: function(newIndex) { + this.checkIndex(newIndex); + this.index = newIndex; + }, + /** + * Skip the next n bytes. + * @param {number} n the number of bytes to skip. + * @throws {Error} if the new index is out of the data. + */ + skip: function(n) { + this.setIndex(this.index + n); + }, + /** + * Get the byte at the specified index. + * @param {number} i the index to use. + * @return {number} a byte. + */ + byteAt: function(i) { + // see implementations + }, + /** + * Get the next number with a given byte size. + * @param {number} size the number of bytes to read. + * @return {number} the corresponding number. + */ + readInt: function(size) { + var result = 0, + i; + this.checkOffset(size); + for (i = this.index + size - 1; i >= this.index; i--) { + result = (result << 8) + this.byteAt(i); + } + this.index += size; + return result; + }, + /** + * Get the next string with a given byte size. + * @param {number} size the number of bytes to read. + * @return {string} the corresponding string. + */ + readString: function(size) { + return utils.transformTo("string", this.readData(size)); + }, + /** + * Get raw data without conversion, bytes. + * @param {number} size the number of bytes to read. + * @return {Object} the raw data, implementation specific. + */ + readData: function(size) { + // see implementations + }, + /** + * Find the last occurrence of a zip signature (4 bytes). + * @param {string} sig the signature to find. + * @return {number} the index of the last occurrence, -1 if not found. + */ + lastIndexOfSignature: function(sig) { + // see implementations + }, + /** + * Read the signature (4 bytes) at the current position and compare it with sig. + * @param {string} sig the expected signature + * @return {boolean} true if the signature matches, false otherwise. + */ + readAndCheckSignature: function(sig) { + // see implementations + }, + /** + * Get the next date. + * @return {Date} the date. + */ + readDate: function() { + var dostime = this.readInt(4); + return new Date(Date.UTC( + ((dostime >> 25) & 0x7f) + 1980, // year + ((dostime >> 21) & 0x0f) - 1, // month + (dostime >> 16) & 0x1f, // day + (dostime >> 11) & 0x1f, // hour + (dostime >> 5) & 0x3f, // minute + (dostime & 0x1f) << 1)); // second + } +}; +module.exports = DataReader; + +},{"../utils":32}],19:[function(require,module,exports){ +'use strict'; +var Uint8ArrayReader = require('./Uint8ArrayReader'); +var utils = require('../utils'); + +function NodeBufferReader(data) { + Uint8ArrayReader.call(this, data); +} +utils.inherits(NodeBufferReader, Uint8ArrayReader); + +/** + * @see DataReader.readData + */ +NodeBufferReader.prototype.readData = function(size) { + this.checkOffset(size); + var result = this.data.slice(this.zero + this.index, this.zero + this.index + size); + this.index += size; + return result; +}; +module.exports = NodeBufferReader; + +},{"../utils":32,"./Uint8ArrayReader":21}],20:[function(require,module,exports){ +'use strict'; +var DataReader = require('./DataReader'); +var utils = require('../utils'); + +function StringReader(data) { + DataReader.call(this, data); +} +utils.inherits(StringReader, DataReader); +/** + * @see DataReader.byteAt + */ +StringReader.prototype.byteAt = function(i) { + return this.data.charCodeAt(this.zero + i); +}; +/** + * @see DataReader.lastIndexOfSignature + */ +StringReader.prototype.lastIndexOfSignature = function(sig) { + return this.data.lastIndexOf(sig) - this.zero; +}; +/** + * @see DataReader.readAndCheckSignature + */ +StringReader.prototype.readAndCheckSignature = function (sig) { + var data = this.readData(4); + return sig === data; +}; +/** + * @see DataReader.readData + */ +StringReader.prototype.readData = function(size) { + this.checkOffset(size); + // this will work because the constructor applied the "& 0xff" mask. + var result = this.data.slice(this.zero + this.index, this.zero + this.index + size); + this.index += size; + return result; +}; +module.exports = StringReader; + +},{"../utils":32,"./DataReader":18}],21:[function(require,module,exports){ +'use strict'; +var ArrayReader = require('./ArrayReader'); +var utils = require('../utils'); + +function Uint8ArrayReader(data) { + ArrayReader.call(this, data); +} +utils.inherits(Uint8ArrayReader, ArrayReader); +/** + * @see DataReader.readData + */ +Uint8ArrayReader.prototype.readData = function(size) { + this.checkOffset(size); + if(size === 0) { + // in IE10, when using subarray(idx, idx), we get the array [0x00] instead of []. + return new Uint8Array(0); + } + var result = this.data.subarray(this.zero + this.index, this.zero + this.index + size); + this.index += size; + return result; +}; +module.exports = Uint8ArrayReader; + +},{"../utils":32,"./ArrayReader":17}],22:[function(require,module,exports){ +'use strict'; + +var utils = require('../utils'); +var support = require('../support'); +var ArrayReader = require('./ArrayReader'); +var StringReader = require('./StringReader'); +var NodeBufferReader = require('./NodeBufferReader'); +var Uint8ArrayReader = require('./Uint8ArrayReader'); + +/** + * Create a reader adapted to the data. + * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data to read. + * @return {DataReader} the data reader. + */ +module.exports = function (data) { + var type = utils.getTypeOf(data); + utils.checkSupport(type); + if (type === "string" && !support.uint8array) { + return new StringReader(data); + } + if (type === "nodebuffer") { + return new NodeBufferReader(data); + } + if (support.uint8array) { + return new Uint8ArrayReader(utils.transformTo("uint8array", data)); + } + return new ArrayReader(utils.transformTo("array", data)); +}; + +},{"../support":30,"../utils":32,"./ArrayReader":17,"./NodeBufferReader":19,"./StringReader":20,"./Uint8ArrayReader":21}],23:[function(require,module,exports){ +'use strict'; +exports.LOCAL_FILE_HEADER = "PK\x03\x04"; +exports.CENTRAL_FILE_HEADER = "PK\x01\x02"; +exports.CENTRAL_DIRECTORY_END = "PK\x05\x06"; +exports.ZIP64_CENTRAL_DIRECTORY_LOCATOR = "PK\x06\x07"; +exports.ZIP64_CENTRAL_DIRECTORY_END = "PK\x06\x06"; +exports.DATA_DESCRIPTOR = "PK\x07\x08"; + +},{}],24:[function(require,module,exports){ +'use strict'; + +var GenericWorker = require('./GenericWorker'); +var utils = require('../utils'); + +/** + * A worker which convert chunks to a specified type. + * @constructor + * @param {String} destType the destination type. + */ +function ConvertWorker(destType) { + GenericWorker.call(this, "ConvertWorker to " + destType); + this.destType = destType; +} +utils.inherits(ConvertWorker, GenericWorker); + +/** + * @see GenericWorker.processChunk + */ +ConvertWorker.prototype.processChunk = function (chunk) { + this.push({ + data : utils.transformTo(this.destType, chunk.data), + meta : chunk.meta + }); +}; +module.exports = ConvertWorker; + +},{"../utils":32,"./GenericWorker":28}],25:[function(require,module,exports){ +'use strict'; + +var GenericWorker = require('./GenericWorker'); +var crc32 = require('../crc32'); +var utils = require('../utils'); + +/** + * A worker which calculate the crc32 of the data flowing through. + * @constructor + */ +function Crc32Probe() { + GenericWorker.call(this, "Crc32Probe"); + this.withStreamInfo("crc32", 0); +} +utils.inherits(Crc32Probe, GenericWorker); + +/** + * @see GenericWorker.processChunk + */ +Crc32Probe.prototype.processChunk = function (chunk) { + this.streamInfo.crc32 = crc32(chunk.data, this.streamInfo.crc32 || 0); + this.push(chunk); +}; +module.exports = Crc32Probe; + +},{"../crc32":4,"../utils":32,"./GenericWorker":28}],26:[function(require,module,exports){ +'use strict'; + +var utils = require('../utils'); +var GenericWorker = require('./GenericWorker'); + +/** + * A worker which calculate the total length of the data flowing through. + * @constructor + * @param {String} propName the name used to expose the length + */ +function DataLengthProbe(propName) { + GenericWorker.call(this, "DataLengthProbe for " + propName); + this.propName = propName; + this.withStreamInfo(propName, 0); +} +utils.inherits(DataLengthProbe, GenericWorker); + +/** + * @see GenericWorker.processChunk + */ +DataLengthProbe.prototype.processChunk = function (chunk) { + if(chunk) { + var length = this.streamInfo[this.propName] || 0; + this.streamInfo[this.propName] = length + chunk.data.length; + } + GenericWorker.prototype.processChunk.call(this, chunk); +}; +module.exports = DataLengthProbe; + + +},{"../utils":32,"./GenericWorker":28}],27:[function(require,module,exports){ +'use strict'; + +var utils = require('../utils'); +var GenericWorker = require('./GenericWorker'); + +// the size of the generated chunks +// TODO expose this as a public variable +var DEFAULT_BLOCK_SIZE = 16 * 1024; + +/** + * A worker that reads a content and emits chunks. + * @constructor + * @param {Promise} dataP the promise of the data to split + */ +function DataWorker(dataP) { + GenericWorker.call(this, "DataWorker"); + var self = this; + this.dataIsReady = false; + this.index = 0; + this.max = 0; + this.data = null; + this.type = ""; + + this._tickScheduled = false; + + dataP.then(function (data) { + self.dataIsReady = true; + self.data = data; + self.max = data && data.length || 0; + self.type = utils.getTypeOf(data); + if(!self.isPaused) { + self._tickAndRepeat(); + } + }, function (e) { + self.error(e); + }); +} + +utils.inherits(DataWorker, GenericWorker); + +/** + * @see GenericWorker.cleanUp + */ +DataWorker.prototype.cleanUp = function () { + GenericWorker.prototype.cleanUp.call(this); + this.data = null; +}; + +/** + * @see GenericWorker.resume + */ +DataWorker.prototype.resume = function () { + if(!GenericWorker.prototype.resume.call(this)) { + return false; + } + + if (!this._tickScheduled && this.dataIsReady) { + this._tickScheduled = true; + utils.delay(this._tickAndRepeat, [], this); + } + return true; +}; + +/** + * Trigger a tick a schedule an other call to this function. + */ +DataWorker.prototype._tickAndRepeat = function() { + this._tickScheduled = false; + if(this.isPaused || this.isFinished) { + return; + } + this._tick(); + if(!this.isFinished) { + utils.delay(this._tickAndRepeat, [], this); + this._tickScheduled = true; + } +}; + +/** + * Read and push a chunk. + */ +DataWorker.prototype._tick = function() { + + if(this.isPaused || this.isFinished) { + return false; + } + + var size = DEFAULT_BLOCK_SIZE; + var data = null, nextIndex = Math.min(this.max, this.index + size); + if (this.index >= this.max) { + // EOF + return this.end(); + } else { + switch(this.type) { + case "string": + data = this.data.substring(this.index, nextIndex); + break; + case "uint8array": + data = this.data.subarray(this.index, nextIndex); + break; + case "array": + case "nodebuffer": + data = this.data.slice(this.index, nextIndex); + break; + } + this.index = nextIndex; + return this.push({ + data : data, + meta : { + percent : this.max ? this.index / this.max * 100 : 0 + } + }); + } +}; + +module.exports = DataWorker; + +},{"../utils":32,"./GenericWorker":28}],28:[function(require,module,exports){ +'use strict'; + +/** + * A worker that does nothing but passing chunks to the next one. This is like + * a nodejs stream but with some differences. On the good side : + * - it works on IE 6-9 without any issue / polyfill + * - it weights less than the full dependencies bundled with browserify + * - it forwards errors (no need to declare an error handler EVERYWHERE) + * + * A chunk is an object with 2 attributes : `meta` and `data`. The former is an + * object containing anything (`percent` for example), see each worker for more + * details. The latter is the real data (String, Uint8Array, etc). + * + * @constructor + * @param {String} name the name of the stream (mainly used for debugging purposes) + */ +function GenericWorker(name) { + // the name of the worker + this.name = name || "default"; + // an object containing metadata about the workers chain + this.streamInfo = {}; + // an error which happened when the worker was paused + this.generatedError = null; + // an object containing metadata to be merged by this worker into the general metadata + this.extraStreamInfo = {}; + // true if the stream is paused (and should not do anything), false otherwise + this.isPaused = true; + // true if the stream is finished (and should not do anything), false otherwise + this.isFinished = false; + // true if the stream is locked to prevent further structure updates (pipe), false otherwise + this.isLocked = false; + // the event listeners + this._listeners = { + 'data':[], + 'end':[], + 'error':[] + }; + // the previous worker, if any + this.previous = null; +} + +GenericWorker.prototype = { + /** + * Push a chunk to the next workers. + * @param {Object} chunk the chunk to push + */ + push : function (chunk) { + this.emit("data", chunk); + }, + /** + * End the stream. + * @return {Boolean} true if this call ended the worker, false otherwise. + */ + end : function () { + if (this.isFinished) { + return false; + } + + this.flush(); + try { + this.emit("end"); + this.cleanUp(); + this.isFinished = true; + } catch (e) { + this.emit("error", e); + } + return true; + }, + /** + * End the stream with an error. + * @param {Error} e the error which caused the premature end. + * @return {Boolean} true if this call ended the worker with an error, false otherwise. + */ + error : function (e) { + if (this.isFinished) { + return false; + } + + if(this.isPaused) { + this.generatedError = e; + } else { + this.isFinished = true; + + this.emit("error", e); + + // in the workers chain exploded in the middle of the chain, + // the error event will go downward but we also need to notify + // workers upward that there has been an error. + if(this.previous) { + this.previous.error(e); + } + + this.cleanUp(); + } + return true; + }, + /** + * Add a callback on an event. + * @param {String} name the name of the event (data, end, error) + * @param {Function} listener the function to call when the event is triggered + * @return {GenericWorker} the current object for chainability + */ + on : function (name, listener) { + this._listeners[name].push(listener); + return this; + }, + /** + * Clean any references when a worker is ending. + */ + cleanUp : function () { + this.streamInfo = this.generatedError = this.extraStreamInfo = null; + this._listeners = []; + }, + /** + * Trigger an event. This will call registered callback with the provided arg. + * @param {String} name the name of the event (data, end, error) + * @param {Object} arg the argument to call the callback with. + */ + emit : function (name, arg) { + if (this._listeners[name]) { + for(var i = 0; i < this._listeners[name].length; i++) { + this._listeners[name][i].call(this, arg); + } + } + }, + /** + * Chain a worker with an other. + * @param {Worker} next the worker receiving events from the current one. + * @return {worker} the next worker for chainability + */ + pipe : function (next) { + return next.registerPrevious(this); + }, + /** + * Same as `pipe` in the other direction. + * Using an API with `pipe(next)` is very easy. + * Implementing the API with the point of view of the next one registering + * a source is easier, see the ZipFileWorker. + * @param {Worker} previous the previous worker, sending events to this one + * @return {Worker} the current worker for chainability + */ + registerPrevious : function (previous) { + if (this.isLocked) { + throw new Error("The stream '" + this + "' has already been used."); + } + + // sharing the streamInfo... + this.streamInfo = previous.streamInfo; + // ... and adding our own bits + this.mergeStreamInfo(); + this.previous = previous; + var self = this; + previous.on('data', function (chunk) { + self.processChunk(chunk); + }); + previous.on('end', function () { + self.end(); + }); + previous.on('error', function (e) { + self.error(e); + }); + return this; + }, + /** + * Pause the stream so it doesn't send events anymore. + * @return {Boolean} true if this call paused the worker, false otherwise. + */ + pause : function () { + if(this.isPaused || this.isFinished) { + return false; + } + this.isPaused = true; + + if(this.previous) { + this.previous.pause(); + } + return true; + }, + /** + * Resume a paused stream. + * @return {Boolean} true if this call resumed the worker, false otherwise. + */ + resume : function () { + if(!this.isPaused || this.isFinished) { + return false; + } + this.isPaused = false; + + // if true, the worker tried to resume but failed + var withError = false; + if(this.generatedError) { + this.error(this.generatedError); + withError = true; + } + if(this.previous) { + this.previous.resume(); + } + + return !withError; + }, + /** + * Flush any remaining bytes as the stream is ending. + */ + flush : function () {}, + /** + * Process a chunk. This is usually the method overridden. + * @param {Object} chunk the chunk to process. + */ + processChunk : function(chunk) { + this.push(chunk); + }, + /** + * Add a key/value to be added in the workers chain streamInfo once activated. + * @param {String} key the key to use + * @param {Object} value the associated value + * @return {Worker} the current worker for chainability + */ + withStreamInfo : function (key, value) { + this.extraStreamInfo[key] = value; + this.mergeStreamInfo(); + return this; + }, + /** + * Merge this worker's streamInfo into the chain's streamInfo. + */ + mergeStreamInfo : function () { + for(var key in this.extraStreamInfo) { + if (!this.extraStreamInfo.hasOwnProperty(key)) { + continue; + } + this.streamInfo[key] = this.extraStreamInfo[key]; + } + }, + + /** + * Lock the stream to prevent further updates on the workers chain. + * After calling this method, all calls to pipe will fail. + */ + lock: function () { + if (this.isLocked) { + throw new Error("The stream '" + this + "' has already been used."); + } + this.isLocked = true; + if (this.previous) { + this.previous.lock(); + } + }, + + /** + * + * Pretty print the workers chain. + */ + toString : function () { + var me = "Worker " + this.name; + if (this.previous) { + return this.previous + " -> " + me; + } else { + return me; + } + } +}; + +module.exports = GenericWorker; + +},{}],29:[function(require,module,exports){ +'use strict'; + +var utils = require('../utils'); +var ConvertWorker = require('./ConvertWorker'); +var GenericWorker = require('./GenericWorker'); +var base64 = require('../base64'); +var support = require("../support"); +var external = require("../external"); + +var NodejsStreamOutputAdapter = null; +if (support.nodestream) { + try { + NodejsStreamOutputAdapter = require('../nodejs/NodejsStreamOutputAdapter'); + } catch(e) {} +} + +/** + * Apply the final transformation of the data. If the user wants a Blob for + * example, it's easier to work with an U8intArray and finally do the + * ArrayBuffer/Blob conversion. + * @param {String} type the name of the final type + * @param {String|Uint8Array|Buffer} content the content to transform + * @param {String} mimeType the mime type of the content, if applicable. + * @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the content in the right format. + */ +function transformZipOutput(type, content, mimeType) { + switch(type) { + case "blob" : + return utils.newBlob(utils.transformTo("arraybuffer", content), mimeType); + case "base64" : + return base64.encode(content); + default : + return utils.transformTo(type, content); + } +} + +/** + * Concatenate an array of data of the given type. + * @param {String} type the type of the data in the given array. + * @param {Array} dataArray the array containing the data chunks to concatenate + * @return {String|Uint8Array|Buffer} the concatenated data + * @throws Error if the asked type is unsupported + */ +function concat (type, dataArray) { + var i, index = 0, res = null, totalLength = 0; + for(i = 0; i < dataArray.length; i++) { + totalLength += dataArray[i].length; + } + switch(type) { + case "string": + return dataArray.join(""); + case "array": + return Array.prototype.concat.apply([], dataArray); + case "uint8array": + res = new Uint8Array(totalLength); + for(i = 0; i < dataArray.length; i++) { + res.set(dataArray[i], index); + index += dataArray[i].length; + } + return res; + case "nodebuffer": + return Buffer.concat(dataArray); + default: + throw new Error("concat : unsupported type '" + type + "'"); + } +} + +/** + * Listen a StreamHelper, accumulate its content and concatenate it into a + * complete block. + * @param {StreamHelper} helper the helper to use. + * @param {Function} updateCallback a callback called on each update. Called + * with one arg : + * - the metadata linked to the update received. + * @return Promise the promise for the accumulation. + */ +function accumulate(helper, updateCallback) { + return new external.Promise(function (resolve, reject){ + var dataArray = []; + var chunkType = helper._internalType, + resultType = helper._outputType, + mimeType = helper._mimeType; + helper + .on('data', function (data, meta) { + dataArray.push(data); + if(updateCallback) { + updateCallback(meta); + } + }) + .on('error', function(err) { + dataArray = []; + reject(err); + }) + .on('end', function (){ + try { + var result = transformZipOutput(resultType, concat(chunkType, dataArray), mimeType); + resolve(result); + } catch (e) { + reject(e); + } + dataArray = []; + }) + .resume(); + }); +} + +/** + * An helper to easily use workers outside of JSZip. + * @constructor + * @param {Worker} worker the worker to wrap + * @param {String} outputType the type of data expected by the use + * @param {String} mimeType the mime type of the content, if applicable. + */ +function StreamHelper(worker, outputType, mimeType) { + var internalType = outputType; + switch(outputType) { + case "blob": + case "arraybuffer": + internalType = "uint8array"; + break; + case "base64": + internalType = "string"; + break; + } + + try { + // the type used internally + this._internalType = internalType; + // the type used to output results + this._outputType = outputType; + // the mime type + this._mimeType = mimeType; + utils.checkSupport(internalType); + this._worker = worker.pipe(new ConvertWorker(internalType)); + // the last workers can be rewired without issues but we need to + // prevent any updates on previous workers. + worker.lock(); + } catch(e) { + this._worker = new GenericWorker("error"); + this._worker.error(e); + } +} + +StreamHelper.prototype = { + /** + * Listen a StreamHelper, accumulate its content and concatenate it into a + * complete block. + * @param {Function} updateCb the update callback. + * @return Promise the promise for the accumulation. + */ + accumulate : function (updateCb) { + return accumulate(this, updateCb); + }, + /** + * Add a listener on an event triggered on a stream. + * @param {String} evt the name of the event + * @param {Function} fn the listener + * @return {StreamHelper} the current helper. + */ + on : function (evt, fn) { + var self = this; + + if(evt === "data") { + this._worker.on(evt, function (chunk) { + fn.call(self, chunk.data, chunk.meta); + }); + } else { + this._worker.on(evt, function () { + utils.delay(fn, arguments, self); + }); + } + return this; + }, + /** + * Resume the flow of chunks. + * @return {StreamHelper} the current helper. + */ + resume : function () { + utils.delay(this._worker.resume, [], this._worker); + return this; + }, + /** + * Pause the flow of chunks. + * @return {StreamHelper} the current helper. + */ + pause : function () { + this._worker.pause(); + return this; + }, + /** + * Return a nodejs stream for this helper. + * @param {Function} updateCb the update callback. + * @return {NodejsStreamOutputAdapter} the nodejs stream. + */ + toNodejsStream : function (updateCb) { + utils.checkSupport("nodestream"); + if (this._outputType !== "nodebuffer") { + // an object stream containing blob/arraybuffer/uint8array/string + // is strange and I don't know if it would be useful. + // I you find this comment and have a good usecase, please open a + // bug report ! + throw new Error(this._outputType + " is not supported by this method"); + } + + return new NodejsStreamOutputAdapter(this, { + objectMode : this._outputType !== "nodebuffer" + }, updateCb); + } +}; + + +module.exports = StreamHelper; + +},{"../base64":1,"../external":6,"../nodejs/NodejsStreamOutputAdapter":13,"../support":30,"../utils":32,"./ConvertWorker":24,"./GenericWorker":28}],30:[function(require,module,exports){ +'use strict'; + +exports.base64 = true; +exports.array = true; +exports.string = true; +exports.arraybuffer = typeof ArrayBuffer !== "undefined" && typeof Uint8Array !== "undefined"; +exports.nodebuffer = typeof Buffer !== "undefined"; +// contains true if JSZip can read/generate Uint8Array, false otherwise. +exports.uint8array = typeof Uint8Array !== "undefined"; + +if (typeof ArrayBuffer === "undefined") { + exports.blob = false; +} +else { + var buffer = new ArrayBuffer(0); + try { + exports.blob = new Blob([buffer], { + type: "application/zip" + }).size === 0; + } + catch (e) { + try { + var Builder = self.BlobBuilder || self.WebKitBlobBuilder || self.MozBlobBuilder || self.MSBlobBuilder; + var builder = new Builder(); + builder.append(buffer); + exports.blob = builder.getBlob('application/zip').size === 0; + } + catch (e) { + exports.blob = false; + } + } +} + +try { + exports.nodestream = !!require('readable-stream').Readable; +} catch(e) { + exports.nodestream = false; +} + +},{"readable-stream":16}],31:[function(require,module,exports){ +'use strict'; + +var utils = require('./utils'); +var support = require('./support'); +var nodejsUtils = require('./nodejsUtils'); +var GenericWorker = require('./stream/GenericWorker'); + +/** + * The following functions come from pako, from pako/lib/utils/strings + * released under the MIT license, see pako https://github.com/nodeca/pako/ + */ + +// Table with utf8 lengths (calculated by first byte of sequence) +// Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS, +// because max possible codepoint is 0x10ffff +var _utf8len = new Array(256); +for (var i=0; i<256; i++) { + _utf8len[i] = (i >= 252 ? 6 : i >= 248 ? 5 : i >= 240 ? 4 : i >= 224 ? 3 : i >= 192 ? 2 : 1); +} +_utf8len[254]=_utf8len[254]=1; // Invalid sequence start + +// convert string to array (typed, when possible) +var string2buf = function (str) { + var buf, c, c2, m_pos, i, str_len = str.length, buf_len = 0; + + // count binary size + for (m_pos = 0; m_pos < str_len; m_pos++) { + c = str.charCodeAt(m_pos); + if ((c & 0xfc00) === 0xd800 && (m_pos+1 < str_len)) { + c2 = str.charCodeAt(m_pos+1); + if ((c2 & 0xfc00) === 0xdc00) { + c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00); + m_pos++; + } + } + buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4; + } + + // allocate buffer + if (support.uint8array) { + buf = new Uint8Array(buf_len); + } else { + buf = new Array(buf_len); + } + + // convert + for (i=0, m_pos = 0; i < buf_len; m_pos++) { + c = str.charCodeAt(m_pos); + if ((c & 0xfc00) === 0xd800 && (m_pos+1 < str_len)) { + c2 = str.charCodeAt(m_pos+1); + if ((c2 & 0xfc00) === 0xdc00) { + c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00); + m_pos++; + } + } + if (c < 0x80) { + /* one byte */ + buf[i++] = c; + } else if (c < 0x800) { + /* two bytes */ + buf[i++] = 0xC0 | (c >>> 6); + buf[i++] = 0x80 | (c & 0x3f); + } else if (c < 0x10000) { + /* three bytes */ + buf[i++] = 0xE0 | (c >>> 12); + buf[i++] = 0x80 | (c >>> 6 & 0x3f); + buf[i++] = 0x80 | (c & 0x3f); + } else { + /* four bytes */ + buf[i++] = 0xf0 | (c >>> 18); + buf[i++] = 0x80 | (c >>> 12 & 0x3f); + buf[i++] = 0x80 | (c >>> 6 & 0x3f); + buf[i++] = 0x80 | (c & 0x3f); + } + } + + return buf; +}; + +// Calculate max possible position in utf8 buffer, +// that will not break sequence. If that's not possible +// - (very small limits) return max size as is. +// +// buf[] - utf8 bytes array +// max - length limit (mandatory); +var utf8border = function(buf, max) { + var pos; + + max = max || buf.length; + if (max > buf.length) { max = buf.length; } + + // go back from last position, until start of sequence found + pos = max-1; + while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) { pos--; } + + // Fuckup - very small and broken sequence, + // return max, because we should return something anyway. + if (pos < 0) { return max; } + + // If we came to start of buffer - that means vuffer is too small, + // return max too. + if (pos === 0) { return max; } + + return (pos + _utf8len[buf[pos]] > max) ? pos : max; +}; + +// convert array to string +var buf2string = function (buf) { + var str, i, out, c, c_len; + var len = buf.length; + + // Reserve max possible length (2 words per char) + // NB: by unknown reasons, Array is significantly faster for + // String.fromCharCode.apply than Uint16Array. + var utf16buf = new Array(len*2); + + for (out=0, i=0; i 4) { utf16buf[out++] = 0xfffd; i += c_len-1; continue; } + + // apply mask on first byte + c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07; + // join the rest + while (c_len > 1 && i < len) { + c = (c << 6) | (buf[i++] & 0x3f); + c_len--; + } + + // terminated by end of string? + if (c_len > 1) { utf16buf[out++] = 0xfffd; continue; } + + if (c < 0x10000) { + utf16buf[out++] = c; + } else { + c -= 0x10000; + utf16buf[out++] = 0xd800 | ((c >> 10) & 0x3ff); + utf16buf[out++] = 0xdc00 | (c & 0x3ff); + } + } + + // shrinkBuf(utf16buf, out) + if (utf16buf.length !== out) { + if(utf16buf.subarray) { + utf16buf = utf16buf.subarray(0, out); + } else { + utf16buf.length = out; + } + } + + // return String.fromCharCode.apply(null, utf16buf); + return utils.applyFromCharCode(utf16buf); +}; + + +// That's all for the pako functions. + + +/** + * Transform a javascript string into an array (typed if possible) of bytes, + * UTF-8 encoded. + * @param {String} str the string to encode + * @return {Array|Uint8Array|Buffer} the UTF-8 encoded string. + */ +exports.utf8encode = function utf8encode(str) { + if (support.nodebuffer) { + return nodejsUtils.newBufferFrom(str, "utf-8"); + } + + return string2buf(str); +}; + + +/** + * Transform a bytes array (or a representation) representing an UTF-8 encoded + * string into a javascript string. + * @param {Array|Uint8Array|Buffer} buf the data de decode + * @return {String} the decoded string. + */ +exports.utf8decode = function utf8decode(buf) { + if (support.nodebuffer) { + return utils.transformTo("nodebuffer", buf).toString("utf-8"); + } + + buf = utils.transformTo(support.uint8array ? "uint8array" : "array", buf); + + return buf2string(buf); +}; + +/** + * A worker to decode utf8 encoded binary chunks into string chunks. + * @constructor + */ +function Utf8DecodeWorker() { + GenericWorker.call(this, "utf-8 decode"); + // the last bytes if a chunk didn't end with a complete codepoint. + this.leftOver = null; +} +utils.inherits(Utf8DecodeWorker, GenericWorker); + +/** + * @see GenericWorker.processChunk + */ +Utf8DecodeWorker.prototype.processChunk = function (chunk) { + + var data = utils.transformTo(support.uint8array ? "uint8array" : "array", chunk.data); + + // 1st step, re-use what's left of the previous chunk + if (this.leftOver && this.leftOver.length) { + if(support.uint8array) { + var previousData = data; + data = new Uint8Array(previousData.length + this.leftOver.length); + data.set(this.leftOver, 0); + data.set(previousData, this.leftOver.length); + } else { + data = this.leftOver.concat(data); + } + this.leftOver = null; + } + + var nextBoundary = utf8border(data); + var usableData = data; + if (nextBoundary !== data.length) { + if (support.uint8array) { + usableData = data.subarray(0, nextBoundary); + this.leftOver = data.subarray(nextBoundary, data.length); + } else { + usableData = data.slice(0, nextBoundary); + this.leftOver = data.slice(nextBoundary, data.length); + } + } + + this.push({ + data : exports.utf8decode(usableData), + meta : chunk.meta + }); +}; + +/** + * @see GenericWorker.flush + */ +Utf8DecodeWorker.prototype.flush = function () { + if(this.leftOver && this.leftOver.length) { + this.push({ + data : exports.utf8decode(this.leftOver), + meta : {} + }); + this.leftOver = null; + } +}; +exports.Utf8DecodeWorker = Utf8DecodeWorker; + +/** + * A worker to endcode string chunks into utf8 encoded binary chunks. + * @constructor + */ +function Utf8EncodeWorker() { + GenericWorker.call(this, "utf-8 encode"); +} +utils.inherits(Utf8EncodeWorker, GenericWorker); + +/** + * @see GenericWorker.processChunk + */ +Utf8EncodeWorker.prototype.processChunk = function (chunk) { + this.push({ + data : exports.utf8encode(chunk.data), + meta : chunk.meta + }); +}; +exports.Utf8EncodeWorker = Utf8EncodeWorker; + +},{"./nodejsUtils":14,"./stream/GenericWorker":28,"./support":30,"./utils":32}],32:[function(require,module,exports){ +'use strict'; + +var support = require('./support'); +var base64 = require('./base64'); +var nodejsUtils = require('./nodejsUtils'); +var setImmediate = require('set-immediate-shim'); +var external = require("./external"); + + +/** + * Convert a string that pass as a "binary string": it should represent a byte + * array but may have > 255 char codes. Be sure to take only the first byte + * and returns the byte array. + * @param {String} str the string to transform. + * @return {Array|Uint8Array} the string in a binary format. + */ +function string2binary(str) { + var result = null; + if (support.uint8array) { + result = new Uint8Array(str.length); + } else { + result = new Array(str.length); + } + return stringToArrayLike(str, result); +} + +/** + * Create a new blob with the given content and the given type. + * @param {String|ArrayBuffer} part the content to put in the blob. DO NOT use + * an Uint8Array because the stock browser of android 4 won't accept it (it + * will be silently converted to a string, "[object Uint8Array]"). + * + * Use only ONE part to build the blob to avoid a memory leak in IE11 / Edge: + * when a large amount of Array is used to create the Blob, the amount of + * memory consumed is nearly 100 times the original data amount. + * + * @param {String} type the mime type of the blob. + * @return {Blob} the created blob. + */ +exports.newBlob = function(part, type) { + exports.checkSupport("blob"); + + try { + // Blob constructor + return new Blob([part], { + type: type + }); + } + catch (e) { + + try { + // deprecated, browser only, old way + var Builder = self.BlobBuilder || self.WebKitBlobBuilder || self.MozBlobBuilder || self.MSBlobBuilder; + var builder = new Builder(); + builder.append(part); + return builder.getBlob(type); + } + catch (e) { + + // well, fuck ?! + throw new Error("Bug : can't construct the Blob."); + } + } + + +}; +/** + * The identity function. + * @param {Object} input the input. + * @return {Object} the same input. + */ +function identity(input) { + return input; +} + +/** + * Fill in an array with a string. + * @param {String} str the string to use. + * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to fill in (will be mutated). + * @return {Array|ArrayBuffer|Uint8Array|Buffer} the updated array. + */ +function stringToArrayLike(str, array) { + for (var i = 0; i < str.length; ++i) { + array[i] = str.charCodeAt(i) & 0xFF; + } + return array; +} + +/** + * An helper for the function arrayLikeToString. + * This contains static information and functions that + * can be optimized by the browser JIT compiler. + */ +var arrayToStringHelper = { + /** + * Transform an array of int into a string, chunk by chunk. + * See the performances notes on arrayLikeToString. + * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform. + * @param {String} type the type of the array. + * @param {Integer} chunk the chunk size. + * @return {String} the resulting string. + * @throws Error if the chunk is too big for the stack. + */ + stringifyByChunk: function(array, type, chunk) { + var result = [], k = 0, len = array.length; + // shortcut + if (len <= chunk) { + return String.fromCharCode.apply(null, array); + } + while (k < len) { + if (type === "array" || type === "nodebuffer") { + result.push(String.fromCharCode.apply(null, array.slice(k, Math.min(k + chunk, len)))); + } + else { + result.push(String.fromCharCode.apply(null, array.subarray(k, Math.min(k + chunk, len)))); + } + k += chunk; + } + return result.join(""); + }, + /** + * Call String.fromCharCode on every item in the array. + * This is the naive implementation, which generate A LOT of intermediate string. + * This should be used when everything else fail. + * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform. + * @return {String} the result. + */ + stringifyByChar: function(array){ + var resultStr = ""; + for(var i = 0; i < array.length; i++) { + resultStr += String.fromCharCode(array[i]); + } + return resultStr; + }, + applyCanBeUsed : { + /** + * true if the browser accepts to use String.fromCharCode on Uint8Array + */ + uint8array : (function () { + try { + return support.uint8array && String.fromCharCode.apply(null, new Uint8Array(1)).length === 1; + } catch (e) { + return false; + } + })(), + /** + * true if the browser accepts to use String.fromCharCode on nodejs Buffer. + */ + nodebuffer : (function () { + try { + return support.nodebuffer && String.fromCharCode.apply(null, nodejsUtils.allocBuffer(1)).length === 1; + } catch (e) { + return false; + } + })() + } +}; + +/** + * Transform an array-like object to a string. + * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform. + * @return {String} the result. + */ +function arrayLikeToString(array) { + // Performances notes : + // -------------------- + // String.fromCharCode.apply(null, array) is the fastest, see + // see http://jsperf.com/converting-a-uint8array-to-a-string/2 + // but the stack is limited (and we can get huge arrays !). + // + // result += String.fromCharCode(array[i]); generate too many strings ! + // + // This code is inspired by http://jsperf.com/arraybuffer-to-string-apply-performance/2 + // TODO : we now have workers that split the work. Do we still need that ? + var chunk = 65536, + type = exports.getTypeOf(array), + canUseApply = true; + if (type === "uint8array") { + canUseApply = arrayToStringHelper.applyCanBeUsed.uint8array; + } else if (type === "nodebuffer") { + canUseApply = arrayToStringHelper.applyCanBeUsed.nodebuffer; + } + + if (canUseApply) { + while (chunk > 1) { + try { + return arrayToStringHelper.stringifyByChunk(array, type, chunk); + } catch (e) { + chunk = Math.floor(chunk / 2); + } + } + } + + // no apply or chunk error : slow and painful algorithm + // default browser on android 4.* + return arrayToStringHelper.stringifyByChar(array); +} + +exports.applyFromCharCode = arrayLikeToString; + + +/** + * Copy the data from an array-like to an other array-like. + * @param {Array|ArrayBuffer|Uint8Array|Buffer} arrayFrom the origin array. + * @param {Array|ArrayBuffer|Uint8Array|Buffer} arrayTo the destination array which will be mutated. + * @return {Array|ArrayBuffer|Uint8Array|Buffer} the updated destination array. + */ +function arrayLikeToArrayLike(arrayFrom, arrayTo) { + for (var i = 0; i < arrayFrom.length; i++) { + arrayTo[i] = arrayFrom[i]; + } + return arrayTo; +} + +// a matrix containing functions to transform everything into everything. +var transform = {}; + +// string to ? +transform["string"] = { + "string": identity, + "array": function(input) { + return stringToArrayLike(input, new Array(input.length)); + }, + "arraybuffer": function(input) { + return transform["string"]["uint8array"](input).buffer; + }, + "uint8array": function(input) { + return stringToArrayLike(input, new Uint8Array(input.length)); + }, + "nodebuffer": function(input) { + return stringToArrayLike(input, nodejsUtils.allocBuffer(input.length)); + } +}; + +// array to ? +transform["array"] = { + "string": arrayLikeToString, + "array": identity, + "arraybuffer": function(input) { + return (new Uint8Array(input)).buffer; + }, + "uint8array": function(input) { + return new Uint8Array(input); + }, + "nodebuffer": function(input) { + return nodejsUtils.newBufferFrom(input); + } +}; + +// arraybuffer to ? +transform["arraybuffer"] = { + "string": function(input) { + return arrayLikeToString(new Uint8Array(input)); + }, + "array": function(input) { + return arrayLikeToArrayLike(new Uint8Array(input), new Array(input.byteLength)); + }, + "arraybuffer": identity, + "uint8array": function(input) { + return new Uint8Array(input); + }, + "nodebuffer": function(input) { + return nodejsUtils.newBufferFrom(new Uint8Array(input)); + } +}; + +// uint8array to ? +transform["uint8array"] = { + "string": arrayLikeToString, + "array": function(input) { + return arrayLikeToArrayLike(input, new Array(input.length)); + }, + "arraybuffer": function(input) { + return input.buffer; + }, + "uint8array": identity, + "nodebuffer": function(input) { + return nodejsUtils.newBufferFrom(input); + } +}; + +// nodebuffer to ? +transform["nodebuffer"] = { + "string": arrayLikeToString, + "array": function(input) { + return arrayLikeToArrayLike(input, new Array(input.length)); + }, + "arraybuffer": function(input) { + return transform["nodebuffer"]["uint8array"](input).buffer; + }, + "uint8array": function(input) { + return arrayLikeToArrayLike(input, new Uint8Array(input.length)); + }, + "nodebuffer": identity +}; + +/** + * Transform an input into any type. + * The supported output type are : string, array, uint8array, arraybuffer, nodebuffer. + * If no output type is specified, the unmodified input will be returned. + * @param {String} outputType the output type. + * @param {String|Array|ArrayBuffer|Uint8Array|Buffer} input the input to convert. + * @throws {Error} an Error if the browser doesn't support the requested output type. + */ +exports.transformTo = function(outputType, input) { + if (!input) { + // undefined, null, etc + // an empty string won't harm. + input = ""; + } + if (!outputType) { + return input; + } + exports.checkSupport(outputType); + var inputType = exports.getTypeOf(input); + var result = transform[inputType][outputType](input); + return result; +}; + +/** + * Return the type of the input. + * The type will be in a format valid for JSZip.utils.transformTo : string, array, uint8array, arraybuffer. + * @param {Object} input the input to identify. + * @return {String} the (lowercase) type of the input. + */ +exports.getTypeOf = function(input) { + if (typeof input === "string") { + return "string"; + } + if (Object.prototype.toString.call(input) === "[object Array]") { + return "array"; + } + if (support.nodebuffer && nodejsUtils.isBuffer(input)) { + return "nodebuffer"; + } + if (support.uint8array && input instanceof Uint8Array) { + return "uint8array"; + } + if (support.arraybuffer && input instanceof ArrayBuffer) { + return "arraybuffer"; + } +}; + +/** + * Throw an exception if the type is not supported. + * @param {String} type the type to check. + * @throws {Error} an Error if the browser doesn't support the requested type. + */ +exports.checkSupport = function(type) { + var supported = support[type.toLowerCase()]; + if (!supported) { + throw new Error(type + " is not supported by this platform"); + } +}; + +exports.MAX_VALUE_16BITS = 65535; +exports.MAX_VALUE_32BITS = -1; // well, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" is parsed as -1 + +/** + * Prettify a string read as binary. + * @param {string} str the string to prettify. + * @return {string} a pretty string. + */ +exports.pretty = function(str) { + var res = '', + code, i; + for (i = 0; i < (str || "").length; i++) { + code = str.charCodeAt(i); + res += '\\x' + (code < 16 ? "0" : "") + code.toString(16).toUpperCase(); + } + return res; +}; + +/** + * Defer the call of a function. + * @param {Function} callback the function to call asynchronously. + * @param {Array} args the arguments to give to the callback. + */ +exports.delay = function(callback, args, self) { + setImmediate(function () { + callback.apply(self || null, args || []); + }); +}; + +/** + * Extends a prototype with an other, without calling a constructor with + * side effects. Inspired by nodejs' `utils.inherits` + * @param {Function} ctor the constructor to augment + * @param {Function} superCtor the parent constructor to use + */ +exports.inherits = function (ctor, superCtor) { + var Obj = function() {}; + Obj.prototype = superCtor.prototype; + ctor.prototype = new Obj(); +}; + +/** + * Merge the objects passed as parameters into a new one. + * @private + * @param {...Object} var_args All objects to merge. + * @return {Object} a new object with the data of the others. + */ +exports.extend = function() { + var result = {}, i, attr; + for (i = 0; i < arguments.length; i++) { // arguments is not enumerable in some browsers + for (attr in arguments[i]) { + if (arguments[i].hasOwnProperty(attr) && typeof result[attr] === "undefined") { + result[attr] = arguments[i][attr]; + } + } + } + return result; +}; + +/** + * Transform arbitrary content into a Promise. + * @param {String} name a name for the content being processed. + * @param {Object} inputData the content to process. + * @param {Boolean} isBinary true if the content is not an unicode string + * @param {Boolean} isOptimizedBinaryString true if the string content only has one byte per character. + * @param {Boolean} isBase64 true if the string content is encoded with base64. + * @return {Promise} a promise in a format usable by JSZip. + */ +exports.prepareContent = function(name, inputData, isBinary, isOptimizedBinaryString, isBase64) { + + // if inputData is already a promise, this flatten it. + var promise = external.Promise.resolve(inputData).then(function(data) { + + + var isBlob = support.blob && (data instanceof Blob || ['[object File]', '[object Blob]'].indexOf(Object.prototype.toString.call(data)) !== -1); + + if (isBlob && typeof FileReader !== "undefined") { + return new external.Promise(function (resolve, reject) { + var reader = new FileReader(); + + reader.onload = function(e) { + resolve(e.target.result); + }; + reader.onerror = function(e) { + reject(e.target.error); + }; + reader.readAsArrayBuffer(data); + }); + } else { + return data; + } + }); + + return promise.then(function(data) { + var dataType = exports.getTypeOf(data); + + if (!dataType) { + return external.Promise.reject( + new Error("Can't read the data of '" + name + "'. Is it " + + "in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?") + ); + } + // special case : it's way easier to work with Uint8Array than with ArrayBuffer + if (dataType === "arraybuffer") { + data = exports.transformTo("uint8array", data); + } else if (dataType === "string") { + if (isBase64) { + data = base64.decode(data); + } + else if (isBinary) { + // optimizedBinaryString === true means that the file has already been filtered with a 0xFF mask + if (isOptimizedBinaryString !== true) { + // this is a string, not in a base64 format. + // Be sure that this is a correct "binary string" + data = string2binary(data); + } + } + } + return data; + }); +}; + +},{"./base64":1,"./external":6,"./nodejsUtils":14,"./support":30,"set-immediate-shim":54}],33:[function(require,module,exports){ +'use strict'; +var readerFor = require('./reader/readerFor'); +var utils = require('./utils'); +var sig = require('./signature'); +var ZipEntry = require('./zipEntry'); +var utf8 = require('./utf8'); +var support = require('./support'); +// class ZipEntries {{{ +/** + * All the entries in the zip file. + * @constructor + * @param {Object} loadOptions Options for loading the stream. + */ +function ZipEntries(loadOptions) { + this.files = []; + this.loadOptions = loadOptions; +} +ZipEntries.prototype = { + /** + * Check that the reader is on the specified signature. + * @param {string} expectedSignature the expected signature. + * @throws {Error} if it is an other signature. + */ + checkSignature: function(expectedSignature) { + if (!this.reader.readAndCheckSignature(expectedSignature)) { + this.reader.index -= 4; + var signature = this.reader.readString(4); + throw new Error("Corrupted zip or bug: unexpected signature " + "(" + utils.pretty(signature) + ", expected " + utils.pretty(expectedSignature) + ")"); + } + }, + /** + * Check if the given signature is at the given index. + * @param {number} askedIndex the index to check. + * @param {string} expectedSignature the signature to expect. + * @return {boolean} true if the signature is here, false otherwise. + */ + isSignature: function(askedIndex, expectedSignature) { + var currentIndex = this.reader.index; + this.reader.setIndex(askedIndex); + var signature = this.reader.readString(4); + var result = signature === expectedSignature; + this.reader.setIndex(currentIndex); + return result; + }, + /** + * Read the end of the central directory. + */ + readBlockEndOfCentral: function() { + this.diskNumber = this.reader.readInt(2); + this.diskWithCentralDirStart = this.reader.readInt(2); + this.centralDirRecordsOnThisDisk = this.reader.readInt(2); + this.centralDirRecords = this.reader.readInt(2); + this.centralDirSize = this.reader.readInt(4); + this.centralDirOffset = this.reader.readInt(4); + + this.zipCommentLength = this.reader.readInt(2); + // warning : the encoding depends of the system locale + // On a linux machine with LANG=en_US.utf8, this field is utf8 encoded. + // On a windows machine, this field is encoded with the localized windows code page. + var zipComment = this.reader.readData(this.zipCommentLength); + var decodeParamType = support.uint8array ? "uint8array" : "array"; + // To get consistent behavior with the generation part, we will assume that + // this is utf8 encoded unless specified otherwise. + var decodeContent = utils.transformTo(decodeParamType, zipComment); + this.zipComment = this.loadOptions.decodeFileName(decodeContent); + }, + /** + * Read the end of the Zip 64 central directory. + * Not merged with the method readEndOfCentral : + * The end of central can coexist with its Zip64 brother, + * I don't want to read the wrong number of bytes ! + */ + readBlockZip64EndOfCentral: function() { + this.zip64EndOfCentralSize = this.reader.readInt(8); + this.reader.skip(4); + // this.versionMadeBy = this.reader.readString(2); + // this.versionNeeded = this.reader.readInt(2); + this.diskNumber = this.reader.readInt(4); + this.diskWithCentralDirStart = this.reader.readInt(4); + this.centralDirRecordsOnThisDisk = this.reader.readInt(8); + this.centralDirRecords = this.reader.readInt(8); + this.centralDirSize = this.reader.readInt(8); + this.centralDirOffset = this.reader.readInt(8); + + this.zip64ExtensibleData = {}; + var extraDataSize = this.zip64EndOfCentralSize - 44, + index = 0, + extraFieldId, + extraFieldLength, + extraFieldValue; + while (index < extraDataSize) { + extraFieldId = this.reader.readInt(2); + extraFieldLength = this.reader.readInt(4); + extraFieldValue = this.reader.readData(extraFieldLength); + this.zip64ExtensibleData[extraFieldId] = { + id: extraFieldId, + length: extraFieldLength, + value: extraFieldValue + }; + } + }, + /** + * Read the end of the Zip 64 central directory locator. + */ + readBlockZip64EndOfCentralLocator: function() { + this.diskWithZip64CentralDirStart = this.reader.readInt(4); + this.relativeOffsetEndOfZip64CentralDir = this.reader.readInt(8); + this.disksCount = this.reader.readInt(4); + if (this.disksCount > 1) { + throw new Error("Multi-volumes zip are not supported"); + } + }, + /** + * Read the local files, based on the offset read in the central part. + */ + readLocalFiles: function() { + var i, file; + for (i = 0; i < this.files.length; i++) { + file = this.files[i]; + this.reader.setIndex(file.localHeaderOffset); + this.checkSignature(sig.LOCAL_FILE_HEADER); + file.readLocalPart(this.reader); + file.handleUTF8(); + file.processAttributes(); + } + }, + /** + * Read the central directory. + */ + readCentralDir: function() { + var file; + + this.reader.setIndex(this.centralDirOffset); + while (this.reader.readAndCheckSignature(sig.CENTRAL_FILE_HEADER)) { + file = new ZipEntry({ + zip64: this.zip64 + }, this.loadOptions); + file.readCentralPart(this.reader); + this.files.push(file); + } + + if (this.centralDirRecords !== this.files.length) { + if (this.centralDirRecords !== 0 && this.files.length === 0) { + // We expected some records but couldn't find ANY. + // This is really suspicious, as if something went wrong. + throw new Error("Corrupted zip or bug: expected " + this.centralDirRecords + " records in central dir, got " + this.files.length); + } else { + // We found some records but not all. + // Something is wrong but we got something for the user: no error here. + // console.warn("expected", this.centralDirRecords, "records in central dir, got", this.files.length); + } + } + }, + /** + * Read the end of central directory. + */ + readEndOfCentral: function() { + var offset = this.reader.lastIndexOfSignature(sig.CENTRAL_DIRECTORY_END); + if (offset < 0) { + // Check if the content is a truncated zip or complete garbage. + // A "LOCAL_FILE_HEADER" is not required at the beginning (auto + // extractible zip for example) but it can give a good hint. + // If an ajax request was used without responseType, we will also + // get unreadable data. + var isGarbage = !this.isSignature(0, sig.LOCAL_FILE_HEADER); + + if (isGarbage) { + throw new Error("Can't find end of central directory : is this a zip file ? " + + "If it is, see https://stuk.github.io/jszip/documentation/howto/read_zip.html"); + } else { + throw new Error("Corrupted zip: can't find end of central directory"); + } + + } + this.reader.setIndex(offset); + var endOfCentralDirOffset = offset; + this.checkSignature(sig.CENTRAL_DIRECTORY_END); + this.readBlockEndOfCentral(); + + + /* extract from the zip spec : + 4) If one of the fields in the end of central directory + record is too small to hold required data, the field + should be set to -1 (0xFFFF or 0xFFFFFFFF) and the + ZIP64 format record should be created. + 5) The end of central directory record and the + Zip64 end of central directory locator record must + reside on the same disk when splitting or spanning + an archive. + */ + if (this.diskNumber === utils.MAX_VALUE_16BITS || this.diskWithCentralDirStart === utils.MAX_VALUE_16BITS || this.centralDirRecordsOnThisDisk === utils.MAX_VALUE_16BITS || this.centralDirRecords === utils.MAX_VALUE_16BITS || this.centralDirSize === utils.MAX_VALUE_32BITS || this.centralDirOffset === utils.MAX_VALUE_32BITS) { + this.zip64 = true; + + /* + Warning : the zip64 extension is supported, but ONLY if the 64bits integer read from + the zip file can fit into a 32bits integer. This cannot be solved : JavaScript represents + all numbers as 64-bit double precision IEEE 754 floating point numbers. + So, we have 53bits for integers and bitwise operations treat everything as 32bits. + see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Bitwise_Operators + and http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf section 8.5 + */ + + // should look for a zip64 EOCD locator + offset = this.reader.lastIndexOfSignature(sig.ZIP64_CENTRAL_DIRECTORY_LOCATOR); + if (offset < 0) { + throw new Error("Corrupted zip: can't find the ZIP64 end of central directory locator"); + } + this.reader.setIndex(offset); + this.checkSignature(sig.ZIP64_CENTRAL_DIRECTORY_LOCATOR); + this.readBlockZip64EndOfCentralLocator(); + + // now the zip64 EOCD record + if (!this.isSignature(this.relativeOffsetEndOfZip64CentralDir, sig.ZIP64_CENTRAL_DIRECTORY_END)) { + // console.warn("ZIP64 end of central directory not where expected."); + this.relativeOffsetEndOfZip64CentralDir = this.reader.lastIndexOfSignature(sig.ZIP64_CENTRAL_DIRECTORY_END); + if (this.relativeOffsetEndOfZip64CentralDir < 0) { + throw new Error("Corrupted zip: can't find the ZIP64 end of central directory"); + } + } + this.reader.setIndex(this.relativeOffsetEndOfZip64CentralDir); + this.checkSignature(sig.ZIP64_CENTRAL_DIRECTORY_END); + this.readBlockZip64EndOfCentral(); + } + + var expectedEndOfCentralDirOffset = this.centralDirOffset + this.centralDirSize; + if (this.zip64) { + expectedEndOfCentralDirOffset += 20; // end of central dir 64 locator + expectedEndOfCentralDirOffset += 12 /* should not include the leading 12 bytes */ + this.zip64EndOfCentralSize; + } + + var extraBytes = endOfCentralDirOffset - expectedEndOfCentralDirOffset; + + if (extraBytes > 0) { + // console.warn(extraBytes, "extra bytes at beginning or within zipfile"); + if (this.isSignature(endOfCentralDirOffset, sig.CENTRAL_FILE_HEADER)) { + // The offsets seem wrong, but we have something at the specified offset. + // So… we keep it. + } else { + // the offset is wrong, update the "zero" of the reader + // this happens if data has been prepended (crx files for example) + this.reader.zero = extraBytes; + } + } else if (extraBytes < 0) { + throw new Error("Corrupted zip: missing " + Math.abs(extraBytes) + " bytes."); + } + }, + prepareReader: function(data) { + this.reader = readerFor(data); + }, + /** + * Read a zip file and create ZipEntries. + * @param {String|ArrayBuffer|Uint8Array|Buffer} data the binary string representing a zip file. + */ + load: function(data) { + this.prepareReader(data); + this.readEndOfCentral(); + this.readCentralDir(); + this.readLocalFiles(); + } +}; +// }}} end of ZipEntries +module.exports = ZipEntries; + +},{"./reader/readerFor":22,"./signature":23,"./support":30,"./utf8":31,"./utils":32,"./zipEntry":34}],34:[function(require,module,exports){ +'use strict'; +var readerFor = require('./reader/readerFor'); +var utils = require('./utils'); +var CompressedObject = require('./compressedObject'); +var crc32fn = require('./crc32'); +var utf8 = require('./utf8'); +var compressions = require('./compressions'); +var support = require('./support'); + +var MADE_BY_DOS = 0x00; +var MADE_BY_UNIX = 0x03; + +/** + * Find a compression registered in JSZip. + * @param {string} compressionMethod the method magic to find. + * @return {Object|null} the JSZip compression object, null if none found. + */ +var findCompression = function(compressionMethod) { + for (var method in compressions) { + if (!compressions.hasOwnProperty(method)) { + continue; + } + if (compressions[method].magic === compressionMethod) { + return compressions[method]; + } + } + return null; +}; + +// class ZipEntry {{{ +/** + * An entry in the zip file. + * @constructor + * @param {Object} options Options of the current file. + * @param {Object} loadOptions Options for loading the stream. + */ +function ZipEntry(options, loadOptions) { + this.options = options; + this.loadOptions = loadOptions; +} +ZipEntry.prototype = { + /** + * say if the file is encrypted. + * @return {boolean} true if the file is encrypted, false otherwise. + */ + isEncrypted: function() { + // bit 1 is set + return (this.bitFlag & 0x0001) === 0x0001; + }, + /** + * say if the file has utf-8 filename/comment. + * @return {boolean} true if the filename/comment is in utf-8, false otherwise. + */ + useUTF8: function() { + // bit 11 is set + return (this.bitFlag & 0x0800) === 0x0800; + }, + /** + * Read the local part of a zip file and add the info in this object. + * @param {DataReader} reader the reader to use. + */ + readLocalPart: function(reader) { + var compression, localExtraFieldsLength; + + // we already know everything from the central dir ! + // If the central dir data are false, we are doomed. + // On the bright side, the local part is scary : zip64, data descriptors, both, etc. + // The less data we get here, the more reliable this should be. + // Let's skip the whole header and dash to the data ! + reader.skip(22); + // in some zip created on windows, the filename stored in the central dir contains \ instead of /. + // Strangely, the filename here is OK. + // I would love to treat these zip files as corrupted (see http://www.info-zip.org/FAQ.html#backslashes + // or APPNOTE#4.4.17.1, "All slashes MUST be forward slashes '/'") but there are a lot of bad zip generators... + // Search "unzip mismatching "local" filename continuing with "central" filename version" on + // the internet. + // + // I think I see the logic here : the central directory is used to display + // content and the local directory is used to extract the files. Mixing / and \ + // may be used to display \ to windows users and use / when extracting the files. + // Unfortunately, this lead also to some issues : http://seclists.org/fulldisclosure/2009/Sep/394 + this.fileNameLength = reader.readInt(2); + localExtraFieldsLength = reader.readInt(2); // can't be sure this will be the same as the central dir + // the fileName is stored as binary data, the handleUTF8 method will take care of the encoding. + this.fileName = reader.readData(this.fileNameLength); + reader.skip(localExtraFieldsLength); + + if (this.compressedSize === -1 || this.uncompressedSize === -1) { + throw new Error("Bug or corrupted zip : didn't get enough information from the central directory " + "(compressedSize === -1 || uncompressedSize === -1)"); + } + + compression = findCompression(this.compressionMethod); + if (compression === null) { // no compression found + throw new Error("Corrupted zip : compression " + utils.pretty(this.compressionMethod) + " unknown (inner file : " + utils.transformTo("string", this.fileName) + ")"); + } + this.decompressed = new CompressedObject(this.compressedSize, this.uncompressedSize, this.crc32, compression, reader.readData(this.compressedSize)); + }, + + /** + * Read the central part of a zip file and add the info in this object. + * @param {DataReader} reader the reader to use. + */ + readCentralPart: function(reader) { + this.versionMadeBy = reader.readInt(2); + reader.skip(2); + // this.versionNeeded = reader.readInt(2); + this.bitFlag = reader.readInt(2); + this.compressionMethod = reader.readString(2); + this.date = reader.readDate(); + this.crc32 = reader.readInt(4); + this.compressedSize = reader.readInt(4); + this.uncompressedSize = reader.readInt(4); + var fileNameLength = reader.readInt(2); + this.extraFieldsLength = reader.readInt(2); + this.fileCommentLength = reader.readInt(2); + this.diskNumberStart = reader.readInt(2); + this.internalFileAttributes = reader.readInt(2); + this.externalFileAttributes = reader.readInt(4); + this.localHeaderOffset = reader.readInt(4); + + if (this.isEncrypted()) { + throw new Error("Encrypted zip are not supported"); + } + + // will be read in the local part, see the comments there + reader.skip(fileNameLength); + this.readExtraFields(reader); + this.parseZIP64ExtraField(reader); + this.fileComment = reader.readData(this.fileCommentLength); + }, + + /** + * Parse the external file attributes and get the unix/dos permissions. + */ + processAttributes: function () { + this.unixPermissions = null; + this.dosPermissions = null; + var madeBy = this.versionMadeBy >> 8; + + // Check if we have the DOS directory flag set. + // We look for it in the DOS and UNIX permissions + // but some unknown platform could set it as a compatibility flag. + this.dir = this.externalFileAttributes & 0x0010 ? true : false; + + if(madeBy === MADE_BY_DOS) { + // first 6 bits (0 to 5) + this.dosPermissions = this.externalFileAttributes & 0x3F; + } + + if(madeBy === MADE_BY_UNIX) { + this.unixPermissions = (this.externalFileAttributes >> 16) & 0xFFFF; + // the octal permissions are in (this.unixPermissions & 0x01FF).toString(8); + } + + // fail safe : if the name ends with a / it probably means a folder + if (!this.dir && this.fileNameStr.slice(-1) === '/') { + this.dir = true; + } + }, + + /** + * Parse the ZIP64 extra field and merge the info in the current ZipEntry. + * @param {DataReader} reader the reader to use. + */ + parseZIP64ExtraField: function(reader) { + + if (!this.extraFields[0x0001]) { + return; + } + + // should be something, preparing the extra reader + var extraReader = readerFor(this.extraFields[0x0001].value); + + // I really hope that these 64bits integer can fit in 32 bits integer, because js + // won't let us have more. + if (this.uncompressedSize === utils.MAX_VALUE_32BITS) { + this.uncompressedSize = extraReader.readInt(8); + } + if (this.compressedSize === utils.MAX_VALUE_32BITS) { + this.compressedSize = extraReader.readInt(8); + } + if (this.localHeaderOffset === utils.MAX_VALUE_32BITS) { + this.localHeaderOffset = extraReader.readInt(8); + } + if (this.diskNumberStart === utils.MAX_VALUE_32BITS) { + this.diskNumberStart = extraReader.readInt(4); + } + }, + /** + * Read the central part of a zip file and add the info in this object. + * @param {DataReader} reader the reader to use. + */ + readExtraFields: function(reader) { + var end = reader.index + this.extraFieldsLength, + extraFieldId, + extraFieldLength, + extraFieldValue; + + if (!this.extraFields) { + this.extraFields = {}; + } + + while (reader.index + 4 < end) { + extraFieldId = reader.readInt(2); + extraFieldLength = reader.readInt(2); + extraFieldValue = reader.readData(extraFieldLength); + + this.extraFields[extraFieldId] = { + id: extraFieldId, + length: extraFieldLength, + value: extraFieldValue + }; + } + + reader.setIndex(end); + }, + /** + * Apply an UTF8 transformation if needed. + */ + handleUTF8: function() { + var decodeParamType = support.uint8array ? "uint8array" : "array"; + if (this.useUTF8()) { + this.fileNameStr = utf8.utf8decode(this.fileName); + this.fileCommentStr = utf8.utf8decode(this.fileComment); + } else { + var upath = this.findExtraFieldUnicodePath(); + if (upath !== null) { + this.fileNameStr = upath; + } else { + // ASCII text or unsupported code page + var fileNameByteArray = utils.transformTo(decodeParamType, this.fileName); + this.fileNameStr = this.loadOptions.decodeFileName(fileNameByteArray); + } + + var ucomment = this.findExtraFieldUnicodeComment(); + if (ucomment !== null) { + this.fileCommentStr = ucomment; + } else { + // ASCII text or unsupported code page + var commentByteArray = utils.transformTo(decodeParamType, this.fileComment); + this.fileCommentStr = this.loadOptions.decodeFileName(commentByteArray); + } + } + }, + + /** + * Find the unicode path declared in the extra field, if any. + * @return {String} the unicode path, null otherwise. + */ + findExtraFieldUnicodePath: function() { + var upathField = this.extraFields[0x7075]; + if (upathField) { + var extraReader = readerFor(upathField.value); + + // wrong version + if (extraReader.readInt(1) !== 1) { + return null; + } + + // the crc of the filename changed, this field is out of date. + if (crc32fn(this.fileName) !== extraReader.readInt(4)) { + return null; + } + + return utf8.utf8decode(extraReader.readData(upathField.length - 5)); + } + return null; + }, + + /** + * Find the unicode comment declared in the extra field, if any. + * @return {String} the unicode comment, null otherwise. + */ + findExtraFieldUnicodeComment: function() { + var ucommentField = this.extraFields[0x6375]; + if (ucommentField) { + var extraReader = readerFor(ucommentField.value); + + // wrong version + if (extraReader.readInt(1) !== 1) { + return null; + } + + // the crc of the comment changed, this field is out of date. + if (crc32fn(this.fileComment) !== extraReader.readInt(4)) { + return null; + } + + return utf8.utf8decode(extraReader.readData(ucommentField.length - 5)); + } + return null; + } +}; +module.exports = ZipEntry; + +},{"./compressedObject":2,"./compressions":3,"./crc32":4,"./reader/readerFor":22,"./support":30,"./utf8":31,"./utils":32}],35:[function(require,module,exports){ +'use strict'; + +var StreamHelper = require('./stream/StreamHelper'); +var DataWorker = require('./stream/DataWorker'); +var utf8 = require('./utf8'); +var CompressedObject = require('./compressedObject'); +var GenericWorker = require('./stream/GenericWorker'); + +/** + * A simple object representing a file in the zip file. + * @constructor + * @param {string} name the name of the file + * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data + * @param {Object} options the options of the file + */ +var ZipObject = function(name, data, options) { + this.name = name; + this.dir = options.dir; + this.date = options.date; + this.comment = options.comment; + this.unixPermissions = options.unixPermissions; + this.dosPermissions = options.dosPermissions; + + this._data = data; + this._dataBinary = options.binary; + // keep only the compression + this.options = { + compression : options.compression, + compressionOptions : options.compressionOptions + }; +}; + +ZipObject.prototype = { + /** + * Create an internal stream for the content of this object. + * @param {String} type the type of each chunk. + * @return StreamHelper the stream. + */ + internalStream: function (type) { + var result = null, outputType = "string"; + try { + if (!type) { + throw new Error("No output type specified."); + } + outputType = type.toLowerCase(); + var askUnicodeString = outputType === "string" || outputType === "text"; + if (outputType === "binarystring" || outputType === "text") { + outputType = "string"; + } + result = this._decompressWorker(); + + var isUnicodeString = !this._dataBinary; + + if (isUnicodeString && !askUnicodeString) { + result = result.pipe(new utf8.Utf8EncodeWorker()); + } + if (!isUnicodeString && askUnicodeString) { + result = result.pipe(new utf8.Utf8DecodeWorker()); + } + } catch (e) { + result = new GenericWorker("error"); + result.error(e); + } + + return new StreamHelper(result, outputType, ""); + }, + + /** + * Prepare the content in the asked type. + * @param {String} type the type of the result. + * @param {Function} onUpdate a function to call on each internal update. + * @return Promise the promise of the result. + */ + async: function (type, onUpdate) { + return this.internalStream(type).accumulate(onUpdate); + }, + + /** + * Prepare the content as a nodejs stream. + * @param {String} type the type of each chunk. + * @param {Function} onUpdate a function to call on each internal update. + * @return Stream the stream. + */ + nodeStream: function (type, onUpdate) { + return this.internalStream(type || "nodebuffer").toNodejsStream(onUpdate); + }, + + /** + * Return a worker for the compressed content. + * @private + * @param {Object} compression the compression object to use. + * @param {Object} compressionOptions the options to use when compressing. + * @return Worker the worker. + */ + _compressWorker: function (compression, compressionOptions) { + if ( + this._data instanceof CompressedObject && + this._data.compression.magic === compression.magic + ) { + return this._data.getCompressedWorker(); + } else { + var result = this._decompressWorker(); + if(!this._dataBinary) { + result = result.pipe(new utf8.Utf8EncodeWorker()); + } + return CompressedObject.createWorkerFrom(result, compression, compressionOptions); + } + }, + /** + * Return a worker for the decompressed content. + * @private + * @return Worker the worker. + */ + _decompressWorker : function () { + if (this._data instanceof CompressedObject) { + return this._data.getContentWorker(); + } else if (this._data instanceof GenericWorker) { + return this._data; + } else { + return new DataWorker(this._data); + } + } +}; + +var removedMethods = ["asText", "asBinary", "asNodeBuffer", "asUint8Array", "asArrayBuffer"]; +var removedFn = function () { + throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide."); +}; + +for(var i = 0; i < removedMethods.length; i++) { + ZipObject.prototype[removedMethods[i]] = removedFn; +} +module.exports = ZipObject; + +},{"./compressedObject":2,"./stream/DataWorker":27,"./stream/GenericWorker":28,"./stream/StreamHelper":29,"./utf8":31}],36:[function(require,module,exports){ +(function (global){ +'use strict'; +var Mutation = global.MutationObserver || global.WebKitMutationObserver; + +var scheduleDrain; + +{ + if (Mutation) { + var called = 0; + var observer = new Mutation(nextTick); + var element = global.document.createTextNode(''); + observer.observe(element, { + characterData: true + }); + scheduleDrain = function () { + element.data = (called = ++called % 2); + }; + } else if (!global.setImmediate && typeof global.MessageChannel !== 'undefined') { + var channel = new global.MessageChannel(); + channel.port1.onmessage = nextTick; + scheduleDrain = function () { + channel.port2.postMessage(0); + }; + } else if ('document' in global && 'onreadystatechange' in global.document.createElement('script')) { + scheduleDrain = function () { + + // Create a + + + + + +var data = {"i0":9,"i1":9,"i2":9,"i3":9,"i4":9,"i5":9,"i6":9,"i7":9,"i8":9,"i9":9,"i10":9,"i11":9,"i12":9}; +var tabs = {65535:["t0","All Methods"],1:["t1","Static Methods"],8:["t4","Concrete Methods"]}; +var altColor = "altColor"; +var rowColor = "rowColor"; +var tableTab = "tableTab"; +var activeTableTab = "activeTableTab"; +var pathtoroot = "../../../"; +var useModuleDirectories = true; +loadScripts(document, 'script'); +
    + +
    +
    -
    org.libjpegturbo.turbojpeg
    +

    Class TJ

    @@ -95,8 +130,7 @@

    Class TJ


    • -
      -
      public final class TJ
      +
      public final class TJ
       extends java.lang.Object
      TurboJPEG utility class (cannot be instantiated)
    • @@ -106,372 +140,601 @@

      Class TJ

      • +
        +
        +
          -
        • +
        • Method Summary

          - - +
          Methods 
          + - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + + + - + - + - + - + - + - +
          All Methods Static Methods Concrete Methods 
          Modifier and TypeMethod and DescriptionMethodDescription
          static intbufSize(int width, +bufSize​(int width, int height, - int jpegSubsamp) + int jpegSubsamp)
          Returns the maximum size of the buffer (in bytes) required to hold a JPEG image with the given width, height, and level of chrominance subsampling.
          static intbufSizeYUV(int width, +bufSizeYUV​(int width, int align, int height, - int subsamp) + int subsamp)
          Returns the size of the buffer (in bytes) required to hold a unified planar YUV image with the given width, height, and level of chrominance subsampling.
          static intgetAlphaOffset(int pixelFormat) -
          For the given pixel format, returns the number of bytes that the alpha +
          getAlphaOffset​(int pixelFormat) +
          For the given pixel format, returns the number of samples that the alpha component is offset from the start of the pixel.
          static intgetBlueOffset(int pixelFormat) -
          For the given pixel format, returns the number of bytes that the blue +
          getBlueOffset​(int pixelFormat) +
          For the given pixel format, returns the number of samples that the blue component is offset from the start of the pixel.
          static intgetGreenOffset(int pixelFormat) -
          For the given pixel format, returns the number of bytes that the green +
          getGreenOffset​(int pixelFormat) +
          For the given pixel format, returns the number of samples that the green component is offset from the start of the pixel.
          static intgetMCUHeight(int subsamp) +getMCUHeight​(int subsamp)
          Returns the MCU block height for the given level of chrominance subsampling.
          static intgetMCUWidth(int subsamp) +getMCUWidth​(int subsamp)
          Returns the MCU block width for the given level of chrominance subsampling.
          static intgetPixelSize(int pixelFormat) -
          Returns the pixel size (in bytes) for the given pixel format.
          +
          getPixelSize​(int pixelFormat) +
          Returns the pixel size (in samples) for the given pixel format.
          static intgetRedOffset(int pixelFormat) -
          For the given pixel format, returns the number of bytes that the red +
          getRedOffset​(int pixelFormat) +
          For the given pixel format, returns the number of samples that the red component is offset from the start of the pixel.
          static TJScalingFactor[]getScalingFactors() +
          static TJScalingFactor[]getScalingFactors()
          Returns a list of fractional scaling factors that the JPEG decompressor supports.
          static intplaneHeight(int componentID, +planeHeight​(int componentID, int height, - int subsamp) + int subsamp)
          Returns the plane height of a YUV image plane with the given parameters.
          static intplaneSizeYUV(int componentID, +planeSizeYUV​(int componentID, int width, int stride, int height, - int subsamp) + int subsamp)
          Returns the size of the buffer (in bytes) required to hold a YUV image plane with the given parameters.
          static intplaneWidth(int componentID, +planeWidth​(int componentID, int width, - int subsamp) + int subsamp)
          Returns the plane width of a YUV image plane with the given parameters.
            -
          • +
          • Methods inherited from class java.lang.Object

            @@ -479,6 +742,7 @@

            Methods inherited from class java.lang.Object

        +
    @@ -486,12 +750,13 @@

    Methods inherited from class java.lang.Object

    • +
        -
      • +
      • Field Detail

        - + - +
          @@ -512,10 +780,13 @@

          SAMP_444

          4:4:4 chrominance subsampling (no chrominance subsampling). The JPEG or YUV image will contain one chrominance component for every pixel in the source image.
          -
          See Also:
          Constant Field Values
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
          @@ -524,10 +795,13 @@

          SAMP_422

          public static final int SAMP_422
          4:2:2 chrominance subsampling. The JPEG or YUV image will contain one chrominance component for every 2x1 block of pixels in the source image.
          -
          See Also:
          Constant Field Values
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
          @@ -536,10 +810,13 @@

          SAMP_420

          public static final int SAMP_420
          4:2:0 chrominance subsampling. The JPEG or YUV image will contain one chrominance component for every 2x2 block of pixels in the source image.
          -
          See Also:
          Constant Field Values
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
          @@ -547,10 +824,13 @@

          SAMP_420

          SAMP_GRAY

          public static final int SAMP_GRAY
          Grayscale. The JPEG or YUV image will contain no chrominance components.
          -
          See Also:
          Constant Field Values
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
          @@ -560,10 +840,13 @@

          SAMP_440

          4:4:0 chrominance subsampling. The JPEG or YUV image will contain one chrominance component for every 1x2 block of pixels in the source image. Note that 4:4:0 subsampling is not fully accelerated in libjpeg-turbo.
          -
          See Also:
          Constant Field Values
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
          @@ -578,10 +861,35 @@

          SAMP_411

          perceptual quality. However, 4:1:1 is better able to reproduce sharp horizontal features. Note that 4:1:1 subsampling is not fully accelerated in libjpeg-turbo. -
          See Also:
          Constant Field Values
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - + + + +
          +
        • +

          SAMP_UNKNOWN

          +
          public static final int SAMP_UNKNOWN
          +
          Unknown subsampling. The JPEG image uses an unusual type of chrominance + subsampling. Such images can be decompressed into packed-pixel images, + but they cannot be +
            +
          • decompressed into planar YUV images, +
          • losslessly transformed if TJTransform.OPT_CROP is specified, + or +
          • partially decompressed using a cropping region. +
          +
          +
          See Also:
          +
          Constant Field Values
          +
          +
        • +
        + - +
          @@ -600,12 +911,15 @@

          NUMPF

          PF_RGB

          public static final int PF_RGB
          RGB pixel format. The red, green, and blue components in the image are - stored in 3-byte pixels in the order R, G, B from lowest to highest byte - address within each pixel.
          -
          See Also:
          Constant Field Values
          + stored in 3-sample pixels in the order R, G, B from lowest to highest + memory address within each pixel. +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
          @@ -613,12 +927,15 @@

          PF_RGB

          PF_BGR

          public static final int PF_BGR
          BGR pixel format. The red, green, and blue components in the image are - stored in 3-byte pixels in the order B, G, R from lowest to highest byte - address within each pixel.
          -
          See Also:
          Constant Field Values
          + stored in 3-sample pixels in the order B, G, R from lowest to highest + memory address within each pixel. +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
          @@ -626,13 +943,16 @@

          PF_BGR

          PF_RGBX

          public static final int PF_RGBX
          RGBX pixel format. The red, green, and blue components in the image are - stored in 4-byte pixels in the order R, G, B from lowest to highest byte - address within each pixel. The X component is ignored when compressing - and undefined when decompressing.
          -
          See Also:
          Constant Field Values
          + stored in 4-sample pixels in the order R, G, B from lowest to highest + memory address within each pixel. The X component is ignored when + compressing and undefined when decompressing. +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
          @@ -640,13 +960,16 @@

          PF_RGBX

          PF_BGRX

          public static final int PF_BGRX
          BGRX pixel format. The red, green, and blue components in the image are - stored in 4-byte pixels in the order B, G, R from lowest to highest byte - address within each pixel. The X component is ignored when compressing - and undefined when decompressing.
          -
          See Also:
          Constant Field Values
          + stored in 4-sample pixels in the order B, G, R from lowest to highest + memory address within each pixel. The X component is ignored when + compressing and undefined when decompressing. +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
          @@ -654,13 +977,16 @@

          PF_BGRX

          PF_XBGR

          public static final int PF_XBGR
          XBGR pixel format. The red, green, and blue components in the image are - stored in 4-byte pixels in the order R, G, B from highest to lowest byte - address within each pixel. The X component is ignored when compressing - and undefined when decompressing.
          -
          See Also:
          Constant Field Values
          + stored in 4-sample pixels in the order R, G, B from highest to lowest + memory address within each pixel. The X component is ignored when + compressing and undefined when decompressing. +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
          @@ -668,77 +994,96 @@

          PF_XBGR

          PF_XRGB

          public static final int PF_XRGB
          XRGB pixel format. The red, green, and blue components in the image are - stored in 4-byte pixels in the order B, G, R from highest to lowest byte - address within each pixel. The X component is ignored when compressing - and undefined when decompressing.
          -
          See Also:
          Constant Field Values
          + stored in 4-sample pixels in the order B, G, R from highest to lowest + memory address within each pixel. The X component is ignored when + compressing and undefined when decompressing. +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
        • PF_GRAY

          public static final int PF_GRAY
          -
          Grayscale pixel format. Each 1-byte pixel represents a luminance - (brightness) level from 0 to 255.
          -
          See Also:
          Constant Field Values
          +
          Grayscale pixel format. Each 1-sample pixel represents a luminance + (brightness) level from 0 to the maximum sample value (255 for 8-bit + samples, 4095 for 12-bit samples, and 65535 for 16-bit samples.)
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
        • PF_RGBA

          public static final int PF_RGBA
          -
          RGBA pixel format. This is the same as PF_RGBX, except that when - decompressing, the X byte is guaranteed to be 0xFF, which can be - interpreted as an opaque alpha channel.
          -
          See Also:
          Constant Field Values
          +
          RGBA pixel format. This is the same as PF_RGBX, except that when + decompressing, the X component is guaranteed to be equal to the maximum + sample value, which can be interpreted as an opaque alpha channel.
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
        • PF_BGRA

          public static final int PF_BGRA
          -
          BGRA pixel format. This is the same as PF_BGRX, except that when - decompressing, the X byte is guaranteed to be 0xFF, which can be - interpreted as an opaque alpha channel.
          -
          See Also:
          Constant Field Values
          +
          BGRA pixel format. This is the same as PF_BGRX, except that when + decompressing, the X component is guaranteed to be equal to the maximum + sample value, which can be interpreted as an opaque alpha channel.
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
        • PF_ABGR

          public static final int PF_ABGR
          -
          ABGR pixel format. This is the same as PF_XBGR, except that when - decompressing, the X byte is guaranteed to be 0xFF, which can be - interpreted as an opaque alpha channel.
          -
          See Also:
          Constant Field Values
          +
          ABGR pixel format. This is the same as PF_XBGR, except that when + decompressing, the X component is guaranteed to be equal to the maximum + sample value, which can be interpreted as an opaque alpha channel.
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
        • PF_ARGB

          public static final int PF_ARGB
          -
          ARGB pixel format. This is the same as PF_XRGB, except that when - decompressing, the X byte is guaranteed to be 0xFF, which can be - interpreted as an opaque alpha channel.
          -
          See Also:
          Constant Field Values
          +
          ARGB pixel format. This is the same as PF_XRGB, except that when + decompressing, the X component is guaranteed to be equal to the maximum + sample value, which can be interpreted as an opaque alpha channel.
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
          @@ -756,12 +1101,15 @@

          PF_CMYK

          vice versa, but the mapping is typically not 1:1 or reversible, nor can it be defined with a simple formula. Thus, such a conversion is out of scope for a codec library. However, the TurboJPEG API allows for compressing - packed-pixel CMYK images into YCCK JPEG images (see CS_YCCK) and + packed-pixel CMYK images into YCCK JPEG images (see CS_YCCK) and decompressing YCCK JPEG images into packed-pixel CMYK images. -
          See Also:
          Constant Field Values
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - + - +
          @@ -782,13 +1133,16 @@

          CS_RGB

          RGB colorspace. When compressing the JPEG image, the R, G, and B components in the source image are reordered into image planes, but no colorspace conversion or subsampling is performed. RGB JPEG images can be - decompressed to packed-pixel images with any of the extended RGB or - grayscale pixel formats, but they cannot be decompressed to planar YUV - images.
          -
          See Also:
          Constant Field Values
          + compressed from and decompressed to packed-pixel images with any of the + extended RGB or grayscale pixel formats, but they cannot be compressed + from or decompressed to planar YUV images. +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
          @@ -810,10 +1164,13 @@

          CS_YCbCr

          images with any of the extended RGB or grayscale pixel formats. YCbCr JPEG images can also be compressed from and decompressed to planar YUV images. -
          See Also:
          Constant Field Values
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
          @@ -826,10 +1183,13 @@

          CS_GRAY

          packed-pixel images with any of the extended RGB or grayscale pixel formats, or they can be compressed from and decompressed to planar YUV images. -
          See Also:
          Constant Field Values
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
          @@ -839,11 +1199,15 @@

          CS_CMYK

          CMYK colorspace. When compressing the JPEG image, the C, M, Y, and K components in the source image are reordered into image planes, but no colorspace conversion or subsampling is performed. CMYK JPEG images can - only be decompressed to packed-pixel images with the CMYK pixel format.
          -
          See Also:
          Constant Field Values
          + only be compressed from and decompressed to packed-pixel images with the + CMYK pixel format. +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
          @@ -857,159 +1221,716 @@

          CS_YCCK

          components in the YCCK pixels can be subsampled without incurring major perceptual loss. YCCK JPEG images can only be compressed from and decompressed to packed-pixel images with the CMYK pixel format. -
          See Also:
          Constant Field Values
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
        • -

          FLAG_BOTTOMUP

          -
          public static final int FLAG_BOTTOMUP
          -
          Rows in the packed-pixel source/destination image are stored in bottom-up - (Windows, OpenGL) order rather than in top-down (X11) order.
          -
          See Also:
          Constant Field Values
          +

          PARAM_STOPONWARNING

          +
          public static final int PARAM_STOPONWARNING
          +
          Error handling behavior + +

          Value +

            +
          • 0 [default] Allow the current + compression/decompression/transform operation to complete unless a fatal + error is encountered. +
          • 1 Immediately discontinue the current + compression/decompression/transform operation if a warning (non-fatal + error) occurs. +
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
        • -

          FLAG_FASTUPSAMPLE

          -
          public static final int FLAG_FASTUPSAMPLE
          -
          When decompressing an image that was compressed using chrominance - subsampling, use the fastest chrominance upsampling algorithm available. - The default is to use smooth upsampling, which creates a smooth transition - between neighboring chrominance components in order to reduce upsampling - artifacts in the decompressed image.
          -
          See Also:
          Constant Field Values
          +

          PARAM_BOTTOMUP

          +
          public static final int PARAM_BOTTOMUP
          +
          Row order in packed-pixel source/destination images + +

          Value +

            +
          • 0 [default] top-down (X11) order +
          • 1 bottom-up (Windows, OpenGL) order +
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
        • -

          FLAG_FASTDCT

          -
          public static final int FLAG_FASTDCT
          -
          Use the fastest DCT/IDCT algorithm available. The default if this flag is - not specified is implementation-specific. For example, the implementation - of the TurboJPEG API in libjpeg-turbo uses the fast algorithm by default - when compressing, because this has been shown to have only a very slight - effect on accuracy, but it uses the accurate algorithm when decompressing, - because this has been shown to have a larger effect.
          -
          See Also:
          Constant Field Values
          +

          PARAM_QUALITY

          +
          public static final int PARAM_QUALITY
          +
          Perceptual quality of lossy JPEG images [compression only] + +

          Value +

            +
          • 1-100 (1 = worst quality but + best compression, 100 = best quality but worst compression) + [no default; must be explicitly specified] +
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
        • -

          FLAG_ACCURATEDCT

          -
          public static final int FLAG_ACCURATEDCT
          -
          Use the most accurate DCT/IDCT algorithm available. The default if this - flag is not specified is implementation-specific. For example, the - implementation of the TurboJPEG API in libjpeg-turbo uses the fast - algorithm by default when compressing, because this has been shown to have - only a very slight effect on accuracy, but it uses the accurate algorithm - when decompressing, because this has been shown to have a larger effect.
          -
          See Also:
          Constant Field Values
          +

          PARAM_SUBSAMP

          +
          public static final int PARAM_SUBSAMP
          +
          Chrominance subsampling level + +

          The JPEG or YUV image uses (decompression, decoding) or will use (lossy + compression, encoding) the specified level of chrominance subsampling. + +

          When pixels are converted from RGB to YCbCr (see CS_YCbCr) or + from CMYK to YCCK (see CS_YCCK) as part of the JPEG compression + process, some of the Cb and Cr (chrominance) components can be discarded + or averaged together to produce a smaller image with little perceptible + loss of image clarity. (The human eye is more sensitive to small changes + in brightness than to small changes in color.) This is called + "chrominance subsampling". + +

          Value +

            +
          • One of TJ.SAMP_* [no default; must be + explicitly specified for lossy compression, encoding, and decoding] +
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
        • -

          FLAG_STOPONWARNING

          -
          public static final int FLAG_STOPONWARNING
          -
          Immediately discontinue the current compression/decompression/transform - operation if a warning (non-fatal error) occurs. The default behavior is - to allow the operation to complete unless a fatal error is encountered. -

          - NOTE: due to the design of the TurboJPEG Java API, only certain methods - (specifically, TJDecompressor.decompress*() methods - with a void return type) will complete and leave the destination image in - a fully recoverable state after a non-fatal error occurs.

          -
          See Also:
          Constant Field Values
          +

          PARAM_JPEGWIDTH

          +
          public static final int PARAM_JPEGWIDTH
          +
          JPEG width (in pixels) [decompression only, read-only]
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
        • -

          FLAG_PROGRESSIVE

          -
          public static final int FLAG_PROGRESSIVE
          -
          Use progressive entropy coding in JPEG images generated by compression and - transform operations. Progressive entropy coding will generally improve - compression relative to baseline entropy coding (the default), but it will - reduce compression and decompression performance considerably. Can be - combined with FLAG_ARITHMETIC.
          -
          See Also:
          Constant Field Values
          +

          PARAM_JPEGHEIGHT

          +
          public static final int PARAM_JPEGHEIGHT
          +
          JPEG height (in pixels) [decompression only, read-only]
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
        • -

          FLAG_LIMITSCANS

          -
          public static final int FLAG_LIMITSCANS
          -
          Limit the number of progressive JPEG scans that the decompression and - transform operations will process. If a progressive JPEG image contains - an unreasonably large number of scans, then this flag will cause the - decompression and transform operations to throw an error. The primary - purpose of this is to allow security-critical applications to guard - against an exploit of the progressive JPEG format described in - this report.
          -
          See Also:
          Constant Field Values
          +

          PARAM_PRECISION

          +
          public static final int PARAM_PRECISION
          +
          JPEG data precision (bits per sample) [decompression only, read-only] + +

          The JPEG image uses the specified number of bits per sample. + +

          Value +

            +
          • 8, 12, or 16 +
          + +

          12-bit data precision implies PARAM_OPTIMIZE.

          +
          +
          See Also:
          +
          Constant Field Values
          +
          +
        • +
        + + + +
          +
        • +

          PARAM_COLORSPACE

          +
          public static final int PARAM_COLORSPACE
          +
          JPEG colorspace + +

          The JPEG image uses (decompression) or will use (lossy compression) the + specified colorspace. + +

          Value +

            +
          • One of TJ.CS_* [default for lossy compression: + automatically selected based on the subsampling level and pixel + format] +
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
        • -

          FLAG_ARITHMETIC

          -
          public static final int FLAG_ARITHMETIC
          -
          Use arithmetic entropy coding in JPEG images generated by compression and - transform operations. Arithmetic entropy coding will generally improve - compression relative to Huffman entropy coding (the default), but it will - reduce compression and decompression performance considerably. Can be - combined with FLAG_PROGRESSIVE.
          -
          See Also:
          Constant Field Values
          +

          PARAM_FASTUPSAMPLE

          +
          public static final int PARAM_FASTUPSAMPLE
          +
          Chrominance upsampling algorithm [lossy decompression only] + +

          Value +

            +
          • 0 [default] Use smooth upsampling when + decompressing a JPEG image that was compressed using chrominance + subsampling. This creates a smooth transition between neighboring + chrominance components in order to reduce upsampling artifacts in the + decompressed image. +
          • 1 Use the fastest chrominance upsampling algorithm + available, which may combine upsampling with color conversion. +
          +
          +
          See Also:
          +
          Constant Field Values
          +
          +
        • +
        + + + +
          +
        • +

          PARAM_FASTDCT

          +
          public static final int PARAM_FASTDCT
          +
          DCT/IDCT algorithm [lossy compression and decompression] + +

          Value +

            +
          • 0 [default] Use the most accurate DCT/IDCT + algorithm available. +
          • 1 Use the fastest DCT/IDCT algorithm available. +
          + +

          This parameter is provided mainly for backward compatibility with + libjpeg, which historically implemented several different DCT/IDCT + algorithms because of performance limitations with 1990s CPUs. In the + libjpeg-turbo implementation of the TurboJPEG API: + +

            +
          • The "fast" and "accurate" DCT/IDCT algorithms perform similarly on + modern x86/x86-64 CPUs that support AVX2 instructions. +
          • The "fast" algorithm is generally only about 5-15% faster than the + "accurate" algorithm on other types of CPUs. +
          • The difference in accuracy between the "fast" and "accurate" + algorithms is the most pronounced at JPEG quality levels above 90 and + tends to be more pronounced with decompression than with compression. +
          • The "fast" algorithm degrades and is not fully accelerated for JPEG + quality levels above 97, so it will be slower than the "accurate" + algorithm. +
          +
          +
          See Also:
          +
          Constant Field Values
          +
          +
        • +
        + + + +
          +
        • +

          PARAM_OPTIMIZE

          +
          public static final int PARAM_OPTIMIZE
          +
          Optimized baseline entropy coding [lossy compression only] + +

          Value +

            +
          • 0 [default] The JPEG image will use the default + Huffman tables. +
          • 1 Optimal Huffman tables will be computed for the JPEG + image. For lossless transformation, this can also be specified using + TJTransform.OPT_OPTIMIZE. +
          + +

          Optimized baseline entropy coding will improve compression slightly + (generally 5% or less), but it will reduce compression performance + considerably.

          +
          +
          See Also:
          +
          Constant Field Values
          +
          +
        • +
        + + + +
          +
        • +

          PARAM_PROGRESSIVE

          +
          public static final int PARAM_PROGRESSIVE
          +
          Progressive entropy coding + +

          Value +

            +
          • 0 [default for compression, lossless + transformation] The lossy JPEG image uses (decompression) or will use + (compression, lossless transformation) baseline entropy coding. +
          • 1 The lossy JPEG image uses (decompression) or will use + (compression, lossless transformation) progressive entropy coding. For + lossless transformation, this can also be specified using + TJTransform.OPT_PROGRESSIVE. +
          + +

          Progressive entropy coding will generally improve compression relative + to baseline entropy coding, but it will reduce compression and + decompression performance considerably. Implies PARAM_OPTIMIZE. + Can be combined with PARAM_ARITHMETIC.

          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
        • -

          FLAG_LOSSLESS

          -
          public static final int FLAG_LOSSLESS
          -
          Generate a lossless JPEG image when compressing. In most cases, - compressing and decompressing lossless JPEG images is considerably slower - than compressing and decompressing lossy JPEG images. Also note that the - following features are not available with lossless JPEG images: +

          PARAM_SCANLIMIT

          +
          public static final int PARAM_SCANLIMIT
          +
          Progressive JPEG scan limit for lossy JPEG images [decompression, lossless + transformation] + +

          Setting this parameter will cause the decompression and transform + functions to return an error if the number of scans in a progressive JPEG + image exceeds the specified limit. The primary purpose of this is to + allow security-critical applications to guard against an exploit of the + progressive JPEG format described in + this report. + +

          Value

            -
          • Colorspace conversion -
          • Chrominance subsampling +
          • maximum number of progressive JPEG scans that the decompression and + transform functions will process [default: 0 (no + limit)] +
          +
          +
          See Also:
          +
          PARAM_PROGRESSIVE, +Constant Field Values
          +
          +
        • +
        + + + +
          +
        • +

          PARAM_ARITHMETIC

          +
          public static final int PARAM_ARITHMETIC
          +
          Arithmetic entropy coding + +

          Value +

            +
          • 0 [default for compression, lossless + transformation] The lossy JPEG image uses (decompression) or will use + (compression, lossless transformation) Huffman entropy coding. +
          • 1 The lossy JPEG image uses (decompression) or will use + (compression, lossless transformation) arithmetic entropy coding. For + lossless transformation, this can also be specified using + TJTransform.OPT_ARITHMETIC. +
          + +

          Arithmetic entropy coding will generally improve compression relative + to Huffman entropy coding, but it will reduce compression and + decompression performance considerably. Can be combined with + PARAM_PROGRESSIVE. Arithmetic entropy coding is currently only + implemented for 8-bit samples.

          +
          +
          See Also:
          +
          Constant Field Values
          +
          +
        • +
        + + + +
          +
        • +

          PARAM_LOSSLESS

          +
          public static final int PARAM_LOSSLESS
          +
          Lossless JPEG + +

          Value +

            +
          • 0 [default for compression] The JPEG image is + (decompression) or will be (compression) lossy/DCT-based. +
          • 1 The JPEG image is (decompression) or will be + (compression) lossless/predictive. +
          + +

          In most cases, compressing and decompressing lossless JPEG images is + considerably slower than compressing and decompressing lossy JPEG images. + Also note that the following features are not available with lossless JPEG + images: +

            +
          • Colorspace conversion (lossless JPEG images always use + CS_RGB, CS_GRAY, or CS_CMYK, depending on the + pixel format of the source image) +
          • Chrominance subsampling (lossless JPEG images always use + SAMP_444)
          • JPEG quality selection
          • DCT/IDCT algorithm selection
          • Progressive entropy coding
          • Arithmetic entropy coding
          • Compression from/decompression to planar YUV images
          • Decompression scaling -
          • Lossless transformations +
          • Lossless transformation +
          +
          +
          See Also:
          +
          PARAM_LOSSLESSPSV, +PARAM_LOSSLESSPT, +Constant Field Values
          +
          +
        • +
        + + + + + + + +
          +
        • +

          PARAM_LOSSLESSPT

          +
          public static final int PARAM_LOSSLESSPT
          +
          Lossless JPEG point transform (Pt) + +

          Value +

            +
          • 0 through precision - 1, where + precision is the JPEG data precision in bits [default for + compression: 0] +
          + +

          A point transform value of 0 is necessary in order to + generate a fully lossless JPEG image. (A non-zero point transform value + right-shifts the input samples by the specified number of bits, which is + effectively a form of lossy color quantization.)

          +
          +
          See Also:
          +
          PARAM_LOSSLESS, +PARAM_PRECISION, +Constant Field Values
          +
          +
        • +
        + + + +
          +
        • +

          PARAM_RESTARTBLOCKS

          +
          public static final int PARAM_RESTARTBLOCKS
          +
          JPEG restart marker interval in MCU blocks (lossy) or samples (lossless) + [compression only] + +

          The nature of entropy coding is such that a corrupt JPEG image cannot + be decompressed beyond the point of corruption unless it contains restart + markers. A restart marker stops and restarts the entropy coding algorithm + so that, if a JPEG image is corrupted, decompression can resume at the + next marker. Thus, adding more restart markers improves the fault + tolerance of the JPEG image, but adding too many restart markers can + adversely affect the compression ratio and performance. + +

          Value +

            +
          • the number of MCU blocks or samples between each restart marker + [default: 0 (no restart markers)] +
          + +

          Setting this parameter to a non-zero value sets + PARAM_RESTARTROWS to 0.

          +
          +
          See Also:
          +
          Constant Field Values
          +
          +
        • +
        + + + +
          +
        • +

          PARAM_RESTARTROWS

          +
          public static final int PARAM_RESTARTROWS
          +
          JPEG restart marker interval in MCU rows (lossy) or sample rows (lossless) + [compression only] + +

          See PARAM_RESTARTBLOCKS for a description of restart markers. + +

          Value +

            +
          • the number of MCU rows or sample rows between each restart marker + [default: 0 (no restart markers)] +
          + +

          Setting this parameter to a non-zero value sets + PARAM_RESTARTBLOCKS to 0.

          +
          +
          See Also:
          +
          Constant Field Values
          +
          +
        • +
        + + + +
          +
        • +

          PARAM_XDENSITY

          +
          public static final int PARAM_XDENSITY
          +
          JPEG horizontal pixel density + +

          Value +

            +
          • The JPEG image has (decompression) or will have (compression) the + specified horizontal pixel density [default for compression: + 1]. +
          + +

          This value is stored in or read from the JPEG header. It does not + affect the contents of the JPEG image.

          +
          +
          See Also:
          +
          PARAM_DENSITYUNITS, +Constant Field Values
          +
          +
        • +
        + + + +
          +
        • +

          PARAM_YDENSITY

          +
          public static final int PARAM_YDENSITY
          +
          JPEG vertical pixel density + +

          Value +

            +
          • The JPEG image has (decompression) or will have (compression) the + specified vertical pixel density [default for compression: + 1]. +
          + +

          This value is stored in or read from the JPEG header. It does not + affect the contents of the JPEG image.

          +
          +
          See Also:
          +
          PARAM_DENSITYUNITS, +Constant Field Values
          +
        - + + + +
          +
        • +

          PARAM_DENSITYUNITS

          +
          public static final int PARAM_DENSITYUNITS
          +
          JPEG pixel density units + +

          Value +

            +
          • 0 [default for compression] The pixel density of + the JPEG image is expressed (decompression) or will be expressed + (compression) in unknown units. +
          • 1 The pixel density of the JPEG image is expressed + (decompression) or will be expressed (compression) in units of + pixels/inch. +
          • 2 The pixel density of the JPEG image is expressed + (decompression) or will be expressed (compression) in units of pixels/cm. +
          + +

          This value is stored in or read from the JPEG header. It does not + affect the contents of the JPEG image.

          +
          +
          See Also:
          +
          PARAM_XDENSITY, +PARAM_YDENSITY, +Constant Field Values
          +
          +
        • +
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + -
          + + + + + + + + +
            +
          • +

            UNCROPPED

            +
            public static final java.awt.Rectangle UNCROPPED
            +
            A java.awt.Rectangle instance that specifies no cropping
            +
          • +
          + +
        +
      +
        -
      • +
      • Method Detail

        - +
        • getMCUWidth

          -
          public static int getMCUWidth(int subsamp)
          +
          public static int getMCUWidth​(int subsamp)
          Returns the MCU block width for the given level of chrominance subsampling.
          -
          Parameters:
          subsamp - the level of chrominance subsampling (one of - SAMP_*)
          -
          Returns:
          the MCU block width for the given level of chrominance - subsampling.
          +
          +
          Parameters:
          +
          subsamp - the level of chrominance subsampling (one of + SAMP_*)
          +
          Returns:
          +
          the MCU block width for the given level of chrominance + subsampling.
          +
        - +
        • getMCUHeight

          -
          public static int getMCUHeight(int subsamp)
          +
          public static int getMCUHeight​(int subsamp)
          Returns the MCU block height for the given level of chrominance subsampling.
          -
          Parameters:
          subsamp - the level of chrominance subsampling (one of - SAMP_*)
          -
          Returns:
          the MCU block height for the given level of chrominance - subsampling.
          +
          +
          Parameters:
          +
          subsamp - the level of chrominance subsampling (one of + SAMP_*)
          +
          Returns:
          +
          the MCU block height for the given level of chrominance + subsampling.
          +
        - +
        • getPixelSize

          -
          public static int getPixelSize(int pixelFormat)
          -
          Returns the pixel size (in bytes) for the given pixel format.
          -
          Parameters:
          pixelFormat - the pixel format (one of PF_*)
          -
          Returns:
          the pixel size (in bytes) for the given pixel format.
          +
          public static int getPixelSize​(int pixelFormat)
          +
          Returns the pixel size (in samples) for the given pixel format.
          +
          +
          Parameters:
          +
          pixelFormat - the pixel format (one of PF_*)
          +
          Returns:
          +
          the pixel size (in samples) for the given pixel format.
          +
        - +
        • getRedOffset

          -
          public static int getRedOffset(int pixelFormat)
          -
          For the given pixel format, returns the number of bytes that the red - component is offset from the start of the pixel. For instance, if a pixel - of format TJ.PF_BGRX is stored in char pixel[], - then the red component will be +
          public static int getRedOffset​(int pixelFormat)
          +
          For the given pixel format, returns the number of samples that the red + component is offset from the start of the pixel. For instance, if an + 8-bit-per-sample pixel of format TJ.PF_BGRX is stored in + char pixel[], then the red component will be pixel[TJ.getRedOffset(TJ.PF_BGRX)].
          -
          Parameters:
          pixelFormat - the pixel format (one of PF_*)
          -
          Returns:
          the red offset for the given pixel format, or -1 if the pixel - format does not have a red component.
          +
          +
          Parameters:
          +
          pixelFormat - the pixel format (one of PF_*)
          +
          Returns:
          +
          the red offset for the given pixel format, or -1 if the pixel + format does not have a red component.
          +
        - +
        • getGreenOffset

          -
          public static int getGreenOffset(int pixelFormat)
          -
          For the given pixel format, returns the number of bytes that the green - component is offset from the start of the pixel. For instance, if a pixel - of format TJ.PF_BGRX is stored in char pixel[], - then the green component will be +
          public static int getGreenOffset​(int pixelFormat)
          +
          For the given pixel format, returns the number of samples that the green + component is offset from the start of the pixel. For instance, if an + 8-bit-per-sample pixel of format TJ.PF_BGRX is stored in + char pixel[], then the green component will be pixel[TJ.getGreenOffset(TJ.PF_BGRX)].
          -
          Parameters:
          pixelFormat - the pixel format (one of PF_*)
          -
          Returns:
          the green offset for the given pixel format, or -1 if the pixel - format does not have a green component.
          +
          +
          Parameters:
          +
          pixelFormat - the pixel format (one of PF_*)
          +
          Returns:
          +
          the green offset for the given pixel format, or -1 if the pixel + format does not have a green component.
          +
        - +
        • getBlueOffset

          -
          public static int getBlueOffset(int pixelFormat)
          -
          For the given pixel format, returns the number of bytes that the blue - component is offset from the start of the pixel. For instance, if a pixel - of format TJ.PF_BGRX is stored in char pixel[], - then the blue component will be +
          public static int getBlueOffset​(int pixelFormat)
          +
          For the given pixel format, returns the number of samples that the blue + component is offset from the start of the pixel. For instance, if an + 8-bit-per-sample pixel of format TJ.PF_BGRX is stored in + char pixel[], then the blue component will be pixel[TJ.getBlueOffset(TJ.PF_BGRX)].
          -
          Parameters:
          pixelFormat - the pixel format (one of PF_*)
          -
          Returns:
          the blue offset for the given pixel format, or -1 if the pixel - format does not have a blue component.
          +
          +
          Parameters:
          +
          pixelFormat - the pixel format (one of PF_*)
          +
          Returns:
          +
          the blue offset for the given pixel format, or -1 if the pixel + format does not have a blue component.
          +
        - +
        • getAlphaOffset

          -
          public static int getAlphaOffset(int pixelFormat)
          -
          For the given pixel format, returns the number of bytes that the alpha - component is offset from the start of the pixel. For instance, if a pixel - of format TJ.PF_BGRA is stored in char pixel[], - then the alpha component will be +
          public static int getAlphaOffset​(int pixelFormat)
          +
          For the given pixel format, returns the number of samples that the alpha + component is offset from the start of the pixel. For instance, if an + 8-bit-per-sample pixel of format TJ.PF_BGRA is stored in + char pixel[], then the alpha component will be pixel[TJ.getAlphaOffset(TJ.PF_BGRA)].
          -
          Parameters:
          pixelFormat - the pixel format (one of PF_*)
          -
          Returns:
          the alpha offset for the given pixel format, or -1 if the pixel - format does not have a alpha component.
          +
          +
          Parameters:
          +
          pixelFormat - the pixel format (one of PF_*)
          +
          Returns:
          +
          the alpha offset for the given pixel format, or -1 if the pixel + format does not have a alpha component.
          +
        - +
        • bufSize

          -
          public static int bufSize(int width,
          -          int height,
          -          int jpegSubsamp)
          +
          public static int bufSize​(int width,
          +                          int height,
          +                          int jpegSubsamp)
          Returns the maximum size of the buffer (in bytes) required to hold a JPEG image with the given width, height, and level of chrominance subsampling.
          -
          Parameters:
          width - the width (in pixels) of the JPEG image
          height - the height (in pixels) of the JPEG image
          jpegSubsamp - the level of chrominance subsampling to be used when - generating the JPEG image (one of TJ.SAMP_*)
          -
          Returns:
          the maximum size of the buffer (in bytes) required to hold a JPEG - image with the given width, height, and level of chrominance subsampling.
          +
          +
          Parameters:
          +
          width - the width (in pixels) of the JPEG image
          +
          height - the height (in pixels) of the JPEG image
          +
          jpegSubsamp - the level of chrominance subsampling to be used when + generating the JPEG image (one of TJ.SAMP_*.) + SAMP_UNKNOWN is treated like SAMP_444, since a buffer + large enough to hold a JPEG image with no subsampling should also be large + enough to hold a JPEG image with an arbitrary level of subsampling. Note + that lossless JPEG images always use SAMP_444.
          +
          Returns:
          +
          the maximum size of the buffer (in bytes) required to hold a JPEG + image with the given width, height, and level of chrominance subsampling.
          +
        - +
        • bufSizeYUV

          -
          public static int bufSizeYUV(int width,
          -             int align,
          -             int height,
          -             int subsamp)
          +
          public static int bufSizeYUV​(int width,
          +                             int align,
          +                             int height,
          +                             int subsamp)
          Returns the size of the buffer (in bytes) required to hold a unified planar YUV image with the given width, height, and level of chrominance subsampling.
          -
          Parameters:
          width - the width (in pixels) of the YUV image
          align - row alignment (in bytes) of the YUV image (must be a power of +
          +
          Parameters:
          +
          width - the width (in pixels) of the YUV image
          +
          align - row alignment (in bytes) of the YUV image (must be a power of 2.) Setting this parameter to n specifies that each row in each plane of the YUV image will be padded to the nearest multiple of n bytes - (1 = unpadded.)
          height - the height (in pixels) of the YUV image
          subsamp - the level of chrominance subsampling used in the YUV - image (one of TJ.SAMP_*)
          -
          Returns:
          the size of the buffer (in bytes) required to hold a unified + (1 = unpadded.)
          +
          height - the height (in pixels) of the YUV image
          +
          subsamp - the level of chrominance subsampling used in the YUV + image (one of TJ.SAMP_*)
          +
          Returns:
          +
          the size of the buffer (in bytes) required to hold a unified planar YUV image with the given width, height, and level of chrominance - subsampling.
          + subsampling.
          +
        - +
        • planeSizeYUV

          -
          public static int planeSizeYUV(int componentID,
          -               int width,
          -               int stride,
          -               int height,
          -               int subsamp)
          +
          public static int planeSizeYUV​(int componentID,
          +                               int width,
          +                               int stride,
          +                               int height,
          +                               int subsamp)
          Returns the size of the buffer (in bytes) required to hold a YUV image plane with the given parameters.
          -
          Parameters:
          componentID - ID number of the image plane (0 = Y, 1 = U/Cb, - 2 = V/Cr)
          width - width (in pixels) of the YUV image. NOTE: this is the width - of the whole image, not the plane width.
          stride - bytes per row in the image plane.
          height - height (in pixels) of the YUV image. NOTE: this is the - height of the whole image, not the plane height.
          subsamp - the level of chrominance subsampling used in the YUV - image (one of TJ.SAMP_*)
          -
          Returns:
          the size of the buffer (in bytes) required to hold a YUV image - plane with the given parameters.
          +
          +
          Parameters:
          +
          componentID - ID number of the image plane (0 = Y, 1 = U/Cb, + 2 = V/Cr)
          +
          width - width (in pixels) of the YUV image. NOTE: this is the width + of the whole image, not the plane width.
          +
          stride - bytes per row in the image plane.
          +
          height - height (in pixels) of the YUV image. NOTE: this is the + height of the whole image, not the plane height.
          +
          subsamp - the level of chrominance subsampling used in the YUV + image (one of TJ.SAMP_*)
          +
          Returns:
          +
          the size of the buffer (in bytes) required to hold a YUV image + plane with the given parameters.
          +
        - +
        • planeWidth

          -
          public static int planeWidth(int componentID,
          -             int width,
          -             int subsamp)
          +
          public static int planeWidth​(int componentID,
          +                             int width,
          +                             int subsamp)
          Returns the plane width of a YUV image plane with the given parameters. - Refer to YUVImage for a description of plane width.
          -
          Parameters:
          componentID - ID number of the image plane (0 = Y, 1 = U/Cb, - 2 = V/Cr)
          width - width (in pixels) of the YUV image
          subsamp - the level of chrominance subsampling used in the YUV image - (one of TJ.SAMP_*)
          -
          Returns:
          the plane width of a YUV image plane with the given parameters.
          + Refer to YUVImage for a description of plane width. +
          +
          Parameters:
          +
          componentID - ID number of the image plane (0 = Y, 1 = U/Cb, + 2 = V/Cr)
          +
          width - width (in pixels) of the YUV image
          +
          subsamp - the level of chrominance subsampling used in the YUV image + (one of TJ.SAMP_*)
          +
          Returns:
          +
          the plane width of a YUV image plane with the given parameters.
          +
        - +
        • planeHeight

          -
          public static int planeHeight(int componentID,
          -              int height,
          -              int subsamp)
          +
          public static int planeHeight​(int componentID,
          +                              int height,
          +                              int subsamp)
          Returns the plane height of a YUV image plane with the given parameters. - Refer to YUVImage for a description of plane height.
          -
          Parameters:
          componentID - ID number of the image plane (0 = Y, 1 = U/Cb, - 2 = V/Cr)
          height - height (in pixels) of the YUV image
          subsamp - the level of chrominance subsampling used in the YUV image - (one of TJ.SAMP_*)
          -
          Returns:
          the plane height of a YUV image plane with the given parameters.
          + Refer to YUVImage for a description of plane height. +
          +
          Parameters:
          +
          componentID - ID number of the image plane (0 = Y, 1 = U/Cb, + 2 = V/Cr)
          +
          height - height (in pixels) of the YUV image
          +
          subsamp - the level of chrominance subsampling used in the YUV image + (one of TJ.SAMP_*)
          +
          Returns:
          +
          the plane height of a YUV image plane with the given parameters.
          +
        - +
        • getScalingFactors

          -
          public static TJScalingFactor[] getScalingFactors()
          +
          public static TJScalingFactor[] getScalingFactors()
          Returns a list of fractional scaling factors that the JPEG decompressor supports.
          -
          Returns:
          a list of fractional scaling factors that the JPEG decompressor - supports.
          +
          +
          Returns:
          +
          a list of fractional scaling factors that the JPEG decompressor + supports.
          +
      +
    +
    + diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html b/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html index 69b7a3e52..a6e6dfcbd 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html @@ -1,9 +1,21 @@ - + + TJCompressor + + + + + + + + + +var data = {"i0":10,"i1":10,"i2":10,"i3":42,"i4":42,"i5":10,"i6":10,"i7":42,"i8":42,"i9":10,"i10":42,"i11":10,"i12":10,"i13":10,"i14":10,"i15":42,"i16":10,"i17":10,"i18":10,"i19":10,"i20":10,"i21":42}; +var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],8:["t4","Concrete Methods"],32:["t6","Deprecated Methods"]}; +var altColor = "altColor"; +var rowColor = "rowColor"; +var tableTab = "tableTab"; +var activeTableTab = "activeTableTab"; +var pathtoroot = "../../../"; +var useModuleDirectories = true; +loadScripts(document, 'script'); +
    + +
    +
    -
    org.libjpegturbo.turbojpeg
    +

    Class TJCompressor

    @@ -96,11 +131,10 @@

    Class TJCompressor

  • All Implemented Interfaces:
    -
    java.io.Closeable, java.lang.AutoCloseable
    +
    java.io.Closeable, java.lang.AutoCloseable

    -
    -
    public class TJCompressor
    +
    public class TJCompressor
     extends java.lang.Object
     implements java.io.Closeable
    TurboJPEG compressor
    @@ -111,166 +145,283 @@

    Class TJCompressor

    • +
        -
      • +
      • Constructor Summary

        - +
        - + + - + - + - +
        Constructors 
        Constructor and DescriptionConstructorDescription
        TJCompressor() +TJCompressor()
        Create a TurboJPEG compressor instance.
        TJCompressor(java.awt.image.BufferedImage srcImage, +TJCompressor​(byte[] srcImage, int x, int y, int width, - int height) -
        Create a TurboJPEG compressor instance and associate the packed-pixel - source image stored in srcImage with the newly created - instance.
        + int pitch, + int height, + int pixelFormat)
        +
        Create a TurboJPEG compressor instance and associate the 8-bit-per-sample + packed-pixel source image stored in srcImage with the newly + created instance.
        TJCompressor(byte[] srcImage, +TJCompressor​(java.awt.image.BufferedImage srcImage, int x, int y, int width, - int pitch, - int height, - int pixelFormat) -
        Create a TurboJPEG compressor instance and associate the packed-pixel - source image stored in srcImage with the newly created - instance.
        + int height)
        +
        Create a TurboJPEG compressor instance and associate the 8-bit-per-sample + packed-pixel source image stored in srcImage with the newly + created instance.
      +
      +
        -
      • +
      • Method Summary

        - - +
        Methods 
        + - + + - + - + - + + + + + + - + - + + + + + + - + - - - + + + - - - + + + - + + + + + + + + + + + - + - + + + + + + - + + - + + + + + + - + - + - + - + - + + + + + + - + - + - + - + - + - + + + + + + + + + + +
        All Methods Instance Methods Concrete Methods Deprecated Methods 
        Modifier and TypeMethod and DescriptionMethodDescription
        voidclose() +close()
        Free the native structures associated with this compressor instance.
        byte[]compress() +
        Compress the packed-pixel or planar YUV source image associated with this + compressor instance and return a buffer containing a JPEG image.
        +
        voidcompress(byte[] dstBuf, - int flags) +compress​(byte[] dstBuf)
        Compress the packed-pixel or planar YUV source image associated with this compressor instance and output a JPEG image to the given destination buffer.
        voidcompress​(byte[] dstBuf, + int flags) +
        Deprecated. +
        Use set() and compress(byte[]) instead.
        +
        +
        byte[]compress(int flags) -
        Compress the packed-pixel or planar YUV source image associated with this - compressor instance and return a buffer containing a JPEG image.
        +
        compress​(int flags) +
        Deprecated. +
        Use set() and compress() instead.
        +
        YUVImageencodeYUV(int[] strides, - int flags) -
        Encode the packed-pixel source image associated with this compressor - instance into separate Y, U (Cb), and V (Cr) image planes and return a - YUVImage instance containing the encoded image planes.
        +
        YUVImageencodeYUV​(int align) +
        Encode the 8-bit-per-sample packed-pixel source image associated with this + compressor instance into an 8-bit-per-sample unified planar YUV image and + return a YUVImage instance containing the encoded image.
        YUVImageencodeYUV(int align, - int flags) -
        Encode the packed-pixel source image associated with this compressor - instance into a unified planar YUV image and return a YUVImage - instance containing the encoded image.
        +
        YUVImageencodeYUV​(int[] strides) +
        Encode the 8-bit-per-sample packed-pixel source image associated with this + compressor instance into separate 8-bit-per-sample Y, U (Cb), and V (Cr) + image planes and return a YUVImage instance containing the encoded + image planes.
        YUVImageencodeYUV​(int[] strides, + int flags) +
        Deprecated. +
        Use set() and encodeYUV(int[]) instead.
        +
        +
        YUVImageencodeYUV​(int align, + int flags) +
        Deprecated. +
        Use set() and encodeYUV(int) instead.
        +
        +
        voidencodeYUV(YUVImage dstImage, - int flags) -
        Encode the packed-pixel source image associated with this compressor - instance into a planar YUV image and store it in the given - YUVImage instance.
        +
        encodeYUV​(YUVImage dstImage) +
        Encode the 8-bit-per-sample packed-pixel source image associated with this + compressor instance into an 8-bit-per-sample planar YUV image and store it + in the given YUVImage instance.
        voidencodeYUV​(YUVImage dstImage, + int flags) +
        Deprecated. +
        Use set() and encodeYUV(YUVImage) + instead.
        +
        +
        protected voidfinalize() finalize() 
        intget​(int param) +
        Get the value of a compression parameter.
        +
        intgetCompressedSize() +getCompressedSize()
        Returns the size of the image (in bytes) generated by the most recent compress operation.
        voidsetJPEGQuality(int quality) -
        Set the JPEG image quality level for subsequent compress operations.
        +
        set​(int param, + int value) +
        Set the value of a compression parameter.
        voidsetSourceImage(java.awt.image.BufferedImage srcImage, +setJPEGQuality​(int quality) +
        Deprecated. +
        Use + set(TJ.PARAM_QUALITY, ...) instead.
        +
        +
        voidsetSourceImage​(byte[] srcImage, int x, int y, int width, - int height) -
        Associate a packed-pixel RGB or grayscale source image with this - compressor instance.
        + int pitch, + int height, + int pixelFormat)
        +
        Associate an 8-bit-per-sample packed-pixel RGB, grayscale, or CMYK source + image with this compressor instance.
        voidsetSourceImage(byte[] srcImage, +setSourceImage​(java.awt.image.BufferedImage srcImage, int x, int y, int width, - int pitch, - int height, - int pixelFormat) -
        Associate a packed-pixel RGB, grayscale, or CMYK source image with this - compressor instance.
        + int height)
        +
        Associate an 8-bit-per-pixel packed-pixel RGB or grayscale source image + with this compressor instance.
        voidsetSourceImage(YUVImage srcImage) -
        Associate a planar YUV source image with this compressor instance.
        +
        setSourceImage​(YUVImage srcImage) +
        Associate an 8-bit-per-sample planar YUV source image with this compressor + instance.
        voidsetSubsamp(int newSubsamp) -
        Set the level of chrominance subsampling for subsequent compress/encode - operations.
        +
        setSourceImage12​(short[] srcImage, + int x, + int y, + int width, + int pitch, + int height, + int pixelFormat) +
        Associate a 12-bit-per-sample packed-pixel RGB, grayscale, or CMYK source + image with this compressor instance.
        +
        voidsetSourceImage16​(short[] srcImage, + int x, + int y, + int width, + int pitch, + int height, + int pixelFormat) +
        Associate a 16-bit-per-sample packed-pixel RGB, grayscale, or CMYK source + image with this compressor instance.
        +
        voidsetSubsamp​(int subsamp) +
        Deprecated. +
        Use + set(TJ.PARAM_SUBSAMP, ...) instead.
        +
          -
        • +
        • Methods inherited from class java.lang.Object

          @@ -278,6 +429,7 @@

          Methods inherited from class java.lang.Object

      +
  • @@ -285,380 +437,643 @@

    Methods inherited from class java.lang.Object

    • +
      +
      +
        -
      • +
      • Method Detail

        - +
        • setSourceImage

          -
          public void setSourceImage(byte[] srcImage,
          -                  int x,
          -                  int y,
          -                  int width,
          -                  int pitch,
          -                  int height,
          -                  int pixelFormat)
          -                    throws TJException
          -
          Associate a packed-pixel RGB, grayscale, or CMYK source image with this - compressor instance.
          -
          Parameters:
          srcImage - buffer containing a packed-pixel RGB, grayscale, or CMYK - source image to be compressed or encoded. This buffer is not modified.
          x - x offset (in pixels) of the region in the source image from which - the JPEG or YUV image should be compressed/encoded
          y - y offset (in pixels) of the region in the source image from which - the JPEG or YUV image should be compressed/encoded
          width - width (in pixels) of the region in the source image from - which the JPEG or YUV image should be compressed/encoded
          pitch - bytes per row in the source image. Normally this should be - width * - TJ.getPixelSize(pixelFormat), - if the source image is unpadded. However, you can use this parameter to, - for instance, specify that the rows in the source image are padded to the - nearest multiple of 4 bytes or to compress/encode a JPEG or YUV image from - a region of a larger source image. You can also be clever and use this - parameter to skip rows, etc. Setting this parameter to 0 is the +
          public void setSourceImage​(byte[] srcImage,
          +                           int x,
          +                           int y,
          +                           int width,
          +                           int pitch,
          +                           int height,
          +                           int pixelFormat)
          +                    throws TJException
          +
          Associate an 8-bit-per-sample packed-pixel RGB, grayscale, or CMYK source + image with this compressor instance.
          +
          +
          Parameters:
          +
          srcImage - buffer containing a packed-pixel RGB, grayscale, or CMYK + source image to be compressed or encoded. This buffer is not modified.
          +
          x - x offset (in pixels) of the region in the source image from which + the JPEG or YUV image should be compressed/encoded
          +
          y - y offset (in pixels) of the region in the source image from which + the JPEG or YUV image should be compressed/encoded
          +
          width - width (in pixels) of the region in the source image from + which the JPEG or YUV image should be compressed/encoded
          +
          pitch - bytes per row in the source image. Normally this should be + width * TJ.getPixelSize(pixelFormat), + if the source image is unpadded. (Setting this parameter to 0 is the + equivalent of setting it to width * + TJ.getPixelSize(pixelFormat).) However, + you can also use this parameter to specify the row alignment/padding of + the source image, to skip rows, or to compress/encode a JPEG or YUV image + from a specific region of a larger source image.
          +
          height - height (in pixels) of the region in the source image from + which the JPEG or YUV image should be compressed/encoded
          +
          pixelFormat - pixel format of the source image (one of + TJ.PF_*)
          +
          Throws:
          +
          TJException
          +
          +
        • +
        + + + +
          +
        • +

          setSourceImage12

          +
          public void setSourceImage12​(short[] srcImage,
          +                             int x,
          +                             int y,
          +                             int width,
          +                             int pitch,
          +                             int height,
          +                             int pixelFormat)
          +                      throws TJException
          +
          Associate a 12-bit-per-sample packed-pixel RGB, grayscale, or CMYK source + image with this compressor instance. Note that 12-bit-per-sample + packed-pixel source images can only be compressed into 12-bit-per-sample + JPEG images.
          +
          +
          Parameters:
          +
          srcImage - buffer containing a packed-pixel RGB, grayscale, or CMYK + source image to be compressed. This buffer is not modified.
          +
          x - x offset (in pixels) of the region in the source image from which + the JPEG image should be compressed
          +
          y - y offset (in pixels) of the region in the source image from which + the JPEG image should be compressed
          +
          width - width (in pixels) of the region in the source image from + which the JPEG image should be compressed
          +
          pitch - samples per row in the source image. Normally this should be + width * TJ.getPixelSize(pixelFormat), + if the source image is unpadded. (Setting this parameter to 0 is the equivalent of setting it to width * - TJ.getPixelSize(pixelFormat).
          height - height (in pixels) of the region in the source image from - which the JPEG or YUV image should be compressed/encoded
          pixelFormat - pixel format of the source image (one of - TJ.PF_*)
          -
          Throws:
          -
          TJException
          + TJ.getPixelSize(pixelFormat).) However, + you can also use this parameter to specify the row alignment/padding of + the source image, to skip rows, or to compress a JPEG image from a + specific region of a larger source image. +
          height - height (in pixels) of the region in the source image from + which the JPEG image should be compressed
          +
          pixelFormat - pixel format of the source image (one of + TJ.PF_*)
          +
          Throws:
          +
          TJException
          +
        - + + + +
          +
        • +

          setSourceImage16

          +
          public void setSourceImage16​(short[] srcImage,
          +                             int x,
          +                             int y,
          +                             int width,
          +                             int pitch,
          +                             int height,
          +                             int pixelFormat)
          +                      throws TJException
          +
          Associate a 16-bit-per-sample packed-pixel RGB, grayscale, or CMYK source + image with this compressor instance. Note that 16-bit-per-sample + packed-pixel source images can only be compressed into 16-bit-per-sample + lossless JPEG images.
          +
          +
          Parameters:
          +
          srcImage - buffer containing a packed-pixel RGB, grayscale, or CMYK + source image to be compressed. This buffer is not modified.
          +
          x - x offset (in pixels) of the region in the source image from which + the JPEG image should be compressed
          +
          y - y offset (in pixels) of the region in the source image from which + the JPEG image should be compressed
          +
          width - width (in pixels) of the region in the source image from + which the JPEG image should be compressed
          +
          pitch - samples per row in the source image. Normally this should be + width * TJ.getPixelSize(pixelFormat), + if the source image is unpadded. (Setting this parameter to 0 is the + equivalent of setting it to width * + TJ.getPixelSize(pixelFormat).) However, + you can also use this parameter to specify the row alignment/padding of + the source image, to skip rows, or to compress a JPEG image from a + specific region of a larger source image.
          +
          height - height (in pixels) of the region in the source image from + which the JPEG image should be compressed
          +
          pixelFormat - pixel format of the source image (one of + TJ.PF_*)
          +
          Throws:
          +
          TJException
          +
          +
        • +
        +
        • setSourceImage

          -
          public void setSourceImage(java.awt.image.BufferedImage srcImage,
          -                  int x,
          -                  int y,
          -                  int width,
          -                  int height)
          -                    throws TJException
          -
          Associate a packed-pixel RGB or grayscale source image with this - compressor instance.
          -
          Parameters:
          srcImage - a BufferedImage instance containing a +
          public void setSourceImage​(java.awt.image.BufferedImage srcImage,
          +                           int x,
          +                           int y,
          +                           int width,
          +                           int height)
          +                    throws TJException
          +
          Associate an 8-bit-per-pixel packed-pixel RGB or grayscale source image + with this compressor instance.
          +
          +
          Parameters:
          +
          srcImage - a BufferedImage instance containing a packed-pixel RGB or grayscale source image to be compressed or encoded. - This image is not modified.
          x - x offset (in pixels) of the region in the source image from which - the JPEG or YUV image should be compressed/encoded
          y - y offset (in pixels) of the region in the source image from which - the JPEG or YUV image should be compressed/encoded
          width - width (in pixels) of the region in the source image from + This image is not modified.
          +
          x - x offset (in pixels) of the region in the source image from which + the JPEG or YUV image should be compressed/encoded
          +
          y - y offset (in pixels) of the region in the source image from which + the JPEG or YUV image should be compressed/encoded
          +
          width - width (in pixels) of the region in the source image from which the JPEG or YUV image should be compressed/encoded (0 = use the - width of the source image)
          height - height (in pixels) of the region in the source image from + width of the source image)
          +
          height - height (in pixels) of the region in the source image from which the JPEG or YUV image should be compressed/encoded (0 = use the height of the source image)
          -
          Throws:
          -
          TJException
          +
          Throws:
          +
          TJException
          +
        - +
        • setSourceImage

          -
          public void setSourceImage(YUVImage srcImage)
          -                    throws TJException
          -
          Associate a planar YUV source image with this compressor instance.
          -
          Parameters:
          srcImage - planar YUV source image to be compressed. This image is +
          public void setSourceImage​(YUVImage srcImage)
          +                    throws TJException
          +
          Associate an 8-bit-per-sample planar YUV source image with this compressor + instance. This method sets TJ.PARAM_SUBSAMP to the chrominance + subsampling level of the source image.
          +
          +
          Parameters:
          +
          srcImage - planar YUV source image to be compressed. This image is not modified.
          -
          Throws:
          -
          TJException
          +
          Throws:
          +
          TJException
          +
        - + + + +
          +
        • +

          set

          +
          public void set​(int param,
          +                int value)
          +
          Set the value of a compression parameter.
          +
          +
          Parameters:
          +
          param - one of TJ.PARAM_*
          +
          value - value of the compression parameter (refer to + parameter documentation)
          +
          +
        • +
        + + + +
          +
        • +

          get

          +
          public int get​(int param)
          +
          Get the value of a compression parameter.
          +
          +
          Parameters:
          +
          param - one of TJ.PARAM_*
          +
          Returns:
          +
          the value of the specified compression parameter, or -1 if the + value is unknown.
          +
          +
        • +
        +
        • setSubsamp

          -
          public void setSubsamp(int newSubsamp)
          -
          Set the level of chrominance subsampling for subsequent compress/encode - operations. When pixels are converted from RGB to YCbCr (see - TJ.CS_YCbCr) or from CMYK to YCCK (see TJ.CS_YCCK) as part - of the JPEG compression process, some of the Cb and Cr (chrominance) - components can be discarded or averaged together to produce a smaller - image with little perceptible loss of image clarity. (The human eye is - more sensitive to small changes in brightness than to small changes in - color.) This is called "chrominance subsampling". -

          - NOTE: This method has no effect when compressing a JPEG image from a - planar YUV source image. In that case, the level of chrominance - subsampling in the JPEG image is determined by the source image. - Furthermore, this method has no effect when encoding to a pre-allocated - YUVImage instance. In that case, the level of chrominance - subsampling is determined by the destination image.

          -
          Parameters:
          newSubsamp - the level of chrominance subsampling to use in - subsequent compress/encode oeprations (one of - TJ.SAMP_*)
          -
        • -
        - +
        @Deprecated
        +public void setSubsamp​(int subsamp)
        +
        +
      • +
      +
      • setJPEGQuality

        -
        public void setJPEGQuality(int quality)
        -
        Set the JPEG image quality level for subsequent compress operations.
        -
        Parameters:
        quality - the new JPEG image quality level (1 to 100, 1 = worst, - 100 = best.) When generating a lossless JPEG image (see - TJ.FLAG_LOSSLESS), quality is - psv * 10 + Pt, where psv is the predictor - selection value (1-7) and Pt is the point transform (0-7). A - point transform value of 0 is necessary in order to create a fully - lossless JPEG image. (A non-zero point transform value right-shifts the - input samples by the specified number of bits, which is effectively a form - of lossy color quantization.)
        +
        @Deprecated
        +public void setJPEGQuality​(int quality)
        +
        Deprecated. +
        Use + set(TJ.PARAM_QUALITY, ...) instead.
        +
      - +
      • compress

        -
        public void compress(byte[] dstBuf,
        -            int flags)
        -              throws TJException
        +
        public void compress​(byte[] dstBuf)
        +              throws TJException
        Compress the packed-pixel or planar YUV source image associated with this compressor instance and output a JPEG image to the given destination buffer.
        -
        Parameters:
        dstBuf - buffer that will receive the JPEG image. Use - TJ.bufSize(int, int, int) to determine the maximum size for this buffer based on - the source image's width and height and the desired level of chrominance - subsampling.
        flags - the bitwise OR of one or more of - TJ.FLAG_*
        -
        Throws:
        -
        TJException
        +
        +
        Parameters:
        +
        dstBuf - buffer that will receive the JPEG image. Use + TJ.bufSize() to determine the maximum size for this + buffer based on the source image's width and height and the desired level + of chrominance subsampling (see TJ.PARAM_SUBSAMP.)
        +
        Throws:
        +
        TJException
        +
      - + + + + +
        +
      • +

        compress

        +
        public byte[] compress()
        +                throws TJException
        Compress the packed-pixel or planar YUV source image associated with this compressor instance and return a buffer containing a JPEG image.
        -
        Parameters:
        flags - the bitwise OR of one or more of - TJ.FLAG_*
        -
        Returns:
        a buffer containing a JPEG image. The length of this buffer will - not be equal to the size of the JPEG image. Use getCompressedSize() to obtain the size of the JPEG image.
        -
        Throws:
        -
        TJException
        +
        +
        Returns:
        +
        a buffer containing a JPEG image. The length of this buffer will + not be equal to the size of the JPEG image. Use + getCompressedSize() to obtain the size of the JPEG image.
        +
        Throws:
        +
        TJException
        +
      - +
      • -

        encodeYUV

        -
        public void encodeYUV(YUVImage dstImage,
        -             int flags)
        -               throws TJException
        -
        Encode the packed-pixel source image associated with this compressor - instance into a planar YUV image and store it in the given - YUVImage instance. This method performs color conversion (which - is accelerated in the libjpeg-turbo implementation) but does not execute - any of the other steps in the JPEG compression process. Encoding CMYK - source images into YUV images is not supported.
        -
        Parameters:
        dstImage - YUVImage instance that will receive the planar YUV - image
        flags - the bitwise OR of one or more of - TJ.FLAG_*
        -
        Throws:
        -
        TJException
        +

        compress

        +
        @Deprecated
        +public byte[] compress​(int flags)
        +                throws TJException
        +
        Deprecated. +
        Use set() and compress() instead.
        +
        +
        +
        Throws:
        +
        TJException
        +
      - +
      • encodeYUV

        -
        public YUVImage encodeYUV(int align,
        -                 int flags)
        -                   throws TJException
        -
        Encode the packed-pixel source image associated with this compressor - instance into a unified planar YUV image and return a YUVImage - instance containing the encoded image. This method performs color +
        public void encodeYUV​(YUVImage dstImage)
        +               throws TJException
        +
        Encode the 8-bit-per-sample packed-pixel source image associated with this + compressor instance into an 8-bit-per-sample planar YUV image and store it + in the given YUVImage instance. This method performs color conversion (which is accelerated in the libjpeg-turbo implementation) but does not execute any of the other steps in the JPEG compression process. - Encoding CMYK source images into YUV images is not supported.
        -
        Parameters:
        align - row alignment (in bytes) of the YUV image (must be a power of - 2.) Setting this parameter to n will cause each row in each plane of the - YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.)
        flags - the bitwise OR of one or more of - TJ.FLAG_*
        -
        Returns:
        a YUVImage instance containing the unified planar YUV - encoded image
        -
        Throws:
        -
        TJException
        + Encoding CMYK source images into YUV images is not supported. This method + sets TJ.PARAM_SUBSAMP to the chrominance subsampling level of the + destination image.
        +
        +
        Parameters:
        +
        dstImage - YUVImage instance that will receive the planar YUV + image
        +
        Throws:
        +
        TJException
        +
      - +
      • encodeYUV

        -
        public YUVImage encodeYUV(int[] strides,
        -                 int flags)
        -                   throws TJException
        -
        Encode the packed-pixel source image associated with this compressor - instance into separate Y, U (Cb), and V (Cr) image planes and return a - YUVImage instance containing the encoded image planes. This +
        @Deprecated
        +public void encodeYUV​(YUVImage dstImage,
        +                      int flags)
        +               throws TJException
        +
        Deprecated. +
        Use set() and encodeYUV(YUVImage) + instead.
        +
        +
        +
        Throws:
        +
        TJException
        +
        +
      • +
      + + + +
        +
      • +

        encodeYUV

        +
        public YUVImage encodeYUV​(int align)
        +                   throws TJException
        +
        Encode the 8-bit-per-sample packed-pixel source image associated with this + compressor instance into an 8-bit-per-sample unified planar YUV image and + return a YUVImage instance containing the encoded image. This method performs color conversion (which is accelerated in the libjpeg-turbo implementation) but does not execute any of the other steps in the JPEG compression process. Encoding CMYK source images into YUV images is not supported.
        -
        Parameters:
        strides - an array of integers, each specifying the number of bytes +
        +
        Parameters:
        +
        align - row alignment (in bytes) of the YUV image (must be a power of + 2.) Setting this parameter to n will cause each row in each plane of the + YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.)
        +
        Returns:
        +
        a YUVImage instance containing the unified planar YUV + encoded image
        +
        Throws:
        +
        TJException
        +
        +
      • +
      + + + + + + + +
        +
      • +

        encodeYUV

        +
        public YUVImage encodeYUV​(int[] strides)
        +                   throws TJException
        +
        Encode the 8-bit-per-sample packed-pixel source image associated with this + compressor instance into separate 8-bit-per-sample Y, U (Cb), and V (Cr) + image planes and return a YUVImage instance containing the encoded + image planes. This method performs color conversion (which is accelerated + in the libjpeg-turbo implementation) but does not execute any of the other + steps in the JPEG compression process. Encoding CMYK source images into + YUV images is not supported.
        +
        +
        Parameters:
        +
        strides - an array of integers, each specifying the number of bytes per row in the corresponding plane of the YUV source image. Setting the stride for any plane to 0 is the same as setting it to the plane width - (see YUVImage.) If strides is null, then the strides + (see YUVImage.) If strides is null, then the strides for all planes will be set to their respective plane widths. You can adjust the strides in order to add an arbitrary amount of row padding to - each plane.
        flags - the bitwise OR of one or more of - TJ.FLAG_*
        -
        Returns:
        a YUVImage instance containing the encoded image planes
        -
        Throws:
        -
        TJException
        + each plane. +
        Returns:
        +
        a YUVImage instance containing the encoded image planes
        +
        Throws:
        +
        TJException
        + +
      • +
      + + + + - +
      • getCompressedSize

        -
        public int getCompressedSize()
        +
        public int getCompressedSize()
        Returns the size of the image (in bytes) generated by the most recent compress operation.
        -
        Returns:
        the size of the image (in bytes) generated by the most recent - compress operation.
        +
        +
        Returns:
        +
        the size of the image (in bytes) generated by the most recent + compress operation.
        +
      - +
      • close

        -
        public void close()
        -           throws TJException
        +
        public void close()
        +           throws TJException
        Free the native structures associated with this compressor instance.
        -
        Specified by:
        -
        close in interface java.io.Closeable
        -
        Specified by:
        +
        Specified by:
        close in interface java.lang.AutoCloseable
        -
        Throws:
        -
        TJException
        +
        Specified by:
        +
        close in interface java.io.Closeable
        +
        Throws:
        +
        TJException
        +
      - +
      • finalize

        -
        protected void finalize()
        +
        protected void finalize()
                          throws java.lang.Throwable
        -
        Overrides:
        +
        Overrides:
        finalize in class java.lang.Object
        -
        Throws:
        -
        java.lang.Throwable
        +
        Throws:
        +
        java.lang.Throwable
        +
    + +
    + diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJCustomFilter.html b/java/doc/org/libjpegturbo/turbojpeg/TJCustomFilter.html index 982079c47..536f40046 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJCustomFilter.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJCustomFilter.html @@ -1,9 +1,21 @@ - + + TJCustomFilter + + + + + + + + + +var data = {"i0":6}; +var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],4:["t3","Abstract Methods"]}; +var altColor = "altColor"; +var rowColor = "rowColor"; +var tableTab = "tableTab"; +var activeTableTab = "activeTableTab"; +var pathtoroot = "../../../"; +var useModuleDirectories = true; +loadScripts(document, 'script'); +
    + +
    +
    -
    org.libjpegturbo.turbojpeg
    +

    Interface TJCustomFilter

    @@ -87,8 +122,7 @@

    Interface TJCustomFilter


    • -
      -
      public interface TJCustomFilter
      +
      public interface TJCustomFilter
      Custom filter callback interface
    @@ -97,25 +131,28 @@

    Interface TJCustomFilter

    • +
        -
      • +
      • Method Summary

        - - +
        Methods 
        + - + + - + - +
        All Methods Instance Methods Abstract Methods 
        Modifier and TypeMethod and DescriptionMethodDescription
        voidcustomFilter(java.nio.ShortBuffer coeffBuffer, +customFilter​(java.nio.ShortBuffer coeffBuffer, java.awt.Rectangle bufferRegion, java.awt.Rectangle planeRegion, int componentID, int transformID, - TJTransform transform) + TJTransform transform)
        A callback function that can be used to modify the DCT coefficients after they are losslessly transformed but before they are transcoded to a new JPEG image.
        @@ -124,6 +161,7 @@

        Method Summary

      +
    @@ -131,62 +169,78 @@

    Method Summary

    • +
        -
      • +
      • Method Detail

        - +
        • customFilter

          -
          void customFilter(java.nio.ShortBuffer coeffBuffer,
          -                java.awt.Rectangle bufferRegion,
          -                java.awt.Rectangle planeRegion,
          -                int componentID,
          -                int transformID,
          -                TJTransform transform)
          -                  throws TJException
          +
          void customFilter​(java.nio.ShortBuffer coeffBuffer,
          +                  java.awt.Rectangle bufferRegion,
          +                  java.awt.Rectangle planeRegion,
          +                  int componentID,
          +                  int transformID,
          +                  TJTransform transform)
          +           throws TJException
          A callback function that can be used to modify the DCT coefficients after they are losslessly transformed but before they are transcoded to a new JPEG image. This allows for custom filters or other transformations to be applied in the frequency domain.
          -
          Parameters:
          coeffBuffer - a buffer containing transformed DCT coefficients. +
          +
          Parameters:
          +
          coeffBuffer - a buffer containing transformed DCT coefficients. (NOTE: this buffer is not guaranteed to be valid once the callback returns, so applications wishing to hand off the DCT coefficients to another function or library should make a copy of them within the body of - the callback.)
          bufferRegion - rectangle containing the width and height of + the callback.)
          +
          bufferRegion - rectangle containing the width and height of coeffBuffer as well as its offset relative to the component plane. TurboJPEG implementations may choose to split each component plane into multiple DCT coefficient buffers and call the callback function once - for each buffer.
          planeRegion - rectangle containing the width and height of the - component plane to which coeffBuffer belongs
          componentID - ID number of the component plane to which + for each buffer.
          +
          planeRegion - rectangle containing the width and height of the + component plane to which coeffBuffer belongs
          +
          componentID - ID number of the component plane to which coeffBuffer belongs. (Y, Cb, and Cr have, respectively, ID's - of 0, 1, and 2 in typical JPEG images.)
          transformID - ID number of the transformed image to which + of 0, 1, and 2 in typical JPEG images.)
          +
          transformID - ID number of the transformed image to which coeffBuffer belongs. This is the same as the index of the - transform in the transforms array that was passed to TJTransformer.transform().
          transform - a TJTransform instance that specifies the + transform in the transforms array that was passed to + TJTransformer.transform().
          +
          transform - a TJTransform instance that specifies the parameters and/or cropping region for this transform
          -
          Throws:
          -
          TJException
          +
          Throws:
          +
          TJException
          +
      +
    +
    + diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html b/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html index 57b300d9c..06de168b3 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html @@ -1,9 +1,21 @@ - + + TJDecompressor + + + + + + + + + +var data = {"i0":10,"i1":42,"i2":42,"i3":42,"i4":42,"i5":42,"i6":10,"i7":10,"i8":10,"i9":10,"i10":10,"i11":10,"i12":10,"i13":10,"i14":10,"i15":10,"i16":10,"i17":42,"i18":42,"i19":10,"i20":42,"i21":10,"i22":10,"i23":42,"i24":10,"i25":10,"i26":10,"i27":42,"i28":42,"i29":42,"i30":10,"i31":10,"i32":10,"i33":10,"i34":10,"i35":10}; +var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],8:["t4","Concrete Methods"],32:["t6","Deprecated Methods"]}; +var altColor = "altColor"; +var rowColor = "rowColor"; +var tableTab = "tableTab"; +var activeTableTab = "activeTableTab"; +var pathtoroot = "../../../"; +var useModuleDirectories = true; +loadScripts(document, 'script'); +
    + +
    +
    -
    org.libjpegturbo.turbojpeg
    +

    Class TJDecompressor

    @@ -96,15 +131,14 @@

    Class TJDecompressor

  • All Implemented Interfaces:
    -
    java.io.Closeable, java.lang.AutoCloseable
    +
    java.io.Closeable, java.lang.AutoCloseable
    Direct Known Subclasses:
    -
    TJTransformer
    +
    TJTransformer

    -
    -
    public class TJDecompressor
    +
    public class TJDecompressor
     extends java.lang.Object
     implements java.io.Closeable
    TurboJPEG decompressor
    @@ -114,83 +148,37 @@

    Class TJDecompressor

    • - - +
        -
      • +
      • Constructor Summary

        - +
        - + + - + - + - + - +
        Constructors 
        Constructor and DescriptionConstructorDescription
        TJDecompressor() +TJDecompressor()
        Create a TurboJPEG decompresssor instance.
        TJDecompressor(byte[] jpegImage) +TJDecompressor​(byte[] jpegImage)
        Create a TurboJPEG decompressor instance and associate the JPEG source image or "abbreviated table specification" (AKA "tables-only") datastream stored in jpegImage with the newly created instance.
        TJDecompressor(byte[] jpegImage, - int imageSize) +TJDecompressor​(byte[] jpegImage, + int imageSize)
        Create a TurboJPEG decompressor instance and associate the JPEG source image or "abbreviated table specification" (AKA "tables-only") datastream of length imageSize bytes stored in jpegImage @@ -198,219 +186,406 @@

        Constructor Summary

        TJDecompressor(YUVImage yuvImage) -
        Create a TurboJPEG decompressor instance and associate the planar YUV - source image stored in yuvImage with the newly created - instance.
        +
        TJDecompressor​(YUVImage yuvImage) +
        Create a TurboJPEG decompressor instance and associate the + 8-bit-per-sample planar YUV source image stored in yuvImage + with the newly created instance.
      +
      +
        -
      • +
      • Method Summary

        - - +
        Methods 
        + - + + - + - + - - - - - + - + - + - + - + - + - + - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - + - + - + + + + + + - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + - + - +
        All Methods Instance Methods Concrete Methods Deprecated Methods 
        Modifier and TypeMethod and DescriptionMethodDescription
        voidclose() +close()
        Free the native structures associated with this decompressor instance.
        voiddecompress(java.awt.image.BufferedImage dstImage, - int flags) -
        Decompress the JPEG source image or decode the planar YUV source image - associated with this decompressor instance and output a packed-pixel - decompressed/decoded image to the given BufferedImage - instance.
        -
        voiddecompress(byte[] dstBuf, +decompress​(byte[] dstBuf, int x, int y, int desiredWidth, int pitch, int desiredHeight, int pixelFormat, - int flags) -
        Decompress the JPEG source image or decode the planar YUV source image - associated with this decompressor instance and output a packed-pixel - grayscale, RGB, or CMYK image to the given destination buffer.
        + int flags)
        +
        voiddecompress(int[] dstBuf, +decompress​(int[] dstBuf, int x, int y, int desiredWidth, int stride, int desiredHeight, int pixelFormat, - int flags) -
        Decompress the JPEG source image or decode the planar YUV source image - associated with this decompressor instance and output a packed-pixel - grayscale, RGB, or CMYK image to the given destination buffer.
        + int flags)
        +
        java.awt.image.BufferedImagedecompress(int desiredWidth, +decompress​(int desiredWidth, int desiredHeight, int bufferedImageType, - int flags) -
        Decompress the JPEG source image or decode the planar YUV source image - associated with this decompressor instance and return a - BufferedImage instance containing the packed-pixel - decompressed/decoded image.
        + int flags)
        +
        Deprecated. + +
        byte[]decompress(int desiredWidth, +decompress​(int desiredWidth, int pitch, int desiredHeight, int pixelFormat, - int flags) -
        Decompress the JPEG source image or decode the planar YUV source image - associated with this decompressor instance and return a buffer containing - the packed-pixel decompressed image.
        + int flags)
        +
        Deprecated. + +
        YUVImagedecompressToYUV(int desiredWidth, +
        voiddecompress​(java.awt.image.BufferedImage dstImage, + int flags) +
        Deprecated. + +
        +
        short[]decompress12​(int pitch, + int pixelFormat) +
        Decompress the 12-bit-per-sample JPEG source image associated with this + decompressor instance and return a buffer containing a 12-bit-per-sample + packed-pixel decompressed image.
        +
        voiddecompress12​(short[] dstBuf, + int x, + int y, + int pitch, + int pixelFormat) +
        Decompress the 12-bit-per-sample JPEG source image associated with this + decompressor instance and output a 12-bit-per-sample packed-pixel + grayscale, RGB, or CMYK image to the given destination buffer.
        +
        short[]decompress16​(int pitch, + int pixelFormat) +
        Decompress the 16-bit-per-sample JPEG source image associated with this + decompressor instance and return a buffer containing a 16-bit-per-sample + packed-pixel decompressed image.
        +
        voiddecompress16​(short[] dstBuf, + int x, + int y, + int pitch, + int pixelFormat) +
        Decompress the 16-bit-per-sample lossless JPEG source image associated + with this decompressor instance and output a 16-bit-per-sample + packed-pixel grayscale, RGB, or CMYK image to the given destination + buffer.
        +
        voiddecompress8​(byte[] dstBuf, + int x, + int y, + int pitch, + int pixelFormat) +
        Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + source image associated with this decompressor instance and output an + 8-bit-per-sample packed-pixel grayscale, RGB, or CMYK image to the given + destination buffer.
        +
        java.awt.image.BufferedImagedecompress8​(int bufferedImageType) +
        Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + source image associated with this decompressor instance and return a + BufferedImage instance containing the 8-bit-per-sample + packed-pixel decompressed/decoded image.
        +
        voiddecompress8​(int[] dstBuf, + int x, + int y, + int stride, + int pixelFormat) +
        Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + source image associated with this decompressor instance and output an + 8-bit-per-sample packed-pixel grayscale, RGB, or CMYK image to the given + destination buffer.
        +
        byte[]decompress8​(int pitch, + int pixelFormat) +
        Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + source image associated with this decompressor instance and return a + buffer containing an 8-bit-per-sample packed-pixel decompressed image.
        +
        voiddecompress8​(java.awt.image.BufferedImage dstImage) +
        Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + source image associated with this decompressor instance and output an + 8-bit-per-sample packed-pixel decompressed/decoded image to the given + BufferedImage instance.
        +
        YUVImagedecompressToYUV​(int align) +
        Decompress the 8-bit-per-sample JPEG source image associated with this + decompressor instance into an 8-bit-per-sample unified planar YUV image + and return a YUVImage instance containing the decompressed image.
        +
        YUVImagedecompressToYUV​(int[] strides) +
        Decompress the 8-bit-per-sample JPEG source image associated with this + decompressor instance into a set of 8-bit-per-sample Y, U (Cb), and V (Cr) + image planes and return a YUVImage instance containing the + decompressed image planes.
        +
        YUVImagedecompressToYUV​(int desiredWidth, int[] strides, int desiredHeight, - int flags) -
        Decompress the JPEG source image associated with this decompressor - instance into a set of Y, U (Cb), and V (Cr) image planes and return a - YUVImage instance containing the decompressed image planes.
        + int flags)
        +
        Deprecated. + +
        YUVImagedecompressToYUV(int desiredWidth, +
        YUVImagedecompressToYUV​(int desiredWidth, int align, int desiredHeight, - int flags) -
        Decompress the JPEG source image associated with this decompressor - instance into a unified planar YUV image and return a YUVImage - instance containing the decompressed image.
        + int flags)
        +
        Deprecated. + +
        voiddecompressToYUV(YUVImage dstImage, - int flags) -
        Decompress the JPEG source image associated with this decompressor - instance into a planar YUV image and store it in the given - YUVImage instance.
        +
        decompressToYUV​(YUVImage dstImage) +
        Decompress the 8-bit-per-sample JPEG source image associated with this + decompressor instance into an 8-bit-per-sample planar YUV image and store + it in the given YUVImage instance.
        voiddecompressToYUV​(YUVImage dstImage, + int flags) +
        Deprecated. + +
        +
        protected voidfinalize() finalize() 
        intgetColorspace() -
        Returns the colorspace used in the source image (JPEG or YUV) associated - with this decompressor instance.
        +
        get​(int param) +
        Get the value of a decompression parameter.
        intgetFlags() -
        Returns the bitwise OR of one or more of the - flags, such as - TJ.FLAG_PROGRESSIVE and - TJ.FLAG_LOSSLESS, that describe the JPEG image.
        +
        getColorspace() +
        Deprecated. +
        Use get(TJ.PARAM_COLORSPACE) + instead.
        +
        intgetHeight() +getHeight()
        Returns the height of the source image (JPEG or YUV) associated with this decompressor instance.
        byte[]getJPEGBuf() +getJPEGBuf()
        Returns the JPEG buffer associated with this decompressor instance.
        intgetJPEGSize() +getJPEGSize()
        Returns the size of the JPEG image (in bytes) associated with this decompressor instance.
        intgetScaledHeight(int desiredWidth, - int desiredHeight) -
        Returns the height of the largest scaled-down image that the TurboJPEG - decompressor can generate without exceeding the desired image width and - height.
        +
        getScaledHeight​(int desiredWidth, + int desiredHeight) +
        Deprecated. + +
        intgetScaledWidth(int desiredWidth, - int desiredHeight) -
        Returns the width of the largest scaled-down image that the TurboJPEG - decompressor can generate without exceeding the desired image width and - height.
        +
        getScaledWidth​(int desiredWidth, + int desiredHeight) +
        Deprecated. + +
        intgetSubsamp() -
        Returns the level of chrominance subsampling used in the source image - (JPEG or YUV) associated with this decompressor instance.
        +
        getSubsamp() +
        Deprecated. +
        Use get(TJ.PARAM_SUBSAMP) + instead.
        +
        intgetWidth() +getWidth()
        Returns the width of the source image (JPEG or YUV) associated with this decompressor instance.
        voidsetSourceImage(byte[] jpegImage, - int imageSize) +set​(int param, + int value) +
        Set the value of a decompression parameter.
        +
        voidsetCroppingRegion​(java.awt.Rectangle croppingRegion) +
        Set the cropping region for partially decompressing a lossy JPEG image + into a packed-pixel image.
        +
        voidsetScalingFactor​(TJScalingFactor scalingFactor) +
        Set the scaling factor for subsequent lossy decompression operations.
        +
        voidsetSourceImage​(byte[] jpegImage, + int imageSize)
        Associate the JPEG image or "abbreviated table specification" (AKA "tables-only") datastream of length imageSize bytes stored in jpegImage with this decompressor instance.
        voidsetSourceImage(YUVImage srcImage) +setSourceImage​(YUVImage srcImage)
        Associate the specified planar YUV source image with this decompressor instance.
          -
        • +
        • Methods inherited from class java.lang.Object

          @@ -418,192 +593,122 @@

          Methods inherited from class java.lang.Object

      +
    • - -
        -
      • - - -

        Field Detail

        - - - -
          -
        • -

          handle

          -
          protected long handle
          -
        • -
        - - - -
          -
        • -

          jpegBuf

          -
          protected byte[] jpegBuf
          -
        • -
        - - - -
          -
        • -

          jpegBufSize

          -
          protected int jpegBufSize
          -
        • -
        - - - -
          -
        • -

          yuvImage

          -
          protected YUVImage yuvImage
          -
        • -
        - - - -
          -
        • -

          jpegWidth

          -
          protected int jpegWidth
          -
        • -
        - - - -
          -
        • -

          jpegHeight

          -
          protected int jpegHeight
          -
        • -
        - - - -
          -
        • -

          jpegSubsamp

          -
          protected int jpegSubsamp
          -
        • -
        - - - -
          -
        • -

          jpegColorspace

          -
          protected int jpegColorspace
          -
        • -
        - - - -
          -
        • -

          jpegFlags

          -
          protected int jpegFlags
          -
        • -
        -
      • -
      +
    Create a TurboJPEG decompresssor instance.
    -
    Throws:
    -
    TJException
    +
    +
    Throws:
    +
    TJException
    +
  • - +
    • TJDecompressor

      -
      public TJDecompressor(byte[] jpegImage)
      -               throws TJException
      +
      public TJDecompressor​(byte[] jpegImage)
      +               throws TJException
      Create a TurboJPEG decompressor instance and associate the JPEG source image or "abbreviated table specification" (AKA "tables-only") datastream - stored in jpegImage with the newly created instance.
      -
      Parameters:
      jpegImage - buffer containing a JPEG source image or tables-only + stored in jpegImage with the newly created instance. Refer + to setSourceImage(byte[], int) for more details.
    +
    +
    Parameters:
    +
    jpegImage - buffer containing a JPEG source image or tables-only datastream. (The size of the JPEG image or datastream is assumed to be the length of the array.) This buffer is not modified.
    -
    Throws:
    -
    TJException
    +
    Throws:
    +
    TJException
    + - +
    • TJDecompressor

      -
      public TJDecompressor(byte[] jpegImage,
      -              int imageSize)
      -               throws TJException
      +
      public TJDecompressor​(byte[] jpegImage,
      +                      int imageSize)
      +               throws TJException
      Create a TurboJPEG decompressor instance and associate the JPEG source image or "abbreviated table specification" (AKA "tables-only") datastream of length imageSize bytes stored in jpegImage - with the newly created instance.
      -
      Parameters:
      jpegImage - buffer containing a JPEG source image or tables-only - datastream. This buffer is not modified.
      imageSize - size of the JPEG source image or tables-only datastream + with the newly created instance. Refer to + setSourceImage(byte[], int) for more details. +
      +
      Parameters:
      +
      jpegImage - buffer containing a JPEG source image or tables-only + datastream. This buffer is not modified.
      +
      imageSize - size of the JPEG source image or tables-only datastream (in bytes)
      -
      Throws:
      -
      TJException
      +
      Throws:
      +
      TJException
      +
    - +
    • TJDecompressor

      -
      public TJDecompressor(YUVImage yuvImage)
      -               throws TJException
      -
      Create a TurboJPEG decompressor instance and associate the planar YUV - source image stored in yuvImage with the newly created - instance.
      -
      Parameters:
      yuvImage - YUVImage instance containing a planar YUV source +
      public TJDecompressor​(YUVImage yuvImage)
      +               throws TJException
      +
      Create a TurboJPEG decompressor instance and associate the + 8-bit-per-sample planar YUV source image stored in yuvImage + with the newly created instance. Refer to + setSourceImage(YUVImage) for more details.
      +
      +
      Parameters:
      +
      yuvImage - YUVImage instance containing a planar YUV source image to be decoded. This image is not modified.
      -
      Throws:
      -
      TJException
      +
      Throws:
      +
      TJException
      +
    + +
      -
    • +
    • Method Detail

      - +
      • setSourceImage

        -
        public void setSourceImage(byte[] jpegImage,
        -                  int imageSize)
        -                    throws TJException
        +
        public void setSourceImage​(byte[] jpegImage,
        +                           int imageSize)
        +                    throws TJException
        Associate the JPEG image or "abbreviated table specification" (AKA "tables-only") datastream of length imageSize bytes stored in jpegImage with this decompressor instance. If @@ -613,555 +718,942 @@

        setSourceImage

        quantization and Huffman tables that can be used when decompressing subsequent "abbreviated image" datastreams. This is useful, for instance, when decompressing video streams in which all frames share the same - quantization and Huffman tables.
        -
        Parameters:
        jpegImage - buffer containing a JPEG source image or tables-only - datastream. This buffer is not modified.
        imageSize - size of the JPEG source image or tables-only datastream + quantization and Huffman tables. If a JPEG image is passed to this + method, then the parameters that describe + the JPEG image will be set when the method returns. +
        +
        Parameters:
        +
        jpegImage - buffer containing a JPEG source image or tables-only + datastream. This buffer is not modified.
        +
        imageSize - size of the JPEG source image or tables-only datastream (in bytes)
        -
        Throws:
        -
        TJException
        +
        Throws:
        +
        TJException
        +
      - +
      • setSourceImage

        -
        public void setSourceImage(YUVImage srcImage)
        +
        public void setSourceImage​(YUVImage srcImage)
        Associate the specified planar YUV source image with this decompressor instance. Subsequent decompression operations will decode this image into - a packed-pixel RGB or grayscale destination image.
        -
        Parameters:
        srcImage - YUVImage instance containing a planar YUV source - image to be decoded. This image is not modified.
        + a packed-pixel RGB or grayscale destination image. This method sets + TJ.PARAM_SUBSAMP to the chrominance subsampling level of the + source image. +
        +
        Parameters:
        +
        srcImage - YUVImage instance containing a planar YUV source + image to be decoded. This image is not modified.
        +
      - +
      • getWidth

        -
        public int getWidth()
        +
        public int getWidth()
        Returns the width of the source image (JPEG or YUV) associated with this decompressor instance.
        -
        Returns:
        the width of the source image (JPEG or YUV) associated with this - decompressor instance.
        +
        +
        Returns:
        +
        the width of the source image (JPEG or YUV) associated with this + decompressor instance.
        +
      - +
      • getHeight

        -
        public int getHeight()
        +
        public int getHeight()
        Returns the height of the source image (JPEG or YUV) associated with this decompressor instance.
        -
        Returns:
        the height of the source image (JPEG or YUV) associated with this - decompressor instance.
        +
        +
        Returns:
        +
        the height of the source image (JPEG or YUV) associated with this + decompressor instance.
        +
      - +
      • -

        getSubsamp

        -
        public int getSubsamp()
        -
        Returns the level of chrominance subsampling used in the source image - (JPEG or YUV) associated with this decompressor instance. See - TJ.SAMP_*.
        -
        Returns:
        the level of chrominance subsampling used in the source image - (JPEG or YUV) associated with this decompressor instance.
        +

        set

        +
        public void set​(int param,
        +                int value)
        +
        Set the value of a decompression parameter.
        +
        +
        Parameters:
        +
        param - one of TJ.PARAM_*
        +
        value - value of the decompression parameter (refer to + parameter documentation)
        +
      - +
      • -

        getColorspace

        -
        public int getColorspace()
        -
        Returns the colorspace used in the source image (JPEG or YUV) associated - with this decompressor instance. See TJ.CS_*. If the - source image is YUV, then this always returns TJ.CS_YCbCr.
        -
        Returns:
        the colorspace used in the source image (JPEG or YUV) associated - with this decompressor instance.
        +

        get

        +
        public int get​(int param)
        +
        Get the value of a decompression parameter.
        +
        +
        Parameters:
        +
        param - one of TJ.PARAM_*
        +
        Returns:
        +
        the value of the specified decompression parameter, or -1 if the + value is unknown.
        +
        +
      • +
      + + + +
        +
      • +

        setScalingFactor

        +
        public void setScalingFactor​(TJScalingFactor scalingFactor)
        +
        Set the scaling factor for subsequent lossy decompression operations.
        +
        +
        Parameters:
        +
        scalingFactor - TJScalingFactor instance that specifies a + fractional scaling factor that the decompressor supports (see + TJ.getScalingFactors()), or TJ.UNSCALED for no scaling. + Decompression scaling is a function of the IDCT algorithm, so scaling + factors are generally limited to multiples of 1/8. If the entire JPEG + image will be decompressed, then the width and height of the scaled + destination image can be determined by calling + scalingFactor.getScaled() + with the JPEG image width and height (see getWidth() and + getHeight().) When decompressing into a planar YUV image, an + intermediate buffer copy will be performed if the width or height of the + scaled destination image is not an even multiple of the MCU block size + (see TJ.getMCUWidth() and TJ.getMCUHeight().) Note that decompression scaling is not available + (and the specified scaling factor is ignored) when decompressing lossless + JPEG images (see TJ.PARAM_LOSSLESS), since the IDCT algorithm is + not used with those images. Note also that TJ.PARAM_FASTDCT is + ignored when decompression scaling is enabled.
        +
      - +
      • -

        getFlags

        -
        public int getFlags()
        -
        Returns the bitwise OR of one or more of the - flags, such as - TJ.FLAG_PROGRESSIVE and - TJ.FLAG_LOSSLESS, that describe the JPEG image.
        -
        Returns:
        the bitwise OR of one or more of the - flags that describe the JPEG image.
        +

        setCroppingRegion

        +
        public void setCroppingRegion​(java.awt.Rectangle croppingRegion)
        +                       throws TJException
        +
        Set the cropping region for partially decompressing a lossy JPEG image + into a packed-pixel image.
        +
        +
        Parameters:
        +
        croppingRegion - java.awt.Rectangle instance that + specifies a subregion of the JPEG image to decompress, or + TJ.UNCROPPED for no cropping. The left boundary of the cropping + region must be evenly divisible by the scaled MCU block width, which can + be determined by calling TJScalingFactor.getScaled() with the specified scaling factor (see + setScalingFactor()) and the MCU block width + (see TJ.getMCUWidth()) for the level of chrominance + subsampling in the JPEG image (see TJ.PARAM_SUBSAMP.) The + cropping region should be specified relative to the scaled image + dimensions. Unless croppingRegion is TJ.UNCROPPED, + the JPEG header must be read (see setSourceImage(byte[], int) + prior to calling this method.
        +
        Throws:
        +
        TJException
        +
      - + + + +
        +
      • +

        getSubsamp

        +
        @Deprecated
        +public int getSubsamp()
        +
        Deprecated. +
        Use get(TJ.PARAM_SUBSAMP) + instead.
        +
        +
      • +
      + + + +
        +
      • +

        getColorspace

        +
        @Deprecated
        +public int getColorspace()
        +
        Deprecated. +
        Use get(TJ.PARAM_COLORSPACE) + instead.
        +
        +
      • +
      +
      • getJPEGBuf

        -
        public byte[] getJPEGBuf()
        +
        public byte[] getJPEGBuf()
        Returns the JPEG buffer associated with this decompressor instance.
        -
        Returns:
        the JPEG buffer associated with this decompressor instance.
        +
        +
        Returns:
        +
        the JPEG buffer associated with this decompressor instance.
        +
      - +
      • getJPEGSize

        -
        public int getJPEGSize()
        +
        public int getJPEGSize()
        Returns the size of the JPEG image (in bytes) associated with this decompressor instance.
        -
        Returns:
        the size of the JPEG image (in bytes) associated with this - decompressor instance.
        +
        +
        Returns:
        +
        the size of the JPEG image (in bytes) associated with this + decompressor instance.
        +
      - +
      • getScaledWidth

        -
        public int getScaledWidth(int desiredWidth,
        -                 int desiredHeight)
        -
        Returns the width of the largest scaled-down image that the TurboJPEG - decompressor can generate without exceeding the desired image width and - height.
        -
        Parameters:
        desiredWidth - desired width (in pixels) of the decompressed image. - Setting this to 0 is the same as setting it to the width of the JPEG - image. (In other words, the width will not be considered when determining - the scaled image size.)
        desiredHeight - desired height (in pixels) of the decompressed image. - Setting this to 0 is the same as setting it to the height of the JPEG - image. (In other words, the height will not be considered when - determining the scaled image size.)
        -
        Returns:
        the width of the largest scaled-down image that the TurboJPEG - decompressor can generate without exceeding the desired image width and - height.
        +
        @Deprecated
        +public int getScaledWidth​(int desiredWidth,
        +                          int desiredHeight)
        +
        Deprecated. + +
      - +
      • getScaledHeight

        -
        public int getScaledHeight(int desiredWidth,
        -                  int desiredHeight)
        -
        Returns the height of the largest scaled-down image that the TurboJPEG - decompressor can generate without exceeding the desired image width and - height.
        -
        Parameters:
        desiredWidth - desired width (in pixels) of the decompressed image. - Setting this to 0 is the same as setting it to the width of the JPEG - image. (In other words, the width will not be considered when determining - the scaled image size.)
        desiredHeight - desired height (in pixels) of the decompressed image. - Setting this to 0 is the same as setting it to the height of the JPEG - image. (In other words, the height will not be considered when - determining the scaled image size.)
        -
        Returns:
        the height of the largest scaled-down image that the TurboJPEG - decompressor can generate without exceeding the desired image width and - height.
        +
        @Deprecated
        +public int getScaledHeight​(int desiredWidth,
        +                           int desiredHeight)
        +
        Deprecated. + +
      - +
      • -

        decompress

        -
        public void decompress(byte[] dstBuf,
        -              int x,
        -              int y,
        -              int desiredWidth,
        -              int pitch,
        -              int desiredHeight,
        -              int pixelFormat,
        -              int flags)
        -                throws TJException
        -
        Decompress the JPEG source image or decode the planar YUV source image - associated with this decompressor instance and output a packed-pixel - grayscale, RGB, or CMYK image to the given destination buffer. +

        decompress8

        +
        public void decompress8​(byte[] dstBuf,
        +                        int x,
        +                        int y,
        +                        int pitch,
        +                        int pixelFormat)
        +                 throws TJException
        +
        Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + source image associated with this decompressor instance and output an + 8-bit-per-sample packed-pixel grayscale, RGB, or CMYK image to the given + destination buffer.

        NOTE: The destination image is fully recoverable if this method throws a - non-fatal TJException (unless - TJ.FLAG_STOPONWARNING is specified.)

        -
        Parameters:
        dstBuf - buffer that will receive the packed-pixel - decompressed/decoded image. If the source image is a JPEG image, then - this buffer should normally be pitch * scaledHeight bytes in - size, where scaledHeight can be determined by calling - scalingFactor.getScaled(jpegHeight) - with one of the scaling factors returned from TJ.getScalingFactors() - or by calling getScaledHeight(int, int). If the source image is a YUV - image, then this buffer should normally be pitch * height - bytes in size, where height is the height of the YUV image. - However, the buffer may also be larger than the dimensions of the source - image, in which case the x, y, and + non-fatal TJException (unless TJ.PARAM_STOPONWARNING is + set.)
        +
        +
        Parameters:
        +
        dstBuf - buffer that will receive the packed-pixel + decompressed/decoded image. This buffer should normally be + pitch * destinationHeight bytes in size. However, the buffer + may also be larger, in which case the x, y, and pitch parameters can be used to specify the region into which - the source image should be decompressed/decoded.
        x - x offset (in pixels) of the region in the destination image into - which the source image should be decompressed/decoded
        y - y offset (in pixels) of the region in the destination image into - which the source image should be decompressed/decoded
        desiredWidth - If the source image is a JPEG image, then this - specifies the desired width (in pixels) of the decompressed image (or - image region.) If the desired destination image dimensions are different - than the source image dimensions, then TurboJPEG will use scaling in the - JPEG decompressor to generate the largest possible image that will fit - within the desired dimensions. Setting this to 0 is the same as setting - it to the width of the JPEG image. (In other words, the width will not be - considered when determining the scaled image size.) This parameter is - ignored if the source image is a YUV image.
        pitch - bytes per row in the destination image. Normally this should - be set to scaledWidth * - TJ.getPixelSize(pixelFormat), - if the destination image will be unpadded. However, you can use this to, - for instance, pad each row of the destination image to the nearest - multiple of 4 bytes or to decompress/decode the source image into a region - of a larger image. NOTE: if the source image is a JPEG image, then - scaledWidth can be determined by calling - scalingFactor.getScaled(jpegWidth) - or by calling getScaledWidth(int, int). If the source image is a YUV - image, then scaledWidth is the width of the YUV image. - Setting this parameter to 0 is the equivalent of setting it to - scaledWidth * - TJ.getPixelSize(pixelFormat).
        desiredHeight - If the source image is a JPEG image, then this - specifies the desired height (in pixels) of the decompressed image (or - image region.) If the desired destination image dimensions are different - than the source image dimensions, then TurboJPEG will use scaling in the - JPEG decompressor to generate the largest possible image that will fit - within the desired dimensions. Setting this to 0 is the same as setting - it to the height of the JPEG image. (In other words, the height will not - be considered when determining the scaled image size.) This parameter is - ignored if the source image is a YUV image.
        pixelFormat - pixel format of the decompressed/decoded image (one of - TJ.PF_*)
        flags - the bitwise OR of one or more of - TJ.FLAG_*
        -
        Throws:
        -
        TJException
        + the source image should be decompressed/decoded. NOTE: If the source + image is a lossy JPEG image, then destinationHeight is either + the scaled JPEG height (see setScalingFactor(), + TJScalingFactor.getScaled(), and + getHeight()) or the height of the cropping region (see + setCroppingRegion().) If the source image is a + YUV image or a lossless JPEG image, then destinationHeight is + the height of the source image. +
        x - x offset (in pixels) of the region in the destination image into + which the source image should be decompressed/decoded
        +
        y - y offset (in pixels) of the region in the destination image into + which the source image should be decompressed/decoded
        +
        pitch - bytes per row in the destination image. Normally this should + be set to destinationWidth * + TJ.getPixelSize(pixelFormat), if the + destination image will be unpadded. (Setting this parameter to 0 is the + equivalent of setting it to destinationWidth * + TJ.getPixelSize(pixelFormat).) However, + you can also use this parameter to specify the row alignment/padding of + the destination image, to skip rows, or to decompress/decode into a + specific region of a larger image. NOTE: if the source image is a lossy + JPEG image, then destinationWidth is either the scaled JPEG + width (see setScalingFactor(), + TJScalingFactor.getScaled(), and + getWidth()) or the width of the cropping region (see + setCroppingRegion().) If the source image is a + YUV image or a lossless JPEG image, then destinationWidth is + the width of the source image.
        +
        pixelFormat - pixel format of the decompressed/decoded image (one of + TJ.PF_*)
        +
        Throws:
        +
        TJException
        +
      - + + + + +
        +
      • +

        decompress8

        +
        public byte[] decompress8​(int pitch,
        +                          int pixelFormat)
        +                   throws TJException
        +
        Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + source image associated with this decompressor instance and return a + buffer containing an 8-bit-per-sample packed-pixel decompressed image.
        +
        +
        Parameters:
        +
        pitch - see + decompress8(byte[], int, int, int, int) for description
        +
        pixelFormat - pixel format of the decompressed image (one of + TJ.PF_*)
        +
        Returns:
        +
        a buffer containing an 8-bit-per-sample packed-pixel decompressed + image.
        +
        Throws:
        +
        TJException
        +
        +
      • +
      + + + + + + + +
        +
      • +

        decompress12

        +
        public void decompress12​(short[] dstBuf,
        +                         int x,
        +                         int y,
        +                         int pitch,
        +                         int pixelFormat)
        +                  throws TJException
        +
        Decompress the 12-bit-per-sample JPEG source image associated with this + decompressor instance and output a 12-bit-per-sample packed-pixel + grayscale, RGB, or CMYK image to the given destination buffer. +

        + NOTE: The destination image is fully recoverable if this method throws a + non-fatal TJException (unless TJ.PARAM_STOPONWARNING is + set.)

        +
        +
        Parameters:
        +
        dstBuf - buffer that will receive the packed-pixel + decompressed image. This buffer should normally be + pitch * destinationHeight samples in size. However, the + buffer may also be larger, in which case the x, + y, and pitch parameters can be used to specify + the region into which the source image should be decompressed. NOTE: If + the source image is a lossy JPEG image, then + destinationHeight is either the scaled JPEG height (see + setScalingFactor(), + TJScalingFactor.getScaled(), and + getHeight()) or the height of the cropping region (see + setCroppingRegion().) If the source image is a + lossless JPEG image, then destinationHeight is the height of + the source image.
        +
        x - x offset (in pixels) of the region in the destination image into + which the source image should be decompressed
        +
        y - y offset (in pixels) of the region in the destination image into + which the source image should be decompressed
        +
        pitch - samples per row in the destination image. Normally this + should be set to destinationWidth * + TJ.getPixelSize(pixelFormat), if the + destination image will be unpadded. (Setting this parameter to 0 is the + equivalent of setting it to destinationWidth * + TJ.getPixelSize(pixelFormat).) However, + you can also use this parameter to specify the row alignment/padding of + the destination image, to skip rows, or to decompress into a specific + region of a larger image. NOTE: if the source image is a lossy JPEG + image, then destinationWidth is either the scaled JPEG width + (see setScalingFactor(), + TJScalingFactor.getScaled(), and + getWidth()) or the width of the cropping region (see + setCroppingRegion().) If the source image is a + YUV image or a lossless JPEG image, then destinationWidth is + the width of the source image.
        +
        pixelFormat - pixel format of the decompressed image (one of + TJ.PF_*)
        +
        Throws:
        +
        TJException
        +
        +
      • +
      + + + +
        +
      • +

        decompress12

        +
        public short[] decompress12​(int pitch,
        +                            int pixelFormat)
        +                     throws TJException
        +
        Decompress the 12-bit-per-sample JPEG source image associated with this + decompressor instance and return a buffer containing a 12-bit-per-sample + packed-pixel decompressed image.
        +
        +
        Parameters:
        +
        pitch - see + decompress12(short[], int, int, int, int) for description
        +
        pixelFormat - pixel format of the decompressed image (one of + TJ.PF_*)
        +
        Returns:
        +
        a buffer containing an 8-bit-per-sample packed-pixel decompressed + image.
        +
        Throws:
        +
        TJException
        +
        +
      • +
      + + + +
        +
      • +

        decompress16

        +
        public void decompress16​(short[] dstBuf,
        +                         int x,
        +                         int y,
        +                         int pitch,
        +                         int pixelFormat)
        +                  throws TJException
        +
        Decompress the 16-bit-per-sample lossless JPEG source image associated + with this decompressor instance and output a 16-bit-per-sample + packed-pixel grayscale, RGB, or CMYK image to the given destination + buffer. +

        + NOTE: The destination image is fully recoverable if this method throws a + non-fatal TJException (unless TJ.PARAM_STOPONWARNING is + set.)

        +
        +
        Parameters:
        +
        dstBuf - buffer that will receive the packed-pixel + decompressed image. This buffer should normally be + pitch * jpegHeight samples in size. However, the buffer may + also be larger, in which case the x, + y, and pitch parameters can be used to specify + the region into which the source image should be decompressed.
        +
        x - x offset (in pixels) of the region in the destination image into + which the source image should be decompressed
        +
        y - y offset (in pixels) of the region in the destination image into + which the source image should be decompressed
        +
        pitch - samples per row in the destination image. Normally this + should be set to jpegWidth * + TJ.getPixelSize(pixelFormat), if the + destination image will be unpadded. (Setting this parameter to 0 is the + equivalent of setting it to jpegWidth * + TJ.getPixelSize(pixelFormat).) However, + you can also use this parameter to specify the row alignment/padding of + the destination image, to skip rows, or to decompress into a specific + region of a larger image.
        +
        pixelFormat - pixel format of the decompressed image (one of + TJ.PF_*)
        +
        Throws:
        +
        TJException
        +
        +
      • +
      + + + +
        +
      • +

        decompress16

        +
        public short[] decompress16​(int pitch,
        +                            int pixelFormat)
        +                     throws TJException
        +
        Decompress the 16-bit-per-sample JPEG source image associated with this + decompressor instance and return a buffer containing a 16-bit-per-sample + packed-pixel decompressed image.
        +
        +
        Parameters:
        +
        pitch - see + decompress16(short[], int, int, int, int) for description
        +
        pixelFormat - pixel format of the decompressed image (one of + TJ.PF_*)
        +
        Returns:
        +
        a buffer containing an 8-bit-per-sample packed-pixel decompressed + image.
        +
        Throws:
        +
        TJException
        +
      - +
      • decompressToYUV

        -
        public void decompressToYUV(YUVImage dstImage,
        -                   int flags)
        -                     throws TJException
        -
        Decompress the JPEG source image associated with this decompressor - instance into a planar YUV image and store it in the given - YUVImage instance. This method performs JPEG decompression but - leaves out the color conversion step, so a planar YUV image is generated - instead of a packed-pixel image. This method cannot be used to decompress - JPEG source images with the CMYK or YCCK colorspace. +
        public void decompressToYUV​(YUVImage dstImage)
        +                     throws TJException
        +
        Decompress the 8-bit-per-sample JPEG source image associated with this + decompressor instance into an 8-bit-per-sample planar YUV image and store + it in the given YUVImage instance. This method performs JPEG + decompression but leaves out the color conversion step, so a planar YUV + image is generated instead of a packed-pixel image. This method cannot be + used to decompress JPEG source images with the CMYK or YCCK colorspace.

        NOTE: The planar YUV destination image is fully recoverable if this method - throws a non-fatal TJException (unless - TJ.FLAG_STOPONWARNING is specified.)

        -
        Parameters:
        dstImage - YUVImage instance that will receive the planar YUV + throws a non-fatal TJException (unless + TJ.PARAM_STOPONWARNING is set.)
        +
        +
        Parameters:
        +
        dstImage - YUVImage instance that will receive the planar YUV decompressed image. The level of subsampling specified in this - YUVImage instance must match that of the JPEG image, and the width - and height specified in the YUVImage instance must match one of - the scaled image sizes that the decompressor is capable of generating from - the JPEG source image.
        flags - the bitwise OR of one or more of - TJ.FLAG_*
        -
        Throws:
        -
        TJException
        + YUVImage instance must match that of the JPEG image, and the width + and height specified in the YUVImage instance must match the + scaled JPEG width and height (see setScalingFactor(), TJScalingFactor.getScaled(), getWidth(), and getHeight().) +
        Throws:
        +
        TJException
        +
      - +
      • decompressToYUV

        -
        public YUVImage decompressToYUV(int desiredWidth,
        -                       int[] strides,
        -                       int desiredHeight,
        -                       int flags)
        -                         throws TJException
        -
        Decompress the JPEG source image associated with this decompressor - instance into a set of Y, U (Cb), and V (Cr) image planes and return a - YUVImage instance containing the decompressed image planes. This - method performs JPEG decompression but leaves out the color conversion - step, so a planar YUV image is generated instead of a packed-pixel image. - This method cannot be used to decompress JPEG source images with the CMYK - or YCCK colorspace.
        -
        Parameters:
        desiredWidth - desired width (in pixels) of the YUV image. If the - desired image dimensions are different than the dimensions of the JPEG - image being decompressed, then TurboJPEG will use scaling in the JPEG - decompressor to generate the largest possible image that will fit within - the desired dimensions. Setting this to 0 is the same as setting it to - the width of the JPEG image. (In other words, the width will not be - considered when determining the scaled image size.)
        strides - an array of integers, each specifying the number of bytes +
        @Deprecated
        +public void decompressToYUV​(YUVImage dstImage,
        +                            int flags)
        +                     throws TJException
        +
        Deprecated. + +
        +
        +
        Throws:
        +
        TJException
        +
        +
      • +
      + + + +
        +
      • +

        decompressToYUV

        +
        public YUVImage decompressToYUV​(int[] strides)
        +                         throws TJException
        +
        Decompress the 8-bit-per-sample JPEG source image associated with this + decompressor instance into a set of 8-bit-per-sample Y, U (Cb), and V (Cr) + image planes and return a YUVImage instance containing the + decompressed image planes. This method performs JPEG decompression but + leaves out the color conversion step, so a planar YUV image is generated + instead of a packed-pixel image. This method cannot be used to decompress + JPEG source images with the CMYK or YCCK colorspace.
        +
        +
        Parameters:
        +
        strides - an array of integers, each specifying the number of bytes per row in the corresponding plane of the YUV image. Setting the stride for any plane to 0 is the same as setting it to the scaled plane width - (see YUVImage.) If strides is null, then the strides + (see YUVImage.) If strides is null, then the strides for all planes will be set to their respective scaled plane widths. You can adjust the strides in order to add an arbitrary amount of row padding - to each plane.
        desiredHeight - desired height (in pixels) of the YUV image. If the - desired image dimensions are different than the dimensions of the JPEG - image being decompressed, then TurboJPEG will use scaling in the JPEG - decompressor to generate the largest possible image that will fit within - the desired dimensions. Setting this to 0 is the same as setting it to - the height of the JPEG image. (In other words, the height will not be - considered when determining the scaled image size.)
        flags - the bitwise OR of one or more of - TJ.FLAG_*
        -
        Returns:
        a YUVImage instance containing the decompressed image + to each plane.
        +
        Returns:
        +
        a YUVImage instance containing the decompressed image planes
        -
        Throws:
        -
        TJException
        +
        Throws:
        +
        TJException
        +
      - +
      • decompressToYUV

        -
        public YUVImage decompressToYUV(int desiredWidth,
        -                       int align,
        -                       int desiredHeight,
        -                       int flags)
        -                         throws TJException
        -
        Decompress the JPEG source image associated with this decompressor - instance into a unified planar YUV image and return a YUVImage - instance containing the decompressed image. This method performs JPEG - decompression but leaves out the color conversion step, so a planar YUV - image is generated instead of a packed-pixel image. This method cannot be - used to decompress JPEG source images with the CMYK or YCCK colorspace.
        -
        Parameters:
        desiredWidth - desired width (in pixels) of the YUV image. If the - desired image dimensions are different than the dimensions of the JPEG - image being decompressed, then TurboJPEG will use scaling in the JPEG - decompressor to generate the largest possible image that will fit within - the desired dimensions. Setting this to 0 is the same as setting it to - the width of the JPEG image. (In other words, the width will not be - considered when determining the scaled image size.)
        align - row alignment (in bytes) of the YUV image (must be a power of +
        @Deprecated
        +public YUVImage decompressToYUV​(int desiredWidth,
        +                                int[] strides,
        +                                int desiredHeight,
        +                                int flags)
        +                         throws TJException
        +
        Deprecated. + +
        +
        +
        Throws:
        +
        TJException
        +
        +
      • +
      + + + +
        +
      • +

        decompressToYUV

        +
        public YUVImage decompressToYUV​(int align)
        +                         throws TJException
        +
        Decompress the 8-bit-per-sample JPEG source image associated with this + decompressor instance into an 8-bit-per-sample unified planar YUV image + and return a YUVImage instance containing the decompressed image. + This method performs JPEG decompression but leaves out the color + conversion step, so a planar YUV image is generated instead of a + packed-pixel image. This method cannot be used to decompress JPEG source + images with the CMYK or YCCK colorspace.
        +
        +
        Parameters:
        +
        align - row alignment (in bytes) of the YUV image (must be a power of 2.) Setting this parameter to n will cause each row in each plane of the - YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.)
        desiredHeight - desired height (in pixels) of the YUV image. If the - desired image dimensions are different than the dimensions of the JPEG - image being decompressed, then TurboJPEG will use scaling in the JPEG - decompressor to generate the largest possible image that will fit within - the desired dimensions. Setting this to 0 is the same as setting it to - the height of the JPEG image. (In other words, the height will not be - considered when determining the scaled image size.)
        flags - the bitwise OR of one or more of - TJ.FLAG_*
        -
        Returns:
        a YUVImage instance containing the unified planar YUV + YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.)
        +
        Returns:
        +
        a YUVImage instance containing the unified planar YUV decompressed image
        -
        Throws:
        -
        TJException
        +
        Throws:
        +
        TJException
        +
      - +
      • -

        decompress

        -
        public void decompress(int[] dstBuf,
        -              int x,
        -              int y,
        -              int desiredWidth,
        -              int stride,
        -              int desiredHeight,
        -              int pixelFormat,
        -              int flags)
        -                throws TJException
        -
        Decompress the JPEG source image or decode the planar YUV source image - associated with this decompressor instance and output a packed-pixel - grayscale, RGB, or CMYK image to the given destination buffer. +

        decompressToYUV

        +
        @Deprecated
        +public YUVImage decompressToYUV​(int desiredWidth,
        +                                int align,
        +                                int desiredHeight,
        +                                int flags)
        +                         throws TJException
        +
        Deprecated. + +
        +
        +
        Throws:
        +
        TJException
        +
        +
      • +
      + + + +
        +
      • +

        decompress8

        +
        public void decompress8​(int[] dstBuf,
        +                        int x,
        +                        int y,
        +                        int stride,
        +                        int pixelFormat)
        +                 throws TJException
        +
        Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + source image associated with this decompressor instance and output an + 8-bit-per-sample packed-pixel grayscale, RGB, or CMYK image to the given + destination buffer.

        NOTE: The destination image is fully recoverable if this method throws a - non-fatal TJException (unless - TJ.FLAG_STOPONWARNING is specified.)

        -
        Parameters:
        dstBuf - buffer that will receive the packed-pixel - decompressed/decoded image. If the source image is a JPEG image, then - this buffer should normally be stride * scaledHeight pixels - in size, where scaledHeight can be determined by calling - scalingFactor.getScaled(jpegHeight) - with one of the scaling factors returned from TJ.getScalingFactors() - or by calling getScaledHeight(int, int). If the source image is a YUV - image, then this buffer should normally be stride * height - pixels in size, where height is the height of the YUV image. - However, the buffer may also be larger than the dimensions of the JPEG - image, in which case the x, y, and - stride parameters can be used to specify the region into - which the source image should be decompressed.
        x - x offset (in pixels) of the region in the destination image into - which the source image should be decompressed/decoded
        y - y offset (in pixels) of the region in the destination image into - which the source image should be decompressed/decoded
        desiredWidth - If the source image is a JPEG image, then this - specifies the desired width (in pixels) of the decompressed image (or - image region.) If the desired destination image dimensions are different - than the source image dimensions, then TurboJPEG will use scaling in the - JPEG decompressor to generate the largest possible image that will fit - within the desired dimensions. Setting this to 0 is the same as setting - it to the width of the JPEG image. (In other words, the width will not be - considered when determining the scaled image size.) This parameter is - ignored if the source image is a YUV image.
        stride - pixels per row in the destination image. Normally this - should be set to scaledWidth, but you can use this to, for - instance, decompress the JPEG image into a region of a larger image. - NOTE: if the source image is a JPEG image, then scaledWidth - can be determined by calling - scalingFactor.getScaled(jpegWidth) - or by calling getScaledWidth(int, int). If the source image is a YUV - image, then scaledWidth is the width of the YUV image. - Setting this parameter to 0 is the equivalent of setting it to - scaledWidth.
        desiredHeight - If the source image is a JPEG image, then this - specifies the desired height (in pixels) of the decompressed image (or - image region.) If the desired destination image dimensions are different - than the source image dimensions, then TurboJPEG will use scaling in the - JPEG decompressor to generate the largest possible image that will fit - within the desired dimensions. Setting this to 0 is the same as setting - it to the height of the JPEG image. (In other words, the height will not - be considered when determining the scaled image size.) This parameter is - ignored if the source image is a YUV image.
        pixelFormat - pixel format of the decompressed image (one of - TJ.PF_*)
        flags - the bitwise OR of one or more of - TJ.FLAG_*
        -
        Throws:
        -
        TJException
        + non-fatal TJException (unless TJ.PARAM_STOPONWARNING + is set.) +
        +
        Parameters:
        +
        dstBuf - buffer that will receive the packed-pixel + decompressed/decoded image. This buffer should normally be + stride * destinationHeight pixels in size. However, the + buffer may also be larger, in which case the x, + y, and pitch parameters can be used to specify + the region into which the source image should be decompressed/decoded. + NOTE: If the source image is a lossy JPEG image, then + destinationHeight is either the scaled JPEG height (see + setScalingFactor(), + TJScalingFactor.getScaled(), and + getHeight()) or the height of the cropping region (see + setCroppingRegion().) If the source image is a + YUV image or a lossless JPEG image, then destinationHeight is + the height of the source image.
        +
        x - x offset (in pixels) of the region in the destination image into + which the source image should be decompressed/decoded
        +
        y - y offset (in pixels) of the region in the destination image into + which the source image should be decompressed/decoded
        +
        stride - pixels per row in the destination image. Normally this + should be set to destinationWidth. (Setting this parameter + to 0 is the equivalent of setting it to destinationWidth.) + However, you can also use this parameter to skip rows or to + decompress/decode into a specific region of a larger image. NOTE: if the + source image is a lossy JPEG image, then destinationWidth is + either the scaled JPEG width (see setScalingFactor(), TJScalingFactor.getScaled(), and getWidth()) or the width of the + cropping region (see setCroppingRegion().) If + the source image is a YUV image or a lossless JPEG image, then + destinationWidth is the width of the source image.
        +
        pixelFormat - pixel format of the decompressed/decoded image (one of + TJ.PF_*)
        +
        Throws:
        +
        TJException
        +
      - +
      • decompress

        -
        public void decompress(java.awt.image.BufferedImage dstImage,
        -              int flags)
        -                throws TJException
        -
        Decompress the JPEG source image or decode the planar YUV source image - associated with this decompressor instance and output a packed-pixel - decompressed/decoded image to the given BufferedImage - instance. +
        @Deprecated
        +public void decompress​(int[] dstBuf,
        +                       int x,
        +                       int y,
        +                       int desiredWidth,
        +                       int stride,
        +                       int desiredHeight,
        +                       int pixelFormat,
        +                       int flags)
        +                throws TJException
        + +
        +
        Throws:
        +
        TJException
        +
        +
      • +
      + + + +
        +
      • +

        decompress8

        +
        public void decompress8​(java.awt.image.BufferedImage dstImage)
        +                 throws TJException
        +
        Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + source image associated with this decompressor instance and output an + 8-bit-per-sample packed-pixel decompressed/decoded image to the given + BufferedImage instance.

        NOTE: The destination image is fully recoverable if this method throws a - non-fatal TJException (unless - TJ.FLAG_STOPONWARNING is specified.)

        -
        Parameters:
        dstImage - a BufferedImage instance that will receive + non-fatal TJException (unless TJ.PARAM_STOPONWARNING + is set.) +
        +
        Parameters:
        +
        dstImage - a BufferedImage instance that will receive the packed-pixel decompressed/decoded image. If the source image is a - JPEG image, then the width and height of the BufferedImage - instance must match one of the scaled image sizes that the decompressor is - capable of generating from the JPEG image. If the source image is a YUV - image, then the width and height of the BufferedImage - instance must match the width and height of the YUV image.
        flags - the bitwise OR of one or more of - TJ.FLAG_*
        -
        Throws:
        -
        TJException
        + lossy JPEG image, then the width and height of the + BufferedImage instance must match the scaled JPEG width and + height (see setScalingFactor(), + TJScalingFactor.getScaled(), + getWidth(), and getHeight()) or the width and height of the + cropping region (see setCroppingRegion().) If + the source image is a YUV image or a lossless JPEG image, then the width + and height of the BufferedImage instance must match the width + and height of the source image.
        +
        Throws:
        +
        TJException
        +
      - + + + + +
        +
      • +

        decompress8

        +
        public java.awt.image.BufferedImage decompress8​(int bufferedImageType)
        +                                         throws TJException
        +
        Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + source image associated with this decompressor instance and return a + BufferedImage instance containing the 8-bit-per-sample + packed-pixel decompressed/decoded image.
        +
        +
        Parameters:
        +
        bufferedImageType - the image type of the BufferedImage instance that will be created (for instance, - BufferedImage.TYPE_INT_RGB)
        flags - the bitwise OR of one or more of - TJ.FLAG_*
        -
        Returns:
        a BufferedImage instance containing the packed-pixel - decompressed/decoded image.
        -
        Throws:
        -
        TJException
        + BufferedImage.TYPE_INT_RGB) +
        Returns:
        +
        a BufferedImage instance containing the + 8-bit-per-sample packed-pixel decompressed/decoded image.
        +
        Throws:
        +
        TJException
        +
      - + + + + +
      • close

        -
        public void close()
        -           throws TJException
        +
        public void close()
        +           throws TJException
        Free the native structures associated with this decompressor instance.
        -
        Specified by:
        -
        close in interface java.io.Closeable
        -
        Specified by:
        +
        Specified by:
        close in interface java.lang.AutoCloseable
        -
        Throws:
        -
        TJException
        +
        Specified by:
        +
        close in interface java.io.Closeable
        +
        Throws:
        +
        TJException
        +
      - +
      • finalize

        -
        protected void finalize()
        +
        protected void finalize()
                          throws java.lang.Throwable
        -
        Overrides:
        +
        Overrides:
        finalize in class java.lang.Object
        -
        Throws:
        -
        java.lang.Throwable
        +
        Throws:
        +
        java.lang.Throwable
        +
    +
    +
    + diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJException.html b/java/doc/org/libjpegturbo/turbojpeg/TJException.html index 66d73e740..5c2905d26 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJException.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJException.html @@ -1,9 +1,21 @@ - + + TJException + + + + + + + + + +var data = {"i0":10}; +var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],8:["t4","Concrete Methods"]}; +var altColor = "altColor"; +var rowColor = "rowColor"; +var tableTab = "tableTab"; +var activeTableTab = "activeTableTab"; +var pathtoroot = "../../../"; +var useModuleDirectories = true; +loadScripts(document, 'script'); +
    + +
    +
    -
    org.libjpegturbo.turbojpeg
    +

    Class TJException

    @@ -111,13 +146,15 @@

    Class TJException

  • All Implemented Interfaces:
    -
    java.io.Serializable
    +
    java.io.Serializable

    -
    -
    public class TJException
    +
    public class TJException
     extends java.io.IOException
    -
    See Also:
    Serialized Form
    +
    +
    See Also:
    +
    Serialized Form
    +
  • @@ -125,65 +162,76 @@

    Class TJException

    • +
        -
      • +
      • Constructor Summary

        - +
        - + + - + + - + + - + + - + + - + +
        Constructors 
        Constructor and DescriptionConstructorDescription
        TJException() TJException() 
        TJException(java.lang.String message) TJException​(java.lang.String message) 
        TJException(java.lang.String message, - int code) TJException​(java.lang.String message, + int code) 
        TJException(java.lang.String message, - java.lang.Throwable cause) TJException​(java.lang.String message, + java.lang.Throwable cause) 
        TJException(java.lang.Throwable cause) TJException​(java.lang.Throwable cause) 
      +
      +
        -
      • +
      • Method Summary

        - - +
        Methods 
        + - + + - + - +
        All Methods Instance Methods Concrete Methods 
        Modifier and TypeMethod and DescriptionMethodDescription
        intgetErrorCode() -
        Returns a code (one of TJ.ERR_*) indicating the severity of the +
        getErrorCode() +
        Returns a code (one of TJ.ERR_*) indicating the severity of the last error.
          -
        • +
        • Methods inherited from class java.lang.Throwable

          addSuppressed, fillInStackTrace, getCause, getLocalizedMessage, getMessage, getStackTrace, getSuppressed, initCause, printStackTrace, printStackTrace, printStackTrace, setStackTrace, toString
          -
        • +
        • Methods inherited from class java.lang.Object

          @@ -191,6 +239,7 @@

          Methods inherited from class java.lang.Object

      +
    @@ -198,12 +247,13 @@

    Methods inherited from class java.lang.Object

    • +
        -
      • +
      • Constructor Detail

        - +
          @@ -212,80 +262,91 @@

          TJException

          public TJException()
        - +
        • TJException

          -
          public TJException(java.lang.String message,
          -           java.lang.Throwable cause)
          +
          public TJException​(java.lang.String message,
          +                   java.lang.Throwable cause)
        - +
        • TJException

          -
          public TJException(java.lang.String message)
          +
          public TJException​(java.lang.String message)
        - +
        • TJException

          -
          public TJException(java.lang.String message,
          -           int code)
          +
          public TJException​(java.lang.String message,
          +                   int code)
        - +
        • TJException

          -
          public TJException(java.lang.Throwable cause)
          +
          public TJException​(java.lang.Throwable cause)
      +
      +
        -
      • +
      • Method Detail

        - +
        • getErrorCode

          -
          public int getErrorCode()
          -
          Returns a code (one of TJ.ERR_*) indicating the severity of the +
          public int getErrorCode()
          +
          Returns a code (one of TJ.ERR_*) indicating the severity of the last error.
          -
          Returns:
          a code (one of TJ.ERR_*) indicating the severity of the - last error.
          +
          +
          Returns:
          +
          a code (one of TJ.ERR_*) indicating the severity of the + last error.
          +
      +
    +
    + diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJScalingFactor.html b/java/doc/org/libjpegturbo/turbojpeg/TJScalingFactor.html index 4006baca5..caa5c5892 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJScalingFactor.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJScalingFactor.html @@ -1,9 +1,21 @@ - + + TJScalingFactor + + + + + + + + + +var data = {"i0":10,"i1":10,"i2":10,"i3":10,"i4":10}; +var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],8:["t4","Concrete Methods"]}; +var altColor = "altColor"; +var rowColor = "rowColor"; +var tableTab = "tableTab"; +var activeTableTab = "activeTableTab"; +var pathtoroot = "../../../"; +var useModuleDirectories = true; +loadScripts(document, 'script'); +
    + +
    +
    -
    org.libjpegturbo.turbojpeg
    +

    Class TJScalingFactor

    @@ -95,8 +130,7 @@

    Class TJScalingFactor


    • -
      -
      public class TJScalingFactor
      +
      public class TJScalingFactor
       extends java.lang.Object
      Fractional scaling factor
    • @@ -106,72 +140,83 @@

      Class TJScalingFactor

      • +
          -
        • +
        • Constructor Summary

          - +
          - + + - +
          Constructors 
          Constructor and DescriptionConstructorDescription
          TJScalingFactor(int num, - int denom) +TJScalingFactor​(int num, + int denom)
          Create a TurboJPEG scaling factor instance.
        +
        +
          -
        • +
        • Method Summary

          - - +
          Methods 
          + - + + - + - + - + - + - + - + - + - + - + - +
          All Methods Instance Methods Concrete Methods 
          Modifier and TypeMethod and DescriptionMethodDescription
          booleanequals(TJScalingFactor other) +equals​(TJScalingFactor other)
          Returns true or false, depending on whether this instance and other have the same numerator and denominator.
          intgetDenom() +getDenom()
          Returns denominator
          intgetNum() +getNum()
          Returns numerator
          intgetScaled(int dimension) +getScaled​(int dimension)
          Returns the scaled value of dimension.
          booleanisOne() +isOne()
          Returns true or false, depending on whether this instance is equal to 1/1.
            -
          • +
          • Methods inherited from class java.lang.Object

            @@ -179,6 +224,7 @@

            Methods inherited from class java.lang.Object

        +
    @@ -186,109 +232,139 @@

    Methods inherited from class java.lang.Object

    • +
        -
      • +
      • Constructor Detail

        - +
        • TJScalingFactor

          -
          public TJScalingFactor(int num,
          -               int denom)
          +
          public TJScalingFactor​(int num,
          +                       int denom)
          Create a TurboJPEG scaling factor instance.
          -
          Parameters:
          num - numerator
          denom - denominator
          +
          +
          Parameters:
          +
          num - numerator
          +
          denom - denominator
          +
      +
      +
        -
      • +
      • Method Detail

        - +
        • getNum

          -
          public int getNum()
          +
          public int getNum()
          Returns numerator
          -
          Returns:
          numerator
          +
          +
          Returns:
          +
          numerator
          +
        - +
        • getDenom

          -
          public int getDenom()
          +
          public int getDenom()
          Returns denominator
          -
          Returns:
          denominator
          +
          +
          Returns:
          +
          denominator
          +
        - +
        • getScaled

          -
          public int getScaled(int dimension)
          +
          public int getScaled​(int dimension)
          Returns the scaled value of dimension. This function performs the integer equivalent of ceil(dimension * scalingFactor).
          -
          Parameters:
          dimension - width or height to multiply by this scaling factor
          -
          Returns:
          the scaled value of dimension.
          +
          +
          Parameters:
          +
          dimension - width or height to multiply by this scaling factor
          +
          Returns:
          +
          the scaled value of dimension.
          +
        - +
        • equals

          -
          public boolean equals(TJScalingFactor other)
          +
          public boolean equals​(TJScalingFactor other)
          Returns true or false, depending on whether this instance and other have the same numerator and denominator.
          -
          Parameters:
          other - the scaling factor against which to compare this one
          -
          Returns:
          true or false, depending on whether this instance and - other have the same numerator and denominator.
          +
          +
          Parameters:
          +
          other - the scaling factor against which to compare this one
          +
          Returns:
          +
          true or false, depending on whether this instance and + other have the same numerator and denominator.
          +
        - +
        • isOne

          -
          public boolean isOne()
          +
          public boolean isOne()
          Returns true or false, depending on whether this instance is equal to 1/1.
          -
          Returns:
          true or false, depending on whether this instance is equal to - 1/1.
          +
          +
          Returns:
          +
          true or false, depending on whether this instance is equal to + 1/1.
          +
      +
    +
    + diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJTransform.html b/java/doc/org/libjpegturbo/turbojpeg/TJTransform.html index 40265efac..fbe0dd8b3 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJTransform.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJTransform.html @@ -1,9 +1,21 @@ - + + TJTransform + + + + + + + + + +var pathtoroot = "../../../"; +var useModuleDirectories = true; +loadScripts(document, 'script'); +
    + +
    +
    -
    org.libjpegturbo.turbojpeg
    +

    Class TJTransform

    @@ -111,14 +140,16 @@

    Class TJTransform

  • All Implemented Interfaces:
    -
    java.awt.Shape, java.io.Serializable, java.lang.Cloneable
    +
    java.awt.Shape, java.io.Serializable, java.lang.Cloneable

    -
    -
    public class TJTransform
    +
    public class TJTransform
     extends java.awt.Rectangle
    Lossless transform parameters
    -
    See Also:
    Serialized Form
    +
    +
    See Also:
    +
    Serialized Form
    +
  • @@ -126,13 +157,14 @@

    Class TJTransform

    • +
        -
      • +
      • Nested Class Summary

          -
        • +
        • Nested classes/interfaces inherited from class java.awt.geom.Rectangle2D

          @@ -140,157 +172,188 @@

          Nested classes/interfaces inherited from class java.awt.geom.Rectangle2

      +
      +
        -
      • +
      • Field Summary

        - +
        - + + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + - + - + - + - +
        Fields 
        Modifier and TypeField and DescriptionFieldDescription
        TJCustomFiltercf +TJCustomFiltercf
        Custom filter instance
        static intNUMOP +NUMOP
        The number of lossless transform operations
        intop -
        Transform operation (one of OP_*)
        +
        op +
        Transform operation (one of OP_*)
        static intOP_HFLIP +OP_HFLIP
        Flip (mirror) image horizontally.
        static intOP_NONE +OP_NONE
        Do not transform the position of the image pixels.
        static intOP_ROT180 +OP_ROT180
        Rotate image 180 degrees.
        static intOP_ROT270 +OP_ROT270
        Rotate image counter-clockwise by 90 degrees.
        static intOP_ROT90 +OP_ROT90
        Rotate image clockwise by 90 degrees.
        static intOP_TRANSPOSE +OP_TRANSPOSE
        Transpose image (flip/mirror along upper left to lower right axis).
        static intOP_TRANSVERSE +OP_TRANSVERSE
        Transverse transpose image (flip/mirror along upper right to lower left axis).
        static intOP_VFLIP +OP_VFLIP
        Flip (mirror) image vertically.
        static intOPT_ARITHMETIC +OPT_ARITHMETIC
        This option will enable arithmetic entropy coding in the JPEG image generated by this particular transform.
        static intOPT_COPYNONE -
        This option will prevent TJTransformer.transform() from copying any extra markers (including EXIF +
        OPT_COPYNONE +
        This option will prevent TJTransformer.transform() from copying any extra markers (including EXIF and ICC profile data) from the source image to the destination image.
        static intOPT_CROP +OPT_CROP
        This option will enable lossless cropping.
        static intOPT_GRAY +OPT_GRAY
        This option will discard the color data in the source image and produce a grayscale destination image.
        static intOPT_NOOUTPUT -
        This option will prevent TJTransformer.transform() from outputting a JPEG image for this +
        OPT_NOOUTPUT +
        This option will prevent TJTransformer.transform() from outputting a JPEG image for this particular transform.
        static intOPT_PERFECT -
        This option will cause TJTransformer.transform() to throw an exception if the transform is not - perfect.
        +
        OPT_OPTIMIZE +
        This option will enable optimized baseline entropy coding in the JPEG + image generated by this particular transform.
        static intOPT_PROGRESSIVE +OPT_PERFECT +
        This option will cause TJTransformer.transform() to throw an exception if the transform is not + perfect.
        +
        static intOPT_PROGRESSIVE
        This option will enable progressive entropy coding in the JPEG image generated by this particular transform.
        static intOPT_TRIM +OPT_TRIM
        This option will discard any partial MCU blocks that cannot be transformed.
        intoptions +options
        Transform options (bitwise OR of one or more of - OPT_*)
        + OPT_*)
          -
        • +
        • Fields inherited from class java.awt.Rectangle

          height, width, x, y
          -
        • +
        • Fields inherited from class java.awt.geom.Rectangle2D

          @@ -298,80 +361,88 @@

          Fields inherited from class java.awt.geom.Rectangle2D

      +
      +
      +
      +
        -
      • +
      • Method Summary

          -
        • +
        • Methods inherited from class java.awt.Rectangle

          add, add, add, contains, contains, contains, contains, createIntersection, createUnion, equals, getBounds, getBounds2D, getHeight, getLocation, getSize, getWidth, getX, getY, grow, inside, intersection, intersects, isEmpty, move, outcode, reshape, resize, setBounds, setBounds, setLocation, setLocation, setRect, setSize, setSize, toString, translate, union
          -
        • +
        • Methods inherited from class java.awt.geom.Rectangle2D

          add, add, add, contains, contains, getPathIterator, getPathIterator, hashCode, intersect, intersects, intersectsLine, intersectsLine, outcode, setFrame, setRect, union
          -
        • +
        • Methods inherited from class java.awt.geom.RectangularShape

          clone, contains, contains, getCenterX, getCenterY, getFrame, getMaxX, getMaxY, getMinX, getMinY, intersects, setFrame, setFrame, setFrameFromCenter, setFrameFromCenter, setFrameFromDiagonal, setFrameFromDiagonal
          -
        • +
        • Methods inherited from class java.lang.Object

          finalize, getClass, notify, notifyAll, wait, wait, wait
          -
        • +
        • Methods inherited from interface java.awt.Shape

          @@ -379,6 +450,7 @@

          Methods inherited from interface java.awt.Shape

      +
    @@ -386,12 +458,13 @@

    Methods inherited from interface java.awt.Shape

    • +
        -
      • +
      • Field Detail

        - + - + - + - + - + - + - + - + - + - +
        • OPT_PERFECT

          public static final int OPT_PERFECT
          -
          This option will cause TJTransformer.transform() to throw an exception if the transform is not +
          This option will cause TJTransformer.transform() to throw an exception if the transform is not perfect. Lossless transforms operate on MCU blocks, whose size depends on the level of chrominance subsampling used. If the image's width or height - is not evenly divisible by the MCU block size (see TJ.getMCUWidth(int) - and TJ.getMCUHeight(int)), then there will be partial MCU blocks on the - right and/or bottom edges. It is not possible to move these partial MCU - blocks to the top or left of the image, so any transform that would - require that is "imperfect." If this option is not specified, then any - partial MCU blocks that cannot be transformed will be left in place, which - will create odd-looking strips on the right or bottom edge of the image.
          -
          See Also:
          Constant Field Values
          + is not evenly divisible by the MCU block size (see TJ.getMCUWidth() and TJ.getMCUHeight()), then + there will be partial MCU blocks on the right and/or bottom edges. It is + not possible to move these partial MCU blocks to the top or left of the + image, so any transform that would require that is "imperfect." If this + option is not specified, then any partial MCU blocks that cannot be + transformed will be left in place, which will create odd-looking strips on + the right or bottom edge of the image.
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
          @@ -534,10 +637,13 @@

          OPT_TRIM

          public static final int OPT_TRIM
          This option will discard any partial MCU blocks that cannot be transformed.
          -
          See Also:
          Constant Field Values
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - + - +
          @@ -557,24 +666,30 @@

          OPT_GRAY

          public static final int OPT_GRAY
          This option will discard the color data in the source image and produce a grayscale destination image.
          -
          See Also:
          Constant Field Values
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - + - +
          @@ -584,24 +699,31 @@

          OPT_PROGRESSIVE

          This option will enable progressive entropy coding in the JPEG image generated by this particular transform. Progressive entropy coding will generally improve compression relative to baseline entropy coding (the - default), but it will reduce decompression performance considerably. Can - be combined with OPT_ARITHMETIC.
          -
          See Also:
          Constant Field Values
          + default), but it will reduce decompression performance considerably. + Implies OPT_OPTIMIZE. Can be combined with + OPT_ARITHMETIC. +
          +
          See Also:
          +
          Constant Field Values
          +
        - + - +
          @@ -612,21 +734,40 @@

          OPT_ARITHMETIC

          generated by this particular transform. Arithmetic entropy coding will generally improve compression relative to Huffman entropy coding (the default), but it will reduce decompression performance considerably. Can - be combined with OPT_PROGRESSIVE. -
          See Also:
          Constant Field Values
          + be combined with OPT_PROGRESSIVE. +
          +
          See Also:
          +
          Constant Field Values
          +
          + +
        + + + +
          +
        • +

          OPT_OPTIMIZE

          +
          public static final int OPT_OPTIMIZE
          +
          This option will enable optimized baseline entropy coding in the JPEG + image generated by this particular transform. Optimized baseline entropy + coding will improve compression slightly (generally 5% or less.)
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - +
        • op

          public int op
          -
          Transform operation (one of OP_*)
          +
          Transform operation (one of OP_*)
        - +
          @@ -634,28 +775,30 @@

          op

          options

          public int options
          Transform options (bitwise OR of one or more of - OPT_*)
          + OPT_*)
        - +
      +
      +
        -
      • +
      • Constructor Detail

        - +
          @@ -665,62 +808,86 @@

          TJTransform

          Create a new lossless transform instance.
        - +
        • TJTransform

          -
          public TJTransform(int x,
          -           int y,
          -           int w,
          -           int h,
          -           int op,
          -           int options,
          -           TJCustomFilter cf)
          +
          public TJTransform​(int x,
          +                   int y,
          +                   int w,
          +                   int h,
          +                   int op,
          +                   int options,
          +                   TJCustomFilter cf)
          Create a new lossless transform instance with the given parameters.
          -
          Parameters:
          x - the left boundary of the cropping region. This must be evenly - divisible by the MCU block width (see TJ.getMCUWidth(int))
          y - the upper boundary of the cropping region. This must be evenly - divisible by the MCU block height (see TJ.getMCUHeight(int))
          w - the width of the cropping region. Setting this to 0 is the +
          +
          Parameters:
          +
          x - the left boundary of the cropping region. This must be evenly + divisible by the MCU block width (see TJ.getMCUWidth())
          +
          y - the upper boundary of the cropping region. This must be evenly + divisible by the MCU block height (see TJ.getMCUHeight())
          +
          w - the width of the cropping region. Setting this to 0 is the equivalent of setting it to (width of the source JPEG image - - x).
          h - the height of the cropping region. Setting this to 0 is the + x).
          +
          h - the height of the cropping region. Setting this to 0 is the equivalent of setting it to (height of the source JPEG image - - y).
          op - one of the transform operations (OP_*)
          options - the bitwise OR of one or more of the transform options - (OPT_*)
          cf - an instance of an object that implements the TJCustomFilter interface, or null if no custom filter is needed
          + y).
          +
          op - one of the transform operations (OP_*)
          +
          options - the bitwise OR of one or more of the transform options + (OPT_*)
          +
          cf - an instance of an object that implements the + TJCustomFilter interface, or null if no custom filter is needed
          +
        - +
        • TJTransform

          -
          public TJTransform(java.awt.Rectangle r,
          -           int op,
          -           int options,
          -           TJCustomFilter cf)
          +
          public TJTransform​(java.awt.Rectangle r,
          +                   int op,
          +                   int options,
          +                   TJCustomFilter cf)
          Create a new lossless transform instance with the given parameters.
          -
          Parameters:
          r - a Rectangle instance that specifies the cropping - region. See TJTransform(int, int, int, int, int, int, TJCustomFilter) for more - detail.
          op - one of the transform operations (OP_*)
          options - the bitwise OR of one or more of the transform options - (OPT_*)
          cf - an instance of an object that implements the TJCustomFilter interface, or null if no custom filter is needed
          +
          +
          Parameters:
          +
          r - a java.awt.Rectangle instance that specifies the + cropping region. See + TJTransform(int, int, int, int, int, int, TJCustomFilter) for + more details.
          +
          op - one of the transform operations (OP_*)
          +
          options - the bitwise OR of one or more of the transform options + (OPT_*)
          +
          cf - an instance of an object that implements the + TJCustomFilter interface, or null if no custom filter is needed
          +
      +
    +
    + diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJTransformer.html b/java/doc/org/libjpegturbo/turbojpeg/TJTransformer.html index a95dcca73..83836eac8 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJTransformer.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJTransformer.html @@ -1,9 +1,21 @@ - + + TJTransformer + + + + + + + + + +var data = {"i0":10,"i1":10,"i2":42,"i3":10,"i4":42}; +var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],8:["t4","Concrete Methods"],32:["t6","Deprecated Methods"]}; +var altColor = "altColor"; +var rowColor = "rowColor"; +var tableTab = "tableTab"; +var activeTableTab = "activeTableTab"; +var pathtoroot = "../../../"; +var useModuleDirectories = true; +loadScripts(document, 'script'); +
    + +
    +
    -
    org.libjpegturbo.turbojpeg
    +

    Class TJTransformer

    @@ -87,7 +122,7 @@

    Class TJTransformer

  • java.lang.Object
  • +
    + diff --git a/java/doc/org/libjpegturbo/turbojpeg/YUVImage.html b/java/doc/org/libjpegturbo/turbojpeg/YUVImage.html index b08fcb3d6..071b16ce4 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/YUVImage.html +++ b/java/doc/org/libjpegturbo/turbojpeg/YUVImage.html @@ -1,9 +1,21 @@ - + + YUVImage + + + + + + + + + +var data = {"i0":10,"i1":10,"i2":10,"i3":10,"i4":10,"i5":10,"i6":10,"i7":10,"i8":10,"i9":10,"i10":10}; +var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],8:["t4","Concrete Methods"]}; +var altColor = "altColor"; +var rowColor = "rowColor"; +var tableTab = "tableTab"; +var activeTableTab = "activeTableTab"; +var pathtoroot = "../../../"; +var useModuleDirectories = true; +loadScripts(document, 'script'); +
    + +
    +
    -
    org.libjpegturbo.turbojpeg
    +

    Class YUVImage

    @@ -95,8 +130,7 @@

    Class YUVImage


    • -
      -
      public class YUVImage
      +
      public class YUVImage
       extends java.lang.Object
      This class encapsulates a planar YUV image and the metadata associated with it. The TurboJPEG API allows both the JPEG compression and @@ -138,99 +172,58 @@

      Class YUVImage

      • - - +
          -
        • +
        • Constructor Summary

          - +
          - + + - + - + - + - + @@ -238,100 +231,114 @@

          Constructor Summary

          Constructors 
          Constructor and DescriptionConstructorDescription
          YUVImage(byte[][] planes, +YUVImage​(byte[][] planes, int[] offsets, int width, int[] strides, int height, - int subsamp) + int subsamp)
          Create a new YUVImage instance from a set of existing image planes.
          YUVImage(byte[] yuvImage, +YUVImage​(byte[] yuvImage, int width, int align, int height, - int subsamp) + int subsamp)
          Create a new YUVImage instance from an existing unified buffer.
          YUVImage(int width, +YUVImage​(int width, int[] strides, int height, - int subsamp) + int subsamp)
          Create a new YUVImage instance backed by separate image planes, and allocate memory for the image planes.
          YUVImage(int width, +YUVImage​(int width, int align, int height, - int subsamp) + int subsamp)
          Create a new YUVImage instance backed by a unified buffer, and allocate memory for the buffer.
        +
        +
          -
        • +
        • Method Summary

          - - +
          Methods 
          + - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
          All Methods Instance Methods Concrete Methods 
          Modifier and TypeMethod and DescriptionMethodDescription
          byte[]getBuf() +getBuf()
          Returns the YUV buffer (if this image is stored in a unified buffer rather than separate image planes.)
          intgetHeight() +getHeight()
          Returns the height of the YUV image (or subregion.)
          int[]getOffsets() +getOffsets()
          Returns the offsets (in bytes) of each plane within the planes of a larger YUV image.
          intgetPad() +getPad()
          Returns the row alignment (in bytes) of the YUV buffer (if this image is stored in a unified buffer rather than separate image planes.)
          byte[][]getPlanes() +getPlanes()
          Returns the YUV image planes.
          intgetSize() +getSize()
          Returns the size (in bytes) of the YUV buffer (if this image is stored in a unified buffer rather than separate image planes.)
          int[]getStrides() +getStrides()
          Returns the number of bytes per row of each plane in the YUV image.
          intgetSubsamp() +getSubsamp()
          Returns the level of chrominance subsampling used in the YUV image.
          intgetWidth() +getWidth()
          Returns the width of the YUV image (or subregion.)
          voidsetBuf(byte[][] planes, +setBuf​(byte[][] planes, int[] offsets, int width, int[] strides, int height, - int subsamp) + int subsamp)
          Assign a set of image planes to this YUVImage instance.
          voidsetBuf(byte[] yuvImage, +setBuf​(byte[] yuvImage, int width, int align, int height, - int subsamp) + int subsamp)
          Assign a unified buffer to this YUVImage instance.
            -
          • +
          • Methods inherited from class java.lang.Object

            @@ -339,384 +346,383 @@

            Methods inherited from class java.lang.Object

        +
      • - -
          -
        • - - -

          Field Detail

          - - - -
            -
          • -

            handle

            -
            protected long handle
            -
          • -
          - - - -
            -
          • -

            yuvPlanes

            -
            protected byte[][] yuvPlanes
            -
          • -
          - - - -
            -
          • -

            yuvOffsets

            -
            protected int[] yuvOffsets
            -
          • -
          - - - -
            -
          • -

            yuvStrides

            -
            protected int[] yuvStrides
            -
          • -
          - - - -
            -
          • -

            yuvAlign

            -
            protected int yuvAlign
            -
          • -
          - - - -
            -
          • -

            yuvWidth

            -
            protected int yuvWidth
            -
          • -
          - - - -
            -
          • -

            yuvHeight

            -
            protected int yuvHeight
            -
          • -
          - - - -
            -
          • -

            yuvSubsamp

            -
            protected int yuvSubsamp
            -
          • -
          -
        • -
        +
          -
        • +
        • Constructor Detail

          - +
          • YUVImage

            -
            public YUVImage(int width,
            -        int[] strides,
            -        int height,
            -        int subsamp)
            +
            public YUVImage​(int width,
            +                int[] strides,
            +                int height,
            +                int subsamp)
            Create a new YUVImage instance backed by separate image planes, and allocate memory for the image planes.
            -
            Parameters:
            width - width (in pixels) of the YUV image
            strides - an array of integers, each specifying the number of bytes +
            +
            Parameters:
            +
            width - width (in pixels) of the YUV image
            +
            strides - an array of integers, each specifying the number of bytes per row in the corresponding plane of the YUV image. Setting the stride for any plane to 0 is the same as setting it to the plane width (see - above.) If strides is null, then the + above.) If strides is null, then the strides for all planes will be set to their respective plane widths. When using this constructor, the stride for each plane must be equal to or - greater than the plane width.
            height - height (in pixels) of the YUV image
            subsamp - the level of chrominance subsampling to be used in the YUV - image (one of TJ.SAMP_*)
            + greater than the plane width.
            +
            height - height (in pixels) of the YUV image
            +
            subsamp - the level of chrominance subsampling to be used in the YUV + image (one of TJ.SAMP_*)
            +
          - +
          • YUVImage

            -
            public YUVImage(int width,
            -        int align,
            -        int height,
            -        int subsamp)
            +
            public YUVImage​(int width,
            +                int align,
            +                int height,
            +                int subsamp)
            Create a new YUVImage instance backed by a unified buffer, and allocate memory for the buffer.
            -
            Parameters:
            width - width (in pixels) of the YUV image
            align - row alignment (in bytes) of the YUV image (must be a power of +
            +
            Parameters:
            +
            width - width (in pixels) of the YUV image
            +
            align - row alignment (in bytes) of the YUV image (must be a power of 2.) Setting this parameter to n specifies that each row in each plane of the YUV image will be padded to the nearest multiple of n bytes - (1 = unpadded.)
            height - height (in pixels) of the YUV image
            subsamp - the level of chrominance subsampling to be used in the YUV - image (one of TJ.SAMP_*)
            + (1 = unpadded.)
            +
            height - height (in pixels) of the YUV image
            +
            subsamp - the level of chrominance subsampling to be used in the YUV + image (one of TJ.SAMP_*)
            +
          - +
          • YUVImage

            -
            public YUVImage(byte[][] planes,
            -        int[] offsets,
            -        int width,
            -        int[] strides,
            -        int height,
            -        int subsamp)
            +
            public YUVImage​(byte[][] planes,
            +                int[] offsets,
            +                int width,
            +                int[] strides,
            +                int height,
            +                int subsamp)
            Create a new YUVImage instance from a set of existing image planes.
            -
            Parameters:
            planes - an array of buffers representing the Y, U (Cb), and V (Cr) +
            +
            Parameters:
            +
            planes - an array of buffers representing the Y, U (Cb), and V (Cr) image planes (or just the Y plane, if the image is grayscale.) These planes can be contiguous or non-contiguous in memory. Plane - i should be at least offsets[i] + - TJ.planeSizeYUV(i, width, strides[i], height, subsamp) - bytes in size.
            offsets - If this YUVImage instance represents a + i should be at least offsets[i] + + TJ.planeSizeYUV(i, width, strides[i], height, subsamp) + bytes in size.
            +
            offsets - If this YUVImage instance represents a subregion of a larger image, then offsets[i] specifies the offset (in bytes) of the subregion within plane i of the larger image. Setting this to null is the same as setting the offsets for - all planes to 0.
            width - width (in pixels) of the new YUV image (or subregion)
            strides - an array of integers, each specifying the number of bytes + all planes to 0.
            +
            width - width (in pixels) of the new YUV image (or subregion)
            +
            strides - an array of integers, each specifying the number of bytes per row in the corresponding plane of the YUV image. Setting the stride for any plane to 0 is the same as setting it to the plane width (see - above.) If strides is null, then the + above.) If strides is null, then the strides for all planes will be set to their respective plane widths. You can adjust the strides in order to add an arbitrary amount of row padding to each plane or to specify that this YUVImage instance is a subregion of a larger image (in which case, strides[i] should - be set to the plane width of plane i in the larger image.)
            height - height (in pixels) of the new YUV image (or subregion)
            subsamp - the level of chrominance subsampling used in the YUV - image (one of TJ.SAMP_*)
            + be set to the plane width of plane i in the larger image.)
            +
            height - height (in pixels) of the new YUV image (or subregion)
            +
            subsamp - the level of chrominance subsampling used in the YUV + image (one of TJ.SAMP_*)
            +
          - +
          • YUVImage

            -
            public YUVImage(byte[] yuvImage,
            -        int width,
            -        int align,
            -        int height,
            -        int subsamp)
            +
            public YUVImage​(byte[] yuvImage,
            +                int width,
            +                int align,
            +                int height,
            +                int subsamp)
            Create a new YUVImage instance from an existing unified buffer.
            -
            Parameters:
            yuvImage - buffer that contains or will receive a unified planar YUV - image. Use TJ.bufSizeYUV(int, int, int, int) to determine the minimum size for this - buffer. The Y, U (Cb), and V (Cr) image planes are stored sequentially in - the buffer. (See above for a description of the image - format.)
            width - width (in pixels) of the YUV image
            align - row alignment (in bytes) of the YUV image (must be a power of +
            +
            Parameters:
            +
            yuvImage - buffer that contains or will receive a unified planar YUV + image. Use TJ.bufSizeYUV() to determine the minimum + size for this buffer. The Y, U (Cb), and V (Cr) image planes are stored + sequentially in the buffer. (See above for a description + of the image format.)
            +
            width - width (in pixels) of the YUV image
            +
            align - row alignment (in bytes) of the YUV image (must be a power of 2.) Setting this parameter to n specifies that each row in each plane of the YUV image will be padded to the nearest multiple of n bytes - (1 = unpadded.)
            height - height (in pixels) of the YUV image
            subsamp - the level of chrominance subsampling used in the YUV - image (one of TJ.SAMP_*)
            + (1 = unpadded.)
            +
            height - height (in pixels) of the YUV image
            +
            subsamp - the level of chrominance subsampling used in the YUV + image (one of TJ.SAMP_*)
            +
        +
        +
          -
        • +
        • Method Detail

          - +
          • setBuf

            -
            public void setBuf(byte[][] planes,
            -          int[] offsets,
            -          int width,
            -          int[] strides,
            -          int height,
            -          int subsamp)
            +
            public void setBuf​(byte[][] planes,
            +                   int[] offsets,
            +                   int width,
            +                   int[] strides,
            +                   int height,
            +                   int subsamp)
            Assign a set of image planes to this YUVImage instance.
            -
            Parameters:
            planes - an array of buffers representing the Y, U (Cb), and V (Cr) +
            +
            Parameters:
            +
            planes - an array of buffers representing the Y, U (Cb), and V (Cr) image planes (or just the Y plane, if the image is grayscale.) These planes can be contiguous or non-contiguous in memory. Plane - i should be at least offsets[i] + - TJ.planeSizeYUV(i, width, strides[i], height, subsamp) - bytes in size.
            offsets - If this YUVImage instance represents a + i should be at least offsets[i] + + TJ.planeSizeYUV(i, width, strides[i], height, subsamp) + bytes in size.
            +
            offsets - If this YUVImage instance represents a subregion of a larger image, then offsets[i] specifies the offset (in bytes) of the subregion within plane i of the larger image. Setting this to null is the same as setting the offsets for - all planes to 0.
            width - width (in pixels) of the YUV image (or subregion)
            strides - an array of integers, each specifying the number of bytes + all planes to 0.
            +
            width - width (in pixels) of the YUV image (or subregion)
            +
            strides - an array of integers, each specifying the number of bytes per row in the corresponding plane of the YUV image. Setting the stride for any plane to 0 is the same as setting it to the plane width (see - above.) If strides is null, then the + above.) If strides is null, then the strides for all planes will be set to their respective plane widths. You can adjust the strides in order to add an arbitrary amount of row padding to each plane or to specify that this YUVImage instance is a subregion of a larger image (in which case, strides[i] should - be set to the plane width of plane i in the larger image.)
            height - height (in pixels) of the YUV image (or subregion)
            subsamp - the level of chrominance subsampling used in the YUV - image (one of TJ.SAMP_*)
            + be set to the plane width of plane i in the larger image.)
            +
            height - height (in pixels) of the YUV image (or subregion)
            +
            subsamp - the level of chrominance subsampling used in the YUV + image (one of TJ.SAMP_*)
            +
          - +
          • setBuf

            -
            public void setBuf(byte[] yuvImage,
            -          int width,
            -          int align,
            -          int height,
            -          int subsamp)
            +
            public void setBuf​(byte[] yuvImage,
            +                   int width,
            +                   int align,
            +                   int height,
            +                   int subsamp)
            Assign a unified buffer to this YUVImage instance.
            -
            Parameters:
            yuvImage - buffer that contains or will receive a unified planar YUV - image. Use TJ.bufSizeYUV(int, int, int, int) to determine the minimum size for this - buffer. The Y, U (Cb), and V (Cr) image planes are stored sequentially in - the buffer. (See above for a description of the image - format.)
            width - width (in pixels) of the YUV image
            align - row alignment (in bytes) of the YUV image (must be a power of +
            +
            Parameters:
            +
            yuvImage - buffer that contains or will receive a unified planar YUV + image. Use TJ.bufSizeYUV() to determine the minimum + size for this buffer. The Y, U (Cb), and V (Cr) image planes are stored + sequentially in the buffer. (See above for a description + of the image format.)
            +
            width - width (in pixels) of the YUV image
            +
            align - row alignment (in bytes) of the YUV image (must be a power of 2.) Setting this parameter to n specifies that each row in each plane of the YUV image will be padded to the nearest multiple of n bytes - (1 = unpadded.)
            height - height (in pixels) of the YUV image
            subsamp - the level of chrominance subsampling used in the YUV - image (one of TJ.SAMP_*)
            + (1 = unpadded.)
            +
            height - height (in pixels) of the YUV image
            +
            subsamp - the level of chrominance subsampling used in the YUV + image (one of TJ.SAMP_*)
            +
          - +
          • getWidth

            -
            public int getWidth()
            +
            public int getWidth()
            Returns the width of the YUV image (or subregion.)
            -
            Returns:
            the width of the YUV image (or subregion)
            +
            +
            Returns:
            +
            the width of the YUV image (or subregion)
            +
          - +
          • getHeight

            -
            public int getHeight()
            +
            public int getHeight()
            Returns the height of the YUV image (or subregion.)
            -
            Returns:
            the height of the YUV image (or subregion)
            +
            +
            Returns:
            +
            the height of the YUV image (or subregion)
            +
          - +
          • getPad

            -
            public int getPad()
            +
            public int getPad()
            Returns the row alignment (in bytes) of the YUV buffer (if this image is stored in a unified buffer rather than separate image planes.)
            -
            Returns:
            the row alignment of the YUV buffer
            +
            +
            Returns:
            +
            the row alignment of the YUV buffer
            +
          - +
          • getStrides

            -
            public int[] getStrides()
            +
            public int[] getStrides()
            Returns the number of bytes per row of each plane in the YUV image.
            -
            Returns:
            the number of bytes per row of each plane in the YUV image
            +
            +
            Returns:
            +
            the number of bytes per row of each plane in the YUV image
            +
          - +
          • getOffsets

            -
            public int[] getOffsets()
            +
            public int[] getOffsets()
            Returns the offsets (in bytes) of each plane within the planes of a larger YUV image.
            -
            Returns:
            the offsets (in bytes) of each plane within the planes of a larger - YUV image
            +
            +
            Returns:
            +
            the offsets (in bytes) of each plane within the planes of a larger + YUV image
            +
          - +
          • getSubsamp

            -
            public int getSubsamp()
            +
            public int getSubsamp()
            Returns the level of chrominance subsampling used in the YUV image. See - TJ.SAMP_*.
            -
            Returns:
            the level of chrominance subsampling used in the YUV image
            + TJ.SAMP_*.
      +
      +
      Returns:
      +
      the level of chrominance subsampling used in the YUV image
      +
    - +
    • getPlanes

      -
      public byte[][] getPlanes()
      +
      public byte[][] getPlanes()
      Returns the YUV image planes. If the image is stored in a unified buffer, then all image planes will point to that buffer.
      -
      Returns:
      the YUV image planes
      +
      +
      Returns:
      +
      the YUV image planes
      +
    - +
    • getBuf

      -
      public byte[] getBuf()
      +
      public byte[] getBuf()
      Returns the YUV buffer (if this image is stored in a unified buffer rather than separate image planes.)
      -
      Returns:
      the YUV buffer
      +
      +
      Returns:
      +
      the YUV buffer
      +
    - +
    • getSize

      -
      public int getSize()
      +
      public int getSize()
      Returns the size (in bytes) of the YUV buffer (if this image is stored in a unified buffer rather than separate image planes.)
      -
      Returns:
      the size (in bytes) of the YUV buffer
      +
      +
      Returns:
      +
      the size (in bytes) of the YUV buffer
      +
    +
    +
    + diff --git a/java/doc/org/libjpegturbo/turbojpeg/package-frame.html b/java/doc/org/libjpegturbo/turbojpeg/package-frame.html deleted file mode 100644 index 08a8bf83e..000000000 --- a/java/doc/org/libjpegturbo/turbojpeg/package-frame.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - -org.libjpegturbo.turbojpeg - - - -

    org.libjpegturbo.turbojpeg

    - - - diff --git a/java/doc/org/libjpegturbo/turbojpeg/package-summary.html b/java/doc/org/libjpegturbo/turbojpeg/package-summary.html index 89dbe05b1..0a027c3ae 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/package-summary.html +++ b/java/doc/org/libjpegturbo/turbojpeg/package-summary.html @@ -1,9 +1,21 @@ - + + org.libjpegturbo.turbojpeg + + + + + + + + + +var pathtoroot = "../../../"; +var useModuleDirectories = true; +loadScripts(document, 'script'); +
    + +
    +

    Package org.libjpegturbo.turbojpeg

    • - +
      @@ -76,7 +105,7 @@

      Package org.libjpegturbo.turbojpeg

      - + @@ -85,7 +114,7 @@

      Package org.libjpegturbo.turbojpeg

      Interface Summary 
      Interface
      TJCustomFilterTJCustomFilter
      Custom filter callback interface
    • - +
      @@ -93,43 +122,43 @@

      Package org.libjpegturbo.turbojpeg

      - + - + - + - + - + - + - +
      Class Summary 
      Class
      TJTJ
      TurboJPEG utility class (cannot be instantiated)
      TJCompressorTJCompressor
      TurboJPEG compressor
      TJDecompressorTJDecompressor
      TurboJPEG decompressor
      TJScalingFactorTJScalingFactor
      Fractional scaling factor
      TJTransformTJTransform
      Lossless transform parameters
      TJTransformerTJTransformer
      TurboJPEG lossless transformer
      YUVImageYUVImage
      This class encapsulates a planar YUV image and the metadata associated with it.
      @@ -139,7 +168,7 @@

      Package org.libjpegturbo.turbojpeg

    • - +
      @@ -147,7 +176,7 @@

      Package org.libjpegturbo.turbojpeg

      - + @@ -155,14 +184,19 @@

      Package org.libjpegturbo.turbojpeg

      + + diff --git a/java/doc/org/libjpegturbo/turbojpeg/package-tree.html b/java/doc/org/libjpegturbo/turbojpeg/package-tree.html index 5f0f8c3ee..ca8d36a8f 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/package-tree.html +++ b/java/doc/org/libjpegturbo/turbojpeg/package-tree.html @@ -1,9 +1,21 @@ - + +org.libjpegturbo.turbojpeg Class Hierarchy + + + + + + + + + +var pathtoroot = "../../../"; +var useModuleDirectories = true; +loadScripts(document, 'script'); +
      + +
      +

      Hierarchy For Package org.libjpegturbo.turbojpeg

      +

      Class Hierarchy

        -
      • java.lang.Object +
      • java.lang.Object
          -
        • java.awt.geom.RectangularShape (implements java.lang.Cloneable, java.awt.Shape) +
        • java.awt.geom.RectangularShape (implements java.lang.Cloneable, java.awt.Shape)
            -
          • java.awt.geom.Rectangle2D +
          • java.awt.geom.Rectangle2D
              -
            • java.awt.Rectangle (implements java.io.Serializable, java.awt.Shape) +
            • java.awt.Rectangle (implements java.io.Serializable, java.awt.Shape)
        • -
        • java.lang.Throwable (implements java.io.Serializable) +
        • java.lang.Throwable (implements java.io.Serializable)
            -
          • java.lang.Exception +
          • java.lang.Exception
              -
            • java.io.IOException +
            • java.io.IOException
        • -
        • org.libjpegturbo.turbojpeg.TJ
        • -
        • org.libjpegturbo.turbojpeg.TJCompressor (implements java.io.Closeable)
        • -
        • org.libjpegturbo.turbojpeg.TJDecompressor (implements java.io.Closeable) +
        • org.libjpegturbo.turbojpeg.TJ
        • +
        • org.libjpegturbo.turbojpeg.TJCompressor (implements java.io.Closeable)
        • +
        • org.libjpegturbo.turbojpeg.TJDecompressor (implements java.io.Closeable)
        • -
        • org.libjpegturbo.turbojpeg.TJScalingFactor
        • -
        • org.libjpegturbo.turbojpeg.YUVImage
        • +
        • org.libjpegturbo.turbojpeg.TJScalingFactor
        • +
        • org.libjpegturbo.turbojpeg.YUVImage
      +
      +

      Interface Hierarchy

      +
      +
      + diff --git a/java/doc/overview-tree.html b/java/doc/overview-tree.html index b6599954a..d2d6d53e5 100644 --- a/java/doc/overview-tree.html +++ b/java/doc/overview-tree.html @@ -1,9 +1,21 @@ - + +Class Hierarchy + + + + + + + + + +var pathtoroot = "./"; +var useModuleDirectories = true; +loadScripts(document, 'script'); +
      + +
      +

      Hierarchy For All Packages

      -Package Hierarchies: +Package Hierarchies:
      +

      Class Hierarchy

        -
      • java.lang.Object +
      • java.lang.Object
          -
        • java.awt.geom.RectangularShape (implements java.lang.Cloneable, java.awt.Shape) +
        • java.awt.geom.RectangularShape (implements java.lang.Cloneable, java.awt.Shape)
            -
          • java.awt.geom.Rectangle2D +
          • java.awt.geom.Rectangle2D
              -
            • java.awt.Rectangle (implements java.io.Serializable, java.awt.Shape) +
            • java.awt.Rectangle (implements java.io.Serializable, java.awt.Shape)
        • -
        • java.lang.Throwable (implements java.io.Serializable) +
        • java.lang.Throwable (implements java.io.Serializable)
            -
          • java.lang.Exception +
          • java.lang.Exception
              -
            • java.io.IOException +
            • java.io.IOException
        • -
        • org.libjpegturbo.turbojpeg.TJ
        • -
        • org.libjpegturbo.turbojpeg.TJCompressor (implements java.io.Closeable)
        • -
        • org.libjpegturbo.turbojpeg.TJDecompressor (implements java.io.Closeable) +
        • org.libjpegturbo.turbojpeg.TJ
        • +
        • org.libjpegturbo.turbojpeg.TJCompressor (implements java.io.Closeable)
        • +
        • org.libjpegturbo.turbojpeg.TJDecompressor (implements java.io.Closeable)
        • -
        • org.libjpegturbo.turbojpeg.TJScalingFactor
        • -
        • org.libjpegturbo.turbojpeg.YUVImage
        • +
        • org.libjpegturbo.turbojpeg.TJScalingFactor
        • +
        • org.libjpegturbo.turbojpeg.YUVImage
      +
      +

      Interface Hierarchy

      +
      +
      + diff --git a/java/doc/package-search-index.js b/java/doc/package-search-index.js new file mode 100644 index 000000000..7f0700b4e --- /dev/null +++ b/java/doc/package-search-index.js @@ -0,0 +1 @@ +packageSearchIndex = [{"l":"All Packages","url":"allpackages-index.html"},{"l":"org.libjpegturbo.turbojpeg"}] \ No newline at end of file diff --git a/java/doc/package-search-index.zip b/java/doc/package-search-index.zip new file mode 100644 index 0000000000000000000000000000000000000000..453ee4a263aca02b3e9117b9ed141044b624b466 GIT binary patch literal 237 zcmWIWW@Zs#;Nak3XrFBn#()GQf$W0BR3-ORj{)C#?<;{3esbz!Fh zHw6`)IT^^|;d9Q<$6q(>QlOSD6Q|m7U1!;dV@+vBxl2xiu^{M;P*CM0I zo4A55dwX?H`tNu9Z*~qf&7g0dK-(gKIKZ2cNrVC6B4jy`i%zcR^uxNo<2iYk@pjY)*5FJz8x~bc{)B zfk z+1T6M-s9WdW8dcJ-wO*3@9+W*5AY543-j^$^!EPz_4eHZ2#>)41`h@dc!2OAgN6$a zCS2I?;lqgx6IR4nkpTe;1RN0f=zxMq2O=q`94V5d$&e>Unta)^<;;^G3>e7yp=ZvW z6DIW3xpSvaogXF?_4%`@(V;s}NR^5J!3hrtJV@1QRV&r5S*L!zYE|rss${iFkg&!? zTN5V#)~=bmMorwgZsEpdOE)iExo+FO-8;8Kga{=HbSQCnF=E6W3?o*|ID%uwi5**> zJXy127Y9m+=HQ|PhXWi+xNwoWv}n_%Pq%(e+H~mGqhq5kv4Mo|-n~g|7!F*xZ{xv< zCpXS~dGg^IGK?4@J-T%b(XnUHFul6n<@2&4)zzyO2) z3Q8`i0+UKY*`$}e9mmp;tg*))`|PsK1|hAo%u0K$vDwm4gaSkm0j{`26k#qAKmbuhxZ#cquDR>B zD{s8+&TH-uNg$C#68QG}1HMBHfrP&L@@w$F_!itRzXdCN@V|LDAu%3!IDtq1#1UV7 z#1RxvT=B(DWbCoU5l=ia$Pp`Hgb_?Mp@hmtxZDI2N-)v#$}PXVvdm1d>@v(v`0TUJ zF)Pu89(q`zv=w^nVTIF3@3BYIPA}c`(@ZCAwbNBEt@PDUKe5CTR8aB66IE1!w%Amt zy+jpcn~k>GZpVFg+H6x{_uOksvBlq0OyT$6TyQZ37k(cOxZr|JEx1sGm<(M9gH z-~PMqyn|tT=))UN`|-FFFUA#KToK0fUOaz=7}Z~KeHhVC&%O27cTfHQ^WBU8z4p&T zp#>D|V}XShTD;Hx745Iz{`>K-Z$A|7!*Boo{mY;G21vjH8t{M!OrQc6$iN0V@PQDF zpadsK!3tXNf*8!81~qnXWuHZ)kytd=_y+ADWvw31ouV;CdZ#ya*(l7-A-C-Y^+iit8O zBy3*`Ls$|5Hn4m_^I^|C7{m7EFn|5vTk;|oywIgCc9Bb*=L+Y$)M>9GC<|HGs@6NB zHLY%03!dDf=eDRt2O6lVSFRcsuWZEwU?=z$CZ0W?#VJfdN>HG(l%oKpyiftJc|Y)xkjSJYCrQal-0PC~()T9xwF!Jf zVi1UA#3BBbh(i8r5&v#Pz!cF41KjbCc?4u2@@Q~oKLirt2TM30;y6b+zyX2`Yl9u; z`0$3;v0-YUp&7JoRsvExf%rEN>jUL}qZ_~k#FbE+Q;{`;0FZwVNX2n-^JoI; zP;4#$8DIy*Yk-P>VN(DUKmPse7mx+ExD4O|;?E5D0Z5($mjO3`*anwQU^s{ZDK#Lz zj>~{qyaIx5K!t%=G&2IJNzg!ChRpyLkO7}Ry!QaotAHAMpbB3AF(}|_f!G-oI|uK6 z`id_dumai5K%C3Y$;tKS_iqMPHg<*|-@e`liWLAggVM!zAP#@l;=c>S03;{#04Z~5 zN_+ss=Yg6*hTr59mzMwZ@+l~q!+?ft!fF66AXT#wWavHt30bZWFCK%!BNk}LN?0Hg z1VF_nfs`Lm^DjYZ1(1uD0u4CSIr)XAaqNdPT#q`cZlbij$jvbRk6R>8g*>}*b9E+WDwmpHAAxYzyT aU_pX{M6b8i>#Dq3onfZy}_nli%!Q$ZV%e&!tN2 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 YpWXKgmku8Vj|K)Vje`mzEKCg608Q#dYybcN diff --git a/java/doc/resources/x.png b/java/doc/resources/x.png new file mode 100644 index 0000000000000000000000000000000000000000..30548a756e151be4e927e8d28c508cc5b3514bf3 GIT binary patch literal 394 zcmV;50d@X~P)W6IT{!St5~1{i=i}zAy76p%_|w8rh@@c0Axr!ns=D-X+|*sY6!@wacG9%)Qn*O zl0sa739kT-&_?#oVxXF6tOnqTD)cZ}2vi$`ZU8RLAlo8=_z#*P3xI~i!lEh+Pdu-L zx{d*wgjtXbnGX_Yf@Tc7Q3YhLhPvc8noGJs2DA~1DySiA&6V{5JzFt ojAY1KXm~va;tU{v7C?Xj0BHw!K;2aXV*mgE07*qoM6N<$f;4TDA^-pY literal 0 HcmV?d00001 diff --git a/java/doc/script.js b/java/doc/script.js index b34635693..7dc93c48e 100644 --- a/java/doc/script.js +++ b/java/doc/script.js @@ -1,9 +1,124 @@ -function show(type) -{ +/* + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +var moduleSearchIndex; +var packageSearchIndex; +var typeSearchIndex; +var memberSearchIndex; +var tagSearchIndex; +function loadScripts(doc, tag) { + createElem(doc, tag, 'jquery/jszip/dist/jszip.js'); + createElem(doc, tag, 'jquery/jszip-utils/dist/jszip-utils.js'); + if (window.navigator.userAgent.indexOf('MSIE ') > 0 || window.navigator.userAgent.indexOf('Trident/') > 0 || + window.navigator.userAgent.indexOf('Edge/') > 0) { + createElem(doc, tag, 'jquery/jszip-utils/dist/jszip-utils-ie.js'); + } + createElem(doc, tag, 'search.js'); + + $.get(pathtoroot + "module-search-index.zip") + .done(function() { + JSZipUtils.getBinaryContent(pathtoroot + "module-search-index.zip", function(e, data) { + JSZip.loadAsync(data).then(function(zip){ + zip.file("module-search-index.json").async("text").then(function(content){ + moduleSearchIndex = JSON.parse(content); + }); + }); + }); + }); + $.get(pathtoroot + "package-search-index.zip") + .done(function() { + JSZipUtils.getBinaryContent(pathtoroot + "package-search-index.zip", function(e, data) { + JSZip.loadAsync(data).then(function(zip){ + zip.file("package-search-index.json").async("text").then(function(content){ + packageSearchIndex = JSON.parse(content); + }); + }); + }); + }); + $.get(pathtoroot + "type-search-index.zip") + .done(function() { + JSZipUtils.getBinaryContent(pathtoroot + "type-search-index.zip", function(e, data) { + JSZip.loadAsync(data).then(function(zip){ + zip.file("type-search-index.json").async("text").then(function(content){ + typeSearchIndex = JSON.parse(content); + }); + }); + }); + }); + $.get(pathtoroot + "member-search-index.zip") + .done(function() { + JSZipUtils.getBinaryContent(pathtoroot + "member-search-index.zip", function(e, data) { + JSZip.loadAsync(data).then(function(zip){ + zip.file("member-search-index.json").async("text").then(function(content){ + memberSearchIndex = JSON.parse(content); + }); + }); + }); + }); + $.get(pathtoroot + "tag-search-index.zip") + .done(function() { + JSZipUtils.getBinaryContent(pathtoroot + "tag-search-index.zip", function(e, data) { + JSZip.loadAsync(data).then(function(zip){ + zip.file("tag-search-index.json").async("text").then(function(content){ + tagSearchIndex = JSON.parse(content); + }); + }); + }); + }); + if (!moduleSearchIndex) { + createElem(doc, tag, 'module-search-index.js'); + } + if (!packageSearchIndex) { + createElem(doc, tag, 'package-search-index.js'); + } + if (!typeSearchIndex) { + createElem(doc, tag, 'type-search-index.js'); + } + if (!memberSearchIndex) { + createElem(doc, tag, 'member-search-index.js'); + } + if (!tagSearchIndex) { + createElem(doc, tag, 'tag-search-index.js'); + } + $(window).resize(function() { + $('.navPadding').css('padding-top', $('.fixedNav').css("height")); + }); +} + +function createElem(doc, tag, path) { + var script = doc.createElement(tag); + var scriptElement = doc.getElementsByTagName(tag)[0]; + script.src = pathtoroot + path; + scriptElement.parentNode.insertBefore(script, scriptElement); +} + +function show(type) { count = 0; - for (var key in methods) { + for (var key in data) { var row = document.getElementById(key); - if ((methods[key] & type) != 0) { + if ((data[key] & type) !== 0) { row.style.display = ''; row.className = (count++ % 2) ? rowColor : altColor; } @@ -13,8 +128,7 @@ function show(type) updateTabs(type); } -function updateTabs(type) -{ +function updateTabs(type) { for (var value in tabs) { var sNode = document.getElementById(tabs[value][0]); var spanNode = sNode.firstChild; @@ -28,3 +142,8 @@ function updateTabs(type) } } } + +function updateModuleFrame(pFrame, cFrame) { + top.packageFrame.location = pFrame; + top.classFrame.location = cFrame; +} diff --git a/java/doc/search.js b/java/doc/search.js new file mode 100644 index 000000000..8492271e7 --- /dev/null +++ b/java/doc/search.js @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +var noResult = {l: "No results found"}; +var catModules = "Modules"; +var catPackages = "Packages"; +var catTypes = "Types"; +var catMembers = "Members"; +var catSearchTags = "SearchTags"; +var highlight = "$&"; +var camelCaseRegexp = ""; +var secondaryMatcher = ""; +function getHighlightedText(item) { + var ccMatcher = new RegExp(camelCaseRegexp); + var label = item.replace(ccMatcher, highlight); + if (label === item) { + label = item.replace(secondaryMatcher, highlight); + } + return label; +} +function getURLPrefix(ui) { + var urlPrefix=""; + if (useModuleDirectories) { + var slash = "/"; + if (ui.item.category === catModules) { + return ui.item.l + slash; + } else if (ui.item.category === catPackages && ui.item.m) { + return ui.item.m + slash; + } else if ((ui.item.category === catTypes && ui.item.p) || ui.item.category === catMembers) { + $.each(packageSearchIndex, function(index, item) { + if (item.m && ui.item.p == item.l) { + urlPrefix = item.m + slash; + } + }); + return urlPrefix; + } else { + return urlPrefix; + } + } + return urlPrefix; +} +var watermark = 'Search'; +$(function() { + $("#search").val(''); + $("#search").prop("disabled", false); + $("#reset").prop("disabled", false); + $("#search").val(watermark).addClass('watermark'); + $("#search").blur(function() { + if ($(this).val().length == 0) { + $(this).val(watermark).addClass('watermark'); + } + }); + $("#search").on('click keydown', function() { + if ($(this).val() == watermark) { + $(this).val('').removeClass('watermark'); + } + }); + $("#reset").click(function() { + $("#search").val(''); + $("#search").focus(); + }); + $("#search").focus(); + $("#search")[0].setSelectionRange(0, 0); +}); +$.widget("custom.catcomplete", $.ui.autocomplete, { + _create: function() { + this._super(); + this.widget().menu("option", "items", "> :not(.ui-autocomplete-category)"); + }, + _renderMenu: function(ul, items) { + var rMenu = this, + currentCategory = ""; + rMenu.menu.bindings = $(); + $.each(items, function(index, item) { + var li; + if (item.l !== noResult.l && item.category !== currentCategory) { + ul.append("
    • " + item.category + "
    • "); + currentCategory = item.category; + } + li = rMenu._renderItemData(ul, item); + if (item.category) { + li.attr("aria-label", item.category + " : " + item.l); + li.attr("class", "resultItem"); + } else { + li.attr("aria-label", item.l); + li.attr("class", "resultItem"); + } + }); + }, + _renderItem: function(ul, item) { + var label = ""; + if (item.category === catModules) { + label = getHighlightedText(item.l); + } else if (item.category === catPackages) { + label = (item.m) + ? getHighlightedText(item.m + "/" + item.l) + : getHighlightedText(item.l); + } else if (item.category === catTypes) { + label = (item.p) + ? getHighlightedText(item.p + "." + item.l) + : getHighlightedText(item.l); + } else if (item.category === catMembers) { + label = getHighlightedText(item.p + "." + (item.c + "." + item.l)); + } else if (item.category === catSearchTags) { + label = getHighlightedText(item.l); + } else { + label = item.l; + } + var li = $("
    • ").appendTo(ul); + var div = $("
      ").appendTo(li); + if (item.category === catSearchTags) { + if (item.d) { + div.html(label + " (" + item.h + ")
      " + + item.d + "
      "); + } else { + div.html(label + " (" + item.h + ")"); + } + } else { + div.html(label); + } + return li; + } +}); +$(function() { + $("#search").catcomplete({ + minLength: 1, + delay: 100, + source: function(request, response) { + var result = new Array(); + var presult = new Array(); + var tresult = new Array(); + var mresult = new Array(); + var tgresult = new Array(); + var secondaryresult = new Array(); + var displayCount = 0; + var exactMatcher = new RegExp("^" + $.ui.autocomplete.escapeRegex(request.term) + "$", "i"); + camelCaseRegexp = ($.ui.autocomplete.escapeRegex(request.term)).split(/(?=[A-Z])/).join("([a-z0-9_$]*?)"); + var camelCaseMatcher = new RegExp("^" + camelCaseRegexp); + secondaryMatcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i"); + + // Return the nested innermost name from the specified object + function nestedName(e) { + return e.l.substring(e.l.lastIndexOf(".") + 1); + } + + function concatResults(a1, a2) { + a1 = a1.concat(a2); + a2.length = 0; + return a1; + } + + if (moduleSearchIndex) { + var mdleCount = 0; + $.each(moduleSearchIndex, function(index, item) { + item.category = catModules; + if (exactMatcher.test(item.l)) { + result.push(item); + mdleCount++; + } else if (camelCaseMatcher.test(item.l)) { + result.push(item); + } else if (secondaryMatcher.test(item.l)) { + secondaryresult.push(item); + } + }); + displayCount = mdleCount; + result = concatResults(result, secondaryresult); + } + if (packageSearchIndex) { + var pCount = 0; + var pkg = ""; + $.each(packageSearchIndex, function(index, item) { + item.category = catPackages; + pkg = (item.m) + ? (item.m + "/" + item.l) + : item.l; + if (exactMatcher.test(item.l)) { + presult.push(item); + pCount++; + } else if (camelCaseMatcher.test(pkg)) { + presult.push(item); + } else if (secondaryMatcher.test(pkg)) { + secondaryresult.push(item); + } + }); + result = result.concat(concatResults(presult, secondaryresult)); + displayCount = (pCount > displayCount) ? pCount : displayCount; + } + if (typeSearchIndex) { + var tCount = 0; + $.each(typeSearchIndex, function(index, item) { + item.category = catTypes; + var s = nestedName(item); + if (exactMatcher.test(s)) { + tresult.push(item); + tCount++; + } else if (camelCaseMatcher.test(s)) { + tresult.push(item); + } else if (secondaryMatcher.test(item.p + "." + item.l)) { + secondaryresult.push(item); + } + }); + result = result.concat(concatResults(tresult, secondaryresult)); + displayCount = (tCount > displayCount) ? tCount : displayCount; + } + if (memberSearchIndex) { + var mCount = 0; + $.each(memberSearchIndex, function(index, item) { + item.category = catMembers; + var s = nestedName(item); + if (exactMatcher.test(s)) { + mresult.push(item); + mCount++; + } else if (camelCaseMatcher.test(s)) { + mresult.push(item); + } else if (secondaryMatcher.test(item.c + "." + item.l)) { + secondaryresult.push(item); + } + }); + result = result.concat(concatResults(mresult, secondaryresult)); + displayCount = (mCount > displayCount) ? mCount : displayCount; + } + if (tagSearchIndex) { + var tgCount = 0; + $.each(tagSearchIndex, function(index, item) { + item.category = catSearchTags; + if (exactMatcher.test(item.l)) { + tgresult.push(item); + tgCount++; + } else if (secondaryMatcher.test(item.l)) { + secondaryresult.push(item); + } + }); + result = result.concat(concatResults(tgresult, secondaryresult)); + displayCount = (tgCount > displayCount) ? tgCount : displayCount; + } + displayCount = (displayCount > 500) ? displayCount : 500; + var counter = function() { + var count = {Modules: 0, Packages: 0, Types: 0, Members: 0, SearchTags: 0}; + var f = function(item) { + count[item.category] += 1; + return (count[item.category] <= displayCount); + }; + return f; + }(); + response(result.filter(counter)); + }, + response: function(event, ui) { + if (!ui.content.length) { + ui.content.push(noResult); + } else { + $("#search").empty(); + } + }, + autoFocus: true, + position: { + collision: "flip" + }, + select: function(event, ui) { + if (ui.item.l !== noResult.l) { + var url = getURLPrefix(ui); + if (ui.item.category === catModules) { + if (useModuleDirectories) { + url += "module-summary.html"; + } else { + url = ui.item.l + "-summary.html"; + } + } else if (ui.item.category === catPackages) { + if (ui.item.url) { + url = ui.item.url; + } else { + url += ui.item.l.replace(/\./g, '/') + "/package-summary.html"; + } + } else if (ui.item.category === catTypes) { + if (ui.item.url) { + url = ui.item.url; + } else if (ui.item.p === "") { + url += ui.item.l + ".html"; + } else { + url += ui.item.p.replace(/\./g, '/') + "/" + ui.item.l + ".html"; + } + } else if (ui.item.category === catMembers) { + if (ui.item.p === "") { + url += ui.item.c + ".html" + "#"; + } else { + url += ui.item.p.replace(/\./g, '/') + "/" + ui.item.c + ".html" + "#"; + } + if (ui.item.url) { + url += ui.item.url; + } else { + url += ui.item.l; + } + } else if (ui.item.category === catSearchTags) { + url += ui.item.u; + } + if (top !== window) { + parent.classFrame.location = pathtoroot + url; + } else { + window.location.href = pathtoroot + url; + } + $("#search").focus(); + } + } + }); +}); diff --git a/java/doc/serialized-form.html b/java/doc/serialized-form.html index e123f3189..33cde533c 100644 --- a/java/doc/serialized-form.html +++ b/java/doc/serialized-form.html @@ -1,9 +1,21 @@ - + + Serialized Form + + + + + + + + + +var pathtoroot = "./"; +var useModuleDirectories = true; +loadScripts(document, 'script'); +
      + +
      +

      Serialized Form

      +
    • + + diff --git a/java/doc/stylesheet.css b/java/doc/stylesheet.css index 0aeaa97fe..de945eda2 100644 --- a/java/doc/stylesheet.css +++ b/java/doc/stylesheet.css @@ -1,73 +1,109 @@ -/* Javadoc style sheet */ +/* + * Javadoc style sheet + */ + +@import url('resources/fonts/dejavu.css'); + /* -Overall document style -*/ + * Styles for individual HTML elements. + * + * These are styles that are specific to individual HTML elements. Changing them affects the style of a particular + * HTML element throughout the page. + */ + body { background-color:#ffffff; color:#353833; - font-family:Arial, Helvetica, sans-serif; - font-size:76%; + font-family:'DejaVu Sans', Arial, Helvetica, sans-serif; + font-size:14px; margin:0; + padding:0; + height:100%; + width:100%; +} +iframe { + margin:0; + padding:0; + height:100%; + width:100%; + overflow-y:scroll; + border:none; } a:link, a:visited { text-decoration:none; - color:#4c6b87; + color:#4A6782; } -a:hover, a:focus { +a[href]:hover, a[href]:focus { text-decoration:none; color:#bb7a2a; } -a:active { - text-decoration:none; - color:#4c6b87; -} a[name] { color:#353833; } -a[name]:hover { - text-decoration:none; - color:#353833; +a[name]:before, a[name]:target, a[id]:before, a[id]:target { + content:""; + display:inline-block; + position:relative; + padding-top:129px; + margin-top:-129px; } pre { - font-size:1.3em; + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; } h1 { - font-size:1.8em; + font-size:20px; } h2 { - font-size:1.5em; + font-size:18px; } h3 { - font-size:1.4em; + font-size:16px; + font-style:italic; } h4 { - font-size:1.3em; + font-size:13px; } h5 { - font-size:1.2em; + font-size:12px; } h6 { - font-size:1.1em; + font-size:11px; } ul { list-style-type:disc; } code, tt { - font-size:1.2em; + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + padding-top:4px; + margin-top:8px; + line-height:1.4em; } dt code { - font-size:1.2em; + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + padding-top:4px; } table tr td dt code { - font-size:1.2em; + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; vertical-align:top; + padding-top:4px; } sup { - font-size:.6em; + font-size:8px; } + /* -Document title and Copyright styles -*/ + * Styles for HTML generated by javadoc. + * + * These are style classes that are used by the standard doclet to generate HTML documentation. + */ + +/* + * Styles for document title and copyright. + */ .clear { clear:both; height:0px; @@ -76,9 +112,9 @@ Document title and Copyright styles .aboutLanguage { float:right; padding:0px 21px; - font-size:.8em; + font-size:11px; z-index:200; - margin-top:-7px; + margin-top:-9px; } .legalCopy { margin-left:.5em; @@ -92,29 +128,33 @@ Document title and Copyright styles } .tab { background-color:#0066FF; - background-image:url(resources/titlebar.gif); - background-position:left top; - background-repeat:no-repeat; color:#ffffff; padding:8px; width:5em; font-weight:bold; } /* -Navigation bar styles -*/ + * Styles for navigation bar. + */ .bar { - background-image:url(resources/background.gif); - background-repeat:repeat-x; + background-color:#4D7A97; color:#FFFFFF; padding:.8em .5em .4em .8em; height:auto;/*height:1.8em;*/ - font-size:1em; + font-size:11px; margin:0; } +.navPadding { + padding-top: 107px; +} +.fixedNav { + position:fixed; + width:100%; + z-index:999; + background-color:#ffffff; +} .topNav { - background-image:url(resources/background.gif); - background-repeat:repeat-x; + background-color:#4D7A97; color:#FFFFFF; float:left; padding:0; @@ -123,11 +163,11 @@ Navigation bar styles height:2.8em; padding-top:10px; overflow:hidden; + font-size:12px; } .bottomNav { margin-top:10px; - background-image:url(resources/background.gif); - background-repeat:repeat-x; + background-color:#4D7A97; color:#FFFFFF; float:left; padding:0; @@ -136,18 +176,20 @@ Navigation bar styles height:2.8em; padding-top:10px; overflow:hidden; + font-size:12px; } .subNav { background-color:#dee3e9; - border-bottom:1px solid #9eadc0; float:left; width:100%; overflow:hidden; + font-size:12px; } .subNav div { clear:left; float:left; padding:0 0 5px 6px; + text-transform:uppercase; } ul.navList, ul.subNavList { float:left; @@ -157,42 +199,74 @@ ul.navList, ul.subNavList { ul.navList li{ list-style:none; float:left; - padding:3px 6px; + padding: 5px 6px; + text-transform:uppercase; +} +ul.navListSearch { + float:right; + margin:0 0 0 0; + padding:0; +} +ul.navListSearch li { + list-style:none; + float:right; + padding: 5px 6px; + text-transform:uppercase; } -ul.subNavList li{ +ul.navListSearch li label { + position:relative; + right:-16px; +} +ul.subNavList li { list-style:none; float:left; - font-size:90%; } .topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited { color:#FFFFFF; text-decoration:none; + text-transform:uppercase; } .topNav a:hover, .bottomNav a:hover { text-decoration:none; color:#bb7a2a; + text-transform:uppercase; } .navBarCell1Rev { - background-image:url(resources/tab.gif); - background-color:#a88834; - color:#FFFFFF; + background-color:#F8981D; + color:#253441; margin: auto 5px; - border:1px solid #c9aa44; +} +.skipNav { + position:absolute; + top:auto; + left:-9999px; + overflow:hidden; } /* -Page header and footer styles -*/ + * Styles for page header and footer. + */ .header, .footer { clear:both; margin:0 20px; padding:5px 0 0 0; } -.indexHeader { - margin:10px; +.indexNav { position:relative; + font-size:12px; + background-color:#dee3e9; } -.indexHeader h1 { - font-size:1.3em; +.indexNav ul { + margin-top:0; + padding:5px; +} +.indexNav ul li { + display:inline; + list-style-type:none; + padding-right:10px; + text-transform:uppercase; +} +.indexNav h1 { + font-size:13px; } .title { color:#2c4557; @@ -202,7 +276,7 @@ Page header and footer styles margin:5px 0 0 0; } .header ul { - margin:0 0 25px 0; + margin:0 0 15px 0; padding:0; } .footer ul { @@ -210,24 +284,22 @@ Page header and footer styles } .header ul li, .footer ul li { list-style:none; - font-size:1.2em; + font-size:13px; } /* -Heading styles -*/ + * Styles for headings. + */ div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 { background-color:#dee3e9; - border-top:1px solid #9eadc0; - border-bottom:1px solid #9eadc0; + border:1px solid #d0d9e0; margin:0 0 6px -8px; - padding:2px 5px; + padding:7px 5px; } ul.blockList ul.blockList ul.blockList li.blockList h3 { background-color:#dee3e9; - border-top:1px solid #9eadc0; - border-bottom:1px solid #9eadc0; + border:1px solid #d0d9e0; margin:0 0 6px -8px; - padding:2px 5px; + padding:7px 5px; } ul.blockList ul.blockList li.blockList h3 { padding:0; @@ -237,9 +309,10 @@ ul.blockList li.blockList h2 { padding:0px 0 20px 0; } /* -Page layout container styles -*/ -.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer { + * Styles for page layout containers. + */ +.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer, +.allClassesContainer, .allPackagesContainer { clear:both; padding:10px 20px; position:relative; @@ -247,10 +320,10 @@ Page layout container styles .indexContainer { margin:10px; position:relative; - font-size:1.0em; + font-size:12px; } .indexContainer h2 { - font-size:1.1em; + font-size:13px; padding:0 0 3px 0; } .indexContainer ul { @@ -259,15 +332,18 @@ Page layout container styles } .indexContainer ul li { list-style:none; + padding-top:2px; } .contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt { - font-size:1.1em; + font-size:12px; font-weight:bold; margin:10px 0 0 0; color:#4E4E4E; } .contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd { - margin:10px 0 10px 20px; + margin:5px 0 10px 0px; + font-size:14px; + font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; } .serializedFormContainer dl.nameValue dt { margin-left:1px; @@ -281,8 +357,11 @@ Page layout container styles display:inline; } /* -List styles -*/ + * Styles for lists. + */ +li.circle { + list-style:circle; +} ul.horizontal li { display:inline; font-size:0.9em; @@ -306,25 +385,24 @@ ul.blockList, ul.blockListLast { } ul.blockList li.blockList, ul.blockListLast li.blockList { list-style:none; - margin-bottom:25px; + margin-bottom:15px; + line-height:1.4; } ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList { padding:0px 20px 5px 10px; - border:1px solid #9eadc0; - background-color:#f9f9f9; + border:1px solid #ededed; + background-color:#f8f8f8; } ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList { padding:0 0 5px 8px; background-color:#ffffff; - border:1px solid #9eadc0; - border-top:none; + border:none; } ul.blockList ul.blockList ul.blockList ul.blockList li.blockList { margin-left:0; padding-left:0; padding-bottom:15px; border:none; - border-bottom:1px solid #9eadc0; } ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast { list-style:none; @@ -336,113 +414,207 @@ table tr td dl, table tr td dl dt, table tr td dl dd { margin-bottom:1px; } /* -Table styles -*/ -.contentContainer table, .classUseContainer table, .constantValuesContainer table { - border-bottom:1px solid #9eadc0; - width:100%; -} -.contentContainer ul li table, .classUseContainer ul li table, .constantValuesContainer ul li table { + * Styles for tables. + */ +.overviewSummary, .memberSummary, .typeSummary, .useSummary, .constantsSummary, .deprecatedSummary, +.requiresSummary, .packagesSummary, .providesSummary, .usesSummary { width:100%; + border-spacing:0; + border-left:1px solid #EEE; + border-right:1px solid #EEE; + border-bottom:1px solid #EEE; } -.contentContainer .description table, .contentContainer .details table { - border-bottom:none; -} -.contentContainer ul li table th.colOne, .contentContainer ul li table th.colFirst, .contentContainer ul li table th.colLast, .classUseContainer ul li table th, .constantValuesContainer ul li table th, .contentContainer ul li table td.colOne, .contentContainer ul li table td.colFirst, .contentContainer ul li table td.colLast, .classUseContainer ul li table td, .constantValuesContainer ul li table td{ - vertical-align:top; - padding-right:20px; -} -.contentContainer ul li table th.colLast, .classUseContainer ul li table th.colLast,.constantValuesContainer ul li table th.colLast, -.contentContainer ul li table td.colLast, .classUseContainer ul li table td.colLast,.constantValuesContainer ul li table td.colLast, -.contentContainer ul li table th.colOne, .classUseContainer ul li table th.colOne, -.contentContainer ul li table td.colOne, .classUseContainer ul li table td.colOne { - padding-right:3px; +.overviewSummary, .memberSummary, .requiresSummary, .packagesSummary, .providesSummary, .usesSummary { + padding:0px; } -.overviewSummary caption, .packageSummary caption, .contentContainer ul.blockList li.blockList caption, .summary caption, .classUseContainer caption, .constantValuesContainer caption { +.overviewSummary caption, .memberSummary caption, .typeSummary caption, +.useSummary caption, .constantsSummary caption, .deprecatedSummary caption, +.requiresSummary caption, .packagesSummary caption, .providesSummary caption, .usesSummary caption { position:relative; text-align:left; background-repeat:no-repeat; - color:#FFFFFF; + color:#253441; font-weight:bold; clear:none; overflow:hidden; padding:0px; + padding-top:10px; + padding-left:1px; margin:0px; -} -caption a:link, caption a:hover, caption a:active, caption a:visited { + white-space:pre; +} +.constantsSummary caption a:link, .constantsSummary caption a:visited, +.useSummary caption a:link, .useSummary caption a:visited { + color:#1f389c; +} +.overviewSummary caption a:link, .memberSummary caption a:link, .typeSummary caption a:link, +.deprecatedSummary caption a:link, +.requiresSummary caption a:link, .packagesSummary caption a:link, .providesSummary caption a:link, +.usesSummary caption a:link, +.overviewSummary caption a:hover, .memberSummary caption a:hover, .typeSummary caption a:hover, +.useSummary caption a:hover, .constantsSummary caption a:hover, .deprecatedSummary caption a:hover, +.requiresSummary caption a:hover, .packagesSummary caption a:hover, .providesSummary caption a:hover, +.usesSummary caption a:hover, +.overviewSummary caption a:active, .memberSummary caption a:active, .typeSummary caption a:active, +.useSummary caption a:active, .constantsSummary caption a:active, .deprecatedSummary caption a:active, +.requiresSummary caption a:active, .packagesSummary caption a:active, .providesSummary caption a:active, +.usesSummary caption a:active, +.overviewSummary caption a:visited, .memberSummary caption a:visited, .typeSummary caption a:visited, +.deprecatedSummary caption a:visited, +.requiresSummary caption a:visited, .packagesSummary caption a:visited, .providesSummary caption a:visited, +.usesSummary caption a:visited { color:#FFFFFF; } -.overviewSummary caption span, .packageSummary caption span, .contentContainer ul.blockList li.blockList caption span, .summary caption span, .classUseContainer caption span, .constantValuesContainer caption span { +.overviewSummary caption span, .memberSummary caption span, .typeSummary caption span, +.useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span, +.requiresSummary caption span, .packagesSummary caption span, .providesSummary caption span, +.usesSummary caption span { white-space:nowrap; - padding-top:8px; - padding-left:8px; - display:block; + padding-top:5px; + padding-left:12px; + padding-right:12px; + padding-bottom:7px; + display:inline-block; float:left; - background-image:url(resources/titlebar.gif); - height:18px; + background-color:#F8981D; + border: none; + height:16px; } -.overviewSummary .tabEnd, .packageSummary .tabEnd, .contentContainer ul.blockList li.blockList .tabEnd, .summary .tabEnd, .classUseContainer .tabEnd, .constantValuesContainer .tabEnd { - width:10px; - background-image:url(resources/titlebar_end.gif); - background-repeat:no-repeat; - background-position:top right; - position:relative; +.memberSummary caption span.activeTableTab span, .packagesSummary caption span.activeTableTab span, +.overviewSummary caption span.activeTableTab span, .typeSummary caption span.activeTableTab span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + margin-right:3px; + display:inline-block; float:left; + background-color:#F8981D; + height:16px; } -ul.blockList ul.blockList li.blockList table { - margin:0 0 12px 0px; - width:100%; +.memberSummary caption span.tableTab span, .packagesSummary caption span.tableTab span, +.overviewSummary caption span.tableTab span, .typeSummary caption span.tableTab span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + margin-right:3px; + display:inline-block; + float:left; + background-color:#4D7A97; + height:16px; +} +.memberSummary caption span.tableTab, .memberSummary caption span.activeTableTab, +.packagesSummary caption span.tableTab, .packagesSummary caption span.activeTableTab, +.overviewSummary caption span.tableTab, .overviewSummary caption span.activeTableTab, +.typeSummary caption span.tableTab, .typeSummary caption span.activeTableTab { + padding-top:0px; + padding-left:0px; + padding-right:0px; + background-image:none; + float:none; + display:inline; } -.tableSubHeadingColor { - background-color: #EEEEFF; +.overviewSummary .tabEnd, .memberSummary .tabEnd, .typeSummary .tabEnd, +.useSummary .tabEnd, .constantsSummary .tabEnd, .deprecatedSummary .tabEnd, +.requiresSummary .tabEnd, .packagesSummary .tabEnd, .providesSummary .tabEnd, .usesSummary .tabEnd { + display:none; + width:5px; + position:relative; + float:left; + background-color:#F8981D; +} +.memberSummary .activeTableTab .tabEnd, .packagesSummary .activeTableTab .tabEnd, +.overviewSummary .activeTableTab .tabEnd, .typeSummary .activeTableTab .tabEnd { + display:none; + width:5px; + margin-right:3px; + position:relative; + float:left; + background-color:#F8981D; } -.altColor { - background-color:#eeeeef; +.memberSummary .tableTab .tabEnd, .packagesSummary .tableTab .tabEnd, +.overviewSummary .tableTab .tabEnd, .typeSummary .tableTab .tabEnd { + display:none; + width:5px; + margin-right:3px; + position:relative; + background-color:#4D7A97; + float:left; } -.rowColor { - background-color:#ffffff; +.rowColor th, .altColor th { + font-weight:normal; } -.overviewSummary td, .packageSummary td, .contentContainer ul.blockList li.blockList td, .summary td, .classUseContainer td, .constantValuesContainer td { +.overviewSummary td, .memberSummary td, .typeSummary td, +.useSummary td, .constantsSummary td, .deprecatedSummary td, +.requiresSummary td, .packagesSummary td, .providesSummary td, .usesSummary td { text-align:left; - padding:3px 3px 3px 7px; + padding:0px 0px 12px 10px; +} +th.colFirst, th.colSecond, th.colLast, th.colConstructorName, th.colDeprecatedItemName, .useSummary th, +.constantsSummary th, .packagesSummary th, td.colFirst, td.colSecond, td.colLast, .useSummary td, +.constantsSummary td { + vertical-align:top; + padding-right:0px; + padding-top:8px; + padding-bottom:3px; } -th.colFirst, th.colLast, th.colOne, .constantValuesContainer th { +th.colFirst, th.colSecond, th.colLast, th.colConstructorName, th.colDeprecatedItemName, .constantsSummary th, +.packagesSummary th { background:#dee3e9; - border-top:1px solid #9eadc0; - border-bottom:1px solid #9eadc0; text-align:left; - padding:3px 3px 3px 7px; -} -td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover { - font-weight:bold; + padding:8px 3px 3px 7px; } td.colFirst, th.colFirst { - border-left:1px solid #9eadc0; - white-space:nowrap; -} -td.colLast, th.colLast { - border-right:1px solid #9eadc0; + font-size:13px; +} +td.colSecond, th.colSecond, td.colLast, th.colConstructorName, th.colDeprecatedItemName, th.colLast { + font-size:13px; +} +.constantsSummary th, .packagesSummary th { + font-size:13px; +} +.providesSummary th.colFirst, .providesSummary th.colLast, .providesSummary td.colFirst, +.providesSummary td.colLast { + white-space:normal; + font-size:13px; +} +.overviewSummary td.colFirst, .overviewSummary th.colFirst, +.requiresSummary td.colFirst, .requiresSummary th.colFirst, +.packagesSummary td.colFirst, .packagesSummary td.colSecond, .packagesSummary th.colFirst, .packagesSummary th, +.usesSummary td.colFirst, .usesSummary th.colFirst, +.providesSummary td.colFirst, .providesSummary th.colFirst, +.memberSummary td.colFirst, .memberSummary th.colFirst, +.memberSummary td.colSecond, .memberSummary th.colSecond, .memberSummary th.colConstructorName, +.typeSummary td.colFirst, .typeSummary th.colFirst { + vertical-align:top; } -td.colOne, th.colOne { - border-right:1px solid #9eadc0; - border-left:1px solid #9eadc0; +.packagesSummary th.colLast, .packagesSummary td.colLast { + white-space:normal; +} +td.colFirst a:link, td.colFirst a:visited, +td.colSecond a:link, td.colSecond a:visited, +th.colFirst a:link, th.colFirst a:visited, +th.colSecond a:link, th.colSecond a:visited, +th.colConstructorName a:link, th.colConstructorName a:visited, +th.colDeprecatedItemName a:link, th.colDeprecatedItemName a:visited, +.constantValuesContainer td a:link, .constantValuesContainer td a:visited, +.allClassesContainer td a:link, .allClassesContainer td a:visited, +.allPackagesContainer td a:link, .allPackagesContainer td a:visited { + font-weight:bold; } -table.overviewSummary { - padding:0px; - margin-left:0px; +.tableSubHeadingColor { + background-color:#EEEEFF; } -table.overviewSummary td.colFirst, table.overviewSummary th.colFirst, -table.overviewSummary td.colOne, table.overviewSummary th.colOne { - width:25%; - vertical-align:middle; +.altColor, .altColor th { + background-color:#FFFFFF; } -table.packageSummary td.colFirst, table.overviewSummary th.colFirst { - width:25%; - vertical-align:middle; +.rowColor, .rowColor th { + background-color:#EEEEEF; } /* -Content styles -*/ + * Styles for contents. + */ .description pre { margin-top:0; } @@ -453,9 +625,22 @@ Content styles .docSummary { padding:0; } +ul.blockList ul.blockList ul.blockList li.blockList h3 { + font-style:normal; +} +div.block { + font-size:14px; + font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; +} +td.colLast div { + padding-top:0px; +} +td.colLast a { + padding-bottom:3px; +} /* -Formatting effect styles -*/ + * Styles for formatting effect. + */ .sourceLineNo { color:green; padding:0 30px 0 0; @@ -463,12 +648,263 @@ Formatting effect styles h1.hidden { visibility:hidden; overflow:hidden; - font-size:.9em; + font-size:10px; } .block { display:block; - margin:3px 0 0 0; + margin:3px 10px 2px 0px; + color:#474747; +} +.deprecatedLabel, .descfrmTypeLabel, .implementationLabel, .memberNameLabel, .memberNameLink, +.moduleLabelInPackage, .moduleLabelInType, .overrideSpecifyLabel, .packageLabelInType, +.packageHierarchyLabel, .paramLabel, .returnLabel, .seeLabel, .simpleTagLabel, +.throwsLabel, .typeNameLabel, .typeNameLink, .searchTagLink { + font-weight:bold; +} +.deprecationComment, .emphasizedPhrase, .interfaceName { + font-style:italic; +} +.deprecationBlock { + font-size:14px; + font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; + border-style:solid; + border-width:thin; + border-radius:10px; + padding:10px; + margin-bottom:10px; + margin-right:10px; + display:inline-block; +} +div.block div.deprecationComment, div.block div.block span.emphasizedPhrase, +div.block div.block span.interfaceName { + font-style:normal; +} +div.contentContainer ul.blockList li.blockList h2 { + padding-bottom:0px; +} +/* + * Styles for IFRAME. + */ +.mainContainer { + margin:0 auto; + padding:0; + height:100%; + width:100%; + position:fixed; + top:0; + left:0; +} +.leftContainer { + height:100%; + position:fixed; + width:320px; +} +.leftTop { + position:relative; + float:left; + width:315px; + top:0; + left:0; + height:30%; + border-right:6px solid #ccc; + border-bottom:6px solid #ccc; +} +.leftBottom { + position:relative; + float:left; + width:315px; + bottom:0; + left:0; + height:70%; + border-right:6px solid #ccc; + border-top:1px solid #000; +} +.rightContainer { + position:absolute; + left:320px; + top:0; + bottom:0; + height:100%; + right:0; + border-left:1px solid #000; +} +.rightIframe { + margin:0; + padding:0; + height:100%; + right:30px; + width:100%; + overflow:visible; + margin-bottom:30px; +} +/* + * Styles specific to HTML5 elements. + */ +main, nav, header, footer, section { + display:block; +} +/* + * Styles for javadoc search. + */ +.ui-autocomplete-category { + font-weight:bold; + font-size:15px; + padding:7px 0 7px 3px; + background-color:#4D7A97; + color:#FFFFFF; +} +.resultItem { + font-size:13px; } -.strong { +.ui-autocomplete { + max-height:85%; + max-width:65%; + overflow-y:scroll; + overflow-x:scroll; + white-space:nowrap; + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); +} +ul.ui-autocomplete { + position:fixed; + z-index:999999; + background-color: #FFFFFF; +} +ul.ui-autocomplete li { + float:left; + clear:both; + width:100%; +} +.resultHighlight { font-weight:bold; } +.ui-autocomplete .result-item { + font-size: inherit; +} +#search { + background-image:url('resources/glass.png'); + background-size:13px; + background-repeat:no-repeat; + background-position:2px 3px; + padding-left:20px; + position:relative; + right:-18px; +} +#reset { + background-color: rgb(255,255,255); + background-image:url('resources/x.png'); + background-position:center; + background-repeat:no-repeat; + background-size:12px; + border:0 none; + width:16px; + height:17px; + position:relative; + left:-4px; + top:-4px; + font-size:0px; +} +.watermark { + color:#545454; +} +.searchTagDescResult { + font-style:italic; + font-size:11px; +} +.searchTagHolderResult { + font-style:italic; + font-size:12px; +} +.searchTagResult:before, .searchTagResult:target { + color:red; +} +.moduleGraph span { + display:none; + position:absolute; +} +.moduleGraph:hover span { + display:block; + margin: -100px 0 0 100px; + z-index: 1; +} +.methodSignature { + white-space:normal; +} + +/* + * Styles for user-provided tables. + * + * borderless: + * No borders, vertical margins, styled caption. + * This style is provided for use with existing doc comments. + * In general, borderless tables should not be used for layout purposes. + * + * plain: + * Plain borders around table and cells, vertical margins, styled caption. + * Best for small tables or for complex tables for tables with cells that span + * rows and columns, when the "striped" style does not work well. + * + * striped: + * Borders around the table and vertical borders between cells, striped rows, + * vertical margins, styled caption. + * Best for tables that have a header row, and a body containing a series of simple rows. + */ + +table.borderless, +table.plain, +table.striped { + margin-top: 10px; + margin-bottom: 10px; +} +table.borderless > caption, +table.plain > caption, +table.striped > caption { + font-weight: bold; + font-size: smaller; +} +table.borderless th, table.borderless td, +table.plain th, table.plain td, +table.striped th, table.striped td { + padding: 2px 5px; +} +table.borderless, +table.borderless > thead > tr > th, table.borderless > tbody > tr > th, table.borderless > tr > th, +table.borderless > thead > tr > td, table.borderless > tbody > tr > td, table.borderless > tr > td { + border: none; +} +table.borderless > thead > tr, table.borderless > tbody > tr, table.borderless > tr { + background-color: transparent; +} +table.plain { + border-collapse: collapse; + border: 1px solid black; +} +table.plain > thead > tr, table.plain > tbody tr, table.plain > tr { + background-color: transparent; +} +table.plain > thead > tr > th, table.plain > tbody > tr > th, table.plain > tr > th, +table.plain > thead > tr > td, table.plain > tbody > tr > td, table.plain > tr > td { + border: 1px solid black; +} +table.striped { + border-collapse: collapse; + border: 1px solid black; +} +table.striped > thead { + background-color: #E3E3E3; +} +table.striped > thead > tr > th, table.striped > thead > tr > td { + border: 1px solid black; +} +table.striped > tbody > tr:nth-child(even) { + background-color: #EEE +} +table.striped > tbody > tr:nth-child(odd) { + background-color: #FFF +} +table.striped > tbody > tr > th, table.striped > tbody > tr > td { + border-left: 1px solid black; + border-right: 1px solid black; +} +table.striped > tbody > tr > th { + font-weight: normal; +} diff --git a/java/doc/type-search-index.js b/java/doc/type-search-index.js new file mode 100644 index 000000000..d4bc56411 --- /dev/null +++ b/java/doc/type-search-index.js @@ -0,0 +1 @@ +typeSearchIndex = [{"l":"All Classes","url":"allclasses-index.html"},{"p":"org.libjpegturbo.turbojpeg","l":"TJ"},{"p":"org.libjpegturbo.turbojpeg","l":"TJCompressor"},{"p":"org.libjpegturbo.turbojpeg","l":"TJCustomFilter"},{"p":"org.libjpegturbo.turbojpeg","l":"TJDecompressor"},{"p":"org.libjpegturbo.turbojpeg","l":"TJException"},{"p":"org.libjpegturbo.turbojpeg","l":"TJScalingFactor"},{"p":"org.libjpegturbo.turbojpeg","l":"TJTransform"},{"p":"org.libjpegturbo.turbojpeg","l":"TJTransformer"},{"p":"org.libjpegturbo.turbojpeg","l":"YUVImage"}] \ No newline at end of file diff --git a/java/doc/type-search-index.zip b/java/doc/type-search-index.zip new file mode 100644 index 0000000000000000000000000000000000000000..81bcf27aa0044f80079cf65d7fd56b05578ee707 GIT binary patch literal 311 zcmWIWW@Zs#;Nak3XrFBn#()IGfb5dWf>hn&)Wo9X4BgDUl++5ntm6EO7VOXzbm+c3#3R&OOs>-aEci7I>53zh$LI=K=>t zTm5-Tm+G^#{|dK%nk#cKq(|U+ZbI;+w|0WQuXb`j&DSg9i}>(tPH{A=xTGsndf?tA z53;;DH}7D66 + *
    • decompressed into planar YUV images, + *
    • losslessly transformed if {@link TJTransform#OPT_CROP} is specified, + * or + *
    • partially decompressed using a cropping region. + * + */ + public static final int SAMP_UNKNOWN = -1; /** * Returns the MCU block width for the given level of chrominance @@ -126,71 +139,72 @@ public static int getMCUHeight(int subsamp) { public static final int NUMPF = 12; /** * RGB pixel format. The red, green, and blue components in the image are - * stored in 3-byte pixels in the order R, G, B from lowest to highest byte - * address within each pixel. + * stored in 3-sample pixels in the order R, G, B from lowest to highest + * memory address within each pixel. */ public static final int PF_RGB = 0; /** * BGR pixel format. The red, green, and blue components in the image are - * stored in 3-byte pixels in the order B, G, R from lowest to highest byte - * address within each pixel. + * stored in 3-sample pixels in the order B, G, R from lowest to highest + * memory address within each pixel. */ public static final int PF_BGR = 1; /** * RGBX pixel format. The red, green, and blue components in the image are - * stored in 4-byte pixels in the order R, G, B from lowest to highest byte - * address within each pixel. The X component is ignored when compressing - * and undefined when decompressing. + * stored in 4-sample pixels in the order R, G, B from lowest to highest + * memory address within each pixel. The X component is ignored when + * compressing and undefined when decompressing. */ public static final int PF_RGBX = 2; /** * BGRX pixel format. The red, green, and blue components in the image are - * stored in 4-byte pixels in the order B, G, R from lowest to highest byte - * address within each pixel. The X component is ignored when compressing - * and undefined when decompressing. + * stored in 4-sample pixels in the order B, G, R from lowest to highest + * memory address within each pixel. The X component is ignored when + * compressing and undefined when decompressing. */ public static final int PF_BGRX = 3; /** * XBGR pixel format. The red, green, and blue components in the image are - * stored in 4-byte pixels in the order R, G, B from highest to lowest byte - * address within each pixel. The X component is ignored when compressing - * and undefined when decompressing. + * stored in 4-sample pixels in the order R, G, B from highest to lowest + * memory address within each pixel. The X component is ignored when + * compressing and undefined when decompressing. */ public static final int PF_XBGR = 4; /** * XRGB pixel format. The red, green, and blue components in the image are - * stored in 4-byte pixels in the order B, G, R from highest to lowest byte - * address within each pixel. The X component is ignored when compressing - * and undefined when decompressing. + * stored in 4-sample pixels in the order B, G, R from highest to lowest + * memory address within each pixel. The X component is ignored when + * compressing and undefined when decompressing. */ public static final int PF_XRGB = 5; /** - * Grayscale pixel format. Each 1-byte pixel represents a luminance - * (brightness) level from 0 to 255. + * Grayscale pixel format. Each 1-sample pixel represents a luminance + * (brightness) level from 0 to the maximum sample value (255 for 8-bit + * samples, 4095 for 12-bit samples, and 65535 for 16-bit samples.) */ public static final int PF_GRAY = 6; /** * RGBA pixel format. This is the same as {@link #PF_RGBX}, except that when - * decompressing, the X byte is guaranteed to be 0xFF, which can be - * interpreted as an opaque alpha channel. + * decompressing, the X component is guaranteed to be equal to the maximum + * sample value, which can be interpreted as an opaque alpha channel. */ public static final int PF_RGBA = 7; /** * BGRA pixel format. This is the same as {@link #PF_BGRX}, except that when - * decompressing, the X byte is guaranteed to be 0xFF, which can be - * interpreted as an opaque alpha channel. + * decompressing, the X component is guaranteed to be equal to the maximum + * sample value, which can be interpreted as an opaque alpha channel. */ public static final int PF_BGRA = 8; /** * ABGR pixel format. This is the same as {@link #PF_XBGR}, except that when - * decompressing, the X byte is guaranteed to be 0xFF, which can be - * interpreted as an opaque alpha channel. + * decompressing, the X component is guaranteed to be equal to the maximum + * sample value, which can be interpreted as an opaque alpha channel. */ public static final int PF_ABGR = 9; /** * ARGB pixel format. This is the same as {@link #PF_XRGB}, except that when - * decompressing, the X byte is guaranteed to be 0xFF, which can be - * interpreted as an opaque alpha channel. + * decompressing, the X component is guaranteed to be equal to the maximum + * sample value, which can be interpreted as an opaque alpha channel. */ public static final int PF_ARGB = 10; /** @@ -212,11 +226,11 @@ public static int getMCUHeight(int subsamp) { /** - * Returns the pixel size (in bytes) for the given pixel format. + * Returns the pixel size (in samples) for the given pixel format. * * @param pixelFormat the pixel format (one of {@link #PF_RGB PF_*}) * - * @return the pixel size (in bytes) for the given pixel format. + * @return the pixel size (in samples) for the given pixel format. */ public static int getPixelSize(int pixelFormat) { checkPixelFormat(pixelFormat); @@ -229,10 +243,10 @@ public static int getPixelSize(int pixelFormat) { /** - * For the given pixel format, returns the number of bytes that the red - * component is offset from the start of the pixel. For instance, if a pixel - * of format TJ.PF_BGRX is stored in char pixel[], - * then the red component will be + * For the given pixel format, returns the number of samples that the red + * component is offset from the start of the pixel. For instance, if an + * 8-bit-per-sample pixel of format TJ.PF_BGRX is stored in + * char pixel[], then the red component will be * pixel[TJ.getRedOffset(TJ.PF_BGRX)]. * * @param pixelFormat the pixel format (one of {@link #PF_RGB PF_*}) @@ -251,10 +265,10 @@ public static int getRedOffset(int pixelFormat) { /** - * For the given pixel format, returns the number of bytes that the green - * component is offset from the start of the pixel. For instance, if a pixel - * of format TJ.PF_BGRX is stored in char pixel[], - * then the green component will be + * For the given pixel format, returns the number of samples that the green + * component is offset from the start of the pixel. For instance, if an + * 8-bit-per-sample pixel of format TJ.PF_BGRX is stored in + * char pixel[], then the green component will be * pixel[TJ.getGreenOffset(TJ.PF_BGRX)]. * * @param pixelFormat the pixel format (one of {@link #PF_RGB PF_*}) @@ -273,10 +287,10 @@ public static int getGreenOffset(int pixelFormat) { /** - * For the given pixel format, returns the number of bytes that the blue - * component is offset from the start of the pixel. For instance, if a pixel - * of format TJ.PF_BGRX is stored in char pixel[], - * then the blue component will be + * For the given pixel format, returns the number of samples that the blue + * component is offset from the start of the pixel. For instance, if an + * 8-bit-per-sample pixel of format TJ.PF_BGRX is stored in + * char pixel[], then the blue component will be * pixel[TJ.getBlueOffset(TJ.PF_BGRX)]. * * @param pixelFormat the pixel format (one of {@link #PF_RGB PF_*}) @@ -295,10 +309,10 @@ public static int getBlueOffset(int pixelFormat) { /** - * For the given pixel format, returns the number of bytes that the alpha - * component is offset from the start of the pixel. For instance, if a pixel - * of format TJ.PF_BGRA is stored in char pixel[], - * then the alpha component will be + * For the given pixel format, returns the number of samples that the alpha + * component is offset from the start of the pixel. For instance, if an + * 8-bit-per-sample pixel of format TJ.PF_BGRA is stored in + * char pixel[], then the alpha component will be * pixel[TJ.getAlphaOffset(TJ.PF_BGRA)]. * * @param pixelFormat the pixel format (one of {@link #PF_RGB PF_*}) @@ -324,9 +338,9 @@ public static int getAlphaOffset(int pixelFormat) { * RGB colorspace. When compressing the JPEG image, the R, G, and B * components in the source image are reordered into image planes, but no * colorspace conversion or subsampling is performed. RGB JPEG images can be - * decompressed to packed-pixel images with any of the extended RGB or - * grayscale pixel formats, but they cannot be decompressed to planar YUV - * images. + * compressed from and decompressed to packed-pixel images with any of the + * extended RGB or grayscale pixel formats, but they cannot be compressed + * from or decompressed to planar YUV images. */ public static final int CS_RGB = 0; /** @@ -361,7 +375,8 @@ public static int getAlphaOffset(int pixelFormat) { * CMYK colorspace. When compressing the JPEG image, the C, M, Y, and K * components in the source image are reordered into image planes, but no * colorspace conversion or subsampling is performed. CMYK JPEG images can - * only be decompressed to packed-pixel images with the CMYK pixel format. + * only be compressed from and decompressed to packed-pixel images with the + * CMYK pixel format. */ public static final int CS_CMYK = 3; /** @@ -377,92 +392,414 @@ public static int getAlphaOffset(int pixelFormat) { /** - * Rows in the packed-pixel source/destination image are stored in bottom-up - * (Windows, OpenGL) order rather than in top-down (X11) order. + * Error handling behavior + * + *

      Value + *

        + *
      • 0 [default] Allow the current + * compression/decompression/transform operation to complete unless a fatal + * error is encountered. + *
      • 1 Immediately discontinue the current + * compression/decompression/transform operation if a warning (non-fatal + * error) occurs. + *
      */ - public static final int FLAG_BOTTOMUP = (1 << 1); - + public static final int PARAM_STOPONWARNING = 0; + /** + * Row order in packed-pixel source/destination images + * + *

      Value + *

        + *
      • 0 [default] top-down (X11) order + *
      • 1 bottom-up (Windows, OpenGL) order + *
      + */ + public static final int PARAM_BOTTOMUP = 1; + /** + * Perceptual quality of lossy JPEG images [compression only] + * + *

      Value + *

        + *
      • 1-100 (1 = worst quality but + * best compression, 100 = best quality but worst compression) + * [no default; must be explicitly specified] + *
      + */ + public static final int PARAM_QUALITY = 3; /** - * When decompressing an image that was compressed using chrominance - * subsampling, use the fastest chrominance upsampling algorithm available. - * The default is to use smooth upsampling, which creates a smooth transition - * between neighboring chrominance components in order to reduce upsampling - * artifacts in the decompressed image. + * Chrominance subsampling level + * + *

      The JPEG or YUV image uses (decompression, decoding) or will use (lossy + * compression, encoding) the specified level of chrominance subsampling. + * + *

      When pixels are converted from RGB to YCbCr (see {@link #CS_YCbCr}) or + * from CMYK to YCCK (see {@link #CS_YCCK}) as part of the JPEG compression + * process, some of the Cb and Cr (chrominance) components can be discarded + * or averaged together to produce a smaller image with little perceptible + * loss of image clarity. (The human eye is more sensitive to small changes + * in brightness than to small changes in color.) This is called + * "chrominance subsampling". + * + *

      Value + *

        + *
      • One of {@link TJ#SAMP_444 TJ.SAMP_*} [no default; must be + * explicitly specified for lossy compression, encoding, and decoding] + *
      */ - public static final int FLAG_FASTUPSAMPLE = (1 << 8); + public static final int PARAM_SUBSAMP = 4; /** - * Use the fastest DCT/IDCT algorithm available. The default if this flag is - * not specified is implementation-specific. For example, the implementation - * of the TurboJPEG API in libjpeg-turbo uses the fast algorithm by default - * when compressing, because this has been shown to have only a very slight - * effect on accuracy, but it uses the accurate algorithm when decompressing, - * because this has been shown to have a larger effect. + * JPEG width (in pixels) [decompression only, read-only] */ - public static final int FLAG_FASTDCT = (1 << 11); + public static final int PARAM_JPEGWIDTH = 5; /** - * Use the most accurate DCT/IDCT algorithm available. The default if this - * flag is not specified is implementation-specific. For example, the - * implementation of the TurboJPEG API in libjpeg-turbo uses the fast - * algorithm by default when compressing, because this has been shown to have - * only a very slight effect on accuracy, but it uses the accurate algorithm - * when decompressing, because this has been shown to have a larger effect. + * JPEG height (in pixels) [decompression only, read-only] */ - public static final int FLAG_ACCURATEDCT = (1 << 12); + public static final int PARAM_JPEGHEIGHT = 6; /** - * Immediately discontinue the current compression/decompression/transform - * operation if a warning (non-fatal error) occurs. The default behavior is - * to allow the operation to complete unless a fatal error is encountered. - *

      - * NOTE: due to the design of the TurboJPEG Java API, only certain methods - * (specifically, {@link TJDecompressor TJDecompressor.decompress*()} methods - * with a void return type) will complete and leave the destination image in - * a fully recoverable state after a non-fatal error occurs. + * JPEG data precision (bits per sample) [decompression only, read-only] + * + *

      The JPEG image uses the specified number of bits per sample. + * + *

      Value + *

        + *
      • 8, 12, or 16 + *
      + * + *

      12-bit data precision implies {@link #PARAM_OPTIMIZE}. + */ + public static final int PARAM_PRECISION = 7; + /** + * JPEG colorspace + * + *

      The JPEG image uses (decompression) or will use (lossy compression) the + * specified colorspace. + * + *

      Value + *

        + *
      • One of {@link TJ#CS_RGB TJ.CS_*} [default for lossy compression: + * automatically selected based on the subsampling level and pixel + * format] + *
      + */ + public static final int PARAM_COLORSPACE = 8; + /** + * Chrominance upsampling algorithm [lossy decompression only] + * + *

      Value + *

        + *
      • 0 [default] Use smooth upsampling when + * decompressing a JPEG image that was compressed using chrominance + * subsampling. This creates a smooth transition between neighboring + * chrominance components in order to reduce upsampling artifacts in the + * decompressed image. + *
      • 1 Use the fastest chrominance upsampling algorithm + * available, which may combine upsampling with color conversion. + *
      + */ + public static final int PARAM_FASTUPSAMPLE = 9; + /** + * DCT/IDCT algorithm [lossy compression and decompression] + * + *

      Value + *

        + *
      • 0 [default] Use the most accurate DCT/IDCT + * algorithm available. + *
      • 1 Use the fastest DCT/IDCT algorithm available. + *
      + * + *

      This parameter is provided mainly for backward compatibility with + * libjpeg, which historically implemented several different DCT/IDCT + * algorithms because of performance limitations with 1990s CPUs. In the + * libjpeg-turbo implementation of the TurboJPEG API: + * + *

        + *
      • The "fast" and "accurate" DCT/IDCT algorithms perform similarly on + * modern x86/x86-64 CPUs that support AVX2 instructions. + *
      • The "fast" algorithm is generally only about 5-15% faster than the + * "accurate" algorithm on other types of CPUs. + *
      • The difference in accuracy between the "fast" and "accurate" + * algorithms is the most pronounced at JPEG quality levels above 90 and + * tends to be more pronounced with decompression than with compression. + *
      • The "fast" algorithm degrades and is not fully accelerated for JPEG + * quality levels above 97, so it will be slower than the "accurate" + * algorithm. + *
      + */ + public static final int PARAM_FASTDCT = 10; + /** + * Optimized baseline entropy coding [lossy compression only] + * + *

      Value + *

        + *
      • 0 [default] The JPEG image will use the default + * Huffman tables. + *
      • 1 Optimal Huffman tables will be computed for the JPEG + * image. For lossless transformation, this can also be specified using + * {@link TJTransform#OPT_OPTIMIZE}. + *
      + * + *

      Optimized baseline entropy coding will improve compression slightly + * (generally 5% or less), but it will reduce compression performance + * considerably. */ - public static final int FLAG_STOPONWARNING = (1 << 13); + public static final int PARAM_OPTIMIZE = 11; /** - * Use progressive entropy coding in JPEG images generated by compression and - * transform operations. Progressive entropy coding will generally improve - * compression relative to baseline entropy coding (the default), but it will - * reduce compression and decompression performance considerably. Can be - * combined with {@link #FLAG_ARITHMETIC}. + * Progressive entropy coding + * + *

      Value + *

        + *
      • 0 [default for compression, lossless + * transformation] The lossy JPEG image uses (decompression) or will use + * (compression, lossless transformation) baseline entropy coding. + *
      • 1 The lossy JPEG image uses (decompression) or will use + * (compression, lossless transformation) progressive entropy coding. For + * lossless transformation, this can also be specified using + * {@link TJTransform#OPT_PROGRESSIVE}. + *
      + * + *

      Progressive entropy coding will generally improve compression relative + * to baseline entropy coding, but it will reduce compression and + * decompression performance considerably. Implies {@link #PARAM_OPTIMIZE}. + * Can be combined with {@link #PARAM_ARITHMETIC}. */ - public static final int FLAG_PROGRESSIVE = (1 << 14); + public static final int PARAM_PROGRESSIVE = 12; /** - * Limit the number of progressive JPEG scans that the decompression and - * transform operations will process. If a progressive JPEG image contains - * an unreasonably large number of scans, then this flag will cause the - * decompression and transform operations to throw an error. The primary - * purpose of this is to allow security-critical applications to guard - * against an exploit of the progressive JPEG format described in + * Progressive JPEG scan limit for lossy JPEG images [decompression, lossless + * transformation] + * + *

      Setting this parameter will cause the decompression and transform + * functions to return an error if the number of scans in a progressive JPEG + * image exceeds the specified limit. The primary purpose of this is to + * allow security-critical applications to guard against an exploit of the + * progressive JPEG format described in * this report. + * + *

      Value + *

        + *
      • maximum number of progressive JPEG scans that the decompression and + * transform functions will process [default: 0 (no + * limit)] + *
      + * + * @see #PARAM_PROGRESSIVE */ - public static final int FLAG_LIMITSCANS = (1 << 15); + public static final int PARAM_SCANLIMIT = 13; /** - * Use arithmetic entropy coding in JPEG images generated by compression and - * transform operations. Arithmetic entropy coding will generally improve - * compression relative to Huffman entropy coding (the default), but it will - * reduce compression and decompression performance considerably. Can be - * combined with {@link #FLAG_PROGRESSIVE}. + * Arithmetic entropy coding + * + *

      Value + *

        + *
      • 0 [default for compression, lossless + * transformation] The lossy JPEG image uses (decompression) or will use + * (compression, lossless transformation) Huffman entropy coding. + *
      • 1 The lossy JPEG image uses (decompression) or will use + * (compression, lossless transformation) arithmetic entropy coding. For + * lossless transformation, this can also be specified using + * {@link TJTransform#OPT_ARITHMETIC}. + *
      + * + *

      Arithmetic entropy coding will generally improve compression relative + * to Huffman entropy coding, but it will reduce compression and + * decompression performance considerably. Can be combined with + * {@link #PARAM_PROGRESSIVE}. Arithmetic entropy coding is currently only + * implemented for 8-bit samples. */ - public static final int FLAG_ARITHMETIC = (1 << 16); + public static final int PARAM_ARITHMETIC = 14; /** - * Generate a lossless JPEG image when compressing. In most cases, - * compressing and decompressing lossless JPEG images is considerably slower - * than compressing and decompressing lossy JPEG images. Also note that the - * following features are not available with lossless JPEG images: + * Lossless JPEG + * + *

      Value *

        - *
      • Colorspace conversion - *
      • Chrominance subsampling + *
      • 0 [default for compression] The JPEG image is + * (decompression) or will be (compression) lossy/DCT-based. + *
      • 1 The JPEG image is (decompression) or will be + * (compression) lossless/predictive. + *
      + * + *

      In most cases, compressing and decompressing lossless JPEG images is + * considerably slower than compressing and decompressing lossy JPEG images. + * Also note that the following features are not available with lossless JPEG + * images: + *

        + *
      • Colorspace conversion (lossless JPEG images always use + * {@link #CS_RGB}, {@link #CS_GRAY}, or {@link #CS_CMYK}, depending on the + * pixel format of the source image) + *
      • Chrominance subsampling (lossless JPEG images always use + * {@link #SAMP_444}) *
      • JPEG quality selection *
      • DCT/IDCT algorithm selection *
      • Progressive entropy coding *
      • Arithmetic entropy coding *
      • Compression from/decompression to planar YUV images *
      • Decompression scaling - *
      • Lossless transformations + *
      • Lossless transformation + *
      + * + * @see #PARAM_LOSSLESSPSV + * @see #PARAM_LOSSLESSPT + */ + public static final int PARAM_LOSSLESS = 15; + /** + * Lossless JPEG predictor selection value (PSV) + * + *

      Value + *

        + *
      • 1-7 [default for compression: + * 1] + *
      + * + * @see #PARAM_LOSSLESS + */ + public static final int PARAM_LOSSLESSPSV = 16; + /** + * Lossless JPEG point transform (Pt) + * + *

      Value + *

        + *
      • 0 through precision - 1, where + * precision is the JPEG data precision in bits [default for + * compression: 0] + *
      + * + *

      A point transform value of 0 is necessary in order to + * generate a fully lossless JPEG image. (A non-zero point transform value + * right-shifts the input samples by the specified number of bits, which is + * effectively a form of lossy color quantization.) + * + * @see #PARAM_LOSSLESS + * @see #PARAM_PRECISION + */ + public static final int PARAM_LOSSLESSPT = 17; + /** + * JPEG restart marker interval in MCU blocks (lossy) or samples (lossless) + * [compression only] + * + *

      The nature of entropy coding is such that a corrupt JPEG image cannot + * be decompressed beyond the point of corruption unless it contains restart + * markers. A restart marker stops and restarts the entropy coding algorithm + * so that, if a JPEG image is corrupted, decompression can resume at the + * next marker. Thus, adding more restart markers improves the fault + * tolerance of the JPEG image, but adding too many restart markers can + * adversely affect the compression ratio and performance. + * + *

      Value + *

        + *
      • the number of MCU blocks or samples between each restart marker + * [default: 0 (no restart markers)] *
      + * + *

      Setting this parameter to a non-zero value sets + * {@link #PARAM_RESTARTROWS} to 0. + */ + public static final int PARAM_RESTARTBLOCKS = 18; + /** + * JPEG restart marker interval in MCU rows (lossy) or sample rows (lossless) + * [compression only] + * + *

      See {@link #PARAM_RESTARTBLOCKS} for a description of restart markers. + * + *

      Value + *

        + *
      • the number of MCU rows or sample rows between each restart marker + * [default: 0 (no restart markers)] + *
      + * + *

      Setting this parameter to a non-zero value sets + * {@link #PARAM_RESTARTBLOCKS} to 0. + */ + public static final int PARAM_RESTARTROWS = 19; + /** + * JPEG horizontal pixel density + * + *

      Value + *

        + *
      • The JPEG image has (decompression) or will have (compression) the + * specified horizontal pixel density [default for compression: + * 1]. + *
      + * + *

      This value is stored in or read from the JPEG header. It does not + * affect the contents of the JPEG image. + * + * @see #PARAM_DENSITYUNITS */ - public static final int FLAG_LOSSLESS = (1 << 17); + public static final int PARAM_XDENSITY = 20; + /** + * JPEG vertical pixel density + * + *

      Value + *

        + *
      • The JPEG image has (decompression) or will have (compression) the + * specified vertical pixel density [default for compression: + * 1]. + *
      + * + *

      This value is stored in or read from the JPEG header. It does not + * affect the contents of the JPEG image. + * + * @see #PARAM_DENSITYUNITS + */ + public static final int PARAM_YDENSITY = 21; + /** + * JPEG pixel density units + * + *

      Value + *

        + *
      • 0 [default for compression] The pixel density of + * the JPEG image is expressed (decompression) or will be expressed + * (compression) in unknown units. + *
      • 1 The pixel density of the JPEG image is expressed + * (decompression) or will be expressed (compression) in units of + * pixels/inch. + *
      • 2 The pixel density of the JPEG image is expressed + * (decompression) or will be expressed (compression) in units of pixels/cm. + *
      + * + *

      This value is stored in or read from the JPEG header. It does not + * affect the contents of the JPEG image. + * + * @see #PARAM_XDENSITY + * @see #PARAM_YDENSITY + */ + public static final int PARAM_DENSITYUNITS = 22; + + + /** + * @deprecated Use {@link #PARAM_BOTTOMUP} instead. + */ + @Deprecated + public static final int FLAG_BOTTOMUP = 2; + /** + * @deprecated Use {@link #PARAM_FASTUPSAMPLE} instead. + */ + @Deprecated + public static final int FLAG_FASTUPSAMPLE = 256; + /** + * @deprecated Use {@link #PARAM_FASTDCT} instead. + */ + @Deprecated + public static final int FLAG_FASTDCT = 2048; + /** + * @deprecated Use {@link #PARAM_FASTDCT} instead. + */ + @Deprecated + public static final int FLAG_ACCURATEDCT = 4096; + /** + * @deprecated Use {@link #PARAM_STOPONWARNING} instead. + */ + @Deprecated + public static final int FLAG_STOPONWARNING = 8192; + /** + * @deprecated Use {@link #PARAM_PROGRESSIVE} instead. + */ + @Deprecated + public static final int FLAG_PROGRESSIVE = 16384; + /** + * @deprecated Use {@link #PARAM_SCANLIMIT} instead. + */ + @Deprecated + public static final int FLAG_LIMITSCANS = 32768; + /** * The number of error codes @@ -493,7 +830,11 @@ public static int getAlphaOffset(int pixelFormat) { * @param height the height (in pixels) of the JPEG image * * @param jpegSubsamp the level of chrominance subsampling to be used when - * generating the JPEG image (one of {@link #SAMP_444 TJ.SAMP_*}) + * generating the JPEG image (one of {@link #SAMP_444 TJ.SAMP_*}.) + * {@link #SAMP_UNKNOWN} is treated like {@link #SAMP_444}, since a buffer + * large enough to hold a JPEG image with no subsampling should also be large + * enough to hold a JPEG image with an arbitrary level of subsampling. Note + * that lossless JPEG images always use {@link #SAMP_444}. * * @return the maximum size of the buffer (in bytes) required to hold a JPEG * image with the given width, height, and level of chrominance subsampling. @@ -550,7 +891,7 @@ public static native int planeSizeYUV(int componentID, int width, int stride, /** * Returns the plane width of a YUV image plane with the given parameters. - * Refer to {@link YUVImage YUVImage} for a description of plane width. + * Refer to {@link YUVImage} for a description of plane width. * * @param componentID ID number of the image plane (0 = Y, 1 = U/Cb, * 2 = V/Cr) @@ -566,7 +907,7 @@ public static native int planeSizeYUV(int componentID, int width, int stride, /** * Returns the plane height of a YUV image plane with the given parameters. - * Refer to {@link YUVImage YUVImage} for a description of plane height. + * Refer to {@link YUVImage} for a description of plane height. * * @param componentID ID number of the image plane (0 = Y, 1 = U/Cb, * 2 = V/Cr) @@ -590,6 +931,17 @@ public static native int planeHeight(int componentID, int height, */ public static native TJScalingFactor[] getScalingFactors(); + /** + * A {@link TJScalingFactor} instance that specifies a scaling factor of 1/1 + * (no scaling) + */ + public static final TJScalingFactor UNSCALED = new TJScalingFactor(1, 1); + + /** + * A java.awt.Rectangle instance that specifies no cropping + */ + public static final Rectangle UNCROPPED = new Rectangle(0, 0, 0, 0); + static { TJLoader.load(); } diff --git a/java/org/libjpegturbo/turbojpeg/TJCompressor.java b/java/org/libjpegturbo/turbojpeg/TJCompressor.java index c6f9d1497..fed47de67 100644 --- a/java/org/libjpegturbo/turbojpeg/TJCompressor.java +++ b/java/org/libjpegturbo/turbojpeg/TJCompressor.java @@ -39,9 +39,6 @@ */ public class TJCompressor implements Closeable { - private static final String NO_ASSOC_ERROR = - "No source image is associated with this instance"; - /** * Create a TurboJPEG compressor instance. */ @@ -50,9 +47,9 @@ public TJCompressor() throws TJException { } /** - * Create a TurboJPEG compressor instance and associate the packed-pixel - * source image stored in srcImage with the newly created - * instance. + * Create a TurboJPEG compressor instance and associate the 8-bit-per-sample + * packed-pixel source image stored in srcImage with the newly + * created instance. * * @param srcImage see {@link #setSourceImage} for description * @@ -75,9 +72,9 @@ public TJCompressor(byte[] srcImage, int x, int y, int width, int pitch, } /** - * Create a TurboJPEG compressor instance and associate the packed-pixel - * source image stored in srcImage with the newly created - * instance. + * Create a TurboJPEG compressor instance and associate the 8-bit-per-sample + * packed-pixel source image stored in srcImage with the newly + * created instance. * * @param srcImage see * {@link #setSourceImage(BufferedImage, int, int, int, int)} for description @@ -100,8 +97,8 @@ public TJCompressor(BufferedImage srcImage, int x, int y, int width, } /** - * Associate a packed-pixel RGB, grayscale, or CMYK source image with this - * compressor instance. + * Associate an 8-bit-per-sample packed-pixel RGB, grayscale, or CMYK source + * image with this compressor instance. * * @param srcImage buffer containing a packed-pixel RGB, grayscale, or CMYK * source image to be compressed or encoded. This buffer is not modified. @@ -116,15 +113,13 @@ public TJCompressor(BufferedImage srcImage, int x, int y, int width, * which the JPEG or YUV image should be compressed/encoded * * @param pitch bytes per row in the source image. Normally this should be - * width * - * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat), - * if the source image is unpadded. However, you can use this parameter to, - * for instance, specify that the rows in the source image are padded to the - * nearest multiple of 4 bytes or to compress/encode a JPEG or YUV image from - * a region of a larger source image. You can also be clever and use this - * parameter to skip rows, etc. Setting this parameter to 0 is the + * width * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat), + * if the source image is unpadded. (Setting this parameter to 0 is the * equivalent of setting it to width * - * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat). + * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat).) However, + * you can also use this parameter to specify the row alignment/padding of + * the source image, to skip rows, or to compress/encode a JPEG or YUV image + * from a specific region of a larger source image. * * @param height height (in pixels) of the region in the source image from * which the JPEG or YUV image should be compressed/encoded @@ -139,7 +134,119 @@ public void setSourceImage(byte[] srcImage, int x, int y, int width, if (srcImage == null || x < 0 || y < 0 || width < 1 || height < 1 || pitch < 0 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF) throw new IllegalArgumentException("Invalid argument in setSourceImage()"); - srcBuf = srcImage; + srcBuf8 = srcImage; + srcWidth = width; + if (pitch == 0) + srcPitch = width * TJ.getPixelSize(pixelFormat); + else + srcPitch = pitch; + srcHeight = height; + srcPixelFormat = pixelFormat; + srcX = x; + srcY = y; + srcBuf12 = null; + srcBuf16 = null; + srcBufInt = null; + srcYUVImage = null; + } + + /** + * Associate a 12-bit-per-sample packed-pixel RGB, grayscale, or CMYK source + * image with this compressor instance. Note that 12-bit-per-sample + * packed-pixel source images can only be compressed into 12-bit-per-sample + * JPEG images. + * + * @param srcImage buffer containing a packed-pixel RGB, grayscale, or CMYK + * source image to be compressed. This buffer is not modified. + * + * @param x x offset (in pixels) of the region in the source image from which + * the JPEG image should be compressed + * + * @param y y offset (in pixels) of the region in the source image from which + * the JPEG image should be compressed + * + * @param width width (in pixels) of the region in the source image from + * which the JPEG image should be compressed + * + * @param pitch samples per row in the source image. Normally this should be + * width * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat), + * if the source image is unpadded. (Setting this parameter to 0 is the + * equivalent of setting it to width * + * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat).) However, + * you can also use this parameter to specify the row alignment/padding of + * the source image, to skip rows, or to compress a JPEG image from a + * specific region of a larger source image. + * + * @param height height (in pixels) of the region in the source image from + * which the JPEG image should be compressed + * + * @param pixelFormat pixel format of the source image (one of + * {@link TJ#PF_RGB TJ.PF_*}) + */ + public void setSourceImage12(short[] srcImage, int x, int y, int width, + int pitch, int height, int pixelFormat) + throws TJException { + if (handle == 0) init(); + if (srcImage == null || x < 0 || y < 0 || width < 1 || height < 1 || + pitch < 0 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF) + throw new IllegalArgumentException("Invalid argument in setSourceImage()"); + srcBuf12 = srcImage; + srcWidth = width; + if (pitch == 0) + srcPitch = width * TJ.getPixelSize(pixelFormat); + else + srcPitch = pitch; + srcHeight = height; + srcPixelFormat = pixelFormat; + srcX = x; + srcY = y; + srcBuf8 = null; + srcBuf16 = null; + srcBufInt = null; + srcYUVImage = null; + } + + /** + * Associate a 16-bit-per-sample packed-pixel RGB, grayscale, or CMYK source + * image with this compressor instance. Note that 16-bit-per-sample + * packed-pixel source images can only be compressed into 16-bit-per-sample + * lossless JPEG images. + * + * @param srcImage buffer containing a packed-pixel RGB, grayscale, or CMYK + * source image to be compressed. This buffer is not modified. + * + * @param x x offset (in pixels) of the region in the source image from which + * the JPEG image should be compressed + * + * @param y y offset (in pixels) of the region in the source image from which + * the JPEG image should be compressed + * + * @param width width (in pixels) of the region in the source image from + * which the JPEG image should be compressed + * + * @param pitch samples per row in the source image. Normally this should be + * width * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat), + * if the source image is unpadded. (Setting this parameter to 0 is the + * equivalent of setting it to width * + * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat).) However, + * you can also use this parameter to specify the row alignment/padding of + * the source image, to skip rows, or to compress a JPEG image from a + * specific region of a larger source image. + * + * @param height height (in pixels) of the region in the source image from + * which the JPEG image should be compressed + * + * @param pixelFormat pixel format of the source image (one of + * {@link TJ#PF_RGB TJ.PF_*}) + */ + public void setSourceImage16(short[] srcImage, int x, int y, int width, + int pitch, int height, int pixelFormat) + throws TJException { + if (handle == 0) init(); + if (srcImage == null || x < 0 || y < 0 || width < 1 || height < 1 || + pitch < 0 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF) + throw new IllegalArgumentException("Invalid argument in setSourceImage()"); + srcBuf16 = srcImage; srcWidth = width; if (pitch == 0) srcPitch = width * TJ.getPixelSize(pixelFormat); @@ -149,13 +256,15 @@ public void setSourceImage(byte[] srcImage, int x, int y, int width, srcPixelFormat = pixelFormat; srcX = x; srcY = y; + srcBuf8 = null; + srcBuf12 = null; srcBufInt = null; srcYUVImage = null; } /** - * Associate a packed-pixel RGB or grayscale source image with this - * compressor instance. + * Associate an 8-bit-per-pixel packed-pixel RGB or grayscale source image + * with this compressor instance. * * @param srcImage a BufferedImage instance containing a * packed-pixel RGB or grayscale source image to be compressed or encoded. @@ -225,7 +334,7 @@ public void setSourceImage(BufferedImage srcImage, int x, int y, int width, srcStride = sm.getScanlineStride(); DataBufferInt db = (DataBufferInt)wr.getDataBuffer(); srcBufInt = db.getData(); - srcBuf = null; + srcBuf8 = null; } else { ComponentSampleModel sm = (ComponentSampleModel)srcImage.getSampleModel(); @@ -234,14 +343,16 @@ public void setSourceImage(BufferedImage srcImage, int x, int y, int width, throw new IllegalArgumentException("Inconsistency between pixel format and pixel size in BufferedImage"); srcPitch = sm.getScanlineStride(); DataBufferByte db = (DataBufferByte)wr.getDataBuffer(); - srcBuf = db.getData(); + srcBuf8 = db.getData(); srcBufInt = null; } srcYUVImage = null; } /** - * Associate a planar YUV source image with this compressor instance. + * Associate an 8-bit-per-sample planar YUV source image with this compressor + * instance. This method sets {@link TJ#PARAM_SUBSAMP} to the chrominance + * subsampling level of the source image. * * @param srcImage planar YUV source image to be compressed. This image is * not modified. @@ -251,54 +362,53 @@ public void setSourceImage(YUVImage srcImage) throws TJException { if (srcImage == null) throw new IllegalArgumentException("Invalid argument in setSourceImage()"); srcYUVImage = srcImage; - srcBuf = null; + set(TJ.PARAM_SUBSAMP, srcImage.getSubsamp()); + srcBuf8 = null; srcBufInt = null; } /** - * Set the level of chrominance subsampling for subsequent compress/encode - * operations. When pixels are converted from RGB to YCbCr (see - * {@link TJ#CS_YCbCr}) or from CMYK to YCCK (see {@link TJ#CS_YCCK}) as part - * of the JPEG compression process, some of the Cb and Cr (chrominance) - * components can be discarded or averaged together to produce a smaller - * image with little perceptible loss of image clarity. (The human eye is - * more sensitive to small changes in brightness than to small changes in - * color.) This is called "chrominance subsampling". - *

      - * NOTE: This method has no effect when compressing a JPEG image from a - * planar YUV source image. In that case, the level of chrominance - * subsampling in the JPEG image is determined by the source image. - * Furthermore, this method has no effect when encoding to a pre-allocated - * {@link YUVImage} instance. In that case, the level of chrominance - * subsampling is determined by the destination image. - * - * @param newSubsamp the level of chrominance subsampling to use in - * subsequent compress/encode oeprations (one of - * {@link TJ#SAMP_444 TJ.SAMP_*}) + * Set the value of a compression parameter. + * + * @param param one of {@link TJ#PARAM_STOPONWARNING TJ.PARAM_*} + * + * @param value value of the compression parameter (refer to + * {@link TJ#PARAM_STOPONWARNING parameter documentation}) + */ + public native void set(int param, int value); + + /** + * Get the value of a compression parameter. + * + * @param param one of {@link TJ#PARAM_STOPONWARNING TJ.PARAM_*} + * + * @return the value of the specified compression parameter, or -1 if the + * value is unknown. + */ + public native int get(int param); + + /** + * @deprecated Use + * {@link #set set}({@link TJ#PARAM_SUBSAMP}, ...) instead. */ - public void setSubsamp(int newSubsamp) { - if (newSubsamp < 0 || newSubsamp >= TJ.NUMSAMP) + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated + public void setSubsamp(int subsamp) { + if (subsamp < 0 || subsamp >= TJ.NUMSAMP) throw new IllegalArgumentException("Invalid argument in setSubsamp()"); - subsamp = newSubsamp; + set(TJ.PARAM_SUBSAMP, subsamp); } /** - * Set the JPEG image quality level for subsequent compress operations. - * - * @param quality the new JPEG image quality level (1 to 100, 1 = worst, - * 100 = best.) When generating a lossless JPEG image (see - * {@link TJ#FLAG_LOSSLESS}), quality is - * psv * 10 + Pt, where psv is the predictor - * selection value (1-7) and Pt is the point transform (0-7). A - * point transform value of 0 is necessary in order to create a fully - * lossless JPEG image. (A non-zero point transform value right-shifts the - * input samples by the specified number of bits, which is effectively a form - * of lossy color quantization.) + * @deprecated Use + * {@link #set set}({@link TJ#PARAM_QUALITY}, ...) instead. */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated public void setJPEGQuality(int quality) { if (quality < 1 || quality > 100) throw new IllegalArgumentException("Invalid argument in setJPEGQuality()"); - jpegQuality = quality; + set(TJ.PARAM_QUALITY, quality); } /** @@ -307,139 +417,180 @@ public void setJPEGQuality(int quality) { * buffer. * * @param dstBuf buffer that will receive the JPEG image. Use - * {@link TJ#bufSize} to determine the maximum size for this buffer based on - * the source image's width and height and the desired level of chrominance - * subsampling. - * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} + * {@link TJ#bufSize TJ.bufSize()} to determine the maximum size for this + * buffer based on the source image's width and height and the desired level + * of chrominance subsampling (see {@link TJ#PARAM_SUBSAMP}.) */ - public void compress(byte[] dstBuf, int flags) throws TJException { - if (dstBuf == null || flags < 0) + public void compress(byte[] dstBuf) throws TJException { + if (dstBuf == null) throw new IllegalArgumentException("Invalid argument in compress()"); - if (srcBuf == null && srcBufInt == null && srcYUVImage == null) - throw new IllegalStateException(NO_ASSOC_ERROR); - if (jpegQuality < 0) - throw new IllegalStateException("JPEG Quality not set"); - if (subsamp < 0 && srcYUVImage == null) - throw new IllegalStateException("Subsampling level not set"); - if (srcYUVImage != null) - compressedSize = compressFromYUV(srcYUVImage.getPlanes(), - srcYUVImage.getOffsets(), - srcYUVImage.getWidth(), - srcYUVImage.getStrides(), - srcYUVImage.getHeight(), - srcYUVImage.getSubsamp(), - dstBuf, jpegQuality, flags); - else if (srcBuf != null) - compressedSize = compress(srcBuf, srcX, srcY, srcWidth, srcPitch, - srcHeight, srcPixelFormat, dstBuf, subsamp, - jpegQuality, flags); + if (srcYUVImage != null) { + checkSubsampling(); + if (get(TJ.PARAM_SUBSAMP) != srcYUVImage.getSubsamp()) + throw new IllegalStateException("TJ.PARAM_SUBSAMP must match subsampling level of YUV image"); + compressedSize = compressFromYUV8(srcYUVImage.getPlanes(), + srcYUVImage.getOffsets(), + srcYUVImage.getWidth(), + srcYUVImage.getStrides(), + srcYUVImage.getHeight(), dstBuf); + } else if (srcBuf8 != null) + compressedSize = compress8(srcBuf8, srcX, srcY, srcWidth, srcPitch, + srcHeight, srcPixelFormat, dstBuf); + else if (srcBuf12 != null) + compressedSize = compress12(srcBuf12, srcX, srcY, srcWidth, srcPitch, + srcHeight, srcPixelFormat, dstBuf); + else if (srcBuf16 != null) + compressedSize = compress16(srcBuf16, srcX, srcY, srcWidth, srcPitch, + srcHeight, srcPixelFormat, dstBuf); else if (srcBufInt != null) - compressedSize = compress(srcBufInt, srcX, srcY, srcWidth, srcStride, - srcHeight, srcPixelFormat, dstBuf, subsamp, - jpegQuality, flags); + compressedSize = compress8(srcBufInt, srcX, srcY, srcWidth, srcStride, + srcHeight, srcPixelFormat, dstBuf); + else + throw new IllegalStateException("No source image is associated with this instance"); + } + + /** + * @deprecated Use {@link #set set()} and {@link #compress(byte[])} instead. + */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated + public void compress(byte[] dstBuf, int flags) throws TJException { + if (flags < 0) + throw new IllegalArgumentException("Invalid argument in compress()"); + processFlags(flags); + compress(dstBuf); } /** * Compress the packed-pixel or planar YUV source image associated with this * compressor instance and return a buffer containing a JPEG image. * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} - * * @return a buffer containing a JPEG image. The length of this buffer will - * not be equal to the size of the JPEG image. Use {@link - * #getCompressedSize} to obtain the size of the JPEG image. + * not be equal to the size of the JPEG image. Use + * {@link #getCompressedSize} to obtain the size of the JPEG image. */ - public byte[] compress(int flags) throws TJException { + public byte[] compress() throws TJException { byte[] buf; if (srcYUVImage != null) { buf = new byte[TJ.bufSize(srcYUVImage.getWidth(), srcYUVImage.getHeight(), srcYUVImage.getSubsamp())]; } else { - checkSourceImage(); + checkSubsampling(); + int subsamp = get(TJ.PARAM_SUBSAMP); buf = new byte[TJ.bufSize(srcWidth, srcHeight, subsamp)]; } - compress(buf, flags); + compress(buf); return buf; } /** - * Encode the packed-pixel source image associated with this compressor - * instance into a planar YUV image and store it in the given - * {@link YUVImage} instance. This method performs color conversion (which - * is accelerated in the libjpeg-turbo implementation) but does not execute - * any of the other steps in the JPEG compression process. Encoding CMYK - * source images into YUV images is not supported. + * @deprecated Use {@link #set set()} and {@link #compress()} instead. + */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated + public byte[] compress(int flags) throws TJException { + processFlags(flags); + return compress(); + } + + /** + * Encode the 8-bit-per-sample packed-pixel source image associated with this + * compressor instance into an 8-bit-per-sample planar YUV image and store it + * in the given {@link YUVImage} instance. This method performs color + * conversion (which is accelerated in the libjpeg-turbo implementation) but + * does not execute any of the other steps in the JPEG compression process. + * Encoding CMYK source images into YUV images is not supported. This method + * sets {@link TJ#PARAM_SUBSAMP} to the chrominance subsampling level of the + * destination image. * * @param dstImage {@link YUVImage} instance that will receive the planar YUV * image - * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} */ - public void encodeYUV(YUVImage dstImage, int flags) throws TJException { - if (dstImage == null || flags < 0) + public void encodeYUV(YUVImage dstImage) throws TJException { + if (dstImage == null) throw new IllegalArgumentException("Invalid argument in encodeYUV()"); - if (srcBuf == null && srcBufInt == null) - throw new IllegalStateException(NO_ASSOC_ERROR); + if (srcBuf8 == null && srcBufInt == null) + throw new IllegalStateException("No 8-bit-per-sample source image is associated with this instance"); if (srcYUVImage != null) throw new IllegalStateException("Source image is not correct type"); - checkSubsampling(); if (srcWidth != dstImage.getWidth() || srcHeight != dstImage.getHeight()) throw new IllegalStateException("Destination image is the wrong size"); + set(TJ.PARAM_SUBSAMP, dstImage.getSubsamp()); if (srcBufInt != null) { - encodeYUV(srcBufInt, srcX, srcY, srcWidth, srcStride, srcHeight, - srcPixelFormat, dstImage.getPlanes(), dstImage.getOffsets(), - dstImage.getStrides(), dstImage.getSubsamp(), flags); + encodeYUV8(srcBufInt, srcX, srcY, srcWidth, srcStride, srcHeight, + srcPixelFormat, dstImage.getPlanes(), dstImage.getOffsets(), + dstImage.getStrides()); } else { - encodeYUV(srcBuf, srcX, srcY, srcWidth, srcPitch, srcHeight, - srcPixelFormat, dstImage.getPlanes(), dstImage.getOffsets(), - dstImage.getStrides(), dstImage.getSubsamp(), flags); + encodeYUV8(srcBuf8, srcX, srcY, srcWidth, srcPitch, srcHeight, + srcPixelFormat, dstImage.getPlanes(), dstImage.getOffsets(), + dstImage.getStrides()); } compressedSize = 0; } /** - * Encode the packed-pixel source image associated with this compressor - * instance into a unified planar YUV image and return a {@link YUVImage} - * instance containing the encoded image. This method performs color - * conversion (which is accelerated in the libjpeg-turbo implementation) but - * does not execute any of the other steps in the JPEG compression process. - * Encoding CMYK source images into YUV images is not supported. + * @deprecated Use {@link #set set()} and {@link #encodeYUV(YUVImage)} + * instead. + */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated + public void encodeYUV(YUVImage dstImage, int flags) throws TJException { + if (flags < 0) + throw new IllegalArgumentException("Invalid argument in encodeYUV()"); + + processFlags(flags); + encodeYUV(dstImage); + } + + /** + * Encode the 8-bit-per-sample packed-pixel source image associated with this + * compressor instance into an 8-bit-per-sample unified planar YUV image and + * return a {@link YUVImage} instance containing the encoded image. This + * method performs color conversion (which is accelerated in the + * libjpeg-turbo implementation) but does not execute any of the other steps + * in the JPEG compression process. Encoding CMYK source images into YUV + * images is not supported. * * @param align row alignment (in bytes) of the YUV image (must be a power of * 2.) Setting this parameter to n will cause each row in each plane of the * YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.) * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} - * * @return a {@link YUVImage} instance containing the unified planar YUV * encoded image */ - public YUVImage encodeYUV(int align, int flags) throws TJException { - checkSourceImage(); + public YUVImage encodeYUV(int align) throws TJException { + if (srcBuf8 == null && srcBufInt == null) + throw new IllegalStateException("No 8-bit-per-sample source image is associated with this instance"); checkSubsampling(); if (align < 1 || ((align & (align - 1)) != 0)) throw new IllegalStateException("Invalid argument in encodeYUV()"); - YUVImage dstYUVImage = new YUVImage(srcWidth, align, srcHeight, subsamp); - encodeYUV(dstYUVImage, flags); + YUVImage dstYUVImage = new YUVImage(srcWidth, align, srcHeight, + get(TJ.PARAM_SUBSAMP)); + encodeYUV(dstYUVImage); return dstYUVImage; } /** - * Encode the packed-pixel source image associated with this compressor - * instance into separate Y, U (Cb), and V (Cr) image planes and return a - * {@link YUVImage} instance containing the encoded image planes. This - * method performs color conversion (which is accelerated in the - * libjpeg-turbo implementation) but does not execute any of the other steps - * in the JPEG compression process. Encoding CMYK source images into YUV - * images is not supported. + * @deprecated Use {@link #set set()} and {@link #encodeYUV(int)} instead. + */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated + public YUVImage encodeYUV(int align, int flags) throws TJException { + processFlags(flags); + return encodeYUV(align); + } + + /** + * Encode the 8-bit-per-sample packed-pixel source image associated with this + * compressor instance into separate 8-bit-per-sample Y, U (Cb), and V (Cr) + * image planes and return a {@link YUVImage} instance containing the encoded + * image planes. This method performs color conversion (which is accelerated + * in the libjpeg-turbo implementation) but does not execute any of the other + * steps in the JPEG compression process. Encoding CMYK source images into + * YUV images is not supported. * * @param strides an array of integers, each specifying the number of bytes * per row in the corresponding plane of the YUV source image. Setting the @@ -449,19 +600,28 @@ public YUVImage encodeYUV(int align, int flags) throws TJException { * adjust the strides in order to add an arbitrary amount of row padding to * each plane. * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} - * * @return a {@link YUVImage} instance containing the encoded image planes */ - public YUVImage encodeYUV(int[] strides, int flags) throws TJException { - checkSourceImage(); + public YUVImage encodeYUV(int[] strides) throws TJException { + if (srcBuf8 == null && srcBufInt == null) + throw new IllegalStateException("No 8-bit-per-sample source image is associated with this instance"); checkSubsampling(); - YUVImage dstYUVImage = new YUVImage(srcWidth, strides, srcHeight, subsamp); - encodeYUV(dstYUVImage, flags); + YUVImage dstYUVImage = new YUVImage(srcWidth, strides, srcHeight, + get(TJ.PARAM_SUBSAMP)); + encodeYUV(dstYUVImage); return dstYUVImage; } + /** + * @deprecated Use {@link #set set()} and {@link #encodeYUV(int[])} instead. + */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated + public YUVImage encodeYUV(int[] strides, int flags) throws TJException { + processFlags(flags); + return encodeYUV(strides); + } + /** * Returns the size of the image (in bytes) generated by the most recent * compress operation. @@ -493,55 +653,82 @@ protected void finalize() throws Throwable { } }; + @SuppressWarnings("deprecation") + private void processFlags(int flags) { + set(TJ.PARAM_BOTTOMUP, (flags & TJ.FLAG_BOTTOMUP) != 0 ? 1 : 0); + + if (get(TJ.PARAM_QUALITY) >= 96 || (flags & TJ.FLAG_ACCURATEDCT) != 0) + set(TJ.PARAM_FASTDCT, 0); + else + set(TJ.PARAM_FASTDCT, 1); + + set(TJ.PARAM_STOPONWARNING, (flags & TJ.FLAG_STOPONWARNING) != 0 ? 1 : 0); + set(TJ.PARAM_PROGRESSIVE, (flags & TJ.FLAG_PROGRESSIVE) != 0 ? 1 : 0); + } + + private void checkSubsampling() { + if (get(TJ.PARAM_SUBSAMP) == TJ.SAMP_UNKNOWN) + throw new IllegalStateException("TJ.PARAM_SUBSAMP must be specified"); + } + private native void init() throws TJException; private native void destroy() throws TJException; // JPEG size in bytes is returned @SuppressWarnings("checkstyle:HiddenField") - private native int compress(byte[] srcBuf, int x, int y, int width, - int pitch, int height, int pixelFormat, byte[] jpegBuf, int jpegSubsamp, - int jpegQual, int flags) throws TJException; + private native int compress8(byte[] srcBuf, int x, int y, int width, + int pitch, int height, int pixelFormat, byte[] jpegBuf) throws TJException; + + @SuppressWarnings("checkstyle:HiddenField") + private native int compress12(short[] srcBuf, int x, int y, int width, + int pitch, int height, int pixelFormat, byte[] jpegBuf) throws TJException; @SuppressWarnings("checkstyle:HiddenField") - private native int compress(int[] srcBuf, int x, int y, int width, - int stride, int height, int pixelFormat, byte[] jpegBuf, int jpegSubsamp, - int jpegQual, int flags) throws TJException; + private native int compress16(short[] srcBuf, int x, int y, int width, + int pitch, int height, int pixelFormat, byte[] jpegBuf) throws TJException; @SuppressWarnings("checkstyle:HiddenField") - private native int compressFromYUV(byte[][] srcPlanes, int[] srcOffsets, - int width, int[] srcStrides, int height, int subsamp, byte[] jpegBuf, - int jpegQual, int flags) + private native int compress8(int[] srcBuf, int x, int y, int width, + int stride, int height, int pixelFormat, byte[] jpegBuf) throws TJException; @SuppressWarnings("checkstyle:HiddenField") - private native void encodeYUV(byte[] srcBuf, int x, int y, int width, - int pitch, int height, int pixelFormat, byte[][] dstPlanes, - int[] dstOffsets, int[] dstStrides, int subsamp, int flags) + private native int compressFromYUV8(byte[][] srcPlanes, int[] srcOffsets, + int width, int[] srcStrides, int height, byte[] jpegBuf) throws TJException; @SuppressWarnings("checkstyle:HiddenField") - private native void encodeYUV(int[] srcBuf, int x, int y, int width, + private native void encodeYUV8(byte[] srcBuf, int x, int y, int width, + int pitch, int height, int pixelFormat, byte[][] dstPlanes, + int[] dstOffsets, int[] dstStrides) throws TJException; + + @SuppressWarnings("checkstyle:HiddenField") + private native void encodeYUV8(int[] srcBuf, int x, int y, int width, int srcStride, int height, int pixelFormat, byte[][] dstPlanes, - int[] dstOffsets, int[] dstStrides, int subsamp, int flags) - throws TJException; + int[] dstOffsets, int[] dstStrides) throws TJException; + + /** + * @hidden + * Ugly hack alert. It isn't straightforward to load 12-bit-per-sample and + * 16-bit-per-sample images using the ImageIO and BufferedImage classes, and + * ImageIO doesn't support PBMPLUS files anyhow. This method accesses + * tj3LoadImage() through JNI and copies the pixel data between the C and + * Java heaps. Currently it is undocumented and used only by TJBench. + */ + @SuppressWarnings("checkstyle:JavadocMethod") + public native Object loadImage(int precision, String fileName, int[] width, + int align, int[] height, int[] pixelFormat) + throws TJException; static { TJLoader.load(); } - private void checkSourceImage() { - if (srcWidth < 1 || srcHeight < 1) - throw new IllegalStateException(NO_ASSOC_ERROR); - } - - private void checkSubsampling() { - if (subsamp < 0) - throw new IllegalStateException("Subsampling level not set"); - } - private long handle = 0; - private byte[] srcBuf = null; + private byte[] srcBuf8 = null; + private short[] srcBuf12 = null; + private short[] srcBuf16 = null; private int[] srcBufInt = null; private int srcWidth = 0; private int srcHeight = 0; @@ -551,8 +738,6 @@ private void checkSubsampling() { private int srcStride = 0; private int srcPixelFormat = -1; private YUVImage srcYUVImage = null; - private int subsamp = -1; - private int jpegQuality = -1; private int compressedSize = 0; private ByteOrder byteOrder = null; } diff --git a/java/org/libjpegturbo/turbojpeg/TJCustomFilter.java b/java/org/libjpegturbo/turbojpeg/TJCustomFilter.java index 3a66fd9ed..78e6e4e1f 100644 --- a/java/org/libjpegturbo/turbojpeg/TJCustomFilter.java +++ b/java/org/libjpegturbo/turbojpeg/TJCustomFilter.java @@ -63,8 +63,8 @@ public interface TJCustomFilter { * * @param transformID ID number of the transformed image to which * coeffBuffer belongs. This is the same as the index of the - * transform in the transforms array that was passed to {@link - * TJTransformer#transform TJTransformer.transform()}. + * transform in the transforms array that was passed to + * {@link TJTransformer#transform TJTransformer.transform()}. * * @param transform a {@link TJTransform} instance that specifies the * parameters and/or cropping region for this transform diff --git a/java/org/libjpegturbo/turbojpeg/TJDecompressor.java b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java index 976d86a65..d8c054591 100644 --- a/java/org/libjpegturbo/turbojpeg/TJDecompressor.java +++ b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java @@ -30,6 +30,7 @@ package org.libjpegturbo.turbojpeg; +import java.awt.Rectangle; import java.awt.image.*; import java.nio.*; import java.io.*; @@ -52,7 +53,8 @@ public TJDecompressor() throws TJException { /** * Create a TurboJPEG decompressor instance and associate the JPEG source * image or "abbreviated table specification" (AKA "tables-only") datastream - * stored in jpegImage with the newly created instance. + * stored in jpegImage with the newly created instance. Refer + * to {@link #setSourceImage(byte[], int)} for more details. * * @param jpegImage buffer containing a JPEG source image or tables-only * datastream. (The size of the JPEG image or datastream is assumed to be @@ -67,7 +69,8 @@ public TJDecompressor(byte[] jpegImage) throws TJException { * Create a TurboJPEG decompressor instance and associate the JPEG source * image or "abbreviated table specification" (AKA "tables-only") datastream * of length imageSize bytes stored in jpegImage - * with the newly created instance. + * with the newly created instance. Refer to + * {@link #setSourceImage(byte[], int)} for more details. * * @param jpegImage buffer containing a JPEG source image or tables-only * datastream. This buffer is not modified. @@ -81,9 +84,10 @@ public TJDecompressor(byte[] jpegImage, int imageSize) throws TJException { } /** - * Create a TurboJPEG decompressor instance and associate the planar YUV - * source image stored in yuvImage with the newly created - * instance. + * Create a TurboJPEG decompressor instance and associate the + * 8-bit-per-sample planar YUV source image stored in yuvImage + * with the newly created instance. Refer to + * {@link #setSourceImage(YUVImage)} for more details. * * @param yuvImage {@link YUVImage} instance containing a planar YUV source * image to be decoded. This image is not modified. @@ -104,7 +108,9 @@ public TJDecompressor(YUVImage yuvImage) throws TJException { * quantization and Huffman tables that can be used when decompressing * subsequent "abbreviated image" datastreams. This is useful, for instance, * when decompressing video streams in which all frames share the same - * quantization and Huffman tables. + * quantization and Huffman tables. If a JPEG image is passed to this + * method, then the {@link TJ#PARAM_STOPONWARNING parameters} that describe + * the JPEG image will be set when the method returns. * * @param jpegImage buffer containing a JPEG source image or tables-only * datastream. This buffer is not modified. @@ -125,7 +131,9 @@ public void setSourceImage(byte[] jpegImage, int imageSize) /** * Associate the specified planar YUV source image with this decompressor * instance. Subsequent decompression operations will decode this image into - * a packed-pixel RGB or grayscale destination image. + * a packed-pixel RGB or grayscale destination image. This method sets + * {@link TJ#PARAM_SUBSAMP} to the chrominance subsampling level of the + * source image. * * @param srcImage {@link YUVImage} instance containing a planar YUV source * image to be decoded. This image is not modified. @@ -134,6 +142,7 @@ public void setSourceImage(YUVImage srcImage) { if (srcImage == null) throw new IllegalArgumentException("Invalid argument in setSourceImage()"); yuvImage = srcImage; + set(TJ.PARAM_SUBSAMP, srcImage.getSubsamp()); jpegBuf = null; jpegBufSize = 0; } @@ -149,6 +158,11 @@ public void setSourceImage(YUVImage srcImage) { public int getWidth() { if (yuvImage != null) return yuvImage.getWidth(); + return getJPEGWidth(); + } + + private int getJPEGWidth() { + int jpegWidth = get(TJ.PARAM_JPEGWIDTH); if (jpegWidth < 1) throw new IllegalStateException(NO_ASSOC_ERROR); return jpegWidth; @@ -164,40 +178,125 @@ public int getWidth() { public int getHeight() { if (yuvImage != null) return yuvImage.getHeight(); + return getJPEGHeight(); + } + + private int getJPEGHeight() { + int jpegHeight = get(TJ.PARAM_JPEGHEIGHT); if (jpegHeight < 1) throw new IllegalStateException(NO_ASSOC_ERROR); return jpegHeight; } /** - * Returns the level of chrominance subsampling used in the source image - * (JPEG or YUV) associated with this decompressor instance. See - * {@link TJ#SAMP_444 TJ.SAMP_*}. + * Set the value of a decompression parameter. + * + * @param param one of {@link TJ#PARAM_STOPONWARNING TJ.PARAM_*} + * + * @param value value of the decompression parameter (refer to + * {@link TJ#PARAM_STOPONWARNING parameter documentation}) + */ + public native void set(int param, int value); + + /** + * Get the value of a decompression parameter. * - * @return the level of chrominance subsampling used in the source image - * (JPEG or YUV) associated with this decompressor instance. + * @param param one of {@link TJ#PARAM_STOPONWARNING TJ.PARAM_*} + * + * @return the value of the specified decompression parameter, or -1 if the + * value is unknown. + */ + public native int get(int param); + + /** + * Set the scaling factor for subsequent lossy decompression operations. + * + * @param scalingFactor {@link TJScalingFactor} instance that specifies a + * fractional scaling factor that the decompressor supports (see + * {@link TJ#getScalingFactors}), or {@link TJ#UNSCALED} for no scaling. + * Decompression scaling is a function of the IDCT algorithm, so scaling + * factors are generally limited to multiples of 1/8. If the entire JPEG + * image will be decompressed, then the width and height of the scaled + * destination image can be determined by calling + * scalingFactor.{@link TJScalingFactor#getScaled getScaled()} + * with the JPEG image width and height (see {@link #getWidth} and + * {@link #getHeight}.) When decompressing into a planar YUV image, an + * intermediate buffer copy will be performed if the width or height of the + * scaled destination image is not an even multiple of the MCU block size + * (see {@link TJ#getMCUWidth TJ.getMCUWidth()} and {@link TJ#getMCUHeight + * TJ.getMCUHeight()}.) Note that decompression scaling is not available + * (and the specified scaling factor is ignored) when decompressing lossless + * JPEG images (see {@link TJ#PARAM_LOSSLESS}), since the IDCT algorithm is + * not used with those images. Note also that {@link TJ#PARAM_FASTDCT} is + * ignored when decompression scaling is enabled. */ + @SuppressWarnings("checkstyle:HiddenField") + public void setScalingFactor(TJScalingFactor scalingFactor) { + if (scalingFactor == null) + throw new IllegalArgumentException("Invalid argument in setScalingFactor()"); + + TJScalingFactor[] sf = TJ.getScalingFactors(); + int i; + for (i = 0; i < sf.length; i++) { + if (scalingFactor.getNum() == sf[i].getNum() && + scalingFactor.getDenom() == sf[i].getDenom()) + break; + } + if (i >= sf.length) + throw new IllegalArgumentException("Unsupported scaling factor"); + + this.scalingFactor = scalingFactor; + } + + /** + * Set the cropping region for partially decompressing a lossy JPEG image + * into a packed-pixel image. + * + * @param croppingRegion java.awt.Rectangle instance that + * specifies a subregion of the JPEG image to decompress, or + * {@link TJ#UNCROPPED} for no cropping. The left boundary of the cropping + * region must be evenly divisible by the scaled MCU block width, which can + * be determined by calling {@link TJScalingFactor#getScaled + * TJScalingFactor.getScaled()} with the specified scaling factor (see + * {@link #setScalingFactor setScalingFactor()}) and the MCU block width + * (see {@link TJ#getMCUWidth TJ.getMCUWidth()}) for the level of chrominance + * subsampling in the JPEG image (see {@link TJ#PARAM_SUBSAMP}.) The + * cropping region should be specified relative to the scaled image + * dimensions. Unless croppingRegion is {@link TJ#UNCROPPED}, + * the JPEG header must be read (see {@link #setSourceImage(byte[], int)} + * prior to calling this method. + */ + @SuppressWarnings("checkstyle:HiddenField") + public void setCroppingRegion(Rectangle croppingRegion) throws TJException { + this.croppingRegion = croppingRegion; + setCroppingRegion(); + } + + /** + * @deprecated Use {@link #get get}({@link TJ#PARAM_SUBSAMP}) + * instead. + */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated public int getSubsamp() { - if (yuvImage != null) - return yuvImage.getSubsamp(); - if (jpegSubsamp < 0) + int subsamp = get(TJ.PARAM_SUBSAMP); + if (subsamp == TJ.SAMP_UNKNOWN) throw new IllegalStateException(NO_ASSOC_ERROR); - if (jpegSubsamp >= TJ.NUMSAMP) + if (subsamp >= TJ.NUMSAMP) throw new IllegalStateException("JPEG header information is invalid"); - return jpegSubsamp; + return subsamp; } /** - * Returns the colorspace used in the source image (JPEG or YUV) associated - * with this decompressor instance. See {@link TJ#CS_RGB TJ.CS_*}. If the - * source image is YUV, then this always returns {@link TJ#CS_YCbCr}. - * - * @return the colorspace used in the source image (JPEG or YUV) associated - * with this decompressor instance. + * @deprecated Use {@link #get get}({@link TJ#PARAM_COLORSPACE}) + * instead. */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated public int getColorspace() { if (yuvImage != null) return TJ.CS_YCbCr; + int jpegColorspace = get(TJ.PARAM_COLORSPACE); if (jpegColorspace < 0) throw new IllegalStateException(NO_ASSOC_ERROR); if (jpegColorspace >= TJ.NUMCS) @@ -205,21 +304,6 @@ public int getColorspace() { return jpegColorspace; } - /** - * Returns the bitwise OR of one or more of the - * {@link TJ#FLAG_BOTTOMUP flags}, such as - * {@link TJ#FLAG_PROGRESSIVE TJ.FLAG_PROGRESSIVE} and - * {@link TJ#FLAG_LOSSLESS TJ.FLAG_LOSSLESS}, that describe the JPEG image. - * - * @return the bitwise OR of one or more of the - * {@link TJ#FLAG_BOTTOMUP flags} that describe the JPEG image. - */ - public int getFlags() { - if (jpegFlags < 0) - throw new IllegalStateException(NO_ASSOC_ERROR); - return jpegFlags; - } - /** * Returns the JPEG buffer associated with this decompressor instance. * @@ -245,109 +329,75 @@ public int getJPEGSize() { } /** - * Returns the width of the largest scaled-down image that the TurboJPEG - * decompressor can generate without exceeding the desired image width and - * height. - * - * @param desiredWidth desired width (in pixels) of the decompressed image. - * Setting this to 0 is the same as setting it to the width of the JPEG - * image. (In other words, the width will not be considered when determining - * the scaled image size.) - * - * @param desiredHeight desired height (in pixels) of the decompressed image. - * Setting this to 0 is the same as setting it to the height of the JPEG - * image. (In other words, the height will not be considered when - * determining the scaled image size.) - * - * @return the width of the largest scaled-down image that the TurboJPEG - * decompressor can generate without exceeding the desired image width and - * height. + * @deprecated Use {@link #setScalingFactor setScalingFactor()} and + * {@link TJScalingFactor#getScaled TJScalingFactor.getScaled()} instead. */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated public int getScaledWidth(int desiredWidth, int desiredHeight) { - if (jpegWidth < 1 || jpegHeight < 1) - throw new IllegalStateException(NO_ASSOC_ERROR); - if (desiredWidth < 0 || desiredHeight < 0) - throw new IllegalArgumentException("Invalid argument in getScaledWidth()"); - TJScalingFactor[] sf = TJ.getScalingFactors(); - if (desiredWidth == 0) - desiredWidth = jpegWidth; - if (desiredHeight == 0) - desiredHeight = jpegHeight; - int scaledWidth = jpegWidth, scaledHeight = jpegHeight; - for (int i = 0; i < sf.length; i++) { - scaledWidth = sf[i].getScaled(jpegWidth); - scaledHeight = sf[i].getScaled(jpegHeight); - if (scaledWidth <= desiredWidth && scaledHeight <= desiredHeight) - break; - } - if (scaledWidth > desiredWidth || scaledHeight > desiredHeight) - throw new IllegalArgumentException("Could not scale down to desired image dimensions"); - return scaledWidth; + TJScalingFactor sf = getScalingFactor(desiredWidth, desiredHeight); + return sf.getScaled(getJPEGWidth()); } /** - * Returns the height of the largest scaled-down image that the TurboJPEG - * decompressor can generate without exceeding the desired image width and - * height. - * - * @param desiredWidth desired width (in pixels) of the decompressed image. - * Setting this to 0 is the same as setting it to the width of the JPEG - * image. (In other words, the width will not be considered when determining - * the scaled image size.) - * - * @param desiredHeight desired height (in pixels) of the decompressed image. - * Setting this to 0 is the same as setting it to the height of the JPEG - * image. (In other words, the height will not be considered when - * determining the scaled image size.) - * - * @return the height of the largest scaled-down image that the TurboJPEG - * decompressor can generate without exceeding the desired image width and - * height. + * @deprecated Use {@link #setScalingFactor setScalingFactor()} and + * {@link TJScalingFactor#getScaled TJScalingFactor.getScaled()} instead. */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated public int getScaledHeight(int desiredWidth, int desiredHeight) { - if (jpegWidth < 1 || jpegHeight < 1) - throw new IllegalStateException(NO_ASSOC_ERROR); + TJScalingFactor sf = getScalingFactor(desiredWidth, desiredHeight); + return sf.getScaled(getJPEGHeight()); + } + + private TJScalingFactor getScalingFactor(int desiredWidth, + int desiredHeight) { + int jpegWidth = getJPEGWidth(); + int jpegHeight = getJPEGHeight(); if (desiredWidth < 0 || desiredHeight < 0) - throw new IllegalArgumentException("Invalid argument in getScaledHeight()"); + throw new IllegalArgumentException("Invalid argument"); + TJScalingFactor[] sf = TJ.getScalingFactors(); + if (desiredWidth == 0) desiredWidth = jpegWidth; if (desiredHeight == 0) desiredHeight = jpegHeight; - int scaledWidth = jpegWidth, scaledHeight = jpegHeight; - for (int i = 0; i < sf.length; i++) { - scaledWidth = sf[i].getScaled(jpegWidth); - scaledHeight = sf[i].getScaled(jpegHeight); - if (scaledWidth <= desiredWidth && scaledHeight <= desiredHeight) + int i; + for (i = 0; i < sf.length; i++) { + if (sf[i].getScaled(jpegWidth) <= desiredWidth && + sf[i].getScaled(jpegHeight) <= desiredHeight) break; } - if (scaledWidth > desiredWidth || scaledHeight > desiredHeight) + if (i >= sf.length) throw new IllegalArgumentException("Could not scale down to desired image dimensions"); - return scaledHeight; + + return sf[i]; } /** - * Decompress the JPEG source image or decode the planar YUV source image - * associated with this decompressor instance and output a packed-pixel - * grayscale, RGB, or CMYK image to the given destination buffer. + * Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + * source image associated with this decompressor instance and output an + * 8-bit-per-sample packed-pixel grayscale, RGB, or CMYK image to the given + * destination buffer. *

      * NOTE: The destination image is fully recoverable if this method throws a - * non-fatal {@link TJException} (unless - * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.) + * non-fatal {@link TJException} (unless {@link TJ#PARAM_STOPONWARNING} is + * set.) * * @param dstBuf buffer that will receive the packed-pixel - * decompressed/decoded image. If the source image is a JPEG image, then - * this buffer should normally be pitch * scaledHeight bytes in - * size, where scaledHeight can be determined by calling - * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegHeight) - * with one of the scaling factors returned from {@link TJ#getScalingFactors} - * or by calling {@link #getScaledHeight}. If the source image is a YUV - * image, then this buffer should normally be pitch * height - * bytes in size, where height is the height of the YUV image. - * However, the buffer may also be larger than the dimensions of the source - * image, in which case the x, y, and + * decompressed/decoded image. This buffer should normally be + * pitch * destinationHeight bytes in size. However, the buffer + * may also be larger, in which case the x, y, and * pitch parameters can be used to specify the region into which - * the source image should be decompressed/decoded. + * the source image should be decompressed/decoded. NOTE: If the source + * image is a lossy JPEG image, then destinationHeight is either + * the scaled JPEG height (see {@link #setScalingFactor setScalingFactor()}, + * {@link TJScalingFactor#getScaled TJScalingFactor.getScaled()}, and + * {@link #getHeight}) or the height of the cropping region (see + * {@link #setCroppingRegion setCroppingRegion()}.) If the source image is a + * YUV image or a lossless JPEG image, then destinationHeight is + * the height of the source image. * * @param x x offset (in pixels) of the region in the destination image into * which the source image should be decompressed/decoded @@ -355,167 +405,338 @@ public int getScaledHeight(int desiredWidth, int desiredHeight) { * @param y y offset (in pixels) of the region in the destination image into * which the source image should be decompressed/decoded * - * @param desiredWidth If the source image is a JPEG image, then this - * specifies the desired width (in pixels) of the decompressed image (or - * image region.) If the desired destination image dimensions are different - * than the source image dimensions, then TurboJPEG will use scaling in the - * JPEG decompressor to generate the largest possible image that will fit - * within the desired dimensions. Setting this to 0 is the same as setting - * it to the width of the JPEG image. (In other words, the width will not be - * considered when determining the scaled image size.) This parameter is - * ignored if the source image is a YUV image. - * * @param pitch bytes per row in the destination image. Normally this should - * be set to scaledWidth * - * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat), - * if the destination image will be unpadded. However, you can use this to, - * for instance, pad each row of the destination image to the nearest - * multiple of 4 bytes or to decompress/decode the source image into a region - * of a larger image. NOTE: if the source image is a JPEG image, then - * scaledWidth can be determined by calling - * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegWidth) - * or by calling {@link #getScaledWidth}. If the source image is a YUV - * image, then scaledWidth is the width of the YUV image. - * Setting this parameter to 0 is the equivalent of setting it to - * scaledWidth * - * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat). - * - * @param desiredHeight If the source image is a JPEG image, then this - * specifies the desired height (in pixels) of the decompressed image (or - * image region.) If the desired destination image dimensions are different - * than the source image dimensions, then TurboJPEG will use scaling in the - * JPEG decompressor to generate the largest possible image that will fit - * within the desired dimensions. Setting this to 0 is the same as setting - * it to the height of the JPEG image. (In other words, the height will not - * be considered when determining the scaled image size.) This parameter is - * ignored if the source image is a YUV image. + * be set to destinationWidth * + * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat), if the + * destination image will be unpadded. (Setting this parameter to 0 is the + * equivalent of setting it to destinationWidth * + * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat).) However, + * you can also use this parameter to specify the row alignment/padding of + * the destination image, to skip rows, or to decompress/decode into a + * specific region of a larger image. NOTE: if the source image is a lossy + * JPEG image, then destinationWidth is either the scaled JPEG + * width (see {@link #setScalingFactor setScalingFactor()}, + * {@link TJScalingFactor#getScaled TJScalingFactor.getScaled()}, and + * {@link #getWidth}) or the width of the cropping region (see + * {@link #setCroppingRegion setCroppingRegion()}.) If the source image is a + * YUV image or a lossless JPEG image, then destinationWidth is + * the width of the source image. * * @param pixelFormat pixel format of the decompressed/decoded image (one of * {@link TJ#PF_RGB TJ.PF_*}) - * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} */ + public void decompress8(byte[] dstBuf, int x, int y, int pitch, + int pixelFormat) throws TJException { + if (jpegBuf == null && yuvImage == null) + throw new IllegalStateException("No source image is associated with this instance"); + if (dstBuf == null || x < 0 || y < 0 || pitch < 0 || pixelFormat < 0 || + pixelFormat >= TJ.NUMPF) + throw new IllegalArgumentException("Invalid argument in decompress8()"); + if (yuvImage != null) { + checkSubsampling(); + decodeYUV8(yuvImage.getPlanes(), yuvImage.getOffsets(), + yuvImage.getStrides(), dstBuf, x, y, yuvImage.getWidth(), + pitch, yuvImage.getHeight(), pixelFormat); + } else + decompress8(jpegBuf, jpegBufSize, dstBuf, x, y, pitch, pixelFormat); + } + + /** + * @deprecated Use {@link #set set()}, + * {@link #setScalingFactor setScalingFactor()}, and + * {@link #decompress8(byte[], int, int, int, int)} instead. + */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated public void decompress(byte[] dstBuf, int x, int y, int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags) throws TJException { - if (jpegBuf == null && yuvImage == null) - throw new IllegalStateException("No source image is associated with this instance"); - if (dstBuf == null || x < 0 || y < 0 || pitch < 0 || - (yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) || - pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0) + if ((yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) || + flags < 0) throw new IllegalArgumentException("Invalid argument in decompress()"); - if (yuvImage != null) - decodeYUV(yuvImage.getPlanes(), yuvImage.getOffsets(), - yuvImage.getStrides(), yuvImage.getSubsamp(), dstBuf, x, y, - yuvImage.getWidth(), pitch, yuvImage.getHeight(), pixelFormat, - flags); - else - decompress(jpegBuf, jpegBufSize, dstBuf, x, y, desiredWidth, pitch, - desiredHeight, pixelFormat, flags); + + if (yuvImage == null) { + TJScalingFactor sf = getScalingFactor(desiredWidth, desiredHeight); + setScalingFactor(sf); + } + processFlags(flags); + decompress8(dstBuf, x, y, pitch, pixelFormat); } /** - * Decompress the JPEG source image or decode the planar YUV source image - * associated with this decompressor instance and return a buffer containing - * the packed-pixel decompressed image. - * - * @param desiredWidth see - * {@link #decompress(byte[], int, int, int, int, int, int, int)} - * for description + * Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + * source image associated with this decompressor instance and return a + * buffer containing an 8-bit-per-sample packed-pixel decompressed image. * * @param pitch see - * {@link #decompress(byte[], int, int, int, int, int, int, int)} - * for description - * - * @param desiredHeight see - * {@link #decompress(byte[], int, int, int, int, int, int, int)} - * for description + * {@link #decompress8(byte[], int, int, int, int)} for description * * @param pixelFormat pixel format of the decompressed image (one of * {@link TJ#PF_RGB TJ.PF_*}) * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} - * - * @return a buffer containing the packed-pixel decompressed image. + * @return a buffer containing an 8-bit-per-sample packed-pixel decompressed + * image. + */ + public byte[] decompress8(int pitch, int pixelFormat) throws TJException { + if (pitch < 0 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF) + throw new IllegalArgumentException("Invalid argument in decompress8()"); + int pixelSize = TJ.getPixelSize(pixelFormat); + int scaledWidth = scalingFactor.getScaled(getJPEGWidth()); + int scaledHeight = scalingFactor.getScaled(getJPEGHeight()); + if (pitch == 0) + pitch = scaledWidth * pixelSize; + byte[] buf = new byte[pitch * scaledHeight]; + decompress8(buf, 0, 0, pitch, pixelFormat); + return buf; + } + + /** + * @deprecated Use {@link #set set()}, + * {@link #setScalingFactor setScalingFactor()}, and + * {@link #decompress8(int, int)} instead. */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated public byte[] decompress(int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags) throws TJException { - if (pitch < 0 || - (yuvImage == null && (desiredWidth < 0 || desiredHeight < 0)) || - pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0) + if ((yuvImage == null && (desiredWidth < 0 || desiredHeight < 0)) || + flags < 0) throw new IllegalArgumentException("Invalid argument in decompress()"); + + if (yuvImage == null) { + TJScalingFactor sf = getScalingFactor(desiredWidth, desiredHeight); + setScalingFactor(sf); + } + processFlags(flags); + return decompress8(pitch, pixelFormat); + } + + /** + * Decompress the 12-bit-per-sample JPEG source image associated with this + * decompressor instance and output a 12-bit-per-sample packed-pixel + * grayscale, RGB, or CMYK image to the given destination buffer. + *

      + * NOTE: The destination image is fully recoverable if this method throws a + * non-fatal {@link TJException} (unless {@link TJ#PARAM_STOPONWARNING} is + * set.) + * + * @param dstBuf buffer that will receive the packed-pixel + * decompressed image. This buffer should normally be + * pitch * destinationHeight samples in size. However, the + * buffer may also be larger, in which case the x, + * y, and pitch parameters can be used to specify + * the region into which the source image should be decompressed. NOTE: If + * the source image is a lossy JPEG image, then + * destinationHeight is either the scaled JPEG height (see + * {@link #setScalingFactor setScalingFactor()}, + * {@link TJScalingFactor#getScaled TJScalingFactor.getScaled()}, and + * {@link #getHeight}) or the height of the cropping region (see + * {@link #setCroppingRegion setCroppingRegion()}.) If the source image is a + * lossless JPEG image, then destinationHeight is the height of + * the source image. + * + * @param x x offset (in pixels) of the region in the destination image into + * which the source image should be decompressed + * + * @param y y offset (in pixels) of the region in the destination image into + * which the source image should be decompressed + * + * @param pitch samples per row in the destination image. Normally this + * should be set to destinationWidth * + * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat), if the + * destination image will be unpadded. (Setting this parameter to 0 is the + * equivalent of setting it to destinationWidth * + * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat).) However, + * you can also use this parameter to specify the row alignment/padding of + * the destination image, to skip rows, or to decompress into a specific + * region of a larger image. NOTE: if the source image is a lossy JPEG + * image, then destinationWidth is either the scaled JPEG width + * (see {@link #setScalingFactor setScalingFactor()}, + * {@link TJScalingFactor#getScaled TJScalingFactor.getScaled()}, and + * {@link #getWidth}) or the width of the cropping region (see + * {@link #setCroppingRegion setCroppingRegion()}.) If the source image is a + * YUV image or a lossless JPEG image, then destinationWidth is + * the width of the source image. + * + * @param pixelFormat pixel format of the decompressed image (one of + * {@link TJ#PF_RGB TJ.PF_*}) + */ + public void decompress12(short[] dstBuf, int x, int y, int pitch, + int pixelFormat) throws TJException { + if (jpegBuf == null) + throw new IllegalStateException(NO_ASSOC_ERROR); + if (dstBuf == null || x < 0 || y < 0 || pitch < 0 || pixelFormat < 0 || + pixelFormat >= TJ.NUMPF) + throw new IllegalArgumentException("Invalid argument in decompress12()"); + decompress12(jpegBuf, jpegBufSize, dstBuf, x, y, pitch, pixelFormat); + } + + /** + * Decompress the 12-bit-per-sample JPEG source image associated with this + * decompressor instance and return a buffer containing a 12-bit-per-sample + * packed-pixel decompressed image. + * + * @param pitch see + * {@link #decompress12(short[], int, int, int, int)} for description + * + * @param pixelFormat pixel format of the decompressed image (one of + * {@link TJ#PF_RGB TJ.PF_*}) + * + * @return a buffer containing an 8-bit-per-sample packed-pixel decompressed + * image. + */ + public short[] decompress12(int pitch, int pixelFormat) throws TJException { + if (pitch < 0 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF) + throw new IllegalArgumentException("Invalid argument in decompress12()"); int pixelSize = TJ.getPixelSize(pixelFormat); - int scaledWidth = getScaledWidth(desiredWidth, desiredHeight); - int scaledHeight = getScaledHeight(desiredWidth, desiredHeight); + int scaledWidth = scalingFactor.getScaled(getJPEGWidth()); + int scaledHeight = scalingFactor.getScaled(getJPEGHeight()); if (pitch == 0) pitch = scaledWidth * pixelSize; - byte[] buf = new byte[pitch * scaledHeight]; - decompress(buf, 0, 0, desiredWidth, pitch, desiredHeight, pixelFormat, - flags); + short[] buf = new short[pitch * scaledHeight]; + decompress12(buf, 0, 0, pitch, pixelFormat); return buf; } /** - * Decompress the JPEG source image associated with this decompressor - * instance into a planar YUV image and store it in the given - * {@link YUVImage} instance. This method performs JPEG decompression but - * leaves out the color conversion step, so a planar YUV image is generated - * instead of a packed-pixel image. This method cannot be used to decompress - * JPEG source images with the CMYK or YCCK colorspace. + * Decompress the 16-bit-per-sample lossless JPEG source image associated + * with this decompressor instance and output a 16-bit-per-sample + * packed-pixel grayscale, RGB, or CMYK image to the given destination + * buffer. + *

      + * NOTE: The destination image is fully recoverable if this method throws a + * non-fatal {@link TJException} (unless {@link TJ#PARAM_STOPONWARNING} is + * set.) + * + * @param dstBuf buffer that will receive the packed-pixel + * decompressed image. This buffer should normally be + * pitch * jpegHeight samples in size. However, the buffer may + * also be larger, in which case the x, + * y, and pitch parameters can be used to specify + * the region into which the source image should be decompressed. + * + * @param x x offset (in pixels) of the region in the destination image into + * which the source image should be decompressed + * + * @param y y offset (in pixels) of the region in the destination image into + * which the source image should be decompressed + * + * @param pitch samples per row in the destination image. Normally this + * should be set to jpegWidth * + * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat), if the + * destination image will be unpadded. (Setting this parameter to 0 is the + * equivalent of setting it to jpegWidth * + * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat).) However, + * you can also use this parameter to specify the row alignment/padding of + * the destination image, to skip rows, or to decompress into a specific + * region of a larger image. + * + * @param pixelFormat pixel format of the decompressed image (one of + * {@link TJ#PF_RGB TJ.PF_*}) + */ + public void decompress16(short[] dstBuf, int x, int y, int pitch, + int pixelFormat) throws TJException { + if (jpegBuf == null) + throw new IllegalStateException(NO_ASSOC_ERROR); + if (dstBuf == null || x < 0 || y < 0 || pitch < 0 || pixelFormat < 0 || + pixelFormat >= TJ.NUMPF) + throw new IllegalArgumentException("Invalid argument in decompress16()"); + decompress16(jpegBuf, jpegBufSize, dstBuf, x, y, pitch, pixelFormat); + } + + /** + * Decompress the 16-bit-per-sample JPEG source image associated with this + * decompressor instance and return a buffer containing a 16-bit-per-sample + * packed-pixel decompressed image. + * + * @param pitch see + * {@link #decompress16(short[], int, int, int, int)} for description + * + * @param pixelFormat pixel format of the decompressed image (one of + * {@link TJ#PF_RGB TJ.PF_*}) + * + * @return a buffer containing an 8-bit-per-sample packed-pixel decompressed + * image. + */ + public short[] decompress16(int pitch, int pixelFormat) throws TJException { + if (pitch < 0 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF) + throw new IllegalArgumentException("Invalid argument in decompress16()"); + int pixelSize = TJ.getPixelSize(pixelFormat); + int scaledWidth = scalingFactor.getScaled(getJPEGWidth()); + int scaledHeight = scalingFactor.getScaled(getJPEGHeight()); + if (pitch == 0) + pitch = scaledWidth * pixelSize; + short[] buf = new short[pitch * scaledHeight]; + decompress16(buf, 0, 0, pitch, pixelFormat); + return buf; + } + + /** + * Decompress the 8-bit-per-sample JPEG source image associated with this + * decompressor instance into an 8-bit-per-sample planar YUV image and store + * it in the given {@link YUVImage} instance. This method performs JPEG + * decompression but leaves out the color conversion step, so a planar YUV + * image is generated instead of a packed-pixel image. This method cannot be + * used to decompress JPEG source images with the CMYK or YCCK colorspace. *

      * NOTE: The planar YUV destination image is fully recoverable if this method * throws a non-fatal {@link TJException} (unless - * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.) + * {@link TJ#PARAM_STOPONWARNING} is set.) * * @param dstImage {@link YUVImage} instance that will receive the planar YUV * decompressed image. The level of subsampling specified in this * {@link YUVImage} instance must match that of the JPEG image, and the width - * and height specified in the {@link YUVImage} instance must match one of - * the scaled image sizes that the decompressor is capable of generating from - * the JPEG source image. - * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} + * and height specified in the {@link YUVImage} instance must match the + * scaled JPEG width and height (see {@link #setScalingFactor + * setScalingFactor()}, {@link TJScalingFactor#getScaled + * TJScalingFactor.getScaled()}, {@link #getWidth}, and {@link #getHeight}.) */ - public void decompressToYUV(YUVImage dstImage, int flags) - throws TJException { + public void decompressToYUV(YUVImage dstImage) throws TJException { if (jpegBuf == null) throw new IllegalStateException(NO_ASSOC_ERROR); - if (dstImage == null || flags < 0) + if (dstImage == null) throw new IllegalArgumentException("Invalid argument in decompressToYUV()"); - int scaledWidth = getScaledWidth(dstImage.getWidth(), - dstImage.getHeight()); - int scaledHeight = getScaledHeight(dstImage.getWidth(), - dstImage.getHeight()); - if (scaledWidth != dstImage.getWidth() || - scaledHeight != dstImage.getHeight()) - throw new IllegalArgumentException("YUVImage dimensions do not match one of the scaled image sizes that the decompressor is capable of generating."); - if (jpegSubsamp != dstImage.getSubsamp()) + checkSubsampling(); + if (get(TJ.PARAM_SUBSAMP) != dstImage.getSubsamp()) throw new IllegalArgumentException("YUVImage subsampling level does not match that of the JPEG image"); + if (scalingFactor.getScaled(getJPEGWidth()) != dstImage.getWidth() || + scalingFactor.getScaled(getJPEGHeight()) != dstImage.getHeight()) + throw new IllegalArgumentException("YUVImage dimensions do not match the scaled JPEG dimensions"); - decompressToYUV(jpegBuf, jpegBufSize, dstImage.getPlanes(), - dstImage.getOffsets(), dstImage.getWidth(), - dstImage.getStrides(), dstImage.getHeight(), flags); + decompressToYUV8(jpegBuf, jpegBufSize, dstImage.getPlanes(), + dstImage.getOffsets(), dstImage.getStrides()); } /** - * Decompress the JPEG source image associated with this decompressor - * instance into a set of Y, U (Cb), and V (Cr) image planes and return a - * {@link YUVImage} instance containing the decompressed image planes. This - * method performs JPEG decompression but leaves out the color conversion - * step, so a planar YUV image is generated instead of a packed-pixel image. - * This method cannot be used to decompress JPEG source images with the CMYK - * or YCCK colorspace. - * - * @param desiredWidth desired width (in pixels) of the YUV image. If the - * desired image dimensions are different than the dimensions of the JPEG - * image being decompressed, then TurboJPEG will use scaling in the JPEG - * decompressor to generate the largest possible image that will fit within - * the desired dimensions. Setting this to 0 is the same as setting it to - * the width of the JPEG image. (In other words, the width will not be - * considered when determining the scaled image size.) + * @deprecated Use {@link #set set()}, {@link #setScalingFactor + * setScalingFactor()}, and {@link #decompressToYUV(YUVImage)} instead. + */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated + public void decompressToYUV(YUVImage dstImage, int flags) + throws TJException { + if (flags < 0) + throw new IllegalArgumentException("Invalid argument in decompressToYUV()"); + + TJScalingFactor sf = getScalingFactor(dstImage.getWidth(), + dstImage.getHeight()); + if (sf.getScaled(getJPEGWidth()) != dstImage.getWidth() || + sf.getScaled(getJPEGHeight()) != dstImage.getHeight()) + throw new IllegalArgumentException("YUVImage dimensions do not match one of the scaled image sizes that the decompressor is capable of generating."); + + setScalingFactor(sf); + processFlags(flags); + decompressToYUV(dstImage); + } + + /** + * Decompress the 8-bit-per-sample JPEG source image associated with this + * decompressor instance into a set of 8-bit-per-sample Y, U (Cb), and V (Cr) + * image planes and return a {@link YUVImage} instance containing the + * decompressed image planes. This method performs JPEG decompression but + * leaves out the color conversion step, so a planar YUV image is generated + * instead of a packed-pixel image. This method cannot be used to decompress + * JPEG source images with the CMYK or YCCK colorspace. * * @param strides an array of integers, each specifying the number of bytes * per row in the corresponding plane of the YUV image. Setting the stride @@ -525,116 +746,115 @@ public void decompressToYUV(YUVImage dstImage, int flags) * can adjust the strides in order to add an arbitrary amount of row padding * to each plane. * - * @param desiredHeight desired height (in pixels) of the YUV image. If the - * desired image dimensions are different than the dimensions of the JPEG - * image being decompressed, then TurboJPEG will use scaling in the JPEG - * decompressor to generate the largest possible image that will fit within - * the desired dimensions. Setting this to 0 is the same as setting it to - * the height of the JPEG image. (In other words, the height will not be - * considered when determining the scaled image size.) - * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} - * * @return a {@link YUVImage} instance containing the decompressed image * planes */ + public YUVImage decompressToYUV(int[] strides) throws TJException { + int jpegWidth = getJPEGWidth(); + int jpegHeight = getJPEGHeight(); + checkSubsampling(); + if (yuvImage != null) + throw new IllegalStateException("Source image is the wrong type"); + + YUVImage dstYUVImage = new YUVImage(scalingFactor.getScaled(jpegWidth), + null, + scalingFactor.getScaled(jpegHeight), + get(TJ.PARAM_SUBSAMP)); + decompressToYUV(dstYUVImage); + return dstYUVImage; + } + + /** + * @deprecated Use {@link #set set()}, {@link #setScalingFactor + * setScalingFactor()}, and {@link #decompressToYUV(int[])} instead. + */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated public YUVImage decompressToYUV(int desiredWidth, int[] strides, int desiredHeight, int flags) throws TJException { if (flags < 0) throw new IllegalArgumentException("Invalid argument in decompressToYUV()"); - if (jpegWidth < 1 || jpegHeight < 1 || jpegSubsamp < 0) - throw new IllegalStateException(NO_ASSOC_ERROR); - if (jpegSubsamp >= TJ.NUMSAMP) - throw new IllegalStateException("JPEG header information is invalid"); - if (yuvImage != null) - throw new IllegalStateException("Source image is the wrong type"); - int scaledWidth = getScaledWidth(desiredWidth, desiredHeight); - int scaledHeight = getScaledHeight(desiredWidth, desiredHeight); - YUVImage dstYUVImage = new YUVImage(scaledWidth, null, scaledHeight, - jpegSubsamp); - decompressToYUV(dstYUVImage, flags); - return dstYUVImage; + TJScalingFactor sf = getScalingFactor(desiredWidth, desiredHeight); + setScalingFactor(sf); + processFlags(flags); + return decompressToYUV(strides); } /** - * Decompress the JPEG source image associated with this decompressor - * instance into a unified planar YUV image and return a {@link YUVImage} - * instance containing the decompressed image. This method performs JPEG - * decompression but leaves out the color conversion step, so a planar YUV - * image is generated instead of a packed-pixel image. This method cannot be - * used to decompress JPEG source images with the CMYK or YCCK colorspace. - * - * @param desiredWidth desired width (in pixels) of the YUV image. If the - * desired image dimensions are different than the dimensions of the JPEG - * image being decompressed, then TurboJPEG will use scaling in the JPEG - * decompressor to generate the largest possible image that will fit within - * the desired dimensions. Setting this to 0 is the same as setting it to - * the width of the JPEG image. (In other words, the width will not be - * considered when determining the scaled image size.) + * Decompress the 8-bit-per-sample JPEG source image associated with this + * decompressor instance into an 8-bit-per-sample unified planar YUV image + * and return a {@link YUVImage} instance containing the decompressed image. + * This method performs JPEG decompression but leaves out the color + * conversion step, so a planar YUV image is generated instead of a + * packed-pixel image. This method cannot be used to decompress JPEG source + * images with the CMYK or YCCK colorspace. * * @param align row alignment (in bytes) of the YUV image (must be a power of * 2.) Setting this parameter to n will cause each row in each plane of the * YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.) * - * @param desiredHeight desired height (in pixels) of the YUV image. If the - * desired image dimensions are different than the dimensions of the JPEG - * image being decompressed, then TurboJPEG will use scaling in the JPEG - * decompressor to generate the largest possible image that will fit within - * the desired dimensions. Setting this to 0 is the same as setting it to - * the height of the JPEG image. (In other words, the height will not be - * considered when determining the scaled image size.) - * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} - * * @return a {@link YUVImage} instance containing the unified planar YUV * decompressed image */ + public YUVImage decompressToYUV(int align) throws TJException { + int jpegWidth = getJPEGWidth(); + int jpegHeight = getJPEGHeight(); + checkSubsampling(); + if (yuvImage != null) + throw new IllegalStateException("Source image is the wrong type"); + + YUVImage dstYUVImage = new YUVImage(scalingFactor.getScaled(jpegWidth), + align, + scalingFactor.getScaled(jpegHeight), + get(TJ.PARAM_SUBSAMP)); + decompressToYUV(dstYUVImage); + return dstYUVImage; + } + + /** + * @deprecated Use {@link #set set()}, {@link #setScalingFactor + * setScalingFactor()}, and {@link #decompressToYUV(int)} instead. + */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated public YUVImage decompressToYUV(int desiredWidth, int align, int desiredHeight, int flags) throws TJException { if (flags < 0) throw new IllegalArgumentException("Invalid argument in decompressToYUV()"); - if (jpegWidth < 1 || jpegHeight < 1 || jpegSubsamp < 0) - throw new IllegalStateException(NO_ASSOC_ERROR); - if (jpegSubsamp >= TJ.NUMSAMP) - throw new IllegalStateException("JPEG header information is invalid"); - if (yuvImage != null) - throw new IllegalStateException("Source image is the wrong type"); - int scaledWidth = getScaledWidth(desiredWidth, desiredHeight); - int scaledHeight = getScaledHeight(desiredWidth, desiredHeight); - YUVImage dstYUVImage = new YUVImage(scaledWidth, align, scaledHeight, - jpegSubsamp); - decompressToYUV(dstYUVImage, flags); - return dstYUVImage; + TJScalingFactor sf = getScalingFactor(desiredWidth, desiredHeight); + setScalingFactor(sf); + processFlags(flags); + return decompressToYUV(align); } /** - * Decompress the JPEG source image or decode the planar YUV source image - * associated with this decompressor instance and output a packed-pixel - * grayscale, RGB, or CMYK image to the given destination buffer. + * Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + * source image associated with this decompressor instance and output an + * 8-bit-per-sample packed-pixel grayscale, RGB, or CMYK image to the given + * destination buffer. *

      * NOTE: The destination image is fully recoverable if this method throws a - * non-fatal {@link TJException} (unless - * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.) + * non-fatal {@link TJException} (unless {@link TJ#PARAM_STOPONWARNING} + * is set.) * * @param dstBuf buffer that will receive the packed-pixel - * decompressed/decoded image. If the source image is a JPEG image, then - * this buffer should normally be stride * scaledHeight pixels - * in size, where scaledHeight can be determined by calling - * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegHeight) - * with one of the scaling factors returned from {@link TJ#getScalingFactors} - * or by calling {@link #getScaledHeight}. If the source image is a YUV - * image, then this buffer should normally be stride * height - * pixels in size, where height is the height of the YUV image. - * However, the buffer may also be larger than the dimensions of the JPEG - * image, in which case the x, y, and - * stride parameters can be used to specify the region into - * which the source image should be decompressed. + * decompressed/decoded image. This buffer should normally be + * stride * destinationHeight pixels in size. However, the + * buffer may also be larger, in which case the x, + * y, and pitch parameters can be used to specify + * the region into which the source image should be decompressed/decoded. + * NOTE: If the source image is a lossy JPEG image, then + * destinationHeight is either the scaled JPEG height (see + * {@link #setScalingFactor setScalingFactor()}, + * {@link TJScalingFactor#getScaled TJScalingFactor.getScaled()}, and + * {@link #getHeight}) or the height of the cropping region (see + * {@link #setCroppingRegion setCroppingRegion()}.) If the source image is a + * YUV image or a lossless JPEG image, then destinationHeight is + * the height of the source image. * * @param x x offset (in pixels) of the region in the destination image into * which the source image should be decompressed/decoded @@ -642,102 +862,94 @@ public YUVImage decompressToYUV(int desiredWidth, int align, * @param y y offset (in pixels) of the region in the destination image into * which the source image should be decompressed/decoded * - * @param desiredWidth If the source image is a JPEG image, then this - * specifies the desired width (in pixels) of the decompressed image (or - * image region.) If the desired destination image dimensions are different - * than the source image dimensions, then TurboJPEG will use scaling in the - * JPEG decompressor to generate the largest possible image that will fit - * within the desired dimensions. Setting this to 0 is the same as setting - * it to the width of the JPEG image. (In other words, the width will not be - * considered when determining the scaled image size.) This parameter is - * ignored if the source image is a YUV image. - * * @param stride pixels per row in the destination image. Normally this - * should be set to scaledWidth, but you can use this to, for - * instance, decompress the JPEG image into a region of a larger image. - * NOTE: if the source image is a JPEG image, then scaledWidth - * can be determined by calling - * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegWidth) - * or by calling {@link #getScaledWidth}. If the source image is a YUV - * image, then scaledWidth is the width of the YUV image. - * Setting this parameter to 0 is the equivalent of setting it to - * scaledWidth. - * - * @param desiredHeight If the source image is a JPEG image, then this - * specifies the desired height (in pixels) of the decompressed image (or - * image region.) If the desired destination image dimensions are different - * than the source image dimensions, then TurboJPEG will use scaling in the - * JPEG decompressor to generate the largest possible image that will fit - * within the desired dimensions. Setting this to 0 is the same as setting - * it to the height of the JPEG image. (In other words, the height will not - * be considered when determining the scaled image size.) This parameter is - * ignored if the source image is a YUV image. + * should be set to destinationWidth. (Setting this parameter + * to 0 is the equivalent of setting it to destinationWidth.) + * However, you can also use this parameter to skip rows or to + * decompress/decode into a specific region of a larger image. NOTE: if the + * source image is a lossy JPEG image, then destinationWidth is + * either the scaled JPEG width (see {@link #setScalingFactor + * setScalingFactor()}, {@link TJScalingFactor#getScaled + * TJScalingFactor.getScaled()}, and {@link #getWidth}) or the width of the + * cropping region (see {@link #setCroppingRegion setCroppingRegion()}.) If + * the source image is a YUV image or a lossless JPEG image, then + * destinationWidth is the width of the source image. * - * @param pixelFormat pixel format of the decompressed image (one of + * @param pixelFormat pixel format of the decompressed/decoded image (one of * {@link TJ#PF_RGB TJ.PF_*}) - * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} */ - public void decompress(int[] dstBuf, int x, int y, int desiredWidth, - int stride, int desiredHeight, int pixelFormat, - int flags) throws TJException { + public void decompress8(int[] dstBuf, int x, int y, int stride, + int pixelFormat) throws TJException { if (jpegBuf == null && yuvImage == null) throw new IllegalStateException("No source image is associated with this instance"); if (dstBuf == null || x < 0 || y < 0 || stride < 0 || - (yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) || - pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0) + pixelFormat < 0 || pixelFormat >= TJ.NUMPF) + throw new IllegalArgumentException("Invalid argument in decompress8()"); + if (yuvImage != null) { + checkSubsampling(); + decodeYUV8(yuvImage.getPlanes(), yuvImage.getOffsets(), + yuvImage.getStrides(), dstBuf, x, y, yuvImage.getWidth(), + stride, yuvImage.getHeight(), pixelFormat); + } else + decompress8(jpegBuf, jpegBufSize, dstBuf, x, y, stride, pixelFormat); + } + + /** + * @deprecated Use {@link #set set()}, {@link #setScalingFactor + * setScalingFactor()}, and {@link #decompress8(int[], int, int, int, int)} + * instead. + */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated + public void decompress(int[] dstBuf, int x, int y, int desiredWidth, + int stride, int desiredHeight, int pixelFormat, + int flags) throws TJException { + if ((yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) || + flags < 0) throw new IllegalArgumentException("Invalid argument in decompress()"); - if (yuvImage != null) - decodeYUV(yuvImage.getPlanes(), yuvImage.getOffsets(), - yuvImage.getStrides(), yuvImage.getSubsamp(), dstBuf, x, y, - yuvImage.getWidth(), stride, yuvImage.getHeight(), pixelFormat, - flags); - else - decompress(jpegBuf, jpegBufSize, dstBuf, x, y, desiredWidth, stride, - desiredHeight, pixelFormat, flags); + + if (yuvImage == null) { + TJScalingFactor sf = getScalingFactor(desiredWidth, desiredHeight); + setScalingFactor(sf); + } + processFlags(flags); + decompress8(dstBuf, x, y, stride, pixelFormat); } /** - * Decompress the JPEG source image or decode the planar YUV source image - * associated with this decompressor instance and output a packed-pixel - * decompressed/decoded image to the given BufferedImage - * instance. + * Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + * source image associated with this decompressor instance and output an + * 8-bit-per-sample packed-pixel decompressed/decoded image to the given + * BufferedImage instance. *

      * NOTE: The destination image is fully recoverable if this method throws a - * non-fatal {@link TJException} (unless - * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.) + * non-fatal {@link TJException} (unless {@link TJ#PARAM_STOPONWARNING} + * is set.) * * @param dstImage a BufferedImage instance that will receive * the packed-pixel decompressed/decoded image. If the source image is a - * JPEG image, then the width and height of the BufferedImage - * instance must match one of the scaled image sizes that the decompressor is - * capable of generating from the JPEG image. If the source image is a YUV - * image, then the width and height of the BufferedImage - * instance must match the width and height of the YUV image. - * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} + * lossy JPEG image, then the width and height of the + * BufferedImage instance must match the scaled JPEG width and + * height (see {@link #setScalingFactor setScalingFactor()}, + * {@link TJScalingFactor#getScaled TJScalingFactor.getScaled()}, + * {@link #getWidth}, and {@link #getHeight}) or the width and height of the + * cropping region (see {@link #setCroppingRegion setCroppingRegion()}.) If + * the source image is a YUV image or a lossless JPEG image, then the width + * and height of the BufferedImage instance must match the width + * and height of the source image. */ - public void decompress(BufferedImage dstImage, int flags) - throws TJException { - if (dstImage == null || flags < 0) - throw new IllegalArgumentException("Invalid argument in decompress()"); - int desiredWidth = dstImage.getWidth(); - int desiredHeight = dstImage.getHeight(); - int scaledWidth, scaledHeight; + public void decompress8(BufferedImage dstImage) throws TJException { + if (dstImage == null) + throw new IllegalArgumentException("Invalid argument in decompress8()"); if (yuvImage != null) { - if (desiredWidth != yuvImage.getWidth() || - desiredHeight != yuvImage.getHeight()) + if (dstImage.getWidth() != yuvImage.getWidth() || + dstImage.getHeight() != yuvImage.getHeight()) throw new IllegalArgumentException("BufferedImage dimensions do not match the dimensions of the source image."); - scaledWidth = yuvImage.getWidth(); - scaledHeight = yuvImage.getHeight(); } else { - scaledWidth = getScaledWidth(desiredWidth, desiredHeight); - scaledHeight = getScaledHeight(desiredWidth, desiredHeight); - if (scaledWidth != desiredWidth || scaledHeight != desiredHeight) - throw new IllegalArgumentException("BufferedImage dimensions do not match one of the scaled image sizes that the decompressor is capable of generating."); + if (scalingFactor.getScaled(getJPEGWidth()) != dstImage.getWidth() || + scalingFactor.getScaled(getJPEGHeight()) != dstImage.getHeight()) + throw new IllegalArgumentException("BufferedImage dimensions do not match the scaled JPEG dimensions."); } int pixelFormat; boolean intPixels = false; if (byteOrder == null) @@ -779,16 +991,15 @@ public void decompress(BufferedImage dstImage, int flags) int stride = sm.getScanlineStride(); DataBufferInt db = (DataBufferInt)wr.getDataBuffer(); int[] buf = db.getData(); - if (yuvImage != null) - decodeYUV(yuvImage.getPlanes(), yuvImage.getOffsets(), - yuvImage.getStrides(), yuvImage.getSubsamp(), buf, 0, 0, - yuvImage.getWidth(), stride, yuvImage.getHeight(), - pixelFormat, flags); - else { + if (yuvImage != null) { + checkSubsampling(); + decodeYUV8(yuvImage.getPlanes(), yuvImage.getOffsets(), + yuvImage.getStrides(), buf, 0, 0, yuvImage.getWidth(), + stride, yuvImage.getHeight(), pixelFormat); + } else { if (jpegBuf == null) throw new IllegalStateException(NO_ASSOC_ERROR); - decompress(jpegBuf, jpegBufSize, buf, 0, 0, scaledWidth, stride, - scaledHeight, pixelFormat, flags); + decompress8(jpegBuf, jpegBufSize, buf, 0, 0, stride, pixelFormat); } } else { ComponentSampleModel sm = @@ -799,47 +1010,76 @@ public void decompress(BufferedImage dstImage, int flags) int pitch = sm.getScanlineStride(); DataBufferByte db = (DataBufferByte)wr.getDataBuffer(); byte[] buf = db.getData(); - decompress(buf, 0, 0, scaledWidth, pitch, scaledHeight, pixelFormat, - flags); + decompress8(buf, 0, 0, pitch, pixelFormat); } } /** - * Decompress the JPEG source image or decode the planar YUV source image - * associated with this decompressor instance and return a - * BufferedImage instance containing the packed-pixel - * decompressed/decoded image. - * - * @param desiredWidth see - * {@link #decompress(byte[], int, int, int, int, int, int, int)} for - * description - * - * @param desiredHeight see - * {@link #decompress(byte[], int, int, int, int, int, int, int)} for - * description + * @deprecated Use {@link #set set()}, {@link #setScalingFactor + * setScalingFactor()}, and {@link #decompress8(BufferedImage)} instead. + */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated + public void decompress(BufferedImage dstImage, int flags) + throws TJException { + if (flags < 0) + throw new IllegalArgumentException("Invalid argument in decompress()"); + + if (yuvImage == null) { + TJScalingFactor sf = getScalingFactor(dstImage.getWidth(), + dstImage.getHeight()); + if (sf.getScaled(getJPEGWidth()) != dstImage.getWidth() || + sf.getScaled(getJPEGHeight()) != dstImage.getHeight()) + throw new IllegalArgumentException("BufferedImage dimensions do not match one of the scaled image sizes that TurboJPEG is capable of generating."); + + setScalingFactor(sf); + } + + processFlags(flags); + decompress8(dstImage); + } + + /** + * Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + * source image associated with this decompressor instance and return a + * BufferedImage instance containing the 8-bit-per-sample + * packed-pixel decompressed/decoded image. * * @param bufferedImageType the image type of the BufferedImage * instance that will be created (for instance, * BufferedImage.TYPE_INT_RGB) * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} - * - * @return a BufferedImage instance containing the packed-pixel - * decompressed/decoded image. + * @return a BufferedImage instance containing the + * 8-bit-per-sample packed-pixel decompressed/decoded image. + */ + public BufferedImage decompress8(int bufferedImageType) throws TJException { + BufferedImage img = + new BufferedImage(scalingFactor.getScaled(getJPEGWidth()), + scalingFactor.getScaled(getJPEGHeight()), + bufferedImageType); + decompress8(img); + return img; + } + + /** + * @deprecated Use {@link #set set()}, {@link #setScalingFactor + * setScalingFactor()}, and {@link #decompress8(int)} instead. */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated public BufferedImage decompress(int desiredWidth, int desiredHeight, int bufferedImageType, int flags) throws TJException { if ((yuvImage == null && (desiredWidth < 0 || desiredHeight < 0)) || flags < 0) throw new IllegalArgumentException("Invalid argument in decompress()"); - int scaledWidth = getScaledWidth(desiredWidth, desiredHeight); - int scaledHeight = getScaledHeight(desiredWidth, desiredHeight); - BufferedImage img = new BufferedImage(scaledWidth, scaledHeight, - bufferedImageType); - decompress(img, flags); - return img; + + if (yuvImage == null) { + TJScalingFactor sf = getScalingFactor(desiredWidth, desiredHeight); + setScalingFactor(sf); + } + processFlags(flags); + return decompress8(bufferedImageType); } /** @@ -862,6 +1102,20 @@ protected void finalize() throws Throwable { } }; + @SuppressWarnings("deprecation") + final void processFlags(int flags) { + set(TJ.PARAM_BOTTOMUP, (flags & TJ.FLAG_BOTTOMUP) != 0 ? 1 : 0); + set(TJ.PARAM_FASTUPSAMPLE, (flags & TJ.FLAG_FASTUPSAMPLE) != 0 ? 1 : 0); + set(TJ.PARAM_FASTDCT, (flags & TJ.FLAG_FASTDCT) != 0 ? 1 : 0); + set(TJ.PARAM_STOPONWARNING, (flags & TJ.FLAG_STOPONWARNING) != 0 ? 1 : 0); + set(TJ.PARAM_SCANLIMIT, (flags & TJ.FLAG_LIMITSCANS) != 0 ? 500 : 0); + } + + final void checkSubsampling() { + if (get(TJ.PARAM_SUBSAMP) == TJ.SAMP_UNKNOWN) + throw new IllegalStateException("Unknown or unspecified subsampling level"); + } + private native void init() throws TJException; private native void destroy() throws TJException; @@ -869,38 +1123,58 @@ protected void finalize() throws Throwable { private native void decompressHeader(byte[] srcBuf, int size) throws TJException; - private native void decompress(byte[] srcBuf, int size, byte[] dstBuf, int x, - int y, int desiredWidth, int pitch, int desiredHeight, int pixelFormat, - int flags) throws TJException; + private native void setCroppingRegion() throws TJException; - private native void decompress(byte[] srcBuf, int size, int[] dstBuf, int x, - int y, int desiredWidth, int stride, int desiredHeight, int pixelFormat, - int flags) throws TJException; + @SuppressWarnings("checkstyle:HiddenField") + private native void decompress8(byte[] srcBuf, int size, byte[] dstBuf, + int x, int y, int pitch, int pixelFormat) throws TJException; - private native void decompressToYUV(byte[] srcBuf, int size, - byte[][] dstPlanes, int[] dstOffsets, int desiredWidth, int[] dstStrides, - int desiredheight, int flags) throws TJException; + @SuppressWarnings("checkstyle:HiddenField") + private native void decompress12(byte[] srcBuf, int size, short[] dstBuf, + int x, int y, int pitch, int pixelFormat) throws TJException; - private native void decodeYUV(byte[][] srcPlanes, int[] srcOffsets, - int[] srcStrides, int subsamp, byte[] dstBuf, int x, int y, int width, - int pitch, int height, int pixelFormat, int flags) throws TJException; + @SuppressWarnings("checkstyle:HiddenField") + private native void decompress16(byte[] srcBuf, int size, short[] dstBuf, + int x, int y, int pitch, int pixelFormat) throws TJException; - private native void decodeYUV(byte[][] srcPlanes, int[] srcOffsets, - int[] srcStrides, int subsamp, int[] dstBuf, int x, int y, int width, - int stride, int height, int pixelFormat, int flags) throws TJException; + @SuppressWarnings("checkstyle:HiddenField") + private native void decompress8(byte[] srcBuf, int size, int[] dstBuf, int x, + int y, int stride, int pixelFormat) throws TJException; + + @SuppressWarnings("checkstyle:HiddenField") + private native void decompressToYUV8(byte[] srcBuf, int size, + byte[][] dstPlanes, int[] dstOffsets, int[] dstStrides) throws TJException; + + private native void decodeYUV8(byte[][] srcPlanes, int[] srcOffsets, + int[] srcStrides, byte[] dstBuf, int x, int y, int width, int pitch, + int height, int pixelFormat) throws TJException; + + private native void decodeYUV8(byte[][] srcPlanes, int[] srcOffsets, + int[] srcStrides, int[] dstBuf, int x, int y, int width, int stride, + int height, int pixelFormat) throws TJException; + + /** + * @hidden + * Ugly hack alert. It isn't straightforward to save 12-bit-per-sample and + * 16-bit-per-sample images using the ImageIO and BufferedImage classes, and + * ImageIO doesn't support PBMPLUS files anyhow. This method accesses + * tj3SaveImage() through JNI and copies the pixel data between the C and + * Java heaps. Currently it is undocumented and used only by TJBench. + */ + @SuppressWarnings("checkstyle:JavadocMethod") + public native void saveImage(int precision, String fileName, Object srcBuf, + int width, int pitch, int height, + int pixelFormat) throws TJException; static { TJLoader.load(); } - protected long handle = 0; - protected byte[] jpegBuf = null; - protected int jpegBufSize = 0; - protected YUVImage yuvImage = null; - protected int jpegWidth = 0; - protected int jpegHeight = 0; - protected int jpegSubsamp = -1; - protected int jpegColorspace = -1; - protected int jpegFlags = -1; + private long handle = 0; + private byte[] jpegBuf = null; + private int jpegBufSize = 0; + private YUVImage yuvImage = null; + private TJScalingFactor scalingFactor = TJ.UNSCALED; + private Rectangle croppingRegion = TJ.UNCROPPED; private ByteOrder byteOrder = null; } diff --git a/java/org/libjpegturbo/turbojpeg/TJTransform.java b/java/org/libjpegturbo/turbojpeg/TJTransform.java index d02d258fd..dae0c164d 100644 --- a/java/org/libjpegturbo/turbojpeg/TJTransform.java +++ b/java/org/libjpegturbo/turbojpeg/TJTransform.java @@ -96,13 +96,14 @@ public class TJTransform extends Rectangle { * TJTransformer.transform()} to throw an exception if the transform is not * perfect. Lossless transforms operate on MCU blocks, whose size depends on * the level of chrominance subsampling used. If the image's width or height - * is not evenly divisible by the MCU block size (see {@link TJ#getMCUWidth} - * and {@link TJ#getMCUHeight}), then there will be partial MCU blocks on the - * right and/or bottom edges. It is not possible to move these partial MCU - * blocks to the top or left of the image, so any transform that would - * require that is "imperfect." If this option is not specified, then any - * partial MCU blocks that cannot be transformed will be left in place, which - * will create odd-looking strips on the right or bottom edge of the image. + * is not evenly divisible by the MCU block size (see {@link TJ#getMCUWidth + * TJ.getMCUWidth()} and {@link TJ#getMCUHeight TJ.getMCUHeight()}), then + * there will be partial MCU blocks on the right and/or bottom edges. It is + * not possible to move these partial MCU blocks to the top or left of the + * image, so any transform that would require that is "imperfect." If this + * option is not specified, then any partial MCU blocks that cannot be + * transformed will be left in place, which will create odd-looking strips on + * the right or bottom edge of the image. */ public static final int OPT_PERFECT = (1 << 0); /** @@ -131,8 +132,9 @@ public class TJTransform extends Rectangle { * This option will enable progressive entropy coding in the JPEG image * generated by this particular transform. Progressive entropy coding will * generally improve compression relative to baseline entropy coding (the - * default), but it will reduce decompression performance considerably. Can - * be combined with {@link #OPT_ARITHMETIC}. + * default), but it will reduce decompression performance considerably. + * Implies {@link #OPT_OPTIMIZE}. Can be combined with + * {@link #OPT_ARITHMETIC}. */ public static final int OPT_PROGRESSIVE = (1 << 5); /** @@ -149,6 +151,12 @@ public class TJTransform extends Rectangle { * be combined with {@link #OPT_PROGRESSIVE}. */ public static final int OPT_ARITHMETIC = (1 << 7); + /** + * This option will enable optimized baseline entropy coding in the JPEG + * image generated by this particular transform. Optimized baseline entropy + * coding will improve compression slightly (generally 5% or less.) + */ + public static final int OPT_OPTIMIZE = (1 << 8); /** @@ -161,10 +169,12 @@ public TJTransform() { * Create a new lossless transform instance with the given parameters. * * @param x the left boundary of the cropping region. This must be evenly - * divisible by the MCU block width (see {@link TJ#getMCUWidth}) + * divisible by the MCU block width (see {@link TJ#getMCUWidth + * TJ.getMCUWidth()}) * * @param y the upper boundary of the cropping region. This must be evenly - * divisible by the MCU block height (see {@link TJ#getMCUHeight}) + * divisible by the MCU block height (see {@link TJ#getMCUHeight + * TJ.getMCUHeight()}) * * @param w the width of the cropping region. Setting this to 0 is the * equivalent of setting it to (width of the source JPEG image - @@ -179,8 +189,8 @@ public TJTransform() { * @param options the bitwise OR of one or more of the transform options * ({@link #OPT_PERFECT OPT_*}) * - * @param cf an instance of an object that implements the {@link - * TJCustomFilter} interface, or null if no custom filter is needed + * @param cf an instance of an object that implements the + * {@link TJCustomFilter} interface, or null if no custom filter is needed */ @SuppressWarnings("checkstyle:HiddenField") public TJTransform(int x, int y, int w, int h, int op, int options, @@ -194,18 +204,18 @@ public TJTransform(int x, int y, int w, int h, int op, int options, /** * Create a new lossless transform instance with the given parameters. * - * @param r a Rectangle instance that specifies the cropping - * region. See {@link - * #TJTransform(int, int, int, int, int, int, TJCustomFilter)} for more - * detail. + * @param r a java.awt.Rectangle instance that specifies the + * cropping region. See + * {@link #TJTransform(int, int, int, int, int, int, TJCustomFilter)} for + * more details. * * @param op one of the transform operations ({@link #OP_NONE OP_*}) * * @param options the bitwise OR of one or more of the transform options * ({@link #OPT_PERFECT OPT_*}) * - * @param cf an instance of an object that implements the {@link - * TJCustomFilter} interface, or null if no custom filter is needed + * @param cf an instance of an object that implements the + * {@link TJCustomFilter} interface, or null if no custom filter is needed */ @SuppressWarnings("checkstyle:HiddenField") public TJTransform(Rectangle r, int op, int options, diff --git a/java/org/libjpegturbo/turbojpeg/TJTransformer.java b/java/org/libjpegturbo/turbojpeg/TJTransformer.java index 2cbf0bfb9..4d46b5a14 100644 --- a/java/org/libjpegturbo/turbojpeg/TJTransformer.java +++ b/java/org/libjpegturbo/turbojpeg/TJTransformer.java @@ -87,23 +87,30 @@ public TJTransformer(byte[] jpegImage, int imageSize) throws TJException { * @param dstBufs an array of JPEG destination buffers. * dstbufs[i] will receive a JPEG image that has been * transformed using the parameters in transforms[i]. Use - * {@link TJ#bufSize} to determine the maximum size for each buffer based on - * the transformed or cropped width and height and the level of subsampling - * used in the source image. + * {@link TJ#bufSize TJ.bufSize()} to determine the maximum size for each + * buffer based on the transformed or cropped width and height and the level + * of subsampling used in the source image. * * @param transforms an array of {@link TJTransform} instances, each of * which specifies the transform parameters and/or cropping region for the * corresponding transformed JPEG image - * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} */ + public void transform(byte[][] dstBufs, TJTransform[] transforms) + throws TJException { + transformedSizes = transform(getJPEGBuf(), getJPEGSize(), dstBufs, + transforms); + } + + /** + * @deprecated Use {@link #set TJDecompressor.set()} and + * {@link #transform(byte[][], TJTransform[])} instead. + */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated public void transform(byte[][] dstBufs, TJTransform[] transforms, int flags) throws TJException { - if (jpegBuf == null) - throw new IllegalStateException("JPEG buffer not initialized"); - transformedSizes = transform(jpegBuf, jpegBufSize, dstBufs, transforms, - flags); + processFlags(flags); + transform(dstBufs, transforms); } /** @@ -115,32 +122,42 @@ public void transform(byte[][] dstBufs, TJTransform[] transforms, * which specifies the transform parameters and/or cropping region for the * corresponding transformed JPEG image * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} - * * @return an array of {@link TJDecompressor} instances, each of * which has a transformed JPEG image associated with it. */ - public TJDecompressor[] transform(TJTransform[] transforms, int flags) + public TJDecompressor[] transform(TJTransform[] transforms) throws TJException { byte[][] dstBufs = new byte[transforms.length][]; - if (jpegWidth < 1 || jpegHeight < 1) + if (getWidth() < 1 || getHeight() < 1) throw new IllegalStateException("JPEG buffer not initialized"); + checkSubsampling(); for (int i = 0; i < transforms.length; i++) { - int w = jpegWidth, h = jpegHeight; + int w = getWidth(), h = getHeight(); if ((transforms[i].options & TJTransform.OPT_CROP) != 0) { if (transforms[i].width != 0) w = transforms[i].width; if (transforms[i].height != 0) h = transforms[i].height; } - dstBufs[i] = new byte[TJ.bufSize(w, h, jpegSubsamp)]; + dstBufs[i] = new byte[TJ.bufSize(w, h, get(TJ.PARAM_SUBSAMP))]; } TJDecompressor[] tjd = new TJDecompressor[transforms.length]; - transform(dstBufs, transforms, flags); + transform(dstBufs, transforms); for (int i = 0; i < transforms.length; i++) tjd[i] = new TJDecompressor(dstBufs[i], transformedSizes[i]); return tjd; } + /** + * @deprecated Use {@link #set TJDecompressor.set()} and + * {@link #transform(TJTransform[])} instead. + */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated + public TJDecompressor[] transform(TJTransform[] transforms, int flags) + throws TJException { + processFlags(flags); + return transform(transforms); + } + /** * Returns an array containing the sizes of the transformed JPEG images * (in bytes) generated by the most recent transform operation. @@ -157,7 +174,7 @@ public int[] getTransformedSizes() { private native void init() throws TJException; private native int[] transform(byte[] srcBuf, int srcSize, byte[][] dstBufs, - TJTransform[] transforms, int flags) throws TJException; + TJTransform[] transforms) throws TJException; static { TJLoader.load(); diff --git a/java/org/libjpegturbo/turbojpeg/YUVImage.java b/java/org/libjpegturbo/turbojpeg/YUVImage.java index 948304647..60ace66af 100644 --- a/java/org/libjpegturbo/turbojpeg/YUVImage.java +++ b/java/org/libjpegturbo/turbojpeg/YUVImage.java @@ -121,8 +121,8 @@ public YUVImage(int width, int align, int height, int subsamp) { * @param planes an array of buffers representing the Y, U (Cb), and V (Cr) * image planes (or just the Y plane, if the image is grayscale.) These * planes can be contiguous or non-contiguous in memory. Plane - * i should be at least offsets[i] + - * {@link TJ#planeSizeYUV TJ.planeSizeYUV}(i, width, strides[i], height, subsamp) + * i should be at least offsets[i] + + * {@link TJ#planeSizeYUV TJ.planeSizeYUV}(i, width, strides[i], height, subsamp) * bytes in size. * * @param offsets If this YUVImage instance represents a @@ -158,10 +158,10 @@ public YUVImage(byte[][] planes, int[] offsets, int width, int[] strides, * buffer. * * @param yuvImage buffer that contains or will receive a unified planar YUV - * image. Use {@link TJ#bufSizeYUV} to determine the minimum size for this - * buffer. The Y, U (Cb), and V (Cr) image planes are stored sequentially in - * the buffer. (See {@link YUVImage above} for a description of the image - * format.) + * image. Use {@link TJ#bufSizeYUV TJ.bufSizeYUV()} to determine the minimum + * size for this buffer. The Y, U (Cb), and V (Cr) image planes are stored + * sequentially in the buffer. (See {@link YUVImage above} for a description + * of the image format.) * * @param width width (in pixels) of the YUV image * @@ -186,8 +186,8 @@ public YUVImage(byte[] yuvImage, int width, int align, int height, * @param planes an array of buffers representing the Y, U (Cb), and V (Cr) * image planes (or just the Y plane, if the image is grayscale.) These * planes can be contiguous or non-contiguous in memory. Plane - * i should be at least offsets[i] + - * {@link TJ#planeSizeYUV TJ.planeSizeYUV}(i, width, strides[i], height, subsamp) + * i should be at least offsets[i] + + * {@link TJ#planeSizeYUV TJ.planeSizeYUV}(i, width, strides[i], height, subsamp) * bytes in size. * * @param offsets If this YUVImage instance represents a @@ -271,10 +271,10 @@ private void setBuf(byte[][] planes, int[] offsets, int width, int[] strides, * Assign a unified buffer to this YUVImage instance. * * @param yuvImage buffer that contains or will receive a unified planar YUV - * image. Use {@link TJ#bufSizeYUV} to determine the minimum size for this - * buffer. The Y, U (Cb), and V (Cr) image planes are stored sequentially in - * the buffer. (See {@link YUVImage above} for a description of the image - * format.) + * image. Use {@link TJ#bufSizeYUV TJ.bufSizeYUV()} to determine the minimum + * size for this buffer. The Y, U (Cb), and V (Cr) image planes are stored + * sequentially in the buffer. (See {@link YUVImage above} for a description + * of the image format.) * * @param width width (in pixels) of the YUV image * @@ -441,12 +441,12 @@ private static int pad(int v, int p) { return (v + p - 1) & (~(p - 1)); } - protected long handle = 0; - protected byte[][] yuvPlanes = null; - protected int[] yuvOffsets = null; - protected int[] yuvStrides = null; - protected int yuvAlign = 1; - protected int yuvWidth = 0; - protected int yuvHeight = 0; - protected int yuvSubsamp = -1; + private long handle = 0; + private byte[][] yuvPlanes = null; + private int[] yuvOffsets = null; + private int[] yuvStrides = null; + private int yuvAlign = 1; + private int yuvWidth = 0; + private int yuvHeight = 0; + private int yuvSubsamp = -1; } diff --git a/java/org_libjpegturbo_turbojpeg_TJ.h b/java/org_libjpegturbo_turbojpeg_TJ.h index e1a46cfd1..5ebaf187d 100644 --- a/java/org_libjpegturbo_turbojpeg_TJ.h +++ b/java/org_libjpegturbo_turbojpeg_TJ.h @@ -21,6 +21,8 @@ extern "C" { #define org_libjpegturbo_turbojpeg_TJ_SAMP_440 4L #undef org_libjpegturbo_turbojpeg_TJ_SAMP_411 #define org_libjpegturbo_turbojpeg_TJ_SAMP_411 5L +#undef org_libjpegturbo_turbojpeg_TJ_SAMP_UNKNOWN +#define org_libjpegturbo_turbojpeg_TJ_SAMP_UNKNOWN -1L #undef org_libjpegturbo_turbojpeg_TJ_NUMPF #define org_libjpegturbo_turbojpeg_TJ_NUMPF 12L #undef org_libjpegturbo_turbojpeg_TJ_PF_RGB @@ -59,6 +61,50 @@ extern "C" { #define org_libjpegturbo_turbojpeg_TJ_CS_CMYK 3L #undef org_libjpegturbo_turbojpeg_TJ_CS_YCCK #define org_libjpegturbo_turbojpeg_TJ_CS_YCCK 4L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_STOPONWARNING +#define org_libjpegturbo_turbojpeg_TJ_PARAM_STOPONWARNING 0L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_BOTTOMUP +#define org_libjpegturbo_turbojpeg_TJ_PARAM_BOTTOMUP 1L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_QUALITY +#define org_libjpegturbo_turbojpeg_TJ_PARAM_QUALITY 3L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_SUBSAMP +#define org_libjpegturbo_turbojpeg_TJ_PARAM_SUBSAMP 4L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_JPEGWIDTH +#define org_libjpegturbo_turbojpeg_TJ_PARAM_JPEGWIDTH 5L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_JPEGHEIGHT +#define org_libjpegturbo_turbojpeg_TJ_PARAM_JPEGHEIGHT 6L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_PRECISION +#define org_libjpegturbo_turbojpeg_TJ_PARAM_PRECISION 7L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_COLORSPACE +#define org_libjpegturbo_turbojpeg_TJ_PARAM_COLORSPACE 8L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_FASTUPSAMPLE +#define org_libjpegturbo_turbojpeg_TJ_PARAM_FASTUPSAMPLE 9L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_FASTDCT +#define org_libjpegturbo_turbojpeg_TJ_PARAM_FASTDCT 10L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_OPTIMIZE +#define org_libjpegturbo_turbojpeg_TJ_PARAM_OPTIMIZE 11L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_PROGRESSIVE +#define org_libjpegturbo_turbojpeg_TJ_PARAM_PROGRESSIVE 12L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_SCANLIMIT +#define org_libjpegturbo_turbojpeg_TJ_PARAM_SCANLIMIT 13L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_ARITHMETIC +#define org_libjpegturbo_turbojpeg_TJ_PARAM_ARITHMETIC 14L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_LOSSLESS +#define org_libjpegturbo_turbojpeg_TJ_PARAM_LOSSLESS 15L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_LOSSLESSPSV +#define org_libjpegturbo_turbojpeg_TJ_PARAM_LOSSLESSPSV 16L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_LOSSLESSPT +#define org_libjpegturbo_turbojpeg_TJ_PARAM_LOSSLESSPT 17L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_RESTARTBLOCKS +#define org_libjpegturbo_turbojpeg_TJ_PARAM_RESTARTBLOCKS 18L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_RESTARTROWS +#define org_libjpegturbo_turbojpeg_TJ_PARAM_RESTARTROWS 19L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_XDENSITY +#define org_libjpegturbo_turbojpeg_TJ_PARAM_XDENSITY 20L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_YDENSITY +#define org_libjpegturbo_turbojpeg_TJ_PARAM_YDENSITY 21L +#undef org_libjpegturbo_turbojpeg_TJ_PARAM_DENSITYUNITS +#define org_libjpegturbo_turbojpeg_TJ_PARAM_DENSITYUNITS 22L #undef org_libjpegturbo_turbojpeg_TJ_FLAG_BOTTOMUP #define org_libjpegturbo_turbojpeg_TJ_FLAG_BOTTOMUP 2L #undef org_libjpegturbo_turbojpeg_TJ_FLAG_FASTUPSAMPLE diff --git a/java/org_libjpegturbo_turbojpeg_TJCompressor.h b/java/org_libjpegturbo_turbojpeg_TJCompressor.h index eeb2d6adc..24bc52150 100644 --- a/java/org_libjpegturbo_turbojpeg_TJCompressor.h +++ b/java/org_libjpegturbo_turbojpeg_TJCompressor.h @@ -7,6 +7,22 @@ #ifdef __cplusplus extern "C" { #endif +/* + * Class: org_libjpegturbo_turbojpeg_TJCompressor + * Method: set + * Signature: (II)V + */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_set + (JNIEnv *, jobject, jint, jint); + +/* + * Class: org_libjpegturbo_turbojpeg_TJCompressor + * Method: get + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_get + (JNIEnv *, jobject, jint); + /* * Class: org_libjpegturbo_turbojpeg_TJCompressor * Method: init @@ -25,43 +41,67 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_destroy /* * Class: org_libjpegturbo_turbojpeg_TJCompressor - * Method: compress - * Signature: ([BIIIIII[BIII)I + * Method: compress8 + * Signature: ([BIIIIII[B)I + */ +JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress8___3BIIIIII_3B + (JNIEnv *, jobject, jbyteArray, jint, jint, jint, jint, jint, jint, jbyteArray); + +/* + * Class: org_libjpegturbo_turbojpeg_TJCompressor + * Method: compress12 + * Signature: ([SIIIIII[B)I + */ +JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress12 + (JNIEnv *, jobject, jshortArray, jint, jint, jint, jint, jint, jint, jbyteArray); + +/* + * Class: org_libjpegturbo_turbojpeg_TJCompressor + * Method: compress16 + * Signature: ([SIIIIII[B)I + */ +JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress16 + (JNIEnv *, jobject, jshortArray, jint, jint, jint, jint, jint, jint, jbyteArray); + +/* + * Class: org_libjpegturbo_turbojpeg_TJCompressor + * Method: compress8 + * Signature: ([IIIIIII[B)I */ -JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3BIIIIII_3BIII - (JNIEnv *, jobject, jbyteArray, jint, jint, jint, jint, jint, jint, jbyteArray, jint, jint, jint); +JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress8___3IIIIIII_3B + (JNIEnv *, jobject, jintArray, jint, jint, jint, jint, jint, jint, jbyteArray); /* * Class: org_libjpegturbo_turbojpeg_TJCompressor - * Method: compress - * Signature: ([IIIIIII[BIII)I + * Method: compressFromYUV8 + * Signature: ([[B[II[II[B)I */ -JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3IIIIIII_3BIII - (JNIEnv *, jobject, jintArray, jint, jint, jint, jint, jint, jint, jbyteArray, jint, jint, jint); +JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFromYUV8 + (JNIEnv *, jobject, jobjectArray, jintArray, jint, jintArray, jint, jbyteArray); /* * Class: org_libjpegturbo_turbojpeg_TJCompressor - * Method: compressFromYUV - * Signature: ([[B[II[III[BII)I + * Method: encodeYUV8 + * Signature: ([BIIIIII[[B[I[I)V */ -JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFromYUV___3_3B_3II_3III_3BII - (JNIEnv *, jobject, jobjectArray, jintArray, jint, jintArray, jint, jint, jbyteArray, jint, jint); +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV8___3BIIIIII_3_3B_3I_3I + (JNIEnv *, jobject, jbyteArray, jint, jint, jint, jint, jint, jint, jobjectArray, jintArray, jintArray); /* * Class: org_libjpegturbo_turbojpeg_TJCompressor - * Method: encodeYUV - * Signature: ([BIIIIII[[B[I[III)V + * Method: encodeYUV8 + * Signature: ([IIIIIII[[B[I[I)V */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3BIIIIII_3_3B_3I_3III - (JNIEnv *, jobject, jbyteArray, jint, jint, jint, jint, jint, jint, jobjectArray, jintArray, jintArray, jint, jint); +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV8___3IIIIIII_3_3B_3I_3I + (JNIEnv *, jobject, jintArray, jint, jint, jint, jint, jint, jint, jobjectArray, jintArray, jintArray); /* * Class: org_libjpegturbo_turbojpeg_TJCompressor - * Method: encodeYUV - * Signature: ([IIIIIII[[B[I[III)V + * Method: loadImage + * Signature: (ILjava/lang/String;[II[I[I)Ljava/lang/Object; */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3IIIIIII_3_3B_3I_3III - (JNIEnv *, jobject, jintArray, jint, jint, jint, jint, jint, jint, jobjectArray, jintArray, jintArray, jint, jint); +JNIEXPORT jobject JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_loadImage + (JNIEnv *, jobject, jint, jstring, jintArray, jint, jintArray, jintArray); #ifdef __cplusplus } diff --git a/java/org_libjpegturbo_turbojpeg_TJDecompressor.h b/java/org_libjpegturbo_turbojpeg_TJDecompressor.h index 96af6a15d..621ad5fc1 100644 --- a/java/org_libjpegturbo_turbojpeg_TJDecompressor.h +++ b/java/org_libjpegturbo_turbojpeg_TJDecompressor.h @@ -7,6 +7,22 @@ #ifdef __cplusplus extern "C" { #endif +/* + * Class: org_libjpegturbo_turbojpeg_TJDecompressor + * Method: set + * Signature: (II)V + */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_set + (JNIEnv *, jobject, jint, jint); + +/* + * Class: org_libjpegturbo_turbojpeg_TJDecompressor + * Method: get + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_get + (JNIEnv *, jobject, jint); + /* * Class: org_libjpegturbo_turbojpeg_TJDecompressor * Method: init @@ -33,43 +49,75 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress /* * Class: org_libjpegturbo_turbojpeg_TJDecompressor - * Method: decompress - * Signature: ([BI[BIIIIIII)V + * Method: setCroppingRegion + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_setCroppingRegion + (JNIEnv *, jobject); + +/* + * Class: org_libjpegturbo_turbojpeg_TJDecompressor + * Method: decompress8 + * Signature: ([BI[BIIII)V + */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress8___3BI_3BIIII + (JNIEnv *, jobject, jbyteArray, jint, jbyteArray, jint, jint, jint, jint); + +/* + * Class: org_libjpegturbo_turbojpeg_TJDecompressor + * Method: decompress12 + * Signature: ([BI[SIIII)V + */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress12 + (JNIEnv *, jobject, jbyteArray, jint, jshortArray, jint, jint, jint, jint); + +/* + * Class: org_libjpegturbo_turbojpeg_TJDecompressor + * Method: decompress16 + * Signature: ([BI[SIIII)V + */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress16 + (JNIEnv *, jobject, jbyteArray, jint, jshortArray, jint, jint, jint, jint); + +/* + * Class: org_libjpegturbo_turbojpeg_TJDecompressor + * Method: decompress8 + * Signature: ([BI[IIIII)V */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress___3BI_3BIIIIIII - (JNIEnv *, jobject, jbyteArray, jint, jbyteArray, jint, jint, jint, jint, jint, jint, jint); +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress8___3BI_3IIIII + (JNIEnv *, jobject, jbyteArray, jint, jintArray, jint, jint, jint, jint); /* * Class: org_libjpegturbo_turbojpeg_TJDecompressor - * Method: decompress - * Signature: ([BI[IIIIIIII)V + * Method: decompressToYUV8 + * Signature: ([BI[[B[I[I)V */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress___3BI_3IIIIIIII - (JNIEnv *, jobject, jbyteArray, jint, jintArray, jint, jint, jint, jint, jint, jint, jint); +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressToYUV8 + (JNIEnv *, jobject, jbyteArray, jint, jobjectArray, jintArray, jintArray); /* * Class: org_libjpegturbo_turbojpeg_TJDecompressor - * Method: decompressToYUV - * Signature: ([BI[[B[II[III)V + * Method: decodeYUV8 + * Signature: ([[B[I[I[BIIIIII)V */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressToYUV___3BI_3_3B_3II_3III - (JNIEnv *, jobject, jbyteArray, jint, jobjectArray, jintArray, jint, jintArray, jint, jint); +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV8___3_3B_3I_3I_3BIIIIII + (JNIEnv *, jobject, jobjectArray, jintArray, jintArray, jbyteArray, jint, jint, jint, jint, jint, jint); /* * Class: org_libjpegturbo_turbojpeg_TJDecompressor - * Method: decodeYUV - * Signature: ([[B[I[II[BIIIIIII)V + * Method: decodeYUV8 + * Signature: ([[B[I[I[IIIIIII)V */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV___3_3B_3I_3II_3BIIIIIII - (JNIEnv *, jobject, jobjectArray, jintArray, jintArray, jint, jbyteArray, jint, jint, jint, jint, jint, jint, jint); +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV8___3_3B_3I_3I_3IIIIIII + (JNIEnv *, jobject, jobjectArray, jintArray, jintArray, jintArray, jint, jint, jint, jint, jint, jint); /* * Class: org_libjpegturbo_turbojpeg_TJDecompressor - * Method: decodeYUV - * Signature: ([[B[I[II[IIIIIIII)V + * Method: saveImage + * Signature: (ILjava/lang/String;Ljava/lang/Object;IIII)V */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV___3_3B_3I_3II_3IIIIIIII - (JNIEnv *, jobject, jobjectArray, jintArray, jintArray, jint, jintArray, jint, jint, jint, jint, jint, jint, jint); +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_saveImage + (JNIEnv *, jobject, jint, jstring, jobject, jint, jint, jint, jint); #ifdef __cplusplus } diff --git a/java/org_libjpegturbo_turbojpeg_TJTransformer.h b/java/org_libjpegturbo_turbojpeg_TJTransformer.h index a9dad4d68..5d860335b 100644 --- a/java/org_libjpegturbo_turbojpeg_TJTransformer.h +++ b/java/org_libjpegturbo_turbojpeg_TJTransformer.h @@ -18,10 +18,10 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_init /* * Class: org_libjpegturbo_turbojpeg_TJTransformer * Method: transform - * Signature: ([BI[[B[Lorg/libjpegturbo/turbojpeg/TJTransform;I)[I + * Signature: ([BI[[B[Lorg/libjpegturbo/turbojpeg/TJTransform;)[I */ JNIEXPORT jintArray JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_transform - (JNIEnv *, jobject, jbyteArray, jint, jobjectArray, jobjectArray, jint); + (JNIEnv *, jobject, jbyteArray, jint, jobjectArray, jobjectArray); #ifdef __cplusplus } diff --git a/jcapimin.c b/jcapimin.c index cbb3d13e1..25ce9a110 100644 --- a/jcapimin.c +++ b/jcapimin.c @@ -5,7 +5,7 @@ * Copyright (C) 1994-1998, Thomas G. Lane. * Modified 2003-2010 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2022, D. R. Commander. + * Copyright (C) 2022-2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -240,10 +240,11 @@ jpeg_write_marker(j_compress_ptr cinfo, int marker, const JOCTET *dataptr, (*cinfo->marker->write_marker_header) (cinfo, marker, datalen); write_marker_byte = cinfo->marker->write_marker_byte; /* copy for speed */ - while (datalen--) { + do { (*write_marker_byte) (cinfo, *dataptr); dataptr++; } + while (--datalen); } /* Same, but piecemeal. */ diff --git a/jdatadst-tj.c b/jdatadst-tj.c index e10d98127..cce263af7 100644 --- a/jdatadst-tj.c +++ b/jdatadst-tj.c @@ -5,7 +5,7 @@ * Copyright (C) 1994-1996, Thomas G. Lane. * Modified 2009-2012 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2011, 2014, 2016, 2019, 2022, D. R. Commander. + * Copyright (C) 2011, 2014, 2016, 2019, 2022-2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -24,7 +24,7 @@ #include "jerror.h" void jpeg_mem_dest_tj(j_compress_ptr cinfo, unsigned char **outbuffer, - unsigned long *outsize, boolean alloc); + size_t *outsize, boolean alloc); #define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */ @@ -36,7 +36,7 @@ typedef struct { struct jpeg_destination_mgr pub; /* public fields */ unsigned char **outbuffer; /* target buffer */ - unsigned long *outsize; + size_t *outsize; unsigned char *newbuffer; /* newly allocated buffer */ JOCTET *buffer; /* start of buffer */ size_t bufsize; @@ -128,7 +128,7 @@ term_mem_destination(j_compress_ptr cinfo) my_mem_dest_ptr dest = (my_mem_dest_ptr)cinfo->dest; if (dest->alloc) *dest->outbuffer = dest->buffer; - *dest->outsize = (unsigned long)(dest->bufsize - dest->pub.free_in_buffer); + *dest->outsize = dest->bufsize - dest->pub.free_in_buffer; } @@ -145,7 +145,7 @@ term_mem_destination(j_compress_ptr cinfo) GLOBAL(void) jpeg_mem_dest_tj(j_compress_ptr cinfo, unsigned char **outbuffer, - unsigned long *outsize, boolean alloc) + size_t *outsize, boolean alloc) { boolean reused = FALSE; my_mem_dest_ptr dest; diff --git a/jdatasrc-tj.c b/jdatasrc-tj.c index 69fb5eaac..a5970b53f 100644 --- a/jdatasrc-tj.c +++ b/jdatasrc-tj.c @@ -5,7 +5,7 @@ * Copyright (C) 1994-1996, Thomas G. Lane. * Modified 2009-2011 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2011, 2016, 2019, D. R. Commander. + * Copyright (C) 2011, 2016, 2019, 2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -24,7 +24,7 @@ #include "jerror.h" void jpeg_mem_src_tj(j_decompress_ptr cinfo, const unsigned char *inbuffer, - unsigned long insize); + size_t insize); /* @@ -161,7 +161,7 @@ term_source(j_decompress_ptr cinfo) GLOBAL(void) jpeg_mem_src_tj(j_decompress_ptr cinfo, const unsigned char *inbuffer, - unsigned long insize) + size_t insize) { struct jpeg_source_mgr *src; @@ -189,6 +189,6 @@ jpeg_mem_src_tj(j_decompress_ptr cinfo, const unsigned char *inbuffer, src->skip_input_data = skip_input_data; src->resync_to_restart = jpeg_resync_to_restart; /* use default method */ src->term_source = term_source; - src->bytes_in_buffer = (size_t)insize; + src->bytes_in_buffer = insize; src->next_input_byte = (const JOCTET *)inbuffer; } diff --git a/rdppm.c b/rdppm.c index 5aaee0416..570580697 100644 --- a/rdppm.c +++ b/rdppm.c @@ -507,6 +507,70 @@ get_word_gray_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) } +METHODDEF(JDIMENSION) +get_word_gray_rgb_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading raw-word-format PGM files with any maxval */ +{ + ppm_source_ptr source = (ppm_source_ptr)sinfo; + register _JSAMPROW ptr; + register U_CHAR *bufferptr; + register _JSAMPLE *rescale = source->rescale; + JDIMENSION col; + unsigned int maxval = source->maxval; + register int rindex = rgb_red[cinfo->in_color_space]; + register int gindex = rgb_green[cinfo->in_color_space]; + register int bindex = rgb_blue[cinfo->in_color_space]; + register int aindex = alpha_index[cinfo->in_color_space]; + register int ps = rgb_pixelsize[cinfo->in_color_space]; + + if (!ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) + ERREXIT(cinfo, JERR_INPUT_EOF); + ptr = source->pub._buffer[0]; + bufferptr = source->iobuffer; + for (col = cinfo->image_width; col > 0; col--) { + register unsigned int temp; + temp = UCH(*bufferptr++) << 8; + temp |= UCH(*bufferptr++); + if (temp > maxval) + ERREXIT(cinfo, JERR_PPM_OUTOFRANGE); + ptr[rindex] = ptr[gindex] = ptr[bindex] = rescale[temp]; + if (aindex >= 0) + ptr[aindex] = _MAXJSAMPLE; + ptr += ps; + } + return 1; +} + + +METHODDEF(JDIMENSION) +get_word_gray_cmyk_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading raw-word-format PGM files with any maxval */ +{ + ppm_source_ptr source = (ppm_source_ptr)sinfo; + register _JSAMPROW ptr; + register U_CHAR *bufferptr; + register _JSAMPLE *rescale = source->rescale; + JDIMENSION col; + unsigned int maxval = source->maxval; + + if (!ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) + ERREXIT(cinfo, JERR_INPUT_EOF); + ptr = source->pub._buffer[0]; + bufferptr = source->iobuffer; + for (col = cinfo->image_width; col > 0; col--) { + register unsigned int gray; + gray = UCH(*bufferptr++) << 8; + gray |= UCH(*bufferptr++); + if (gray > maxval) + ERREXIT(cinfo, JERR_PPM_OUTOFRANGE); + rgb_to_cmyk(rescale[gray], rescale[gray], rescale[gray], ptr, ptr + 1, + ptr + 2, ptr + 3); + ptr += 4; + } + return 1; +} + + METHODDEF(JDIMENSION) get_word_rgb_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) /* This version is for reading raw-word-format PPM files with any maxval */ @@ -552,6 +616,43 @@ get_word_rgb_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) } +METHODDEF(JDIMENSION) +get_word_rgb_cmyk_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading raw-word-format PPM files with any maxval */ +{ + ppm_source_ptr source = (ppm_source_ptr)sinfo; + register _JSAMPROW ptr; + register U_CHAR *bufferptr; + register _JSAMPLE *rescale = source->rescale; + JDIMENSION col; + unsigned int maxval = source->maxval; + + if (!ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) + ERREXIT(cinfo, JERR_INPUT_EOF); + ptr = source->pub._buffer[0]; + bufferptr = source->iobuffer; + for (col = cinfo->image_width; col > 0; col--) { + register unsigned int r, g, b; + r = UCH(*bufferptr++) << 8; + r |= UCH(*bufferptr++); + if (r > maxval) + ERREXIT(cinfo, JERR_PPM_OUTOFRANGE); + g = UCH(*bufferptr++) << 8; + g |= UCH(*bufferptr++); + if (g > maxval) + ERREXIT(cinfo, JERR_PPM_OUTOFRANGE); + b = UCH(*bufferptr++) << 8; + b |= UCH(*bufferptr++); + if (b > maxval) + ERREXIT(cinfo, JERR_PPM_OUTOFRANGE); + rgb_to_cmyk(rescale[r], rescale[g], rescale[b], ptr, ptr + 1, ptr + 2, + ptr + 3); + ptr += 4; + } + return 1; +} + + /* * Read the file header; return image size and component count. */ @@ -641,6 +742,10 @@ start_input_ppm(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) if (maxval > 255) { if (cinfo->in_color_space == JCS_GRAYSCALE) source->pub.get_pixel_rows = get_word_gray_row; + else if (IsExtRGB(cinfo->in_color_space)) + source->pub.get_pixel_rows = get_word_gray_rgb_row; + else if (cinfo->in_color_space == JCS_CMYK) + source->pub.get_pixel_rows = get_word_gray_cmyk_row; else ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); } else if (maxval == _MAXJSAMPLE && sizeof(_JSAMPLE) == sizeof(U_CHAR) && @@ -667,6 +772,8 @@ start_input_ppm(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) if (maxval > 255) { if (IsExtRGB(cinfo->in_color_space)) source->pub.get_pixel_rows = get_word_rgb_row; + else if (cinfo->in_color_space == JCS_CMYK) + source->pub.get_pixel_rows = get_word_rgb_cmyk_row; else ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); } else if (maxval == _MAXJSAMPLE && sizeof(_JSAMPLE) == sizeof(U_CHAR) && diff --git a/testimages/big_building16.ppm b/testimages/big_building16.ppm new file mode 100644 index 0000000000000000000000000000000000000000..b0b843932d3ee44a293adf5b0b1b4ebe7d28ce5b GIT binary patch literal 202955 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�ik%++;p)!h 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

        {RS#S;Z`U1*KVm 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�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%0A>O;`ghmj{vwS#Q}NAOV80 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 literal 0 HcmV?d00001 diff --git a/testimages/big_tree8.bmp b/testimages/big_tree8.bmp new file mode 100644 index 0000000000000000000000000000000000000000..b1dfc45f5f3433005859f40e21eecddf5b1e3814 GIT binary patch literal 82998 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Þ?3 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 0) { - SNPRINTF(qualStr, 13, "_Q%d", jpegQual); - qualStr[12] = 0; + SNPRINTF(qualStr, 16, "_%s%d", lossless ? "PSV" : "Q", jpegQual); + qualStr[15] = 0; } - if ((handle = tjInitDecompress()) == NULL) - THROW_TJ("executing tjInitDecompress()"); + if ((handle = tj3Init(TJINIT_DECOMPRESS)) == NULL) + THROW_TJ("executing tj3Init()"); + if (tj3Set(handle, TJPARAM_STOPONWARNING, stopOnWarning) == -1) + THROW_TJ("executing tj3Set()"); + if (tj3Set(handle, TJPARAM_BOTTOMUP, bottomUp) == -1) + THROW_TJ("executing tj3Set()"); + if (tj3Set(handle, TJPARAM_FASTUPSAMPLE, fastUpsample) == -1) + THROW_TJ("executing tj3Set()"); + if (tj3Set(handle, TJPARAM_FASTDCT, fastDCT) == -1) + THROW_TJ("executing tj3Set()"); + if (tj3Set(handle, TJPARAM_SCANLIMIT, limitScans ? 500 : 0) == -1) + THROW_TJ("executing tj3Set()"); + + if (IS_CROPPED(cr)) { + if (tj3DecompressHeader(handle, jpegBufs[0], jpegSizes[0]) == -1) + THROW_TJ("executing tj3DecompressHeader()"); + } + if (tj3SetScalingFactor(handle, sf) == -1) + THROW_TJ("executing tj3SetScalingFactor()"); + if (tj3SetCroppingRegion(handle, cr) == -1) + THROW_TJ("executing tj3SetCroppingRegion()"); + if (IS_CROPPED(cr)) { + scaledw = cr.w ? cr.w : scaledw - cr.x; + scaledh = cr.h ? cr.h : scaledh - cr.y; + } + pitch = scaledw * ps; if (dstBuf == NULL) { - if ((unsigned long long)pitch * (unsigned long long)scaledh > - (unsigned long long)((size_t)-1)) + if ((unsigned long long)pitch * (unsigned long long)scaledh * + (unsigned long long)sampleSize > (unsigned long long)((size_t)-1)) THROW("allocating destination buffer", "Image is too large"); - if ((dstBuf = (unsigned char *)malloc((size_t)pitch * scaledh)) == NULL) + if ((dstBuf = malloc((size_t)pitch * scaledh * sampleSize)) == NULL) THROW_UNIX("allocating destination buffer"); dstBufAlloc = 1; } + /* Set the destination buffer to gray so we know whether the decompressor attempted to write to it */ - memset(dstBuf, 127, (size_t)pitch * scaledh); + if (precision == 8) + memset((unsigned char *)dstBuf, 127, (size_t)pitch * scaledh); + else if (precision == 12) { + for (i = 0; i < pitch * scaledh; i++) + ((short *)dstBuf)[i] = (short)2047; + } else { + for (i = 0; i < pitch * scaledh; i++) + ((unsigned short *)dstBuf)[i] = (unsigned short)32767; + } if (doYUV) { int width = doTile ? tilew : scaledw; int height = doTile ? tileh : scaledh; - unsigned long yuvSize = tjBufSizeYUV2(width, yuvAlign, height, subsamp); + size_t yuvSize = tj3YUVBufSize(width, yuvAlign, height, subsamp); - if (yuvSize == (unsigned long)-1) + if (yuvSize == 0) THROW_TJ("allocating YUV buffer"); if ((yuvBuf = (unsigned char *)malloc(yuvSize)) == NULL) THROW_UNIX("allocating YUV buffer"); @@ -204,27 +261,38 @@ static int decomp(unsigned char *srcBuf, unsigned char **jpegBuf, double start = getTime(); for (row = 0, dstPtr = dstBuf; row < ntilesh; - row++, dstPtr += (size_t)pitch * tileh) { + row++, dstPtr += (size_t)pitch * tileh * sampleSize) { for (col = 0, dstPtr2 = dstPtr; col < ntilesw; - col++, tile++, dstPtr2 += ps * tilew) { + col++, tile++, dstPtr2 += ps * tilew * sampleSize) { int width = doTile ? min(tilew, w - col * tilew) : scaledw; int height = doTile ? min(tileh, h - row * tileh) : scaledh; if (doYUV) { double startDecode; - if (tjDecompressToYUV2(handle, jpegBuf[tile], jpegSize[tile], yuvBuf, - width, yuvAlign, height, flags) == -1) - THROW_TJ("executing tjDecompressToYUV2()"); + if (tj3DecompressToYUV8(handle, jpegBufs[tile], jpegSizes[tile], + yuvBuf, yuvAlign) == -1) + THROW_TJ("executing tj3DecompressToYUV8()"); startDecode = getTime(); - if (tjDecodeYUV(handle, yuvBuf, yuvAlign, subsamp, dstPtr2, width, - pitch, height, pf, flags) == -1) - THROW_TJ("executing tjDecodeYUV()"); + if (tj3DecodeYUV8(handle, yuvBuf, yuvAlign, dstPtr2, width, pitch, + height, pf) == -1) + THROW_TJ("executing tj3DecodeYUV8()"); if (iter >= 0) elapsedDecode += getTime() - startDecode; - } else if (tjDecompress2(handle, jpegBuf[tile], jpegSize[tile], - dstPtr2, width, pitch, height, pf, - flags) == -1) - THROW_TJ("executing tjDecompress2()"); + } else { + if (precision == 8) { + if (tj3Decompress8(handle, jpegBufs[tile], jpegSizes[tile], + dstPtr2, pitch, pf) == -1) + THROW_TJ("executing tj3Decompress8()"); + } else if (precision == 12) { + if (tj3Decompress12(handle, jpegBufs[tile], jpegSizes[tile], + (short *)dstPtr2, pitch, pf) == -1) + THROW_TJ("executing tj3Decompress12()"); + } else { + if (tj3Decompress16(handle, jpegBufs[tile], jpegSizes[tile], + (unsigned short *)dstPtr2, pitch, pf) == -1) + THROW_TJ("executing tj3Decompress16()"); + } + } } } elapsed += getTime() - start; @@ -238,9 +306,6 @@ static int decomp(unsigned char *srcBuf, unsigned char **jpegBuf, } if (doYUV) elapsed -= elapsedDecode; - if (tjDestroy(handle) == -1) THROW_TJ("executing tjDestroy()"); - handle = NULL; - if (quiet) { printf("%-6s%s", sigfig((double)(w * h) / 1000000. * (double)iter / elapsed, 4, @@ -274,80 +339,58 @@ static int decomp(unsigned char *srcBuf, unsigned char **jpegBuf, if (decompOnly) SNPRINTF(tempStr, 1024, "%s_%s.%s", fileName, sizeStr, ext); else - SNPRINTF(tempStr, 1024, "%s_%s%s_%s.%s", fileName, subName[subsamp], - qualStr, sizeStr, ext); - - if (tjSaveImage(tempStr, dstBuf, scaledw, 0, scaledh, pf, flags) == -1) - THROW_TJG("saving output image"); - ptr = strrchr(tempStr, '.'); - SNPRINTF(ptr, 1024 - (ptr - tempStr), "-err.%s", ext); - if (srcBuf && sf.num == 1 && sf.denom == 1) { - if (!quiet) printf("Compression error written to %s.\n", tempStr); - if (subsamp == TJSAMP_GRAY) { - unsigned long index, index2; - - for (row = 0, index = 0; row < h; row++, index += pitch) { - for (col = 0, index2 = index; col < w; col++, index2 += ps) { - unsigned long rindex = index2 + tjRedOffset[pf]; - unsigned long gindex = index2 + tjGreenOffset[pf]; - unsigned long bindex = index2 + tjBlueOffset[pf]; - int y = (int)((double)srcBuf[rindex] * 0.299 + - (double)srcBuf[gindex] * 0.587 + - (double)srcBuf[bindex] * 0.114 + 0.5); - - if (y > 255) y = 255; - if (y < 0) y = 0; - dstBuf[rindex] = (unsigned char)abs(dstBuf[rindex] - y); - dstBuf[gindex] = (unsigned char)abs(dstBuf[gindex] - y); - dstBuf[bindex] = (unsigned char)abs(dstBuf[bindex] - y); - } - } - } else { - for (row = 0; row < h; row++) - for (col = 0; col < w * ps; col++) - dstBuf[pitch * row + col] = - (unsigned char)abs(dstBuf[pitch * row + col] - - srcBuf[pitch * row + col]); - } - if (tjSaveImage(tempStr, dstBuf, w, 0, h, pf, flags) == -1) + SNPRINTF(tempStr, 1024, "%s_%s%s_%s.%s", fileName, + lossless ? "LOSSLS" : subName[subsamp], qualStr, sizeStr, ext); + + if (precision == 8) { + if (tj3SaveImage8(handle, tempStr, (unsigned char *)dstBuf, scaledw, 0, + scaledh, pf) == -1) + THROW_TJG("saving output image"); + } else if (precision == 12) { + if (tj3SaveImage12(handle, tempStr, (short *)dstBuf, scaledw, 0, scaledh, + pf) == -1) + THROW_TJG("saving output image"); + } else { + if (tj3SaveImage16(handle, tempStr, (unsigned short *)dstBuf, scaledw, 0, + scaledh, pf) == -1) THROW_TJG("saving output image"); } bailout: if (file) fclose(file); - if (handle) tjDestroy(handle); + tj3Destroy(handle); if (dstBufAlloc) free(dstBuf); free(yuvBuf); return retval; } -static int fullTest(unsigned char *srcBuf, int w, int h, int subsamp, +static int fullTest(tjhandle handle, void *srcBuf, int w, int h, int subsamp, int jpegQual, char *fileName) { char tempStr[1024], tempStr2[80]; FILE *file = NULL; - tjhandle handle = NULL; - unsigned char **jpegBuf = NULL, *yuvBuf = NULL, *tmpBuf = NULL, *srcPtr, - *srcPtr2; + unsigned char **jpegBufs = NULL, *yuvBuf = NULL, *srcPtr, *srcPtr2; + void *tmpBuf = NULL; double start, elapsed, elapsedEncode; - int totalJpegSize = 0, row, col, i, tilew = w, tileh = h, retval = 0; + int row, col, i, tilew = w, tileh = h, retval = 0; int iter; - unsigned long *jpegSize = NULL, yuvSize = 0; + size_t totalJpegSize = 0, *jpegSizes = NULL, yuvSize = 0; int ps = tjPixelSize[pf]; int ntilesw = 1, ntilesh = 1, pitch = w * ps; const char *pfStr = pixFormatStr[pf]; - if ((unsigned long long)pitch * (unsigned long long)h > - (unsigned long long)((size_t)-1)) + if ((unsigned long long)pitch * (unsigned long long)h * + (unsigned long long)sampleSize > (unsigned long long)((size_t)-1)) THROW("allocating temporary image buffer", "Image is too large"); - if ((tmpBuf = (unsigned char *)malloc((size_t)pitch * h)) == NULL) + if ((tmpBuf = malloc((size_t)pitch * h * sampleSize)) == NULL) THROW_UNIX("allocating temporary image buffer"); if (!quiet) - printf(">>>>> %s (%s) <--> JPEG %s Q%d <<<<<\n", pfStr, - (flags & TJFLAG_BOTTOMUP) ? "Bottom-up" : "Top-down", - subNameLong[subsamp], jpegQual); + printf(">>>>> %s (%s) <--> %d-bit JPEG (%s %s%d) <<<<<\n", pfStr, + bottomUp ? "Bottom-up" : "Top-down", precision, + lossless ? "Lossless" : subNameLong[subsamp], + lossless ? "PSV" : "Q", jpegQual); for (tilew = doTile ? 8 : w, tileh = doTile ? 8 : h; ; tilew *= 2, tileh *= 2) { @@ -356,37 +399,70 @@ static int fullTest(unsigned char *srcBuf, int w, int h, int subsamp, ntilesw = (w + tilew - 1) / tilew; ntilesh = (h + tileh - 1) / tileh; - if ((jpegBuf = (unsigned char **)malloc(sizeof(unsigned char *) * - ntilesw * ntilesh)) == NULL) + if ((jpegBufs = (unsigned char **)malloc(sizeof(unsigned char *) * + ntilesw * ntilesh)) == NULL) THROW_UNIX("allocating JPEG tile array"); - memset(jpegBuf, 0, sizeof(unsigned char *) * ntilesw * ntilesh); - if ((jpegSize = (unsigned long *)malloc(sizeof(unsigned long) * - ntilesw * ntilesh)) == NULL) + memset(jpegBufs, 0, sizeof(unsigned char *) * ntilesw * ntilesh); + if ((jpegSizes = (size_t *)malloc(sizeof(size_t) * ntilesw * + ntilesh)) == NULL) THROW_UNIX("allocating JPEG size array"); - memset(jpegSize, 0, sizeof(unsigned long) * ntilesw * ntilesh); + memset(jpegSizes, 0, sizeof(size_t) * ntilesw * ntilesh); - if ((flags & TJFLAG_NOREALLOC) != 0) + if (noRealloc) { for (i = 0; i < ntilesw * ntilesh; i++) { - if (tjBufSize(tilew, tileh, subsamp) > (unsigned long)INT_MAX) + size_t jpegBufSize = tj3JPEGBufSize(tilew, tileh, subsamp); + if (jpegBufSize == 0) + THROW_TJ("getting buffer size"); + if (jpegBufSize > (size_t)INT_MAX) THROW("getting buffer size", "Image is too large"); - if ((jpegBuf[i] = (unsigned char *) - tjAlloc(tjBufSize(tilew, tileh, subsamp))) == NULL) + if ((jpegBufs[i] = tj3Alloc(jpegBufSize)) == NULL) THROW_UNIX("allocating JPEG tiles"); } + } /* Compression test */ if (quiet == 1) - printf("%-4s (%s) %-5s %-3d ", pfStr, - (flags & TJFLAG_BOTTOMUP) ? "BU" : "TD", subNameLong[subsamp], - jpegQual); - for (i = 0; i < h; i++) - memcpy(&tmpBuf[pitch * i], &srcBuf[w * ps * i], w * ps); - if ((handle = tjInitCompress()) == NULL) - THROW_TJ("executing tjInitCompress()"); + printf("%-4s(%s) %-2d/%-6s %-3d ", pfStr, bottomUp ? "BU" : "TD", + precision, lossless ? "LOSSLS" : subNameLong[subsamp], jpegQual); + if (precision == 8) { + for (i = 0; i < h; i++) + memcpy(&((unsigned char *)tmpBuf)[pitch * i], + &((unsigned char *)srcBuf)[w * ps * i], w * ps); + } else { + for (i = 0; i < h; i++) + memcpy(&((unsigned short *)tmpBuf)[pitch * i], + &((unsigned short *)srcBuf)[w * ps * i], w * ps * sampleSize); + } + + if (tj3Set(handle, TJPARAM_NOREALLOC, noRealloc) == -1) + THROW_TJ("executing tj3Set()"); + if (tj3Set(handle, TJPARAM_SUBSAMP, subsamp) == -1) + THROW_TJ("executing tj3Set()"); + if (tj3Set(handle, TJPARAM_FASTDCT, fastDCT) == -1) + THROW_TJ("executing tj3Set()"); + if (tj3Set(handle, TJPARAM_OPTIMIZE, optimize) == -1) + THROW_TJ("executing tj3Set()"); + if (tj3Set(handle, TJPARAM_PROGRESSIVE, progressive) == -1) + THROW_TJ("executing tj3Set()"); + if (tj3Set(handle, TJPARAM_ARITHMETIC, arithmetic) == -1) + THROW_TJ("executing tj3Set()"); + if (tj3Set(handle, TJPARAM_LOSSLESS, lossless) == -1) + THROW_TJ("executing tj3Set()"); + if (lossless) { + if (tj3Set(handle, TJPARAM_LOSSLESSPSV, jpegQual) == -1) + THROW_TJ("executing tj3Set()"); + } else { + if (tj3Set(handle, TJPARAM_QUALITY, jpegQual) == -1) + THROW_TJ("executing tj3Set()"); + } + if (tj3Set(handle, TJPARAM_RESTARTBLOCKS, restartIntervalBlocks) == -1) + THROW_TJ("executing tj3Set()"); + if (tj3Set(handle, TJPARAM_RESTARTROWS, restartIntervalRows) == -1) + THROW_TJ("executing tj3Set()"); if (doYUV) { - yuvSize = tjBufSizeYUV2(tilew, yuvAlign, tileh, subsamp); - if (yuvSize == (unsigned long)-1) + yuvSize = tj3YUVBufSize(tilew, yuvAlign, tileh, subsamp); + if (yuvSize == 0) THROW_TJ("allocating YUV buffer"); if ((yuvBuf = (unsigned char *)malloc(yuvSize)) == NULL) THROW_UNIX("allocating YUV buffer"); @@ -402,30 +478,39 @@ static int fullTest(unsigned char *srcBuf, int w, int h, int subsamp, totalJpegSize = 0; start = getTime(); for (row = 0, srcPtr = srcBuf; row < ntilesh; - row++, srcPtr += pitch * tileh) { + row++, srcPtr += pitch * tileh * sampleSize) { for (col = 0, srcPtr2 = srcPtr; col < ntilesw; - col++, tile++, srcPtr2 += ps * tilew) { + col++, tile++, srcPtr2 += ps * tilew * sampleSize) { int width = min(tilew, w - col * tilew); int height = min(tileh, h - row * tileh); if (doYUV) { double startEncode = getTime(); - if (tjEncodeYUV3(handle, srcPtr2, width, pitch, height, pf, yuvBuf, - yuvAlign, subsamp, flags) == -1) - THROW_TJ("executing tjEncodeYUV3()"); + if (tj3EncodeYUV8(handle, srcPtr2, width, pitch, height, pf, + yuvBuf, yuvAlign) == -1) + THROW_TJ("executing tj3EncodeYUV8()"); if (iter >= 0) elapsedEncode += getTime() - startEncode; - if (tjCompressFromYUV(handle, yuvBuf, width, yuvAlign, height, - subsamp, &jpegBuf[tile], &jpegSize[tile], - jpegQual, flags) == -1) - THROW_TJ("executing tjCompressFromYUV()"); + if (tj3CompressFromYUV8(handle, yuvBuf, width, yuvAlign, height, + &jpegBufs[tile], &jpegSizes[tile]) == -1) + THROW_TJ("executing tj3CompressFromYUV8()"); } else { - if (tjCompress2(handle, srcPtr2, width, pitch, height, pf, - &jpegBuf[tile], &jpegSize[tile], subsamp, jpegQual, - flags) == -1) - THROW_TJ("executing tjCompress2()"); + if (precision == 8) { + if (tj3Compress8(handle, srcPtr2, width, pitch, height, pf, + &jpegBufs[tile], &jpegSizes[tile]) == -1) + THROW_TJ("executing tj3Compress8()"); + } else if (precision == 12) { + if (tj3Compress12(handle, (short *)srcPtr2, width, pitch, height, + pf, &jpegBufs[tile], &jpegSizes[tile]) == -1) + THROW_TJ("executing tj3Compress12()"); + } else { + if (tj3Compress16(handle, (unsigned short *)srcPtr2, width, + pitch, height, pf, &jpegBufs[tile], + &jpegSizes[tile]) == -1) + THROW_TJ("executing tj3Compress16()"); + } } - totalJpegSize += jpegSize[tile]; + totalJpegSize += jpegSizes[tile]; } } elapsed += getTime() - start; @@ -439,9 +524,6 @@ static int fullTest(unsigned char *srcBuf, int w, int h, int subsamp, } if (doYUV) elapsed -= elapsedEncode; - if (tjDestroy(handle) == -1) THROW_TJ("executing tjDestroy()"); - handle = NULL; - if (quiet == 1) printf("%-5d %-5d ", tilew, tileh); if (quiet) { if (doYUV) @@ -462,7 +544,8 @@ static int fullTest(unsigned char *srcBuf, int w, int h, int subsamp, if (doYUV) { printf("Encode YUV --> Frame rate: %f fps\n", (double)iter / elapsedEncode); - printf(" Output image size: %lu bytes\n", yuvSize); + printf(" Output image size: %lu bytes\n", + (unsigned long)yuvSize); printf(" Compression ratio: %f:1\n", (double)(w * h * ps) / (double)yuvSize); printf(" Throughput: %f Megapixels/sec\n", @@ -473,8 +556,8 @@ static int fullTest(unsigned char *srcBuf, int w, int h, int subsamp, printf("%s --> Frame rate: %f fps\n", doYUV ? "Comp from YUV" : "Compress ", (double)iter / elapsed); - printf(" Output image size: %d bytes\n", - totalJpegSize); + printf(" Output image size: %lu bytes\n", + (unsigned long)totalJpegSize); printf(" Compression ratio: %f:1\n", (double)(w * h * ps) / (double)totalJpegSize); printf(" Throughput: %f Megapixels/sec\n", @@ -483,11 +566,12 @@ static int fullTest(unsigned char *srcBuf, int w, int h, int subsamp, (double)totalJpegSize * 8. / 1000000. * (double)iter / elapsed); } if (tilew == w && tileh == h && doWrite) { - SNPRINTF(tempStr, 1024, "%s_%s_Q%d.jpg", fileName, subName[subsamp], - jpegQual); + SNPRINTF(tempStr, 1024, "%s_%s_%s%d.jpg", fileName, + lossless ? "LOSSLS" : subName[subsamp], + lossless ? "PSV" : "Q", jpegQual); if ((file = fopen(tempStr, "wb")) == NULL) THROW_UNIX("opening reference image"); - if (fwrite(jpegBuf[0], jpegSize[0], 1, file) != 1) + if (fwrite(jpegBufs[0], jpegSizes[0], 1, file) != 1) THROW_UNIX("writing reference image"); fclose(file); file = NULL; if (!quiet) printf("Reference image written to %s\n", tempStr); @@ -495,17 +579,17 @@ static int fullTest(unsigned char *srcBuf, int w, int h, int subsamp, /* Decompression test */ if (!compOnly) { - if (decomp(srcBuf, jpegBuf, jpegSize, tmpBuf, w, h, subsamp, jpegQual, + if (decomp(jpegBufs, jpegSizes, tmpBuf, w, h, subsamp, jpegQual, fileName, tilew, tileh) == -1) goto bailout; } else if (quiet == 1) printf("N/A\n"); for (i = 0; i < ntilesw * ntilesh; i++) { - tjFree(jpegBuf[i]); - jpegBuf[i] = NULL; + tj3Free(jpegBufs[i]); + jpegBufs[i] = NULL; } - free(jpegBuf); jpegBuf = NULL; - free(jpegSize); jpegSize = NULL; + free(jpegBufs); jpegBufs = NULL; + free(jpegSizes); jpegSizes = NULL; if (doYUV) { free(yuvBuf); yuvBuf = NULL; } @@ -515,15 +599,14 @@ static int fullTest(unsigned char *srcBuf, int w, int h, int subsamp, bailout: if (file) fclose(file); - if (jpegBuf) { + if (jpegBufs) { for (i = 0; i < ntilesw * ntilesh; i++) - tjFree(jpegBuf[i]); + tj3Free(jpegBufs[i]); } - free(jpegBuf); + free(jpegBufs); free(yuvBuf); - free(jpegSize); + free(jpegSizes); free(tmpBuf); - if (handle) tjDestroy(handle); return retval; } @@ -532,8 +615,8 @@ static int decompTest(char *fileName) { FILE *file = NULL; tjhandle handle = NULL; - unsigned char **jpegBuf = NULL, *srcBuf = NULL; - unsigned long *jpegSize = NULL, srcSize, totalJpegSize; + unsigned char **jpegBufs = NULL, *srcBuf = NULL; + size_t *jpegSizes = NULL, srcSize, totalJpegSize; tjtransform *t = NULL; double start, elapsed; int ps = tjPixelSize[pf], tile, row, col, i, iter, retval = 0, decompsrc = 0; @@ -542,12 +625,12 @@ static int decompTest(char *fileName) int w = 0, h = 0, tilew, tileh, ntilesw = 1, ntilesh = 1, subsamp = -1, cs = -1; /* Transformed image */ - int tw, th, ttilew, ttileh, tntilesw, tntilesh, tsubsamp, jpegFlags; + int tw, th, ttilew, ttileh, tntilesw, tntilesh, tsubsamp; if ((file = fopen(fileName, "rb")) == NULL) THROW_UNIX("opening file"); if (fseek(file, 0, SEEK_END) < 0 || - (srcSize = ftell(file)) == (unsigned long)-1) + (srcSize = ftell(file)) == (size_t)-1) THROW_UNIX("determining file size"); if ((srcBuf = (unsigned char *)malloc(srcSize)) == NULL) THROW_UNIX("allocating memory"); @@ -560,32 +643,64 @@ static int decompTest(char *fileName) temp = strrchr(fileName, '.'); if (temp != NULL) *temp = '\0'; - if ((handle = tjInitTransform()) == NULL) - THROW_TJ("executing tjInitTransform()"); - if (tjDecompressHeader4(handle, srcBuf, srcSize, &w, &h, &subsamp, &cs, - &jpegFlags) == -1) - THROW_TJ("executing tjDecompressHeader4()"); + if ((handle = tj3Init(TJINIT_TRANSFORM)) == NULL) + THROW_TJ("executing tj3Init()"); + if (tj3Set(handle, TJPARAM_STOPONWARNING, stopOnWarning) == -1) + THROW_TJ("executing tj3Set()"); + if (tj3Set(handle, TJPARAM_BOTTOMUP, bottomUp) == -1) + THROW_TJ("executing tj3Set()"); + if (tj3Set(handle, TJPARAM_NOREALLOC, noRealloc) == -1) + THROW_TJ("executing tj3Set()"); + if (tj3Set(handle, TJPARAM_FASTUPSAMPLE, fastUpsample) == -1) + THROW_TJ("executing tj3Set()"); + if (tj3Set(handle, TJPARAM_FASTDCT, fastDCT) == -1) + THROW_TJ("executing tj3Set()"); + if (tj3Set(handle, TJPARAM_SCANLIMIT, limitScans ? 500 : 0) == -1) + THROW_TJ("executing tj3Set()"); + + if (tj3DecompressHeader(handle, srcBuf, srcSize) == -1) + THROW_TJ("executing tj3DecompressHeader()"); + w = tj3Get(handle, TJPARAM_JPEGWIDTH); + h = tj3Get(handle, TJPARAM_JPEGHEIGHT); + subsamp = tj3Get(handle, TJPARAM_SUBSAMP); + precision = tj3Get(handle, TJPARAM_PRECISION); + if (tj3Get(handle, TJPARAM_PROGRESSIVE) == 1) + printf("JPEG image uses progressive entropy coding\n\n"); + if (tj3Get(handle, TJPARAM_ARITHMETIC) == 1) + printf("JPEG image uses arithmetic entropy coding\n\n"); + if (tj3Set(handle, TJPARAM_PROGRESSIVE, progressive) == -1) + THROW_TJ("executing tj3Set()"); + if (tj3Set(handle, TJPARAM_ARITHMETIC, arithmetic) == -1) + THROW_TJ("executing tj3Set()"); + + lossless = tj3Get(handle, TJPARAM_LOSSLESS); + sampleSize = (precision == 8 ? sizeof(unsigned char) : sizeof(short)); + cs = tj3Get(handle, TJPARAM_COLORSPACE); if (w < 1 || h < 1) THROW("reading JPEG header", "Invalid image dimensions"); if (cs == TJCS_YCCK || cs == TJCS_CMYK) { pf = TJPF_CMYK; ps = tjPixelSize[pf]; } - if (jpegFlags & TJFLAG_LOSSLESS) - sf.num = sf.denom = 1; + if (lossless) sf = TJUNSCALED; + + if (tj3SetScalingFactor(handle, sf) == -1) + THROW_TJ("executing tj3SetScalingFactor()"); + if (tj3SetCroppingRegion(handle, cr) == -1) + THROW_TJ("executing tj3SetCroppingRegion()"); if (quiet == 1) { printf("All performance values in Mpixels/sec\n\n"); - printf("Pixel JPEG JPEG %s %s Xform Comp Decomp ", + printf("Pixel JPEG %s %s Xform Comp Decomp ", doTile ? "Tile " : "Image", doTile ? "Tile " : "Image"); if (doYUV) printf("Decode"); printf("\n"); - printf("Format CS Subsamp Width Height Perf Ratio Perf "); + printf("Format Format Width Height Perf Ratio Perf "); if (doYUV) printf("Perf"); printf("\n\n"); } else if (!quiet) - printf(">>>>> JPEG %s --> %s (%s) <<<<<\n", + printf(">>>>> %d-bit JPEG (%s) --> %s (%s) <<<<<\n", precision, formatName(subsamp, cs, tempStr), pixFormatStr[pf], - (flags & TJFLAG_BOTTOMUP) ? "Bottom-up" : "Top-down"); + bottomUp ? "Bottom-up" : "Top-down"); for (tilew = doTile ? 16 : w, tileh = doTile ? 16 : h; ; tilew *= 2, tileh *= 2) { @@ -594,36 +709,38 @@ static int decompTest(char *fileName) ntilesw = (w + tilew - 1) / tilew; ntilesh = (h + tileh - 1) / tileh; - if ((jpegBuf = (unsigned char **)malloc(sizeof(unsigned char *) * - ntilesw * ntilesh)) == NULL) + if ((jpegBufs = (unsigned char **)malloc(sizeof(unsigned char *) * + ntilesw * ntilesh)) == NULL) THROW_UNIX("allocating JPEG tile array"); - memset(jpegBuf, 0, sizeof(unsigned char *) * ntilesw * ntilesh); - if ((jpegSize = (unsigned long *)malloc(sizeof(unsigned long) * - ntilesw * ntilesh)) == NULL) + memset(jpegBufs, 0, sizeof(unsigned char *) * ntilesw * ntilesh); + if ((jpegSizes = (size_t *)malloc(sizeof(size_t) * ntilesw * + ntilesh)) == NULL) THROW_UNIX("allocating JPEG size array"); - memset(jpegSize, 0, sizeof(unsigned long) * ntilesw * ntilesh); + memset(jpegSizes, 0, sizeof(size_t) * ntilesw * ntilesh); - if ((flags & TJFLAG_NOREALLOC) != 0 && - (doTile || xformOp != TJXOP_NONE || xformOpt != 0 || customFilter)) + if (noRealloc && + (doTile || xformOp != TJXOP_NONE || xformOpt != 0 || customFilter)) { for (i = 0; i < ntilesw * ntilesh; i++) { - if (tjBufSize(tilew, tileh, subsamp) > (unsigned long)INT_MAX) + size_t jpegBufSize = tj3JPEGBufSize(tilew, tileh, subsamp); + if (jpegBufSize == 0) + THROW_TJ("getting buffer size"); + if (jpegBufSize > (size_t)INT_MAX) THROW("getting buffer size", "Image is too large"); - if ((jpegBuf[i] = (unsigned char *) - tjAlloc(tjBufSize(tilew, tileh, subsamp))) == NULL) + if ((jpegBufs[i] = tj3Alloc(jpegBufSize)) == NULL) THROW_UNIX("allocating JPEG tiles"); } + } tw = w; th = h; ttilew = tilew; ttileh = tileh; if (!quiet) { printf("\n%s size: %d x %d", doTile ? "Tile" : "Image", ttilew, ttileh); - if (sf.num != 1 || sf.denom != 1) - printf(" --> %d x %d", TJSCALED(tw, sf), TJSCALED(th, sf)); + if (sf.num != 1 || sf.denom != 1 || IS_CROPPED(cr)) + printf(" --> %d x %d", CROPPED_WIDTH(tw), CROPPED_HEIGHT(th)); printf("\n"); } else if (quiet == 1) { - printf("%-4s (%s) %-5s %-5s ", pixFormatStr[pf], - (flags & TJFLAG_BOTTOMUP) ? "BU" : "TD", csName[cs], - subNameLong[subsamp]); - printf("%-5d %-5d ", tilew, tileh); + printf("%-4s(%s) %-14s ", pixFormatStr[pf], + bottomUp ? "BU" : "TD", formatName(subsamp, cs, tempStr)); + printf("%-5d %-5d ", CROPPED_WIDTH(tilew), CROPPED_HEIGHT(tileh)); } tsubsamp = subsamp; @@ -637,6 +754,10 @@ static int decompTest(char *fileName) tw = h; th = w; ttilew = tileh; ttileh = tilew; } + if (xformOp != TJXOP_NONE && xformOp != TJXOP_TRANSPOSE && + subsamp == TJSAMP_UNKNOWN) + THROW("transforming", + "Could not determine subsampling level of JPEG image"); if (xformOpt & TJXOPT_GRAY) tsubsamp = TJSAMP_GRAY; if (xformOp == TJXOP_HFLIP || xformOp == TJXOP_ROT180) tw = tw - (tw % tjMCUWidth[tsubsamp]); @@ -664,8 +785,8 @@ static int decompTest(char *fileName) t[tile].op = xformOp; t[tile].options = xformOpt | TJXOPT_TRIM; t[tile].customFilter = customFilter; - if (t[tile].options & TJXOPT_NOOUTPUT && jpegBuf[tile]) { - tjFree(jpegBuf[tile]); jpegBuf[tile] = NULL; + if (t[tile].options & TJXOPT_NOOUTPUT && jpegBufs[tile]) { + tj3Free(jpegBufs[tile]); jpegBufs[tile] = NULL; } } } @@ -674,9 +795,9 @@ static int decompTest(char *fileName) elapsed = 0.; while (1) { start = getTime(); - if (tjTransform(handle, srcBuf, srcSize, tntilesw * tntilesh, jpegBuf, - jpegSize, t, flags) == -1) - THROW_TJ("executing tjTransform()"); + if (tj3Transform(handle, srcBuf, srcSize, tntilesw * tntilesh, + jpegBufs, jpegSizes, t) == -1) + THROW_TJ("executing tj3Transform()"); elapsed += getTime() - start; if (iter >= 0) { iter++; @@ -690,7 +811,7 @@ static int decompTest(char *fileName) free(t); t = NULL; for (tile = 0, totalJpegSize = 0; tile < tntilesw * tntilesh; tile++) - totalJpegSize += jpegSize[tile]; + totalJpegSize += jpegSizes[tile]; if (quiet) { printf("%-6s%s%-6s%s", @@ -703,7 +824,7 @@ static int decompTest(char *fileName) printf("Transform --> Frame rate: %f fps\n", 1.0 / elapsed); printf(" Output image size: %lu bytes\n", - totalJpegSize); + (unsigned long)totalJpegSize); printf(" Compression ratio: %f:1\n", (double)(w * h * ps) / (double)totalJpegSize); printf(" Throughput: %f Megapixels/sec\n", @@ -713,41 +834,41 @@ static int decompTest(char *fileName) } } else { if (quiet == 1) printf("N/A N/A "); - tjFree(jpegBuf[0]); - jpegBuf[0] = NULL; + tj3Free(jpegBufs[0]); + jpegBufs[0] = NULL; decompsrc = 1; } if (w == tilew) ttilew = tw; if (h == tileh) ttileh = th; if (!(xformOpt & TJXOPT_NOOUTPUT)) { - if (decomp(NULL, decompsrc ? &srcBuf : jpegBuf, - decompsrc ? &srcSize : jpegSize, NULL, tw, th, tsubsamp, 0, + if (decomp(decompsrc ? &srcBuf : jpegBufs, + decompsrc ? &srcSize : jpegSizes, NULL, tw, th, tsubsamp, 0, fileName, ttilew, ttileh) == -1) goto bailout; } else if (quiet == 1) printf("N/A\n"); for (i = 0; i < ntilesw * ntilesh; i++) { - tjFree(jpegBuf[i]); - jpegBuf[i] = NULL; + tj3Free(jpegBufs[i]); + jpegBufs[i] = NULL; } - free(jpegBuf); jpegBuf = NULL; - free(jpegSize); jpegSize = NULL; + free(jpegBufs); jpegBufs = NULL; + free(jpegSizes); jpegSizes = NULL; if (tilew == w && tileh == h) break; } bailout: if (file) fclose(file); - if (jpegBuf) { + if (jpegBufs) { for (i = 0; i < ntilesw * ntilesh; i++) - tjFree(jpegBuf[i]); + tj3Free(jpegBufs[i]); } - free(jpegBuf); - free(jpegSize); + free(jpegBufs); + free(jpegSizes); free(srcBuf); free(t); - if (handle) { tjDestroy(handle); handle = NULL; } + tj3Destroy(handle); return retval; } @@ -757,40 +878,62 @@ static void usage(char *progName) int i; printf("USAGE: %s\n", progName); - printf(" [options]\n\n"); + printf(" [options]\n\n"); printf(" %s\n", progName); - printf(" [options]\n\n"); - printf("Options:\n\n"); + printf(" [options]\n"); + + printf("\nGENERAL OPTIONS\n"); + printf("---------------\n"); printf("-alloc = Dynamically allocate JPEG buffers\n"); + printf("-benchtime T = Run each benchmark for at least T seconds [default = 5.0]\n"); printf("-bmp = Use Windows Bitmap format for output images [default = PPM]\n"); + printf(" ** 8-bit data precision only **\n"); printf("-bottomup = Use bottom-up row order for packed-pixel source/destination buffers\n"); - printf("-tile = Compress/transform the input image into separate JPEG tiles of varying\n"); - printf(" sizes (useful for measuring JPEG overhead)\n"); + printf("-componly = Stop after running compression tests. Do not test decompression.\n"); + printf("-lossless = Generate lossless JPEG images when compressing (implies\n"); + printf(" -subsamp 444). PSV is the predictor selection value (1-7).\n"); + printf("-nowrite = Do not write reference or output images (improves consistency of\n"); + printf(" benchmark results)\n"); printf("-rgb, -bgr, -rgbx, -bgrx, -xbgr, -xrgb =\n"); printf(" Use the specified pixel format for packed-pixel source/destination buffers\n"); printf(" [default = BGR]\n"); printf("-cmyk = Indirectly test YCCK JPEG compression/decompression\n"); printf(" (use the CMYK pixel format for packed-pixel source/destination buffers)\n"); - printf("-fastupsample = Use the fastest chrominance upsampling algorithm available\n"); - printf("-fastdct = Use the fastest DCT/IDCT algorithm available\n"); - printf("-accuratedct = Use the most accurate DCT/IDCT algorithm available\n"); - printf("-progressive = Use progressive entropy coding in JPEG images generated by\n"); - printf(" compression and transform operations (can be combined with -arithmetic)\n"); + printf("-precision N = Use N-bit data precision when compressing [N is 8, 12, or 16;\n"); + printf(" default = 8; if N is 16, then -lossless must also be specified]\n"); + printf("-quiet = Output results in tabular rather than verbose format\n"); + printf("-restart N = When compressing, add a restart marker every N MCU rows (lossy) or\n"); + printf(" N sample rows (lossless) [default = 0 (no restart markers)]. Append 'B'\n"); + printf(" to specify the restart marker interval in MCU blocks (lossy) or samples\n"); + printf(" (lossless).\n"); + printf("-stoponwarning = Immediately discontinue the current\n"); + printf(" compression/decompression/transform operation if a warning (non-fatal\n"); + printf(" error) occurs\n"); + printf("-tile = Compress/transform the input image into separate JPEG tiles of varying\n"); + printf(" sizes (useful for measuring JPEG overhead)\n"); + printf("-warmup T = Run each benchmark for T seconds [default = 1.0] prior to starting\n"); + printf(" the timer, in order to prime the caches and thus improve the consistency\n"); + printf(" of the benchmark results\n"); + + printf("\nLOSSY JPEG OPTIONS\n"); + printf("------------------\n"); printf("-arithmetic = Use arithmetic entropy coding in JPEG images generated by\n"); printf(" compression and transform operations (can be combined with -progressive)\n"); - printf("-lossless = Generate lossless JPEG images when compressing (implies\n"); - printf(" -subsamp 444). When generating lossless JPEG images, Quality is\n"); - printf(" psv * 10 + Pt, where psv is the predictor selection value (1-7) and Pt is\n"); - printf(" the point transform (0-7). A point transform value of 0 is necessary in\n"); - printf(" order to create a fully lossless JPEG image.\n"); - printf("-subsamp = When compressing, use the specified level of chrominance\n"); - printf(" subsampling ( = 444, 422, 440, 420, 411, or GRAY) [default = test\n"); - printf(" Grayscale, 4:2:0, 4:2:2, and 4:4:4 in sequence]\n"); - printf("-quiet = Output results in tabular rather than verbose format\n"); - printf("-yuv = Compress from/decompress to intermediate planar YUV images\n"); - printf("-yuvpad

        = The number of bytes by which each row in each plane of an\n"); - printf(" intermediate YUV image is evenly divisible (must be a power of 2)\n"); - printf(" [default = 1]\n"); + printf(" ** 8-bit data precision only **\n"); + printf("-crop WxH+X+Y = Decompress only the specified region of the JPEG image, where W\n"); + printf(" and H are the width and height of the region (0 = maximum possible width\n"); + printf(" or height) and X and Y are the left and upper boundary of the region, all\n"); + printf(" specified relative to the scaled image dimensions. X must be divible by\n"); + printf(" the scaled MCU width.\n"); + printf("-fastdct = Use the fastest DCT/IDCT algorithm available\n"); + printf("-fastupsample = Use the fastest chrominance upsampling algorithm available\n"); + printf("-optimize = Use optimized baseline entropy coding in JPEG images generated by\n"); + printf(" compession and transform operations\n"); + printf("-progressive = Use progressive entropy coding in JPEG images generated by\n"); + printf(" compression and transform operations (implies -optimize; can be combined\n"); + printf(" with -arithmetic)\n"); + printf("-limitscans = Refuse to decompress or transform progressive JPEG images that\n"); + printf(" have an unreasonably large number of scans\n"); printf("-scale M/N = When decompressing, scale the width/height of the JPEG image by a\n"); printf(" factor of M/N (M/N = "); for (i = 0; i < nsf; i++) { @@ -803,6 +946,9 @@ static void usage(char *progName) if (i % 8 == 0 && i != 0) printf("\n "); } printf(")\n"); + printf("-subsamp S = When compressing, use the specified level of chrominance\n"); + printf(" subsampling (S = 444, 422, 440, 420, 411, or GRAY) [default = test\n"); + printf(" Grayscale, 4:2:0, 4:2:2, and 4:4:4 in sequence]\n"); printf("-hflip, -vflip, -transpose, -transverse, -rot90, -rot180, -rot270 =\n"); printf(" Perform the specified lossless transform operation on the input image\n"); printf(" prior to decompression (these operations are mutually exclusive)\n"); @@ -810,33 +956,28 @@ static void usage(char *progName) printf(" decompression (can be combined with the other transform operations above)\n"); printf("-copynone = Do not copy any extra markers (including EXIF and ICC profile data)\n"); printf(" when transforming the input image\n"); - printf("-benchtime = Run each benchmark for at least seconds [default = 5.0]\n"); - printf("-warmup = Run each benchmark for seconds [default = 1.0] prior to\n"); - printf(" starting the timer, in order to prime the caches and thus improve the\n"); - printf(" consistency of the benchmark results\n"); - printf("-componly = Stop after running compression tests. Do not test decompression.\n"); - printf("-nowrite = Do not write reference or output images (improves consistency of\n"); - printf(" benchmark results)\n"); - printf("-limitscans = Refuse to decompress or transform progressive JPEG images that\n"); - printf(" have an unreasonably large number of scans\n"); - printf("-stoponwarning = Immediately discontinue the current\n"); - printf(" compression/decompression/transform operation if a warning (non-fatal\n"); - printf(" error) occurs\n\n"); - printf("NOTE: If the quality is specified as a range (e.g. 90-100), a separate\n"); - printf("test will be performed for all quality values in the range.\n\n"); + printf("-yuv = Compress from/decompress to intermediate planar YUV images\n"); + printf(" ** 8-bit data precision only **\n"); + printf("-yuvpad N = The number of bytes by which each row in each plane of an\n"); + printf(" intermediate YUV image is evenly divisible (N must be a power of 2)\n"); + printf(" [default = 1]\n"); + + printf("\nNOTE: If the quality/PSV is specified as a range (e.g. 90-100 or 1-4), a\n"); + printf("separate test will be performed for all values in the range.\n\n"); exit(1); } int main(int argc, char *argv[]) { - unsigned char *srcBuf = NULL; + void *srcBuf = NULL; int w = 0, h = 0, i, j, minQual = -1, maxQual = -1; char *temp; int minArg = 2, retval = 0, subsamp = -1; + tjhandle handle = NULL; - if ((scalingFactors = tjGetScalingFactors(&nsf)) == NULL || nsf == 0) - THROW("executing tjGetScalingFactors()", tjGetErrorStr()); + if ((scalingFactors = tj3GetScalingFactors(&nsf)) == NULL || nsf == 0) + THROW("executing tj3GetScalingFactors()", tj3GetErrorStr(NULL)); if (argc < minArg) usage(argv[0]); @@ -852,13 +993,9 @@ int main(int argc, char *argv[]) if (!decompOnly) { minArg = 3; if (argc < minArg) usage(argv[0]); - if ((minQual = atoi(argv[2])) < 1 || minQual > 100) { - puts("ERROR: Quality must be between 1 and 100."); - exit(1); - } + minQual = atoi(argv[2]); if ((temp = strchr(argv[2], '-')) != NULL && strlen(temp) > 1 && - sscanf(&temp[1], "%d", &maxQual) == 1 && maxQual > minQual && - maxQual >= 1 && maxQual <= 100) {} + sscanf(&temp[1], "%d", &maxQual) == 1 && maxQual > minQual) {} else maxQual = minQual; } @@ -866,26 +1003,32 @@ int main(int argc, char *argv[]) for (i = minArg; i < argc; i++) { if (!strcasecmp(argv[i], "-tile")) { doTile = 1; xformOpt |= TJXOPT_CROP; + } else if (!strcasecmp(argv[i], "-precision") && i < argc - 1) { + int tempi = atoi(argv[++i]); + + if (tempi != 8 && tempi != 12 && tempi != 16) + usage(argv[0]); + precision = tempi; } else if (!strcasecmp(argv[i], "-fastupsample")) { printf("Using fastest upsampling algorithm\n\n"); - flags |= TJFLAG_FASTUPSAMPLE; + fastUpsample = 1; } else if (!strcasecmp(argv[i], "-fastdct")) { printf("Using fastest DCT/IDCT algorithm\n\n"); - flags |= TJFLAG_FASTDCT; - } else if (!strcasecmp(argv[i], "-accuratedct")) { - printf("Using most accurate DCT/IDCT algorithm\n\n"); - flags |= TJFLAG_ACCURATEDCT; + fastDCT = 1; + } else if (!strcasecmp(argv[i], "-optimize")) { + printf("Using optimized baseline entropy coding\n\n"); + optimize = 1; + xformOpt |= TJXOPT_OPTIMIZE; } else if (!strcasecmp(argv[i], "-progressive")) { printf("Using progressive entropy coding\n\n"); - flags |= TJFLAG_PROGRESSIVE; + progressive = 1; xformOpt |= TJXOPT_PROGRESSIVE; } else if (!strcasecmp(argv[i], "-arithmetic")) { printf("Using arithmetic entropy coding\n\n"); - flags |= TJFLAG_ARITHMETIC; + arithmetic = 1; xformOpt |= TJXOPT_ARITHMETIC; } else if (!strcasecmp(argv[i], "-lossless")) { - printf("Using lossless JPEG\n\n"); - flags |= TJFLAG_LOSSLESS; + lossless = 1; subsamp = TJSAMP_444; } else if (!strcasecmp(argv[i], "-rgb")) pf = TJPF_RGB; @@ -902,7 +1045,7 @@ int main(int argc, char *argv[]) else if (!strcasecmp(argv[i], "-cmyk")) pf = TJPF_CMYK; else if (!strcasecmp(argv[i], "-bottomup")) - flags |= TJFLAG_BOTTOMUP; + bottomUp = 1; else if (!strcasecmp(argv[i], "-quiet")) quiet = 1; else if (!strcasecmp(argv[i], "-qq")) @@ -912,15 +1055,22 @@ int main(int argc, char *argv[]) if (sscanf(argv[++i], "%d/%d", &temp1, &temp2) == 2) { for (j = 0; j < nsf; j++) { - if ((double)temp1 / (double)temp2 == - (double)scalingFactors[j].num / - (double)scalingFactors[j].denom) { + if (temp1 == scalingFactors[j].num && + temp2 == scalingFactors[j].denom) { sf = scalingFactors[j]; match = 1; break; } } if (!match) usage(argv[0]); } else usage(argv[0]); + } else if (!strcasecmp(argv[i], "-crop") && i < argc - 1) { + int temp1 = -1, temp2 = -1, temp3 = -1, temp4 = -1; + + if (sscanf(argv[++i], "%dx%d+%d+%d", &temp1, &temp2, &temp3, + &temp4) == 4 && temp1 >= 0 && temp2 >= 0 && temp3 >= 0 && + temp4 >= 0) { + cr.w = temp1; cr.h = temp2; cr.x = temp3; cr.y = temp4; + } else usage(argv[0]); } else if (!strcasecmp(argv[i], "-hflip")) xformOp = TJXOP_HFLIP; else if (!strcasecmp(argv[i], "-vflip")) @@ -955,7 +1105,7 @@ int main(int argc, char *argv[]) else usage(argv[0]); printf("Warmup time = %.1f seconds\n\n", warmup); } else if (!strcasecmp(argv[i], "-alloc")) - flags &= (~TJFLAG_NOREALLOC); + noRealloc = 0; else if (!strcasecmp(argv[i], "-bmp")) ext = "bmp"; else if (!strcasecmp(argv[i], "-yuv")) { @@ -986,41 +1136,103 @@ int main(int argc, char *argv[]) else if (!strcasecmp(argv[i], "-nowrite")) doWrite = 0; else if (!strcasecmp(argv[i], "-limitscans")) - flags |= TJFLAG_LIMITSCANS; - else if (!strcasecmp(argv[i], "-stoponwarning")) - flags |= TJFLAG_STOPONWARNING; + limitScans = 1; + else if (!strcasecmp(argv[i], "-restart") && i < argc - 1) { + int tempi = -1, nscan; char tempc = 0; + + if ((nscan = sscanf(argv[++i], "%d%c", &tempi, &tempc)) < 1 || + tempi < 0 || tempi > 65535 || + (nscan == 2 && tempc != 'B' && tempc != 'b')) + usage(argv[0]); + + if (tempc == 'B' || tempc == 'b') + restartIntervalBlocks = tempi; + else + restartIntervalRows = tempi; + } else if (!strcasecmp(argv[i], "-stoponwarning")) + stopOnWarning = 1; else usage(argv[0]); } } + if (precision == 16 && !lossless) { + printf("ERROR: -lossless must be specified along with -precision 16\n"); + retval = -1; goto bailout; + } + if (precision != 8 && arithmetic) { + printf("ERROR: -arithmetic requires 8-bit data precision\n"); + retval = -1; goto bailout; + } + if (precision != 8 && doYUV) { + printf("ERROR: -yuv requires 8-bit data precision\n"); + retval = -1; goto bailout; + } + if (lossless && doYUV) { + printf("ERROR: -lossless and -yuv are incompatible\n"); + retval = -1; goto bailout; + } + sampleSize = (precision == 8 ? sizeof(unsigned char) : sizeof(short)); + if ((sf.num != 1 || sf.denom != 1) && doTile) { printf("Disabling tiled compression/decompression tests, because those tests do not\n"); printf("work when scaled decompression is enabled.\n\n"); doTile = 0; xformOpt &= (~TJXOPT_CROP); } - if ((flags & TJFLAG_NOREALLOC) == 0 && doTile) { + if (IS_CROPPED(cr)) { + if (!decompOnly) { + printf("ERROR: Partial image decompression can only be enabled for JPEG input images\n"); + retval = -1; goto bailout; + } + if (doTile) { + printf("Disabling tiled compression/decompression tests, because those tests do not\n"); + printf("work when partial image decompression is enabled.\n\n"); + doTile = 0; xformOpt &= (~TJXOPT_CROP); + } + if (doYUV) { + printf("ERROR: -crop and -yuv are incompatible\n"); + retval = -1; goto bailout; + } + } + + if (!noRealloc && doTile) { printf("Disabling tiled compression/decompression tests, because those tests do not\n"); printf("work when dynamic JPEG buffer allocation is enabled.\n\n"); doTile = 0; xformOpt &= (~TJXOPT_CROP); } if (!decompOnly) { - if ((srcBuf = tjLoadImage(argv[1], &w, 1, &h, &pf, flags)) == NULL) - THROW_TJG("loading input image"); + if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL) + THROW_TJ("executing tj3Init()"); + if (tj3Set(handle, TJPARAM_STOPONWARNING, stopOnWarning) == -1) + THROW_TJ("executing tj3Set()"); + if (tj3Set(handle, TJPARAM_BOTTOMUP, bottomUp) == -1) + THROW_TJ("executing tj3Set()"); + + if (precision == 8) { + if ((srcBuf = tj3LoadImage8(handle, argv[1], &w, 1, &h, &pf)) == NULL) + THROW_TJG("loading input image"); + } else if (precision == 12) { + if ((srcBuf = tj3LoadImage12(handle, argv[1], &w, 1, &h, &pf)) == NULL) + THROW_TJG("loading input image"); + } else { + if ((srcBuf = tj3LoadImage16(handle, argv[1], &w, 1, &h, &pf)) == NULL) + THROW_TJG("loading input image"); + } temp = strrchr(argv[1], '.'); if (temp != NULL) *temp = '\0'; } if (quiet == 1 && !decompOnly) { printf("All performance values in Mpixels/sec\n\n"); - printf("Pixel JPEG JPEG %s %s ", + printf("Pixel JPEG JPEG %s %s ", doTile ? "Tile " : "Image", doTile ? "Tile " : "Image"); if (doYUV) printf("Encode "); printf("Comp Comp Decomp "); if (doYUV) printf("Decode"); printf("\n"); - printf("Format Subsamp Qual Width Height "); + printf("Format Format %s Width Height ", + lossless ? "PSV " : "Qual"); if (doYUV) printf("Perf "); printf("Perf Ratio Perf "); if (doYUV) printf("Perf"); @@ -1032,28 +1244,40 @@ int main(int argc, char *argv[]) printf("\n"); goto bailout; } + if (lossless) { + if (minQual < 1 || minQual > 7 || maxQual < 1 || maxQual > 7) { + puts("ERROR: PSV must be between 1 and 7."); + exit(1); + } + } else { + if (minQual < 1 || minQual > 100 || maxQual < 1 || maxQual > 100) { + puts("ERROR: Quality must be between 1 and 100."); + exit(1); + } + } if (subsamp >= 0 && subsamp < TJ_NUMSAMP) { for (i = maxQual; i >= minQual; i--) - fullTest(srcBuf, w, h, subsamp, i, argv[1]); + fullTest(handle, srcBuf, w, h, subsamp, i, argv[1]); printf("\n"); } else { if (pf != TJPF_CMYK) { for (i = maxQual; i >= minQual; i--) - fullTest(srcBuf, w, h, TJSAMP_GRAY, i, argv[1]); + fullTest(handle, srcBuf, w, h, TJSAMP_GRAY, i, argv[1]); printf("\n"); } for (i = maxQual; i >= minQual; i--) - fullTest(srcBuf, w, h, TJSAMP_420, i, argv[1]); + fullTest(handle, srcBuf, w, h, TJSAMP_420, i, argv[1]); printf("\n"); for (i = maxQual; i >= minQual; i--) - fullTest(srcBuf, w, h, TJSAMP_422, i, argv[1]); + fullTest(handle, srcBuf, w, h, TJSAMP_422, i, argv[1]); printf("\n"); for (i = maxQual; i >= minQual; i--) - fullTest(srcBuf, w, h, TJSAMP_444, i, argv[1]); + fullTest(handle, srcBuf, w, h, TJSAMP_444, i, argv[1]); printf("\n"); } bailout: - tjFree(srcBuf); + tj3Destroy(handle); + tj3Free(srcBuf); return retval; } diff --git a/tjbenchtest.in b/tjbenchtest.in index 9b847cd00..7862906c2 100755 --- a/tjbenchtest.in +++ b/tjbenchtest.in @@ -16,27 +16,31 @@ onexit() runme() { echo \*\*\* $* - $* + "$@" } EXT=bmp -IMAGES="vgl_5674_0098.${EXT} vgl_6434_0018a.${EXT} vgl_6548_0026a.${EXT} nightshot_iso_100.${EXT}" +IMAGES="vgl_5674_0098.${EXT} vgl_6434_0018a.${EXT} vgl_6548_0026a.${EXT} big_tree8.${EXT}" IMGDIR=@CMAKE_CURRENT_SOURCE_DIR@/testimages OUTDIR=`mktemp -d /tmp/__tjbenchtest_output.XXXXXX` EXEDIR=@CMAKE_CURRENT_BINARY_DIR@ +JAVA="@Java_JAVA_EXECUTABLE@" +JAVAARGS="-cp $EXEDIR/java/turbojpeg.jar -Djava.library.path=$EXEDIR" +TJBENCH=$EXEDIR/tjbench BMPARG= NSARG= YUVARG= ALLOC=0 ALLOCARG= -PROGARG= -ARIARG= +ENTROPYARG= +JAVAARG= LOSSLSARG= LOSSLSPSV= TJQUAL=95 h1SUBSAMP="GRAY 444" h2SUBSAMP="420 422" ALLSUBSAMP="GRAY 420 422 444" +PRECISION=8 if [ "$EXT" = "bmp" ]; then BMPARG=-bmp; fi if [ -d $OUTDIR ]; then @@ -50,118 +54,176 @@ while [ $# -gt 0 ]; do NSARG=-nosmooth YUVARG=-yuv -# NOTE: The combination of tjEncodeYUV*() and tjCompressFromYUV*() does not -# always produce bitwise-identical results to tjCompress*() if subsampling is +# NOTE: The combination of tj3EncodeYUV*() and tj3CompressFromYUV*() does not +# always produce bitwise-identical results to tj3Compress*() if subsampling is # enabled. In both cases, if the image width or height are not evenly # divisible by the MCU width/height, then the bottom and/or right edge are # expanded. However, the libjpeg code performs this expansion prior to -# downsampling, and TurboJPEG performs it in tjCompressFromYUV*(), which is +# downsampling, and TurboJPEG performs it in tj3CompressFromYUV*(), which is # after downsampling. Thus, the two will agree only if the width/height along # each downsampled dimension is an odd number or is evenly divisible by the MCU # width/height. This disagreement basically amounts to a round-off error, but # there is no easy way around it, so for now, we just test the only image that -# works. (NOTE: nightshot_iso_100 does not suffer from the above issue, but -# it suffers from an unrelated problem whereby the combination of -# tjDecompressToYUV*() and tjDecodeYUV*() do not produce bitwise-identical -# results to tjDecompress*() if decompression scaling is enabled. This latter -# phenomenon is not yet fully understood but is also believed to be some sort -# of round-off error.) +# works. (NOTE: big_tree8 does not suffer from the above issue, but it suffers +# from an unrelated problem whereby the combination of tj3DecompressToYUV*() +# and tj3DecodeYUV*() do not produce bitwise-identical results to +# tj3Decompress*() if decompression scaling is enabled. This latter phenomenon +# is not yet fully understood but is also believed to be some sort of round-off +# error.) IMAGES="vgl_6548_0026a.${EXT}" ;; -alloc) ALLOCARG=-alloc ALLOC=1 ;; + -java) + JAVAARG=-java + TJBENCH="$JAVA $JAVAARGS TJBench" + ;; + -optimize) + ENTROPYARG=-optimize + ;; -progressive) - PROGARG=-progressive + ENTROPYARG=-progressive ;; -arithmetic) - ARIARG=-arithmetic + ENTROPYARG=-arithmetic ;; -lossless) LOSSLSARG="-lossless" LOSSLSPSV=4 - TJQUAL=40 + TJQUAL=4 h1SUBSAMP=444 h2SUBSAMP=444 ALLSUBSAMP=444 ;; + -precision) + shift + PRECISION=$1 + if [ $PRECISION != 8 ]; then + EXT=ppm + IMAGES="big_building16.${EXT}" + BMPARG= + fi + ;; esac shift done -exec >$EXEDIR/tjbenchtest$YUVARG$ALLOCARG$PROGARG$ARIARG$LOSSLSARG.log +if [ $PRECISION = 8 -a "$YUVARG" = "" ]; then + if [ "$ENTROPYARG" = "-optimize" ]; then + IMAGES="vgl_6434_0018a.${EXT}" + elif [ "$ENTROPYARG" = "-progressive" ]; then + IMAGES="vgl_6548_0026a.${EXT}" + elif [ "$ENTROPYARG" = "-arithmetic" ]; then + IMAGES="big_tree8.${EXT}" + fi +fi + +exec >$EXEDIR/tjbenchtest$JAVAARG$YUVARG$ALLOCARG$ENTROPYARG$LOSSLSARG-$PRECISION.log # Standard tests for image in $IMAGES; do cp $IMGDIR/$image $OUTDIR basename=`basename $image .${EXT}` - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG $ARIARG $LOSSLSARG $LOSSLSPSV -grayscale -outfile $OUTDIR/${basename}_GRAY_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG $ARIARG $LOSSLSARG $LOSSLSPSV -sample 2x2 -outfile $OUTDIR/${basename}_420_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG $ARIARG $LOSSLSARG $LOSSLSPSV -sample 2x1 -outfile $OUTDIR/${basename}_422_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG $ARIARG $LOSSLSARG $LOSSLSPSV -sample 1x1 -outfile $OUTDIR/${basename}_444_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG $ARIARG $LOSSLSARG $LOSSLSPSV -grayscale -outfile $OUTDIR/${basename}_GRAY_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG $ARIARG $LOSSLSARG $LOSSLSPSV -sample 2x2 -outfile $OUTDIR/${basename}_420_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG $ARIARG $LOSSLSARG $LOSSLSPSV -sample 2x1 -outfile $OUTDIR/${basename}_422_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG $ARIARG $LOSSLSARG $LOSSLSPSV -sample 1x1 -outfile $OUTDIR/${basename}_444_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct fast $ENTROPYARG $LOSSLSARG $LOSSLSPSV -grayscale -outfile $OUTDIR/${basename}_GRAY_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct fast $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 2x2 -outfile $OUTDIR/${basename}_420_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct fast $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 2x1 -outfile $OUTDIR/${basename}_422_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct fast $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 1x1 -outfile $OUTDIR/${basename}_444_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct int $ENTROPYARG $LOSSLSARG $LOSSLSPSV -grayscale -outfile $OUTDIR/${basename}_GRAY_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct int $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 2x2 -outfile $OUTDIR/${basename}_420_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct int $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 2x1 -outfile $OUTDIR/${basename}_422_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct int $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 1x1 -outfile $OUTDIR/${basename}_444_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} for samp in $ALLSUBSAMP; do - runme $EXEDIR/djpeg -rgb $NSARG $BMPARG -outfile $OUTDIR/${basename}_${samp}_default_djpeg.${EXT} $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg runme $EXEDIR/djpeg -dct fast -rgb $NSARG $BMPARG -outfile $OUTDIR/${basename}_${samp}_fast_djpeg.${EXT} $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg runme $EXEDIR/djpeg -dct int -rgb $NSARG $BMPARG -outfile $OUTDIR/${basename}_${samp}_accurate_djpeg.${EXT} $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg done for samp in $h2SUBSAMP; do - runme $EXEDIR/djpeg -nosmooth $BMPARG -outfile $OUTDIR/${basename}_${samp}_default_nosmooth_djpeg.${EXT} $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg runme $EXEDIR/djpeg -dct fast -nosmooth $BMPARG -outfile $OUTDIR/${basename}_${samp}_fast_nosmooth_djpeg.${EXT} $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg runme $EXEDIR/djpeg -dct int -nosmooth $BMPARG -outfile $OUTDIR/${basename}_${samp}_accurate_nosmooth_djpeg.${EXT} $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg done # Compression for dct in accurate fast; do - runme $EXEDIR/tjbench $OUTDIR/$image $TJQUAL -rgb -quiet -benchtime 0.01 -warmup 0 -${dct}dct $YUVARG $ALLOCARG $PROGARG $ARIARG $LOSSLSARG + dctarg= + if [ "${dct}" = "fast" ]; then + dctarg=-fastdct + fi + runme $TJBENCH $OUTDIR/$image $TJQUAL -precision $PRECISION -rgb -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG for samp in $ALLSUBSAMP; do - runme cmp $OUTDIR/${basename}_${samp}_Q${TJQUAL}.jpg $OUTDIR/${basename}_${samp}_${dct}_cjpeg.jpg + if [ "$LOSSLSARG" = "-lossless" ]; then + runme cmp $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}.jpg $OUTDIR/${basename}_${samp}_${dct}_cjpeg.jpg + else + runme cmp $OUTDIR/${basename}_${samp}_Q${TJQUAL}.jpg $OUTDIR/${basename}_${samp}_${dct}_cjpeg.jpg + fi done done - for dct in fast accurate default; do - dctarg=-${dct}dct - if [ "${dct}" = "default" ]; then - dctarg= + for dct in fast accurate; do + dctarg= + if [ "${dct}" = "fast" ]; then + dctarg=-fastdct fi # Tiled compression & decompression - runme $EXEDIR/tjbench $OUTDIR/$image $TJQUAL -rgb -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $PROGARG $ARIARG $LOSSLSARG + runme $TJBENCH $OUTDIR/$image $TJQUAL -precision $PRECISION -rgb -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG for samp in $h1SUBSAMP; do if [ $ALLOC = 1 ]; then - runme cmp $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.${EXT} $OUTDIR/${basename}_${samp}_${dct}_djpeg.${EXT} - rm $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.${EXT} + if [ "$LOSSLSARG" = "-lossless" ]; then + runme cmp $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}_full.${EXT} $OUTDIR/${basename}_${samp}_${dct}_djpeg.${EXT} + rm $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}_full.${EXT} + else + runme cmp $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.${EXT} $OUTDIR/${basename}_${samp}_${dct}_djpeg.${EXT} + rm $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.${EXT} + fi else - for i in $OUTDIR/${basename}_${samp}_Q${TJQUAL}_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ - $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.${EXT}; do - runme cmp $i $OUTDIR/${basename}_${samp}_${dct}_djpeg.${EXT} - rm $i - done + if [ "$LOSSLSARG" = "-lossless" ]; then + for i in $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ + $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}_full.${EXT}; do + runme cmp $i $OUTDIR/${basename}_${samp}_${dct}_djpeg.${EXT} + rm $i + done + else + for i in $OUTDIR/${basename}_${samp}_Q${TJQUAL}_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ + $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.${EXT}; do + runme cmp $i $OUTDIR/${basename}_${samp}_${dct}_djpeg.${EXT} + rm $i + done + fi fi done - runme $EXEDIR/tjbench $OUTDIR/$image $TJQUAL -rgb -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $ALLOCARG $PROGARG $ARIARG $LOSSLSARG + runme $TJBENCH $OUTDIR/$image $TJQUAL -precision $PRECISION -rgb -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG for samp in $h2SUBSAMP; do if [ $ALLOC = 1 ]; then - runme cmp $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.${EXT} $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.${EXT} - rm $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.${EXT} + if [ "$LOSSLSARG" = "-lossless" ]; then + runme cmp $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}_full.${EXT} $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.${EXT} + rm $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}_full.${EXT} + else + runme cmp $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.${EXT} $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.${EXT} + rm $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.${EXT} + fi else - for i in $OUTDIR/${basename}_${samp}_Q${TJQUAL}_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ - $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.${EXT}; do - runme cmp $i $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.${EXT} - rm $i - done + if [ "$LOSSLSARG" = "-lossless" ]; then + for i in $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ + $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}_full.${EXT}; do + runme cmp $i $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.${EXT} + rm $i + done + else + for i in $OUTDIR/${basename}_${samp}_Q${TJQUAL}_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ + $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.${EXT}; do + runme cmp $i $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.${EXT} + rm $i + done + fi fi done # Tiled decompression if [ "$LOSSLSARG" != "-lossless" ]; then for samp in GRAY 444; do - runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $PROGARG $ARIARG + runme $TJBENCH $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG if [ $ALLOC = 1 ]; then runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${dct}_djpeg.${EXT} rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} @@ -174,7 +236,7 @@ for image in $IMAGES; do fi done for samp in 420 422; do - runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $ALLOCARG $PROGARG $ARIARG + runme $TJBENCH $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG if [ $ALLOC = 1 ]; then runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.${EXT} rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} @@ -189,6 +251,26 @@ for image in $IMAGES; do fi done + # Partial decompression + if [ "$LOSSLSARG" != "-lossless" -a "$YUVARG" != "-yuv" ]; then + for samp in $ALLSUBSAMP; do + runme $EXEDIR/djpeg -rgb -crop 103x90+16+5 $NSARG -outfile $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg + runme $TJBENCH $OUTDIR/${basename}_${samp}_Q${TJQUAL}.jpg -crop 103x90+16+5 -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + runme cmp $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.ppm $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm + rm $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.ppm $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm + + runme $EXEDIR/djpeg -rgb -scale 7/8 -crop 91x81+14+3 $NSARG -outfile $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg + runme $TJBENCH $OUTDIR/${basename}_${samp}_Q${TJQUAL}.jpg -scale 7/8 -crop 91x81+14+3 -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + runme cmp $OUTDIR/${basename}_${samp}_Q${TJQUAL}_7_8.ppm $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm + rm $OUTDIR/${basename}_${samp}_Q${TJQUAL}_7_8.ppm $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm + + runme $EXEDIR/djpeg -rgb -scale 1/2 -crop 40x40+0+0 $NSARG -outfile $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg + runme $TJBENCH $OUTDIR/${basename}_${samp}_Q${TJQUAL}.jpg -scale 1/2 -crop 40x40+0+0 -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + runme cmp $OUTDIR/${basename}_${samp}_Q${TJQUAL}_1_2.ppm $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm + rm $OUTDIR/${basename}_${samp}_Q${TJQUAL}_1_2.ppm $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm + done + fi + # Scaled decompression for scale in 2_1 15_8 7_4 13_8 3_2 11_8 5_4 9_8 7_8 3_4 5_8 1_2 3_8 1_4 1_8; do scalearg=`echo $scale | sed 's/\_/\//g'` @@ -197,10 +279,16 @@ for image in $IMAGES; do SCALE=full fi for samp in $ALLSUBSAMP; do - runme $EXEDIR/djpeg -rgb -scale ${scalearg} $NSARG $BMPARG -outfile $OUTDIR/${basename}_${samp}_${scale}_djpeg.${EXT} $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg - runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q${TJQUAL}.jpg $BMPARG -scale ${scalearg} -quiet -benchtime 0.01 -warmup 0 $YUVARG $ALLOCARG $PROGARG $ARIARG $LOSSLSARG - runme cmp $OUTDIR/${basename}_${samp}_Q${TJQUAL}_${SCALE}.${EXT} $OUTDIR/${basename}_${samp}_${scale}_djpeg.${EXT} - rm $OUTDIR/${basename}_${samp}_Q${TJQUAL}_${SCALE}.${EXT} + runme $EXEDIR/djpeg -rgb -scale ${scalearg} $NSARG $BMPARG -outfile $OUTDIR/${basename}_${samp}_${scale}_djpeg.${EXT} $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg + if [ "$LOSSLSARG" = "-lossless" ]; then + runme $TJBENCH $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}.jpg $BMPARG -scale ${scalearg} -quiet -benchtime 0.01 -warmup 0 $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + runme cmp $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}_${SCALE}.${EXT} $OUTDIR/${basename}_${samp}_${scale}_djpeg.${EXT} + rm $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}_${SCALE}.${EXT} + else + runme $TJBENCH $OUTDIR/${basename}_${samp}_Q${TJQUAL}.jpg $BMPARG -scale ${scalearg} -quiet -benchtime 0.01 -warmup 0 $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + runme cmp $OUTDIR/${basename}_${samp}_Q${TJQUAL}_${SCALE}.${EXT} $OUTDIR/${basename}_${samp}_${scale}_djpeg.${EXT} + rm $OUTDIR/${basename}_${samp}_Q${TJQUAL}_${SCALE}.${EXT} + fi done done @@ -218,7 +306,7 @@ for image in $IMAGES; do for xform in hflip vflip transpose transverse rot90 rot180 rot270; do for samp in GRAY 444; do runme $EXEDIR/djpeg -rgb $BMPARG -outfile $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg - runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -tile -quiet -benchtime 0.01 -warmup 0 $YUVARG $ALLOCARG $PROGARG $ARIARG + runme $TJBENCH $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -tile -quiet -benchtime 0.01 -warmup 0 $YUVARG $ALLOCARG $ENTROPYARG if [ $ALLOC = 1 ]; then runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} @@ -232,7 +320,7 @@ for image in $IMAGES; do done for samp in 420 422; do runme $EXEDIR/djpeg -nosmooth -rgb $BMPARG -outfile $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg - runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample $YUVARG $ALLOCARG $PROGARG $ARIARG + runme $TJBENCH $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample $YUVARG $ALLOCARG $ENTROPYARG if [ $ALLOC = 1 ]; then runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} @@ -249,7 +337,7 @@ for image in $IMAGES; do # Grayscale transform for xform in hflip vflip transpose transverse rot90 rot180 rot270; do for samp in GRAY 444 422 420; do - runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -tile -quiet -benchtime 0.01 -warmup 0 -grayscale $YUVARG $ALLOCARG $PROGARG $ARIARG + runme $TJBENCH $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -tile -quiet -benchtime 0.01 -warmup 0 -grayscale $YUVARG $ALLOCARG $ENTROPYARG if [ $ALLOC = 1 ]; then runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_GRAY_${xform}_jpegtran.${EXT} rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} @@ -269,7 +357,7 @@ for image in $IMAGES; do for scale in 2_1 15_8 7_4 13_8 3_2 11_8 5_4 9_8 7_8 3_4 5_8 1_2 3_8 1_4 1_8; do scalearg=`echo $scale | sed 's/\_/\//g'` runme $EXEDIR/djpeg -rgb -scale ${scalearg} $NSARG $BMPARG -outfile $OUTDIR/${basename}_${samp}_${xform}_${scale}_jpegtran.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg - runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -scale ${scalearg} -quiet -benchtime 0.01 -warmup 0 $YUVARG $ALLOCARG $PROGARG $ARIARG + runme $TJBENCH $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -scale ${scalearg} -quiet -benchtime 0.01 -warmup 0 $YUVARG $ALLOCARG $ENTROPYARG runme cmp $OUTDIR/${basename}_${samp}_Q95_${scale}.${EXT} $OUTDIR/${basename}_${samp}_${xform}_${scale}_jpegtran.${EXT} rm $OUTDIR/${basename}_${samp}_Q95_${scale}.${EXT} done diff --git a/tjbenchtest.java.in b/tjbenchtest.java.in deleted file mode 100755 index f99c5ce73..000000000 --- a/tjbenchtest.java.in +++ /dev/null @@ -1,241 +0,0 @@ -#!/bin/bash - -set -u -set -e -trap onexit INT -trap onexit TERM -trap onexit EXIT - -onexit() -{ - if [ -d $OUTDIR ]; then - rm -rf $OUTDIR - fi -} - -runme() -{ - echo \*\*\* $* - "$@" -} - -IMAGES="vgl_5674_0098.bmp vgl_6434_0018a.bmp vgl_6548_0026a.bmp nightshot_iso_100.bmp" -IMGDIR=@CMAKE_CURRENT_SOURCE_DIR@/testimages -OUTDIR=`mktemp -d /tmp/__tjbenchtest_java_output.XXXXXX` -EXEDIR=@CMAKE_CURRENT_BINARY_DIR@ -JAVA="@Java_JAVA_EXECUTABLE@" -JAVAARGS="-cp $EXEDIR/java/turbojpeg.jar -Djava.library.path=$EXEDIR" -BMPARG= -NSARG= -YUVARG= -PROGARG= -ARIARG= -LOSSLSARG= -LOSSLSPSV= -TJQUAL=95 -h1SUBSAMP="GRAY 444" -h2SUBSAMP="420 422" -ALLSUBSAMP="GRAY 420 422 444" - -if [ -d $OUTDIR ]; then - rm -rf $OUTDIR -fi -mkdir -p $OUTDIR - -while [ $# -gt 0 ]; do - case "$1" in - -yuv) - NSARG=-nosmooth - YUVARG=-yuv - -# NOTE: The combination of tjEncodeYUV*() and tjCompressFromYUV*() does not -# always produce bitwise-identical results to tjCompress*() if subsampling is -# enabled. In both cases, if the image width or height are not evenly -# divisible by the MCU width/height, then the bottom and/or right edge are -# expanded. However, the libjpeg code performs this expansion prior to -# downsampling, and TurboJPEG performs it in tjCompressFromYUV*(), which is -# after downsampling. Thus, the two will agree only if the width/height along -# each downsampled dimension is an odd number or is evenly divisible by the MCU -# width/height. This disagreement basically amounts to a round-off error, but -# there is no easy way around it, so for now, we just test the only image that -# works. (NOTE: nightshot_iso_100 does not suffer from the above issue, but -# it suffers from an unrelated problem whereby the combination of -# tjDecompressToYUV*() and tjDecodeYUV*() do not produce bitwise-identical -# results to tjDecompress*() if decompression scaling is enabled. This latter -# phenomenon is not yet fully understood but is also believed to be some sort -# of round-off error.) - IMAGES="vgl_6548_0026a.bmp" - ;; - -progressive) - PROGARG=-progressive - ;; - -arithmetic) - ARIARG=-arithmetic - ;; - -lossless) - LOSSLSARG="-lossless" - LOSSLSPSV=4 - TJQUAL=40 - h1SUBSAMP=444 - h2SUBSAMP=444 - ALLSUBSAMP=444 - ;; - esac - shift -done - -exec >$EXEDIR/tjbenchtest-java$YUVARG$PROGARG$ARIARG$LOSSLSARG.log - -# Standard tests -for image in $IMAGES; do - - cp $IMGDIR/$image $OUTDIR - basename=`basename $image .bmp` - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG $ARIARG $LOSSLSARG $LOSSLSPSV -grayscale -outfile $OUTDIR/${basename}_GRAY_fast_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG $ARIARG $LOSSLSARG $LOSSLSPSV -sample 2x2 -outfile $OUTDIR/${basename}_420_fast_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG $ARIARG $LOSSLSARG $LOSSLSPSV -sample 2x1 -outfile $OUTDIR/${basename}_422_fast_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG $ARIARG $LOSSLSARG $LOSSLSPSV -sample 1x1 -outfile $OUTDIR/${basename}_444_fast_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG $ARIARG $LOSSLSARG $LOSSLSPSV -grayscale -outfile $OUTDIR/${basename}_GRAY_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG $ARIARG $LOSSLSARG $LOSSLSPSV -sample 2x2 -outfile $OUTDIR/${basename}_420_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG $ARIARG $LOSSLSARG $LOSSLSPSV -sample 2x1 -outfile $OUTDIR/${basename}_422_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG $ARIARG $LOSSLSARG $LOSSLSPSV -sample 1x1 -outfile $OUTDIR/${basename}_444_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp - for samp in $ALLSUBSAMP; do - runme $EXEDIR/djpeg -rgb -bmp -outfile $OUTDIR/${basename}_${samp}_default_djpeg.bmp $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg - runme $EXEDIR/djpeg -dct fast -rgb -bmp -outfile $OUTDIR/${basename}_${samp}_fast_djpeg.bmp $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg - runme $EXEDIR/djpeg -dct int -rgb -bmp -outfile $OUTDIR/${basename}_${samp}_accurate_djpeg.bmp $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg - done - for samp in $h2SUBSAMP; do - runme $EXEDIR/djpeg -nosmooth -bmp -outfile $OUTDIR/${basename}_${samp}_default_nosmooth_djpeg.bmp $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg - runme $EXEDIR/djpeg -dct fast -nosmooth -bmp -outfile $OUTDIR/${basename}_${samp}_fast_nosmooth_djpeg.bmp $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg - runme $EXEDIR/djpeg -dct int -nosmooth -bmp -outfile $OUTDIR/${basename}_${samp}_accurate_nosmooth_djpeg.bmp $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg - done - - # Compression - for dct in accurate fast; do - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/$image $TJQUAL -rgb -quiet -benchtime 0.01 -warmup 0 -${dct}dct $YUVARG $PROGARG $ARIARG $LOSSLSARG - for samp in $ALLSUBSAMP; do - runme cmp $OUTDIR/${basename}_${samp}_Q${TJQUAL}.jpg $OUTDIR/${basename}_${samp}_${dct}_cjpeg.jpg - done - done - - for dct in fast accurate default; do - dctarg=-${dct}dct - if [ "${dct}" = "default" ]; then - dctarg= - fi - - # Tiled compression & decompression - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/$image $TJQUAL -rgb -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $PROGARG $ARIARG $LOSSLSARG - for samp in $h1SUBSAMP; do - for i in $OUTDIR/${basename}_${samp}_Q${TJQUAL}_[0-9]*[0-9]x[0-9]*[0-9].bmp \ - $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.bmp; do - runme cmp -i 54:54 $i $OUTDIR/${basename}_${samp}_${dct}_djpeg.bmp - rm $i - done - done - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/$image $TJQUAL -rgb -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $PROGARG $ARIARG $LOSSLSARG - for samp in $h2SUBSAMP; do - for i in $OUTDIR/${basename}_${samp}_Q${TJQUAL}_[0-9]*[0-9]x[0-9]*[0-9].bmp \ - $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.bmp; do - runme cmp -i 54:54 $i $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.bmp - rm $i - done - done - - # Tiled decompression - if [ "$LOSSLSARG" != "-lossless" ]; then - for samp in GRAY 444; do - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $PROGARG $ARIARG - for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].bmp \ - $OUTDIR/${basename}_${samp}_Q95_full.bmp; do - runme cmp -i 54:54 $i $OUTDIR/${basename}_${samp}_${dct}_djpeg.bmp - rm $i - done - done - for samp in 420 422; do - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $PROGARG $ARIARG - for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].bmp \ - $OUTDIR/${basename}_${samp}_Q95_full.bmp; do - runme cmp $i -i 54:54 $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.bmp - rm $i - done - done - fi - done - - # Scaled decompression - for scale in 2_1 15_8 7_4 13_8 3_2 11_8 5_4 9_8 7_8 3_4 5_8 1_2 3_8 1_4 1_8; do - scalearg=`echo $scale | sed 's/\_/\//g'` - SCALE=$scale - if [ "$LOSSLSARG" = "-lossless" ]; then - SCALE=full - fi - for samp in $ALLSUBSAMP; do - runme $EXEDIR/djpeg -rgb -scale ${scalearg} $NSARG -bmp -outfile $OUTDIR/${basename}_${samp}_${scale}_djpeg.bmp $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q${TJQUAL}.jpg -scale ${scalearg} -quiet -benchtime 0.01 -warmup 0 $YUVARG $PROGARG $ARIARG $LOSSLSARG - runme cmp -i 54:54 $OUTDIR/${basename}_${samp}_Q${TJQUAL}_${SCALE}.bmp $OUTDIR/${basename}_${samp}_${scale}_djpeg.bmp - rm $OUTDIR/${basename}_${samp}_Q${TJQUAL}_${SCALE}.bmp - done - done - - # Transforms - if [ "$LOSSLSARG" != "-lossless" ]; then - for samp in GRAY 420 422 444; do - runme $EXEDIR/jpegtran -flip horizontal -trim -outfile $OUTDIR/${basename}_${samp}_hflip_jpegtran.jpg $OUTDIR/${basename}_${samp}_Q95.jpg - runme $EXEDIR/jpegtran -flip vertical -trim -outfile $OUTDIR/${basename}_${samp}_vflip_jpegtran.jpg $OUTDIR/${basename}_${samp}_Q95.jpg - runme $EXEDIR/jpegtran -transpose -trim -outfile $OUTDIR/${basename}_${samp}_transpose_jpegtran.jpg $OUTDIR/${basename}_${samp}_Q95.jpg - runme $EXEDIR/jpegtran -transverse -trim -outfile $OUTDIR/${basename}_${samp}_transverse_jpegtran.jpg $OUTDIR/${basename}_${samp}_Q95.jpg - runme $EXEDIR/jpegtran -rotate 90 -trim -outfile $OUTDIR/${basename}_${samp}_rot90_jpegtran.jpg $OUTDIR/${basename}_${samp}_Q95.jpg - runme $EXEDIR/jpegtran -rotate 180 -trim -outfile $OUTDIR/${basename}_${samp}_rot180_jpegtran.jpg $OUTDIR/${basename}_${samp}_Q95.jpg - runme $EXEDIR/jpegtran -rotate 270 -trim -outfile $OUTDIR/${basename}_${samp}_rot270_jpegtran.jpg $OUTDIR/${basename}_${samp}_Q95.jpg - done - for xform in hflip vflip transpose transverse rot90 rot180 rot270; do - for samp in GRAY 444; do - runme $EXEDIR/djpeg -rgb -bmp -outfile $OUTDIR/${basename}_${samp}_${xform}_jpegtran.bmp $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -$xform -tile -quiet -benchtime 0.01 -warmup 0 $YUVARG $PROGARG $ARIARG - for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].bmp \ - $OUTDIR/${basename}_${samp}_Q95_full.bmp; do - runme cmp -i 54:54 $i $OUTDIR/${basename}_${samp}_${xform}_jpegtran.bmp - rm $i - done - done - for samp in 420 422; do - runme $EXEDIR/djpeg -nosmooth -rgb -bmp -outfile $OUTDIR/${basename}_${samp}_${xform}_jpegtran.bmp $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -$xform -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample $YUVARG $PROGARG $ARIARG - for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].bmp \ - $OUTDIR/${basename}_${samp}_Q95_full.bmp; do - runme cmp -i 54:54 $i $OUTDIR/${basename}_${samp}_${xform}_jpegtran.bmp - rm $i - done - done - done - - # Grayscale transform - for xform in hflip vflip transpose transverse rot90 rot180 rot270; do - for samp in GRAY 444 422 420; do - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -$xform -tile -quiet -benchtime 0.01 -warmup 0 -grayscale $YUVARG $PROGARG $ARIARG - for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].bmp \ - $OUTDIR/${basename}_${samp}_Q95_full.bmp; do - runme cmp -i 54:54 $i $OUTDIR/${basename}_GRAY_${xform}_jpegtran.bmp - rm $i - done - done - done - - # Transforms with scaling - for xform in hflip vflip transpose transverse rot90 rot180 rot270; do - for samp in GRAY 444 422 420; do - for scale in 2_1 15_8 7_4 13_8 3_2 11_8 5_4 9_8 7_8 3_4 5_8 1_2 3_8 1_4 1_8; do - scalearg=`echo $scale | sed 's/\_/\//g'` - runme $EXEDIR/djpeg -rgb -scale ${scalearg} $NSARG -bmp -outfile $OUTDIR/${basename}_${samp}_${xform}_${scale}_jpegtran.bmp $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -$xform -scale ${scalearg} -quiet -benchtime 0.01 -warmup 0 $YUVARG $PROGARG $ARIARG - runme cmp -i 54:54 $OUTDIR/${basename}_${samp}_Q95_${scale}.bmp $OUTDIR/${basename}_${samp}_${xform}_${scale}_jpegtran.bmp - rm $OUTDIR/${basename}_${samp}_Q95_${scale}.bmp - done - done - done - fi - -done - -echo SUCCESS! diff --git a/tjexample.c b/tjexample.c index 857a7f633..1c6c7a15d 100644 --- a/tjexample.c +++ b/tjexample.c @@ -53,7 +53,7 @@ retval = -1; goto bailout; \ } -#define THROW_TJ(action) THROW(action, tjGetErrorStr2(tjInstance)) +#define THROW_TJ(action) THROW(action, tj3GetErrorStr(tjInstance)) #define THROW_UNIX(action) THROW(action, strerror(errno)) @@ -153,18 +153,16 @@ static void usage(char *programName) printf("-fastdct = Use the fastest DCT/IDCT algorithm available\n\n"); - printf("-accuratedct = Use the most accurate DCT/IDCT algorithm available\n\n"); - exit(1); } int main(int argc, char **argv) { - tjscalingfactor scalingFactor = { 1, 1 }; + tjscalingfactor scalingFactor = TJUNSCALED; int outSubsamp = -1, outQual = -1; tjtransform xform; - int flags = 0; + int fastUpsample = 0, fastDCT = 0; int width, height; char *inFormat, *outFormat; FILE *jpegFile = NULL; @@ -172,7 +170,7 @@ int main(int argc, char **argv) int retval = 0, i, pixelFormat = TJPF_UNKNOWN; tjhandle tjInstance = NULL; - if ((scalingFactors = tjGetScalingFactors(&numScalingFactors)) == NULL) + if ((scalingFactors = tj3GetScalingFactors(&numScalingFactors)) == NULL) THROW_TJ("getting scaling factors"); memset(&xform, 0, sizeof(tjtransform)); @@ -238,13 +236,10 @@ int main(int argc, char **argv) xform.options |= TJXOPT_CROP; } else if (!strcasecmp(argv[i], "-fastupsample")) { printf("Using fast upsampling code\n"); - flags |= TJFLAG_FASTUPSAMPLE; + fastUpsample = 1; } else if (!strcasecmp(argv[i], "-fastdct")) { printf("Using fastest DCT/IDCT algorithm\n"); - flags |= TJFLAG_FASTDCT; - } else if (!strcasecmp(argv[i], "-accuratedct")) { - printf("Using most accurate DCT/IDCT algorithm\n"); - flags |= TJFLAG_ACCURATEDCT; + fastDCT = 1; } else usage(argv[0]); } @@ -260,10 +255,10 @@ int main(int argc, char **argv) if (!strcasecmp(inFormat, "jpg")) { /* Input image is a JPEG image. Decompress and/or transform it. */ long size; - int inSubsamp, inColorspace, inFlags; + int inSubsamp, inColorspace; int doTransform = (xform.op != TJXOP_NONE || xform.options != 0 || xform.customFilter != NULL); - unsigned long jpegSize; + size_t jpegSize; /* Read the JPEG file into memory. */ if ((jpegFile = fopen(argv[1], "rb")) == NULL) @@ -273,8 +268,8 @@ int main(int argc, char **argv) THROW_UNIX("determining input file size"); if (size == 0) THROW("determining input file size", "Input file contains no data"); - jpegSize = (unsigned long)size; - if ((jpegBuf = (unsigned char *)tjAlloc(jpegSize)) == NULL) + jpegSize = size; + if ((jpegBuf = tj3Alloc(jpegSize)) == NULL) THROW_UNIX("allocating JPEG buffer"); if (fread(jpegBuf, jpegSize, 1, jpegFile) < 1) THROW_UNIX("reading input file"); @@ -283,30 +278,37 @@ int main(int argc, char **argv) if (doTransform) { /* Transform it. */ unsigned char *dstBuf = NULL; /* Dynamically allocate the JPEG buffer */ - unsigned long dstSize = 0; + size_t dstSize = 0; - if ((tjInstance = tjInitTransform()) == NULL) + if ((tjInstance = tj3Init(TJINIT_TRANSFORM)) == NULL) THROW_TJ("initializing transformer"); xform.options |= TJXOPT_TRIM; - if (tjTransform(tjInstance, jpegBuf, jpegSize, 1, &dstBuf, &dstSize, - &xform, flags) < 0) { - tjFree(dstBuf); + if (tj3Transform(tjInstance, jpegBuf, jpegSize, 1, &dstBuf, &dstSize, + &xform) < 0) { + tj3Free(dstBuf); THROW_TJ("transforming input image"); } - tjFree(jpegBuf); + tj3Free(jpegBuf); jpegBuf = dstBuf; jpegSize = dstSize; } else { - if ((tjInstance = tjInitDecompress()) == NULL) + if ((tjInstance = tj3Init(TJINIT_DECOMPRESS)) == NULL) THROW_TJ("initializing decompressor"); } + if (tj3Set(tjInstance, TJPARAM_FASTUPSAMPLE, fastUpsample) < 0) + THROW_TJ("setting TJPARAM_FASTUPSAMPLE"); + if (tj3Set(tjInstance, TJPARAM_FASTDCT, fastDCT) < 0) + THROW_TJ("setting TJPARAM_FASTDCT"); - if (tjDecompressHeader4(tjInstance, jpegBuf, jpegSize, &width, &height, - &inSubsamp, &inColorspace, &inFlags) < 0) + if (tj3DecompressHeader(tjInstance, jpegBuf, jpegSize) < 0) THROW_TJ("reading JPEG header"); + width = tj3Get(tjInstance, TJPARAM_JPEGWIDTH); + height = tj3Get(tjInstance, TJPARAM_JPEGHEIGHT); + inSubsamp = tj3Get(tjInstance, TJPARAM_SUBSAMP); + inColorspace = tj3Get(tjInstance, TJPARAM_COLORSPACE); - if (inFlags & TJFLAG_LOSSLESS) - scalingFactor.num = scalingFactor.denom = 1; + if (tj3Get(tjInstance, TJPARAM_LOSSLESS)) + scalingFactor = TJUNSCALED; printf("%s Image: %d x %d pixels, %s subsampling, %s colorspace\n", (doTransform ? "Transformed" : "Input"), width, height, @@ -328,25 +330,27 @@ int main(int argc, char **argv) /* Scaling and/or a non-JPEG output image format and/or compression options have been selected, so we need to decompress the input/transformed image. */ + if (tj3SetScalingFactor(tjInstance, scalingFactor) < 0) + THROW_TJ("setting scaling factor"); width = TJSCALED(width, scalingFactor); height = TJSCALED(height, scalingFactor); if (outSubsamp < 0) outSubsamp = inSubsamp; pixelFormat = TJPF_BGRX; - if ((imgBuf = (unsigned char *)tjAlloc(width * height * - tjPixelSize[pixelFormat])) == NULL) + if ((imgBuf = tj3Alloc(width * height * tjPixelSize[pixelFormat])) == NULL) THROW_UNIX("allocating uncompressed image buffer"); - if (tjDecompress2(tjInstance, jpegBuf, jpegSize, imgBuf, width, 0, height, - pixelFormat, flags) < 0) + if (tj3Decompress8(tjInstance, jpegBuf, jpegSize, imgBuf, 0, + pixelFormat) < 0) THROW_TJ("decompressing JPEG image"); - tjFree(jpegBuf); jpegBuf = NULL; - tjDestroy(tjInstance); tjInstance = NULL; + tj3Free(jpegBuf); jpegBuf = NULL; } else { /* Input image is not a JPEG image. Load it into memory. */ - if ((imgBuf = tjLoadImage(argv[1], &width, 1, &height, &pixelFormat, - 0)) == NULL) + if ((tjInstance = tj3Init(TJINIT_COMPRESS)) == NULL) + THROW_TJ("initializing compressor"); + if ((imgBuf = tj3LoadImage8(tjInstance, argv[1], &width, 1, &height, + &pixelFormat)) == NULL) THROW_TJ("loading input image"); if (outSubsamp < 0) { if (pixelFormat == TJPF_GRAY) @@ -361,7 +365,7 @@ int main(int argc, char **argv) if (!strcasecmp(outFormat, "jpg")) { /* Output image format is JPEG. Compress the uncompressed image. */ - unsigned long jpegSize = 0; + size_t jpegSize = 0; jpegBuf = NULL; /* Dynamically allocate the JPEG buffer */ @@ -370,33 +374,38 @@ int main(int argc, char **argv) printf(", %s subsampling, quality = %d\n", subsampName[outSubsamp], outQual); - if ((tjInstance = tjInitCompress()) == NULL) - THROW_TJ("initializing compressor"); - if (tjCompress2(tjInstance, imgBuf, width, 0, height, pixelFormat, - &jpegBuf, &jpegSize, outSubsamp, outQual, flags) < 0) + if (tj3Set(tjInstance, TJPARAM_SUBSAMP, outSubsamp) < 0) + THROW_TJ("setting TJPARAM_SUBSAMP"); + if (tj3Set(tjInstance, TJPARAM_QUALITY, outQual) < 0) + THROW_TJ("setting TJPARAM_QUALITY"); + if (tj3Set(tjInstance, TJPARAM_FASTDCT, fastDCT) < 0) + THROW_TJ("setting TJPARAM_FASTDCT"); + if (tj3Compress8(tjInstance, imgBuf, width, 0, height, pixelFormat, + &jpegBuf, &jpegSize) < 0) THROW_TJ("compressing image"); - tjDestroy(tjInstance); tjInstance = NULL; + tj3Destroy(tjInstance); tjInstance = NULL; /* Write the JPEG image to disk. */ if ((jpegFile = fopen(argv[2], "wb")) == NULL) THROW_UNIX("opening output file"); if (fwrite(jpegBuf, jpegSize, 1, jpegFile) < 1) THROW_UNIX("writing output file"); - tjDestroy(tjInstance); tjInstance = NULL; + tj3Destroy(tjInstance); tjInstance = NULL; fclose(jpegFile); jpegFile = NULL; - tjFree(jpegBuf); jpegBuf = NULL; + tj3Free(jpegBuf); jpegBuf = NULL; } else { /* Output image format is not JPEG. Save the uncompressed image directly to disk. */ printf("\n"); - if (tjSaveImage(argv[2], imgBuf, width, 0, height, pixelFormat, 0) < 0) + if (tj3SaveImage8(tjInstance, argv[2], imgBuf, width, 0, height, + pixelFormat) < 0) THROW_TJ("saving output image"); } bailout: - tjFree(imgBuf); - if (tjInstance) tjDestroy(tjInstance); - tjFree(jpegBuf); + tj3Free(imgBuf); + tj3Destroy(tjInstance); + tj3Free(jpegBuf); if (jpegFile) fclose(jpegFile); return retval; } diff --git a/tjexampletest.in b/tjexampletest.in index 0d3047e26..c56fc4299 100755 --- a/tjexampletest.in +++ b/tjexampletest.in @@ -19,17 +19,34 @@ runme() $* } -IMAGES="vgl_5674_0098.bmp vgl_6434_0018a.bmp vgl_6548_0026a.bmp nightshot_iso_100.bmp" +IMAGES="vgl_5674_0098.bmp vgl_6434_0018a.bmp vgl_6548_0026a.bmp big_tree8.bmp" IMGDIR=@CMAKE_CURRENT_SOURCE_DIR@/testimages OUTDIR=`mktemp -d /tmp/__tjexampletest_output.XXXXXX` EXEDIR=@CMAKE_CURRENT_BINARY_DIR@ +JAVA="@Java_JAVA_EXECUTABLE@" +JAVAARGS="-cp $EXEDIR/java/turbojpeg.jar -Djava.library.path=$EXEDIR" +TJEXAMPLE=$EXEDIR/tjexample +JAVAARG= if [ -d $OUTDIR ]; then rm -rf $OUTDIR fi mkdir -p $OUTDIR -exec >$EXEDIR/tjexampletest.log +while [ $# -gt 0 ]; do + case "$1" in + -java) + JAVAARG=-java + TJEXAMPLE="$JAVA $JAVAARGS TJExample" + # The Java version of TJExample can't currently handle pixel density + # information, so it fails on big_tree8.bmp. + IMAGES="vgl_5674_0098.bmp vgl_6434_0018a.bmp vgl_6548_0026a.bmp" + ;; + esac + shift +done + +exec >$EXEDIR/tjexampletest$JAVAARG.log for image in $IMAGES; do @@ -44,39 +61,39 @@ for image in $IMAGES; do runme $EXEDIR/cjpeg -quality 95 -dct int -sample 2x1 -outfile $OUTDIR/${basename}_422_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp runme $EXEDIR/cjpeg -quality 95 -dct int -sample 1x1 -outfile $OUTDIR/${basename}_444_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp for samp in GRAY 420 422 444; do - runme $EXEDIR/djpeg -rgb -bmp -outfile $OUTDIR/${basename}_${samp}_default_djpeg.bmp $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg runme $EXEDIR/djpeg -dct fast -rgb -bmp -outfile $OUTDIR/${basename}_${samp}_fast_djpeg.bmp $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg runme $EXEDIR/djpeg -dct int -rgb -bmp -outfile $OUTDIR/${basename}_${samp}_accurate_djpeg.bmp $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg done for samp in 420 422; do - runme $EXEDIR/djpeg -nosmooth -bmp -outfile $OUTDIR/${basename}_${samp}_default_nosmooth_djpeg.bmp $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg runme $EXEDIR/djpeg -dct fast -nosmooth -bmp -outfile $OUTDIR/${basename}_${samp}_fast_nosmooth_djpeg.bmp $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg runme $EXEDIR/djpeg -dct int -nosmooth -bmp -outfile $OUTDIR/${basename}_${samp}_accurate_nosmooth_djpeg.bmp $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg done # Compression for dct in fast accurate; do + dctarg= + if [ "${dct}" = "fast" ]; then + dctarg=-fastdct + fi for samp in GRAY 420 422 444; do - runme $EXEDIR/tjexample $OUTDIR/$image $OUTDIR/${basename}_${samp}_${dct}.jpg -q 95 -subsamp ${samp} -${dct}dct + runme $TJEXAMPLE $OUTDIR/$image $OUTDIR/${basename}_${samp}_${dct}.jpg -q 95 -subsamp ${samp} ${dctarg} runme cmp $OUTDIR/${basename}_${samp}_${dct}.jpg $OUTDIR/${basename}_${samp}_${dct}_cjpeg.jpg done done # Decompression - for dct in fast accurate default; do - srcdct=${dct} - dctarg=-${dct}dct - if [ "${dct}" = "default" ]; then - srcdct=fast - dctarg= + for dct in fast accurate; do + dctarg= + if [ "${dct}" = "fast" ]; then + dctarg=-fastdct fi for samp in GRAY 420 422 444; do - runme $EXEDIR/tjexample $OUTDIR/${basename}_${samp}_${srcdct}.jpg $OUTDIR/${basename}_${samp}_${dct}.bmp ${dctarg} + runme $TJEXAMPLE $OUTDIR/${basename}_${samp}_${dct}.jpg $OUTDIR/${basename}_${samp}_${dct}.bmp ${dctarg} runme cmp -i 54:54 $OUTDIR/${basename}_${samp}_${dct}.bmp $OUTDIR/${basename}_${samp}_${dct}_djpeg.bmp rm $OUTDIR/${basename}_${samp}_${dct}.bmp done for samp in 420 422; do - runme $EXEDIR/tjexample $OUTDIR/${basename}_${samp}_${srcdct}.jpg $OUTDIR/${basename}_${samp}_${dct}_nosmooth.bmp -fastupsample ${dctarg} + runme $TJEXAMPLE $OUTDIR/${basename}_${samp}_${dct}.jpg $OUTDIR/${basename}_${samp}_${dct}_nosmooth.bmp -fastupsample ${dctarg} runme cmp -i 54:54 $OUTDIR/${basename}_${samp}_${dct}_nosmooth.bmp $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.bmp rm $OUTDIR/${basename}_${samp}_${dct}_nosmooth.bmp done @@ -87,7 +104,7 @@ for image in $IMAGES; do scalearg=`echo $scale | sed 's/\_/\//g'` for samp in GRAY 420 422 444; do runme $EXEDIR/djpeg -rgb -bmp -scale ${scalearg} -outfile $OUTDIR/${basename}_${samp}_${scale}_djpeg.bmp $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg - runme $EXEDIR/tjexample $OUTDIR/${basename}_${samp}_fast.jpg $OUTDIR/${basename}_${samp}_${scale}.bmp -scale ${scalearg} + runme $TJEXAMPLE $OUTDIR/${basename}_${samp}_fast.jpg $OUTDIR/${basename}_${samp}_${scale}.bmp -scale ${scalearg} runme cmp -i 54:54 $OUTDIR/${basename}_${samp}_${scale}.bmp $OUTDIR/${basename}_${samp}_${scale}_djpeg.bmp rm $OUTDIR/${basename}_${samp}_${scale}.bmp done @@ -105,16 +122,16 @@ for image in $IMAGES; do done for xform in hflip vflip transpose transverse rot90 rot180 rot270; do for samp in GRAY 420 422 444; do - runme $EXEDIR/tjexample $OUTDIR/${basename}_${samp}_fast.jpg $OUTDIR/${basename}_${samp}_${xform}.jpg -$xform -crop 70x60+16+16 + runme $TJEXAMPLE $OUTDIR/${basename}_${samp}_fast.jpg $OUTDIR/${basename}_${samp}_${xform}.jpg -$xform -crop 70x60+16+16 runme cmp $OUTDIR/${basename}_${samp}_${xform}.jpg $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg runme $EXEDIR/djpeg -rgb -bmp -outfile $OUTDIR/${basename}_${samp}_${xform}_jpegtran.bmp $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg - runme $EXEDIR/tjexample $OUTDIR/${basename}_${samp}_fast.jpg $OUTDIR/${basename}_${samp}_${xform}.bmp -$xform -crop 70x60+16+16 + runme $TJEXAMPLE $OUTDIR/${basename}_${samp}_fast.jpg $OUTDIR/${basename}_${samp}_${xform}.bmp -$xform -crop 70x60+16+16 runme cmp -i 54:54 $OUTDIR/${basename}_${samp}_${xform}.bmp $OUTDIR/${basename}_${samp}_${xform}_jpegtran.bmp rm $OUTDIR/${basename}_${samp}_${xform}.bmp done for samp in 420 422; do runme $EXEDIR/djpeg -nosmooth -rgb -bmp -outfile $OUTDIR/${basename}_${samp}_${xform}_jpegtran.bmp $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg - runme $EXEDIR/tjexample $OUTDIR/${basename}_${samp}_fast.jpg $OUTDIR/${basename}_${samp}_${xform}.bmp -$xform -crop 70x60+16+16 -fastupsample + runme $TJEXAMPLE $OUTDIR/${basename}_${samp}_fast.jpg $OUTDIR/${basename}_${samp}_${xform}.bmp -$xform -crop 70x60+16+16 -fastupsample runme cmp -i 54:54 $OUTDIR/${basename}_${samp}_${xform}.bmp $OUTDIR/${basename}_${samp}_${xform}_jpegtran.bmp rm $OUTDIR/${basename}_${samp}_${xform}.bmp done @@ -123,9 +140,9 @@ for image in $IMAGES; do # Grayscale transform for xform in hflip vflip transpose transverse rot90 rot180 rot270; do for samp in GRAY 444 422 420; do - runme $EXEDIR/tjexample $OUTDIR/${basename}_${samp}_fast.jpg $OUTDIR/${basename}_${samp}_${xform}.jpg -$xform -grayscale -crop 70x60+16+16 + runme $TJEXAMPLE $OUTDIR/${basename}_${samp}_fast.jpg $OUTDIR/${basename}_${samp}_${xform}.jpg -$xform -grayscale -crop 70x60+16+16 runme cmp $OUTDIR/${basename}_${samp}_${xform}.jpg $OUTDIR/${basename}_GRAY_${xform}_jpegtran.jpg - runme $EXEDIR/tjexample $OUTDIR/${basename}_${samp}_fast.jpg $OUTDIR/${basename}_${samp}_${xform}.bmp -$xform -grayscale -crop 70x60+16+16 + runme $TJEXAMPLE $OUTDIR/${basename}_${samp}_fast.jpg $OUTDIR/${basename}_${samp}_${xform}.bmp -$xform -grayscale -crop 70x60+16+16 runme cmp -i 54:54 $OUTDIR/${basename}_${samp}_${xform}.bmp $OUTDIR/${basename}_GRAY_${xform}_jpegtran.bmp rm $OUTDIR/${basename}_${samp}_${xform}.bmp done @@ -137,7 +154,7 @@ for image in $IMAGES; do for scale in 2_1 15_8 7_4 13_8 3_2 11_8 5_4 9_8 7_8 3_4 5_8 1_2 3_8 1_4 1_8; do scalearg=`echo $scale | sed 's/\_/\//g'` runme $EXEDIR/djpeg -rgb -bmp -scale ${scalearg} -outfile $OUTDIR/${basename}_${samp}_${xform}_${scale}_jpegtran.bmp $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg - runme $EXEDIR/tjexample $OUTDIR/${basename}_${samp}_fast.jpg $OUTDIR/${basename}_${samp}_${xform}_${scale}.bmp -$xform -scale ${scalearg} -crop 70x60+16+16 + runme $TJEXAMPLE $OUTDIR/${basename}_${samp}_fast.jpg $OUTDIR/${basename}_${samp}_${xform}_${scale}.bmp -$xform -scale ${scalearg} -crop 70x60+16+16 runme cmp -i 54:54 $OUTDIR/${basename}_${samp}_${xform}_${scale}.bmp $OUTDIR/${basename}_${samp}_${xform}_${scale}_jpegtran.bmp rm $OUTDIR/${basename}_${samp}_${xform}_${scale}.bmp done diff --git a/tjexampletest.java.in b/tjexampletest.java.in deleted file mode 100755 index d4b63bc54..000000000 --- a/tjexampletest.java.in +++ /dev/null @@ -1,151 +0,0 @@ -#!/bin/bash - -set -u -set -e -trap onexit INT -trap onexit TERM -trap onexit EXIT - -onexit() -{ - if [ -d $OUTDIR ]; then - rm -rf $OUTDIR - fi -} - -runme() -{ - echo \*\*\* $* - "$@" -} - -IMAGES="vgl_5674_0098.bmp vgl_6434_0018a.bmp vgl_6548_0026a.bmp nightshot_iso_100.bmp" -IMGDIR=@CMAKE_CURRENT_SOURCE_DIR@/testimages -OUTDIR=`mktemp -d /tmp/__tjexampletest_java_output.XXXXXX` -EXEDIR=@CMAKE_CURRENT_BINARY_DIR@ -JAVA="@Java_JAVA_EXECUTABLE@" -JAVAARGS="-cp $EXEDIR/java/turbojpeg.jar -Djava.library.path=$EXEDIR" - -if [ -d $OUTDIR ]; then - rm -rf $OUTDIR -fi -mkdir -p $OUTDIR - -exec >$EXEDIR/tjexampletest-java.log - -for image in $IMAGES; do - - cp $IMGDIR/$image $OUTDIR - basename=`basename $image .bmp` - runme $EXEDIR/cjpeg -quality 95 -dct fast -grayscale -outfile $OUTDIR/${basename}_GRAY_fast_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct fast -sample 2x2 -outfile $OUTDIR/${basename}_420_fast_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct fast -sample 2x1 -outfile $OUTDIR/${basename}_422_fast_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct fast -sample 1x1 -outfile $OUTDIR/${basename}_444_fast_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct int -grayscale -outfile $OUTDIR/${basename}_GRAY_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct int -sample 2x2 -outfile $OUTDIR/${basename}_420_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct int -sample 2x1 -outfile $OUTDIR/${basename}_422_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct int -sample 1x1 -outfile $OUTDIR/${basename}_444_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp - for samp in GRAY 420 422 444; do - runme $EXEDIR/djpeg -rgb -bmp -outfile $OUTDIR/${basename}_${samp}_default_djpeg.bmp $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg - runme $EXEDIR/djpeg -dct fast -rgb -bmp -outfile $OUTDIR/${basename}_${samp}_fast_djpeg.bmp $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg - runme $EXEDIR/djpeg -dct int -rgb -bmp -outfile $OUTDIR/${basename}_${samp}_accurate_djpeg.bmp $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg - done - for samp in 420 422; do - runme $EXEDIR/djpeg -nosmooth -bmp -outfile $OUTDIR/${basename}_${samp}_default_nosmooth_djpeg.bmp $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg - runme $EXEDIR/djpeg -dct fast -nosmooth -bmp -outfile $OUTDIR/${basename}_${samp}_fast_nosmooth_djpeg.bmp $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg - runme $EXEDIR/djpeg -dct int -nosmooth -bmp -outfile $OUTDIR/${basename}_${samp}_accurate_nosmooth_djpeg.bmp $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg - done - - # Compression - for dct in fast accurate; do - for samp in GRAY 420 422 444; do - runme "$JAVA" $JAVAARGS TJExample $OUTDIR/$image $OUTDIR/${basename}_${samp}_${dct}.jpg -q 95 -subsamp ${samp} -${dct}dct - runme cmp $OUTDIR/${basename}_${samp}_${dct}.jpg $OUTDIR/${basename}_${samp}_${dct}_cjpeg.jpg - done - done - - # Decompression - for dct in fast accurate default; do - srcdct=${dct} - dctarg=-${dct}dct - if [ "${dct}" = "default" ]; then - srcdct=fast - dctarg= - fi - for samp in GRAY 420 422 444; do - runme "$JAVA" $JAVAARGS TJExample $OUTDIR/${basename}_${samp}_${srcdct}.jpg $OUTDIR/${basename}_${samp}_${dct}.bmp ${dctarg} - runme cmp -i 54:54 $OUTDIR/${basename}_${samp}_${dct}.bmp $OUTDIR/${basename}_${samp}_${dct}_djpeg.bmp - rm $OUTDIR/${basename}_${samp}_${dct}.bmp - done - for samp in 420 422; do - runme "$JAVA" $JAVAARGS TJExample $OUTDIR/${basename}_${samp}_${srcdct}.jpg $OUTDIR/${basename}_${samp}_${dct}_nosmooth.bmp -fastupsample ${dctarg} - runme cmp -i 54:54 $OUTDIR/${basename}_${samp}_${dct}_nosmooth.bmp $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.bmp - rm $OUTDIR/${basename}_${samp}_${dct}_nosmooth.bmp - done - done - - # Scaled decompression - for scale in 2_1 15_8 7_4 13_8 3_2 11_8 5_4 9_8 7_8 3_4 5_8 1_2 3_8 1_4 1_8; do - scalearg=`echo $scale | sed 's/\_/\//g'` - for samp in GRAY 420 422 444; do - runme $EXEDIR/djpeg -rgb -bmp -scale ${scalearg} -outfile $OUTDIR/${basename}_${samp}_${scale}_djpeg.bmp $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg - runme "$JAVA" $JAVAARGS TJExample $OUTDIR/${basename}_${samp}_fast.jpg $OUTDIR/${basename}_${samp}_${scale}.bmp -scale ${scalearg} - runme cmp -i 54:54 $OUTDIR/${basename}_${samp}_${scale}.bmp $OUTDIR/${basename}_${samp}_${scale}_djpeg.bmp - rm $OUTDIR/${basename}_${samp}_${scale}.bmp - done - done - - # Transforms - for samp in GRAY 420 422 444; do - runme $EXEDIR/jpegtran -crop 70x60+16+16 -flip horizontal -trim -outfile $OUTDIR/${basename}_${samp}_hflip_jpegtran.jpg $OUTDIR/${basename}_${samp}_fast.jpg - runme $EXEDIR/jpegtran -crop 70x60+16+16 -flip vertical -trim -outfile $OUTDIR/${basename}_${samp}_vflip_jpegtran.jpg $OUTDIR/${basename}_${samp}_fast.jpg - runme $EXEDIR/jpegtran -crop 70x60+16+16 -transpose -trim -outfile $OUTDIR/${basename}_${samp}_transpose_jpegtran.jpg $OUTDIR/${basename}_${samp}_fast.jpg - runme $EXEDIR/jpegtran -crop 70x60+16+16 -transverse -trim -outfile $OUTDIR/${basename}_${samp}_transverse_jpegtran.jpg $OUTDIR/${basename}_${samp}_fast.jpg - runme $EXEDIR/jpegtran -crop 70x60+16+16 -rotate 90 -trim -outfile $OUTDIR/${basename}_${samp}_rot90_jpegtran.jpg $OUTDIR/${basename}_${samp}_fast.jpg - runme $EXEDIR/jpegtran -crop 70x60+16+16 -rotate 180 -trim -outfile $OUTDIR/${basename}_${samp}_rot180_jpegtran.jpg $OUTDIR/${basename}_${samp}_fast.jpg - runme $EXEDIR/jpegtran -crop 70x60+16+16 -rotate 270 -trim -outfile $OUTDIR/${basename}_${samp}_rot270_jpegtran.jpg $OUTDIR/${basename}_${samp}_fast.jpg - done - for xform in hflip vflip transpose transverse rot90 rot180 rot270; do - for samp in GRAY 420 422 444; do - runme "$JAVA" $JAVAARGS TJExample $OUTDIR/${basename}_${samp}_fast.jpg $OUTDIR/${basename}_${samp}_${xform}.jpg -$xform -crop 70x60+16+16 - runme cmp $OUTDIR/${basename}_${samp}_${xform}.jpg $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg - runme $EXEDIR/djpeg -rgb -bmp -outfile $OUTDIR/${basename}_${samp}_${xform}_jpegtran.bmp $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg - runme "$JAVA" $JAVAARGS TJExample $OUTDIR/${basename}_${samp}_fast.jpg $OUTDIR/${basename}_${samp}_${xform}.bmp -$xform -crop 70x60+16+16 - runme cmp -i 54:54 $OUTDIR/${basename}_${samp}_${xform}.bmp $OUTDIR/${basename}_${samp}_${xform}_jpegtran.bmp - rm $OUTDIR/${basename}_${samp}_${xform}.bmp - done - for samp in 420 422; do - runme $EXEDIR/djpeg -nosmooth -rgb -bmp -outfile $OUTDIR/${basename}_${samp}_${xform}_jpegtran.bmp $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg - runme "$JAVA" $JAVAARGS TJExample $OUTDIR/${basename}_${samp}_fast.jpg $OUTDIR/${basename}_${samp}_${xform}.bmp -$xform -crop 70x60+16+16 -fastupsample - runme cmp -i 54:54 $OUTDIR/${basename}_${samp}_${xform}.bmp $OUTDIR/${basename}_${samp}_${xform}_jpegtran.bmp - rm $OUTDIR/${basename}_${samp}_${xform}.bmp - done - done - - # Grayscale transform - for xform in hflip vflip transpose transverse rot90 rot180 rot270; do - for samp in GRAY 444 422 420; do - runme "$JAVA" $JAVAARGS TJExample $OUTDIR/${basename}_${samp}_fast.jpg $OUTDIR/${basename}_${samp}_${xform}.jpg -$xform -grayscale -crop 70x60+16+16 - runme cmp $OUTDIR/${basename}_${samp}_${xform}.jpg $OUTDIR/${basename}_GRAY_${xform}_jpegtran.jpg - runme "$JAVA" $JAVAARGS TJExample $OUTDIR/${basename}_${samp}_fast.jpg $OUTDIR/${basename}_${samp}_${xform}.bmp -$xform -grayscale -crop 70x60+16+16 - runme cmp -i 54:54 $OUTDIR/${basename}_${samp}_${xform}.bmp $OUTDIR/${basename}_GRAY_${xform}_jpegtran.bmp - rm $OUTDIR/${basename}_${samp}_${xform}.bmp - done - done - - # Transforms with scaling - for xform in hflip vflip transpose transverse rot90 rot180 rot270; do - for samp in GRAY 444 422 420; do - for scale in 2_1 15_8 7_4 13_8 3_2 11_8 5_4 9_8 7_8 3_4 5_8 1_2 3_8 1_4 1_8; do - scalearg=`echo $scale | sed 's/\_/\//g'` - runme $EXEDIR/djpeg -rgb -bmp -scale ${scalearg} -outfile $OUTDIR/${basename}_${samp}_${xform}_${scale}_jpegtran.bmp $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg - runme "$JAVA" $JAVAARGS TJExample $OUTDIR/${basename}_${samp}_fast.jpg $OUTDIR/${basename}_${samp}_${xform}_${scale}.bmp -$xform -scale ${scalearg} -crop 70x60+16+16 - runme cmp -i 54:54 $OUTDIR/${basename}_${samp}_${xform}_${scale}.bmp $OUTDIR/${basename}_${samp}_${xform}_${scale}_jpegtran.bmp - rm $OUTDIR/${basename}_${samp}_${xform}_${scale}.bmp - done - done - done - -done - -echo SUCCESS! diff --git a/tjunittest.c b/tjunittest.c index 98f270d81..4e0a969ae 100644 --- a/tjunittest.c +++ b/tjunittest.c @@ -43,7 +43,7 @@ #include "tjutil.h" #include "turbojpeg.h" #include "md5/md5.h" -#include "cmyk.h" +#include "jconfigint.h" #ifdef _WIN32 #include #define random() rand() @@ -57,8 +57,11 @@ static void usage(char *progName) printf("\nUSAGE: %s [options]\n\n", progName); printf("Options:\n"); printf("-yuv = test YUV encoding/compression/decompression/decoding\n"); + printf(" (8-bit data precision only)\n"); printf("-noyuvpad = do not pad each row in each Y, U, and V plane to the nearest\n"); printf(" multiple of 4 bytes\n"); + printf("-precision N = test N-bit data precision (N is 8, 12, or 16; default is 8; if N\n"); + printf(" is 16, then -lossless is implied)\n"); printf("-lossless = test lossless JPEG compression/decompression\n"); printf("-alloc = test automatic JPEG buffer allocation\n"); printf("-bmp = test packed-pixel image I/O\n"); @@ -66,11 +69,11 @@ static void usage(char *progName) } -#define THROW_TJ() { \ - printf("TurboJPEG ERROR:\n%s\n", tjGetErrorStr()); \ +#define THROW_TJ(handle) { \ + printf("TurboJPEG ERROR:\n%s\n", tj3GetErrorStr(handle)); \ BAILOUT() \ } -#define TRY_TJ(f) { if ((f) == -1) THROW_TJ(); } +#define TRY_TJ(handle, f) { if ((f) == -1) THROW_TJ(handle); } #define THROW(m) { printf("ERROR: %s\n", m); BAILOUT() } #define THROW_MD5(filename, md5sum, ref) { \ printf("\n%s has an MD5 sum of %s.\n Should be %s.\n", filename, md5sum, \ @@ -90,67 +93,79 @@ const char *pixFormatStr[TJ_NUMPF] = { "RGBA", "BGRA", "ABGR", "ARGB", "CMYK" }; -const int _3byteFormats[] = { TJPF_RGB, TJPF_BGR }; -const int _4byteFormats[] = { +const int _3sampleFormats[] = { TJPF_RGB, TJPF_BGR }; +const int _4sampleFormats[] = { TJPF_RGBX, TJPF_BGRX, TJPF_XBGR, TJPF_XRGB, TJPF_CMYK }; const int _onlyGray[] = { TJPF_GRAY }; const int _onlyRGB[] = { TJPF_RGB }; -int doYUV = 0, lossless = 0, alloc = 0, yuvAlign = 4, psv = 1; +int doYUV = 0, lossless = 0, psv = 1, alloc = 0, yuvAlign = 4; +int precision = 8, sampleSize, maxSample, tolerance, redToY, yellowToY; int exitStatus = 0; #define BAILOUT() { exitStatus = -1; goto bailout; } -static void initBuf(unsigned char *buf, int w, int h, int pf, int flags) +static void setVal(void *buf, int index, int value) +{ + if (precision == 8) + ((unsigned char *)buf)[index] = (unsigned char)value; + else if (precision == 12) + ((short *)buf)[index] = (short)value; + else + ((unsigned short *)buf)[index] = (unsigned short)value; +} + +static void initBuf(void *buf, int w, int h, int pf, int bottomUp) { int roffset = tjRedOffset[pf]; int goffset = tjGreenOffset[pf]; int boffset = tjBlueOffset[pf]; int ps = tjPixelSize[pf]; - int index, row, col, halfway = 16; + int i, index, row, col, halfway = 16; if (pf == TJPF_GRAY) { - memset(buf, 0, w * h * ps); + memset(buf, 0, w * h * ps * sampleSize); for (row = 0; row < h; row++) { for (col = 0; col < w; col++) { - if (flags & TJFLAG_BOTTOMUP) index = (h - row - 1) * w + col; + if (bottomUp) index = (h - row - 1) * w + col; else index = row * w + col; if (((row / 8) + (col / 8)) % 2 == 0) - buf[index] = (row < halfway) ? 255 : 0; - else buf[index] = (row < halfway) ? 76 : 226; + setVal(buf, index, (row < halfway) ? maxSample : 0); + else setVal(buf, index, (row < halfway) ? redToY : yellowToY); } } } else if (pf == TJPF_CMYK) { - memset(buf, 255, w * h * ps); + for (i = 0; i < w * h * ps; i++) + setVal(buf, i, maxSample); for (row = 0; row < h; row++) { for (col = 0; col < w; col++) { - if (flags & TJFLAG_BOTTOMUP) index = (h - row - 1) * w + col; + if (bottomUp) index = (h - row - 1) * w + col; else index = row * w + col; if (((row / 8) + (col / 8)) % 2 == 0) { - if (row >= halfway) buf[index * ps + 3] = 0; + if (row >= halfway) setVal(buf, index * ps + 3, 0); } else { - buf[index * ps + 2] = 0; - if (row < halfway) buf[index * ps + 1] = 0; + setVal(buf, index * ps + 2, 0); + if (row < halfway) setVal(buf, index * ps + 1, 0); } } } } else { - memset(buf, 0, w * h * ps); + memset(buf, 0, w * h * ps * sampleSize); for (row = 0; row < h; row++) { for (col = 0; col < w; col++) { - if (flags & TJFLAG_BOTTOMUP) index = (h - row - 1) * w + col; + if (bottomUp) index = (h - row - 1) * w + col; else index = row * w + col; if (((row / 8) + (col / 8)) % 2 == 0) { if (row < halfway) { - buf[index * ps + roffset] = 255; - buf[index * ps + goffset] = 255; - buf[index * ps + boffset] = 255; + setVal(buf, index * ps + roffset, maxSample); + setVal(buf, index * ps + goffset, maxSample); + setVal(buf, index * ps + boffset, maxSample); } } else { - buf[index * ps + roffset] = 255; - if (row >= halfway) buf[index * ps + goffset] = 255; + setVal(buf, index * ps + roffset, maxSample); + if (row >= halfway) setVal(buf, index * ps + goffset, maxSample); } } } @@ -159,7 +174,7 @@ static void initBuf(unsigned char *buf, int w, int h, int pf, int flags) #define CHECKVAL(v, cv) { \ - if (v < cv - (1 - lossless) || v > cv + (1 - lossless)) { \ + if (v < cv - tolerance || v > cv + tolerance) { \ printf("\nComp. %s at %d,%d should be %d, not %d\n", #v, row, col, cv, \ v); \ retval = 0; exitStatus = -1; goto bailout; \ @@ -167,22 +182,33 @@ static void initBuf(unsigned char *buf, int w, int h, int pf, int flags) } #define CHECKVAL0(v) { \ - if (v > (1 - lossless)) { \ + if (v > tolerance) { \ printf("\nComp. %s at %d,%d should be 0, not %d\n", #v, row, col, v); \ retval = 0; exitStatus = -1; goto bailout; \ } \ } -#define CHECKVAL255(v) { \ - if (v < 255 - (1 - lossless)) { \ - printf("\nComp. %s at %d,%d should be 255, not %d\n", #v, row, col, v); \ +#define CHECKVALMAX(v) { \ + if (v < maxSample - tolerance) { \ + printf("\nComp. %s at %d,%d should be %d, not %d\n", #v, row, col, \ + maxSample, v); \ retval = 0; exitStatus = -1; goto bailout; \ } \ } -static int checkBuf(unsigned char *buf, int w, int h, int pf, int subsamp, - tjscalingfactor sf, int flags) +static int getVal(void *buf, int index) +{ + if (precision == 8) + return ((unsigned char *)buf)[index]; + else if (precision == 12) + return ((short *)buf)[index]; + else + return ((unsigned short *)buf)[index]; +} + +static int checkBuf(void *buf, int w, int h, int pf, int subsamp, + tjscalingfactor sf, int bottomUp) { int roffset = tjRedOffset[pf]; int goffset = tjGreenOffset[pf]; @@ -198,22 +224,22 @@ static int checkBuf(unsigned char *buf, int w, int h, int pf, int subsamp, if (pf == TJPF_CMYK) { for (row = 0; row < h; row++) { for (col = 0; col < w; col++) { - unsigned char c, m, y, k; + int c, m, y, k; - if (flags & TJFLAG_BOTTOMUP) index = (h - row - 1) * w + col; + if (bottomUp) index = (h - row - 1) * w + col; else index = row * w + col; - c = buf[index * ps]; - m = buf[index * ps + 1]; - y = buf[index * ps + 2]; - k = buf[index * ps + 3]; + c = getVal(buf, index * ps); + m = getVal(buf, index * ps + 1); + y = getVal(buf, index * ps + 2); + k = getVal(buf, index * ps + 3); if (((row / blocksize) + (col / blocksize)) % 2 == 0) { - CHECKVAL255(c); CHECKVAL255(m); CHECKVAL255(y); - if (row < halfway) CHECKVAL255(k) + CHECKVALMAX(c); CHECKVALMAX(m); CHECKVALMAX(y); + if (row < halfway) CHECKVALMAX(k) else CHECKVAL0(k) } else { - CHECKVAL255(c); CHECKVAL0(y); CHECKVAL255(k); + CHECKVALMAX(c); CHECKVAL0(y); CHECKVALMAX(k); if (row < halfway) CHECKVAL0(m) - else CHECKVAL255(m) + else CHECKVALMAX(m) } } } @@ -222,36 +248,37 @@ static int checkBuf(unsigned char *buf, int w, int h, int pf, int subsamp, for (row = 0; row < h; row++) { for (col = 0; col < w; col++) { - unsigned char r, g, b, a; + int r, g, b, a; - if (flags & TJFLAG_BOTTOMUP) index = (h - row - 1) * w + col; + if (bottomUp) index = (h - row - 1) * w + col; else index = row * w + col; - r = buf[index * ps + roffset]; - g = buf[index * ps + goffset]; - b = buf[index * ps + boffset]; - a = aoffset >= 0 ? buf[index * ps + aoffset] : 0xFF; + r = getVal(buf, index * ps + roffset); + g = getVal(buf, index * ps + goffset); + b = getVal(buf, index * ps + boffset); + a = aoffset >= 0 ? getVal(buf, index * ps + aoffset) : maxSample; if (((row / blocksize) + (col / blocksize)) % 2 == 0) { if (row < halfway) { - CHECKVAL255(r); CHECKVAL255(g); CHECKVAL255(b); + CHECKVALMAX(r); CHECKVALMAX(g); CHECKVALMAX(b); } else { CHECKVAL0(r); CHECKVAL0(g); CHECKVAL0(b); } } else { if (subsamp == TJSAMP_GRAY) { if (row < halfway) { - CHECKVAL(r, 76); CHECKVAL(g, 76); CHECKVAL(b, 76); + CHECKVAL(r, redToY); CHECKVAL(g, redToY); CHECKVAL(b, redToY); } else { - CHECKVAL(r, 226); CHECKVAL(g, 226); CHECKVAL(b, 226); + CHECKVAL(r, yellowToY); CHECKVAL(g, yellowToY); + CHECKVAL(b, yellowToY); } } else { if (row < halfway) { - CHECKVAL255(r); CHECKVAL0(g); CHECKVAL0(b); + CHECKVALMAX(r); CHECKVAL0(g); CHECKVAL0(b); } else { - CHECKVAL255(r); CHECKVAL255(g); CHECKVAL0(b); + CHECKVALMAX(r); CHECKVALMAX(g); CHECKVAL0(b); } } } - CHECKVAL255(a); + CHECKVALMAX(a); } } @@ -260,13 +287,15 @@ static int checkBuf(unsigned char *buf, int w, int h, int pf, int subsamp, for (row = 0; row < h; row++) { for (col = 0; col < w; col++) { if (pf == TJPF_CMYK) - printf("%.3d/%.3d/%.3d/%.3d ", buf[(row * w + col) * ps], - buf[(row * w + col) * ps + 1], buf[(row * w + col) * ps + 2], - buf[(row * w + col) * ps + 3]); + printf("%.3d/%.3d/%.3d/%.3d ", getVal(buf, (row * w + col) * ps), + getVal(buf, (row * w + col) * ps + 1), + getVal(buf, (row * w + col) * ps + 2), + getVal(buf, (row * w + col) * ps + 3)); else - printf("%.3d/%.3d/%.3d ", buf[(row * w + col) * ps + roffset], - buf[(row * w + col) * ps + goffset], - buf[(row * w + col) * ps + boffset]); + printf("%.3d/%.3d/%.3d ", + getVal(buf, (row * w + col) * ps + roffset), + getVal(buf, (row * w + col) * ps + goffset), + getVal(buf, (row * w + col) * ps + boffset)); } printf("\n"); } @@ -294,11 +323,11 @@ static int checkBufYUV(unsigned char *buf, int w, int h, int subsamp, unsigned char y = buf[ypitch * row + col]; if (((row / blocksize) + (col / blocksize)) % 2 == 0) { - if (row < halfway) CHECKVAL255(y) + if (row < halfway) CHECKVALMAX(y) else CHECKVAL0(y); } else { if (row < halfway) CHECKVAL(y, 76) - else CHECKVAL(y, 226); + else CHECKVAL(y, 225); } } } @@ -314,7 +343,7 @@ static int checkBufYUV(unsigned char *buf, int w, int h, int subsamp, CHECKVAL(u, 128); CHECKVAL(v, 128); } else { if (row < halfway) { - CHECKVAL(u, 85); CHECKVAL255(v); + CHECKVAL(u, 85); CHECKVALMAX(v); } else { CHECKVAL0(u); CHECKVAL(v, 149); } @@ -349,8 +378,7 @@ static int checkBufYUV(unsigned char *buf, int w, int h, int subsamp, } -static void writeJPEG(unsigned char *jpegBuf, unsigned long jpegSize, - char *filename) +static void writeJPEG(unsigned char *jpegBuf, size_t jpegSize, char *filename) { FILE *file = fopen(filename, "wb"); @@ -364,55 +392,75 @@ static void writeJPEG(unsigned char *jpegBuf, unsigned long jpegSize, } -static void compTest(tjhandle handle, unsigned char **dstBuf, - unsigned long *dstSize, int w, int h, int pf, - char *basename, int subsamp, int jpegQual, int flags) +static void compTest(tjhandle handle, unsigned char **dstBuf, size_t *dstSize, + int w, int h, int pf, char *basename) { char tempStr[1024]; - unsigned char *srcBuf = NULL, *yuvBuf = NULL; + void *srcBuf = NULL; + unsigned char *yuvBuf = NULL; const char *pfStr = pixFormatStr[pf]; - const char *buStrLong = - (flags & TJFLAG_BOTTOMUP) ? "Bottom-Up" : "Top-Down "; - const char *buStr = (flags & TJFLAG_BOTTOMUP) ? "BU" : "TD"; - - if ((srcBuf = (unsigned char *)malloc(w * h * tjPixelSize[pf])) == NULL) - THROW("Memory allocation failure"); - initBuf(srcBuf, w, h, pf, flags); + int bottomUp = tj3Get(handle, TJPARAM_BOTTOMUP); + int subsamp = tj3Get(handle, TJPARAM_SUBSAMP); + int jpegPSV = tj3Get(handle, TJPARAM_LOSSLESSPSV); + int jpegQual = tj3Get(handle, TJPARAM_QUALITY); + const char *buStrLong = bottomUp ? "Bottom-Up" : "Top-Down "; + const char *buStr = bottomUp ? "BU" : "TD"; + + if ((srcBuf = malloc(w * h * tjPixelSize[pf] * sampleSize)) == NULL) + THROW("Memory allocation failure"); + initBuf(srcBuf, w, h, pf, bottomUp); if (*dstBuf && *dstSize > 0) memset(*dstBuf, 0, *dstSize); - if (!alloc) flags |= TJFLAG_NOREALLOC; if (doYUV) { - unsigned long yuvSize = tjBufSizeYUV2(w, yuvAlign, h, subsamp); + size_t yuvSize = tj3YUVBufSize(w, yuvAlign, h, subsamp); tjscalingfactor sf = { 1, 1 }; - tjhandle handle2 = tjInitCompress(); + tjhandle handle2 = NULL; - if (!handle2) THROW_TJ(); + if ((handle2 = tj3Init(TJINIT_COMPRESS)) == NULL) + THROW_TJ(NULL); + TRY_TJ(handle2, tj3Set(handle2, TJPARAM_BOTTOMUP, bottomUp)); + TRY_TJ(handle2, tj3Set(handle2, TJPARAM_SUBSAMP, subsamp)); if ((yuvBuf = (unsigned char *)malloc(yuvSize)) == NULL) THROW("Memory allocation failure"); memset(yuvBuf, 0, yuvSize); printf("%s %s -> YUV %s ... ", pfStr, buStrLong, subNameLong[subsamp]); - TRY_TJ(tjEncodeYUV3(handle2, srcBuf, w, 0, h, pf, yuvBuf, yuvAlign, - subsamp, flags)); - tjDestroy(handle2); + TRY_TJ(handle2, tj3EncodeYUV8(handle2, (unsigned char *)srcBuf, w, 0, h, + pf, yuvBuf, yuvAlign)); + tj3Destroy(handle2); if (checkBufYUV(yuvBuf, w, h, subsamp, sf)) printf("Passed.\n"); else printf("FAILED!\n"); printf("YUV %s %s -> JPEG Q%d ... ", subNameLong[subsamp], buStrLong, jpegQual); - TRY_TJ(tjCompressFromYUV(handle, yuvBuf, w, yuvAlign, h, subsamp, dstBuf, - dstSize, jpegQual, flags)); + TRY_TJ(handle, tj3CompressFromYUV8(handle, yuvBuf, w, yuvAlign, h, dstBuf, + dstSize)); } else { - printf("%s %s -> %s Q%d ... ", pfStr, buStrLong, subNameLong[subsamp], - jpegQual); - TRY_TJ(tjCompress2(handle, srcBuf, w, 0, h, pf, dstBuf, dstSize, subsamp, - jpegQual, flags)); + if (lossless) + printf("%s %s -> LOSSLESS PSV%d ... ", pfStr, buStrLong, jpegPSV); + else + printf("%s %s -> %s Q%d ... ", pfStr, buStrLong, subNameLong[subsamp], + jpegQual); + if (precision == 8) { + TRY_TJ(handle, tj3Compress8(handle, (unsigned char *)srcBuf, w, 0, h, pf, + dstBuf, dstSize)); + } else if (precision == 12) { + TRY_TJ(handle, tj3Compress12(handle, (short *)srcBuf, w, 0, h, pf, + dstBuf, dstSize)); + } else { + TRY_TJ(handle, tj3Compress16(handle, (unsigned short *)srcBuf, w, 0, h, + pf, dstBuf, dstSize)); + } } - SNPRINTF(tempStr, 1024, "%s_enc_%s_%s_%s_Q%d.jpg", basename, pfStr, buStr, - subName[subsamp], jpegQual); + if (lossless) + SNPRINTF(tempStr, 1024, "%s_enc%d_%s_%s_LOSSLESS_PSV%d.jpg", basename, + precision, pfStr, buStr, jpegPSV); + else + SNPRINTF(tempStr, 1024, "%s_enc%d_%s_%s_%s_Q%d.jpg", basename, precision, + pfStr, buStr, subName[subsamp], jpegQual); writeJPEG(*dstBuf, *dstSize, tempStr); printf("Done.\n Result in %s\n", tempStr); @@ -423,34 +471,42 @@ static void compTest(tjhandle handle, unsigned char **dstBuf, static void _decompTest(tjhandle handle, unsigned char *jpegBuf, - unsigned long jpegSize, int w, int h, int pf, - char *basename, int subsamp, int flags, - tjscalingfactor sf) + size_t jpegSize, int w, int h, int pf, char *basename, + int subsamp, tjscalingfactor sf) { - unsigned char *dstBuf = NULL, *yuvBuf = NULL; - int _hdrw = 0, _hdrh = 0, _hdrsubsamp = -1; + void *dstBuf = NULL; + unsigned char *yuvBuf = NULL; + int _hdrw = 0, _hdrh = 0, _hdrsubsamp; int scaledWidth = TJSCALED(w, sf); int scaledHeight = TJSCALED(h, sf); - unsigned long dstSize = 0; + size_t dstSize = 0; + int bottomUp = tj3Get(handle, TJPARAM_BOTTOMUP); + + TRY_TJ(handle, tj3SetScalingFactor(handle, sf)); - TRY_TJ(tjDecompressHeader2(handle, jpegBuf, jpegSize, &_hdrw, &_hdrh, - &_hdrsubsamp)); + TRY_TJ(handle, tj3DecompressHeader(handle, jpegBuf, jpegSize)); + _hdrw = tj3Get(handle, TJPARAM_JPEGWIDTH); + _hdrh = tj3Get(handle, TJPARAM_JPEGHEIGHT); + _hdrsubsamp = tj3Get(handle, TJPARAM_SUBSAMP); if (lossless && subsamp != TJSAMP_444 && subsamp != TJSAMP_GRAY) subsamp = TJSAMP_444; if (_hdrw != w || _hdrh != h || _hdrsubsamp != subsamp) THROW("Incorrect JPEG header"); dstSize = scaledWidth * scaledHeight * tjPixelSize[pf]; - if ((dstBuf = (unsigned char *)malloc(dstSize)) == NULL) + if ((dstBuf = malloc(dstSize * sampleSize)) == NULL) THROW("Memory allocation failure"); - memset(dstBuf, 0, dstSize); + memset(dstBuf, 0, dstSize * sampleSize); if (doYUV) { - unsigned long yuvSize = tjBufSizeYUV2(scaledWidth, yuvAlign, scaledHeight, - subsamp); - tjhandle handle2 = tjInitDecompress(); + size_t yuvSize = tj3YUVBufSize(scaledWidth, yuvAlign, scaledHeight, + subsamp); + tjhandle handle2 = NULL; - if (!handle2) THROW_TJ(); + if ((handle2 = tj3Init(TJINIT_DECOMPRESS)) == NULL) + THROW_TJ(NULL); + TRY_TJ(handle2, tj3Set(handle2, TJPARAM_BOTTOMUP, bottomUp)); + TRY_TJ(handle2, tj3Set(handle2, TJPARAM_SUBSAMP, subsamp)); if ((yuvBuf = (unsigned char *)malloc(yuvSize)) == NULL) THROW("Memory allocation failure"); @@ -460,35 +516,37 @@ static void _decompTest(tjhandle handle, unsigned char *jpegBuf, if (sf.num != 1 || sf.denom != 1) printf("%d/%d ... ", sf.num, sf.denom); else printf("... "); - /* We pass scaledWidth + 1 and scaledHeight + 1 to validate that - tjDecompressToYUV2() generates the largest possible scaled image that - fits within the desired dimensions, as documented. */ - TRY_TJ(tjDecompressToYUV2(handle, jpegBuf, jpegSize, yuvBuf, - scaledWidth + 1, yuvAlign, scaledHeight + 1, - flags)); + TRY_TJ(handle, tj3DecompressToYUV8(handle, jpegBuf, jpegSize, yuvBuf, + yuvAlign)); if (checkBufYUV(yuvBuf, scaledWidth, scaledHeight, subsamp, sf)) printf("Passed.\n"); else printf("FAILED!\n"); printf("YUV %s -> %s %s ... ", subNameLong[subsamp], pixFormatStr[pf], - (flags & TJFLAG_BOTTOMUP) ? "Bottom-Up" : "Top-Down "); - TRY_TJ(tjDecodeYUV(handle2, yuvBuf, yuvAlign, subsamp, dstBuf, scaledWidth, - 0, scaledHeight, pf, flags)); - tjDestroy(handle2); + bottomUp ? "Bottom-Up" : "Top-Down "); + TRY_TJ(handle2, tj3DecodeYUV8(handle2, yuvBuf, yuvAlign, + (unsigned char *)dstBuf, scaledWidth, 0, + scaledHeight, pf)); + tj3Destroy(handle2); } else { printf("JPEG -> %s %s ", pixFormatStr[pf], - (flags & TJFLAG_BOTTOMUP) ? "Bottom-Up" : "Top-Down "); + bottomUp ? "Bottom-Up" : "Top-Down "); if (sf.num != 1 || sf.denom != 1) printf("%d/%d ... ", sf.num, sf.denom); else printf("... "); - /* We pass scaledWidth + 1 and scaledHeight + 1 to validate that - tjDecompress2() generates the largest possible scaled image that fits - within the desired dimensions, as documented. */ - TRY_TJ(tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, scaledWidth + 1, 0, - scaledHeight + 1, pf, flags)); + if (precision == 8) { + TRY_TJ(handle, tj3Decompress8(handle, jpegBuf, jpegSize, + (unsigned char *)dstBuf, 0, pf)); + } else if (precision == 12) { + TRY_TJ(handle, tj3Decompress12(handle, jpegBuf, jpegSize, + (short *)dstBuf, 0, pf)); + } else { + TRY_TJ(handle, tj3Decompress16(handle, jpegBuf, jpegSize, + (unsigned short *)dstBuf, 0, pf)); + } } - if (checkBuf(dstBuf, scaledWidth, scaledHeight, pf, subsamp, sf, flags)) + if (checkBuf(dstBuf, scaledWidth, scaledHeight, pf, subsamp, sf, bottomUp)) printf("Passed."); else printf("FAILED!"); printf("\n"); @@ -500,20 +558,20 @@ static void _decompTest(tjhandle handle, unsigned char *jpegBuf, static void decompTest(tjhandle handle, unsigned char *jpegBuf, - unsigned long jpegSize, int w, int h, int pf, - char *basename, int subsamp, int flags) + size_t jpegSize, int w, int h, int pf, char *basename, + int subsamp) { int i, n = 0; - tjscalingfactor *sf = NULL, sf1 = { 1, 1 }; + tjscalingfactor *sf = NULL; if (lossless) { - _decompTest(handle, jpegBuf, jpegSize, w, h, pf, basename, subsamp, flags, - sf1); + _decompTest(handle, jpegBuf, jpegSize, w, h, pf, basename, subsamp, + TJUNSCALED); return; } - sf = tjGetScalingFactors(&n); - if (!sf || !n) THROW_TJ(); + sf = tj3GetScalingFactors(&n); + if (!sf || !n) THROW_TJ(NULL); for (i = 0; i < n; i++) { if (subsamp == TJSAMP_444 || subsamp == TJSAMP_GRAY || @@ -522,7 +580,7 @@ static void decompTest(tjhandle handle, unsigned char *jpegBuf, (subsamp != TJSAMP_411 && sf[i].num == 1 && (sf[i].denom == 4 || sf[i].denom == 2 || sf[i].denom == 1))) _decompTest(handle, jpegBuf, jpegSize, w, h, pf, basename, subsamp, - flags, sf[i]); + sf[i]); } bailout: @@ -535,42 +593,46 @@ static void doTest(int w, int h, const int *formats, int nformats, int subsamp, { tjhandle chandle = NULL, dhandle = NULL; unsigned char *dstBuf = NULL; - unsigned long size = 0; - int pfi, pf, i, quality = 100; + size_t size = 0; + int pfi, pf, i; if (lossless && subsamp != TJSAMP_GRAY) subsamp = TJSAMP_444; if (!alloc) - size = tjBufSize(w, h, subsamp); + size = tj3JPEGBufSize(w, h, subsamp); if (size != 0) - if ((dstBuf = (unsigned char *)tjAlloc(size)) == NULL) + if ((dstBuf = (unsigned char *)tj3Alloc(size)) == NULL) THROW("Memory allocation failure."); - if ((chandle = tjInitCompress()) == NULL || - (dhandle = tjInitDecompress()) == NULL) - THROW_TJ(); + if ((chandle = tj3Init(TJINIT_COMPRESS)) == NULL || + (dhandle = tj3Init(TJINIT_DECOMPRESS)) == NULL) + THROW_TJ(NULL); + + TRY_TJ(chandle, tj3Set(chandle, TJPARAM_NOREALLOC, !alloc)); + if (lossless) { + TRY_TJ(chandle, tj3Set(chandle, TJPARAM_LOSSLESS, lossless)); + TRY_TJ(chandle, tj3Set(chandle, TJPARAM_LOSSLESSPSV, + ((psv++ - 1) % 7) + 1)); + } else { + TRY_TJ(chandle, tj3Set(chandle, TJPARAM_QUALITY, 100)); + if (subsamp == TJSAMP_422 || subsamp == TJSAMP_420 || + subsamp == TJSAMP_440 || subsamp == TJSAMP_411) + TRY_TJ(dhandle, tj3Set(dhandle, TJPARAM_FASTUPSAMPLE, 1)); + } + TRY_TJ(chandle, tj3Set(chandle, TJPARAM_SUBSAMP, subsamp)); for (pfi = 0; pfi < nformats; pfi++) { for (i = 0; i < 2; i++) { - int flags = 0; - - if (lossless) { - flags |= TJFLAG_LOSSLESS; - quality = (((psv++ - 1) % 7) + 1) * 10; - } - if (subsamp == TJSAMP_422 || subsamp == TJSAMP_420 || - subsamp == TJSAMP_440 || subsamp == TJSAMP_411) - flags |= TJFLAG_FASTUPSAMPLE; - if (i == 1) flags |= TJFLAG_BOTTOMUP; + TRY_TJ(chandle, tj3Set(chandle, TJPARAM_BOTTOMUP, i == 1)); + TRY_TJ(dhandle, tj3Set(dhandle, TJPARAM_BOTTOMUP, i == 1)); pf = formats[pfi]; - compTest(chandle, &dstBuf, &size, w, h, pf, basename, subsamp, quality, - flags); - decompTest(dhandle, dstBuf, size, w, h, pf, basename, subsamp, flags); + compTest(chandle, &dstBuf, &size, w, h, pf, basename); + decompTest(dhandle, dstBuf, size, w, h, pf, basename, subsamp); if (pf >= TJPF_RGBX && pf <= TJPF_XRGB) { printf("\n"); decompTest(dhandle, dstBuf, size, w, h, pf + (TJPF_RGBA - TJPF_RGBX), - basename, subsamp, flags); + basename, subsamp); } printf("\n"); } @@ -578,55 +640,74 @@ static void doTest(int w, int h, const int *formats, int nformats, int subsamp, printf("--------------------\n\n"); bailout: - if (chandle) tjDestroy(chandle); - if (dhandle) tjDestroy(dhandle); - tjFree(dstBuf); + tj3Destroy(chandle); + tj3Destroy(dhandle); + tj3Free(dstBuf); } #if SIZEOF_SIZE_T == 8 #define CHECKSIZE(function) { \ - if ((unsigned long long)size < (unsigned long long)0xFFFFFFFF) \ + if (size && size < (size_t)0xFFFFFFFF) \ + THROW(#function " overflow"); \ +} +#define CHECKSIZEUL(function) { \ + if ((unsigned long long)ulsize < (unsigned long long)0xFFFFFFFF) \ THROW(#function " overflow"); \ } #else #define CHECKSIZE(function) { \ - if (size != (unsigned long)(-1) || \ - !strcmp(tjGetErrorStr2(NULL), "No error")) \ + if (size != 0 || !strcmp(tj3GetErrorStr(NULL), "No error")) \ + THROW(#function " overflow"); \ +} +#define CHECKSIZEUL(function) { \ + if (ulsize != (unsigned long)(-1) || \ + !strcmp(tj3GetErrorStr(NULL), "No error")) \ THROW(#function " overflow"); \ } #endif #define CHECKSIZEINT(function) { \ - if (intsize != -1 || !strcmp(tjGetErrorStr2(NULL), "No error")) \ + if (intsize != 0 || !strcmp(tj3GetErrorStr(NULL), "No error")) \ THROW(#function " overflow"); \ } static void overflowTest(void) { /* Ensure that the various buffer size functions don't overflow */ - unsigned long size; + size_t size; + unsigned long ulsize; int intsize; - size = tjBufSize(26755, 26755, TJSAMP_444); - CHECKSIZE(tjBufSize()); - size = TJBUFSIZE(26755, 26755); - CHECKSIZE(TJBUFSIZE()); - size = tjBufSizeYUV2(37838, 1, 37838, TJSAMP_444); - CHECKSIZE(tjBufSizeYUV2()); - size = tjBufSizeYUV2(37837, 3, 37837, TJSAMP_444); - CHECKSIZE(tjBufSizeYUV2()); - size = tjBufSizeYUV2(37837, -1, 37837, TJSAMP_444); - CHECKSIZE(tjBufSizeYUV2()); - size = TJBUFSIZEYUV(37838, 37838, TJSAMP_444); - CHECKSIZE(TJBUFSIZEYUV()); - size = tjBufSizeYUV(37838, 37838, TJSAMP_444); - CHECKSIZE(tjBufSizeYUV()); - size = tjPlaneSizeYUV(0, 65536, 0, 65536, TJSAMP_444); - CHECKSIZE(tjPlaneSizeYUV()); - intsize = tjPlaneWidth(0, INT_MAX, TJSAMP_420); - CHECKSIZEINT(tjPlaneWidth()); - intsize = tjPlaneHeight(0, INT_MAX, TJSAMP_420); - CHECKSIZEINT(tjPlaneHeight()); + size = tj3JPEGBufSize(26755, 26755, TJSAMP_444); + CHECKSIZE(tj3JPEGBufSize()); + ulsize = tjBufSize(26755, 26755, TJSAMP_444); + CHECKSIZEUL(tjBufSize()); + ulsize = TJBUFSIZE(26755, 26755); + CHECKSIZEUL(TJBUFSIZE()); + size = tj3YUVBufSize(37838, 1, 37838, TJSAMP_444); + CHECKSIZE(tj3YUVBufSize()); + size = tj3YUVBufSize(37837, 3, 37837, TJSAMP_444); + CHECKSIZE(tj3YUVBufSize()); + size = tj3YUVBufSize(37837, -1, 37837, TJSAMP_444); + CHECKSIZE(tj3YUVBufSize()); + ulsize = tjBufSizeYUV2(37838, 1, 37838, TJSAMP_444); + CHECKSIZEUL(tjBufSizeYUV2()); + ulsize = tjBufSizeYUV2(37837, 3, 37837, TJSAMP_444); + CHECKSIZEUL(tjBufSizeYUV2()); + ulsize = tjBufSizeYUV2(37837, -1, 37837, TJSAMP_444); + CHECKSIZEUL(tjBufSizeYUV2()); + ulsize = TJBUFSIZEYUV(37838, 37838, TJSAMP_444); + CHECKSIZEUL(TJBUFSIZEYUV()); + ulsize = tjBufSizeYUV(37838, 37838, TJSAMP_444); + CHECKSIZEUL(tjBufSizeYUV()); + size = tj3YUVPlaneSize(0, 65536, 0, 65536, TJSAMP_444); + CHECKSIZE(tj3YUVPlaneSize()); + ulsize = tjPlaneSizeYUV(0, 65536, 0, 65536, TJSAMP_444); + CHECKSIZEUL(tjPlaneSizeYUV()); + intsize = tj3YUVPlaneWidth(0, INT_MAX, TJSAMP_420); + CHECKSIZEINT(tj3YUVPlaneWidth()); + intsize = tj3YUVPlaneHeight(0, INT_MAX, TJSAMP_420); + CHECKSIZEINT(tj3YUVPlaneHeight()); bailout: return; @@ -636,77 +717,98 @@ static void overflowTest(void) static void bufSizeTest(void) { int w, h, i, subsamp; - unsigned char *srcBuf = NULL, *dstBuf = NULL; + void *srcBuf = NULL; + unsigned char *dstBuf = NULL; tjhandle handle = NULL; - unsigned long dstSize = 0; - int flags = 0, quality = 100, numSamp = TJ_NUMSAMP; + size_t dstSize = 0; + int numSamp = TJ_NUMSAMP; - if (!alloc) flags |= TJFLAG_NOREALLOC; + if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL) + THROW_TJ(NULL); + + TRY_TJ(handle, tj3Set(handle, TJPARAM_NOREALLOC, !alloc)); if (lossless) { - flags |= TJFLAG_LOSSLESS; - quality = (((psv++ - 1) % 7) + 1) * 10; + TRY_TJ(handle, tj3Set(handle, TJPARAM_LOSSLESS, lossless)); + TRY_TJ(handle, tj3Set(handle, TJPARAM_LOSSLESSPSV, + ((psv++ - 1) % 7) + 1)); numSamp = 1; - } - - if ((handle = tjInitCompress()) == NULL) THROW_TJ(); + } else + TRY_TJ(handle, tj3Set(handle, TJPARAM_QUALITY, 100)); printf("Buffer size regression test\n"); for (subsamp = 0; subsamp < numSamp; subsamp++) { + TRY_TJ(handle, tj3Set(handle, TJPARAM_SUBSAMP, subsamp)); for (w = 1; w < 48; w++) { int maxh = (w == 1) ? 2048 : 48; for (h = 1; h < maxh; h++) { if (h % 100 == 0) printf("%.4d x %.4d\b\b\b\b\b\b\b\b\b\b\b", w, h); - if ((srcBuf = (unsigned char *)malloc(w * h * 4)) == NULL) + if ((srcBuf = malloc(w * h * 4 * sampleSize)) == NULL) THROW("Memory allocation failure"); if (!alloc || doYUV) { - if (doYUV) dstSize = tjBufSizeYUV2(w, yuvAlign, h, subsamp); - else dstSize = tjBufSize(w, h, subsamp); - if ((dstBuf = (unsigned char *)tjAlloc(dstSize)) == NULL) + if (doYUV) dstSize = tj3YUVBufSize(w, yuvAlign, h, subsamp); + else dstSize = tj3JPEGBufSize(w, h, subsamp); + if ((dstBuf = (unsigned char *)tj3Alloc(dstSize)) == NULL) THROW("Memory allocation failure"); } for (i = 0; i < w * h * 4; i++) { - if (random() < RAND_MAX / 2) srcBuf[i] = 0; - else srcBuf[i] = 255; + if (random() < RAND_MAX / 2) setVal(srcBuf, i, 0); + else setVal(srcBuf, i, maxSample); } if (doYUV) { - TRY_TJ(tjEncodeYUV3(handle, srcBuf, w, 0, h, TJPF_BGRX, dstBuf, - yuvAlign, subsamp, 0)); + TRY_TJ(handle, tj3EncodeYUV8(handle, (unsigned char *)srcBuf, w, 0, + h, TJPF_BGRX, dstBuf, yuvAlign)); } else { - TRY_TJ(tjCompress2(handle, srcBuf, w, 0, h, TJPF_BGRX, &dstBuf, - &dstSize, subsamp, quality, flags)); + if (precision == 8) { + TRY_TJ(handle, tj3Compress8(handle, (unsigned char *)srcBuf, w, 0, + h, TJPF_BGRX, &dstBuf, &dstSize)); + } else if (precision == 12) { + TRY_TJ(handle, tj3Compress12(handle, (short *)srcBuf, w, 0, h, + TJPF_BGRX, &dstBuf, &dstSize)); + } else { + TRY_TJ(handle, tj3Compress16(handle, (unsigned short *)srcBuf, w, + 0, h, TJPF_BGRX, &dstBuf, &dstSize)); + } } free(srcBuf); srcBuf = NULL; if (!alloc || doYUV) { - tjFree(dstBuf); dstBuf = NULL; + tj3Free(dstBuf); dstBuf = NULL; } - if ((srcBuf = (unsigned char *)malloc(h * w * 4)) == NULL) + if ((srcBuf = malloc(h * w * 4 * sampleSize)) == NULL) THROW("Memory allocation failure"); if (!alloc || doYUV) { - if (doYUV) dstSize = tjBufSizeYUV2(h, yuvAlign, w, subsamp); - else dstSize = tjBufSize(h, w, subsamp); - if ((dstBuf = (unsigned char *)tjAlloc(dstSize)) == NULL) + if (doYUV) dstSize = tj3YUVBufSize(h, yuvAlign, w, subsamp); + else dstSize = tj3JPEGBufSize(h, w, subsamp); + if ((dstBuf = (unsigned char *)tj3Alloc(dstSize)) == NULL) THROW("Memory allocation failure"); } for (i = 0; i < h * w * 4; i++) { - if (random() < RAND_MAX / 2) srcBuf[i] = 0; - else srcBuf[i] = 255; + if (random() < RAND_MAX / 2) setVal(srcBuf, i, 0); + else setVal(srcBuf, i, maxSample); } if (doYUV) { - TRY_TJ(tjEncodeYUV3(handle, srcBuf, h, 0, w, TJPF_BGRX, dstBuf, - yuvAlign, subsamp, 0)); + TRY_TJ(handle, tj3EncodeYUV8(handle, (unsigned char *)srcBuf, h, 0, + w, TJPF_BGRX, dstBuf, yuvAlign)); } else { - TRY_TJ(tjCompress2(handle, srcBuf, h, 0, w, TJPF_BGRX, &dstBuf, - &dstSize, subsamp, quality, flags)); + if (precision == 8) { + TRY_TJ(handle, tj3Compress8(handle, (unsigned char *)srcBuf, h, 0, + w, TJPF_BGRX, &dstBuf, &dstSize)); + } else if (precision == 12) { + TRY_TJ(handle, tj3Compress12(handle, (short *)srcBuf, h, 0, w, + TJPF_BGRX, &dstBuf, &dstSize)); + } else { + TRY_TJ(handle, tj3Compress16(handle, (unsigned short *)srcBuf, h, + 0, w, TJPF_BGRX, &dstBuf, &dstSize)); + } } free(srcBuf); srcBuf = NULL; if (!alloc || doYUV) { - tjFree(dstBuf); dstBuf = NULL; + tj3Free(dstBuf); dstBuf = NULL; } } } @@ -715,47 +817,78 @@ static void bufSizeTest(void) bailout: free(srcBuf); - tjFree(dstBuf); - if (handle) tjDestroy(handle); + tj3Free(dstBuf); + tj3Destroy(handle); } -static void initBitmap(unsigned char *buf, int width, int pitch, int height, - int pf, int flags) +static void rgb_to_cmyk(int r, int g, int b, int *c, int *m, int *y, int *k) +{ + double ctmp = 1.0 - ((double)r / (double)maxSample); + double mtmp = 1.0 - ((double)g / (double)maxSample); + double ytmp = 1.0 - ((double)b / (double)maxSample); + double ktmp = min(min(ctmp, mtmp), ytmp); + + if (ktmp == 1.0) ctmp = mtmp = ytmp = 0.0; + else { + ctmp = (ctmp - ktmp) / (1.0 - ktmp); + mtmp = (mtmp - ktmp) / (1.0 - ktmp); + ytmp = (ytmp - ktmp) / (1.0 - ktmp); + } + *c = (int)((double)maxSample - ctmp * (double)maxSample + 0.5); + *m = (int)((double)maxSample - mtmp * (double)maxSample + 0.5); + *y = (int)((double)maxSample - ytmp * (double)maxSample + 0.5); + *k = (int)((double)maxSample - ktmp * (double)maxSample + 0.5); +} + +static void initBitmap(void *buf, int width, int pitch, int height, int pf, + int bottomUp) { int roffset = tjRedOffset[pf]; int goffset = tjGreenOffset[pf]; int boffset = tjBlueOffset[pf]; int ps = tjPixelSize[pf]; - int i, j; + int i, j, ci; for (j = 0; j < height; j++) { - int row = (flags & TJFLAG_BOTTOMUP) ? height - j - 1 : j; + int row = bottomUp ? height - j - 1 : j; for (i = 0; i < width; i++) { - unsigned char r = (i * 256 / width) % 256; - unsigned char g = (j * 256 / height) % 256; - unsigned char b = (j * 256 / height + i * 256 / width) % 256; - - memset(&buf[row * pitch + i * ps], 0, ps); - if (pf == TJPF_GRAY) buf[row * pitch + i * ps] = b; - else if (pf == TJPF_CMYK) - rgb_to_cmyk(r, g, b, &buf[row * pitch + i * ps + 0], - &buf[row * pitch + i * ps + 1], - &buf[row * pitch + i * ps + 2], - &buf[row * pitch + i * ps + 3]); - else { - buf[row * pitch + i * ps + roffset] = r; - buf[row * pitch + i * ps + goffset] = g; - buf[row * pitch + i * ps + boffset] = b; + int r = (i * (maxSample + 1) / width) % (maxSample + 1); + int g = (j * (maxSample + 1) / height) % (maxSample + 1); + int b = (j * (maxSample + 1) / height + + i * (maxSample + 1) / width) % (maxSample + 1); + + for (ci = 0; ci < ps; ci++) + setVal(buf, row * pitch + i * ps + ci, 0); + if (pf == TJPF_GRAY) setVal(buf, row * pitch + i * ps, b); + else if (pf == TJPF_CMYK) { + int c, m, y, k; + + rgb_to_cmyk(r, g, b, &c, &m, &y, &k); + setVal(buf, row * pitch + i * ps + 0, c); + setVal(buf, row * pitch + i * ps + 1, m); + setVal(buf, row * pitch + i * ps + 2, y); + setVal(buf, row * pitch + i * ps + 3, k); + } else { + setVal(buf, row * pitch + i * ps + roffset, r); + setVal(buf, row * pitch + i * ps + goffset, g); + setVal(buf, row * pitch + i * ps + boffset, b); } } } } -static int cmpBitmap(unsigned char *buf, int width, int pitch, int height, - int pf, int flags, int gray2rgb) +static void cmyk_to_rgb(int c, int m, int y, int k, int *r, int *g, int *b) +{ + *r = (int)((double)c * (double)k / (double)maxSample + 0.5); + *g = (int)((double)m * (double)k / (double)maxSample + 0.5); + *b = (int)((double)y * (double)k / (double)maxSample + 0.5); +} + +static int cmpBitmap(void *buf, int width, int pitch, int height, int pf, + int bottomUp, int gray2rgb) { int roffset = tjRedOffset[pf]; int goffset = tjGreenOffset[pf]; @@ -765,38 +898,40 @@ static int cmpBitmap(unsigned char *buf, int width, int pitch, int height, int i, j; for (j = 0; j < height; j++) { - int row = (flags & TJFLAG_BOTTOMUP) ? height - j - 1 : j; + int row = bottomUp ? height - j - 1 : j; for (i = 0; i < width; i++) { - unsigned char r = (i * 256 / width) % 256; - unsigned char g = (j * 256 / height) % 256; - unsigned char b = (j * 256 / height + i * 256 / width) % 256; + int r = (i * (maxSample + 1) / width) % (maxSample + 1); + int g = (j * (maxSample + 1) / height) % (maxSample + 1); + int b = (j * (maxSample + 1) / height + + i * (maxSample + 1) / width) % (maxSample + 1); if (pf == TJPF_GRAY) { - if (buf[row * pitch + i * ps] != b) + if (getVal(buf, row * pitch + i * ps) != b) return 0; } else if (pf == TJPF_CMYK) { - unsigned char rf, gf, bf; + int rf, gf, bf; - cmyk_to_rgb(buf[row * pitch + i * ps + 0], - buf[row * pitch + i * ps + 1], - buf[row * pitch + i * ps + 2], - buf[row * pitch + i * ps + 3], &rf, &gf, &bf); + cmyk_to_rgb(getVal(buf, row * pitch + i * ps + 0), + getVal(buf, row * pitch + i * ps + 1), + getVal(buf, row * pitch + i * ps + 2), + getVal(buf, row * pitch + i * ps + 3), &rf, &gf, &bf); if (gray2rgb) { if (rf != b || gf != b || bf != b) return 0; } else if (rf != r || gf != g || bf != b) return 0; } else { if (gray2rgb) { - if (buf[row * pitch + i * ps + roffset] != b || - buf[row * pitch + i * ps + goffset] != b || - buf[row * pitch + i * ps + boffset] != b) + if (getVal(buf, row * pitch + i * ps + roffset) != b || + getVal(buf, row * pitch + i * ps + goffset) != b || + getVal(buf, row * pitch + i * ps + boffset) != b) return 0; - } else if (buf[row * pitch + i * ps + roffset] != r || - buf[row * pitch + i * ps + goffset] != g || - buf[row * pitch + i * ps + boffset] != b) + } else if (getVal(buf, row * pitch + i * ps + roffset) != r || + getVal(buf, row * pitch + i * ps + goffset) != g || + getVal(buf, row * pitch + i * ps + boffset) != b) return 0; - if (aoffset >= 0 && buf[row * pitch + i * ps + aoffset] != 0xFF) + if (aoffset >= 0 && + getVal(buf, row * pitch + i * ps + aoffset) != maxSample) return 0; } } @@ -806,89 +941,154 @@ static int cmpBitmap(unsigned char *buf, int width, int pitch, int height, static int doBmpTest(const char *ext, int width, int align, int height, int pf, - int flags) + int bottomUp) { + tjhandle handle = NULL; char filename[80], *md5sum, md5buf[65]; int ps = tjPixelSize[pf], pitch = PAD(width * ps, align), loadWidth = 0, loadHeight = 0, retval = 0, pixelFormat = pf; - unsigned char *buf = NULL; + void *buf = NULL; char *md5ref; + if ((handle = tj3Init(TJINIT_TRANSFORM)) == NULL) + THROW_TJ(NULL); + TRY_TJ(handle, tj3Set(handle, TJPARAM_BOTTOMUP, bottomUp)); + if (pf == TJPF_GRAY) { - md5ref = !strcasecmp(ext, "ppm") ? "112c682e82ce5de1cca089e20d60000b" : - "51976530acf75f02beddf5d21149101d"; + if (precision == 8) + md5ref = !strcasecmp(ext, "ppm") ? "112c682e82ce5de1cca089e20d60000b" : + "51976530acf75f02beddf5d21149101d"; + else if (precision == 12) + md5ref = "0d1895c7e6f2b2c9af6e821a655c239c"; + else + md5ref = "64f3320b226ea37fb58080713b4df1b2"; } else { - md5ref = !strcasecmp(ext, "ppm") ? "c0c9f772b464d1896326883a5c79c545" : - "6d659071b9bfcdee2def22cb58ddadca"; + if (precision == 8) + md5ref = !strcasecmp(ext, "ppm") ? "c0c9f772b464d1896326883a5c79c545" : + "6d659071b9bfcdee2def22cb58ddadca"; + else if (precision == 12) + md5ref = "2ff5299287017502832c99718450c90a"; + else + md5ref = "623f54661b928d170bd2324bc3620565"; } - if ((buf = (unsigned char *)tjAlloc(pitch * height)) == NULL) + if ((buf = tj3Alloc(pitch * height * sampleSize)) == NULL) THROW("Could not allocate memory"); - initBitmap(buf, width, pitch, height, pf, flags); - - SNPRINTF(filename, 80, "test_bmp_%s_%d_%s.%s", pixFormatStr[pf], align, - (flags & TJFLAG_BOTTOMUP) ? "bu" : "td", ext); - TRY_TJ(tjSaveImage(filename, buf, width, pitch, height, pf, flags)); + initBitmap(buf, width, pitch, height, pf, bottomUp); + + SNPRINTF(filename, 80, "test_bmp%d_%s_%d_%s.%s", precision, pixFormatStr[pf], + align, bottomUp ? "bu" : "td", ext); + if (precision == 8) { + TRY_TJ(handle, tj3SaveImage8(handle, filename, (unsigned char *)buf, width, + pitch, height, pf)); + } else if (precision == 12) { + TRY_TJ(handle, tj3SaveImage12(handle, filename, (short *)buf, width, pitch, + height, pf)); + } else { + TRY_TJ(handle, tj3SaveImage16(handle, filename, (unsigned short *)buf, + width, pitch, height, pf)); + } md5sum = MD5File(filename, md5buf); if (strcasecmp(md5sum, md5ref)) THROW_MD5(filename, md5sum, md5ref); - tjFree(buf); buf = NULL; - if ((buf = tjLoadImage(filename, &loadWidth, align, &loadHeight, &pf, - flags)) == NULL) - THROW_TJ(); + tj3Free(buf); buf = NULL; + if (precision == 8) { + if ((buf = tj3LoadImage8(handle, filename, &loadWidth, align, &loadHeight, + &pf)) == NULL) + THROW_TJ(handle); + } else if (precision == 12) { + if ((buf = tj3LoadImage12(handle, filename, &loadWidth, align, &loadHeight, + &pf)) == NULL) + THROW_TJ(handle); + } else { + if ((buf = tj3LoadImage16(handle, filename, &loadWidth, align, &loadHeight, + &pf)) == NULL) + THROW_TJ(handle); + } if (width != loadWidth || height != loadHeight) { printf("\n Image dimensions of %s are bogus\n", filename); retval = -1; goto bailout; } - if (!cmpBitmap(buf, width, pitch, height, pf, flags, 0)) { + if (!cmpBitmap(buf, width, pitch, height, pf, bottomUp, 0)) { printf("\n Pixel data in %s is bogus\n", filename); retval = -1; goto bailout; } if (pf == TJPF_GRAY) { - tjFree(buf); buf = NULL; + tj3Free(buf); buf = NULL; pf = TJPF_XBGR; - if ((buf = tjLoadImage(filename, &loadWidth, align, &loadHeight, &pf, - flags)) == NULL) - THROW_TJ(); + if (precision == 8) { + if ((buf = tj3LoadImage8(handle, filename, &loadWidth, align, + &loadHeight, &pf)) == NULL) + THROW_TJ(handle); + } else if (precision == 12) { + if ((buf = tj3LoadImage12(handle, filename, &loadWidth, align, + &loadHeight, &pf)) == NULL) + THROW_TJ(handle); + } else { + if ((buf = tj3LoadImage16(handle, filename, &loadWidth, align, + &loadHeight, &pf)) == NULL) + THROW_TJ(handle); + } pitch = PAD(width * tjPixelSize[pf], align); - if (!cmpBitmap(buf, width, pitch, height, pf, flags, 1)) { + if (!cmpBitmap(buf, width, pitch, height, pf, bottomUp, 1)) { printf("\n Converting %s to RGB failed\n", filename); retval = -1; goto bailout; } - tjFree(buf); buf = NULL; + tj3Free(buf); buf = NULL; pf = TJPF_CMYK; - if ((buf = tjLoadImage(filename, &loadWidth, align, &loadHeight, &pf, - flags)) == NULL) - THROW_TJ(); + if (precision == 8) { + if ((buf = tj3LoadImage8(handle, filename, &loadWidth, align, + &loadHeight, &pf)) == NULL) + THROW_TJ(handle); + } else if (precision == 12) { + if ((buf = tj3LoadImage12(handle, filename, &loadWidth, align, + &loadHeight, &pf)) == NULL) + THROW_TJ(handle); + } else { + if ((buf = tj3LoadImage16(handle, filename, &loadWidth, align, + &loadHeight, &pf)) == NULL) + THROW_TJ(handle); + } pitch = PAD(width * tjPixelSize[pf], align); - if (!cmpBitmap(buf, width, pitch, height, pf, flags, 1)) { + if (!cmpBitmap(buf, width, pitch, height, pf, bottomUp, 1)) { printf("\n Converting %s to CMYK failed\n", filename); retval = -1; goto bailout; } } - /* Verify that tjLoadImage() returns the proper "preferred" pixel format for - the file type. */ - tjFree(buf); buf = NULL; + /* Verify that tj3LoadImage*() returns the proper "preferred" pixel format + for the file type. */ + tj3Free(buf); buf = NULL; pf = pixelFormat; pixelFormat = TJPF_UNKNOWN; - if ((buf = tjLoadImage(filename, &loadWidth, align, &loadHeight, - &pixelFormat, flags)) == NULL) - THROW_TJ(); + if (precision == 8) { + if ((buf = tj3LoadImage8(handle, filename, &loadWidth, align, &loadHeight, + &pixelFormat)) == NULL) + THROW_TJ(handle); + } else if (precision == 12) { + if ((buf = tj3LoadImage12(handle, filename, &loadWidth, align, &loadHeight, + &pixelFormat)) == NULL) + THROW_TJ(handle); + } else { + if ((buf = tj3LoadImage16(handle, filename, &loadWidth, align, &loadHeight, + &pixelFormat)) == NULL) + THROW_TJ(handle); + } if ((pf == TJPF_GRAY && pixelFormat != TJPF_GRAY) || (pf != TJPF_GRAY && !strcasecmp(ext, "bmp") && pixelFormat != TJPF_BGR) || (pf != TJPF_GRAY && !strcasecmp(ext, "ppm") && pixelFormat != TJPF_RGB)) { - printf("\n tjLoadImage() returned unexpected pixel format: %s\n", + printf("\n tj3LoadImage8() returned unexpected pixel format: %s\n", pixFormatStr[pixelFormat]); retval = -1; } unlink(filename); bailout: - tjFree(buf); + tj3Destroy(handle); + tj3Free(buf); if (exitStatus < 0) return exitStatus; return retval; } @@ -900,29 +1100,31 @@ static int bmpTest(void) for (align = 1; align <= 8; align *= 2) { for (format = 0; format < TJ_NUMPF; format++) { - printf("%s Top-Down BMP (row alignment = %d bytes) ... ", - pixFormatStr[format], align); - if (doBmpTest("bmp", width, align, height, format, 0) == -1) - return -1; - printf("OK.\n"); + if (precision == 8) { + printf("%s Top-Down BMP (row alignment = %d samples) ... ", + pixFormatStr[format], align); + if (doBmpTest("bmp", width, align, height, format, 0) == -1) + return -1; + printf("OK.\n"); + } - printf("%s Top-Down PPM (row alignment = %d bytes) ... ", + printf("%s Top-Down PPM (row alignment = %d samples) ... ", pixFormatStr[format], align); - if (doBmpTest("ppm", width, align, height, format, - TJFLAG_BOTTOMUP) == -1) + if (doBmpTest("ppm", width, align, height, format, 1) == -1) return -1; printf("OK.\n"); - printf("%s Bottom-Up BMP (row alignment = %d bytes) ... ", - pixFormatStr[format], align); - if (doBmpTest("bmp", width, align, height, format, 0) == -1) - return -1; - printf("OK.\n"); + if (precision == 8) { + printf("%s Bottom-Up BMP (row alignment = %d samples) ... ", + pixFormatStr[format], align); + if (doBmpTest("bmp", width, align, height, format, 0) == -1) + return -1; + printf("OK.\n"); + } - printf("%s Bottom-Up PPM (row alignment = %d bytes) ... ", + printf("%s Bottom-Up PPM (row alignment = %d samples) ... ", pixFormatStr[format], align); - if (doBmpTest("ppm", width, align, height, format, - TJFLAG_BOTTOMUP) == -1) + if (doBmpTest("ppm", width, align, height, format, 1) == -1) return -1; printf("OK.\n"); } @@ -934,7 +1136,7 @@ static int bmpTest(void) int main(int argc, char *argv[]) { - int i, num4bf = 5; + int i, bmp = 0, num4bf = 5; #ifdef _WIN32 srand((unsigned int)time(NULL)); @@ -945,31 +1147,50 @@ int main(int argc, char *argv[]) else if (!strcasecmp(argv[i], "-noyuvpad")) yuvAlign = 1; else if (!strcasecmp(argv[i], "-lossless")) lossless = 1; else if (!strcasecmp(argv[i], "-alloc")) alloc = 1; - else if (!strcasecmp(argv[i], "-bmp")) return bmpTest(); - else usage(argv[0]); + else if (!strcasecmp(argv[i], "-bmp")) bmp = 1; + else if (!strcasecmp(argv[i], "-precision") && i < argc - 1) { + int tempi = atoi(argv[++i]); + + if (tempi != 8 && tempi != 12 && tempi != 16) + usage(argv[0]); + precision = tempi; + if (precision == 16) lossless = 1; + } else + usage(argv[0]); } } if (lossless && doYUV) THROW("Lossless JPEG and YUV encoding/decoding are incompatible."); + if (precision != 8 && doYUV) + THROW("YUV encoding/decoding requires 8-bit data precision."); + + printf("Testing %d-bit precision\n", precision); + sampleSize = (precision == 8 ? sizeof(unsigned char) : sizeof(short)); + maxSample = (1 << precision) - 1; + tolerance = (lossless ? 0 : (precision > 8 ? 2 : 1)); + redToY = (19595U * maxSample) >> 16; + yellowToY = (58065U * maxSample) >> 16; + + if (bmp) return bmpTest(); if (alloc) printf("Testing automatic buffer allocation\n"); if (doYUV) num4bf = 4; overflowTest(); - doTest(35, 39, _3byteFormats, 2, TJSAMP_444, "test"); - doTest(39, 41, _4byteFormats, num4bf, TJSAMP_444, "test"); - doTest(41, 35, _3byteFormats, 2, TJSAMP_422, "test"); + doTest(35, 39, _3sampleFormats, 2, TJSAMP_444, "test"); + doTest(39, 41, _4sampleFormats, num4bf, TJSAMP_444, "test"); + doTest(41, 35, _3sampleFormats, 2, TJSAMP_422, "test"); if (!lossless) { - doTest(35, 39, _4byteFormats, num4bf, TJSAMP_422, "test"); - doTest(39, 41, _3byteFormats, 2, TJSAMP_420, "test"); - doTest(41, 35, _4byteFormats, num4bf, TJSAMP_420, "test"); - doTest(35, 39, _3byteFormats, 2, TJSAMP_440, "test"); - doTest(39, 41, _4byteFormats, num4bf, TJSAMP_440, "test"); - doTest(41, 35, _3byteFormats, 2, TJSAMP_411, "test"); - doTest(35, 39, _4byteFormats, num4bf, TJSAMP_411, "test"); + doTest(35, 39, _4sampleFormats, num4bf, TJSAMP_422, "test"); + doTest(39, 41, _3sampleFormats, 2, TJSAMP_420, "test"); + doTest(41, 35, _4sampleFormats, num4bf, TJSAMP_420, "test"); + doTest(35, 39, _3sampleFormats, 2, TJSAMP_440, "test"); + doTest(39, 41, _4sampleFormats, num4bf, TJSAMP_440, "test"); + doTest(41, 35, _3sampleFormats, 2, TJSAMP_411, "test"); + doTest(35, 39, _4sampleFormats, num4bf, TJSAMP_411, "test"); } doTest(39, 41, _onlyGray, 1, TJSAMP_GRAY, "test"); if (!lossless) { - doTest(41, 35, _3byteFormats, 2, TJSAMP_GRAY, "test"); - doTest(35, 39, _4byteFormats, 4, TJSAMP_GRAY, "test"); + doTest(41, 35, _3sampleFormats, 2, TJSAMP_GRAY, "test"); + doTest(35, 39, _4sampleFormats, 4, TJSAMP_GRAY, "test"); } bufSizeTest(); if (doYUV) { diff --git a/turbojpeg-jni.c b/turbojpeg-jni.c index aa6611ef0..d9531a254 100644 --- a/turbojpeg-jni.c +++ b/turbojpeg-jni.c @@ -61,13 +61,13 @@ jobject _excobj; \ jstring _errstr; \ \ - BAILIF0(_errstr = (*env)->NewStringUTF(env, tjGetErrorStr2(handle))); \ + BAILIF0(_errstr = (*env)->NewStringUTF(env, tj3GetErrorStr(handle))); \ BAILIF0(_exccls = (*env)->FindClass(env, \ "org/libjpegturbo/turbojpeg/TJException")); \ BAILIF0(_excid = (*env)->GetMethodID(env, _exccls, "", \ "(Ljava/lang/String;I)V")); \ BAILIF0(_excobj = (*env)->NewObject(env, _exccls, _excid, _errstr, \ - tjGetErrorCode(handle))); \ + tj3GetErrorCode(handle))); \ (*env)->Throw(env, _excobj); \ goto bailout; \ } @@ -85,58 +85,20 @@ BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "handle", "J")); \ handle = (tjhandle)(size_t)(*env)->GetLongField(env, obj, _fid); -#ifndef NO_PUTENV -#define PROP2ENV(property, envvar) { \ - if ((jName = (*env)->NewStringUTF(env, property)) != NULL) { \ - jboolean exception; \ - jValue = (*env)->CallStaticObjectMethod(env, cls, mid, jName); \ - exception = (*env)->ExceptionCheck(env); \ - if (jValue && !exception && \ - (value = (*env)->GetStringUTFChars(env, jValue, 0)) != NULL) { \ - PUTENV_S(envvar, value); \ - (*env)->ReleaseStringUTFChars(env, jValue, value); \ - } \ - } \ -} -#endif - #define SAFE_RELEASE(javaArray, cArray) { \ if (javaArray && cArray) \ (*env)->ReleasePrimitiveArrayCritical(env, javaArray, (void *)cArray, 0); \ cArray = NULL; \ } -static int ProcessSystemProperties(JNIEnv *env) -{ - jclass cls; - jmethodID mid; - jstring jName, jValue; - const char *value; - - BAILIF0(cls = (*env)->FindClass(env, "java/lang/System")); - BAILIF0(mid = (*env)->GetStaticMethodID(env, cls, "getProperty", - "(Ljava/lang/String;)Ljava/lang/String;")); - -#ifndef NO_PUTENV - PROP2ENV("turbojpeg.optimize", "TJ_OPTIMIZE"); - PROP2ENV("turbojpeg.arithmetic", "TJ_ARITHMETIC"); - PROP2ENV("turbojpeg.restart", "TJ_RESTART"); - PROP2ENV("turbojpeg.progressive", "TJ_PROGRESSIVE"); -#endif - return 0; - -bailout: - return -1; -} - /* TurboJPEG 1.2.x: TJ::bufSize() */ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSize (JNIEnv *env, jclass cls, jint width, jint height, jint jpegSubsamp) { - unsigned long retval = tjBufSize(width, height, jpegSubsamp); + size_t retval = tj3JPEGBufSize(width, height, jpegSubsamp); - if (retval == (unsigned long)-1) THROW_ARG(tjGetErrorStr()); - if (retval > (unsigned long)INT_MAX) + if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL)); + if (retval > (size_t)INT_MAX) THROW_ARG("Image is too large"); bailout: @@ -147,10 +109,10 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSize JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSizeYUV__IIII (JNIEnv *env, jclass cls, jint width, jint align, jint height, jint subsamp) { - unsigned long retval = tjBufSizeYUV2(width, align, height, subsamp); + size_t retval = tj3YUVBufSize(width, align, height, subsamp); - if (retval == (unsigned long)-1) THROW_ARG(tjGetErrorStr()); - if (retval > (unsigned long)INT_MAX) + if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL)); + if (retval > (size_t)INT_MAX) THROW_ARG("Image is too large"); bailout: @@ -162,11 +124,10 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_planeSizeYUV__IIIII (JNIEnv *env, jclass cls, jint componentID, jint width, jint stride, jint height, jint subsamp) { - unsigned long retval = tjPlaneSizeYUV(componentID, width, stride, height, - subsamp); + size_t retval = tj3YUVPlaneSize(componentID, width, stride, height, subsamp); - if (retval == (unsigned long)-1) THROW_ARG(tjGetErrorStr()); - if (retval > (unsigned long)INT_MAX) + if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL)); + if (retval > (size_t)INT_MAX) THROW_ARG("Image is too large"); bailout: @@ -177,9 +138,9 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_planeSizeYUV__IIIII JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_planeWidth__III (JNIEnv *env, jclass cls, jint componentID, jint width, jint subsamp) { - jint retval = (jint)tjPlaneWidth(componentID, width, subsamp); + jint retval = (jint)tj3YUVPlaneWidth(componentID, width, subsamp); - if (retval == -1) THROW_ARG(tjGetErrorStr()); + if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL)); bailout: return retval; @@ -189,9 +150,9 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_planeWidth__III JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_planeHeight__III (JNIEnv *env, jclass cls, jint componentID, jint height, jint subsamp) { - jint retval = (jint)tjPlaneHeight(componentID, height, subsamp); + jint retval = (jint)tj3YUVPlaneHeight(componentID, height, subsamp); - if (retval == -1) THROW_ARG(tjGetErrorStr()); + if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL)); bailout: return retval; @@ -205,8 +166,8 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_init jfieldID fid; tjhandle handle; - if ((handle = tjInitCompress()) == NULL) - THROW(tjGetErrorStr(), "org/libjpegturbo/turbojpeg/TJException"); + if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL) + THROW(tj3GetErrorStr(NULL), "org/libjpegturbo/turbojpeg/TJException"); BAILIF0(cls = (*env)->GetObjectClass(env, obj)); BAILIF0(fid = (*env)->GetFieldID(env, cls, "handle", "J")); @@ -216,21 +177,52 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_init return; } +/* TurboJPEG 3: TJCompressor::set() */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_set + (JNIEnv *env, jobject obj, jint param, jint value) +{ + tjhandle handle = 0; + + GET_HANDLE(); + + if (tj3Set(handle, param, value) == -1) + THROW_TJ(); + +bailout: + return; +} + +/* TurboJPEG 3: TJCompressor::get() */ +JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_get + (JNIEnv *env, jobject obj, jint param) +{ + tjhandle handle = 0; + + GET_HANDLE(); + + return tj3Get(handle, param); + +bailout: + return -1; +} + static jint TJCompressor_compress - (JNIEnv *env, jobject obj, jarray src, jint srcElementSize, jint x, jint y, - jint width, jint pitch, jint height, jint pf, jbyteArray dst, - jint jpegSubsamp, jint jpegQual, jint flags) + (JNIEnv *env, jobject obj, jarray src, jint srcElementSize, jint precision, + jint x, jint y, jint width, jint pitch, jint height, jint pf, + jbyteArray dst) { tjhandle handle = 0; - unsigned long jpegSize = 0; + size_t jpegSize = 0; jsize arraySize = 0, actualPitch; - unsigned char *srcBuf = NULL, *jpegBuf = NULL; + void *srcBuf = NULL; + unsigned char *jpegBuf = NULL; + int jpegSubsamp; GET_HANDLE(); if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF || width < 1 || height < 1 || pitch < 0) - THROW_ARG("Invalid argument in compress()"); + THROW_ARG("Invalid argument in compress*()"); if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF) THROW_ARG("Mismatch between Java and C API"); @@ -238,23 +230,45 @@ static jint TJCompressor_compress arraySize = (y + height - 1) * actualPitch + (x + width) * tjPixelSize[pf]; if ((*env)->GetArrayLength(env, src) * srcElementSize < arraySize) THROW_ARG("Source buffer is not large enough"); - if (flags & TJFLAG_LOSSLESS && jpegSubsamp != TJSAMP_GRAY) + jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP); + if (tj3Get(handle, TJPARAM_LOSSLESS) && jpegSubsamp != TJSAMP_GRAY) jpegSubsamp = TJSAMP_444; - jpegSize = tjBufSize(width, height, jpegSubsamp); + else if (jpegSubsamp == TJSAMP_UNKNOWN) + THROW_ARG("TJPARAM_SUBSAMP must be specified"); + jpegSize = tj3JPEGBufSize(width, height, jpegSubsamp); if ((*env)->GetArrayLength(env, dst) < (jsize)jpegSize) THROW_ARG("Destination buffer is not large enough"); - if (ProcessSystemProperties(env) < 0) goto bailout; + if (tj3Set(handle, TJPARAM_NOREALLOC, 1) == -1) + THROW_TJ(); BAILIF0NOEC(srcBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0)); BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0)); - if (tjCompress2(handle, &srcBuf[y * actualPitch + x * tjPixelSize[pf]], - width, pitch, height, pf, &jpegBuf, &jpegSize, jpegSubsamp, - jpegQual, flags | TJFLAG_NOREALLOC) == -1) { - SAFE_RELEASE(dst, jpegBuf); - SAFE_RELEASE(src, srcBuf); - THROW_TJ(); + if (precision == 8) { + if (tj3Compress8(handle, &((unsigned char *)srcBuf)[y * actualPitch + + x * tjPixelSize[pf]], + width, pitch, height, pf, &jpegBuf, &jpegSize) == -1) { + SAFE_RELEASE(dst, jpegBuf); + SAFE_RELEASE(src, srcBuf); + THROW_TJ(); + } + } else if (precision == 12) { + if (tj3Compress12(handle, &((short *)srcBuf)[y * actualPitch + + x * tjPixelSize[pf]], + width, pitch, height, pf, &jpegBuf, &jpegSize) == -1) { + SAFE_RELEASE(dst, jpegBuf); + SAFE_RELEASE(src, srcBuf); + THROW_TJ(); + } + } else { + if (tj3Compress16(handle, &((unsigned short *)srcBuf)[y * actualPitch + + x * tjPixelSize[pf]], + width, pitch, height, pf, &jpegBuf, &jpegSize) == -1) { + SAFE_RELEASE(dst, jpegBuf); + SAFE_RELEASE(src, srcBuf); + THROW_TJ(); + } } bailout: @@ -263,58 +277,73 @@ static jint TJCompressor_compress return (jint)jpegSize; } -/* TurboJPEG 1.3.x: TJCompressor::compress() byte source */ -JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3BIIIIII_3BIII +/* TurboJPEG 3: TJCompressor::compress8() byte source */ +JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress8___3BIIIIII_3B (JNIEnv *env, jobject obj, jbyteArray src, jint x, jint y, jint width, - jint pitch, jint height, jint pf, jbyteArray dst, jint jpegSubsamp, - jint jpegQual, jint flags) + jint pitch, jint height, jint pf, jbyteArray dst) { - return TJCompressor_compress(env, obj, src, 1, x, y, width, pitch, height, - pf, dst, jpegSubsamp, jpegQual, flags); + return TJCompressor_compress(env, obj, src, 1, 8, x, y, width, pitch, height, + pf, dst); } -/* TurboJPEG 1.3.x: TJCompressor::compress() int source */ -JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3IIIIIII_3BIII +/* TurboJPEG 3: TJCompressor::compress12() */ +JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress12 + (JNIEnv *env, jobject obj, jshortArray src, jint x, jint y, jint width, + jint pitch, jint height, jint pf, jbyteArray dst) +{ + return TJCompressor_compress(env, obj, src, 1, 12, x, y, width, pitch, + height, pf, dst); +} + +/* TurboJPEG 3: TJCompressor::compress16() */ +JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress16 + (JNIEnv *env, jobject obj, jshortArray src, jint x, jint y, jint width, + jint pitch, jint height, jint pf, jbyteArray dst) +{ + return TJCompressor_compress(env, obj, src, 1, 16, x, y, width, pitch, + height, pf, dst); +} + +/* TurboJPEG 3: TJCompressor::compress8() int source */ +JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress8___3IIIIIII_3B (JNIEnv *env, jobject obj, jintArray src, jint x, jint y, jint width, - jint stride, jint height, jint pf, jbyteArray dst, jint jpegSubsamp, - jint jpegQual, jint flags) + jint stride, jint height, jint pf, jbyteArray dst) { if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF) - THROW_ARG("Invalid argument in compress()"); + THROW_ARG("Invalid argument in compress8()"); if (tjPixelSize[pf] != sizeof(jint)) THROW_ARG("Pixel format must be 32-bit when compressing from an integer buffer."); - return TJCompressor_compress(env, obj, src, sizeof(jint), x, y, width, - stride * sizeof(jint), height, pf, dst, - jpegSubsamp, jpegQual, flags); + return TJCompressor_compress(env, obj, src, sizeof(jint), 8, x, y, width, + stride * sizeof(jint), height, pf, dst); bailout: return 0; } -/* TurboJPEG 1.4.x: TJCompressor::compressFromYUV() */ -JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFromYUV___3_3B_3II_3III_3BII +/* TurboJPEG 3: TJCompressor::compressFromYUV8() */ +JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFromYUV8 (JNIEnv *env, jobject obj, jobjectArray srcobjs, jintArray jSrcOffsets, - jint width, jintArray jSrcStrides, jint height, jint subsamp, - jbyteArray dst, jint jpegQual, jint flags) + jint width, jintArray jSrcStrides, jint height, jbyteArray dst) { tjhandle handle = 0; - unsigned long jpegSize = 0; + size_t jpegSize = 0; jbyteArray jSrcPlanes[3] = { NULL, NULL, NULL }; const unsigned char *srcPlanesTmp[3] = { NULL, NULL, NULL }; const unsigned char *srcPlanes[3] = { NULL, NULL, NULL }; jint srcOffsetsTmp[3] = { 0, 0, 0 }, srcStridesTmp[3] = { 0, 0, 0 }; int srcOffsets[3] = { 0, 0, 0 }, srcStrides[3] = { 0, 0, 0 }; unsigned char *jpegBuf = NULL; - int nc = (subsamp == org_libjpegturbo_turbojpeg_TJ_SAMP_GRAY ? 1 : 3), i; + int nc = 0, i, subsamp; GET_HANDLE(); - if (subsamp < 0 || subsamp >= org_libjpegturbo_turbojpeg_TJ_NUMSAMP) - THROW_ARG("Invalid argument in compressFromYUV()"); if (org_libjpegturbo_turbojpeg_TJ_NUMSAMP != TJ_NUMSAMP) THROW_ARG("Mismatch between Java and C API"); + if ((subsamp = tj3Get(handle, TJPARAM_SUBSAMP)) == TJSAMP_UNKNOWN) + THROW_ARG("TJPARAM_SUBSAMP must be specified"); + nc = subsamp == TJSAMP_GRAY ? 1 : 3; if ((*env)->GetArrayLength(env, srcobjs) < nc) THROW_ARG("Planes array is too small for the subsampling type"); if ((*env)->GetArrayLength(env, jSrcOffsets) < nc) @@ -322,11 +351,12 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFrom if ((*env)->GetArrayLength(env, jSrcStrides) < nc) THROW_ARG("Strides array is too small for the subsampling type"); - jpegSize = tjBufSize(width, height, subsamp); + jpegSize = tj3JPEGBufSize(width, height, subsamp); if ((*env)->GetArrayLength(env, dst) < (jsize)jpegSize) THROW_ARG("Destination buffer is not large enough"); - if (ProcessSystemProperties(env) < 0) goto bailout; + if (tj3Set(handle, TJPARAM_NOREALLOC, 1) == -1) + THROW_TJ(); (*env)->GetIntArrayRegion(env, jSrcOffsets, 0, nc, srcOffsetsTmp); if ((*env)->ExceptionCheck(env)) goto bailout; @@ -339,20 +369,23 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFrom srcStrides[i] = srcStridesTmp[i]; for (i = 0; i < nc; i++) { - int planeSize = tjPlaneSizeYUV(i, width, srcStrides[i], height, subsamp); - int pw = tjPlaneWidth(i, width, subsamp); + size_t planeSize = tj3YUVPlaneSize(i, width, srcStrides[i], height, + subsamp); + int pw = tj3YUVPlaneWidth(i, width, subsamp); - if (planeSize < 0 || pw < 0) - THROW_ARG(tjGetErrorStr()); + if (planeSize == 0 || pw == 0) + THROW_ARG(tj3GetErrorStr(NULL)); + if (planeSize > (size_t)INT_MAX) + THROW_ARG("Source plane is too large"); if (srcOffsets[i] < 0) - THROW_ARG("Invalid argument in compressFromYUV()"); - if (srcStrides[i] < 0 && srcOffsets[i] - planeSize + pw < 0) + THROW_ARG("Invalid argument in compressFromYUV8()"); + if (srcStrides[i] < 0 && srcOffsets[i] - (int)planeSize + pw < 0) THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary"); BAILIF0(jSrcPlanes[i] = (*env)->GetObjectArrayElement(env, srcobjs, i)); if ((*env)->GetArrayLength(env, jSrcPlanes[i]) < - srcOffsets[i] + planeSize) + srcOffsets[i] + (int)planeSize) THROW_ARG("Source plane is not large enough"); } for (i = 0; i < nc; i++) { @@ -362,9 +395,8 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFrom } BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0)); - if (tjCompressFromYUVPlanes(handle, srcPlanes, width, srcStrides, height, - subsamp, &jpegBuf, &jpegSize, jpegQual, - flags | TJFLAG_NOREALLOC) == -1) { + if (tj3CompressFromYUVPlanes8(handle, srcPlanes, width, srcStrides, height, + &jpegBuf, &jpegSize) == -1) { SAFE_RELEASE(dst, jpegBuf); for (i = 0; i < nc; i++) SAFE_RELEASE(jSrcPlanes[i], srcPlanesTmp[i]); @@ -378,10 +410,10 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFrom return (jint)jpegSize; } -static void TJCompressor_encodeYUV +static void TJCompressor_encodeYUV8 (JNIEnv *env, jobject obj, jarray src, jint srcElementSize, jint x, jint y, jint width, jint pitch, jint height, jint pf, jobjectArray dstobjs, - jintArray jDstOffsets, jintArray jDstStrides, jint subsamp, jint flags) + jintArray jDstOffsets, jintArray jDstStrides) { tjhandle handle = 0; jsize arraySize = 0, actualPitch; @@ -391,18 +423,20 @@ static void TJCompressor_encodeYUV unsigned char *dstPlanes[3] = { NULL, NULL, NULL }; jint dstOffsetsTmp[3] = { 0, 0, 0 }, dstStridesTmp[3] = { 0, 0, 0 }; int dstOffsets[3] = { 0, 0, 0 }, dstStrides[3] = { 0, 0, 0 }; - int nc = (subsamp == org_libjpegturbo_turbojpeg_TJ_SAMP_GRAY ? 1 : 3), i; + int nc = 0, i, subsamp; GET_HANDLE(); if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF || width < 1 || - height < 1 || pitch < 0 || subsamp < 0 || - subsamp >= org_libjpegturbo_turbojpeg_TJ_NUMSAMP) - THROW_ARG("Invalid argument in encodeYUV()"); + height < 1 || pitch < 0) + THROW_ARG("Invalid argument in encodeYUV8()"); if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF || org_libjpegturbo_turbojpeg_TJ_NUMSAMP != TJ_NUMSAMP) THROW_ARG("Mismatch between Java and C API"); + if ((subsamp = tj3Get(handle, TJPARAM_SUBSAMP)) == TJSAMP_UNKNOWN) + THROW_ARG("TJPARAM_SUBSAMP must be specified"); + nc = subsamp == TJSAMP_GRAY ? 1 : 3; if ((*env)->GetArrayLength(env, dstobjs) < nc) THROW_ARG("Planes array is too small for the subsampling type"); if ((*env)->GetArrayLength(env, jDstOffsets) < nc) @@ -426,20 +460,23 @@ static void TJCompressor_encodeYUV dstStrides[i] = dstStridesTmp[i]; for (i = 0; i < nc; i++) { - int planeSize = tjPlaneSizeYUV(i, width, dstStrides[i], height, subsamp); - int pw = tjPlaneWidth(i, width, subsamp); + size_t planeSize = tj3YUVPlaneSize(i, width, dstStrides[i], height, + subsamp); + int pw = tj3YUVPlaneWidth(i, width, subsamp); - if (planeSize < 0 || pw < 0) - THROW_ARG(tjGetErrorStr()); + if (planeSize == 0 || pw == 0) + THROW_ARG(tj3GetErrorStr(NULL)); + if (planeSize > (size_t)INT_MAX) + THROW_ARG("Destination plane is too large"); if (dstOffsets[i] < 0) - THROW_ARG("Invalid argument in encodeYUV()"); - if (dstStrides[i] < 0 && dstOffsets[i] - planeSize + pw < 0) + THROW_ARG("Invalid argument in encodeYUV8()"); + if (dstStrides[i] < 0 && dstOffsets[i] - (int)planeSize + pw < 0) THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary"); BAILIF0(jDstPlanes[i] = (*env)->GetObjectArrayElement(env, dstobjs, i)); if ((*env)->GetArrayLength(env, jDstPlanes[i]) < - dstOffsets[i] + planeSize) + dstOffsets[i] + (int)planeSize) THROW_ARG("Destination plane is not large enough"); } for (i = 0; i < nc; i++) { @@ -449,9 +486,10 @@ static void TJCompressor_encodeYUV } BAILIF0NOEC(srcBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0)); - if (tjEncodeYUVPlanes(handle, &srcBuf[y * actualPitch + x * tjPixelSize[pf]], - width, pitch, height, pf, dstPlanes, dstStrides, - subsamp, flags) == -1) { + if (tj3EncodeYUVPlanes8(handle, + &srcBuf[y * actualPitch + x * tjPixelSize[pf]], + width, pitch, height, pf, dstPlanes, + dstStrides) == -1) { SAFE_RELEASE(src, srcBuf); for (i = 0; i < nc; i++) SAFE_RELEASE(jDstPlanes[i], dstPlanesTmp[i]); @@ -464,30 +502,30 @@ static void TJCompressor_encodeYUV SAFE_RELEASE(jDstPlanes[i], dstPlanesTmp[i]); } -/* TurboJPEG 1.4.x: TJCompressor::encodeYUV() byte source */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3BIIIIII_3_3B_3I_3III +/* TurboJPEG 3: TJCompressor::encodeYUV8() byte source */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV8___3BIIIIII_3_3B_3I_3I (JNIEnv *env, jobject obj, jbyteArray src, jint x, jint y, jint width, jint pitch, jint height, jint pf, jobjectArray dstobjs, - jintArray jDstOffsets, jintArray jDstStrides, jint subsamp, jint flags) + jintArray jDstOffsets, jintArray jDstStrides) { - TJCompressor_encodeYUV(env, obj, src, 1, x, y, width, pitch, height, pf, - dstobjs, jDstOffsets, jDstStrides, subsamp, flags); + TJCompressor_encodeYUV8(env, obj, src, 1, x, y, width, pitch, height, pf, + dstobjs, jDstOffsets, jDstStrides); } -/* TurboJPEG 1.4.x: TJCompressor::encodeYUV() int source */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3IIIIIII_3_3B_3I_3III +/* TurboJPEG 3: TJCompressor::encodeYUV8() int source */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV8___3IIIIIII_3_3B_3I_3I (JNIEnv *env, jobject obj, jintArray src, jint x, jint y, jint width, jint stride, jint height, jint pf, jobjectArray dstobjs, - jintArray jDstOffsets, jintArray jDstStrides, jint subsamp, jint flags) + jintArray jDstOffsets, jintArray jDstStrides) { if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF) - THROW_ARG("Invalid argument in encodeYUV()"); + THROW_ARG("Invalid argument in encodeYUV8()"); if (tjPixelSize[pf] != sizeof(jint)) THROW_ARG("Pixel format must be 32-bit when encoding from an integer buffer."); - TJCompressor_encodeYUV(env, obj, src, sizeof(jint), x, y, width, - stride * sizeof(jint), height, pf, dstobjs, - jDstOffsets, jDstStrides, subsamp, flags); + TJCompressor_encodeYUV8(env, obj, src, sizeof(jint), x, y, width, + stride * sizeof(jint), height, pf, dstobjs, + jDstOffsets, jDstStrides); bailout: return; @@ -501,7 +539,7 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_destroy GET_HANDLE(); - if (tjDestroy(handle) == -1) THROW_TJ(); + tj3Destroy(handle); (*env)->SetLongField(env, obj, _fid, 0); bailout: @@ -516,8 +554,8 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_init jfieldID fid; tjhandle handle; - if ((handle = tjInitDecompress()) == NULL) - THROW(tjGetErrorStr(), "org/libjpegturbo/turbojpeg/TJException"); + if ((handle = tj3Init(TJINIT_DECOMPRESS)) == NULL) + THROW(tj3GetErrorStr(NULL), "org/libjpegturbo/turbojpeg/TJException"); BAILIF0(cls = (*env)->GetObjectClass(env, obj)); BAILIF0(fid = (*env)->GetFieldID(env, cls, "handle", "J")); @@ -527,6 +565,20 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_init return; } +/* TurboJPEG 3: TJDecompressor::set() */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_set + (JNIEnv *env, jobject obj, jint param, jint value) +{ + Java_org_libjpegturbo_turbojpeg_TJCompressor_set(env, obj, param, value); +} + +/* TurboJPEG 3: TJDecompressor::get() */ +JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_get + (JNIEnv *env, jobject obj, jint param) +{ + return Java_org_libjpegturbo_turbojpeg_TJCompressor_get(env, obj, param); +} + /* TurboJPEG 1.2.x: TJDecompressor::getScalingFactors() */ JNIEXPORT jobjectArray JNICALL Java_org_libjpegturbo_turbojpeg_TJ_getScalingFactors (JNIEnv *env, jclass cls) @@ -538,8 +590,8 @@ JNIEXPORT jobjectArray JNICALL Java_org_libjpegturbo_turbojpeg_TJ_getScalingFact jobject sfobj = NULL; jobjectArray sfjava = NULL; - if ((sf = tjGetScalingFactors(&n)) == NULL || n == 0) - THROW_ARG(tjGetErrorStr()); + if ((sf = tj3GetScalingFactors(&n)) == NULL || n == 0) + THROW_ARG(tj3GetErrorStr(NULL)); BAILIF0(sfcls = (*env)->FindClass(env, "org/libjpegturbo/turbojpeg/TJScalingFactor")); @@ -564,8 +616,6 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress { tjhandle handle = 0; unsigned char *jpegBuf = NULL; - int width = 0, height = 0, jpegSubsamp = -1, jpegColorspace = -1, - jpegFlags = -1; GET_HANDLE(); @@ -574,66 +624,158 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0)); - if (tjDecompressHeader4(handle, jpegBuf, (unsigned long)jpegSize, &width, - &height, &jpegSubsamp, &jpegColorspace, - &jpegFlags) == -1) { + if (tj3DecompressHeader(handle, jpegBuf, (size_t)jpegSize) == -1) { SAFE_RELEASE(src, jpegBuf); THROW_TJ(); } +bailout: SAFE_RELEASE(src, jpegBuf); +} - BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegSubsamp", "I")); - (*env)->SetIntField(env, obj, _fid, jpegSubsamp); - if ((_fid = (*env)->GetFieldID(env, _cls, "jpegColorspace", "I")) == 0) - (*env)->ExceptionClear(env); - else - (*env)->SetIntField(env, obj, _fid, jpegColorspace); - if ((_fid = (*env)->GetFieldID(env, _cls, "jpegFlags", "I")) == 0) - (*env)->ExceptionClear(env); - else - (*env)->SetIntField(env, obj, _fid, jpegFlags); - BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegWidth", "I")); - (*env)->SetIntField(env, obj, _fid, width); - BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegHeight", "I")); - (*env)->SetIntField(env, obj, _fid, height); +/* TurboJPEG 3: TJDecompressor::setCroppingRegion() */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_setCroppingRegion + (JNIEnv *env, jobject obj) +{ + tjhandle handle = 0; + jclass sfcls, crcls; + jobject sfobj, crobj; + tjregion croppingRegion; + tjscalingfactor scalingFactor; + + GET_HANDLE(); + + BAILIF0(sfcls = (*env)->FindClass(env, + "org/libjpegturbo/turbojpeg/TJScalingFactor")); + BAILIF0(_fid = + (*env)->GetFieldID(env, _cls, "scalingFactor", + "Lorg/libjpegturbo/turbojpeg/TJScalingFactor;")); + BAILIF0(sfobj = (*env)->GetObjectField(env, obj, _fid)); + BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "num", "I")); + scalingFactor.num = (*env)->GetIntField(env, sfobj, _fid); + BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "denom", "I")); + scalingFactor.denom = (*env)->GetIntField(env, sfobj, _fid); + + if (tj3SetScalingFactor(handle, scalingFactor) == -1) + THROW_TJ(); + + BAILIF0(crcls = (*env)->FindClass(env, "java/awt/Rectangle")); + BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "croppingRegion", + "Ljava/awt/Rectangle;")); + BAILIF0(crobj = (*env)->GetObjectField(env, obj, _fid)); + BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "x", "I")); + croppingRegion.x = (*env)->GetIntField(env, crobj, _fid); + BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "y", "I")); + croppingRegion.y = (*env)->GetIntField(env, crobj, _fid); + BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "width", "I")); + croppingRegion.w = (*env)->GetIntField(env, crobj, _fid); + BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "height", "I")); + croppingRegion.h = (*env)->GetIntField(env, crobj, _fid); + + if (tj3SetCroppingRegion(handle, croppingRegion) == -1) + THROW_TJ(); bailout: - SAFE_RELEASE(src, jpegBuf); + return; } static void TJDecompressor_decompress (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jarray dst, - jint dstElementSize, jint x, jint y, jint width, jint pitch, jint height, - jint pf, jint flags) + jint dstElementSize, int precision, jint x, jint y, jint pitch, jint pf) { tjhandle handle = 0; jsize arraySize = 0, actualPitch; - unsigned char *jpegBuf = NULL, *dstBuf = NULL; + unsigned char *jpegBuf = NULL; + void *dstBuf = NULL; + jclass sfcls, crcls; + jobject sfobj, crobj; + tjscalingfactor scalingFactor; + tjregion cr; + int jpegWidth, jpegHeight, scaledWidth, scaledHeight; GET_HANDLE(); if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF) - THROW_ARG("Invalid argument in decompress()"); + THROW_ARG("Invalid argument in decompress*()"); if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF) THROW_ARG("Mismatch between Java and C API"); if ((*env)->GetArrayLength(env, src) < jpegSize) THROW_ARG("Source buffer is not large enough"); - actualPitch = (pitch == 0) ? width * tjPixelSize[pf] : pitch; - arraySize = (y + height - 1) * actualPitch + (x + width) * tjPixelSize[pf]; + if ((jpegWidth = tj3Get(handle, TJPARAM_JPEGWIDTH)) == -1) + THROW_ARG("JPEG header has not yet been read"); + if ((jpegHeight = tj3Get(handle, TJPARAM_JPEGHEIGHT)) == -1) + THROW_ARG("JPEG header has not yet been read"); + + BAILIF0(sfcls = (*env)->FindClass(env, + "org/libjpegturbo/turbojpeg/TJScalingFactor")); + BAILIF0(_fid = + (*env)->GetFieldID(env, _cls, "scalingFactor", + "Lorg/libjpegturbo/turbojpeg/TJScalingFactor;")); + BAILIF0(sfobj = (*env)->GetObjectField(env, obj, _fid)); + BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "num", "I")); + scalingFactor.num = (*env)->GetIntField(env, sfobj, _fid); + BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "denom", "I")); + scalingFactor.denom = (*env)->GetIntField(env, sfobj, _fid); + + if (tj3SetScalingFactor(handle, scalingFactor) == -1) + THROW_TJ(); + scaledWidth = TJSCALED(jpegWidth, scalingFactor); + scaledHeight = TJSCALED(jpegHeight, scalingFactor); + + BAILIF0(crcls = (*env)->FindClass(env, "java/awt/Rectangle")); + BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "croppingRegion", + "Ljava/awt/Rectangle;")); + BAILIF0(crobj = (*env)->GetObjectField(env, obj, _fid)); + BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "x", "I")); + cr.x = (*env)->GetIntField(env, crobj, _fid); + BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "y", "I")); + cr.y = (*env)->GetIntField(env, crobj, _fid); + BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "width", "I")); + cr.w = (*env)->GetIntField(env, crobj, _fid); + BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "height", "I")); + cr.h = (*env)->GetIntField(env, crobj, _fid); + if (cr.x != 0 || cr.y != 0 || cr.w != 0 || cr.h != 0) { + scaledWidth = cr.w ? cr.w : scaledWidth - cr.x; + scaledHeight = cr.h ? cr.h : scaledHeight - cr.y; + } + + actualPitch = (pitch == 0) ? scaledWidth * tjPixelSize[pf] : pitch; + arraySize = (y + scaledHeight - 1) * actualPitch + + (x + scaledWidth) * tjPixelSize[pf]; if ((*env)->GetArrayLength(env, dst) * dstElementSize < arraySize) THROW_ARG("Destination buffer is not large enough"); BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0)); BAILIF0NOEC(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0)); - if (tjDecompress2(handle, jpegBuf, (unsigned long)jpegSize, - &dstBuf[y * actualPitch + x * tjPixelSize[pf]], width, - pitch, height, pf, flags) == -1) { - SAFE_RELEASE(dst, dstBuf); - SAFE_RELEASE(src, jpegBuf); - THROW_TJ(); + if (precision == 8) { + if (tj3Decompress8(handle, jpegBuf, (size_t)jpegSize, + &((unsigned char *)dstBuf)[y * actualPitch + + x * tjPixelSize[pf]], + pitch, pf) == -1) { + SAFE_RELEASE(dst, dstBuf); + SAFE_RELEASE(src, jpegBuf); + THROW_TJ(); + } + } else if (precision == 12) { + if (tj3Decompress12(handle, jpegBuf, (size_t)jpegSize, + &((short *)dstBuf)[y * actualPitch + + x * tjPixelSize[pf]], + pitch, pf) == -1) { + SAFE_RELEASE(dst, dstBuf); + SAFE_RELEASE(src, jpegBuf); + THROW_TJ(); + } + } else { + if (tj3Decompress16(handle, jpegBuf, (size_t)jpegSize, + &((unsigned short *)dstBuf)[y * actualPitch + + x * tjPixelSize[pf]], + pitch, pf) == -1) { + SAFE_RELEASE(dst, dstBuf); + SAFE_RELEASE(src, jpegBuf); + THROW_TJ(); + } } bailout: @@ -641,37 +783,54 @@ static void TJDecompressor_decompress SAFE_RELEASE(src, jpegBuf); } -/* TurboJPEG 1.3.x: TJDecompressor::decompress() byte destination */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress___3BI_3BIIIIIII +/* TurboJPEG 3: TJDecompressor::decompress8() byte destination */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress8___3BI_3BIIII (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jbyteArray dst, - jint x, jint y, jint width, jint pitch, jint height, jint pf, jint flags) + jint x, jint y, jint pitch, jint pf) +{ + TJDecompressor_decompress(env, obj, src, jpegSize, dst, 1, 8, x, y, pitch, + pf); +} + +/* TurboJPEG 3: TJDecompressor::decompress12() */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress12 + (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jshortArray dst, + jint x, jint y, jint pitch, jint pf) +{ + TJDecompressor_decompress(env, obj, src, jpegSize, dst, 1, 12, x, y, pitch, + pf); +} + +/* TurboJPEG 3: TJDecompressor::decompress16() */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress16 + (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jshortArray dst, + jint x, jint y, jint pitch, jint pf) { - TJDecompressor_decompress(env, obj, src, jpegSize, dst, 1, x, y, width, - pitch, height, pf, flags); + TJDecompressor_decompress(env, obj, src, jpegSize, dst, 1, 16, x, y, pitch, + pf); } -/* TurboJPEG 1.3.x: TJDecompressor::decompress() int destination */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress___3BI_3IIIIIIII +/* TurboJPEG 3: TJDecompressor::decompress8() int destination */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress8___3BI_3IIIII (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jintArray dst, - jint x, jint y, jint width, jint stride, jint height, jint pf, jint flags) + jint x, jint y, jint stride, jint pf) { if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF) - THROW_ARG("Invalid argument in decompress()"); + THROW_ARG("Invalid argument in decompress8()"); if (tjPixelSize[pf] != sizeof(jint)) THROW_ARG("Pixel format must be 32-bit when decompressing to an integer buffer."); - TJDecompressor_decompress(env, obj, src, jpegSize, dst, sizeof(jint), x, y, - width, stride * sizeof(jint), height, pf, flags); + TJDecompressor_decompress(env, obj, src, jpegSize, dst, sizeof(jint), 8, x, + y, stride * sizeof(jint), pf); bailout: return; } -/* TurboJPEG 1.4.x: TJDecompressor::decompressToYUV() */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressToYUV___3BI_3_3B_3II_3III +/* TurboJPEG 3: TJDecompressor::decompressToYUV8() */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressToYUV8 (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, - jobjectArray dstobjs, jintArray jDstOffsets, jint desiredWidth, - jintArray jDstStrides, jint desiredHeight, jint flags) + jobjectArray dstobjs, jintArray jDstOffsets, jintArray jDstStrides) { tjhandle handle = 0; unsigned char *jpegBuf = NULL; @@ -680,38 +839,40 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress unsigned char *dstPlanes[3] = { NULL, NULL, NULL }; jint dstOffsetsTmp[3] = { 0, 0, 0 }, dstStridesTmp[3] = { 0, 0, 0 }; int dstOffsets[3] = { 0, 0, 0 }, dstStrides[3] = { 0, 0, 0 }; - int jpegSubsamp = -1, jpegWidth = 0, jpegHeight = 0; - int nc = 0, i, width, height, scaledWidth, scaledHeight, nsf = 0; - tjscalingfactor *sf; + jclass sfcls; + jobject sfobj; + int jpegSubsamp, jpegWidth = 0, jpegHeight = 0; + int nc = 0, i, scaledWidth, scaledHeight; + tjscalingfactor scalingFactor; GET_HANDLE(); if ((*env)->GetArrayLength(env, src) < jpegSize) THROW_ARG("Source buffer is not large enough"); - BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegSubsamp", "I")); - jpegSubsamp = (int)(*env)->GetIntField(env, obj, _fid); - BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegWidth", "I")); - jpegWidth = (int)(*env)->GetIntField(env, obj, _fid); - BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegHeight", "I")); - jpegHeight = (int)(*env)->GetIntField(env, obj, _fid); - - nc = (jpegSubsamp == org_libjpegturbo_turbojpeg_TJ_SAMP_GRAY ? 1 : 3); - - width = desiredWidth; - height = desiredHeight; - if (width == 0) width = jpegWidth; - if (height == 0) height = jpegHeight; - sf = tjGetScalingFactors(&nsf); - if (!sf || nsf < 1) - THROW_ARG(tjGetErrorStr()); - for (i = 0; i < nsf; i++) { - scaledWidth = TJSCALED(jpegWidth, sf[i]); - scaledHeight = TJSCALED(jpegHeight, sf[i]); - if (scaledWidth <= width && scaledHeight <= height) - break; - } - if (i >= nsf) - THROW_ARG("Could not scale down to desired image dimensions"); + if ((jpegWidth = tj3Get(handle, TJPARAM_JPEGWIDTH)) == -1) + THROW_ARG("JPEG header has not yet been read"); + if ((jpegHeight = tj3Get(handle, TJPARAM_JPEGHEIGHT)) == -1) + THROW_ARG("JPEG header has not yet been read"); + + BAILIF0(sfcls = (*env)->FindClass(env, + "org/libjpegturbo/turbojpeg/TJScalingFactor")); + BAILIF0(_fid = + (*env)->GetFieldID(env, _cls, "scalingFactor", + "Lorg/libjpegturbo/turbojpeg/TJScalingFactor;")); + BAILIF0(sfobj = (*env)->GetObjectField(env, obj, _fid)); + BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "num", "I")); + scalingFactor.num = (*env)->GetIntField(env, sfobj, _fid); + BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "denom", "I")); + scalingFactor.denom = (*env)->GetIntField(env, sfobj, _fid); + + if (tj3SetScalingFactor(handle, scalingFactor) == -1) + THROW_TJ(); + scaledWidth = TJSCALED(jpegWidth, scalingFactor); + scaledHeight = TJSCALED(jpegHeight, scalingFactor); + + if ((jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP)) == TJSAMP_UNKNOWN) + THROW_ARG("TJPARAM_SUBSAMP must be specified"); + nc = jpegSubsamp == TJSAMP_GRAY ? 1 : 3; (*env)->GetIntArrayRegion(env, jDstOffsets, 0, nc, dstOffsetsTmp); if ((*env)->ExceptionCheck(env)) goto bailout; @@ -724,21 +885,23 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress dstStrides[i] = dstStridesTmp[i]; for (i = 0; i < nc; i++) { - int planeSize = tjPlaneSizeYUV(i, scaledWidth, dstStrides[i], scaledHeight, - jpegSubsamp); - int pw = tjPlaneWidth(i, scaledWidth, jpegSubsamp); + size_t planeSize = tj3YUVPlaneSize(i, scaledWidth, dstStrides[i], + scaledHeight, jpegSubsamp); + int pw = tj3YUVPlaneWidth(i, scaledWidth, jpegSubsamp); - if (planeSize < 0 || pw < 0) - THROW_ARG(tjGetErrorStr()); + if (planeSize == 0 || pw == 0) + THROW_ARG(tj3GetErrorStr(NULL)); + if (planeSize > (size_t)INT_MAX) + THROW_ARG("Destination plane is too large"); if (dstOffsets[i] < 0) - THROW_ARG("Invalid argument in decompressToYUV()"); - if (dstStrides[i] < 0 && dstOffsets[i] - planeSize + pw < 0) + THROW_ARG("Invalid argument in decompressToYUV8()"); + if (dstStrides[i] < 0 && dstOffsets[i] - (int)planeSize + pw < 0) THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary"); BAILIF0(jDstPlanes[i] = (*env)->GetObjectArrayElement(env, dstobjs, i)); if ((*env)->GetArrayLength(env, jDstPlanes[i]) < - dstOffsets[i] + planeSize) + dstOffsets[i] + (int)planeSize) THROW_ARG("Destination plane is not large enough"); } for (i = 0; i < nc; i++) { @@ -748,9 +911,8 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress } BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0)); - if (tjDecompressToYUVPlanes(handle, jpegBuf, (unsigned long)jpegSize, - dstPlanes, desiredWidth, dstStrides, - desiredHeight, flags) == -1) { + if (tj3DecompressToYUVPlanes8(handle, jpegBuf, (size_t)jpegSize, dstPlanes, + dstStrides) == -1) { SAFE_RELEASE(src, jpegBuf); for (i = 0; i < nc; i++) SAFE_RELEASE(jDstPlanes[i], dstPlanesTmp[i]); @@ -763,10 +925,10 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress SAFE_RELEASE(jDstPlanes[i], dstPlanesTmp[i]); } -static void TJDecompressor_decodeYUV +static void TJDecompressor_decodeYUV8 (JNIEnv *env, jobject obj, jobjectArray srcobjs, jintArray jSrcOffsets, - jintArray jSrcStrides, jint subsamp, jarray dst, jint dstElementSize, - jint x, jint y, jint width, jint pitch, jint height, jint pf, jint flags) + jintArray jSrcStrides, jarray dst, jint dstElementSize, jint x, jint y, + jint width, jint pitch, jint height, jint pf) { tjhandle handle = 0; jsize arraySize = 0, actualPitch; @@ -776,17 +938,19 @@ static void TJDecompressor_decodeYUV jint srcOffsetsTmp[3] = { 0, 0, 0 }, srcStridesTmp[3] = { 0, 0, 0 }; int srcOffsets[3] = { 0, 0, 0 }, srcStrides[3] = { 0, 0, 0 }; unsigned char *dstBuf = NULL; - int nc = (subsamp == org_libjpegturbo_turbojpeg_TJ_SAMP_GRAY ? 1 : 3), i; + int nc = 0, i, subsamp; GET_HANDLE(); - if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF || subsamp < 0 || - subsamp >= org_libjpegturbo_turbojpeg_TJ_NUMSAMP) - THROW_ARG("Invalid argument in decodeYUV()"); + if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF) + THROW_ARG("Invalid argument in decodeYUV8()"); if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF || org_libjpegturbo_turbojpeg_TJ_NUMSAMP != TJ_NUMSAMP) THROW_ARG("Mismatch between Java and C API"); + if ((subsamp = tj3Get(handle, TJPARAM_SUBSAMP)) == TJSAMP_UNKNOWN) + THROW_ARG("TJPARAM_SUBSAMP must be specified"); + nc = subsamp == TJSAMP_GRAY ? 1 : 3; if ((*env)->GetArrayLength(env, srcobjs) < nc) THROW_ARG("Planes array is too small for the subsampling type"); if ((*env)->GetArrayLength(env, jSrcOffsets) < nc) @@ -810,20 +974,23 @@ static void TJDecompressor_decodeYUV srcStrides[i] = srcStridesTmp[i]; for (i = 0; i < nc; i++) { - int planeSize = tjPlaneSizeYUV(i, width, srcStrides[i], height, subsamp); - int pw = tjPlaneWidth(i, width, subsamp); + size_t planeSize = tj3YUVPlaneSize(i, width, srcStrides[i], height, + subsamp); + int pw = tj3YUVPlaneWidth(i, width, subsamp); - if (planeSize < 0 || pw < 0) - THROW_ARG(tjGetErrorStr()); + if (planeSize == 0 || pw == 0) + THROW_ARG(tj3GetErrorStr(NULL)); + if (planeSize > (size_t)INT_MAX) + THROW_ARG("Source plane is too large"); if (srcOffsets[i] < 0) - THROW_ARG("Invalid argument in decodeYUV()"); - if (srcStrides[i] < 0 && srcOffsets[i] - planeSize + pw < 0) + THROW_ARG("Invalid argument in decodeYUV8()"); + if (srcStrides[i] < 0 && srcOffsets[i] - (int)planeSize + pw < 0) THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary"); BAILIF0(jSrcPlanes[i] = (*env)->GetObjectArrayElement(env, srcobjs, i)); if ((*env)->GetArrayLength(env, jSrcPlanes[i]) < - srcOffsets[i] + planeSize) + srcOffsets[i] + (int)planeSize) THROW_ARG("Source plane is not large enough"); } for (i = 0; i < nc; i++) { @@ -833,9 +1000,9 @@ static void TJDecompressor_decodeYUV } BAILIF0NOEC(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0)); - if (tjDecodeYUVPlanes(handle, srcPlanes, srcStrides, subsamp, - &dstBuf[y * actualPitch + x * tjPixelSize[pf]], width, - pitch, height, pf, flags) == -1) { + if (tj3DecodeYUVPlanes8(handle, srcPlanes, srcStrides, + &dstBuf[y * actualPitch + x * tjPixelSize[pf]], + width, pitch, height, pf) == -1) { SAFE_RELEASE(dst, dstBuf); for (i = 0; i < nc; i++) SAFE_RELEASE(jSrcPlanes[i], srcPlanesTmp[i]); @@ -848,31 +1015,30 @@ static void TJDecompressor_decodeYUV SAFE_RELEASE(jSrcPlanes[i], srcPlanesTmp[i]); } -/* TurboJPEG 1.4.x: TJDecompressor::decodeYUV() byte destination */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV___3_3B_3I_3II_3BIIIIIII +/* TurboJPEG 3: TJDecompressor::decodeYUV8() byte destination */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV8___3_3B_3I_3I_3BIIIIII (JNIEnv *env, jobject obj, jobjectArray srcobjs, jintArray jSrcOffsets, - jintArray jSrcStrides, jint subsamp, jbyteArray dst, jint x, jint y, - jint width, jint pitch, jint height, jint pf, jint flags) + jintArray jSrcStrides, jbyteArray dst, jint x, jint y, jint width, + jint pitch, jint height, jint pf) { - TJDecompressor_decodeYUV(env, obj, srcobjs, jSrcOffsets, jSrcStrides, - subsamp, dst, 1, x, y, width, pitch, height, pf, - flags); + TJDecompressor_decodeYUV8(env, obj, srcobjs, jSrcOffsets, jSrcStrides, dst, + 1, x, y, width, pitch, height, pf); } -/* TurboJPEG 1.4.x: TJDecompressor::decodeYUV() int destination */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV___3_3B_3I_3II_3IIIIIIII +/* TurboJPEG 3: TJDecompressor::decodeYUV8() int destination */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV8___3_3B_3I_3I_3IIIIIII (JNIEnv *env, jobject obj, jobjectArray srcobjs, jintArray jSrcOffsets, - jintArray jSrcStrides, jint subsamp, jintArray dst, jint x, jint y, - jint width, jint stride, jint height, jint pf, jint flags) + jintArray jSrcStrides, jintArray dst, jint x, jint y, jint width, + jint stride, jint height, jint pf) { if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF) - THROW_ARG("Invalid argument in decodeYUV()"); + THROW_ARG("Invalid argument in decodeYUV8()"); if (tjPixelSize[pf] != sizeof(jint)) THROW_ARG("Pixel format must be 32-bit when decoding to an integer buffer."); - TJDecompressor_decodeYUV(env, obj, srcobjs, jSrcOffsets, jSrcStrides, - subsamp, dst, sizeof(jint), x, y, width, - stride * sizeof(jint), height, pf, flags); + TJDecompressor_decodeYUV8(env, obj, srcobjs, jSrcOffsets, jSrcStrides, dst, + sizeof(jint), x, y, width, stride * sizeof(jint), + height, pf); bailout: return; @@ -886,8 +1052,8 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_init jfieldID fid; tjhandle handle; - if ((handle = tjInitTransform()) == NULL) - THROW(tjGetErrorStr(), "org/libjpegturbo/turbojpeg/TJException"); + if ((handle = tj3Init(TJINIT_TRANSFORM)) == NULL) + THROW(tj3GetErrorStr(NULL), "org/libjpegturbo/turbojpeg/TJException"); BAILIF0(cls = (*env)->GetObjectClass(env, obj)); BAILIF0(fid = (*env)->GetFieldID(env, cls, "handle", "J")); @@ -965,12 +1131,12 @@ static int JNICustomFilter(short *coeffs, tjregion arrayRegion, /* TurboJPEG 1.2.x: TJTransformer::transform() */ JNIEXPORT jintArray JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_transform (JNIEnv *env, jobject obj, jbyteArray jsrcBuf, jint jpegSize, - jobjectArray dstobjs, jobjectArray tobjs, jint flags) + jobjectArray dstobjs, jobjectArray tobjs) { tjhandle handle = 0; unsigned char *jpegBuf = NULL, **dstBufs = NULL; jsize n = 0; - unsigned long *dstSizes = NULL; + size_t *dstSizes = NULL; tjtransform *t = NULL; jbyteArray *jdstBufs = NULL; int i, jpegWidth = 0, jpegHeight = 0, jpegSubsamp; @@ -982,12 +1148,12 @@ JNIEXPORT jintArray JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_transf if ((*env)->GetArrayLength(env, jsrcBuf) < jpegSize) THROW_ARG("Source buffer is not large enough"); - BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegWidth", "I")); - jpegWidth = (int)(*env)->GetIntField(env, obj, _fid); - BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegHeight", "I")); - jpegHeight = (int)(*env)->GetIntField(env, obj, _fid); - BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegSubsamp", "I")); - jpegSubsamp = (int)(*env)->GetIntField(env, obj, _fid); + if ((jpegWidth = tj3Get(handle, TJPARAM_JPEGWIDTH)) == -1) + THROW_ARG("JPEG header has not yet been read"); + if ((jpegHeight = tj3Get(handle, TJPARAM_JPEGHEIGHT)) == -1) + THROW_ARG("JPEG header has not yet been read"); + if ((jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP)) == TJSAMP_UNKNOWN) + THROW_ARG("TJPARAM_SUBSAMP must be specified"); n = (*env)->GetArrayLength(env, dstobjs); if (n != (*env)->GetArrayLength(env, tobjs)) @@ -998,7 +1164,7 @@ JNIEXPORT jintArray JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_transf THROW_MEM(); if ((jdstBufs = (jbyteArray *)malloc(sizeof(jbyteArray) * n)) == NULL) THROW_MEM(); - if ((dstSizes = (unsigned long *)malloc(sizeof(unsigned long) * n)) == NULL) + if ((dstSizes = (size_t *)malloc(sizeof(size_t) * n)) == NULL) THROW_MEM(); if ((t = (tjtransform *)malloc(sizeof(tjtransform) * n)) == NULL) THROW_MEM(); @@ -1041,14 +1207,17 @@ JNIEXPORT jintArray JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_transf } } + if (tj3Set(handle, TJPARAM_NOREALLOC, 1) == -1) + THROW_TJ(); + for (i = 0; i < n; i++) { int w = jpegWidth, h = jpegHeight; if (t[i].r.w != 0) w = t[i].r.w; if (t[i].r.h != 0) h = t[i].r.h; BAILIF0(jdstBufs[i] = (*env)->GetObjectArrayElement(env, dstobjs, i)); - if ((unsigned long)(*env)->GetArrayLength(env, jdstBufs[i]) < - tjBufSize(w, h, jpegSubsamp)) + if ((size_t)(*env)->GetArrayLength(env, jdstBufs[i]) < + tj3JPEGBufSize(w, h, jpegSubsamp)) THROW_ARG("Destination buffer is not large enough"); } BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, jsrcBuf, 0)); @@ -1056,8 +1225,7 @@ JNIEXPORT jintArray JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_transf BAILIF0NOEC(dstBufs[i] = (*env)->GetPrimitiveArrayCritical(env, jdstBufs[i], 0)); - if (tjTransform(handle, jpegBuf, jpegSize, n, dstBufs, dstSizes, t, - flags | TJFLAG_NOREALLOC) == -1) { + if (tj3Transform(handle, jpegBuf, jpegSize, n, dstBufs, dstSizes, t) == -1) { for (i = 0; i < n; i++) SAFE_RELEASE(jdstBufs[i], dstBufs[i]); SAFE_RELEASE(jsrcBuf, jpegBuf); @@ -1094,3 +1262,135 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_destroy { Java_org_libjpegturbo_turbojpeg_TJCompressor_destroy(env, obj); } + +/* Private image I/O routines (used only by TJBench) */ +JNIEXPORT jobject JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_loadImage + (JNIEnv *env, jobject obj, jint precision, jstring jfilename, + jintArray jwidth, jint align, jintArray jheight, jintArray jpixelFormat) +{ + tjhandle handle = NULL; + void *dstBuf = NULL, *jdstPtr; + int width, *warr, height, *harr, pixelFormat, *pfarr, n; + const char *filename = NULL; + jboolean isCopy; + jobject jdstBuf = NULL; + + GET_HANDLE(); + + if ((precision != 8 && precision != 12 && precision != 16) || + jfilename == NULL || jwidth == NULL || + (*env)->GetArrayLength(env, jwidth) < 1 || jheight == NULL || + (*env)->GetArrayLength(env, jheight) < 1 || jpixelFormat == NULL || + (*env)->GetArrayLength(env, jpixelFormat) < 1) + THROW_ARG("Invalid argument in loadImage()"); + + BAILIF0NOEC(warr = (*env)->GetPrimitiveArrayCritical(env, jwidth, 0)); + width = warr[0]; + (*env)->ReleasePrimitiveArrayCritical(env, jwidth, warr, 0); + BAILIF0NOEC(harr = (*env)->GetPrimitiveArrayCritical(env, jheight, 0)); + height = harr[0]; + (*env)->ReleasePrimitiveArrayCritical(env, jheight, harr, 0); + BAILIF0NOEC(pfarr = (*env)->GetPrimitiveArrayCritical(env, jpixelFormat, 0)); + pixelFormat = pfarr[0]; + (*env)->ReleasePrimitiveArrayCritical(env, jpixelFormat, pfarr, 0); + BAILIF0(filename = (*env)->GetStringUTFChars(env, jfilename, &isCopy)); + + if (precision == 8) { + if ((dstBuf = tj3LoadImage8(handle, filename, &width, align, &height, + &pixelFormat)) == NULL) + THROW_TJ(); + } else if (precision == 12) { + if ((dstBuf = tj3LoadImage12(handle, filename, &width, align, &height, + &pixelFormat)) == NULL) + THROW_TJ(); + } else { + if ((dstBuf = tj3LoadImage16(handle, filename, &width, align, &height, + &pixelFormat)) == NULL) + THROW_TJ(); + } + + (*env)->ReleaseStringUTFChars(env, jfilename, filename); + filename = NULL; + + if ((unsigned long long)width * (unsigned long long)height * + (unsigned long long)tjPixelSize[pixelFormat] > + (unsigned long long)((unsigned int)-1)) + THROW_ARG("Image is too large"); + + BAILIF0NOEC(warr = (*env)->GetPrimitiveArrayCritical(env, jwidth, 0)); + warr[0] = width; + (*env)->ReleasePrimitiveArrayCritical(env, jwidth, warr, 0); + BAILIF0NOEC(harr = (*env)->GetPrimitiveArrayCritical(env, jheight, 0)); + harr[0] = height; + (*env)->ReleasePrimitiveArrayCritical(env, jheight, harr, 0); + BAILIF0NOEC(pfarr = (*env)->GetPrimitiveArrayCritical(env, jpixelFormat, 0)); + pfarr[0] = pixelFormat; + (*env)->ReleasePrimitiveArrayCritical(env, jpixelFormat, pfarr, 0); + + n = width * height * tjPixelSize[pixelFormat]; + if (precision == 8) + jdstBuf = (*env)->NewByteArray(env, n); + else + jdstBuf = (*env)->NewShortArray(env, n); + BAILIF0NOEC(jdstPtr = (*env)->GetPrimitiveArrayCritical(env, jdstBuf, 0)); + memcpy(jdstPtr, dstBuf, n * (precision > 8 ? 2 : 1)); + (*env)->ReleasePrimitiveArrayCritical(env, jdstBuf, jdstPtr, 0); + +bailout: + if (filename) (*env)->ReleaseStringUTFChars(env, jfilename, filename); + tj3Free(dstBuf); + return jdstBuf; +} + + +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_saveImage + (JNIEnv *env, jobject obj, jint precision, jstring jfilename, + jobject jsrcBuf, jint width, jint pitch, jint height, jint pixelFormat) +{ + tjhandle handle = NULL; + void *srcBuf = NULL, *jsrcPtr; + const char *filename = NULL; + int n; + jboolean isCopy; + + GET_HANDLE(); + + if ((precision != 8 && precision != 12 && precision != 16) || + jfilename == NULL || jsrcBuf == NULL || width < 1 || height < 1 || + pixelFormat < 0 || pixelFormat >= TJ_NUMPF) + THROW_ARG("Invalid argument in saveImage()"); + + if ((unsigned long long)width * (unsigned long long)height * + (unsigned long long)tjPixelSize[pixelFormat] > + (unsigned long long)((unsigned int)-1)) + THROW_ARG("Image is too large"); + n = width * height * tjPixelSize[pixelFormat]; + if ((*env)->GetArrayLength(env, jsrcBuf) < n) + THROW_ARG("Source buffer is not large enough"); + + if ((srcBuf = malloc(n * (precision > 8 ? 2 : 1))) == NULL) + THROW_MEM(); + + BAILIF0NOEC(jsrcPtr = (*env)->GetPrimitiveArrayCritical(env, jsrcBuf, 0)); + memcpy(srcBuf, jsrcPtr, n * (precision > 8 ? 2 : 1)); + (*env)->ReleasePrimitiveArrayCritical(env, jsrcBuf, jsrcPtr, 0); + BAILIF0(filename = (*env)->GetStringUTFChars(env, jfilename, &isCopy)); + + if (precision == 8) { + if (tj3SaveImage8(handle, filename, srcBuf, width, pitch, height, + pixelFormat) == -1) + THROW_TJ(); + } else if (precision == 12) { + if (tj3SaveImage12(handle, filename, srcBuf, width, pitch, height, + pixelFormat) == -1) + THROW_TJ(); + } else { + if (tj3SaveImage16(handle, filename, srcBuf, width, pitch, height, + pixelFormat) == -1) + THROW_TJ(); + } + +bailout: + if (filename) (*env)->ReleaseStringUTFChars(env, jfilename, filename); + free(srcBuf); +} diff --git a/turbojpeg-mapfile b/turbojpeg-mapfile index 9d2b7dafe..6aab87132 100644 --- a/turbojpeg-mapfile +++ b/turbojpeg-mapfile @@ -64,8 +64,45 @@ TURBOJPEG_2.0 tjSaveImage; } TURBOJPEG_1.4; -TURBOJPEG_2.2 +TURBOJPEG_3 { global: - tjDecompressHeader4; + tj3Alloc; + tj3Compress8; + tj3Compress12; + tj3Compress16; + tj3CompressFromYUV8; + tj3CompressFromYUVPlanes8; + tj3DecodeYUV8; + tj3DecodeYUVPlanes8; + tj3Decompress8; + tj3Decompress12; + tj3Decompress16; + tj3DecompressHeader; + tj3DecompressToYUV8; + tj3DecompressToYUVPlanes8; + tj3Destroy; + tj3EncodeYUV8; + tj3EncodeYUVPlanes8; + tj3Free; + tj3Get; + tj3GetErrorCode; + tj3GetErrorStr; + tj3GetScalingFactors; + tj3Init; + tj3JPEGBufSize; + tj3LoadImage8; + tj3LoadImage12; + tj3LoadImage16; + tj3SaveImage8; + tj3SaveImage12; + tj3SaveImage16; + tj3Set; + tj3SetCroppingRegion; + tj3SetScalingFactor; + tj3Transform; + tj3YUVBufSize; + tj3YUVPlaneHeight; + tj3YUVPlaneSize; + tj3YUVPlaneWidth; } TURBOJPEG_2.0; diff --git a/turbojpeg-mapfile.jni b/turbojpeg-mapfile.jni index 5e3011383..31be75085 100644 --- a/turbojpeg-mapfile.jni +++ b/turbojpeg-mapfile.jni @@ -36,33 +36,16 @@ TURBOJPEG_1.2 tjInitTransform; tjTransform; Java_org_libjpegturbo_turbojpeg_TJ_bufSize; - Java_org_libjpegturbo_turbojpeg_TJ_bufSizeYUV__III; Java_org_libjpegturbo_turbojpeg_TJ_getScalingFactors; Java_org_libjpegturbo_turbojpeg_TJCompressor_init; - Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3BIIII_3BIII; - Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3IIIII_3BIII; - Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3BIIII_3BII; - Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3IIIII_3BII; Java_org_libjpegturbo_turbojpeg_TJCompressor_destroy; Java_org_libjpegturbo_turbojpeg_TJDecompressor_init; Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressHeader; - Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress___3BI_3BIIIII; - Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress___3BI_3IIIIII; - Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressToYUV___3BI_3BI; Java_org_libjpegturbo_turbojpeg_TJDecompressor_destroy; Java_org_libjpegturbo_turbojpeg_TJTransformer_init; Java_org_libjpegturbo_turbojpeg_TJTransformer_transform; } TURBOJPEG_1.1; -TURBOJPEG_1.3 -{ - global: - Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3BIIIIII_3BIII; - Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3IIIIIII_3BIII; - Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress___3BI_3BIIIIIII; - Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress___3BI_3IIIIIIII; -} TURBOJPEG_1.2; - TURBOJPEG_1.4 { global: @@ -80,16 +63,10 @@ TURBOJPEG_1.4 tjPlaneSizeYUV; tjPlaneWidth; Java_org_libjpegturbo_turbojpeg_TJ_bufSizeYUV__IIII; - Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFromYUV___3_3B_3II_3III_3BII; - Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3BIIIIII_3_3B_3I_3III; - Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3IIIIIII_3_3B_3I_3III; - Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressToYUV___3BI_3_3B_3II_3III; - Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV___3_3B_3I_3II_3BIIIIIII; - Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV___3_3B_3I_3II_3IIIIIIII; Java_org_libjpegturbo_turbojpeg_TJ_planeHeight__III; Java_org_libjpegturbo_turbojpeg_TJ_planeSizeYUV__IIIII; Java_org_libjpegturbo_turbojpeg_TJ_planeWidth__III; -} TURBOJPEG_1.3; +} TURBOJPEG_1.2; TURBOJPEG_2.0 { @@ -100,8 +77,66 @@ TURBOJPEG_2.0 tjSaveImage; } TURBOJPEG_1.4; -TURBOJPEG_2.2 +TURBOJPEG_3 { global: - tjDecompressHeader4; + tj3Alloc; + tj3Compress8; + tj3Compress12; + tj3Compress16; + tj3CompressFromYUV8; + tj3CompressFromYUVPlanes8; + tj3DecodeYUV8; + tj3DecodeYUVPlanes8; + tj3Decompress8; + tj3Decompress12; + tj3Decompress16; + tj3DecompressHeader; + tj3DecompressToYUV8; + tj3DecompressToYUVPlanes8; + tj3Destroy; + tj3EncodeYUV8; + tj3EncodeYUVPlanes8; + tj3Free; + tj3Get; + tj3GetErrorCode; + tj3GetErrorStr; + tj3GetScalingFactors; + tj3Init; + tj3JPEGBufSize; + tj3LoadImage8; + tj3LoadImage12; + tj3LoadImage16; + tj3SaveImage8; + tj3SaveImage12; + tj3SaveImage16; + tj3Set; + tj3SetCroppingRegion; + tj3SetScalingFactor; + tj3Transform; + tj3YUVBufSize; + tj3YUVPlaneHeight; + tj3YUVPlaneSize; + tj3YUVPlaneWidth; + Java_org_libjpegturbo_turbojpeg_TJCompressor_compress8___3BIIIIII_3B; + Java_org_libjpegturbo_turbojpeg_TJCompressor_compress8___3IIIIIII_3B; + Java_org_libjpegturbo_turbojpeg_TJCompressor_compress12; + Java_org_libjpegturbo_turbojpeg_TJCompressor_compress16; + Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFromYUV8; + Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV8___3BIIIIII_3_3B_3I_3I; + Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV8___3IIIIIII_3_3B_3I_3I; + Java_org_libjpegturbo_turbojpeg_TJCompressor_get; + Java_org_libjpegturbo_turbojpeg_TJCompressor_loadImage; + Java_org_libjpegturbo_turbojpeg_TJCompressor_set; + Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV8___3_3B_3I_3I_3BIIIIII; + Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV8___3_3B_3I_3I_3IIIIIII; + Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress8___3BI_3BIIII; + Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress8___3BI_3IIIII; + Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress12; + Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress16; + Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressToYUV8; + Java_org_libjpegturbo_turbojpeg_TJDecompressor_get; + Java_org_libjpegturbo_turbojpeg_TJDecompressor_saveImage; + Java_org_libjpegturbo_turbojpeg_TJDecompressor_set; + Java_org_libjpegturbo_turbojpeg_TJDecompressor_setCroppingRegion; } TURBOJPEG_2.0; diff --git a/turbojpeg-mp.c b/turbojpeg-mp.c new file mode 100644 index 000000000..b4bf1eda8 --- /dev/null +++ b/turbojpeg-mp.c @@ -0,0 +1,493 @@ +/* + * Copyright (C)2009-2023 D. R. Commander. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the libjpeg-turbo Project nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* TurboJPEG API functions that must be compiled for multiple data + precisions */ + +#if BITS_IN_JSAMPLE == 8 +#define _JSAMPLE JSAMPLE +#define _JSAMPROW JSAMPROW +#define _buffer buffer +#define _jinit_read_ppm jinit_read_ppm +#define _jinit_write_ppm jinit_write_ppm +#define _jpeg_crop_scanline jpeg_crop_scanline +#define _jpeg_read_scanlines jpeg_read_scanlines +#define _jpeg_skip_scanlines jpeg_skip_scanlines +#define _jpeg_write_scanlines jpeg_write_scanlines +#elif BITS_IN_JSAMPLE == 12 +#define _JSAMPLE J12SAMPLE +#define _JSAMPROW J12SAMPROW +#define _buffer buffer12 +#define _jinit_read_ppm j12init_read_ppm +#define _jinit_write_ppm j12init_write_ppm +#define _jpeg_crop_scanline jpeg12_crop_scanline +#define _jpeg_read_scanlines jpeg12_read_scanlines +#define _jpeg_skip_scanlines jpeg12_skip_scanlines +#define _jpeg_write_scanlines jpeg12_write_scanlines +#elif BITS_IN_JSAMPLE == 16 +#define _JSAMPLE J16SAMPLE +#define _JSAMPROW J16SAMPROW +#define _buffer buffer16 +#define _jinit_read_ppm j16init_read_ppm +#define _jinit_write_ppm j16init_write_ppm +#define _jpeg_read_scanlines jpeg16_read_scanlines +#define _jpeg_write_scanlines jpeg16_write_scanlines +#endif + +#define _GET_NAME(name, suffix) name##suffix +#define GET_NAME(name, suffix) _GET_NAME(name, suffix) +#define _GET_STRING(name, suffix) #name #suffix +#define GET_STRING(name, suffix) _GET_STRING(name, suffix) + + +/******************************** Compressor *********************************/ + +/* TurboJPEG 3+ */ +DLLEXPORT int GET_NAME(tj3Compress, BITS_IN_JSAMPLE) + (tjhandle handle, const _JSAMPLE *srcBuf, int width, int pitch, int height, + int pixelFormat, unsigned char **jpegBuf, size_t *jpegSize) +{ + static const char FUNCTION_NAME[] = GET_STRING(tj3Compress, BITS_IN_JSAMPLE); + int i, retval = 0; + boolean alloc = TRUE; + _JSAMPROW *row_pointer = NULL; + + GET_CINSTANCE(handle) + if ((this->init & COMPRESS) == 0) + THROW("Instance has not been initialized for compression"); + + if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 || + pixelFormat < 0 || pixelFormat >= TJ_NUMPF || jpegBuf == NULL || + jpegSize == NULL) + THROW("Invalid argument"); + + if (!this->lossless && this->quality == -1) + THROW("TJPARAM_QUALITY must be specified"); + if (!this->lossless && this->subsamp == TJSAMP_UNKNOWN) + THROW("TJPARAM_SUBSAMP must be specified"); + + if (pitch == 0) pitch = width * tjPixelSize[pixelFormat]; + + if ((row_pointer = (_JSAMPROW *)malloc(sizeof(_JSAMPROW) * height)) == NULL) + THROW("Memory allocation failure"); + + if (setjmp(this->jerr.setjmp_buffer)) { + /* If we get here, the JPEG code has signaled an error. */ + retval = -1; goto bailout; + } + + cinfo->image_width = width; + cinfo->image_height = height; + cinfo->data_precision = BITS_IN_JSAMPLE; + + setCompDefaults(this, pixelFormat); + if (this->noRealloc) { + alloc = FALSE; + *jpegSize = tj3JPEGBufSize(width, height, this->subsamp); + } + jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc); + + jpeg_start_compress(cinfo, TRUE); + for (i = 0; i < height; i++) { + if (this->bottomUp) + row_pointer[i] = (_JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch]; + else + row_pointer[i] = (_JSAMPROW)&srcBuf[i * (size_t)pitch]; + } + while (cinfo->next_scanline < cinfo->image_height) + _jpeg_write_scanlines(cinfo, &row_pointer[cinfo->next_scanline], + cinfo->image_height - cinfo->next_scanline); + jpeg_finish_compress(cinfo); + +bailout: + if (cinfo->global_state > CSTATE_START) { + if (alloc) (*cinfo->dest->term_destination) (cinfo); + jpeg_abort_compress(cinfo); + } + free(row_pointer); + if (this->jerr.warning) retval = -1; + return retval; +} + + +/******************************* Decompressor ********************************/ + +/* TurboJPEG 3+ */ +DLLEXPORT int GET_NAME(tj3Decompress, BITS_IN_JSAMPLE) + (tjhandle handle, const unsigned char *jpegBuf, size_t jpegSize, + _JSAMPLE *dstBuf, int pitch, int pixelFormat) +{ + static const char FUNCTION_NAME[] = + GET_STRING(tj3Decompress, BITS_IN_JSAMPLE); + _JSAMPROW *row_pointer = NULL; + int croppedHeight, i, retval = 0; +#if BITS_IN_JSAMPLE != 16 + int scaledWidth; +#endif + struct my_progress_mgr progress; + + GET_DINSTANCE(handle); + if ((this->init & DECOMPRESS) == 0) + THROW("Instance has not been initialized for decompression"); + + if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || pitch < 0 || + pixelFormat < 0 || pixelFormat >= TJ_NUMPF) + THROW("Invalid argument"); + + if (this->scanLimit) { + memset(&progress, 0, sizeof(struct my_progress_mgr)); + progress.pub.progress_monitor = my_progress_monitor; + progress.this = this; + dinfo->progress = &progress.pub; + } else + dinfo->progress = NULL; + + if (setjmp(this->jerr.setjmp_buffer)) { + /* If we get here, the JPEG code has signaled an error. */ + retval = -1; goto bailout; + } + + if (!this->headerRead) { + jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize); + jpeg_read_header(dinfo, TRUE); + } + this->headerRead = FALSE; + setDecompParameters(this); + this->dinfo.out_color_space = pf2cs[pixelFormat]; +#if BITS_IN_JSAMPLE != 16 + scaledWidth = TJSCALED(dinfo->image_width, this->scalingFactor); +#endif + dinfo->do_fancy_upsampling = !this->fastUpsample; + this->dinfo.dct_method = this->fastDCT ? JDCT_FASTEST : JDCT_ISLOW; + + dinfo->scale_num = this->scalingFactor.num; + dinfo->scale_denom = this->scalingFactor.denom; + + jpeg_start_decompress(dinfo); + +#if BITS_IN_JSAMPLE != 16 + if (this->croppingRegion.x != 0 || + (this->croppingRegion.w != 0 && this->croppingRegion.w != scaledWidth)) { + JDIMENSION crop_x = this->croppingRegion.x; + JDIMENSION crop_w = this->croppingRegion.w; + + _jpeg_crop_scanline(dinfo, &crop_x, &crop_w); + if ((int)crop_x != this->croppingRegion.x) + THROWI("Unexplained mismatch between specified and actual (%d) cropping region left boundary", + (int)crop_x); + if ((int)crop_w != this->croppingRegion.w) + THROWI("Unexplained mismatch between specified and actual (%d) cropping region width", + (int)crop_w); + } +#endif + + if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat]; + + croppedHeight = dinfo->output_height; +#if BITS_IN_JSAMPLE != 16 + if (this->croppingRegion.y != 0 || this->croppingRegion.h != 0) + croppedHeight = this->croppingRegion.h; +#endif + if ((row_pointer = + (_JSAMPROW *)malloc(sizeof(_JSAMPROW) * croppedHeight)) == NULL) + THROW("Memory allocation failure"); + if (setjmp(this->jerr.setjmp_buffer)) { + /* If we get here, the JPEG code has signaled an error. */ + retval = -1; goto bailout; + } + for (i = 0; i < (int)croppedHeight; i++) { + if (this->bottomUp) + row_pointer[i] = &dstBuf[(croppedHeight - i - 1) * (size_t)pitch]; + else + row_pointer[i] = &dstBuf[i * (size_t)pitch]; + } + +#if BITS_IN_JSAMPLE != 16 + if (this->croppingRegion.y != 0 || this->croppingRegion.h != 0) { + if (this->croppingRegion.y != 0) { + JDIMENSION lines = _jpeg_skip_scanlines(dinfo, this->croppingRegion.y); + + if ((int)lines != this->croppingRegion.y) + THROWI("Unexplained mismatch between specified and actual (%d) cropping region upper boundary", + (int)lines); + } + while ((int)dinfo->output_scanline < + this->croppingRegion.y + this->croppingRegion.h) + _jpeg_read_scanlines(dinfo, &row_pointer[dinfo->output_scanline - + this->croppingRegion.y], + this->croppingRegion.y + this->croppingRegion.h - + dinfo->output_scanline); + if (this->croppingRegion.y + this->croppingRegion.h != + (int)dinfo->output_height) { + JDIMENSION lines = _jpeg_skip_scanlines(dinfo, dinfo->output_height - + this->croppingRegion.y - + this->croppingRegion.h); + + if (lines != dinfo->output_height - this->croppingRegion.y - + this->croppingRegion.h) + THROWI("Unexplained mismatch between specified and actual (%d) cropping region lower boundary", + (int)(dinfo->output_height - lines)); + } + } else +#endif + { + while (dinfo->output_scanline < dinfo->output_height) + _jpeg_read_scanlines(dinfo, &row_pointer[dinfo->output_scanline], + dinfo->output_height - dinfo->output_scanline); + } + jpeg_finish_decompress(dinfo); + +bailout: + if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo); + free(row_pointer); + if (this->jerr.warning) retval = -1; + return retval; +} + + +/*************************** Packed-Pixel Image I/O **************************/ + +/* TurboJPEG 3+ */ +DLLEXPORT _JSAMPLE *GET_NAME(tj3LoadImage, BITS_IN_JSAMPLE) + (tjhandle handle, const char *filename, int *width, int align, int *height, + int *pixelFormat) +{ + static const char FUNCTION_NAME[] = + GET_STRING(tj3LoadImage, BITS_IN_JSAMPLE); + int retval = 0, tempc; + size_t pitch; + tjhandle handle2 = NULL; + tjinstance *this2; + j_compress_ptr cinfo = NULL; + cjpeg_source_ptr src; + _JSAMPLE *dstBuf = NULL; + FILE *file = NULL; + boolean invert; + + GET_TJINSTANCE(handle, NULL) + + if (!filename || !width || align < 1 || !height || !pixelFormat || + *pixelFormat < TJPF_UNKNOWN || *pixelFormat >= TJ_NUMPF) + THROW("Invalid argument"); + if ((align & (align - 1)) != 0) + THROW("Alignment must be a power of 2"); + + /* The instance handle passed to this function is used only for parameter + retrieval. Create a new temporary instance to avoid interfering with the + libjpeg state of the primary instance. */ + if ((handle2 = tj3Init(TJINIT_COMPRESS)) == NULL) return NULL; + this2 = (tjinstance *)handle2; + cinfo = &this2->cinfo; + +#ifdef _MSC_VER + if (fopen_s(&file, filename, "rb") || file == NULL) +#else + if ((file = fopen(filename, "rb")) == NULL) +#endif + THROW_UNIX("Cannot open input file"); + + if ((tempc = getc(file)) < 0 || ungetc(tempc, file) == EOF) + THROW_UNIX("Could not read input file") + else if (tempc == EOF) + THROW("Input file contains no data"); + + if (setjmp(this2->jerr.setjmp_buffer)) { + /* If we get here, the JPEG code has signaled an error. */ + retval = -1; goto bailout; + } + + cinfo->data_precision = BITS_IN_JSAMPLE; + if (*pixelFormat == TJPF_UNKNOWN) cinfo->in_color_space = JCS_UNKNOWN; + else cinfo->in_color_space = pf2cs[*pixelFormat]; + if (tempc == 'B') { + if ((src = jinit_read_bmp(cinfo, FALSE)) == NULL) + THROW("Could not initialize bitmap loader"); + invert = !this->bottomUp; + } else if (tempc == 'P') { + if ((src = _jinit_read_ppm(cinfo)) == NULL) + THROW("Could not initialize PPM loader"); + invert = this->bottomUp; + } else + THROW("Unsupported file type"); + + src->input_file = file; +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + /* Refuse to load images larger than 1 Megapixel when fuzzing. */ + src->max_pixels = this->maxPixels; +#endif + (*src->start_input) (cinfo, src); + if (tempc == 'B') { + if (cinfo->X_density && cinfo->Y_density) { + this->xDensity = cinfo->X_density; + this->yDensity = cinfo->Y_density; + this->densityUnits = cinfo->density_unit; + } + } + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr)cinfo); + + *width = cinfo->image_width; *height = cinfo->image_height; + *pixelFormat = cs2pf[cinfo->in_color_space]; + + pitch = PAD((*width) * tjPixelSize[*pixelFormat], align); + if ((unsigned long long)pitch * (unsigned long long)(*height) > + (unsigned long long)((size_t)-1) || + (dstBuf = (_JSAMPLE *)malloc(pitch * (*height) * + sizeof(_JSAMPLE))) == NULL) + THROW("Memory allocation failure"); + + if (setjmp(this2->jerr.setjmp_buffer)) { + /* If we get here, the JPEG code has signaled an error. */ + retval = -1; goto bailout; + } + + while (cinfo->next_scanline < cinfo->image_height) { + int i, nlines = (*src->get_pixel_rows) (cinfo, src); + + for (i = 0; i < nlines; i++) { + _JSAMPLE *dstptr; + int row; + + row = cinfo->next_scanline + i; + if (invert) dstptr = &dstBuf[((*height) - row - 1) * pitch]; + else dstptr = &dstBuf[row * pitch]; + memcpy(dstptr, src->_buffer[i], + (*width) * tjPixelSize[*pixelFormat] * sizeof(_JSAMPLE)); + } + cinfo->next_scanline += nlines; + } + + (*src->finish_input) (cinfo, src); + +bailout: + tj3Destroy(handle2); + if (file) fclose(file); + if (retval < 0) { free(dstBuf); dstBuf = NULL; } + return dstBuf; +} + + +/* TurboJPEG 3+ */ +DLLEXPORT int GET_NAME(tj3SaveImage, BITS_IN_JSAMPLE) + (tjhandle handle, const char *filename, const _JSAMPLE *buffer, int width, + int pitch, int height, int pixelFormat) +{ + static const char FUNCTION_NAME[] = + GET_STRING(tj3SaveImage, BITS_IN_JSAMPLE); + int retval = 0; + tjhandle handle2 = NULL; + tjinstance *this2; + j_decompress_ptr dinfo = NULL; + djpeg_dest_ptr dst; + FILE *file = NULL; + char *ptr = NULL; + boolean invert; + + GET_TJINSTANCE(handle, -1) + + if (!filename || !buffer || width < 1 || pitch < 0 || height < 1 || + pixelFormat < 0 || pixelFormat >= TJ_NUMPF) + THROW("Invalid argument"); + + /* The instance handle passed to this function is used only for parameter + retrieval. Create a new temporary instance to avoid interfering with the + libjpeg state of the primary instance. */ + if ((handle2 = tj3Init(TJINIT_DECOMPRESS)) == NULL) + return -1; + this2 = (tjinstance *)handle2; + dinfo = &this2->dinfo; + +#ifdef _MSC_VER + if (fopen_s(&file, filename, "wb") || file == NULL) +#else + if ((file = fopen(filename, "wb")) == NULL) +#endif + THROW_UNIX("Cannot open output file"); + + if (setjmp(this2->jerr.setjmp_buffer)) { + /* If we get here, the JPEG code has signaled an error. */ + retval = -1; goto bailout; + } + + this2->dinfo.out_color_space = pf2cs[pixelFormat]; + dinfo->image_width = width; dinfo->image_height = height; + dinfo->global_state = DSTATE_READY; + dinfo->scale_num = dinfo->scale_denom = 1; + dinfo->data_precision = BITS_IN_JSAMPLE; + + ptr = strrchr(filename, '.'); + if (ptr && !strcasecmp(ptr, ".bmp")) { + if ((dst = jinit_write_bmp(dinfo, FALSE, FALSE)) == NULL) + THROW("Could not initialize bitmap writer"); + invert = !this->bottomUp; + dinfo->X_density = (UINT16)this->xDensity; + dinfo->Y_density = (UINT16)this->yDensity; + dinfo->density_unit = (UINT8)this->densityUnits; + } else { + if ((dst = _jinit_write_ppm(dinfo)) == NULL) + THROW("Could not initialize PPM writer"); + invert = this->bottomUp; + } + + dst->output_file = file; + (*dst->start_output) (dinfo, dst); + (*dinfo->mem->realize_virt_arrays) ((j_common_ptr)dinfo); + + if (pitch == 0) pitch = width * tjPixelSize[pixelFormat]; + + while (dinfo->output_scanline < dinfo->output_height) { + _JSAMPLE *rowptr; + + if (invert) + rowptr = + (_JSAMPLE *)&buffer[(height - dinfo->output_scanline - 1) * pitch]; + else + rowptr = (_JSAMPLE *)&buffer[dinfo->output_scanline * pitch]; + memcpy(dst->_buffer[0], rowptr, + width * tjPixelSize[pixelFormat] * sizeof(_JSAMPLE)); + (*dst->put_pixel_rows) (dinfo, dst, 1); + dinfo->output_scanline++; + } + + (*dst->finish_output) (dinfo, dst); + +bailout: + tj3Destroy(handle2); + if (file) fclose(file); + return retval; +} + + +#undef _JSAMPLE +#undef _JSAMPROW +#undef _buffer +#undef _jinit_read_ppm +#undef _jinit_write_ppm +#undef _jpeg_crop_scanline +#undef _jpeg_read_scanlines +#undef _jpeg_skip_scanlines +#undef _jpeg_write_scanlines diff --git a/turbojpeg.c b/turbojpeg.c index fa25a3acc..3c2358616 100644 --- a/turbojpeg.c +++ b/turbojpeg.c @@ -44,10 +44,9 @@ #include "./jpegapicomp.h" #include "./cdjpeg.h" -extern void jpeg_mem_dest_tj(j_compress_ptr, unsigned char **, unsigned long *, +extern void jpeg_mem_dest_tj(j_compress_ptr, unsigned char **, size_t *, boolean); -extern void jpeg_mem_src_tj(j_decompress_ptr, const unsigned char *, - unsigned long); +extern void jpeg_mem_src_tj(j_decompress_ptr, const unsigned char *, size_t); #define PAD(v, p) ((v + (p) - 1) & (~((p) - 1))) #define IS_POW2(x) (((x) & (x - 1)) == 0) @@ -106,11 +105,43 @@ typedef struct _tjinstance { struct jpeg_compress_struct cinfo; struct jpeg_decompress_struct dinfo; struct my_error_mgr jerr; - int init, headerRead; + int init; + boolean headerRead; char errStr[JMSG_LENGTH_MAX]; boolean isInstanceError; + /* Parameters */ +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + int maxPixels; +#endif + boolean bottomUp; + boolean noRealloc; + int quality; + int subsamp; + int jpegWidth; + int jpegHeight; + int precision; + int colorspace; + boolean fastUpsample; + boolean fastDCT; + boolean optimize; + boolean progressive; + int scanLimit; + boolean arithmetic; + boolean lossless; + int losslessPSV; + int losslessPt; + int restartIntervalBlocks; + int restartIntervalRows; + int xDensity; + int yDensity; + int densityUnits; + tjscalingfactor scalingFactor; + tjregion croppingRegion; } tjinstance; +static tjhandle _tjInitCompress(tjinstance *this); +static tjhandle _tjInitDecompress(tjinstance *this); + struct my_progress_mgr { struct jpeg_progress_mgr pub; tjinstance *this; @@ -125,11 +156,13 @@ static void my_progress_monitor(j_common_ptr dinfo) if (dinfo->is_decompressor) { int scan_no = ((j_decompress_ptr)dinfo)->input_scan_number; - if (scan_no > 500) { + if (scan_no > myprog->this->scanLimit) { SNPRINTF(myprog->this->errStr, JMSG_LENGTH_MAX, - "Progressive JPEG image has more than 500 scans"); + "Progressive JPEG image has more than %d scans", + myprog->this->scanLimit); SNPRINTF(errStr, JMSG_LENGTH_MAX, - "Progressive JPEG image has more than 500 scans"); + "Progressive JPEG image has more than %d scans", + myprog->this->scanLimit); myprog->this->isInstanceError = TRUE; myerr->warning = FALSE; longjmp(myerr->setjmp_buffer, 1); @@ -137,8 +170,6 @@ static void my_progress_monitor(j_common_ptr dinfo) } } -static const int pixelsize[TJ_NUMSAMP] = { 3, 3, 3, 1, 3, 3 }; - static const JXFORM_CODE xformtypes[TJ_NUMXOP] = { JXFORM_NONE, JXFORM_FLIP_H, JXFORM_FLIP_V, JXFORM_TRANSPOSE, JXFORM_TRANSVERSE, JXFORM_ROT_90, JXFORM_ROT_180, JXFORM_ROT_270 @@ -190,32 +221,42 @@ static int cs2pf[JPEG_NUMCS] = { TJPF_UNKNOWN }; -#define THROWG(m) { \ - SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s", m); \ - retval = -1; goto bailout; \ +#define THROWG(m, rv) { \ + SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): %s", FUNCTION_NAME, m); \ + retval = rv; goto bailout; \ } #ifdef _MSC_VER #define THROW_UNIX(m) { \ char strerrorBuf[80] = { 0 }; \ strerror_s(strerrorBuf, 80, errno); \ - SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s\n%s", m, strerrorBuf); \ + SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s(): %s\n%s", FUNCTION_NAME, m, \ + strerrorBuf); \ + this->isInstanceError = TRUE; \ + SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): %s\n%s", FUNCTION_NAME, m, \ + strerrorBuf); \ retval = -1; goto bailout; \ } #else #define THROW_UNIX(m) { \ - SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s\n%s", m, strerror(errno)); \ + SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s(): %s\n%s", FUNCTION_NAME, m, \ + strerror(errno)); \ + this->isInstanceError = TRUE; \ + SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): %s\n%s", FUNCTION_NAME, m, \ + strerror(errno)); \ retval = -1; goto bailout; \ } #endif #define THROW(m) { \ - SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s", m); \ - this->isInstanceError = TRUE; THROWG(m) \ + SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s(): %s", FUNCTION_NAME, m); \ + this->isInstanceError = TRUE; THROWG(m, -1) \ +} +#define THROWI(format, val) { \ + SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s(): " format, FUNCTION_NAME, \ + val); \ + this->isInstanceError = TRUE; \ + SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): " format, FUNCTION_NAME, val); \ + retval = -1; goto bailout; \ } - -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -/* Private flag that triggers different TurboJPEG API behavior when fuzzing */ -#define TJFLAG_FUZZING (1 << 30) -#endif #define GET_INSTANCE(handle) \ tjinstance *this = (tjinstance *)handle; \ @@ -223,7 +264,7 @@ static int cs2pf[JPEG_NUMCS] = { j_decompress_ptr dinfo = NULL; \ \ if (!this) { \ - SNPRINTF(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \ + SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): Invalid handle", FUNCTION_NAME); \ return -1; \ } \ cinfo = &this->cinfo; dinfo = &this->dinfo; \ @@ -235,7 +276,7 @@ static int cs2pf[JPEG_NUMCS] = { j_compress_ptr cinfo = NULL; \ \ if (!this) { \ - SNPRINTF(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \ + SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): Invalid handle", FUNCTION_NAME); \ return -1; \ } \ cinfo = &this->cinfo; \ @@ -247,13 +288,23 @@ static int cs2pf[JPEG_NUMCS] = { j_decompress_ptr dinfo = NULL; \ \ if (!this) { \ - SNPRINTF(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \ + SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): Invalid handle", FUNCTION_NAME); \ return -1; \ } \ dinfo = &this->dinfo; \ this->jerr.warning = FALSE; \ this->isInstanceError = FALSE; +#define GET_TJINSTANCE(handle, errorReturn) \ + tjinstance *this = (tjinstance *)handle; \ + \ + if (!this) { \ + SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): Invalid handle", FUNCTION_NAME); \ + return errorReturn; \ + } \ + this->jerr.warning = FALSE; \ + this->isInstanceError = FALSE; + static int getPixelFormat(int pixelSize, int flags) { if (pixelSize == 1) return TJPF_GRAY; @@ -273,100 +324,74 @@ static int getPixelFormat(int pixelSize, int flags) return -1; } -static int setCompDefaults(tjhandle handle, int pixelFormat, int subsamp, - int jpegQual, int flags) +static void setCompDefaults(tjinstance *this, int pixelFormat) { -#ifndef NO_GETENV - char env[7] = { 0 }; -#endif - int retval = 0; - GET_CINSTANCE(handle); - - cinfo->in_color_space = pf2cs[pixelFormat]; - cinfo->input_components = tjPixelSize[pixelFormat]; - jpeg_set_defaults(cinfo); - -#ifndef NO_GETENV - if (!GETENV_S(env, 7, "TJ_OPTIMIZE") && !strcmp(env, "1")) - cinfo->optimize_coding = TRUE; - if (!GETENV_S(env, 7, "TJ_RESTART") && strlen(env) > 0) { - int temp = -1; - char tempc = 0; - -#ifdef _MSC_VER - if (sscanf_s(env, "%d%c", &temp, &tempc, 1) >= 1 && temp >= 0 && - temp <= 65535) { -#else - if (sscanf(env, "%d%c", &temp, &tempc) >= 1 && temp >= 0 && - temp <= 65535) { -#endif - if (toupper(tempc) == 'B') { - cinfo->restart_interval = temp; - cinfo->restart_in_rows = 0; - } else - cinfo->restart_in_rows = temp; - } - } -#endif + this->cinfo.in_color_space = pf2cs[pixelFormat]; + this->cinfo.input_components = tjPixelSize[pixelFormat]; + jpeg_set_defaults(&this->cinfo); - if (jpegQual >= 0) { - if (flags & TJFLAG_LOSSLESS) { - int psv = jpegQual / 10, pt = jpegQual % 10; + this->cinfo.restart_interval = this->restartIntervalBlocks; + this->cinfo.restart_in_rows = this->restartIntervalRows; + this->cinfo.X_density = (UINT16)this->xDensity; + this->cinfo.Y_density = (UINT16)this->yDensity; + this->cinfo.density_unit = (UINT8)this->densityUnits; - if (psv < 1 || psv > 7 || pt < 0 || pt > 7) - THROW("Invalid lossless parameters"); + if (this->lossless) { #ifdef C_LOSSLESS_SUPPORTED - jpeg_enable_lossless(cinfo, psv, pt); + jpeg_enable_lossless(&this->cinfo, this->losslessPSV, this->losslessPt); #endif - } else { - jpeg_set_quality(cinfo, jpegQual, TRUE); - if (jpegQual >= 96 || flags & TJFLAG_ACCURATEDCT) - cinfo->dct_method = JDCT_ISLOW; - else - cinfo->dct_method = JDCT_FASTEST; - } + if (pixelFormat == TJPF_GRAY) + this->subsamp = TJSAMP_GRAY; + else if (this->subsamp != TJSAMP_GRAY) + this->subsamp = TJSAMP_444; + return; + } + + jpeg_set_quality(&this->cinfo, this->quality, TRUE); + this->cinfo.dct_method = this->fastDCT ? JDCT_FASTEST : JDCT_ISLOW; + + switch (this->colorspace) { + case TJCS_RGB: + jpeg_set_colorspace(&this->cinfo, JCS_RGB); break; + case TJCS_YCbCr: + jpeg_set_colorspace(&this->cinfo, JCS_YCbCr); break; + case TJCS_GRAY: + jpeg_set_colorspace(&this->cinfo, JCS_GRAYSCALE); break; + case TJCS_CMYK: + jpeg_set_colorspace(&this->cinfo, JCS_CMYK); break; + case TJCS_YCCK: + jpeg_set_colorspace(&this->cinfo, JCS_YCCK); break; + default: + if (this->subsamp == TJSAMP_GRAY) + jpeg_set_colorspace(&this->cinfo, JCS_GRAYSCALE); + else if (pixelFormat == TJPF_CMYK) + jpeg_set_colorspace(&this->cinfo, JCS_YCCK); + else + jpeg_set_colorspace(&this->cinfo, JCS_YCbCr); } - if (subsamp == TJSAMP_GRAY) - jpeg_set_colorspace(cinfo, JCS_GRAYSCALE); - else if (pixelFormat == TJPF_CMYK) - jpeg_set_colorspace(cinfo, JCS_YCCK); - else - jpeg_set_colorspace(cinfo, JCS_YCbCr); + this->cinfo.optimize_coding = this->optimize; #ifdef C_PROGRESSIVE_SUPPORTED - if (flags & TJFLAG_PROGRESSIVE) - jpeg_simple_progression(cinfo); -#ifndef NO_GETENV - else if (!GETENV_S(env, 7, "TJ_PROGRESSIVE") && !strcmp(env, "1")) - jpeg_simple_progression(cinfo); + if (this->progressive) jpeg_simple_progression(&this->cinfo); #endif -#endif - if (flags & TJFLAG_ARITHMETIC) - cinfo->arith_code = TRUE; -#ifndef NO_GETENV - else if (!GETENV_S(env, 7, "TJ_ARITHMETIC") && !strcmp(env, "1")) - cinfo->arith_code = TRUE; -#endif - - cinfo->comp_info[0].h_samp_factor = tjMCUWidth[subsamp] / 8; - cinfo->comp_info[1].h_samp_factor = 1; - cinfo->comp_info[2].h_samp_factor = 1; - if (cinfo->num_components > 3) - cinfo->comp_info[3].h_samp_factor = tjMCUWidth[subsamp] / 8; - cinfo->comp_info[0].v_samp_factor = tjMCUHeight[subsamp] / 8; - cinfo->comp_info[1].v_samp_factor = 1; - cinfo->comp_info[2].v_samp_factor = 1; - if (cinfo->num_components > 3) - cinfo->comp_info[3].v_samp_factor = tjMCUHeight[subsamp] / 8; - - bailout: - return retval; + this->cinfo.arith_code = this->arithmetic; + + this->cinfo.comp_info[0].h_samp_factor = tjMCUWidth[this->subsamp] / 8; + this->cinfo.comp_info[1].h_samp_factor = 1; + this->cinfo.comp_info[2].h_samp_factor = 1; + if (this->cinfo.num_components > 3) + this->cinfo.comp_info[3].h_samp_factor = tjMCUWidth[this->subsamp] / 8; + this->cinfo.comp_info[0].v_samp_factor = tjMCUHeight[this->subsamp] / 8; + this->cinfo.comp_info[1].v_samp_factor = 1; + this->cinfo.comp_info[2].v_samp_factor = 1; + if (this->cinfo.num_components > 3) + this->cinfo.comp_info[3].v_samp_factor = tjMCUHeight[this->subsamp] / 8; } static int getSubsamp(j_decompress_ptr dinfo) { - int retval = -1, i, k; + int retval = TJSAMP_UNKNOWN, i, k; /* The sampling factors actually have no meaning with grayscale JPEG files, and in fact it's possible to generate grayscale JPEGs with sampling @@ -376,10 +401,12 @@ static int getSubsamp(j_decompress_ptr dinfo) return TJSAMP_GRAY; for (i = 0; i < TJ_NUMSAMP; i++) { - if (dinfo->num_components == pixelsize[i] || + if (i == TJSAMP_GRAY) continue; + + if (dinfo->num_components == 3 || ((dinfo->jpeg_color_space == JCS_YCCK || dinfo->jpeg_color_space == JCS_CMYK) && - pixelsize[i] == 3 && dinfo->num_components == 4)) { + dinfo->num_components == 4)) { if (dinfo->comp_info[0].h_samp_factor == tjMCUWidth[i] / 8 && dinfo->comp_info[0].v_samp_factor == tjMCUHeight[i] / 8) { int match = 0; @@ -425,7 +452,7 @@ static int getSubsamp(j_decompress_ptr dinfo) non-standard ways. */ if (dinfo->comp_info[0].h_samp_factor * dinfo->comp_info[0].v_samp_factor <= - D_MAX_BLOCKS_IN_MCU / pixelsize[i] && i == TJSAMP_444) { + D_MAX_BLOCKS_IN_MCU / 3 && i == TJSAMP_444) { int match = 0; for (k = 1; k < dinfo->num_components; k++) { if (dinfo->comp_info[k].h_samp_factor == @@ -444,10 +471,309 @@ static int getSubsamp(j_decompress_ptr dinfo) } +static void setDecompParameters(tjinstance *this) +{ + this->subsamp = getSubsamp(&this->dinfo); + this->jpegWidth = this->dinfo.image_width; + this->jpegHeight = this->dinfo.image_height; + this->precision = this->dinfo.data_precision; + switch (this->dinfo.jpeg_color_space) { + case JCS_GRAYSCALE: this->colorspace = TJCS_GRAY; break; + case JCS_RGB: this->colorspace = TJCS_RGB; break; + case JCS_YCbCr: this->colorspace = TJCS_YCbCr; break; + case JCS_CMYK: this->colorspace = TJCS_CMYK; break; + case JCS_YCCK: this->colorspace = TJCS_YCCK; break; + default: this->colorspace = -1; break; + } + this->progressive = this->dinfo.progressive_mode; + this->arithmetic = this->dinfo.arith_code; + this->lossless = this->dinfo.master->lossless; + this->losslessPSV = this->dinfo.Ss; + this->losslessPt = this->dinfo.Al; + this->xDensity = this->dinfo.X_density; + this->yDensity = this->dinfo.Y_density; + this->densityUnits = this->dinfo.density_unit; +} + + +static void processFlags(tjhandle handle, int flags, int operation) +{ + tjinstance *this = (tjinstance *)handle; + + this->bottomUp = !!(flags & TJFLAG_BOTTOMUP); + +#ifndef NO_PUTENV + if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1"); + else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1"); + else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1"); +#endif + + this->fastUpsample = !!(flags & TJFLAG_FASTUPSAMPLE); + this->noRealloc = !!(flags & TJFLAG_NOREALLOC); + + if (operation == COMPRESS) { + if (this->quality >= 96 || flags & TJFLAG_ACCURATEDCT) + this->fastDCT = FALSE; + else + this->fastDCT = TRUE; + } else + this->fastDCT = !!(flags & TJFLAG_FASTDCT); + + this->jerr.stopOnWarning = !!(flags & TJFLAG_STOPONWARNING); + this->progressive = !!(flags & TJFLAG_PROGRESSIVE); + + if (flags & TJFLAG_LIMITSCANS) this->scanLimit = 500; +} + + /*************************** General API functions ***************************/ -/* TurboJPEG 2.0+ */ -DLLEXPORT char *tjGetErrorStr2(tjhandle handle) +/* TurboJPEG 3+ */ +DLLEXPORT tjhandle tj3Init(int initType) +{ + static const char FUNCTION_NAME[] = "tj3Init"; + tjinstance *this = NULL; + tjhandle retval = NULL; + + if (initType < 0 || initType >= TJ_NUMINIT) + THROWG("Invalid argument", NULL); + + if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) + THROWG("Memory allocation failure", NULL); + memset(this, 0, sizeof(tjinstance)); + SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "No error"); + + this->quality = -1; + this->subsamp = TJSAMP_UNKNOWN; + this->jpegWidth = -1; + this->jpegHeight = -1; + this->precision = 8; + this->colorspace = -1; + this->losslessPSV = 1; + this->xDensity = 1; + this->yDensity = 1; + this->scalingFactor = TJUNSCALED; + + switch (initType) { + case TJINIT_COMPRESS: return _tjInitCompress(this); + case TJINIT_DECOMPRESS: return _tjInitDecompress(this); + case TJINIT_TRANSFORM: + retval = _tjInitCompress(this); + if (!retval) return NULL; + retval = _tjInitDecompress(this); + return retval; + } + +bailout: + return retval; +} + + +#define SET_PARAM(field, minValue, maxValue) { \ + if (value < minValue || (maxValue > 0 && value > maxValue)) \ + THROW("Parameter value out of range"); \ + this->field = value; \ +} + +#define SET_BOOL_PARAM(field) { \ + if (value < 0 || value > 1) \ + THROW("Parameter value out of range"); \ + this->field = (boolean)value; \ +} + +/* TurboJPEG 3+ */ +DLLEXPORT int tj3Set(tjhandle handle, int param, int value) +{ + static const char FUNCTION_NAME[] = "tj3Set"; + int retval = 0; + + GET_TJINSTANCE(handle, -1); + + switch (param) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + case TJPARAM_MAXPIXELS: + SET_PARAM(maxPixels, 0, -1); + break; +#endif + case TJPARAM_STOPONWARNING: + SET_BOOL_PARAM(jerr.stopOnWarning); + break; + case TJPARAM_BOTTOMUP: + SET_BOOL_PARAM(bottomUp); + break; + case TJPARAM_NOREALLOC: + if (!(this->init & COMPRESS)) + THROW("TJPARAM_NOREALLOC is not applicable to decompression instances."); + SET_BOOL_PARAM(noRealloc); + break; + case TJPARAM_QUALITY: + if (!(this->init & COMPRESS)) + THROW("TJPARAM_QUALITY is not applicable to decompression instances."); + SET_PARAM(quality, 1, 100); + break; + case TJPARAM_SUBSAMP: + SET_PARAM(subsamp, 0, TJ_NUMSAMP - 1); + break; + case TJPARAM_JPEGWIDTH: + if (!(this->init & DECOMPRESS)) + THROW("TJPARAM_JPEGWIDTH is not applicable to compression instances."); + THROW("TJPARAM_JPEGWIDTH is read-only in decompression instances."); + break; + case TJPARAM_JPEGHEIGHT: + if (!(this->init & DECOMPRESS)) + THROW("TJPARAM_JPEGHEIGHT is not applicable to compression instances."); + THROW("TJPARAM_JPEGHEIGHT is read-only in decompression instances."); + break; + case TJPARAM_PRECISION: + if (!(this->init & DECOMPRESS)) + THROW("TJPARAM_PRECISION is not applicable to compression instances."); + THROW("TJPARAM_PRECISION is read-only in decompression instances."); + break; + case TJPARAM_COLORSPACE: + if (!(this->init & COMPRESS)) + THROW("TJPARAM_COLORSPACE is read-only in decompression instances."); + SET_PARAM(colorspace, 0, TJ_NUMCS - 1); + break; + case TJPARAM_FASTUPSAMPLE: + if (!(this->init & DECOMPRESS)) + THROW("TJPARAM_FASTUPSAMPLE is not applicable to compression instances."); + SET_BOOL_PARAM(fastUpsample); + break; + case TJPARAM_FASTDCT: + SET_BOOL_PARAM(fastDCT); + break; + case TJPARAM_OPTIMIZE: + if (!(this->init & COMPRESS)) + THROW("TJPARAM_OPTIMIZE is not applicable to decompression instances."); + SET_BOOL_PARAM(optimize); + break; + case TJPARAM_PROGRESSIVE: + if (!(this->init & COMPRESS)) + THROW("TJPARAM_PROGRESSIVE is read-only in decompression instances."); + SET_BOOL_PARAM(progressive); + break; + case TJPARAM_SCANLIMIT: + if (!(this->init & DECOMPRESS)) + THROW("TJPARAM_SCANLIMIT is not applicable to compression instances."); + SET_PARAM(scanLimit, 0, -1); + break; + case TJPARAM_ARITHMETIC: + if (!(this->init & COMPRESS)) + THROW("TJPARAM_ARITHMETIC is read-only in decompression instances."); + SET_BOOL_PARAM(arithmetic); + break; + case TJPARAM_LOSSLESS: + if (!(this->init & COMPRESS)) + THROW("TJPARAM_LOSSLESS is read-only in decompression instances."); + SET_BOOL_PARAM(lossless); + break; + case TJPARAM_LOSSLESSPSV: + if (!(this->init & COMPRESS)) + THROW("TJPARAM_LOSSLESSPSV is read-only in decompression instances."); + SET_PARAM(losslessPSV, 1, 7); + break; + case TJPARAM_LOSSLESSPT: + if (!(this->init & COMPRESS)) + THROW("TJPARAM_LOSSLESSPT is read-only in decompression instances."); + SET_PARAM(losslessPt, 0, this->precision - 1); + break; + case TJPARAM_RESTARTBLOCKS: + if (!(this->init & COMPRESS)) + THROW("TJPARAM_RESTARTBLOCKS is not applicable to decompression instances."); + SET_PARAM(restartIntervalBlocks, 0, 65535); + if (value != 0) this->restartIntervalRows = 0; + break; + case TJPARAM_RESTARTROWS: + if (!(this->init & COMPRESS)) + THROW("TJPARAM_RESTARTROWS is not applicable to decompression instances."); + SET_PARAM(restartIntervalRows, 0, 65535); + if (value != 0) this->restartIntervalBlocks = 0; + break; + case TJPARAM_XDENSITY: + if (!(this->init & COMPRESS)) + THROW("TJPARAM_XDENSITY is read-only in decompression instances."); + SET_PARAM(xDensity, 1, 65535); + break; + case TJPARAM_YDENSITY: + if (!(this->init & COMPRESS)) + THROW("TJPARAM_YDENSITY is read-only in decompression instances."); + SET_PARAM(yDensity, 1, 65535); + break; + case TJPARAM_DENSITYUNITS: + if (!(this->init & COMPRESS)) + THROW("TJPARAM_DENSITYUNITS is read-only in decompression instances."); + SET_PARAM(densityUnits, 0, 2); + break; + default: + THROW("Invalid parameter"); + } + +bailout: + return retval; +} + + +/* TurboJPEG 3+ */ +DLLEXPORT int tj3Get(tjhandle handle, int param) +{ + tjinstance *this = (tjinstance *)handle; + if (!this) return -1; + + switch (param) { + case TJPARAM_STOPONWARNING: + return this->jerr.stopOnWarning; + case TJPARAM_BOTTOMUP: + return this->bottomUp; + case TJPARAM_NOREALLOC: + return this->noRealloc; + case TJPARAM_QUALITY: + return this->quality; + case TJPARAM_SUBSAMP: + return this->subsamp; + case TJPARAM_JPEGWIDTH: + return this->jpegWidth; + case TJPARAM_JPEGHEIGHT: + return this->jpegHeight; + case TJPARAM_PRECISION: + return this->precision; + case TJPARAM_COLORSPACE: + return this->colorspace; + case TJPARAM_FASTUPSAMPLE: + return this->fastUpsample; + case TJPARAM_FASTDCT: + return this->fastDCT; + case TJPARAM_OPTIMIZE: + return this->optimize; + case TJPARAM_PROGRESSIVE: + return this->progressive; + case TJPARAM_SCANLIMIT: + return this->scanLimit; + case TJPARAM_ARITHMETIC: + return this->arithmetic; + case TJPARAM_LOSSLESS: + return this->lossless; + case TJPARAM_LOSSLESSPSV: + return this->losslessPSV; + case TJPARAM_LOSSLESSPT: + return this->losslessPt; + case TJPARAM_RESTARTBLOCKS: + return this->restartIntervalBlocks; + case TJPARAM_RESTARTROWS: + return this->restartIntervalRows; + case TJPARAM_XDENSITY: + return this->xDensity; + case TJPARAM_YDENSITY: + return this->yDensity; + case TJPARAM_DENSITYUNITS: + return this->densityUnits; + } + + return -1; +} + + +/* TurboJPEG 3+ */ +DLLEXPORT char *tj3GetErrorStr(tjhandle handle) { tjinstance *this = (tjinstance *)handle; @@ -458,6 +784,11 @@ DLLEXPORT char *tjGetErrorStr2(tjhandle handle) return errStr; } +/* TurboJPEG 2.0+ */ +DLLEXPORT char *tjGetErrorStr2(tjhandle handle) +{ + return tj3GetErrorStr(handle); +} /* TurboJPEG 1.0+ */ DLLEXPORT char *tjGetErrorStr(void) @@ -466,8 +797,8 @@ DLLEXPORT char *tjGetErrorStr(void) } -/* TurboJPEG 2.0+ */ -DLLEXPORT int tjGetErrorCode(tjhandle handle) +/* TurboJPEG 3+ */ +DLLEXPORT int tj3GetErrorCode(tjhandle handle) { tjinstance *this = (tjinstance *)handle; @@ -475,17 +806,46 @@ DLLEXPORT int tjGetErrorCode(tjhandle handle) else return TJERR_FATAL; } +/* TurboJPEG 2.0+ */ +DLLEXPORT int tjGetErrorCode(tjhandle handle) +{ + return tj3GetErrorCode(handle); +} -/* TurboJPEG 1.0+ */ -DLLEXPORT int tjDestroy(tjhandle handle) + +/* TurboJPEG 3+ */ +DLLEXPORT void tj3Destroy(tjhandle handle) { - GET_INSTANCE(handle); + tjinstance *this = (tjinstance *)handle; + j_compress_ptr cinfo = NULL; + j_decompress_ptr dinfo = NULL; + + if (!this) return; + + cinfo = &this->cinfo; dinfo = &this->dinfo; + this->jerr.warning = FALSE; + this->isInstanceError = FALSE; - if (setjmp(this->jerr.setjmp_buffer)) return -1; + if (setjmp(this->jerr.setjmp_buffer)) return; if (this->init & COMPRESS) jpeg_destroy_compress(cinfo); if (this->init & DECOMPRESS) jpeg_destroy_decompress(dinfo); free(this); - return 0; +} + +/* TurboJPEG 1.0+ */ +DLLEXPORT int tjDestroy(tjhandle handle) +{ + static const char FUNCTION_NAME[] = "tjDestroy"; + int retval = 0; + + if (!handle) THROWG("Invalid handle", -1); + + SNPRINTF(errStr, JMSG_LENGTH_MAX, "No error"); + tj3Destroy(handle); + if (strcmp(errStr, "No error")) retval = -1; + +bailout: + return retval; } @@ -494,17 +854,29 @@ DLLEXPORT int tjDestroy(tjhandle handle) with turbojpeg.dll for compatibility reasons. However, these functions can potentially be used for other purposes by different implementations. */ +/* TurboJPEG 3+ */ +DLLEXPORT void tj3Free(void *buf) +{ + free(buf); +} + /* TurboJPEG 1.2+ */ DLLEXPORT void tjFree(unsigned char *buf) { - free(buf); + tj3Free(buf); } +/* TurboJPEG 3+ */ +DLLEXPORT void *tj3Alloc(size_t bytes) +{ + return malloc(bytes); +} + /* TurboJPEG 1.2+ */ DLLEXPORT unsigned char *tjAlloc(int bytes) { - return (unsigned char *)malloc(bytes); + return (unsigned char *)tj3Alloc((size_t)bytes); } @@ -514,7 +886,7 @@ static tjhandle _tjInitCompress(tjinstance *this) { static unsigned char buffer[1]; unsigned char *buf = buffer; - unsigned long size = 1; + size_t size = 1; /* This is also straight out of example.c */ this->cinfo.err = jpeg_std_error(&this->jerr.pub); @@ -543,27 +915,24 @@ static tjhandle _tjInitCompress(tjinstance *this) /* TurboJPEG 1.0+ */ DLLEXPORT tjhandle tjInitCompress(void) { - tjinstance *this = NULL; - - if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) { - SNPRINTF(errStr, JMSG_LENGTH_MAX, - "tjInitCompress(): Memory allocation failure"); - return NULL; - } - memset(this, 0, sizeof(tjinstance)); - SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "No error"); - return _tjInitCompress(this); + return tj3Init(TJINIT_COMPRESS); } -/* TurboJPEG 1.2+ */ -DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp) +/* TurboJPEG 3+ */ +DLLEXPORT size_t tj3JPEGBufSize(int width, int height, int jpegSubsamp) { unsigned long long retval = 0; int mcuw, mcuh, chromasf; - if (width < 1 || height < 1 || jpegSubsamp < 0 || jpegSubsamp >= TJ_NUMSAMP) - THROWG("tjBufSize(): Invalid argument"); + if (width < 1 || height < 1 || jpegSubsamp < TJSAMP_UNKNOWN || + jpegSubsamp >= TJ_NUMSAMP) { + SNPRINTF(errStr, JMSG_LENGTH_MAX, "tj3JPEGBufSize(): Invalid argument"); + return 0; + } + + if (jpegSubsamp == TJSAMP_UNKNOWN) + jpegSubsamp = TJSAMP_444; /* This allows for rare corner cases in which a JPEG image can actually be larger than the uncompressed input (we wouldn't mention it if it hadn't @@ -572,57 +941,83 @@ DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp) mcuh = tjMCUHeight[jpegSubsamp]; chromasf = jpegSubsamp == TJSAMP_GRAY ? 0 : 4 * 64 / (mcuw * mcuh); retval = PAD(width, mcuw) * PAD(height, mcuh) * (2ULL + chromasf) + 2048ULL; - if (retval > (unsigned long long)((unsigned long)-1)) - THROWG("tjBufSize(): Image is too large"); + if (retval > (unsigned long long)((unsigned long)-1)) { + SNPRINTF(errStr, JMSG_LENGTH_MAX, "tj3JPEGBufSize(): Image is too large"); + return 0; + } -bailout: - return (unsigned long)retval; + return (size_t)retval; +} + +/* TurboJPEG 1.2+ */ +DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp) +{ + size_t retval; + + if (jpegSubsamp < 0) { + SNPRINTF(errStr, JMSG_LENGTH_MAX, "tjBufSize(): Invalid argument"); + return (unsigned long)-1; + } + + retval = tj3JPEGBufSize(width, height, jpegSubsamp); + return (retval == 0) ? (unsigned long)-1 : (unsigned long)retval; } /* TurboJPEG 1.0+ */ DLLEXPORT unsigned long TJBUFSIZE(int width, int height) { + static const char FUNCTION_NAME[] = "TJBUFSIZE"; unsigned long long retval = 0; if (width < 1 || height < 1) - THROWG("TJBUFSIZE(): Invalid argument"); + THROWG("Invalid argument", (unsigned long)-1); /* This allows for rare corner cases in which a JPEG image can actually be larger than the uncompressed input (we wouldn't mention it if it hadn't happened before.) */ retval = PAD(width, 16) * PAD(height, 16) * 6ULL + 2048ULL; if (retval > (unsigned long long)((unsigned long)-1)) - THROWG("TJBUFSIZE(): Image is too large"); + THROWG("Image is too large", (unsigned long)-1); bailout: return (unsigned long)retval; } -/* TurboJPEG 1.4+ */ -DLLEXPORT unsigned long tjBufSizeYUV2(int width, int align, int height, - int subsamp) +/* TurboJPEG 3+ */ +DLLEXPORT size_t tj3YUVBufSize(int width, int align, int height, int subsamp) { unsigned long long retval = 0; int nc, i; - if (align < 1 || !IS_POW2(align) || subsamp < 0 || subsamp >= TJ_NUMSAMP) - THROWG("tjBufSizeYUV2(): Invalid argument"); + if (align < 1 || !IS_POW2(align) || subsamp < 0 || subsamp >= TJ_NUMSAMP) { + SNPRINTF(errStr, JMSG_LENGTH_MAX, "tj3YUVBufSize(): Invalid argument"); + return 0; + } nc = (subsamp == TJSAMP_GRAY ? 1 : 3); for (i = 0; i < nc; i++) { - int pw = tjPlaneWidth(i, width, subsamp); + int pw = tj3YUVPlaneWidth(i, width, subsamp); int stride = PAD(pw, align); - int ph = tjPlaneHeight(i, height, subsamp); + int ph = tj3YUVPlaneHeight(i, height, subsamp); - if (pw < 0 || ph < 0) return -1; + if (pw == 0 || ph == 0) return 0; else retval += (unsigned long long)stride * ph; } - if (retval > (unsigned long long)((unsigned long)-1)) - THROWG("tjBufSizeYUV2(): Image is too large"); + if (retval > (unsigned long long)((unsigned long)-1)) { + SNPRINTF(errStr, JMSG_LENGTH_MAX, "tj3YUVBufSize(): Image is too large"); + return 0; + } -bailout: - return (unsigned long)retval; + return (size_t)retval; +} + +/* TurboJPEG 1.4+ */ +DLLEXPORT unsigned long tjBufSizeYUV2(int width, int align, int height, + int subsamp) +{ + size_t retval = tj3YUVBufSize(width, align, height, subsamp); + return (retval == 0) ? (unsigned long)-1 : (unsigned long)retval; } /* TurboJPEG 1.2+ */ @@ -638,17 +1033,18 @@ DLLEXPORT unsigned long TJBUFSIZEYUV(int width, int height, int subsamp) } -/* TurboJPEG 1.4+ */ -DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp) +/* TurboJPEG 3+ */ +DLLEXPORT int tj3YUVPlaneWidth(int componentID, int width, int subsamp) { + static const char FUNCTION_NAME[] = "tj3YUVPlaneWidth"; unsigned long long pw, retval = 0; int nc; if (width < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP) - THROWG("tjPlaneWidth(): Invalid argument"); + THROWG("Invalid argument", 0); nc = (subsamp == TJSAMP_GRAY ? 1 : 3); if (componentID < 0 || componentID >= nc) - THROWG("tjPlaneWidth(): Invalid argument"); + THROWG("Invalid argument", 0); pw = PAD((unsigned long long)width, tjMCUWidth[subsamp] / 8); if (componentID == 0) @@ -657,24 +1053,32 @@ DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp) retval = pw * 8 / tjMCUWidth[subsamp]; if (retval > (unsigned long long)INT_MAX) - THROWG("tjPlaneWidth(): Width is too large"); + THROWG("Width is too large", 0); bailout: return (int)retval; } - /* TurboJPEG 1.4+ */ -DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp) +DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp) +{ + int retval = tj3YUVPlaneWidth(componentID, width, subsamp); + return (retval == 0) ? -1 : retval; +} + + +/* TurboJPEG 3+ */ +DLLEXPORT int tj3YUVPlaneHeight(int componentID, int height, int subsamp) { + static const char FUNCTION_NAME[] = "tj3YUVPlaneHeight"; unsigned long long ph, retval = 0; int nc; if (height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP) - THROWG("tjPlaneHeight(): Invalid argument"); + THROWG("Invalid argument", 0); nc = (subsamp == TJSAMP_GRAY ? 1 : 3); if (componentID < 0 || componentID >= nc) - THROWG("tjPlaneHeight(): Invalid argument"); + THROWG("Invalid argument", 0); ph = PAD((unsigned long long)height, tjMCUHeight[subsamp] / 8); if (componentID == 0) @@ -683,110 +1087,94 @@ DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp) retval = ph * 8 / tjMCUHeight[subsamp]; if (retval > (unsigned long long)INT_MAX) - THROWG("tjPlaneHeight(): Height is too large"); + THROWG("Height is too large", 0); bailout: return (int)retval; } - /* TurboJPEG 1.4+ */ -DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride, - int height, int subsamp) +DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp) +{ + int retval = tj3YUVPlaneHeight(componentID, height, subsamp); + return (retval == 0) ? -1 : retval; +} + + +/* TurboJPEG 3+ */ +DLLEXPORT size_t tj3YUVPlaneSize(int componentID, int width, int stride, + int height, int subsamp) { unsigned long long retval = 0; int pw, ph; - if (width < 1 || height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP) - THROWG("tjPlaneSizeYUV(): Invalid argument"); + if (width < 1 || height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP) { + SNPRINTF(errStr, JMSG_LENGTH_MAX, "tj3YUVPlaneSize(): Invalid argument"); + return 0; + } - pw = tjPlaneWidth(componentID, width, subsamp); - ph = tjPlaneHeight(componentID, height, subsamp); - if (pw < 0 || ph < 0) return -1; + pw = tj3YUVPlaneWidth(componentID, width, subsamp); + ph = tj3YUVPlaneHeight(componentID, height, subsamp); + if (pw == 0 || ph == 0) return 0; if (stride == 0) stride = pw; else stride = abs(stride); retval = (unsigned long long)stride * (ph - 1) + pw; - if (retval > (unsigned long long)((unsigned long)-1)) - THROWG("tjPlaneSizeYUV(): Image is too large"); + if (retval > (unsigned long long)((unsigned long)-1)) { + SNPRINTF(errStr, JMSG_LENGTH_MAX, "tj3YUVPlaneSize(): Image is too large"); + return 0; + } -bailout: - return (unsigned long)retval; + return (size_t)retval; +} + +/* TurboJPEG 1.4+ */ +DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride, + int height, int subsamp) +{ + size_t retval = tj3YUVPlaneSize(componentID, width, stride, height, subsamp); + return (retval == 0) ? -1 : (unsigned long)retval; } +/* tj3Compress*() is implemented in turbojpeg-mp.c */ +#define BITS_IN_JSAMPLE 8 +#include "turbojpeg-mp.c" +#undef BITS_IN_JSAMPLE +#define BITS_IN_JSAMPLE 12 +#include "turbojpeg-mp.c" +#undef BITS_IN_JSAMPLE +#define BITS_IN_JSAMPLE 16 +#include "turbojpeg-mp.c" +#undef BITS_IN_JSAMPLE + /* TurboJPEG 1.2+ */ DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf, int width, int pitch, int height, int pixelFormat, unsigned char **jpegBuf, unsigned long *jpegSize, int jpegSubsamp, int jpegQual, int flags) { - int i, retval = 0; - boolean alloc = TRUE; - JSAMPROW *row_pointer = NULL; + static const char FUNCTION_NAME[] = "tjCompress2"; + int retval = 0; + size_t size; - GET_CINSTANCE(handle) - this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE; - if ((this->init & COMPRESS) == 0) - THROW("tjCompress2(): Instance has not been initialized for compression"); + GET_TJINSTANCE(handle, -1); - if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 || - pixelFormat < 0 || pixelFormat >= TJ_NUMPF || jpegBuf == NULL || - jpegSize == NULL || jpegSubsamp < 0 || jpegSubsamp >= TJ_NUMSAMP || + if (jpegSize == NULL || jpegSubsamp < 0 || jpegSubsamp >= TJ_NUMSAMP || jpegQual < 0 || jpegQual > 100) - THROW("tjCompress2(): Invalid argument"); - - if (pitch == 0) pitch = width * tjPixelSize[pixelFormat]; + THROW("Invalid argument"); - if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * height)) == NULL) - THROW("tjCompress2(): Memory allocation failure"); + this->quality = jpegQual; + this->subsamp = jpegSubsamp; + processFlags(handle, flags, COMPRESS); - if (setjmp(this->jerr.setjmp_buffer)) { - /* If we get here, the JPEG code has signaled an error. */ - retval = -1; goto bailout; - } - - cinfo->image_width = width; - cinfo->image_height = height; - -#ifndef NO_PUTENV - if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1"); - else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1"); - else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1"); -#endif - if (flags & TJFLAG_LOSSLESS && jpegSubsamp != TJSAMP_GRAY) - jpegSubsamp = TJSAMP_444; - - if (flags & TJFLAG_NOREALLOC) { - alloc = FALSE; *jpegSize = tjBufSize(width, height, jpegSubsamp); - } - jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc); - if (setCompDefaults(cinfo, pixelFormat, jpegSubsamp, jpegQual, - flags) == -1) { - retval = -1; goto bailout; - } - - jpeg_start_compress(cinfo, TRUE); - for (i = 0; i < height; i++) { - if (flags & TJFLAG_BOTTOMUP) - row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch]; - else - row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch]; - } - while (cinfo->next_scanline < cinfo->image_height) - jpeg_write_scanlines(cinfo, &row_pointer[cinfo->next_scanline], - cinfo->image_height - cinfo->next_scanline); - jpeg_finish_compress(cinfo); + size = (size_t)(*jpegSize); + retval = tj3Compress8(handle, srcBuf, width, pitch, height, pixelFormat, + jpegBuf, &size); + *jpegSize = (unsigned long)size; bailout: - if (cinfo->global_state > CSTATE_START) { - if (alloc) (*cinfo->dest->term_destination) (cinfo); - jpeg_abort_compress(cinfo); - } - free(row_pointer); - if (this->jerr.warning) retval = -1; - this->jerr.stopOnWarning = FALSE; return retval; } @@ -797,7 +1185,7 @@ DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width, int jpegSubsamp, int jpegQual, int flags) { int retval = 0; - unsigned long size; + unsigned long size = jpegSize ? *jpegSize : 0; if (flags & TJ_YUV) { size = tjBufSizeYUV(width, height, jpegSubsamp); @@ -814,12 +1202,13 @@ DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width, } -/* TurboJPEG 1.4+ */ -DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf, - int width, int pitch, int height, - int pixelFormat, unsigned char **dstPlanes, - int *strides, int subsamp, int flags) +/* TurboJPEG 3+ */ +DLLEXPORT int tj3EncodeYUVPlanes8(tjhandle handle, const unsigned char *srcBuf, + int width, int pitch, int height, + int pixelFormat, unsigned char **dstPlanes, + int *strides) { + static const char FUNCTION_NAME[] = "tj3EncodeYUVPlanes8"; JSAMPROW *row_pointer = NULL; JSAMPLE *_tmpbuf[MAX_COMPONENTS], *_tmpbuf2[MAX_COMPONENTS]; JSAMPROW *tmpbuf[MAX_COMPONENTS], *tmpbuf2[MAX_COMPONENTS]; @@ -828,8 +1217,7 @@ DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf, JSAMPLE *ptr; jpeg_component_info *compptr; - GET_CINSTANCE(handle); - this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE; + GET_CINSTANCE(handle) for (i = 0; i < MAX_COMPONENTS; i++) { tmpbuf[i] = NULL; _tmpbuf[i] = NULL; @@ -837,17 +1225,19 @@ DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf, } if ((this->init & COMPRESS) == 0) - THROW("tjEncodeYUVPlanes(): Instance has not been initialized for compression"); + THROW("Instance has not been initialized for compression"); if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 || pixelFormat < 0 || pixelFormat >= TJ_NUMPF || !dstPlanes || - !dstPlanes[0] || subsamp < 0 || subsamp >= TJ_NUMSAMP) - THROW("tjEncodeYUVPlanes(): Invalid argument"); - if (subsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2])) - THROW("tjEncodeYUVPlanes(): Invalid argument"); + !dstPlanes[0]) + THROW("Invalid argument"); + if (this->subsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2])) + THROW("Invalid argument"); + if (this->subsamp == TJSAMP_UNKNOWN) + THROW("TJPARAM_SUBSAMP must be specified"); if (pixelFormat == TJPF_CMYK) - THROW("tjEncodeYUVPlanes(): Cannot generate YUV images from packed-pixel CMYK images"); + THROW("Cannot generate YUV images from packed-pixel CMYK images"); if (pitch == 0) pitch = width * tjPixelSize[pixelFormat]; @@ -858,23 +1248,16 @@ DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf, cinfo->image_width = width; cinfo->image_height = height; + cinfo->data_precision = 8; -#ifndef NO_PUTENV - if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1"); - else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1"); - else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1"); -#endif - - if (setCompDefaults(cinfo, pixelFormat, subsamp, -1, flags) == -1) { - retval = -1; goto bailout; - } + setCompDefaults(this, pixelFormat); /* Execute only the parts of jpeg_start_compress() that we need. If we were to call the whole jpeg_start_compress() function, then it would try to write the file headers, which could overflow the output buffer if the YUV image were very small. */ if (cinfo->global_state != CSTATE_START) - THROW("tjEncodeYUVPlanes(): libjpeg API is in the wrong state"); + THROW("libjpeg API is in the wrong state"); (*cinfo->err->reset_error_mgr) ((j_common_ptr)cinfo); jinit_c_master_control(cinfo, FALSE); jinit_color_converter(cinfo); @@ -885,9 +1268,9 @@ DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf, ph0 = PAD(height, cinfo->max_v_samp_factor); if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL) - THROW("tjEncodeYUVPlanes(): Memory allocation failure"); + THROW("Memory allocation failure"); for (i = 0; i < height; i++) { - if (flags & TJFLAG_BOTTOMUP) + if (this->bottomUp) row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch]; else row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch]; @@ -902,11 +1285,11 @@ DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf, compptr->h_samp_factor, 32) * cinfo->max_v_samp_factor + 32); if (!_tmpbuf[i]) - THROW("tjEncodeYUVPlanes(): Memory allocation failure"); + THROW("Memory allocation failure"); tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * cinfo->max_v_samp_factor); if (!tmpbuf[i]) - THROW("tjEncodeYUVPlanes(): Memory allocation failure"); + THROW("Memory allocation failure"); for (row = 0; row < cinfo->max_v_samp_factor; row++) { unsigned char *_tmpbuf_aligned = (unsigned char *)PAD((JUINTPTR)_tmpbuf[i], 32); @@ -919,10 +1302,10 @@ DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf, (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) * compptr->v_samp_factor + 32); if (!_tmpbuf2[i]) - THROW("tjEncodeYUVPlanes(): Memory allocation failure"); + THROW("Memory allocation failure"); tmpbuf2[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor); if (!tmpbuf2[i]) - THROW("tjEncodeYUVPlanes(): Memory allocation failure"); + THROW("Memory allocation failure"); for (row = 0; row < compptr->v_samp_factor; row++) { unsigned char *_tmpbuf2_aligned = (unsigned char *)PAD((JUINTPTR)_tmpbuf2[i], 32); @@ -934,7 +1317,7 @@ DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf, ph[i] = ph0 * compptr->v_samp_factor / cinfo->max_v_samp_factor; outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]); if (!outbuf[i]) - THROW("tjEncodeYUVPlanes(): Memory allocation failure"); + THROW("Memory allocation failure"); ptr = dstPlanes[i]; for (row = 0; row < ph[i]; row++) { outbuf[i][row] = ptr; @@ -971,45 +1354,94 @@ DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf, free(outbuf[i]); } if (this->jerr.warning) retval = -1; - this->jerr.stopOnWarning = FALSE; return retval; } /* TurboJPEG 1.4+ */ -DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf, - int width, int pitch, int height, int pixelFormat, - unsigned char *dstBuf, int align, int subsamp, - int flags) +DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf, + int width, int pitch, int height, + int pixelFormat, unsigned char **dstPlanes, + int *strides, int subsamp, int flags) { + static const char FUNCTION_NAME[] = "tjEncodeYUVPlanes"; + int retval = 0; + + GET_TJINSTANCE(handle, -1); + + if (subsamp < 0 || subsamp >= TJ_NUMSAMP) + THROW("Invalid argument"); + + this->subsamp = subsamp; + processFlags(handle, flags, COMPRESS); + + return tj3EncodeYUVPlanes8(handle, srcBuf, width, pitch, height, pixelFormat, + dstPlanes, strides); + +bailout: + return retval; +} + + +/* TurboJPEG 3+ */ +DLLEXPORT int tj3EncodeYUV8(tjhandle handle, const unsigned char *srcBuf, + int width, int pitch, int height, int pixelFormat, + unsigned char *dstBuf, int align) +{ + static const char FUNCTION_NAME[] = "tj3EncodeYUV8"; unsigned char *dstPlanes[3]; int pw0, ph0, strides[3], retval = -1; - tjinstance *this = (tjinstance *)handle; - if (!this) THROWG("tjEncodeYUV3(): Invalid handle"); - this->isInstanceError = FALSE; + GET_TJINSTANCE(handle, -1); if (width <= 0 || height <= 0 || dstBuf == NULL || align < 1 || - !IS_POW2(align) || subsamp < 0 || subsamp >= TJ_NUMSAMP) - THROW("tjEncodeYUV3(): Invalid argument"); + !IS_POW2(align)) + THROW("Invalid argument"); - pw0 = tjPlaneWidth(0, width, subsamp); - ph0 = tjPlaneHeight(0, height, subsamp); + if (this->subsamp == TJSAMP_UNKNOWN) + THROW("TJPARAM_SUBSAMP must be specified"); + + pw0 = tj3YUVPlaneWidth(0, width, this->subsamp); + ph0 = tj3YUVPlaneHeight(0, height, this->subsamp); dstPlanes[0] = dstBuf; strides[0] = PAD(pw0, align); - if (subsamp == TJSAMP_GRAY) { + if (this->subsamp == TJSAMP_GRAY) { strides[1] = strides[2] = 0; dstPlanes[1] = dstPlanes[2] = NULL; } else { - int pw1 = tjPlaneWidth(1, width, subsamp); - int ph1 = tjPlaneHeight(1, height, subsamp); + int pw1 = tj3YUVPlaneWidth(1, width, this->subsamp); + int ph1 = tj3YUVPlaneHeight(1, height, this->subsamp); strides[1] = strides[2] = PAD(pw1, align); dstPlanes[1] = dstPlanes[0] + strides[0] * ph0; dstPlanes[2] = dstPlanes[1] + strides[1] * ph1; } - return tjEncodeYUVPlanes(handle, srcBuf, width, pitch, height, pixelFormat, - dstPlanes, strides, subsamp, flags); + return tj3EncodeYUVPlanes8(handle, srcBuf, width, pitch, height, pixelFormat, + dstPlanes, strides); + +bailout: + return retval; +} + +/* TurboJPEG 1.4+ */ +DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf, + int width, int pitch, int height, int pixelFormat, + unsigned char *dstBuf, int align, int subsamp, + int flags) +{ + static const char FUNCTION_NAME[] = "tjEncodeYUV3"; + int retval = 0; + + GET_TJINSTANCE(handle, -1); + + if (subsamp < 0 || subsamp >= TJ_NUMSAMP) + THROW("Invalid argument"); + + this->subsamp = subsamp; + processFlags(handle, flags, COMPRESS); + + return tj3EncodeYUV8(handle, srcBuf, width, pitch, height, pixelFormat, + dstBuf, align); bailout: return retval; @@ -1035,15 +1467,14 @@ DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width, } -/* TurboJPEG 1.4+ */ -DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle, - const unsigned char **srcPlanes, - int width, const int *strides, - int height, int subsamp, - unsigned char **jpegBuf, - unsigned long *jpegSize, int jpegQual, - int flags) +/* TurboJPEG 3+ */ +DLLEXPORT int tj3CompressFromYUVPlanes8(tjhandle handle, + const unsigned char * const *srcPlanes, + int width, const int *strides, + int height, unsigned char **jpegBuf, + size_t *jpegSize) { + static const char FUNCTION_NAME[] = "tj3CompressFromYUVPlanes8"; int i, row, retval = 0; boolean alloc = TRUE; int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS], @@ -1052,21 +1483,24 @@ DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle, JSAMPROW *inbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS]; GET_CINSTANCE(handle) - this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE; for (i = 0; i < MAX_COMPONENTS; i++) { tmpbuf[i] = NULL; inbuf[i] = NULL; } if ((this->init & COMPRESS) == 0) - THROW("tjCompressFromYUVPlanes(): Instance has not been initialized for compression"); + THROW("Instance has not been initialized for compression"); if (!srcPlanes || !srcPlanes[0] || width <= 0 || height <= 0 || - subsamp < 0 || subsamp >= TJ_NUMSAMP || jpegBuf == NULL || - jpegSize == NULL || jpegQual < 0 || jpegQual > 100) - THROW("tjCompressFromYUVPlanes(): Invalid argument"); - if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2])) - THROW("tjCompressFromYUVPlanes(): Invalid argument"); + jpegBuf == NULL || jpegSize == NULL) + THROW("Invalid argument"); + if (this->subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2])) + THROW("Invalid argument"); + + if (this->quality == -1) + THROW("TJPARAM_QUALITY must be specified"); + if (this->subsamp == TJSAMP_UNKNOWN) + THROW("TJPARAM_SUBSAMP must be specified"); if (setjmp(this->jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. */ @@ -1075,20 +1509,13 @@ DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle, cinfo->image_width = width; cinfo->image_height = height; + cinfo->data_precision = 8; -#ifndef NO_PUTENV - if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1"); - else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1"); - else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1"); -#endif - - if (flags & TJFLAG_NOREALLOC) { - alloc = FALSE; *jpegSize = tjBufSize(width, height, subsamp); + if (this->noRealloc) { + alloc = FALSE; *jpegSize = tj3JPEGBufSize(width, height, this->subsamp); } jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc); - if (setCompDefaults(cinfo, TJPF_RGB, subsamp, jpegQual, flags) == -1) { - retval = -1; goto bailout; - } + setCompDefaults(this, TJPF_RGB); cinfo->raw_data_in = TRUE; jpeg_start_compress(cinfo, TRUE); @@ -1106,7 +1533,7 @@ DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle, th[i] = compptr->v_samp_factor * DCTSIZE; tmpbufsize += iw[i] * th[i]; if ((inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL) - THROW("tjCompressFromYUVPlanes(): Memory allocation failure"); + THROW("Memory allocation failure"); ptr = (JSAMPLE *)srcPlanes[i]; for (row = 0; row < ph[i]; row++) { inbuf[i][row] = ptr; @@ -1115,11 +1542,11 @@ DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle, } if (usetmpbuf) { if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL) - THROW("tjCompressFromYUVPlanes(): Memory allocation failure"); + THROW("Memory allocation failure"); ptr = _tmpbuf; for (i = 0; i < cinfo->num_components; i++) { if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL) - THROW("tjCompressFromYUVPlanes(): Memory allocation failure"); + THROW("Memory allocation failure"); for (row = 0; row < th[i]; row++) { tmpbuf[i][row] = ptr; ptr += iw[i]; @@ -1172,46 +1599,108 @@ DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle, } free(_tmpbuf); if (this->jerr.warning) retval = -1; - this->jerr.stopOnWarning = FALSE; return retval; } /* TurboJPEG 1.4+ */ -DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf, - int width, int align, int height, int subsamp, - unsigned char **jpegBuf, - unsigned long *jpegSize, int jpegQual, - int flags) +DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle, + const unsigned char **srcPlanes, + int width, const int *strides, + int height, int subsamp, + unsigned char **jpegBuf, + unsigned long *jpegSize, int jpegQual, + int flags) { + static const char FUNCTION_NAME[] = "tjCompressFromYUVPlanes"; + int retval = 0; + size_t size; + + GET_TJINSTANCE(handle, -1); + + if (subsamp < 0 || subsamp >= TJ_NUMSAMP || jpegSize == NULL || + jpegQual < 0 || jpegQual > 100) + THROW("Invalid argument"); + + this->quality = jpegQual; + this->subsamp = subsamp; + processFlags(handle, flags, COMPRESS); + + size = (size_t)(*jpegSize); + retval = tj3CompressFromYUVPlanes8(handle, srcPlanes, width, strides, height, + jpegBuf, &size); + *jpegSize = (unsigned long)size; + +bailout: + return retval; +} + + +/* TurboJPEG 3+ */ +DLLEXPORT int tj3CompressFromYUV8(tjhandle handle, + const unsigned char *srcBuf, int width, + int align, int height, + unsigned char **jpegBuf, size_t *jpegSize) +{ + static const char FUNCTION_NAME[] = "tj3CompressFromYUV8"; const unsigned char *srcPlanes[3]; int pw0, ph0, strides[3], retval = -1; - tjinstance *this = (tjinstance *)handle; - if (!this) THROWG("tjCompressFromYUV(): Invalid handle"); - this->isInstanceError = FALSE; + GET_TJINSTANCE(handle, -1); if (srcBuf == NULL || width <= 0 || align < 1 || !IS_POW2(align) || - height <= 0 || subsamp < 0 || subsamp >= TJ_NUMSAMP) - THROW("tjCompressFromYUV(): Invalid argument"); + height <= 0) + THROW("Invalid argument"); - pw0 = tjPlaneWidth(0, width, subsamp); - ph0 = tjPlaneHeight(0, height, subsamp); + if (this->subsamp == TJSAMP_UNKNOWN) + THROW("TJPARAM_SUBSAMP must be specified"); + + pw0 = tj3YUVPlaneWidth(0, width, this->subsamp); + ph0 = tj3YUVPlaneHeight(0, height, this->subsamp); srcPlanes[0] = srcBuf; strides[0] = PAD(pw0, align); - if (subsamp == TJSAMP_GRAY) { + if (this->subsamp == TJSAMP_GRAY) { strides[1] = strides[2] = 0; srcPlanes[1] = srcPlanes[2] = NULL; } else { - int pw1 = tjPlaneWidth(1, width, subsamp); - int ph1 = tjPlaneHeight(1, height, subsamp); + int pw1 = tjPlaneWidth(1, width, this->subsamp); + int ph1 = tjPlaneHeight(1, height, this->subsamp); strides[1] = strides[2] = PAD(pw1, align); srcPlanes[1] = srcPlanes[0] + strides[0] * ph0; srcPlanes[2] = srcPlanes[1] + strides[1] * ph1; } - return tjCompressFromYUVPlanes(handle, srcPlanes, width, strides, height, - subsamp, jpegBuf, jpegSize, jpegQual, flags); + return tj3CompressFromYUVPlanes8(handle, srcPlanes, width, strides, height, + jpegBuf, jpegSize); + +bailout: + return retval; +} + +/* TurboJPEG 1.4+ */ +DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf, + int width, int align, int height, int subsamp, + unsigned char **jpegBuf, + unsigned long *jpegSize, int jpegQual, + int flags) +{ + static const char FUNCTION_NAME[] = "tjCompressFromYUV"; + int retval = -1; + size_t size; + + GET_TJINSTANCE(handle, -1); + + if (subsamp < 0 || subsamp >= TJ_NUMSAMP) + THROW("Invalid argument"); + + this->quality = jpegQual; + this->subsamp = subsamp; + processFlags(handle, flags, COMPRESS); + + size = (size_t)(*jpegSize); + retval = tj3CompressFromYUV8(handle, srcBuf, width, align, height, jpegBuf, + &size); + *jpegSize = (unsigned long)size; bailout: return retval; @@ -1251,35 +1740,24 @@ static tjhandle _tjInitDecompress(tjinstance *this) /* TurboJPEG 1.0+ */ DLLEXPORT tjhandle tjInitDecompress(void) { - tjinstance *this; - - if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) { - SNPRINTF(errStr, JMSG_LENGTH_MAX, - "tjInitDecompress(): Memory allocation failure"); - return NULL; - } - memset(this, 0, sizeof(tjinstance)); - SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "No error"); - return _tjInitDecompress(this); + return tj3Init(TJINIT_DECOMPRESS); } -/* TurboJPEG 2.2+ */ -DLLEXPORT int tjDecompressHeader4(tjhandle handle, +/* TurboJPEG 3+ */ +DLLEXPORT int tj3DecompressHeader(tjhandle handle, const unsigned char *jpegBuf, - unsigned long jpegSize, int *width, - int *height, int *jpegSubsamp, - int *jpegColorspace, int *jpegFlags) + size_t jpegSize) { + static const char FUNCTION_NAME[] = "tj3DecompressHeader"; int retval = 0; GET_DINSTANCE(handle); if ((this->init & DECOMPRESS) == 0) - THROW("tjDecompressHeader4(): Instance has not been initialized for decompression"); + THROW("Instance has not been initialized for decompression"); - if (jpegBuf == NULL || jpegSize <= 0 || width == NULL || height == NULL || - jpegSubsamp == NULL || jpegColorspace == NULL || jpegFlags == NULL) - THROW("tjDecompressHeader4(): Invalid argument"); + if (jpegBuf == NULL || jpegSize <= 0) + THROW("Invalid argument"); if (setjmp(this->jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. */ @@ -1295,30 +1773,14 @@ DLLEXPORT int tjDecompressHeader4(tjhandle handle, if (jpeg_read_header(dinfo, FALSE) == JPEG_HEADER_TABLES_ONLY) return 0; - *width = dinfo->image_width; - *height = dinfo->image_height; - *jpegSubsamp = getSubsamp(dinfo); - switch (dinfo->jpeg_color_space) { - case JCS_GRAYSCALE: *jpegColorspace = TJCS_GRAY; break; - case JCS_RGB: *jpegColorspace = TJCS_RGB; break; - case JCS_YCbCr: *jpegColorspace = TJCS_YCbCr; break; - case JCS_CMYK: *jpegColorspace = TJCS_CMYK; break; - case JCS_YCCK: *jpegColorspace = TJCS_YCCK; break; - default: *jpegColorspace = -1; break; - } - *jpegFlags = 0; - if (dinfo->progressive_mode) *jpegFlags |= TJFLAG_PROGRESSIVE; - if (dinfo->arith_code) *jpegFlags |= TJFLAG_ARITHMETIC; - if (dinfo->master->lossless) *jpegFlags |= TJFLAG_LOSSLESS; + setDecompParameters(this); jpeg_abort_decompress(dinfo); - if (*jpegSubsamp < 0) - THROW("tjDecompressHeader4(): Could not determine subsampling type for JPEG image"); - if (*jpegColorspace < 0) - THROW("tjDecompressHeader4(): Could not determine colorspace of JPEG image"); - if (*width < 1 || *height < 1) - THROW("tjDecompressHeader4(): Invalid data returned in header"); + if (this->colorspace < 0) + THROW("Could not determine colorspace of JPEG image"); + if (this->jpegWidth < 1 || this->jpegHeight < 1) + THROW("Invalid data returned in header"); bailout: if (this->jerr.warning) retval = -1; @@ -1332,10 +1794,26 @@ DLLEXPORT int tjDecompressHeader3(tjhandle handle, int *height, int *jpegSubsamp, int *jpegColorspace) { - int flags; + static const char FUNCTION_NAME[] = "tjDecompressHeader3"; + int retval = 0; + + GET_TJINSTANCE(handle, -1); - return tjDecompressHeader4(handle, jpegBuf, jpegSize, width, height, - jpegSubsamp, jpegColorspace, &flags); + if (width == NULL || height == NULL || jpegSubsamp == NULL || + jpegColorspace == NULL) + THROW("Invalid argument"); + + retval = tj3DecompressHeader(handle, jpegBuf, jpegSize); + + *width = tj3Get(handle, TJPARAM_JPEGWIDTH); + *height = tj3Get(handle, TJPARAM_JPEGHEIGHT); + *jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP); + if (*jpegSubsamp == TJSAMP_UNKNOWN) + THROW("Could not determine subsampling level of JPEG image"); + *jpegColorspace = tj3Get(handle, TJPARAM_COLORSPACE); + +bailout: + return retval; } /* TurboJPEG 1.1+ */ @@ -1361,12 +1839,12 @@ DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf, } -/* TurboJPEG 1.2+ */ -DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numScalingFactors) +/* TurboJPEG 3+ */ +DLLEXPORT tjscalingfactor *tj3GetScalingFactors(int *numScalingFactors) { if (numScalingFactors == NULL) { SNPRINTF(errStr, JMSG_LENGTH_MAX, - "tjGetScalingFactors(): Invalid argument"); + "tj3GetScalingFactors(): Invalid argument"); return NULL; } @@ -1374,6 +1852,88 @@ DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numScalingFactors) return (tjscalingfactor *)sf; } +/* TurboJPEG 1.2+ */ +DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numScalingFactors) +{ + return tj3GetScalingFactors(numScalingFactors); +} + + +/* TurboJPEG 3+ */ +DLLEXPORT int tj3SetScalingFactor(tjhandle handle, + tjscalingfactor scalingFactor) +{ + static const char FUNCTION_NAME[] = "tj3SetScalingFactor"; + int i, retval = 0; + + GET_TJINSTANCE(handle, -1); + if ((this->init & DECOMPRESS) == 0) + THROW("Instance has not been initialized for decompression"); + + for (i = 0; i < NUMSF; i++) { + if (scalingFactor.num == sf[i].num && scalingFactor.denom == sf[i].denom) + break; + } + if (i >= NUMSF) + THROW("Unsupported scaling factor"); + + this->scalingFactor = scalingFactor; + +bailout: + return retval; +} + + +/* TurboJPEG 3+ */ +DLLEXPORT int tj3SetCroppingRegion(tjhandle handle, tjregion croppingRegion) +{ + static const char FUNCTION_NAME[] = "tj3SetCroppingRegion"; + int retval = 0, scaledWidth, scaledHeight; + + GET_TJINSTANCE(handle, -1); + if ((this->init & DECOMPRESS) == 0) + THROW("Instance has not been initialized for decompression"); + + if (croppingRegion.x == 0 && croppingRegion.y == 0 && + croppingRegion.w == 0 && croppingRegion.h == 0) { + this->croppingRegion = croppingRegion; + return 0; + } + + if (croppingRegion.x < 0 || croppingRegion.y < 0 || croppingRegion.w < 0 || + croppingRegion.h < 0) + THROW("Invalid cropping region"); + if (this->jpegWidth < 0 || this->jpegHeight < 0) + THROW("JPEG header has not yet been read"); + if (this->precision == 16 || this->lossless) + THROW("Cannot partially decompress lossless JPEG images"); + if (this->subsamp == TJSAMP_UNKNOWN) + THROW("Could not determine subsampling level of JPEG image"); + + scaledWidth = TJSCALED(this->jpegWidth, this->scalingFactor); + scaledHeight = TJSCALED(this->jpegHeight, this->scalingFactor); + + if (croppingRegion.x % + TJSCALED(tjMCUWidth[this->subsamp], this->scalingFactor) != 0) + THROWI("The left boundary of the cropping region is not divisible by the scaled MCU width (%d)", + TJSCALED(tjMCUWidth[this->subsamp], this->scalingFactor)); + if (croppingRegion.w == 0) + croppingRegion.w = scaledWidth - croppingRegion.x; + if (croppingRegion.h == 0) + croppingRegion.h = scaledHeight - croppingRegion.y; + if (croppingRegion.w < 0 || croppingRegion.h < 0 || + croppingRegion.x + croppingRegion.w > scaledWidth || + croppingRegion.y + croppingRegion.h > scaledHeight) + THROW("The cropping region exceeds the scaled image dimensions"); + + this->croppingRegion = croppingRegion; + +bailout: + return retval; +} + + +/* tj3Decompress*() is implemented in turbojpeg-mp.c */ /* TurboJPEG 1.2+ */ DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf, @@ -1381,32 +1941,15 @@ DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf, int width, int pitch, int height, int pixelFormat, int flags) { - JSAMPROW *row_pointer = NULL; + static const char FUNCTION_NAME[] = "tjDecompress2"; int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh; - struct my_progress_mgr progress; GET_DINSTANCE(handle); - this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE; if ((this->init & DECOMPRESS) == 0) - THROW("tjDecompress2(): Instance has not been initialized for decompression"); - - if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 || - pitch < 0 || height < 0 || pixelFormat < 0 || pixelFormat >= TJ_NUMPF) - THROW("tjDecompress2(): Invalid argument"); - -#ifndef NO_PUTENV - if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1"); - else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1"); - else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1"); -#endif + THROW("Instance has not been initialized for decompression"); - if (flags & TJFLAG_LIMITSCANS) { - memset(&progress, 0, sizeof(struct my_progress_mgr)); - progress.pub.progress_monitor = my_progress_monitor; - progress.this = this; - dinfo->progress = &progress.pub; - } else - dinfo->progress = NULL; + if (jpegBuf == NULL || jpegSize <= 0 || width < 0 || height < 0) + THROW("Invalid argument"); if (setjmp(this->jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. */ @@ -1415,10 +1958,6 @@ DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf, jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize); jpeg_read_header(dinfo, TRUE); - this->dinfo.out_color_space = pf2cs[pixelFormat]; - if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST; - if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE; - jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height; if (width == 0) width = jpegwidth; if (height == 0) height = jpegheight; @@ -1429,37 +1968,20 @@ DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf, break; } if (i >= NUMSF) - THROW("tjDecompress2(): Could not scale down to desired image dimensions"); - width = scaledw; height = scaledh; - dinfo->scale_num = sf[i].num; - dinfo->scale_denom = sf[i].denom; + THROW("Could not scale down to desired image dimensions"); - jpeg_start_decompress(dinfo); - if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat]; + processFlags(handle, flags, DECOMPRESS); - if ((row_pointer = - (JSAMPROW *)malloc(sizeof(JSAMPROW) * dinfo->output_height)) == NULL) - THROW("tjDecompress2(): Memory allocation failure"); - if (setjmp(this->jerr.setjmp_buffer)) { - /* If we get here, the JPEG code has signaled an error. */ - retval = -1; goto bailout; - } - for (i = 0; i < (int)dinfo->output_height; i++) { - if (flags & TJFLAG_BOTTOMUP) - row_pointer[i] = &dstBuf[(dinfo->output_height - i - 1) * (size_t)pitch]; - else - row_pointer[i] = &dstBuf[i * (size_t)pitch]; - } - while (dinfo->output_scanline < dinfo->output_height) - jpeg_read_scanlines(dinfo, &row_pointer[dinfo->output_scanline], - dinfo->output_height - dinfo->output_scanline); - jpeg_finish_decompress(dinfo); + this->headerRead = TRUE; + if (tj3SetScalingFactor(handle, sf[i]) == -1) + return -1; + if (tj3SetCroppingRegion(handle, TJUNCROPPED) == -1) + return -1; + return tj3Decompress8(handle, jpegBuf, jpegSize, dstBuf, pitch, pixelFormat); bailout: if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo); - free(row_pointer); if (this->jerr.warning) retval = -1; - this->jerr.stopOnWarning = FALSE; return retval; } @@ -1477,41 +1999,41 @@ DLLEXPORT int tjDecompress(tjhandle handle, unsigned char *jpegBuf, } -static void setDecodeDefaults(struct jpeg_decompress_struct *dinfo, - int pixelFormat, int subsamp, int flags) +static void setDecodeDefaults(tjinstance *this, int pixelFormat) { int i; - dinfo->scale_num = dinfo->scale_denom = 1; + this->dinfo.scale_num = this->dinfo.scale_denom = 1; - if (subsamp == TJSAMP_GRAY) { - dinfo->num_components = dinfo->comps_in_scan = 1; - dinfo->jpeg_color_space = JCS_GRAYSCALE; + if (this->subsamp == TJSAMP_GRAY) { + this->dinfo.num_components = this->dinfo.comps_in_scan = 1; + this->dinfo.jpeg_color_space = JCS_GRAYSCALE; } else { - dinfo->num_components = dinfo->comps_in_scan = 3; - dinfo->jpeg_color_space = JCS_YCbCr; + this->dinfo.num_components = this->dinfo.comps_in_scan = 3; + this->dinfo.jpeg_color_space = JCS_YCbCr; } - dinfo->comp_info = (jpeg_component_info *) - (*dinfo->mem->alloc_small) ((j_common_ptr)dinfo, JPOOL_IMAGE, - dinfo->num_components * - sizeof(jpeg_component_info)); + this->dinfo.comp_info = (jpeg_component_info *) + (*this->dinfo.mem->alloc_small) ((j_common_ptr)&this->dinfo, JPOOL_IMAGE, + this->dinfo.num_components * + sizeof(jpeg_component_info)); - for (i = 0; i < dinfo->num_components; i++) { - jpeg_component_info *compptr = &dinfo->comp_info[i]; + for (i = 0; i < this->dinfo.num_components; i++) { + jpeg_component_info *compptr = &this->dinfo.comp_info[i]; - compptr->h_samp_factor = (i == 0) ? tjMCUWidth[subsamp] / 8 : 1; - compptr->v_samp_factor = (i == 0) ? tjMCUHeight[subsamp] / 8 : 1; + compptr->h_samp_factor = (i == 0) ? tjMCUWidth[this->subsamp] / 8 : 1; + compptr->v_samp_factor = (i == 0) ? tjMCUHeight[this->subsamp] / 8 : 1; compptr->component_index = i; compptr->component_id = i + 1; compptr->quant_tbl_no = compptr->dc_tbl_no = compptr->ac_tbl_no = (i == 0) ? 0 : 1; - dinfo->cur_comp_info[i] = compptr; + this->dinfo.cur_comp_info[i] = compptr; } - dinfo->data_precision = 8; + this->dinfo.data_precision = 8; for (i = 0; i < 2; i++) { - if (dinfo->quant_tbl_ptrs[i] == NULL) - dinfo->quant_tbl_ptrs[i] = jpeg_alloc_quant_table((j_common_ptr)dinfo); + if (this->dinfo.quant_tbl_ptrs[i] == NULL) + this->dinfo.quant_tbl_ptrs[i] = + jpeg_alloc_quant_table((j_common_ptr)&this->dinfo); } } @@ -1525,13 +2047,14 @@ static void my_reset_marker_reader(j_decompress_ptr dinfo) { } -/* TurboJPEG 1.4+ */ -DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle, - const unsigned char **srcPlanes, - const int *strides, int subsamp, - unsigned char *dstBuf, int width, int pitch, - int height, int pixelFormat, int flags) +/* TurboJPEG 3+ */ +DLLEXPORT int tj3DecodeYUVPlanes8(tjhandle handle, + const unsigned char * const *srcPlanes, + const int *strides, unsigned char *dstBuf, + int width, int pitch, int height, + int pixelFormat) { + static const char FUNCTION_NAME[] = "tj3DecodeYUVPlanes8"; JSAMPROW *row_pointer = NULL; JSAMPLE *_tmpbuf[MAX_COMPONENTS]; JSAMPROW *tmpbuf[MAX_COMPONENTS], *inbuf[MAX_COMPONENTS]; @@ -1542,44 +2065,38 @@ DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle, void (*old_reset_marker_reader) (j_decompress_ptr); GET_DINSTANCE(handle); - this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE; for (i = 0; i < MAX_COMPONENTS; i++) { tmpbuf[i] = NULL; _tmpbuf[i] = NULL; inbuf[i] = NULL; } if ((this->init & DECOMPRESS) == 0) - THROW("tjDecodeYUVPlanes(): Instance has not been initialized for decompression"); + THROW("Instance has not been initialized for decompression"); - if (!srcPlanes || !srcPlanes[0] || subsamp < 0 || subsamp >= TJ_NUMSAMP || - dstBuf == NULL || width <= 0 || pitch < 0 || height <= 0 || - pixelFormat < 0 || pixelFormat >= TJ_NUMPF) - THROW("tjDecodeYUVPlanes(): Invalid argument"); - if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2])) - THROW("tjDecodeYUVPlanes(): Invalid argument"); + if (!srcPlanes || !srcPlanes[0] || dstBuf == NULL || width <= 0 || + pitch < 0 || height <= 0 || pixelFormat < 0 || pixelFormat >= TJ_NUMPF) + THROW("Invalid argument"); + if (this->subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2])) + THROW("Invalid argument"); if (setjmp(this->jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. */ retval = -1; goto bailout; } + if (this->subsamp == TJSAMP_UNKNOWN) + THROW("TJPARAM_SUBSAMP must be specified"); if (pixelFormat == TJPF_CMYK) - THROW("tjDecodeYUVPlanes(): Cannot decode YUV images into packed-pixel CMYK images."); + THROW("Cannot decode YUV images into packed-pixel CMYK images."); if (pitch == 0) pitch = width * tjPixelSize[pixelFormat]; dinfo->image_width = width; dinfo->image_height = height; -#ifndef NO_PUTENV - if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1"); - else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1"); - else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1"); -#endif - dinfo->progressive_mode = dinfo->inputctl->has_multiple_scans = FALSE; dinfo->Ss = dinfo->Ah = dinfo->Al = 0; dinfo->Se = DCTSIZE2 - 1; - setDecodeDefaults(dinfo, pixelFormat, subsamp, flags); + setDecodeDefaults(this, pixelFormat); old_read_markers = dinfo->marker->read_markers; dinfo->marker->read_markers = my_read_markers; old_reset_marker_reader = dinfo->marker->reset_marker_reader; @@ -1589,7 +2106,7 @@ DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle, dinfo->marker->reset_marker_reader = old_reset_marker_reader; this->dinfo.out_color_space = pf2cs[pixelFormat]; - if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST; + this->dinfo.dct_method = this->fastDCT ? JDCT_FASTEST : JDCT_ISLOW; dinfo->do_fancy_upsampling = FALSE; dinfo->Se = DCTSIZE2 - 1; jinit_master_decompress(dinfo); @@ -1601,9 +2118,9 @@ DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle, if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat]; if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL) - THROW("tjDecodeYUVPlanes(): Memory allocation failure"); + THROW("Memory allocation failure"); for (i = 0; i < height; i++) { - if (flags & TJFLAG_BOTTOMUP) + if (this->bottomUp) row_pointer[i] = &dstBuf[(height - i - 1) * (size_t)pitch]; else row_pointer[i] = &dstBuf[i * (size_t)pitch]; @@ -1617,10 +2134,10 @@ DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle, (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) * compptr->v_samp_factor + 32); if (!_tmpbuf[i]) - THROW("tjDecodeYUVPlanes(): Memory allocation failure"); + THROW("Memory allocation failure"); tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor); if (!tmpbuf[i]) - THROW("tjDecodeYUVPlanes(): Memory allocation failure"); + THROW("Memory allocation failure"); for (row = 0; row < compptr->v_samp_factor; row++) { unsigned char *_tmpbuf_aligned = (unsigned char *)PAD((JUINTPTR)_tmpbuf[i], 32); @@ -1632,7 +2149,7 @@ DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle, ph[i] = ph0 * compptr->v_samp_factor / dinfo->max_v_samp_factor; inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]); if (!inbuf[i]) - THROW("tjDecodeYUVPlanes(): Memory allocation failure"); + THROW("Memory allocation failure"); ptr = (JSAMPLE *)srcPlanes[i]; for (row = 0; row < ph[i]; row++) { inbuf[i][row] = ptr; @@ -1668,59 +2185,110 @@ DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle, free(inbuf[i]); } if (this->jerr.warning) retval = -1; - this->jerr.stopOnWarning = FALSE; return retval; } /* TurboJPEG 1.4+ */ -DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf, - int align, int subsamp, unsigned char *dstBuf, - int width, int pitch, int height, int pixelFormat, - int flags) +DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle, + const unsigned char **srcPlanes, + const int *strides, int subsamp, + unsigned char *dstBuf, int width, int pitch, + int height, int pixelFormat, int flags) +{ + static const char FUNCTION_NAME[] = "tjDecodeYUVPlanes"; + int retval = 0; + + GET_TJINSTANCE(handle, -1); + + if (subsamp < 0 || subsamp >= TJ_NUMSAMP) + THROW("Invalid argument"); + + this->subsamp = subsamp; + processFlags(handle, flags, DECOMPRESS); + + return tj3DecodeYUVPlanes8(handle, srcPlanes, strides, dstBuf, width, pitch, + height, pixelFormat); + +bailout: + return retval; +} + + +/* TurboJPEG 3+ */ +DLLEXPORT int tj3DecodeYUV8(tjhandle handle, const unsigned char *srcBuf, + int align, unsigned char *dstBuf, int width, + int pitch, int height, int pixelFormat) { + static const char FUNCTION_NAME[] = "tj3DecodeYUV8"; const unsigned char *srcPlanes[3]; int pw0, ph0, strides[3], retval = -1; - tjinstance *this = (tjinstance *)handle; - if (!this) THROWG("tjDecodeYUV(): Invalid handle"); - this->isInstanceError = FALSE; + GET_TJINSTANCE(handle, -1); - if (srcBuf == NULL || align < 1 || !IS_POW2(align) || subsamp < 0 || - subsamp >= TJ_NUMSAMP || width <= 0 || height <= 0) - THROW("tjDecodeYUV(): Invalid argument"); + if (srcBuf == NULL || align < 1 || !IS_POW2(align) || width <= 0 || + height <= 0) + THROW("Invalid argument"); - pw0 = tjPlaneWidth(0, width, subsamp); - ph0 = tjPlaneHeight(0, height, subsamp); + if (this->subsamp == TJSAMP_UNKNOWN) + THROW("TJPARAM_SUBSAMP must be specified"); + + pw0 = tj3YUVPlaneWidth(0, width, this->subsamp); + ph0 = tj3YUVPlaneHeight(0, height, this->subsamp); srcPlanes[0] = srcBuf; strides[0] = PAD(pw0, align); - if (subsamp == TJSAMP_GRAY) { + if (this->subsamp == TJSAMP_GRAY) { strides[1] = strides[2] = 0; srcPlanes[1] = srcPlanes[2] = NULL; } else { - int pw1 = tjPlaneWidth(1, width, subsamp); - int ph1 = tjPlaneHeight(1, height, subsamp); + int pw1 = tj3YUVPlaneWidth(1, width, this->subsamp); + int ph1 = tj3YUVPlaneHeight(1, height, this->subsamp); strides[1] = strides[2] = PAD(pw1, align); srcPlanes[1] = srcPlanes[0] + strides[0] * ph0; srcPlanes[2] = srcPlanes[1] + strides[1] * ph1; } - return tjDecodeYUVPlanes(handle, srcPlanes, strides, subsamp, dstBuf, width, - pitch, height, pixelFormat, flags); + return tj3DecodeYUVPlanes8(handle, srcPlanes, strides, dstBuf, width, pitch, + height, pixelFormat); bailout: return retval; } /* TurboJPEG 1.4+ */ -DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle, - const unsigned char *jpegBuf, - unsigned long jpegSize, - unsigned char **dstPlanes, int width, - int *strides, int height, int flags) +DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf, + int align, int subsamp, unsigned char *dstBuf, + int width, int pitch, int height, int pixelFormat, + int flags) +{ + static const char FUNCTION_NAME[] = "tjDecodeYUV"; + int retval = -1; + + GET_TJINSTANCE(handle, -1); + + if (subsamp < 0 || subsamp >= TJ_NUMSAMP) + THROW("Invalid argument"); + + this->subsamp = subsamp; + processFlags(handle, flags, DECOMPRESS); + + return tj3DecodeYUV8(handle, srcBuf, align, dstBuf, width, pitch, height, + pixelFormat); + +bailout: + return retval; +} + + +/* TurboJPEG 3+ */ +DLLEXPORT int tj3DecompressToYUVPlanes8(tjhandle handle, + const unsigned char *jpegBuf, + size_t jpegSize, + unsigned char **dstPlanes, + int *strides) { - int i, sfi, row, retval = 0; - int jpegwidth, jpegheight, jpegSubsamp, scaledw, scaledh; + static const char FUNCTION_NAME[] = "tj3DecompressToYUVPlanes8"; + int i, row, retval = 0; int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS], tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS]; JSAMPLE *_tmpbuf = NULL, *ptr; @@ -1729,26 +2297,18 @@ DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle, struct my_progress_mgr progress; GET_DINSTANCE(handle); - this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE; for (i = 0; i < MAX_COMPONENTS; i++) { tmpbuf[i] = NULL; outbuf[i] = NULL; } if ((this->init & DECOMPRESS) == 0) - THROW("tjDecompressToYUVPlanes(): Instance has not been initialized for decompression"); + THROW("Instance has not been initialized for decompression"); - if (jpegBuf == NULL || jpegSize <= 0 || !dstPlanes || !dstPlanes[0] || - width < 0 || height < 0) - THROW("tjDecompressToYUVPlanes(): Invalid argument"); - -#ifndef NO_PUTENV - if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1"); - else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1"); - else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1"); -#endif + if (jpegBuf == NULL || jpegSize <= 0 || !dstPlanes || !dstPlanes[0]) + THROW("Invalid argument"); - if (flags & TJFLAG_LIMITSCANS) { + if (this->scanLimit) { memset(&progress, 0, sizeof(struct my_progress_mgr)); progress.pub.progress_monitor = my_progress_monitor; progress.this = this; @@ -1765,35 +2325,22 @@ DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle, jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize); jpeg_read_header(dinfo, TRUE); } - this->headerRead = 0; - jpegSubsamp = getSubsamp(dinfo); - if (jpegSubsamp < 0) - THROW("tjDecompressToYUVPlanes(): Could not determine subsampling type for JPEG image"); + this->headerRead = FALSE; + setDecompParameters(this); + if (this->subsamp == TJSAMP_UNKNOWN) + THROW("Could not determine subsampling level of JPEG image"); - if (jpegSubsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2])) - THROW("tjDecompressToYUVPlanes(): Invalid argument"); + if (this->subsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2])) + THROW("Invalid argument"); - jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height; - if (width == 0) width = jpegwidth; - if (height == 0) height = jpegheight; - for (i = 0; i < NUMSF; i++) { - scaledw = TJSCALED(jpegwidth, sf[i]); - scaledh = TJSCALED(jpegheight, sf[i]); - if (scaledw <= width && scaledh <= height) - break; - } - if (i >= NUMSF) - THROW("tjDecompressToYUVPlanes(): Could not scale down to desired image dimensions"); if (dinfo->num_components > 3) - THROW("tjDecompressToYUVPlanes(): JPEG image must have 3 or fewer components"); + THROW("JPEG image must have 3 or fewer components"); - width = scaledw; height = scaledh; - dinfo->scale_num = sf[i].num; - dinfo->scale_denom = sf[i].denom; - sfi = i; + dinfo->scale_num = this->scalingFactor.num; + dinfo->scale_denom = this->scalingFactor.denom; jpeg_calc_output_dimensions(dinfo); - dctsize = DCTSIZE * sf[sfi].num / sf[sfi].denom; + dctsize = DCTSIZE * this->scalingFactor.num / this->scalingFactor.denom; for (i = 0; i < dinfo->num_components; i++) { jpeg_component_info *compptr = &dinfo->comp_info[i]; @@ -1801,13 +2348,13 @@ DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle, iw[i] = compptr->width_in_blocks * dctsize; ih = compptr->height_in_blocks * dctsize; - pw[i] = tjPlaneWidth(i, dinfo->output_width, jpegSubsamp); - ph[i] = tjPlaneHeight(i, dinfo->output_height, jpegSubsamp); + pw[i] = tj3YUVPlaneWidth(i, dinfo->output_width, this->subsamp); + ph[i] = tj3YUVPlaneHeight(i, dinfo->output_height, this->subsamp); if (iw[i] != pw[i] || ih != ph[i]) usetmpbuf = 1; th[i] = compptr->v_samp_factor * dctsize; tmpbufsize += iw[i] * th[i]; if ((outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL) - THROW("tjDecompressToYUVPlanes(): Memory allocation failure"); + THROW("Memory allocation failure"); ptr = dstPlanes[i]; for (row = 0; row < ph[i]; row++) { outbuf[i][row] = ptr; @@ -1816,11 +2363,11 @@ DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle, } if (usetmpbuf) { if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL) - THROW("tjDecompressToYUVPlanes(): Memory allocation failure"); + THROW("Memory allocation failure"); ptr = _tmpbuf; for (i = 0; i < dinfo->num_components; i++) { if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL) - THROW("tjDecompressToYUVPlanes(): Memory allocation failure"); + THROW("Memory allocation failure"); for (row = 0; row < th[i]; row++) { tmpbuf[i][row] = ptr; ptr += iw[i]; @@ -1833,8 +2380,8 @@ DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle, retval = -1; goto bailout; } - if (flags & TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling = FALSE; - if (flags & TJFLAG_FASTDCT) dinfo->dct_method = JDCT_FASTEST; + dinfo->do_fancy_upsampling = !this->fastUpsample; + dinfo->dct_method = this->fastDCT ? JDCT_FASTEST : JDCT_ISLOW; dinfo->raw_data_out = TRUE; jpeg_start_decompress(dinfo); @@ -1846,7 +2393,7 @@ DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle, for (i = 0; i < dinfo->num_components; i++) { jpeg_component_info *compptr = &dinfo->comp_info[i]; - if (jpegSubsamp == TJSAMP_420) { + if (this->subsamp == TJSAMP_420) { /* When 4:2:0 subsampling is used with IDCT scaling, libjpeg will try to be clever and use the IDCT to perform upsampling on the U and V planes. For instance, if the output image is to be scaled by 1/2 @@ -1858,8 +2405,8 @@ DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle, internal libjpeg parameters to force it to use the "scaled" IDCT functions on the U and V planes. */ compptr->_DCT_scaled_size = dctsize; - compptr->MCU_sample_width = tjMCUWidth[jpegSubsamp] * - sf[sfi].num / sf[sfi].denom * + compptr->MCU_sample_width = tjMCUWidth[this->subsamp] * + this->scalingFactor.num / this->scalingFactor.denom * compptr->v_samp_factor / dinfo->max_v_samp_factor; dinfo->idct->inverse_DCT[i] = dinfo->idct->inverse_DCT[0]; } @@ -1889,37 +2436,33 @@ DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle, } free(_tmpbuf); if (this->jerr.warning) retval = -1; - this->jerr.stopOnWarning = FALSE; return retval; } /* TurboJPEG 1.4+ */ -DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf, - unsigned long jpegSize, unsigned char *dstBuf, - int width, int align, int height, int flags) +DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle, + const unsigned char *jpegBuf, + unsigned long jpegSize, + unsigned char **dstPlanes, int width, + int *strides, int height, int flags) { - unsigned char *dstPlanes[3]; - int pw0, ph0, strides[3], retval = -1, jpegSubsamp = -1; - int i, jpegwidth, jpegheight, scaledw, scaledh; + static const char FUNCTION_NAME[] = "tjDecompressToYUVPlanes"; + int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh; GET_DINSTANCE(handle); - this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE; + if ((this->init & DECOMPRESS) == 0) + THROW("Instance has not been initialized for decompression"); - if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 || - align < 1 || !IS_POW2(align) || height < 0) - THROW("tjDecompressToYUV2(): Invalid argument"); + if (jpegBuf == NULL || jpegSize <= 0 || width < 0 || height < 0) + THROW("Invalid argument"); if (setjmp(this->jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. */ - return -1; + retval = -1; goto bailout; } jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize); jpeg_read_header(dinfo, TRUE); - jpegSubsamp = getSubsamp(dinfo); - if (jpegSubsamp < 0) - THROW("tjDecompressToYUV2(): Could not determine subsampling type for JPEG image"); - jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height; if (width == 0) width = jpegwidth; if (height == 0) height = jpegheight; @@ -1930,32 +2473,128 @@ DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf, break; } if (i >= NUMSF) - THROW("tjDecompressToYUV2(): Could not scale down to desired image dimensions"); + THROW("Could not scale down to desired image dimensions"); - width = scaledw; height = scaledh; + processFlags(handle, flags, DECOMPRESS); + + this->headerRead = TRUE; + if (tj3SetScalingFactor(handle, sf[i]) == -1) + return -1; + return tj3DecompressToYUVPlanes8(handle, jpegBuf, jpegSize, dstPlanes, + strides); - pw0 = tjPlaneWidth(0, width, jpegSubsamp); - ph0 = tjPlaneHeight(0, height, jpegSubsamp); +bailout: + if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo); + if (this->jerr.warning) retval = -1; + return retval; +} + + +/* TurboJPEG 3+ */ +DLLEXPORT int tj3DecompressToYUV8(tjhandle handle, + const unsigned char *jpegBuf, + size_t jpegSize, + unsigned char *dstBuf, int align) +{ + static const char FUNCTION_NAME[] = "tj3DecompressToYUV8"; + unsigned char *dstPlanes[3]; + int pw0, ph0, strides[3], retval = -1; + int width, height; + + GET_DINSTANCE(handle); + + if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || align < 1 || + !IS_POW2(align)) + THROW("Invalid argument"); + + if (setjmp(this->jerr.setjmp_buffer)) { + /* If we get here, the JPEG code has signaled an error. */ + retval = -1; goto bailout; + } + + if (!this->headerRead) { + jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize); + jpeg_read_header(dinfo, TRUE); + } + setDecompParameters(this); + if (this->subsamp == TJSAMP_UNKNOWN) + THROW("Could not determine subsampling level of JPEG image"); + + width = TJSCALED(dinfo->image_width, this->scalingFactor); + height = TJSCALED(dinfo->image_height, this->scalingFactor); + + pw0 = tj3YUVPlaneWidth(0, width, this->subsamp); + ph0 = tj3YUVPlaneHeight(0, height, this->subsamp); dstPlanes[0] = dstBuf; strides[0] = PAD(pw0, align); - if (jpegSubsamp == TJSAMP_GRAY) { + if (this->subsamp == TJSAMP_GRAY) { strides[1] = strides[2] = 0; dstPlanes[1] = dstPlanes[2] = NULL; } else { - int pw1 = tjPlaneWidth(1, width, jpegSubsamp); - int ph1 = tjPlaneHeight(1, height, jpegSubsamp); + int pw1 = tj3YUVPlaneWidth(1, width, this->subsamp); + int ph1 = tj3YUVPlaneHeight(1, height, this->subsamp); strides[1] = strides[2] = PAD(pw1, align); dstPlanes[1] = dstPlanes[0] + strides[0] * ph0; dstPlanes[2] = dstPlanes[1] + strides[1] * ph1; } - this->headerRead = 1; - return tjDecompressToYUVPlanes(handle, jpegBuf, jpegSize, dstPlanes, width, - strides, height, flags); + this->headerRead = TRUE; + return tj3DecompressToYUVPlanes8(handle, jpegBuf, jpegSize, dstPlanes, + strides); + +bailout: + if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo); + if (this->jerr.warning) retval = -1; + return retval; +} + +/* TurboJPEG 1.4+ */ +DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf, + unsigned long jpegSize, unsigned char *dstBuf, + int width, int align, int height, int flags) +{ + static const char FUNCTION_NAME[] = "tjDecompressToYUV2"; + int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh; + + GET_DINSTANCE(handle); + if ((this->init & DECOMPRESS) == 0) + THROW("Instance has not been initialized for decompression"); + + if (jpegBuf == NULL || jpegSize <= 0 || width < 0 || height < 0) + THROW("Invalid argument"); + + if (setjmp(this->jerr.setjmp_buffer)) { + /* If we get here, the JPEG code has signaled an error. */ + retval = -1; goto bailout; + } + + jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize); + jpeg_read_header(dinfo, TRUE); + jpegwidth = dinfo->image_width; jpegheight = dinfo->image_height; + if (width == 0) width = jpegwidth; + if (height == 0) height = jpegheight; + for (i = 0; i < NUMSF; i++) { + scaledw = TJSCALED(jpegwidth, sf[i]); + scaledh = TJSCALED(jpegheight, sf[i]); + if (scaledw <= width && scaledh <= height) + break; + } + if (i >= NUMSF) + THROW("Could not scale down to desired image dimensions"); + + width = scaledw; height = scaledh; + + processFlags(handle, flags, DECOMPRESS); + + this->headerRead = TRUE; + if (tj3SetScalingFactor(handle, sf[i]) == -1) + return -1; + return tj3DecompressToYUV8(handle, jpegBuf, (size_t)jpegSize, dstBuf, align); bailout: - this->jerr.stopOnWarning = FALSE; + if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo); + if (this->jerr.warning) retval = -1; return retval; } @@ -1973,51 +2612,31 @@ DLLEXPORT int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf, /* TurboJPEG 1.2+ */ DLLEXPORT tjhandle tjInitTransform(void) { - tjinstance *this = NULL; - tjhandle handle = NULL; - - if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) { - SNPRINTF(errStr, JMSG_LENGTH_MAX, - "tjInitTransform(): Memory allocation failure"); - return NULL; - } - memset(this, 0, sizeof(tjinstance)); - SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "No error"); - handle = _tjInitCompress(this); - if (!handle) return NULL; - handle = _tjInitDecompress(this); - return handle; + return tj3Init(TJINIT_TRANSFORM); } -/* TurboJPEG 1.2+ */ -DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf, - unsigned long jpegSize, int n, - unsigned char **dstBufs, unsigned long *dstSizes, - tjtransform *t, int flags) +/* TurboJPEG 3+ */ +DLLEXPORT int tj3Transform(tjhandle handle, const unsigned char *jpegBuf, + size_t jpegSize, int n, unsigned char **dstBufs, + size_t *dstSizes, const tjtransform *t) { + static const char FUNCTION_NAME[] = "tj3Transform"; jpeg_transform_info *xinfo = NULL; jvirt_barray_ptr *srccoefs, *dstcoefs; - int retval = 0, i, jpegSubsamp, saveMarkers = 0; + int retval = 0, i, saveMarkers = 0; boolean alloc = TRUE; struct my_progress_mgr progress; GET_INSTANCE(handle); - this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE; if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0) - THROW("tjTransform(): Instance has not been initialized for transformation"); + THROW("Instance has not been initialized for transformation"); if (jpegBuf == NULL || jpegSize <= 0 || n < 1 || dstBufs == NULL || - dstSizes == NULL || t == NULL || flags < 0) - THROW("tjTransform(): Invalid argument"); - -#ifndef NO_PUTENV - if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1"); - else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1"); - else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1"); -#endif + dstSizes == NULL || t == NULL) + THROW("Invalid argument"); - if (flags & TJFLAG_LIMITSCANS) { + if (this->scanLimit) { memset(&progress, 0, sizeof(struct my_progress_mgr)); progress.pub.progress_monitor = my_progress_monitor; progress.this = this; @@ -2027,7 +2646,7 @@ DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf, if ((xinfo = (jpeg_transform_info *)malloc(sizeof(jpeg_transform_info) * n)) == NULL) - THROW("tjTransform(): Memory allocation failure"); + THROW("Memory allocation failure"); memset(xinfo, 0, sizeof(jpeg_transform_info) * n); if (setjmp(this->jerr.setjmp_buffer)) { @@ -2035,7 +2654,8 @@ DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf, retval = -1; goto bailout; } - jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize); + if (!this->headerRead) + jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize); for (i = 0; i < n; i++) { xinfo[i].transform = xformtypes[t[i].op]; @@ -2062,22 +2682,23 @@ DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf, } jcopy_markers_setup(dinfo, saveMarkers ? JCOPYOPT_ALL : JCOPYOPT_NONE); - jpeg_read_header(dinfo, TRUE); - jpegSubsamp = getSubsamp(dinfo); - if (jpegSubsamp < 0) - THROW("tjTransform(): Could not determine subsampling type for JPEG image"); + if (!this->headerRead) + jpeg_read_header(dinfo, TRUE); + this->subsamp = getSubsamp(&this->dinfo); for (i = 0; i < n; i++) { if (!jtransform_request_workspace(dinfo, &xinfo[i])) - THROW("tjTransform(): Transform is not perfect"); + THROW("Transform is not perfect"); if (xinfo[i].crop) { - if ((t[i].r.x % tjMCUWidth[jpegSubsamp]) != 0 || - (t[i].r.y % tjMCUHeight[jpegSubsamp]) != 0) { + if (this->subsamp == TJSAMP_UNKNOWN) + THROW("Could not determine subsampling level of JPEG image"); + if ((t[i].r.x % tjMCUWidth[this->subsamp]) != 0 || + (t[i].r.y % tjMCUHeight[this->subsamp]) != 0) { SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "To crop this JPEG image, x must be a multiple of %d\n" "and y must be a multiple of %d.\n", - tjMCUWidth[jpegSubsamp], tjMCUHeight[jpegSubsamp]); + tjMCUWidth[this->subsamp], tjMCUHeight[this->subsamp]); this->isInstanceError = TRUE; retval = -1; goto bailout; } @@ -2094,18 +2715,20 @@ DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf, } else { w = xinfo[i].crop_width; h = xinfo[i].crop_height; } - if (flags & TJFLAG_NOREALLOC) { - alloc = FALSE; dstSizes[i] = tjBufSize(w, h, jpegSubsamp); + if (this->noRealloc) { + alloc = FALSE; dstSizes[i] = tj3JPEGBufSize(w, h, this->subsamp); } if (!(t[i].options & TJXOPT_NOOUTPUT)) jpeg_mem_dest_tj(cinfo, &dstBufs[i], &dstSizes[i], alloc); jpeg_copy_critical_parameters(dinfo, cinfo); dstcoefs = jtransform_adjust_parameters(dinfo, cinfo, srccoefs, &xinfo[i]); + if (this->optimize || t[i].options & TJXOPT_OPTIMIZE) + cinfo->optimize_coding = TRUE; #ifdef C_PROGRESSIVE_SUPPORTED - if (flags & TJFLAG_PROGRESSIVE || t[i].options & TJXOPT_PROGRESSIVE) + if (this->progressive || t[i].options & TJXOPT_PROGRESSIVE) jpeg_simple_progression(cinfo); #endif - if (flags & TJFLAG_ARITHMETIC || t[i].options & TJXOPT_ARITHMETIC) + if (this->arithmetic || t[i].options & TJXOPT_ARITHMETIC) cinfo->arith_code = TRUE; if (!(t[i].options & TJXOPT_NOOUTPUT)) { jpeg_write_coefficients(cinfo, dstcoefs); @@ -2136,8 +2759,8 @@ DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf, for (y = 0; y < compptr->v_samp_factor; y++) { if (t[i].customFilter(barray[y][0], arrayRegion, planeRegion, ci, - i, &t[i]) == -1) - THROW("tjTransform(): Error in custom filter"); + i, (tjtransform *)&t[i]) == -1) + THROW("Error in custom filter"); arrayRegion.y += DCTSIZE; } } @@ -2156,189 +2779,93 @@ DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf, if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo); free(xinfo); if (this->jerr.warning) retval = -1; - this->jerr.stopOnWarning = FALSE; return retval; } - -/*************************** Packed-Pixel Image I/O **************************/ - -/* TurboJPEG 2.0+ */ -DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width, - int align, int *height, int *pixelFormat, - int flags) +/* TurboJPEG 1.2+ */ +DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf, + unsigned long jpegSize, int n, + unsigned char **dstBufs, unsigned long *dstSizes, + tjtransform *t, int flags) { - int retval = 0, tempc; - size_t pitch; - tjhandle handle = NULL; - tjinstance *this; - j_compress_ptr cinfo = NULL; - cjpeg_source_ptr src; - unsigned char *dstBuf = NULL; - FILE *file = NULL; - boolean invert; - - if (!filename || !width || align < 1 || !height || !pixelFormat || - *pixelFormat < TJPF_UNKNOWN || *pixelFormat >= TJ_NUMPF) - THROWG("tjLoadImage(): Invalid argument"); - if ((align & (align - 1)) != 0) - THROWG("tjLoadImage(): Alignment must be a power of 2"); - - if ((handle = tjInitCompress()) == NULL) return NULL; - this = (tjinstance *)handle; - cinfo = &this->cinfo; + static const char FUNCTION_NAME[] = "tjTransform"; + int i, retval = 0; + size_t *sizes = NULL; -#ifdef _MSC_VER - if (fopen_s(&file, filename, "rb") || file == NULL) -#else - if ((file = fopen(filename, "rb")) == NULL) -#endif - THROW_UNIX("tjLoadImage(): Cannot open input file"); + GET_DINSTANCE(handle); + if ((this->init & DECOMPRESS) == 0) + THROW("Instance has not been initialized for decompression"); - if ((tempc = getc(file)) < 0 || ungetc(tempc, file) == EOF) - THROW_UNIX("tjLoadImage(): Could not read input file") - else if (tempc == EOF) - THROWG("tjLoadImage(): Input file contains no data"); + if (n < 1 || dstSizes == NULL) + THROW("Invalid argument"); if (setjmp(this->jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. */ retval = -1; goto bailout; } - if (*pixelFormat == TJPF_UNKNOWN) cinfo->in_color_space = JCS_UNKNOWN; - else cinfo->in_color_space = pf2cs[*pixelFormat]; - if (tempc == 'B') { - if ((src = jinit_read_bmp(cinfo, FALSE)) == NULL) - THROWG("tjLoadImage(): Could not initialize bitmap loader"); - invert = (flags & TJFLAG_BOTTOMUP) == 0; - } else if (tempc == 'P') { - if ((src = jinit_read_ppm(cinfo)) == NULL) - THROWG("tjLoadImage(): Could not initialize PPM loader"); - invert = (flags & TJFLAG_BOTTOMUP) != 0; - } else - THROWG("tjLoadImage(): Unsupported file type"); + jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize); + jpeg_read_header(dinfo, TRUE); + if (getSubsamp(dinfo) == TJSAMP_UNKNOWN) + THROW("Could not determine subsampling level of JPEG image"); + processFlags(handle, flags, COMPRESS); + + if ((sizes = (size_t *)malloc(n * sizeof(size_t))) == NULL) + THROW("Memory allocation failure"); + for (i = 0; i < n; i++) + sizes[i] = (size_t)dstSizes[i]; + this->headerRead = TRUE; + retval = tj3Transform(handle, jpegBuf, (size_t)jpegSize, n, dstBufs, sizes, + t); + for (i = 0; i < n; i++) + dstSizes[i] = (unsigned long)sizes[i]; - src->input_file = file; -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - /* Refuse to load images larger than 1 Megapixel when fuzzing. */ - if (flags & TJFLAG_FUZZING) - src->max_pixels = 1048576; -#endif - (*src->start_input) (cinfo, src); - (*cinfo->mem->realize_virt_arrays) ((j_common_ptr)cinfo); +bailout: + free(sizes); + return retval; +} - *width = cinfo->image_width; *height = cinfo->image_height; - *pixelFormat = cs2pf[cinfo->in_color_space]; - pitch = PAD((*width) * tjPixelSize[*pixelFormat], align); - if ((unsigned long long)pitch * (unsigned long long)(*height) > - (unsigned long long)((size_t)-1) || - (dstBuf = (unsigned char *)malloc(pitch * (*height))) == NULL) - THROWG("tjLoadImage(): Memory allocation failure"); +/*************************** Packed-Pixel Image I/O **************************/ - if (setjmp(this->jerr.setjmp_buffer)) { - /* If we get here, the JPEG code has signaled an error. */ - retval = -1; goto bailout; - } +/* tj3LoadImage*() is implemented in turbojpeg-mp.c */ - while (cinfo->next_scanline < cinfo->image_height) { - int i, nlines = (*src->get_pixel_rows) (cinfo, src); +/* TurboJPEG 2.0+ */ +DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width, + int align, int *height, + int *pixelFormat, int flags) +{ + tjhandle handle = NULL; + unsigned char *dstBuf = NULL; - for (i = 0; i < nlines; i++) { - unsigned char *dstptr; - int row; + if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL) return NULL; - row = cinfo->next_scanline + i; - if (invert) dstptr = &dstBuf[((*height) - row - 1) * pitch]; - else dstptr = &dstBuf[row * pitch]; - memcpy(dstptr, src->buffer[i], (*width) * tjPixelSize[*pixelFormat]); - } - cinfo->next_scanline += nlines; - } + processFlags(handle, flags, COMPRESS); - (*src->finish_input) (cinfo, src); + dstBuf = tj3LoadImage8(handle, filename, width, align, height, pixelFormat); -bailout: - if (handle) tjDestroy(handle); - if (file) fclose(file); - if (retval < 0) { free(dstBuf); dstBuf = NULL; } + tj3Destroy(handle); return dstBuf; } +/* tj3SaveImage*() is implemented in turbojpeg-mp.c */ + /* TurboJPEG 2.0+ */ DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer, int width, int pitch, int height, int pixelFormat, int flags) { - int retval = 0; tjhandle handle = NULL; - tjinstance *this; - j_decompress_ptr dinfo = NULL; - djpeg_dest_ptr dst; - FILE *file = NULL; - char *ptr = NULL; - boolean invert; - - if (!filename || !buffer || width < 1 || pitch < 0 || height < 1 || - pixelFormat < 0 || pixelFormat >= TJ_NUMPF) - THROWG("tjSaveImage(): Invalid argument"); - - if ((handle = tjInitDecompress()) == NULL) - return -1; - this = (tjinstance *)handle; - dinfo = &this->dinfo; - -#ifdef _MSC_VER - if (fopen_s(&file, filename, "wb") || file == NULL) -#else - if ((file = fopen(filename, "wb")) == NULL) -#endif - THROW_UNIX("tjSaveImage(): Cannot open output file"); - - if (setjmp(this->jerr.setjmp_buffer)) { - /* If we get here, the JPEG code has signaled an error. */ - retval = -1; goto bailout; - } - - this->dinfo.out_color_space = pf2cs[pixelFormat]; - dinfo->image_width = width; dinfo->image_height = height; - dinfo->global_state = DSTATE_READY; - dinfo->scale_num = dinfo->scale_denom = 1; - - ptr = strrchr(filename, '.'); - if (ptr && !strcasecmp(ptr, ".bmp")) { - if ((dst = jinit_write_bmp(dinfo, FALSE, FALSE)) == NULL) - THROWG("tjSaveImage(): Could not initialize bitmap writer"); - invert = (flags & TJFLAG_BOTTOMUP) == 0; - } else { - if ((dst = jinit_write_ppm(dinfo)) == NULL) - THROWG("tjSaveImage(): Could not initialize PPM writer"); - invert = (flags & TJFLAG_BOTTOMUP) != 0; - } - - dst->output_file = file; - (*dst->start_output) (dinfo, dst); - (*dinfo->mem->realize_virt_arrays) ((j_common_ptr)dinfo); - - if (pitch == 0) pitch = width * tjPixelSize[pixelFormat]; + int retval = -1; - while (dinfo->output_scanline < dinfo->output_height) { - unsigned char *rowptr; + if ((handle = tj3Init(TJINIT_DECOMPRESS)) == NULL) return -1; - if (invert) - rowptr = &buffer[(height - dinfo->output_scanline - 1) * pitch]; - else - rowptr = &buffer[dinfo->output_scanline * pitch]; - memcpy(dst->buffer[0], rowptr, width * tjPixelSize[pixelFormat]); - (*dst->put_pixel_rows) (dinfo, dst, 1); - dinfo->output_scanline++; - } + processFlags(handle, flags, DECOMPRESS); - (*dst->finish_output) (dinfo, dst); + retval = tj3SaveImage8(handle, filename, buffer, width, pitch, height, + pixelFormat); -bailout: - if (handle) tjDestroy(handle); - if (file) fclose(file); + tj3Destroy(handle); return retval; } diff --git a/turbojpeg.h b/turbojpeg.h index 5f1fdd821..475282a54 100644 --- a/turbojpeg.h +++ b/turbojpeg.h @@ -30,6 +30,8 @@ #ifndef __TURBOJPEG_H__ #define __TURBOJPEG_H__ +#include + #if defined(_WIN32) && defined(DLLDEFINE) #define DLLEXPORT __declspec(dllexport) #else @@ -77,6 +79,31 @@ */ +/** + * The number of initialization options + */ +#define TJ_NUMINIT 3 + +/** + * Initialization options. + */ +enum TJINIT { + /** + * Initialize the TurboJPEG instance for compression. + */ + TJINIT_COMPRESS, + /** + * Initialize the TurboJPEG instance for decompression. + */ + TJINIT_DECOMPRESS, + /** + * Initialize the TurboJPEG instance for lossless transformation (both + * compression and decompression.) + */ + TJINIT_TRANSFORM +}; + + /** * The number of chrominance subsampling options */ @@ -97,7 +124,7 @@ enum TJSAMP { * YUV image will contain one chrominance component for every pixel in the * source image. */ - TJSAMP_444 = 0, + TJSAMP_444, /** * 4:2:2 chrominance subsampling. The JPEG or YUV image will contain one * chrominance component for every 2x1 block of pixels in the source image. @@ -130,7 +157,16 @@ enum TJSAMP { * * @note 4:1:1 subsampling is not fully accelerated in libjpeg-turbo. */ - TJSAMP_411 + TJSAMP_411, + /** + * Unknown subsampling. The JPEG image uses an unusual type of chrominance + * subsampling. Such images can be decompressed into packed-pixel images, + * but they cannot be + * - decompressed into planar YUV images, + * - losslessly transformed if #TJXOPT_CROP is specified, or + * - partially decompressed using a cropping region. + */ + TJSAMP_UNKNOWN = -1 }; /** @@ -167,71 +203,72 @@ static const int tjMCUHeight[TJ_NUMSAMP] = { 8, 8, 16, 8, 16, 8 }; enum TJPF { /** * RGB pixel format. The red, green, and blue components in the image are - * stored in 3-byte pixels in the order R, G, B from lowest to highest byte - * address within each pixel. + * stored in 3-sample pixels in the order R, G, B from lowest to highest + * memory address within each pixel. */ - TJPF_RGB = 0, + TJPF_RGB, /** * BGR pixel format. The red, green, and blue components in the image are - * stored in 3-byte pixels in the order B, G, R from lowest to highest byte - * address within each pixel. + * stored in 3-sample pixels in the order B, G, R from lowest to highest + * memory address within each pixel. */ TJPF_BGR, /** * RGBX pixel format. The red, green, and blue components in the image are - * stored in 4-byte pixels in the order R, G, B from lowest to highest byte - * address within each pixel. The X component is ignored when compressing - * and undefined when decompressing. + * stored in 4-sample pixels in the order R, G, B from lowest to highest + * memory address within each pixel. The X component is ignored when + * compressing and undefined when decompressing. */ TJPF_RGBX, /** * BGRX pixel format. The red, green, and blue components in the image are - * stored in 4-byte pixels in the order B, G, R from lowest to highest byte - * address within each pixel. The X component is ignored when compressing - * and undefined when decompressing. + * stored in 4-sample pixels in the order B, G, R from lowest to highest + * memory address within each pixel. The X component is ignored when + * compressing and undefined when decompressing. */ TJPF_BGRX, /** * XBGR pixel format. The red, green, and blue components in the image are - * stored in 4-byte pixels in the order R, G, B from highest to lowest byte - * address within each pixel. The X component is ignored when compressing - * and undefined when decompressing. + * stored in 4-sample pixels in the order R, G, B from highest to lowest + * memory address within each pixel. The X component is ignored when + * compressing and undefined when decompressing. */ TJPF_XBGR, /** * XRGB pixel format. The red, green, and blue components in the image are - * stored in 4-byte pixels in the order B, G, R from highest to lowest byte - * address within each pixel. The X component is ignored when compressing - * and undefined when decompressing. + * stored in 4-sample pixels in the order B, G, R from highest to lowest + * memory address within each pixel. The X component is ignored when + * compressing and undefined when decompressing. */ TJPF_XRGB, /** - * Grayscale pixel format. Each 1-byte pixel represents a luminance - * (brightness) level from 0 to 255. + * Grayscale pixel format. Each 1-sample pixel represents a luminance + * (brightness) level from 0 to the maximum sample value (255 for 8-bit + * samples, 4095 for 12-bit samples, and 65535 for 16-bit samples.) */ TJPF_GRAY, /** * RGBA pixel format. This is the same as @ref TJPF_RGBX, except that when - * decompressing, the X component is guaranteed to be 0xFF, which can be - * interpreted as an opaque alpha channel. + * decompressing, the X component is guaranteed to be equal to the maximum + * sample value, which can be interpreted as an opaque alpha channel. */ TJPF_RGBA, /** * BGRA pixel format. This is the same as @ref TJPF_BGRX, except that when - * decompressing, the X component is guaranteed to be 0xFF, which can be - * interpreted as an opaque alpha channel. + * decompressing, the X component is guaranteed to be equal to the maximum + * sample value, which can be interpreted as an opaque alpha channel. */ TJPF_BGRA, /** * ABGR pixel format. This is the same as @ref TJPF_XBGR, except that when - * decompressing, the X component is guaranteed to be 0xFF, which can be - * interpreted as an opaque alpha channel. + * decompressing, the X component is guaranteed to be equal to the maximum + * sample value, which can be interpreted as an opaque alpha channel. */ TJPF_ABGR, /** * ARGB pixel format. This is the same as @ref TJPF_XRGB, except that when - * decompressing, the X component is guaranteed to be 0xFF, which can be - * interpreted as an opaque alpha channel. + * decompressing, the X component is guaranteed to be equal to the maximum + * sample value, which can be interpreted as an opaque alpha channel. */ TJPF_ARGB, /** @@ -251,27 +288,28 @@ enum TJPF { */ TJPF_CMYK, /** - * Unknown pixel format. Currently this is only used by #tjLoadImage(). + * Unknown pixel format. Currently this is only used by #tj3LoadImage8(), + * #tj3LoadImage12(), and #tj3LoadImage16(). */ TJPF_UNKNOWN = -1 }; /** - * Red offset (in bytes) for a given pixel format. This specifies the number - * of bytes that the red component is offset from the start of the pixel. For - * instance, if a pixel of format TJPF_BGRX is stored in - * `unsigned char pixel[]`, then the red component will be - *`pixel[tjRedOffset[TJPF_BGRX]]`. This will be -1 if the pixel format does + * Red offset (in samples) for a given pixel format. This specifies the number + * of samples that the red component is offset from the start of the pixel. + * For instance, if an 8-bit-per-component pixel of format TJPF_BGRX is stored + * in `unsigned char pixel[]`, then the red component will be + * `pixel[tjRedOffset[TJPF_BGRX]]`. This will be -1 if the pixel format does * not have a red component. */ static const int tjRedOffset[TJ_NUMPF] = { 0, 2, 0, 2, 3, 1, -1, 0, 2, 3, 1, -1 }; /** - * Green offset (in bytes) for a given pixel format. This specifies the number - * of bytes that the green component is offset from the start of the pixel. - * For instance, if a pixel of format TJPF_BGRX is stored in - * `unsigned char pixel[]`, then the green component will be + * Green offset (in samples) for a given pixel format. This specifies the + * number of samples that the green component is offset from the start of the + * pixel. For instance, if an 8-bit-per-component pixel of format TJPF_BGRX is + * stored in `unsigned char pixel[]`, then the green component will be * `pixel[tjGreenOffset[TJPF_BGRX]]`. This will be -1 if the pixel format does * not have a green component. */ @@ -279,10 +317,10 @@ static const int tjGreenOffset[TJ_NUMPF] = { 1, 1, 1, 1, 2, 2, -1, 1, 1, 2, 2, -1 }; /** - * Blue offset (in bytes) for a given pixel format. This specifies the number - * of bytes that the blue component is offset from the start of the pixel. For - * instance, if a pixel of format TJPF_BGRX is stored in - * `unsigned char pixel[]`, then the blue component will be + * Blue offset (in samples) for a given pixel format. This specifies the + * number of samples that the blue component is offset from the start of the + * pixel. For instance, if an 8-bit-per-component pixel of format TJPF_BGRX is + * stored in `unsigned char pixel[]`, then the blue component will be * `pixel[tjBlueOffset[TJPF_BGRX]]`. This will be -1 if the pixel format does * not have a blue component. */ @@ -290,10 +328,10 @@ static const int tjBlueOffset[TJ_NUMPF] = { 2, 0, 2, 0, 1, 3, -1, 2, 0, 1, 3, -1 }; /** - * Alpha offset (in bytes) for a given pixel format. This specifies the number - * of bytes that the alpha component is offset from the start of the pixel. - * For instance, if a pixel of format TJPF_BGRA is stored in - * `unsigned char pixel[]`, then the alpha component will be + * Alpha offset (in samples) for a given pixel format. This specifies the + * number of samples that the alpha component is offset from the start of the + * pixel. For instance, if an 8-bit-per-component pixel of format TJPF_BGRA is + * stored in `unsigned char pixel[]`, then the alpha component will be * `pixel[tjAlphaOffset[TJPF_BGRA]]`. This will be -1 if the pixel format does * not have an alpha component. */ @@ -301,7 +339,7 @@ static const int tjAlphaOffset[TJ_NUMPF] = { -1, -1, -1, -1, -1, -1, -1, 3, 3, 0, 0, -1 }; /** - * Pixel size (in bytes) for a given pixel format + * Pixel size (in samples) for a given pixel format */ static const int tjPixelSize[TJ_NUMPF] = { 3, 3, 4, 4, 4, 4, 1, 4, 4, 4, 4, 4 @@ -321,11 +359,11 @@ enum TJCS { * RGB colorspace. When compressing the JPEG image, the R, G, and B * components in the source image are reordered into image planes, but no * colorspace conversion or subsampling is performed. RGB JPEG images can be - * decompressed to packed-pixel images with any of the extended RGB or - * grayscale pixel formats, but they cannot be decompressed to planar YUV - * images. + * compressed from and decompressed to packed-pixel images with any of the + * extended RGB or grayscale pixel formats, but they cannot be compressed + * from or decompressed to planar YUV images. */ - TJCS_RGB = 0, + TJCS_RGB, /** * YCbCr colorspace. YCbCr is not an absolute colorspace but rather a * mathematical transformation of RGB designed solely for storage and @@ -356,7 +394,8 @@ enum TJCS { * CMYK colorspace. When compressing the JPEG image, the C, M, Y, and K * components in the source image are reordered into image planes, but no * colorspace conversion or subsampling is performed. CMYK JPEG images can - * only be decompressed to packed-pixel images with the CMYK pixel format. + * only be compressed from and decompressed to packed-pixel images with the + * CMYK pixel format. */ TJCS_CMYK, /** @@ -373,91 +412,340 @@ enum TJCS { /** - * Rows in the packed-pixel source/destination image are stored in bottom-up - * (Windows, OpenGL) order rather than in top-down (X11) order. - */ -#define TJFLAG_BOTTOMUP (1 << 1) -/** - * When decompressing an image that was compressed using chrominance - * subsampling, use the fastest chrominance upsampling algorithm available. - * The default is to use smooth upsampling, which creates a smooth transition - * between neighboring chrominance components in order to reduce upsampling - * artifacts in the decompressed image. - */ -#define TJFLAG_FASTUPSAMPLE (1 << 8) -/** - * Disable JPEG buffer (re)allocation. If passed to one of the JPEG - * compression or transform functions, this flag will cause those functions to - * generate an error if the JPEG destination buffer is invalid or too small, - * rather than attempt to allocate or reallocate that buffer. - */ -#define TJFLAG_NOREALLOC (1 << 10) -/** - * Use the fastest DCT/IDCT algorithm available. The default if this flag is - * not specified is implementation-specific. For example, the implementation - * of the TurboJPEG API in libjpeg-turbo uses the fast algorithm by default - * when compressing, because this has been shown to have only a very slight - * effect on accuracy, but it uses the accurate algorithm when decompressing, - * because this has been shown to have a larger effect. - */ -#define TJFLAG_FASTDCT (1 << 11) -/** - * Use the most accurate DCT/IDCT algorithm available. The default if this - * flag is not specified is implementation-specific. For example, the - * implementation of the TurboJPEG API in libjpeg-turbo uses the fast algorithm - * by default when compressing, because this has been shown to have only a very - * slight effect on accuracy, but it uses the accurate algorithm when - * decompressing, because this has been shown to have a larger effect. - */ -#define TJFLAG_ACCURATEDCT (1 << 12) -/** - * Immediately discontinue the current compression/decompression/transform - * operation if a warning (non-fatal error) occurs. The default behavior is to - * allow the operation to complete unless a fatal error is encountered. + * The number of parameters */ -#define TJFLAG_STOPONWARNING (1 << 13) -/** - * Use progressive entropy coding in JPEG images generated by the compression - * and transform functions. Progressive entropy coding will generally improve - * compression relative to baseline entropy coding (the default), but it will - * reduce compression and decompression performance considerably. Can be - * combined with #TJFLAG_ARITHMETIC. - */ -#define TJFLAG_PROGRESSIVE (1 << 14) -/** - * Limit the number of progressive JPEG scans that the decompression and - * transform functions will process. If a progressive JPEG image contains an - * unreasonably large number of scans, then this flag will cause the - * decompression and transform functions to return an error. The primary - * purpose of this is to allow security-critical applications to guard against - * an exploit of the progressive JPEG format described in - * this report. - */ -#define TJFLAG_LIMITSCANS (1 << 15) -/** - * Use arithmetic entropy coding in JPEG images generated by the compression - * and transform functions. Arithmetic entropy coding will generally improve - * compression relative to Huffman entropy coding (the default), but it will - * reduce compression and decompression performance considerably. Can be - * combined with #TJFLAG_PROGRESSIVE. - */ -#define TJFLAG_ARITHMETIC (1 << 16) +#define TJ_NUMPARAM + /** - * Generate a lossless JPEG image when compressing. In most cases, compressing - * and decompressing lossless JPEG images is considerably slower than - * compressing and decompressing lossy JPEG images. Also note that the - * following features are not available with lossless JPEG images: - * - Colorspace conversion - * - Chrominance subsampling - * - JPEG quality selection - * - DCT/IDCT algorithm selection - * - Progressive entropy coding - * - Arithmetic entropy coding - * - Compression from/decompression to planar YUV images - * - Decompression scaling - * - Lossless transformations + * Parameters */ -#define TJFLAG_LOSSLESS (1 << 17) +enum TJPARAM { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + TJPARAM_MAXPIXELS = -1, +#endif + /** + * Error handling behavior + * + * **Value** + * - `0` *[default]* Allow the current compression/decompression/transform + * operation to complete unless a fatal error is encountered. + * - `1` Immediately discontinue the current + * compression/decompression/transform operation if a warning (non-fatal + * error) occurs. + */ + TJPARAM_STOPONWARNING, + /** + * Row order in packed-pixel source/destination images + * + * **Value** + * - `0` *[default]* top-down (X11) order + * - `1` bottom-up (Windows, OpenGL) order + */ + TJPARAM_BOTTOMUP, + /** + * JPEG destination buffer (re)allocation [compression, lossless + * transformation] + * + * **Value** + * - `0` *[default]* Attempt to allocate or reallocate the JPEG destination + * buffer as needed. + * - `1` Generate an error if the JPEG destination buffer is invalid or too + * small. + */ + TJPARAM_NOREALLOC, + /** + * Perceptual quality of lossy JPEG images [compression only] + * + * **Value** + * - `1`-`100` (`1` = worst quality but best compression, `100` = best + * quality but worst compression) *[no default; must be explicitly + * specified]* + */ + TJPARAM_QUALITY, + /** + * Chrominance subsampling level + * + * The JPEG or YUV image uses (decompression, decoding) or will use (lossy + * compression, encoding) the specified level of chrominance subsampling. + * + * **Value** + * - One of the @ref TJSAMP "chrominance subsampling options" *[no default; + * must be explicitly specified for lossy compression, encoding, and + * decoding]* + */ + TJPARAM_SUBSAMP, + /** + * JPEG width (in pixels) [decompression only, read-only] + */ + TJPARAM_JPEGWIDTH, + /** + * JPEG height (in pixels) [decompression only, read-only] + */ + TJPARAM_JPEGHEIGHT, + /** + * JPEG data precision (bits per sample) [decompression only, read-only] + * + * The JPEG image uses the specified number of bits per sample. + * + * **Value** + * - `8`, `12`, or `16` + * + * 12-bit data precision implies #TJPARAM_OPTIMIZE. + */ + TJPARAM_PRECISION, + /** + * JPEG colorspace + * + * The JPEG image uses (decompression) or will use (lossy compression) the + * specified colorspace. + * + * **Value** + * - One of the @ref TJCS "JPEG colorspaces" *[default for lossy compression: + * automatically selected based on the subsampling level and pixel format]* + */ + TJPARAM_COLORSPACE, + /** + * Chrominance upsampling algorithm [lossy decompression only] + * + * **Value** + * - `0` *[default]* Use smooth upsampling when decompressing a JPEG image + * that was compressed using chrominance subsampling. This creates a smooth + * transition between neighboring chrominance components in order to reduce + * upsampling artifacts in the decompressed image. + * - `1` Use the fastest chrominance upsampling algorithm available, which + * may combine upsampling with color conversion. + */ + TJPARAM_FASTUPSAMPLE, + /** + * DCT/IDCT algorithm [lossy compression and decompression] + * + * **Value** + * - `0` *[default]* Use the most accurate DCT/IDCT algorithm available. + * - `1` Use the fastest DCT/IDCT algorithm available. + * + * This parameter is provided mainly for backward compatibility with libjpeg, + * which historically implemented several different DCT/IDCT algorithms + * because of performance limitations with 1990s CPUs. In the libjpeg-turbo + * implementation of the TurboJPEG API: + * - The "fast" and "accurate" DCT/IDCT algorithms perform similarly on + * modern x86/x86-64 CPUs that support AVX2 instructions. + * - The "fast" algorithm is generally only about 5-15% faster than the + * "accurate" algorithm on other types of CPUs. + * - The difference in accuracy between the "fast" and "accurate" algorithms + * is the most pronounced at JPEG quality levels above 90 and tends to be + * more pronounced with decompression than with compression. + * - The "fast" algorithm degrades and is not fully accelerated for JPEG + * quality levels above 97, so it will be slower than the "accurate" + * algorithm. + */ + TJPARAM_FASTDCT, + /** + * Optimized baseline entropy coding [lossy compression only] + * + * **Value** + * - `0` *[default]* The JPEG image will use the default Huffman tables. + * - `1` Optimal Huffman tables will be computed for the JPEG image. For + * lossless transformation, this can also be specified using + * #TJXOPT_OPTIMIZE. + * + * Optimized baseline entropy coding will improve compression slightly + * (generally 5% or less), but it will reduce compression performance + * considerably. + */ + TJPARAM_OPTIMIZE, + /** + * Progressive entropy coding + * + * **Value** + * - `0` *[default for compression, lossless transformation]* The lossy JPEG + * image uses (decompression) or will use (compression, lossless + * transformation) baseline entropy coding. + * - `1` The lossy JPEG image uses (decompression) or will use (compression, + * lossless transformation) progressive entropy coding. For lossless + * transformation, this can also be specified using #TJXOPT_PROGRESSIVE. + * + * Progressive entropy coding will generally improve compression relative to + * baseline entropy coding, but it will reduce compression and decompression + * performance considerably. Implies #TJPARAM_OPTIMIZE. Can be combined + * with #TJPARAM_ARITHMETIC. + */ + TJPARAM_PROGRESSIVE, + /** + * Progressive JPEG scan limit for lossy JPEG images [decompression, lossless + * transformation] + * + * Setting this parameter will cause the decompression and transform + * functions to return an error if the number of scans in a progressive JPEG + * image exceeds the specified limit. The primary purpose of this is to + * allow security-critical applications to guard against an exploit of the + * progressive JPEG format described in + * this report. + * + * **Value** + * - maximum number of progressive JPEG scans that the decompression and + * transform functions will process *[default: `0` (no limit)]* + * + * @see #TJPARAM_PROGRESSIVE + */ + TJPARAM_SCANLIMIT, + /** + * Arithmetic entropy coding + * + * **Value** + * - `0` *[default for compression, lossless transformation]* The lossy JPEG + * image uses (decompression) or will use (compression, lossless + * transformation) Huffman entropy coding. + * - `1` The lossy JPEG image uses (decompression) or will use (compression, + * lossless transformation) arithmetic entropy coding. For lossless + * transformation, this can also be specified using #TJXOPT_ARITHMETIC. + * + * Arithmetic entropy coding will generally improve compression relative to + * Huffman entropy coding, but it will reduce compression and decompression + * performance considerably. Can be combined with #TJPARAM_PROGRESSIVE. + * Arithmetic entropy coding is currently only implemented for 8-bit samples. + */ + TJPARAM_ARITHMETIC, + /** + * Lossless JPEG + * + * **Value** + * - `0` *[default for compression]* The JPEG image is (decompression) or + * will be (compression) lossy/DCT-based. + * - `1` The JPEG image is (decompression) or will be (compression) + * lossless/predictive. + * + * In most cases, compressing and decompressing lossless JPEG images is + * considerably slower than compressing and decompressing lossy JPEG images. + * Also note that the following features are not available with lossless JPEG + * images: + * - Colorspace conversion (lossless JPEG images always use #TJCS_RGB, + * #TJCS_GRAY, or #TJCS_CMYK, depending on the pixel format of the source + * image) + * - Chrominance subsampling (lossless JPEG images always use #TJSAMP_444) + * - JPEG quality selection + * - DCT/IDCT algorithm selection + * - Progressive entropy coding + * - Arithmetic entropy coding + * - Compression from/decompression to planar YUV images + * - Decompression scaling + * - Lossless transformation + * + * @see #TJPARAM_LOSSLESSPSV, #TJPARAM_LOSSLESSPT + */ + TJPARAM_LOSSLESS, + /** + * Lossless JPEG predictor selection value (PSV) + * + * **Value** + * - `1`-`7` *[default for compression: `1`]* + * + * @see #TJPARAM_LOSSLESS + */ + TJPARAM_LOSSLESSPSV, + /** + * Lossless JPEG point transform (Pt) + * + * **Value** + * - `0` through ***precision*** *- 1*, where ***precision*** is the JPEG + * data precision in bits *[default for compression: `0`]* + * + * A point transform value of `0` is necessary in order to generate a fully + * lossless JPEG image. (A non-zero point transform value right-shifts the + * input samples by the specified number of bits, which is effectively a form + * of lossy color quantization.) + * + * @see #TJPARAM_LOSSLESS, #TJPARAM_PRECISION + */ + TJPARAM_LOSSLESSPT, + /** + * JPEG restart marker interval in MCU blocks (lossy) or samples (lossless) + * [compression only] + * + * The nature of entropy coding is such that a corrupt JPEG image cannot + * be decompressed beyond the point of corruption unless it contains restart + * markers. A restart marker stops and restarts the entropy coding algorithm + * so that, if a JPEG image is corrupted, decompression can resume at the + * next marker. Thus, adding more restart markers improves the fault + * tolerance of the JPEG image, but adding too many restart markers can + * adversely affect the compression ratio and performance. + * + * **Value** + * - the number of MCU blocks or samples between each restart marker + * *[default: `0` (no restart markers)]* + * + * Setting this parameter to a non-zero value sets #TJPARAM_RESTARTROWS to 0. + */ + TJPARAM_RESTARTBLOCKS, + /** + * JPEG restart marker interval in MCU rows (lossy) or sample rows (lossless) + * [compression only] + * + * See #TJPARAM_RESTARTBLOCKS for a description of restart markers. + * + * **Value** + * - the number of MCU rows or sample rows between each restart marker + * *[default: `0` (no restart markers)]* + * + * Setting this parameter to a non-zero value sets #TJPARAM_RESTARTBLOCKS to + * 0. + */ + TJPARAM_RESTARTROWS, + /** + * JPEG horizontal pixel density + * + * **Value** + * - The JPEG image has (decompression) or will have (compression) the + * specified horizontal pixel density *[default for compression: `1`]*. + * + * This value is stored in or read from the JPEG header. It does not affect + * the contents of the JPEG image. Note that this parameter is set by + * #tj3LoadImage8() when loading a Windows BMP file that contains pixel + * density information, and the value of this parameter is stored to a + * Windows BMP file by #tj3SaveImage8() if the value of #TJPARAM_DENSITYUNIT + * is `2`. + * + * @see TJPARAM_DENSITYUNIT + */ + TJPARAM_XDENSITY, + /** + * JPEG vertical pixel density + * + * **Value** + * - The JPEG image has (decompression) or will have (compression) the + * specified vertical pixel density *[default for compression: `1`]*. + * + * This value is stored in or read from the JPEG header. It does not affect + * the contents of the JPEG image. Note that this parameter is set by + * #tj3LoadImage8() when loading a Windows BMP file that contains pixel + * density information, and the value of this parameter is stored to a + * Windows BMP file by #tj3SaveImage8() if the value of #TJPARAM_DENSITYUNIT + * is `2`. + * + * @see TJPARAM_DENSITYUNIT + */ + TJPARAM_YDENSITY, + /** + * JPEG pixel density units + * + * **Value** + * - `0` *[default for compression]* The pixel density of the JPEG image is + * expressed (decompression) or will be expressed (compression) in unknown + * units. + * - `1` The pixel density of the JPEG image is expressed (decompression) or + * will be expressed (compression) in units of pixels/inch. + * - `2` The pixel density of the JPEG image is expressed (decompression) or + * will be expressed (compression) in units of pixels/cm. + * + * This value is stored in or read from the JPEG header. It does not affect + * the contents of the JPEG image. Note that this parameter is set by + * #tj3LoadImage8() when loading a Windows BMP file that contains pixel + * density information, and the value of this parameter is stored to a + * Windows BMP file by #tj3SaveImage8() if the value is `2`. + * + * @see TJPARAM_XDENSITY, TJPARAM_YDENSITY + */ + TJPARAM_DENSITYUNITS +}; /** @@ -473,7 +761,7 @@ enum TJERR { * The error was non-fatal and recoverable, but the destination image may * still be corrupt. */ - TJERR_WARNING = 0, + TJERR_WARNING, /** * The error was fatal and non-recoverable. */ @@ -487,13 +775,13 @@ enum TJERR { #define TJ_NUMXOP 8 /** - * Transform operations for #tjTransform() + * Transform operations for #tj3Transform() */ enum TJXOP { /** * Do not transform the position of the image pixels */ - TJXOP_NONE = 0, + TJXOP_NONE, /** * Flip (mirror) image horizontally. This transform is imperfect if there * are any partial MCU blocks on the right edge (see #TJXOPT_PERFECT.) @@ -536,9 +824,9 @@ enum TJXOP { /** - * This option will cause #tjTransform() to return an error if the transform is - * not perfect. Lossless transforms operate on MCU blocks, whose size depends - * on the level of chrominance subsampling used (see #tjMCUWidth and + * This option will cause #tj3Transform() to return an error if the transform + * is not perfect. Lossless transforms operate on MCU blocks, whose size + * depends on the level of chrominance subsampling used (see #tjMCUWidth and * #tjMCUHeight.) If the image's width or height is not evenly divisible by * the MCU block size, then there will be partial MCU blocks on the right * and/or bottom edges. It is not possible to move these partial MCU blocks to @@ -549,12 +837,12 @@ enum TJXOP { */ #define TJXOPT_PERFECT (1 << 0) /** - * This option will cause #tjTransform() to discard any partial MCU blocks that - * cannot be transformed. + * This option will cause #tj3Transform() to discard any partial MCU blocks + * that cannot be transformed. */ #define TJXOPT_TRIM (1 << 1) /** - * This option will enable lossless cropping. See #tjTransform() for more + * This option will enable lossless cropping. See #tj3Transform() for more * information. */ #define TJXOPT_CROP (1 << 2) @@ -564,7 +852,7 @@ enum TJXOP { */ #define TJXOPT_GRAY (1 << 3) /** - * This option will prevent #tjTransform() from outputting a JPEG image for + * This option will prevent #tj3Transform() from outputting a JPEG image for * this particular transform. (This can be used in conjunction with a custom * filter to capture the transformed DCT coefficients without transcoding * them.) @@ -574,12 +862,12 @@ enum TJXOP { * This option will enable progressive entropy coding in the JPEG image * generated by this particular transform. Progressive entropy coding will * generally improve compression relative to baseline entropy coding (the - * default), but it will reduce decompression performance considerably. Can be - * combined with #TJXOPT_ARITHMETIC. + * default), but it will reduce decompression performance considerably. + * Implies #TJXOPT_OPTIMIZE. Can be combined with #TJXOPT_ARITHMETIC. */ #define TJXOPT_PROGRESSIVE (1 << 5) /** - * This option will prevent #tjTransform() from copying any extra markers + * This option will prevent #tj3Transform() from copying any extra markers * (including EXIF and ICC profile data) from the source image to the * destination image. */ @@ -589,9 +877,16 @@ enum TJXOP { * generated by this particular transform. Arithmetic entropy coding will * generally improve compression relative to Huffman entropy coding (the * default), but it will reduce decompression performance considerably. Can be - * combined with #TJXOPT_PROGRESSIVE. + * combined with #TJXOPT_PROGRESSIVE. Arithmetic entropy coding is currently + * only implemented for 8-bit samples. */ #define TJXOPT_ARITHMETIC (1 << 7) +/** + * This option will enable optimized baseline entropy coding in the JPEG image + * generated by this particular transform. Optimized baseline entropy coding + * will improve compression slightly (generally 5% or less.) + */ +#define TJXOPT_OPTIMIZE (1 << 8) /** @@ -618,8 +913,8 @@ typedef struct { */ int x; /** - * The upper boundary of the cropping region. This must be evenly divisible - * by the MCU block height (see #tjMCUHeight.) + * The upper boundary of the cropping region. For lossless transformation, + * this must be evenly divisible by the MCU block height (see #tjMCUHeight.) */ int y; /** @@ -634,6 +929,11 @@ typedef struct { int h; } tjregion; +/** + * A #tjregion structure that specifies no cropping + */ +static const tjregion TJUNCROPPED = { 0, 0, 0, 0 }; + /** * Lossless transform */ @@ -682,7 +982,7 @@ typedef struct tjtransform { * * @param transformID ID number of the transformed image to which `coeffs` * belongs. This is the same as the index of the transform in the - * `transforms` array that was passed to #tjTransform(). + * `transforms` array that was passed to #tj3Transform(). * * @param transform a pointer to a #tjtransform structure that specifies the * parameters and/or cropping region for this transform @@ -700,11 +1000,6 @@ typedef struct tjtransform { typedef void *tjhandle; -/** - * Pad the given width to the nearest multiple of 4 - */ -#define TJPAD(width) (((width) + 3) & (~3)) - /** * Compute the scaled value of `dimension` using the given scaling factor. * This macro performs the integer equivalent of `ceil(dimension * @@ -714,6 +1009,12 @@ typedef void *tjhandle; (((dimension) * scalingFactor.num + scalingFactor.denom - 1) / \ scalingFactor.denom) +/** + * A #tjscalingfactor structure that specifies a scaling factor of 1/1 (no + * scaling) + */ +static const tjscalingfactor TJUNSCALED = { 1, 1 }; + #ifdef __cplusplus extern "C" { @@ -721,31 +1022,63 @@ extern "C" { /** - * Create a TurboJPEG compressor instance. + * Create a new TurboJPEG instance. + * + * @param initType one of the @ref TJINIT "initialization options" * * @return a handle to the newly-created instance, or NULL if an error occurred - * (see #tjGetErrorStr2().) + * (see #tj3GetErrorStr().) */ -DLLEXPORT tjhandle tjInitCompress(void); +DLLEXPORT tjhandle tj3Init(int initType); + + +/** + * Set the value of a parameter. + * + * @param handle handle to a TurboJPEG instance + * + * @param param one of the @ref TJPARAM "parameters" + * + * @param value value of the parameter (refer to @ref TJPARAM + * "parameter documentation") + * + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr().) + */ +DLLEXPORT int tj3Set(tjhandle handle, int param, int value); + + +/** + * Get the value of a parameter. + * + * @param handle handle to a TurboJPEG instance + * + * @param param one of the @ref TJPARAM "parameters" + * + * @return the value of the specified parameter, or -1 if the value is unknown. + */ +DLLEXPORT int tj3Get(tjhandle handle, int param); /** - * Compress a packed-pixel RGB, grayscale, or CMYK image into a JPEG image. + * Compress an 8-bit-per-sample packed-pixel RGB, grayscale, or CMYK image into + * an 8-bit-per-sample JPEG image. * - * @param handle a handle to a TurboJPEG compressor or transformer instance + * @param handle handle to a TurboJPEG instance that has been initialized for + * compression * * @param srcBuf pointer to a buffer containing a packed-pixel RGB, grayscale, - * or CMYK source image to be compressed + * or CMYK source image to be compressed. This buffer should normally be + * `pitch * height` samples in size. However, you can also use this parameter + * to compress from a specific region of a larger buffer. * * @param width width (in pixels) of the source image * - * @param pitch bytes per row in the source image. Normally this should be - * width * #tjPixelSize[pixelFormat], if the image is unpadded, or - * #TJPAD(width * #tjPixelSize[pixelFormat]) if each row of the image - * is padded to the nearest multiple of 4 bytes, as is the case for Windows - * bitmaps. You can also be clever and use this parameter to skip rows, etc. - * Setting this parameter to 0 is the equivalent of setting it to - * width * #tjPixelSize[pixelFormat]. + * @param pitch samples per row in the source image. Normally this should be + * width * #tjPixelSize[pixelFormat], if the image is unpadded. + * (Setting this parameter to 0 is the equivalent of setting it to + * width * #tjPixelSize[pixelFormat].) However, you can also use this + * parameter to specify the row alignment/padding of the source image, to skip + * rows, or to compress from a specific region of a larger buffer. * * @param height height (in pixels) of the source image * @@ -755,62 +1088,67 @@ DLLEXPORT tjhandle tjInitCompress(void); * @param jpegBuf address of a pointer to a byte buffer that will receive the * JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to * accommodate the size of the JPEG image. Thus, you can choose to: - * -# pre-allocate the JPEG buffer with an arbitrary size using #tjAlloc() and + * -# pre-allocate the JPEG buffer with an arbitrary size using #tj3Alloc() and * let TurboJPEG grow the buffer as needed, * -# set `*jpegBuf` to NULL to tell TurboJPEG to allocate the buffer for you, * or * -# pre-allocate the buffer to a "worst case" size determined by calling - * #tjBufSize(). This should ensure that the buffer never has to be - * re-allocated. (Setting #TJFLAG_NOREALLOC guarantees that it won't be.) + * #tj3JPEGBufSize(). This should ensure that the buffer never has to be + * re-allocated. (Setting #TJPARAM_NOREALLOC guarantees that it won't be.) * . * If you choose option 1, then `*jpegSize` should be set to the size of your - * pre-allocated buffer. In any case, unless you have set #TJFLAG_NOREALLOC, + * pre-allocated buffer. In any case, unless you have set #TJPARAM_NOREALLOC, * you should always check `*jpegBuf` upon return from this function, as it may * have changed. * - * @param jpegSize pointer to an unsigned long variable that holds the size of - * the JPEG buffer. If `*jpegBuf` points to a pre-allocated buffer, then - * `*jpegSize` should be set to the size of the buffer. Upon return, - * `*jpegSize` will contain the size of the JPEG image (in bytes.) If - * `*jpegBuf` points to a JPEG buffer that is being reused from a previous call - * to one of the JPEG compression functions, then `*jpegSize` is ignored. + * @param jpegSize pointer to a size_t variable that holds the size of the JPEG + * buffer. If `*jpegBuf` points to a pre-allocated buffer, then `*jpegSize` + * should be set to the size of the buffer. Upon return, `*jpegSize` will + * contain the size of the JPEG image (in bytes.) If `*jpegBuf` points to a + * JPEG buffer that is being reused from a previous call to one of the JPEG + * compression functions, then `*jpegSize` is ignored. * - * @param jpegSubsamp the level of chrominance subsampling to be used when - * generating the JPEG image (see @ref TJSAMP - * "Chrominance subsampling options".) - * - * @param jpegQual the image quality of the generated JPEG image (1 = worst, - * 100 = best.) When generating a lossless JPEG image (see #TJFLAG_LOSSLESS), - * jpegQual is psv * 10 + Pt, where psv is the - * predictor selection value (1-7) and Pt is the point transform - * (0-7). A point transform value of 0 is necessary in order to create a fully - * lossless JPEG image. (A non-zero point transform value right-shifts the - * input samples by the specified number of bits, which is effectively a form - * of lossy color quantization.) + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) + */ +DLLEXPORT int tj3Compress8(tjhandle handle, const unsigned char *srcBuf, + int width, int pitch, int height, int pixelFormat, + unsigned char **jpegBuf, size_t *jpegSize); + +/** + * Compress a 12-bit-per-sample packed-pixel RGB, grayscale, or CMYK image into + * a 12-bit-per-sample JPEG image. * - * @param flags the bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT - * "flags" + * \details \copydetails tj3Compress8() + */ +DLLEXPORT int tj3Compress12(tjhandle handle, const short *srcBuf, int width, + int pitch, int height, int pixelFormat, + unsigned char **jpegBuf, size_t *jpegSize); + +/** + * Compress a 16-bit-per-sample packed-pixel RGB, grayscale, or CMYK image into + * a 16-bit-per-sample lossless JPEG image. * - * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() - * and #tjGetErrorCode().) + * \details \copydetails tj3Compress8() */ -DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf, - int width, int pitch, int height, int pixelFormat, - unsigned char **jpegBuf, unsigned long *jpegSize, - int jpegSubsamp, int jpegQual, int flags); +DLLEXPORT int tj3Compress16(tjhandle handle, const unsigned short *srcBuf, + int width, int pitch, int height, int pixelFormat, + unsigned char **jpegBuf, size_t *jpegSize); /** - * Compress a unified planar YUV image into a JPEG image. + * Compress an 8-bit-per-sample unified planar YUV image into an + * 8-bit-per-sample JPEG image. * - * @param handle a handle to a TurboJPEG compressor or transformer instance + * @param handle handle to a TurboJPEG instance that has been initialized for + * compression * * @param srcBuf pointer to a buffer containing a unified planar YUV source * image to be compressed. The size of this buffer should match the value - * returned by #tjBufSizeYUV2() for the given image width, height, row - * alignment, and level of chrominance subsampling. The Y, U (Cb), and V (Cr) - * image planes should be stored sequentially in the buffer. (Refer to - * @ref YUVnotes "YUV Image Format Notes".) + * returned by #tj3YUVBufSize() for the given image width, height, row + * alignment, and level of chrominance subsampling (see #TJPARAM_SUBSAMP.) The + * Y, U (Cb), and V (Cr) image planes should be stored sequentially in the + * buffer. (Refer to @ref YUVnotes "YUV Image Format Notes".) * * @param width width (in pixels) of the source image. If the width is not an * even multiple of the MCU block width (see #tjMCUWidth), then an intermediate @@ -825,60 +1163,52 @@ DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf, * an even multiple of the MCU block height (see #tjMCUHeight), then an * intermediate buffer copy will be performed. * - * @param subsamp the level of chrominance subsampling used in the source image - * (see @ref TJSAMP "Chrominance subsampling options".) - * * @param jpegBuf address of a pointer to a byte buffer that will receive the * JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to * accommodate the size of the JPEG image. Thus, you can choose to: - * -# pre-allocate the JPEG buffer with an arbitrary size using #tjAlloc() and + * -# pre-allocate the JPEG buffer with an arbitrary size using #tj3Alloc() and * let TurboJPEG grow the buffer as needed, * -# set `*jpegBuf` to NULL to tell TurboJPEG to allocate the buffer for you, * or * -# pre-allocate the buffer to a "worst case" size determined by calling - * #tjBufSize(). This should ensure that the buffer never has to be - * re-allocated. (Setting #TJFLAG_NOREALLOC guarantees that it won't be.) + * #tj3JPEGBufSize(). This should ensure that the buffer never has to be + * re-allocated. (Setting #TJPARAM_NOREALLOC guarantees that it won't be.) * . * If you choose option 1, then `*jpegSize` should be set to the size of your - * pre-allocated buffer. In any case, unless you have set #TJFLAG_NOREALLOC, + * pre-allocated buffer. In any case, unless you have set #TJPARAM_NOREALLOC, * you should always check `*jpegBuf` upon return from this function, as it may * have changed. * - * @param jpegSize pointer to an unsigned long variable that holds the size of - * the JPEG buffer. If `*jpegBuf` points to a pre-allocated buffer, then - * `*jpegSize` should be set to the size of the buffer. Upon return, - * `*jpegSize` will contain the size of the JPEG image (in bytes.) If - * `*jpegBuf` points to a JPEG buffer that is being reused from a previous call - * to one of the JPEG compression functions, then `*jpegSize` is ignored. - * - * @param jpegQual the image quality of the generated JPEG image (1 = worst, - * 100 = best) + * @param jpegSize pointer to a size_t variable that holds the size of the JPEG + * buffer. If `*jpegBuf` points to a pre-allocated buffer, then `*jpegSize` + * should be set to the size of the buffer. Upon return, `*jpegSize` will + * contain the size of the JPEG image (in bytes.) If `*jpegBuf` points to a + * JPEG buffer that is being reused from a previous call to one of the JPEG + * compression functions, then `*jpegSize` is ignored. * - * @param flags the bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT - * "flags" - * - * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() - * and #tjGetErrorCode().) + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) */ -DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf, - int width, int align, int height, int subsamp, - unsigned char **jpegBuf, - unsigned long *jpegSize, int jpegQual, - int flags); +DLLEXPORT int tj3CompressFromYUV8(tjhandle handle, + const unsigned char *srcBuf, int width, + int align, int height, + unsigned char **jpegBuf, size_t *jpegSize); /** - * Compress a set of Y, U (Cb), and V (Cr) image planes into a JPEG image. + * Compress a set of 8-bit-per-sample Y, U (Cb), and V (Cr) image planes into + * an 8-bit-per-sample JPEG image. * - * @param handle a handle to a TurboJPEG compressor or transformer instance + * @param handle handle to a TurboJPEG instance that has been initialized for + * compression * * @param srcPlanes an array of pointers to Y, U (Cb), and V (Cr) image planes * (or just a Y plane, if compressing a grayscale image) that contain a YUV * source image to be compressed. These planes can be contiguous or * non-contiguous in memory. The size of each plane should match the value - * returned by #tjPlaneSizeYUV() for the given image width, height, strides, - * and level of chrominance subsampling. Refer to @ref YUVnotes - * "YUV Image Format Notes" for more details. + * returned by #tj3YUVPlaneSize() for the given image width, height, strides, + * and level of chrominance subsampling (see #TJPARAM_SUBSAMP.) Refer to + * @ref YUVnotes "YUV Image Format Notes" for more details. * * @param width width (in pixels) of the source image. If the width is not an * even multiple of the MCU block width (see #tjMCUWidth), then an intermediate @@ -897,48 +1227,37 @@ DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf, * an even multiple of the MCU block height (see #tjMCUHeight), then an * intermediate buffer copy will be performed. * - * @param subsamp the level of chrominance subsampling used in the source image - * (see @ref TJSAMP "Chrominance subsampling options".) - * * @param jpegBuf address of a pointer to a byte buffer that will receive the * JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to * accommodate the size of the JPEG image. Thus, you can choose to: - * -# pre-allocate the JPEG buffer with an arbitrary size using #tjAlloc() and + * -# pre-allocate the JPEG buffer with an arbitrary size using #tj3Alloc() and * let TurboJPEG grow the buffer as needed, * -# set `*jpegBuf` to NULL to tell TurboJPEG to allocate the buffer for you, * or * -# pre-allocate the buffer to a "worst case" size determined by calling - * #tjBufSize(). This should ensure that the buffer never has to be - * re-allocated. (Setting #TJFLAG_NOREALLOC guarantees that it won't be.) + * #tj3JPEGBufSize(). This should ensure that the buffer never has to be + * re-allocated. (Setting #TJPARAM_NOREALLOC guarantees that it won't be.) * . * If you choose option 1, then `*jpegSize` should be set to the size of your - * pre-allocated buffer. In any case, unless you have set #TJFLAG_NOREALLOC, + * pre-allocated buffer. In any case, unless you have set #TJPARAM_NOREALLOC, * you should always check `*jpegBuf` upon return from this function, as it may * have changed. * - * @param jpegSize pointer to an unsigned long variable that holds the size of - * the JPEG buffer. If `*jpegBuf` points to a pre-allocated buffer, then - * `*jpegSize` should be set to the size of the buffer. Upon return, - * `*jpegSize` will contain the size of the JPEG image (in bytes.) If - * `*jpegBuf` points to a JPEG buffer that is being reused from a previous call - * to one of the JPEG compression functions, then `*jpegSize` is ignored. - * - * @param jpegQual the image quality of the generated JPEG image (1 = worst, - * 100 = best) - * - * @param flags the bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT - * "flags" + * @param jpegSize pointer to a size_t variable that holds the size of the JPEG + * buffer. If `*jpegBuf` points to a pre-allocated buffer, then `*jpegSize` + * should be set to the size of the buffer. Upon return, `*jpegSize` will + * contain the size of the JPEG image (in bytes.) If `*jpegBuf` points to a + * JPEG buffer that is being reused from a previous call to one of the JPEG + * compression functions, then `*jpegSize` is ignored. * - * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() - * and #tjGetErrorCode().) + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) */ -DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle, - const unsigned char **srcPlanes, - int width, const int *strides, - int height, int subsamp, - unsigned char **jpegBuf, - unsigned long *jpegSize, int jpegQual, - int flags); +DLLEXPORT int tj3CompressFromYUVPlanes8(tjhandle handle, + const unsigned char * const *srcPlanes, + int width, const int *strides, + int height, unsigned char **jpegBuf, + size_t *jpegSize); /** @@ -958,12 +1277,16 @@ DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle, * * @param jpegSubsamp the level of chrominance subsampling to be used when * generating the JPEG image (see @ref TJSAMP - * "Chrominance subsampling options".) + * "Chrominance subsampling options".) #TJSAMP_UNKNOWN is treated like + * #TJSAMP_444, since a buffer large enough to hold a JPEG image with no + * subsampling should also be large enough to hold a JPEG image with an + * arbitrary level of subsampling. Note that lossless JPEG images always + * use #TJSAMP_444. * * @return the maximum size of the buffer (in bytes) required to hold the - * image, or -1 if the arguments are out of bounds. + * image, or 0 if the arguments are out of bounds. */ -DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp); +DLLEXPORT size_t tj3JPEGBufSize(int width, int height, int jpegSubsamp); /** @@ -981,11 +1304,10 @@ DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp); * @param subsamp level of chrominance subsampling in the image (see * @ref TJSAMP "Chrominance subsampling options".) * - * @return the size of the buffer (in bytes) required to hold the image, or -1 + * @return the size of the buffer (in bytes) required to hold the image, or 0 * if the arguments are out of bounds. */ -DLLEXPORT unsigned long tjBufSizeYUV2(int width, int align, int height, - int subsamp); +DLLEXPORT size_t tj3YUVBufSize(int width, int align, int height, int subsamp); /** @@ -1007,10 +1329,10 @@ DLLEXPORT unsigned long tjBufSizeYUV2(int width, int align, int height, * @ref TJSAMP "Chrominance subsampling options".) * * @return the size of the buffer (in bytes) required to hold the YUV image - * plane, or -1 if the arguments are out of bounds. + * plane, or 0 if the arguments are out of bounds. */ -DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride, - int height, int subsamp); +DLLEXPORT size_t tj3YUVPlaneSize(int componentID, int width, int stride, + int height, int subsamp); /** @@ -1024,10 +1346,10 @@ DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride, * @param subsamp level of chrominance subsampling in the image (see * @ref TJSAMP "Chrominance subsampling options".) * - * @return the plane width of a YUV image plane with the given parameters, or - * -1 if the arguments are out of bounds. + * @return the plane width of a YUV image plane with the given parameters, or 0 + * if the arguments are out of bounds. */ -DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp); +DLLEXPORT int tj3YUVPlaneWidth(int componentID, int width, int subsamp); /** @@ -1042,31 +1364,33 @@ DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp); * @ref TJSAMP "Chrominance subsampling options".) * * @return the plane height of a YUV image plane with the given parameters, or - * -1 if the arguments are out of bounds. + * 0 if the arguments are out of bounds. */ -DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp); +DLLEXPORT int tj3YUVPlaneHeight(int componentID, int height, int subsamp); /** - * Encode a packed-pixel RGB or grayscale image into a unified planar YUV - * image. This function performs color conversion (which is accelerated in the - * libjpeg-turbo implementation) but does not execute any of the other steps in - * the JPEG compression process. + * Encode an 8-bit-per-sample packed-pixel RGB or grayscale image into an + * 8-bit-per-sample unified planar YUV image. This function performs color + * conversion (which is accelerated in the libjpeg-turbo implementation) but + * does not execute any of the other steps in the JPEG compression process. * - * @param handle a handle to a TurboJPEG compressor or transformer instance + * @param handle handle to a TurboJPEG instance that has been initialized for + * compression * * @param srcBuf pointer to a buffer containing a packed-pixel RGB or grayscale - * source image to be encoded + * source image to be encoded. This buffer should normally be `pitch * height` + * bytes in size. However, you can also use this parameter to encode from a + * specific region of a larger buffer. * * @param width width (in pixels) of the source image * * @param pitch bytes per row in the source image. Normally this should be - * width * #tjPixelSize[pixelFormat], if the image is unpadded, or - * #TJPAD(width * #tjPixelSize[pixelFormat]) if each row of the image - * is padded to the nearest multiple of 4 bytes, as is the case for Windows - * bitmaps. You can also be clever and use this parameter to skip rows, etc. - * Setting this parameter to 0 is the equivalent of setting it to - * width * #tjPixelSize[pixelFormat]. + * width * #tjPixelSize[pixelFormat], if the image is unpadded. + * (Setting this parameter to 0 is the equivalent of setting it to + * width * #tjPixelSize[pixelFormat].) However, you can also use this + * parameter to specify the row alignment/padding of the source image, to skip + * rows, or to encode from a specific region of a larger packed-pixel image. * * @param height height (in pixels) of the source image * @@ -1074,55 +1398,48 @@ DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp); * "Pixel formats".) * * @param dstBuf pointer to a buffer that will receive the unified planar YUV - * image. Use #tjBufSizeYUV2() to determine the appropriate size for this + * image. Use #tj3YUVBufSize() to determine the appropriate size for this * buffer based on the image width, height, row alignment, and level of - * chrominance subsampling. The Y, U (Cb), and V (Cr) image planes will be - * stored sequentially in the buffer. (Refer to @ref YUVnotes - * "YUV Image Format Notes".) + * chrominance subsampling (see #TJPARAM_SUBSAMP.) The Y, U (Cb), and V (Cr) + * image planes will be stored sequentially in the buffer. (Refer to + * @ref YUVnotes "YUV Image Format Notes".) * * @param align row alignment (in bytes) of the YUV image (must be a power of * 2.) Setting this parameter to n will cause each row in each plane of the * YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.) * To generate images suitable for X Video, `align` should be set to 4. * - * @param subsamp the level of chrominance subsampling to be used when - * generating the YUV image (see @ref TJSAMP - * "Chrominance subsampling options".) To generate images suitable for X - * Video, `subsamp` should be set to @ref TJSAMP_420. This produces an image - * compatible with the I420 (AKA "YUV420P") format. - * - * @param flags the bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT - * "flags" - * - * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() - * and #tjGetErrorCode().) + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) */ -DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf, - int width, int pitch, int height, int pixelFormat, - unsigned char *dstBuf, int align, int subsamp, - int flags); +DLLEXPORT int tj3EncodeYUV8(tjhandle handle, const unsigned char *srcBuf, + int width, int pitch, int height, int pixelFormat, + unsigned char *dstBuf, int align); /** - * Encode a packed-pixel RGB or grayscale image into separate Y, U (Cb), and - * V (Cr) image planes. This function performs color conversion (which is - * accelerated in the libjpeg-turbo implementation) but does not execute any of - * the other steps in the JPEG compression process. + * Encode an 8-bit-per-sample packed-pixel RGB or grayscale image into separate + * 8-bit-per-sample Y, U (Cb), and V (Cr) image planes. This function performs + * color conversion (which is accelerated in the libjpeg-turbo implementation) + * but does not execute any of the other steps in the JPEG compression process. * - * @param handle a handle to a TurboJPEG compressor or transformer instance + * @param handle handle to a TurboJPEG instance that has been initialized for + * compression * * @param srcBuf pointer to a buffer containing a packed-pixel RGB or grayscale - * source image to be encoded + * source image to be encoded. This buffer should normally be `pitch * height` + * bytes in size. However, you can also use this parameter to encode from a + * specific region of a larger buffer. + * * * @param width width (in pixels) of the source image * * @param pitch bytes per row in the source image. Normally this should be - * width * #tjPixelSize[pixelFormat], if the image is unpadded, or - * #TJPAD(width * #tjPixelSize[pixelFormat]) if each row of the image - * is padded to the nearest multiple of 4 bytes, as is the case for Windows - * bitmaps. You can also be clever and use this parameter to skip rows, etc. - * Setting this parameter to 0 is the equivalent of setting it to - * width * #tjPixelSize[pixelFormat]. + * width * #tjPixelSize[pixelFormat], if the image is unpadded. + * (Setting this parameter to 0 is the equivalent of setting it to + * width * #tjPixelSize[pixelFormat].) However, you can also use this + * parameter to specify the row alignment/padding of the source image, to skip + * rows, or to encode from a specific region of a larger packed-pixel image. * * @param height height (in pixels) of the source image * @@ -1132,9 +1449,10 @@ DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf, * @param dstPlanes an array of pointers to Y, U (Cb), and V (Cr) image planes * (or just a Y plane, if generating a grayscale image) that will receive the * encoded image. These planes can be contiguous or non-contiguous in memory. - * Use #tjPlaneSizeYUV() to determine the appropriate size for each plane based - * on the image width, height, strides, and level of chrominance subsampling. - * Refer to @ref YUVnotes "YUV Image Format Notes" for more details. + * Use #tj3YUVPlaneSize() to determine the appropriate size for each plane + * based on the image width, height, strides, and level of chrominance + * subsampling (see #TJPARAM_SUBSAMP.) Refer to @ref YUVnotes + * "YUV Image Format Notes" for more details. * * @param strides an array of integers, each specifying the number of bytes per * row in the corresponding plane of the YUV image. Setting the stride for any @@ -1145,38 +1463,23 @@ DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf, * to encode an RGB or grayscale image into a subregion of a larger planar YUV * image. * - * @param subsamp the level of chrominance subsampling to be used when - * generating the YUV image (see @ref TJSAMP - * "Chrominance subsampling options".) To generate images suitable for X - * Video, `subsamp` should be set to @ref TJSAMP_420. This produces an image - * compatible with the I420 (AKA "YUV420P") format. - * - * @param flags the bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT - * "flags" - * - * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() - * and #tjGetErrorCode().) - */ -DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf, - int width, int pitch, int height, - int pixelFormat, unsigned char **dstPlanes, - int *strides, int subsamp, int flags); - - -/** - * Create a TurboJPEG decompressor instance. - * - * @return a handle to the newly-created instance, or NULL if an error occurred - * (see #tjGetErrorStr2().) + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) */ -DLLEXPORT tjhandle tjInitDecompress(void); +DLLEXPORT int tj3EncodeYUVPlanes8(tjhandle handle, const unsigned char *srcBuf, + int width, int pitch, int height, + int pixelFormat, unsigned char **dstPlanes, + int *strides); /** * Retrieve information about a JPEG image without decompressing it, or prime - * the decompressor with quantization and Huffman tables. + * the decompressor with quantization and Huffman tables. If a JPEG image is + * passed to this function, then the @ref TJPARAM "parameters" that describe + * the JPEG image will be set when the function returns. * - * @param handle a handle to a TurboJPEG decompressor or transformer instance + * @param handle handle to a TurboJPEG instance that has been initialized for + * decompression * * @param jpegBuf pointer to a byte buffer containing a JPEG image or an * "abbreviated table specification" (AKA "tables-only") datastream. Passing a @@ -1188,38 +1491,12 @@ DLLEXPORT tjhandle tjInitDecompress(void); * * @param jpegSize size of the JPEG image or tables-only datastream (in bytes) * - * @param width pointer to an integer variable that will receive the width (in - * pixels) of the JPEG image. If `jpegBuf` points to a tables-only datastream, - * then `width` is ignored. - * - * @param height pointer to an integer variable that will receive the height - * (in pixels) of the JPEG image. If `jpegBuf` points to a tables-only - * datastream, then `height` is ignored. - * - * @param jpegSubsamp pointer to an integer variable that will receive the - * level of chrominance subsampling used when the JPEG image was compressed - * (see @ref TJSAMP "Chrominance subsampling options".) If `jpegBuf` points to - * a tables-only datastream, then `jpegSubsamp` is ignored. - * - * @param jpegColorspace pointer to an integer variable that will receive one - * of the JPEG colorspace constants, indicating the colorspace of the JPEG - * image (see @ref TJCS "JPEG colorspaces".) If `jpegBuf` points to a - * tables-only datastream, then `jpegColorspace` is ignored. - * - * @param jpegFlags pointer to an integer variable that will receive the - * bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT "flags", such as - * #TJFLAG_PROGRESSIVE and #TJFLAG_LOSSLESS, that describe the JPEG image. If - * jpegBuf points to a tables-only datastream, then jpegFlags - * is ignored. - * - * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() - * and #tjGetErrorCode().) + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) */ -DLLEXPORT int tjDecompressHeader4(tjhandle handle, +DLLEXPORT int tj3DecompressHeader(tjhandle handle, const unsigned char *jpegBuf, - unsigned long jpegSize, int *width, - int *height, int *jpegSubsamp, - int *jpegColorspace, int *jpegFlags); + size_t jpegSize); /** @@ -1230,15 +1507,69 @@ DLLEXPORT int tjDecompressHeader4(tjhandle handle, * the number of elements in the list * * @return a pointer to a list of fractional scaling factors, or NULL if an - * error is encountered (see #tjGetErrorStr2().) + * error is encountered (see #tj3GetErrorStr().) + */ +DLLEXPORT tjscalingfactor *tj3GetScalingFactors(int *numScalingFactors); + + +/** + * Set the scaling factor for subsequent lossy decompression operations. + * + * @param handle handle to a TurboJPEG instance that has been initialized for + * decompression + * + * @param scalingFactor #tjscalingfactor structure that specifies a fractional + * scaling factor that the decompressor supports (see #tj3GetScalingFactors()), + * or #TJUNSCALED for no scaling. Decompression scaling is a function + * of the IDCT algorithm, so scaling factors are generally limited to multiples + * of 1/8. If the entire JPEG image will be decompressed, then the width and + * height of the scaled destination image can be determined by calling + * #TJSCALED() with the JPEG width and height (see #TJPARAM_JPEGWIDTH and + * #TJPARAM_JPEGHEIGHT) and the specified scaling factor. When decompressing + * into a planar YUV image, an intermediate buffer copy will be performed if + * the width or height of the scaled destination image is not an even multiple + * of the MCU block size (see #tjMCUWidth and #tjMCUHeight.) Note that + * decompression scaling is not available (and the specified scaling factor is + * ignored) when decompressing lossless JPEG images (see #TJPARAM_LOSSLESS), + * since the IDCT algorithm is not used with those images. Note also that + * #TJPARAM_FASTDCT is ignored when decompression scaling is enabled. + * + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr().) + */ +DLLEXPORT int tj3SetScalingFactor(tjhandle handle, + tjscalingfactor scalingFactor); + + +/** + * Set the cropping region for partially decompressing a lossy JPEG image into + * a packed-pixel image + * + * @param handle handle to a TurboJPEG instance that has been initialized for + * decompression + * + * @param croppingRegion #tjregion structure that specifies a subregion of the + * JPEG image to decompress, or #TJUNCROPPED for no cropping. The + * left boundary of the cropping region must be evenly divisible by the scaled + * MCU block width (#TJSCALED(#tjMCUWidth[subsamp], scalingFactor), + * where `subsamp` is the level of chrominance subsampling in the JPEG image + * (see #TJPARAM_SUBSAMP) and `scalingFactor` is the decompression scaling + * factor (see #tj3SetScalingFactor().) The cropping region should be + * specified relative to the scaled image dimensions. Unless `croppingRegion` + * is #TJUNCROPPED, the JPEG header must be read (see + * #tj3DecompressHeader()) prior to calling this function. + * + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr().) */ -DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numScalingFactors); +DLLEXPORT int tj3SetCroppingRegion(tjhandle handle, tjregion croppingRegion); /** - * Decompress a JPEG image into a packed-pixel RGB, grayscale, or CMYK image. + * Decompress an 8-bit-per-sample JPEG image into an 8-bit-per-sample + * packed-pixel RGB, grayscale, or CMYK image. The @ref TJPARAM "parameters" + * that describe the JPEG image will be set when this function returns. * - * @param handle a handle to a TurboJPEG decompressor or transformer instance + * @param handle handle to a TurboJPEG instance that has been initialized for + * decompression * * @param jpegBuf pointer to a byte buffer containing the JPEG image to * decompress @@ -1246,59 +1577,68 @@ DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numScalingFactors); * @param jpegSize size of the JPEG image (in bytes) * * @param dstBuf pointer to a buffer that will receive the packed-pixel - * decompressed image. This buffer should normally be `pitch * scaledHeight` - * bytes in size, where `scaledHeight` can be determined by calling #TJSCALED() - * with the JPEG image height and one of the scaling factors returned by - * #tjGetScalingFactors(). The `dstBuf` pointer may also be used to decompress - * into a specific region of a larger buffer. - * - * @param width desired width (in pixels) of the destination image. If this is - * different than the width of the JPEG image being decompressed, then - * TurboJPEG will use scaling in the JPEG decompressor to generate the largest - * possible image that will fit within the desired width. If `width` is set to - * 0, then only the height will be considered when determining the scaled image - * size. - * - * @param pitch bytes per row in the destination image. Normally this should - * be set to scaledWidth * #tjPixelSize[pixelFormat], if the - * destination image should be unpadded, or - * #TJPAD(scaledWidth * #tjPixelSize[pixelFormat]) if each row of the - * destination image should be padded to the nearest multiple of 4 bytes, as is - * the case for Windows bitmaps. (NOTE: `scaledWidth` can be determined by - * calling #TJSCALED() with the JPEG image width and one of the scaling factors - * returned by #tjGetScalingFactors().) You can also be clever and use the - * pitch parameter to skip rows, etc. Setting this parameter to 0 is the + * decompressed image. This buffer should normally be + * `pitch * destinationHeight` samples in size. However, you can also use this + * parameter to decompress into a specific region of a larger buffer. NOTE: + * If the JPEG image is lossy, then `destinationHeight` is either the scaled + * JPEG height (see #TJSCALED(), #TJPARAM_JPEGHEIGHT, and + * #tj3SetScalingFactor()) or the height of the cropping region (see + * #tj3SetCroppingRegion().) If the JPEG image is lossless, then + * `destinationHeight` is the JPEG height. + * + * @param pitch samples per row in the destination image. Normally this should + * be set to destinationWidth * #tjPixelSize[pixelFormat], if the + * destination image should be unpadded. (Setting this parameter to 0 is the * equivalent of setting it to - * scaledWidth * #tjPixelSize[pixelFormat]. - * - * @param height desired height (in pixels) of the destination image. If this - * is different than the height of the JPEG image being decompressed, then - * TurboJPEG will use scaling in the JPEG decompressor to generate the largest - * possible image that will fit within the desired height. If `height` is set - * to 0, then only the width will be considered when determining the scaled - * image size. + * destinationWidth * #tjPixelSize[pixelFormat].) However, you can + * also use this parameter to specify the row alignment/padding of the + * destination image, to skip rows, or to decompress into a specific region of + * a larger buffer. NOTE: If the JPEG image is lossy, then `destinationWidth` + * is either the scaled JPEG width (see #TJSCALED(), #TJPARAM_JPEGWIDTH, and + * #tj3SetScalingFactor()) or the width of the cropping region (see + * #tj3SetCroppingRegion().) If the JPEG image is lossless, then + * `destinationWidth` is the JPEG width. * * @param pixelFormat pixel format of the destination image (see @ref * TJPF "Pixel formats".) * - * @param flags the bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT - * "flags" + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) + */ +DLLEXPORT int tj3Decompress8(tjhandle handle, const unsigned char *jpegBuf, + size_t jpegSize, unsigned char *dstBuf, int pitch, + int pixelFormat); + +/** + * Decompress a 12-bit-per-sample JPEG image into a 12-bit-per-sample + * packed-pixel RGB, grayscale, or CMYK image. * - * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() - * and #tjGetErrorCode().) + * \details \copydetails tj3Decompress8() */ -DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf, - unsigned long jpegSize, unsigned char *dstBuf, - int width, int pitch, int height, int pixelFormat, - int flags); +DLLEXPORT int tj3Decompress12(tjhandle handle, const unsigned char *jpegBuf, + size_t jpegSize, short *dstBuf, int pitch, + int pixelFormat); + +/** + * Decompress a 16-bit-per-sample lossless JPEG image into a 16-bit-per-sample + * packed-pixel RGB, grayscale, or CMYK image. + * + * \details \copydetails tj3Decompress8() + */ +DLLEXPORT int tj3Decompress16(tjhandle handle, const unsigned char *jpegBuf, + size_t jpegSize, unsigned short *dstBuf, + int pitch, int pixelFormat); /** - * Decompress a JPEG image into a unified planar YUV image. This function - * performs JPEG decompression but leaves out the color conversion step, so a - * planar YUV image is generated instead of a packed-pixel image. + * Decompress an 8-bit-per-sample JPEG image into an 8-bit-per-sample unified + * planar YUV image. This function performs JPEG decompression but leaves out + * the color conversion step, so a planar YUV image is generated instead of a + * packed-pixel image. The @ref TJPARAM "parameters" that describe the JPEG + * image will be set when this function returns. * - * @param handle a handle to a TurboJPEG decompressor or transformer instance + * @param handle handle to a TurboJPEG instance that has been initialized for + * decompression * * @param jpegBuf pointer to a byte buffer containing the JPEG image to * decompress @@ -1306,52 +1646,36 @@ DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf, * @param jpegSize size of the JPEG image (in bytes) * * @param dstBuf pointer to a buffer that will receive the unified planar YUV - * decompressed image. Use #tjBufSizeYUV2() to determine the appropriate size - * for this buffer based on the scaled image width, scaled image height, row - * alignment, and level of chrominance subsampling. The Y, U (Cb), and V (Cr) - * image planes will be stored sequentially in the buffer. (Refer to - * @ref YUVnotes "YUV Image Format Notes".) - * - * @param width desired width (in pixels) of the YUV image. If this is - * different than the width of the JPEG image being decompressed, then - * TurboJPEG will use scaling in the JPEG decompressor to generate the largest - * possible image that will fit within the desired width. If `width` is set to - * 0, then only the height will be considered when determining the scaled image - * size. If the scaled width is not an even multiple of the MCU block width - * (see #tjMCUWidth), then an intermediate buffer copy will be performed. + * decompressed image. Use #tj3YUVBufSize() to determine the appropriate size + * for this buffer based on the scaled JPEG width and height (see #TJSCALED(), + * #TJPARAM_JPEGWIDTH, #TJPARAM_JPEGHEIGHT, and #tj3SetScalingFactor()), row + * alignment, and level of chrominance subsampling (see #TJPARAM_SUBSAMP.) The + * Y, U (Cb), and V (Cr) image planes will be stored sequentially in the + * buffer. (Refer to @ref YUVnotes "YUV Image Format Notes".) * * @param align row alignment (in bytes) of the YUV image (must be a power of * 2.) Setting this parameter to n will cause each row in each plane of the * YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.) * To generate images suitable for X Video, `align` should be set to 4. * - * @param height desired height (in pixels) of the YUV image. If this is - * different than the height of the JPEG image being decompressed, then - * TurboJPEG will use scaling in the JPEG decompressor to generate the largest - * possible image that will fit within the desired height. If `height` is set - * to 0, then only the width will be considered when determining the scaled - * image size. If the scaled height is not an even multiple of the MCU block - * height (see #tjMCUHeight), then an intermediate buffer copy will be - * performed. - * - * @param flags the bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT - * "flags" - * - * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() - * and #tjGetErrorCode().) + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) */ -DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf, - unsigned long jpegSize, unsigned char *dstBuf, - int width, int align, int height, int flags); +DLLEXPORT int tj3DecompressToYUV8(tjhandle handle, + const unsigned char *jpegBuf, + size_t jpegSize, + unsigned char *dstBuf, int align); /** - * Decompress a JPEG image into separate Y, U (Cb), and V (Cr) image - * planes. This function performs JPEG decompression but leaves out the color - * conversion step, so a planar YUV image is generated instead of a - * packed-pixel image. + * Decompress an 8-bit-per-sample JPEG image into separate 8-bit-per-sample Y, + * U (Cb), and V (Cr) image planes. This function performs JPEG decompression + * but leaves out the color conversion step, so a planar YUV image is generated + * instead of a packed-pixel image. The @ref TJPARAM "parameters" that + * describe the JPEG image will be set when this function returns. * - * @param handle a handle to a TurboJPEG decompressor or transformer instance + * @param handle handle to a TurboJPEG instance that has been initialized for + * decompression * * @param jpegBuf pointer to a byte buffer containing the JPEG image to * decompress @@ -1361,18 +1685,11 @@ DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf, * @param dstPlanes an array of pointers to Y, U (Cb), and V (Cr) image planes * (or just a Y plane, if decompressing a grayscale image) that will receive * the decompressed image. These planes can be contiguous or non-contiguous in - * memory. Use #tjPlaneSizeYUV() to determine the appropriate size for each - * plane based on the scaled image width, scaled image height, strides, and - * level of chrominance subsampling. Refer to @ref YUVnotes - * "YUV Image Format Notes" for more details. - * - * @param width desired width (in pixels) of the YUV image. If this is - * different than the width of the JPEG image being decompressed, then - * TurboJPEG will use scaling in the JPEG decompressor to generate the largest - * possible image that will fit within the desired width. If `width` is set to - * 0, then only the height will be considered when determining the scaled image - * size. If the scaled width is not an even multiple of the MCU block width - * (see #tjMCUWidth), then an intermediate buffer copy will be performed. + * memory. Use #tj3YUVPlaneSize() to determine the appropriate size for each + * plane based on the scaled JPEG width and height (see #TJSCALED(), + * #TJPARAM_JPEGWIDTH, #TJPARAM_JPEGHEIGHT, and #tj3SetScalingFactor()), + * strides, and level of chrominance subsampling (see #TJPARAM_SUBSAMP.) Refer + * to @ref YUVnotes "YUV Image Format Notes" for more details. * * @param strides an array of integers, each specifying the number of bytes per * row in the corresponding plane of the YUV image. Setting the stride for any @@ -1383,99 +1700,82 @@ DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf, * padding to each plane or to decompress the JPEG image into a subregion of a * larger planar YUV image. * - * @param height desired height (in pixels) of the YUV image. If this is - * different than the height of the JPEG image being decompressed, then - * TurboJPEG will use scaling in the JPEG decompressor to generate the largest - * possible image that will fit within the desired height. If `height` is set - * to 0, then only the width will be considered when determining the scaled - * image size. If the scaled height is not an even multiple of the MCU block - * height (see #tjMCUHeight), then an intermediate buffer copy will be - * performed. - * - * @param flags the bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT - * "flags" - * - * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() - * and #tjGetErrorCode().) + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) */ -DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle, - const unsigned char *jpegBuf, - unsigned long jpegSize, - unsigned char **dstPlanes, int width, - int *strides, int height, int flags); +DLLEXPORT int tj3DecompressToYUVPlanes8(tjhandle handle, + const unsigned char *jpegBuf, + size_t jpegSize, + unsigned char **dstPlanes, + int *strides); /** - * Decode a unified planar YUV image into a packed-pixel RGB or grayscale - * image. This function performs color conversion (which is accelerated in the - * libjpeg-turbo implementation) but does not execute any of the other steps in - * the JPEG decompression process. + * Decode an 8-bit-per-sample unified planar YUV image into an 8-bit-per-sample + * packed-pixel RGB or grayscale image. This function performs color + * conversion (which is accelerated in the libjpeg-turbo implementation) but + * does not execute any of the other steps in the JPEG decompression process. * - * @param handle a handle to a TurboJPEG decompressor or transformer instance + * @param handle handle to a TurboJPEG instance that has been initialized for + * decompression * * @param srcBuf pointer to a buffer containing a unified planar YUV source * image to be decoded. The size of this buffer should match the value - * returned by #tjBufSizeYUV2() for the given image width, height, row - * alignment, and level of chrominance subsampling. The Y, U (Cb), and V (Cr) - * image planes should be stored sequentially in the source buffer. (Refer to - * @ref YUVnotes "YUV Image Format Notes".) + * returned by #tj3YUVBufSize() for the given image width, height, row + * alignment, and level of chrominance subsampling (see #TJPARAM_SUBSAMP.) The + * Y, U (Cb), and V (Cr) image planes should be stored sequentially in the + * source buffer. (Refer to @ref YUVnotes "YUV Image Format Notes".) * * @param align row alignment (in bytes) of the YUV source image (must be a * power of 2.) Setting this parameter to n indicates that each row in each * plane of the YUV source image is padded to the nearest multiple of n bytes * (1 = unpadded.) * - * @param subsamp the level of chrominance subsampling used in the YUV source - * image (see @ref TJSAMP "Chrominance subsampling options".) - * * @param dstBuf pointer to a buffer that will receive the packed-pixel decoded - * image. This buffer should normally be `pitch * height` bytes in size, but - * the `dstBuf` pointer can also be used to decode into a specific region of a - * larger buffer. + * image. This buffer should normally be `pitch * height` bytes in size. + * However, you can also use this parameter to decode into a specific region of + * a larger buffer. * * @param width width (in pixels) of the source and destination images * * @param pitch bytes per row in the destination image. Normally this should * be set to width * #tjPixelSize[pixelFormat], if the destination - * image should be unpadded, or - * #TJPAD(width * #tjPixelSize[pixelFormat]) if each row of the - * destination image should be padded to the nearest multiple of 4 bytes, as is - * the case for Windows bitmaps. You can also be clever and use the pitch - * parameter to skip rows, etc. Setting this parameter to 0 is the equivalent - * of setting it to width * #tjPixelSize[pixelFormat]. + * image should be unpadded. (Setting this parameter to 0 is the equivalent of + * setting it to width * #tjPixelSize[pixelFormat].) However, you can + * also use this parameter to specify the row alignment/padding of the + * destination image, to skip rows, or to decode into a specific region of a + * larger buffer. * * @param height height (in pixels) of the source and destination images * * @param pixelFormat pixel format of the destination image (see @ref TJPF * "Pixel formats".) * - * @param flags the bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT - * "flags" - * - * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() - * and #tjGetErrorCode().) + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) */ -DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf, - int align, int subsamp, unsigned char *dstBuf, - int width, int pitch, int height, int pixelFormat, - int flags); +DLLEXPORT int tj3DecodeYUV8(tjhandle handle, const unsigned char *srcBuf, + int align, unsigned char *dstBuf, int width, + int pitch, int height, int pixelFormat); /** - * Decode a set of Y, U (Cb), and V (Cr) image planes into a packed-pixel RGB - * or grayscale image. This function performs color conversion (which is - * accelerated in the libjpeg-turbo implementation) but does not execute any of - * the other steps in the JPEG decompression process. + * Decode a set of 8-bit-per-sample Y, U (Cb), and V (Cr) image planes into an + * 8-bit-per-sample packed-pixel RGB or grayscale image. This function + * performs color conversion (which is accelerated in the libjpeg-turbo + * implementation) but does not execute any of the other steps in the JPEG + * decompression process. * - * @param handle a handle to a TurboJPEG decompressor or transformer instance + * @param handle handle to a TurboJPEG instance that has been initialized for + * decompression * * @param srcPlanes an array of pointers to Y, U (Cb), and V (Cr) image planes * (or just a Y plane, if decoding a grayscale image) that contain a YUV image * to be decoded. These planes can be contiguous or non-contiguous in memory. - * The size of each plane should match the value returned by #tjPlaneSizeYUV() + * The size of each plane should match the value returned by #tj3YUVPlaneSize() * for the given image width, height, strides, and level of chrominance - * subsampling. Refer to @ref YUVnotes "YUV Image Format Notes" for more - * details. + * subsampling (see #TJPARAM_SUBSAMP.) Refer to @ref YUVnotes + * "YUV Image Format Notes" for more details. * * @param strides an array of integers, each specifying the number of bytes per * row in the corresponding plane of the YUV source image. Setting the stride @@ -1485,50 +1785,34 @@ DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf, * can adjust the strides in order to specify an arbitrary amount of row * padding in each plane or to decode a subregion of a larger planar YUV image. * - * @param subsamp the level of chrominance subsampling used in the YUV source - * image (see @ref TJSAMP "Chrominance subsampling options".) - * * @param dstBuf pointer to a buffer that will receive the packed-pixel decoded - * image. This buffer should normally be `pitch * height` bytes in size, but - * the `dstBuf` pointer can also be used to decode into a specific region of a - * larger buffer. + * image. This buffer should normally be `pitch * height` bytes in size. + * However, you can also use this parameter to decode into a specific region of + * a larger buffer. * * @param width width (in pixels) of the source and destination images * * @param pitch bytes per row in the destination image. Normally this should * be set to width * #tjPixelSize[pixelFormat], if the destination - * image should be unpadded, or - * #TJPAD(width * #tjPixelSize[pixelFormat]) if each row of the - * destination image should be padded to the nearest multiple of 4 bytes, as is - * the case for Windows bitmaps. You can also be clever and use the pitch - * parameter to skip rows, etc. Setting this parameter to 0 is the equivalent - * of setting it to width * #tjPixelSize[pixelFormat]. + * image should be unpadded. (Setting this parameter to 0 is the equivalent of + * setting it to width * #tjPixelSize[pixelFormat].) However, you can + * also use this parameter to specify the row alignment/padding of the + * destination image, to skip rows, or to decode into a specific region of a + * larger buffer. * * @param height height (in pixels) of the source and destination images * * @param pixelFormat pixel format of the destination image (see @ref TJPF * "Pixel formats".) * - * @param flags the bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT - * "flags" - * - * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() - * and #tjGetErrorCode().) - */ -DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle, - const unsigned char **srcPlanes, - const int *strides, int subsamp, - unsigned char *dstBuf, int width, int pitch, - int height, int pixelFormat, int flags); - - -/** - * Create a new TurboJPEG transformer instance. - * - * @return a handle to the newly-created instance, or NULL if an error - * occurred (see #tjGetErrorStr2().) + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) */ -DLLEXPORT tjhandle tjInitTransform(void); +DLLEXPORT int tj3DecodeYUVPlanes8(tjhandle handle, + const unsigned char * const *srcPlanes, + const int *strides, unsigned char *dstBuf, + int width, int pitch, int height, + int pixelFormat); /** @@ -1537,14 +1821,15 @@ DLLEXPORT tjhandle tjInitTransform(void); * structure to another without altering the values of the coefficients. While * this is typically faster than decompressing the image, transforming it, and * re-compressing it, lossless transforms are not free. Each lossless - * transform requires reading and performing Huffman decoding on all of the + * transform requires reading and performing entropy decoding on all of the * coefficients in the source image, regardless of the size of the destination * image. Thus, this function provides a means of generating multiple * transformed images from the same source or applying multiple transformations * simultaneously, in order to eliminate the need to read the source * coefficients multiple times. * - * @param handle a handle to a TurboJPEG transformer instance + * @param handle handle to a TurboJPEG instance that has been initialized for + * lossless transformation * * @param jpegBuf pointer to a byte buffer containing the JPEG source image to * transform @@ -1559,92 +1844,92 @@ DLLEXPORT tjhandle tjInitTransform(void); * destination buffer to accommodate the size of the transformed JPEG image. * Thus, you can choose to: * -# pre-allocate the JPEG destination buffer with an arbitrary size using - * #tjAlloc() and let TurboJPEG grow the buffer as needed, + * #tj3Alloc() and let TurboJPEG grow the buffer as needed, * -# set `dstBufs[i]` to NULL to tell TurboJPEG to allocate the buffer for * you, or * -# pre-allocate the buffer to a "worst case" size determined by calling - * #tjBufSize() with the transformed or cropped width and height. Under normal - * circumstances, this should ensure that the buffer never has to be - * re-allocated. (Setting #TJFLAG_NOREALLOC guarantees that it won't be.) + * #tj3JPEGBufSize() with the transformed or cropped width and height. Under + * normal circumstances, this should ensure that the buffer never has to be + * re-allocated. (Setting #TJPARAM_NOREALLOC guarantees that it won't be.) * Note, however, that there are some rare cases (such as transforming images * with a large amount of embedded EXIF or ICC profile data) in which the * transformed JPEG image will be larger than the worst-case size, and - * #TJFLAG_NOREALLOC cannot be used in those cases. + * #TJPARAM_NOREALLOC cannot be used in those cases. * . * If you choose option 1, then `dstSizes[i]` should be set to the size of your - * pre-allocated buffer. In any case, unless you have set #TJFLAG_NOREALLOC, + * pre-allocated buffer. In any case, unless you have set #TJPARAM_NOREALLOC, * you should always check `dstBufs[i]` upon return from this function, as it * may have changed. * - * @param dstSizes pointer to an array of n unsigned long variables that will - * receive the actual sizes (in bytes) of each transformed JPEG image. If - * `dstBufs[i]` points to a pre-allocated buffer, then `dstSizes[i]` should be - * set to the size of the buffer. Upon return, `dstSizes[i]` will contain the - * size of the transformed JPEG image (in bytes.) + * @param dstSizes pointer to an array of n size_t variables that will receive + * the actual sizes (in bytes) of each transformed JPEG image. If `dstBufs[i]` + * points to a pre-allocated buffer, then `dstSizes[i]` should be set to the + * size of the buffer. Upon return, `dstSizes[i]` will contain the size of the + * transformed JPEG image (in bytes.) * * @param transforms pointer to an array of n #tjtransform structures, each of * which specifies the transform parameters and/or cropping region for the * corresponding transformed JPEG image. * - * @param flags the bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT - * "flags" - * - * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() - * and #tjGetErrorCode().) + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) */ -DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf, - unsigned long jpegSize, int n, - unsigned char **dstBufs, unsigned long *dstSizes, - tjtransform *transforms, int flags); +DLLEXPORT int tj3Transform(tjhandle handle, const unsigned char *jpegBuf, + size_t jpegSize, int n, unsigned char **dstBufs, + size_t *dstSizes, const tjtransform *transforms); /** - * Destroy a TurboJPEG compressor, decompressor, or transformer instance. - * - * @param handle a handle to a TurboJPEG compressor, decompressor or - * transformer instance + * Destroy a TurboJPEG instance. * - * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2().) + * @param handle handle to a TurboJPEG instance. If the handle is NULL, then + * this function has no effect. */ -DLLEXPORT int tjDestroy(tjhandle handle); +DLLEXPORT void tj3Destroy(tjhandle handle); /** * Allocate a byte buffer for use with TurboJPEG. You should always use this * function to allocate the JPEG destination buffer(s) for the compression and * transform functions unless you are disabling automatic buffer (re)allocation - * (by setting #TJFLAG_NOREALLOC.) + * (by setting #TJPARAM_NOREALLOC.) * * @param bytes the number of bytes to allocate * * @return a pointer to a newly-allocated buffer with the specified number of * bytes. * - * @sa tjFree() + * @see tj3Free() */ -DLLEXPORT unsigned char *tjAlloc(int bytes); +DLLEXPORT void *tj3Alloc(size_t bytes); /** - * Load a packed-pixel image from disk into memory. + * Load an 8-bit-per-sample packed-pixel image from disk into memory. + * + * @param handle handle to a TurboJPEG instance * * @param filename name of a file containing a packed-pixel image in Windows - * BMP or PBMPLUS (PPM/PGM) format + * BMP or PBMPLUS (PPM/PGM) format. Windows BMP files require 8-bit-per-sample + * data precision. If the data precision of the PBMPLUS file does not match + * the target data precision, then upconverting or downconverting will be + * performed. * * @param width pointer to an integer variable that will receive the width (in * pixels) of the packed-pixel image * - * @param align row alignment of the packed-pixel buffer to be returned (must - * be a power of 2.) Setting this parameter to n will cause all rows in the - * buffer to be padded to the nearest multiple of n bytes (1 = unpadded.) + * @param align row alignment (in samples) of the packed-pixel buffer to be + * returned (must be a power of 2.) Setting this parameter to n will cause all + * rows in the buffer to be padded to the nearest multiple of n samples + * (1 = unpadded.) * * @param height pointer to an integer variable that will receive the height * (in pixels) of the packed-pixel image * * @param pixelFormat pointer to an integer variable that specifies or will - * receive the pixel format of the packed-pixel buffer. The behavior of - * #tjLoadImage() will vary depending on the value of `*pixelFormat` passed to - * the function: + * receive the pixel format of the packed-pixel buffer. The behavior of this + * function will vary depending on the value of `*pixelFormat` passed to the + * function: * - @ref TJPF_UNKNOWN : The packed-pixel buffer returned by this function will * use the most optimal pixel format for the file type, and `*pixelFormat` will * contain the ID of that pixel format upon successful return from this @@ -1659,32 +1944,50 @@ DLLEXPORT unsigned char *tjAlloc(int bytes); * specified pixel format, and pixel format conversion will be performed if * necessary. * - * @param flags the bitwise OR of one or more of the @ref TJFLAG_BOTTOMUP - * "flags". - * * @return a pointer to a newly-allocated buffer containing the packed-pixel * image, converted to the chosen pixel format and with the chosen row - * alignment, or NULL if an error occurred (see #tjGetErrorStr2().) This - * buffer should be freed using #tjFree(). + * alignment, or NULL if an error occurred (see #tj3GetErrorStr().) This + * buffer should be freed using #tj3Free(). */ -DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width, - int align, int *height, int *pixelFormat, - int flags); +DLLEXPORT unsigned char *tj3LoadImage8(tjhandle handle, const char *filename, + int *width, int align, int *height, + int *pixelFormat); + +/** + * Load a 12-bit-per-sample packed-pixel image from disk into memory. + * + * \details \copydetails tj3LoadImage8() + */ +DLLEXPORT short *tj3LoadImage12(tjhandle handle, const char *filename, + int *width, int align, int *height, + int *pixelFormat); + +/** + * Load a 16-bit-per-sample packed-pixel image from disk into memory. + * + * \details \copydetails tj3LoadImage8() + */ +DLLEXPORT unsigned short *tj3LoadImage16(tjhandle handle, const char *filename, + int *width, int align, int *height, + int *pixelFormat); /** - * Save a packed-pixel image from memory to disk. + * Save an 8-bit-per-sample packed-pixel image from memory to disk. + * + * @param handle handle to a TurboJPEG instance * * @param filename name of a file to which to save the packed-pixel image. The * image will be stored in Windows BMP or PBMPLUS (PPM/PGM) format, depending - * on the file extension. + * on the file extension. Windows BMP files require 8-bit-per-sample data + * precision. * * @param buffer pointer to a buffer containing a packed-pixel RGB, grayscale, * or CMYK image to be saved * * @param width width (in pixels) of the packed-pixel image * - * @param pitch bytes per row in the packed-pixel image. Setting this + * @param pitch samples per row in the packed-pixel image. Setting this * parameter to 0 is the equivalent of setting it to * width * #tjPixelSize[pixelFormat]. * @@ -1699,54 +2002,68 @@ DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width, * testing purposes. (Proper conversion between CMYK and other formats * requires a color management system.) * - * @param flags the bitwise OR of one or more of the @ref TJFLAG_BOTTOMUP - * "flags". + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr().) + */ +DLLEXPORT int tj3SaveImage8(tjhandle handle, const char *filename, + const unsigned char *buffer, int width, int pitch, + int height, int pixelFormat); + +/** + * Save a 12-bit-per-sample packed-pixel image from memory to disk. * - * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2().) + * \details \copydetails tj3SaveImage8() */ -DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer, - int width, int pitch, int height, int pixelFormat, - int flags); +DLLEXPORT int tj3SaveImage12(tjhandle handle, const char *filename, + const short *buffer, int width, int pitch, + int height, int pixelFormat); + +/** + * Save a 16-bit-per-sample packed-pixel image from memory to disk. + * + * \details \copydetails tj3SaveImage8() + */ +DLLEXPORT int tj3SaveImage16(tjhandle handle, const char *filename, + const unsigned short *buffer, int width, + int pitch, int height, int pixelFormat); /** * Free a byte buffer previously allocated by TurboJPEG. You should always use * this function to free JPEG destination buffer(s) that were automatically * (re)allocated by the compression and transform functions or that were - * manually allocated using #tjAlloc(). + * manually allocated using #tj3Alloc(). * * @param buffer address of the buffer to free. If the address is NULL, then * this function has no effect. * - * @sa tjAlloc() + * @see tj3Alloc() */ -DLLEXPORT void tjFree(unsigned char *buffer); +DLLEXPORT void tj3Free(void *buffer); /** * Returns a descriptive error message explaining why the last command failed. * - * @param handle a handle to a TurboJPEG compressor, decompressor, or - * transformer instance, or NULL if the error was generated by a global - * function (but note that retrieving the error message for a global function - * is thread-safe only on platforms that support thread-local storage.) + * @param handle handle to a TurboJPEG instance, or NULL if the error was + * generated by a global function (but note that retrieving the error message + * for a global function is thread-safe only on platforms that support + * thread-local storage.) * * @return a descriptive error message explaining why the last command failed. */ -DLLEXPORT char *tjGetErrorStr2(tjhandle handle); +DLLEXPORT char *tj3GetErrorStr(tjhandle handle); /** * Returns a code indicating the severity of the last error. See * @ref TJERR "Error codes". * - * @param handle a handle to a TurboJPEG compressor, decompressor or - * transformer instance + * @param handle handle to a TurboJPEG instance * * @return a code indicating the severity of the last error. See * @ref TJERR "Error codes". */ -DLLEXPORT int tjGetErrorCode(tjhandle handle); +DLLEXPORT int tj3GetErrorCode(tjhandle handle); /* Backward compatibility functions and macros (nothing to see here) */ @@ -1769,6 +2086,8 @@ DLLEXPORT int tjGetErrorCode(tjhandle handle); #define TJ_FORCESSE3 TJFLAG_FORCESSE3 #define TJ_FASTUPSAMPLE TJFLAG_FASTUPSAMPLE +#define TJPAD(width) (((width) + 3) & (~3)) + DLLEXPORT unsigned long TJBUFSIZE(int width, int height); DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width, @@ -1785,8 +2104,14 @@ DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height); +DLLEXPORT int tjDestroy(tjhandle handle); + DLLEXPORT char *tjGetErrorStr(void); +DLLEXPORT tjhandle tjInitCompress(void); + +DLLEXPORT tjhandle tjInitDecompress(void); + /* TurboJPEG 1.1+ */ #define TJ_YUV 512 @@ -1807,25 +2132,134 @@ DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width, /* TurboJPEG 1.2+ */ +#define TJFLAG_BOTTOMUP 2 #define TJFLAG_FORCEMMX 8 #define TJFLAG_FORCESSE 16 #define TJFLAG_FORCESSE2 32 #define TJFLAG_FORCESSE3 128 +#define TJFLAG_FASTUPSAMPLE 256 +#define TJFLAG_NOREALLOC 1024 + +DLLEXPORT unsigned char *tjAlloc(int bytes); + +DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp); DLLEXPORT unsigned long tjBufSizeYUV(int width, int height, int subsamp); +DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf, + int width, int pitch, int height, int pixelFormat, + unsigned char **jpegBuf, unsigned long *jpegSize, + int jpegSubsamp, int jpegQual, int flags); + +DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf, + unsigned long jpegSize, unsigned char *dstBuf, + int width, int pitch, int height, int pixelFormat, + int flags); + DLLEXPORT int tjEncodeYUV2(tjhandle handle, unsigned char *srcBuf, int width, int pitch, int height, int pixelFormat, unsigned char *dstBuf, int subsamp, int flags); +DLLEXPORT void tjFree(unsigned char *buffer); + +DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numscalingfactors); + +DLLEXPORT tjhandle tjInitTransform(void); + +DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf, + unsigned long jpegSize, int n, + unsigned char **dstBufs, unsigned long *dstSizes, + tjtransform *transforms, int flags); + +/* TurboJPEG 1.2.1+ */ + +#define TJFLAG_FASTDCT 2048 +#define TJFLAG_ACCURATEDCT 4096 + /* TurboJPEG 1.4+ */ +DLLEXPORT unsigned long tjBufSizeYUV2(int width, int align, int height, + int subsamp); + +DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf, + int width, int align, int height, int subsamp, + unsigned char **jpegBuf, + unsigned long *jpegSize, int jpegQual, + int flags); + +DLLEXPORT int tjCompressFromYUVPlanes(tjhandle handle, + const unsigned char **srcPlanes, + int width, const int *strides, + int height, int subsamp, + unsigned char **jpegBuf, + unsigned long *jpegSize, int jpegQual, + int flags); + +DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf, + int align, int subsamp, unsigned char *dstBuf, + int width, int pitch, int height, int pixelFormat, + int flags); + +DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle, + const unsigned char **srcPlanes, + const int *strides, int subsamp, + unsigned char *dstBuf, int width, int pitch, + int height, int pixelFormat, int flags); + DLLEXPORT int tjDecompressHeader3(tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height, int *jpegSubsamp, int *jpegColorspace); +DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf, + unsigned long jpegSize, unsigned char *dstBuf, + int width, int align, int height, int flags); + +DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle, + const unsigned char *jpegBuf, + unsigned long jpegSize, + unsigned char **dstPlanes, int width, + int *strides, int height, int flags); + +DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf, + int width, int pitch, int height, int pixelFormat, + unsigned char *dstBuf, int align, int subsamp, + int flags); + +DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf, + int width, int pitch, int height, + int pixelFormat, unsigned char **dstPlanes, + int *strides, int subsamp, int flags); + +DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp); + +DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride, + int height, int subsamp); + +DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp); + +/* TurboJPEG 2.0+ */ + +#define TJFLAG_STOPONWARNING 8192 +#define TJFLAG_PROGRESSIVE 16384 + +DLLEXPORT int tjGetErrorCode(tjhandle handle); + +DLLEXPORT char *tjGetErrorStr2(tjhandle handle); + +DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width, + int align, int *height, int *pixelFormat, + int flags); + +DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer, + int width, int pitch, int height, int pixelFormat, + int flags); + +/* TurboJPEG 2.1+ */ + +#define TJFLAG_LIMITSCANS 32768 + /** * @} */ From 96bc40c1b36775afdbad4ae05a6b3f48e2eebeb9 Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 26 Jan 2023 13:11:58 -0600 Subject: [PATCH 087/162] Implement arithmetic coding with 12-bit precision This actually works and apparently always has worked. It only failed because the libjpeg code, which did not originally support arithmetic coding, assumed that optimize_coding should always be TRUE for 12-bit data precision. --- cmakescripts/tjbenchtest.cmake | 10 +++++++++ doc/html/group___turbo_j_p_e_g.html | 10 ++++----- fuzz/compress12.cc | 1 + java/TJBench.java | 8 +++----- java/doc/member-search-index.zip | Bin 1927 -> 1927 bytes java/doc/org/libjpegturbo/turbojpeg/TJ.html | 11 +++++----- .../libjpegturbo/turbojpeg/TJTransform.html | 4 ++-- java/doc/package-search-index.zip | Bin 237 -> 237 bytes java/doc/type-search-index.zip | Bin 311 -> 311 bytes java/org/libjpegturbo/turbojpeg/TJ.java | 11 +++++----- .../libjpegturbo/turbojpeg/TJTransform.java | 4 ++-- jcarith.c | 3 --- jcmaster.c | 4 ++-- jcparam.c | 4 ++-- jdarith.c | 3 --- tjbench.c | 10 +++------ tjbenchtest.in | 19 +++++++++++++++--- turbojpeg.c | 4 +++- turbojpeg.h | 14 ++++++------- 19 files changed, 68 insertions(+), 52 deletions(-) diff --git a/cmakescripts/tjbenchtest.cmake b/cmakescripts/tjbenchtest.cmake index 1f28b8a66..facb20303 100644 --- a/cmakescripts/tjbenchtest.cmake +++ b/cmakescripts/tjbenchtest.cmake @@ -35,7 +35,12 @@ if(NOT PRECISION EQUAL 16) endif() if(PRECISION EQUAL 8) run_test(tjbenchtest "-precision;${PRECISION};-progressive;-yuv") +endif() +if(NOT PRECISION EQUAL 16) run_test(tjbenchtest "-precision;${PRECISION};-arithmetic") + run_test(tjbenchtest "-precision;${PRECISION};-progressive;-arithmetic") +endif() +if(PRECISION EQUAL 8) run_test(tjbenchtest "-precision;${PRECISION};-arithmetic;-yuv") endif() run_test(tjbenchtest "-precision;${PRECISION};-lossless") @@ -57,7 +62,12 @@ if(WITH_JAVA) endif() if(PRECISION EQUAL 8) run_test(tjbenchtest "-java;-precision;${PRECISION};-progressive;-yuv") + endif() + if(NOT PRECISION EQUAL 16) run_test(tjbenchtest "-java;-precision;${PRECISION};-arithmetic") + run_test(tjbenchtest "-java;-precision;${PRECISION};-progressive;-arithmetic") + endif() + if(PRECISION EQUAL 8) run_test(tjbenchtest "-java;-precision;${PRECISION};-arithmetic;-yuv") endif() run_test(tjbenchtest "-java;-precision;${PRECISION};-lossless") diff --git a/doc/html/group___turbo_j_p_e_g.html b/doc/html/group___turbo_j_p_e_g.html index 1e96c4744..1d0507f7e 100644 --- a/doc/html/group___turbo_j_p_e_g.html +++ b/doc/html/group___turbo_j_p_e_g.html @@ -572,7 +572,7 @@

        This option will enable arithmetic entropy coding in the JPEG image generated by this particular transform.

        -

        Arithmetic entropy coding will generally improve compression relative to Huffman entropy coding (the default), but it will reduce decompression performance considerably. Can be combined with TJXOPT_PROGRESSIVE. Arithmetic entropy coding is currently only implemented for 8-bit samples.

        +

        Arithmetic entropy coding will generally improve compression relative to Huffman entropy coding (the default), but it will reduce decompression performance considerably. Can be combined with TJXOPT_PROGRESSIVE.

        @@ -689,7 +689,7 @@

        This option will enable progressive entropy coding in the JPEG image generated by this particular transform.

        -

        Progressive entropy coding will generally improve compression relative to baseline entropy coding (the default), but it will reduce decompression performance considerably. Implies TJXOPT_OPTIMIZE. Can be combined with TJXOPT_ARITHMETIC.

        +

        Progressive entropy coding will generally improve compression relative to baseline entropy coding (the default), but it will reduce decompression performance considerably. Can be combined with TJXOPT_ARITHMETIC. Implies TJXOPT_OPTIMIZE unless TJXOPT_ARITHMETIC is also specified.

        @@ -874,7 +874,7 @@

        TJPARAM_OPTIMIZE.

        +

        12-bit data precision implies TJPARAM_OPTIMIZE unless TJPARAM_ARITHMETIC is set.

    • @@ -413,7 +414,7 @@

      YUV Image Format Notes

      Technically, the JPEG format uses the YCbCr colorspace (which is technically not a colorspace but a color transform), but per the convention of the digital video community, the TurboJPEG API uses "YUV" to refer to an image format consisting of Y, Cb, and Cr image planes.

      -

      Each plane is simply a 2D array of bytes, each byte representing the value of one of the components (Y, Cb, or Cr) at a particular location in the image. The width and height of each plane are determined by the image width, height, and level of chrominance subsampling. The luminance plane width is the image width padded to the nearest multiple of the horizontal subsampling factor (1 in the case of 4:4:4, grayscale, or 4:4:0; 2 in the case of 4:2:2 or 4:2:0; 4 in the case of 4:1:1.) Similarly, the luminance plane height is the image height padded to the nearest multiple of the vertical subsampling factor (1 in the case of 4:4:4, 4:2:2, grayscale, or 4:1:1; 2 in the case of 4:2:0 or 4:4:0.) This is irrespective of any additional padding that may be specified as an argument to the various YUV functions. The chrominance plane width is equal to the luminance plane width divided by the horizontal subsampling factor, and the chrominance plane height is equal to the luminance plane height divided by the vertical subsampling factor.

      +

      Each plane is simply a 2D array of bytes, each byte representing the value of one of the components (Y, Cb, or Cr) at a particular location in the image. The width and height of each plane are determined by the image width, height, and level of chrominance subsampling. The luminance plane width is the image width padded to the nearest multiple of the horizontal subsampling factor (1 in the case of 4:4:4, grayscale, 4:4:0, or 4:4:1; 2 in the case of 4:2:2 or 4:2:0; 4 in the case of 4:1:1.) Similarly, the luminance plane height is the image height padded to the nearest multiple of the vertical subsampling factor (1 in the case of 4:4:4, 4:2:2, grayscale, or 4:1:1; 2 in the case of 4:2:0 or 4:4:0; 4 in the case of 4:4:1.) This is irrespective of any additional padding that may be specified as an argument to the various YUV functions. The chrominance plane width is equal to the luminance plane width divided by the horizontal subsampling factor, and the chrominance plane height is equal to the luminance plane height divided by the vertical subsampling factor.

      For example, if the source image is 35 x 35 pixels and 4:2:2 subsampling is used, then the luminance plane would be 36 x 35 bytes, and each of the chrominance planes would be 18 x 35 bytes. If you specify a row alignment of 4 bytes on top of this, then the luminance plane would be 36 x 35 bytes, and each of the chrominance planes would be 20 x 35 bytes.

      Macro Definition Documentation

      @@ -1092,6 +1093,10 @@

      Note
      4:1:1 subsampling is not fully accelerated in libjpeg-turbo.
      +

      - + + + + + + - + - + diff --git a/java/doc/index-all.html b/java/doc/index-all.html index 22c930572..4b153050a 100644 --- a/java/doc/index-all.html +++ b/java/doc/index-all.html @@ -899,6 +899,10 @@

      S

      4:4:0 chrominance subsampling.
      +
      SAMP_441 - Static variable in class org.libjpegturbo.turbojpeg.TJ
      +
      +
      4:4:1 chrominance subsampling.
      +
      SAMP_444 - Static variable in class org.libjpegturbo.turbojpeg.TJ
      4:4:4 chrominance subsampling (no chrominance subsampling).
      diff --git a/java/doc/member-search-index.js b/java/doc/member-search-index.js index cb143698f..c1c104fd0 100644 --- a/java/doc/member-search-index.js +++ b/java/doc/member-search-index.js @@ -1 +1 @@ -memberSearchIndex = [{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"bufSize(int, int, int)","url":"bufSize(int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"bufSizeYUV(int, int, int, int)","url":"bufSizeYUV(int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"cf"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"close()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"close()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"compress()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"compress(byte[], int)","url":"compress(byte[],int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"compress(byte[])"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"compress(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"CS_CMYK"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"CS_GRAY"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"CS_RGB"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"CS_YCbCr"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"CS_YCCK"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCustomFilter","l":"customFilter(ShortBuffer, Rectangle, Rectangle, int, int, TJTransform)","url":"customFilter(java.nio.ShortBuffer,java.awt.Rectangle,java.awt.Rectangle,int,int,org.libjpegturbo.turbojpeg.TJTransform)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress(BufferedImage, int)","url":"decompress(java.awt.image.BufferedImage,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress(byte[], int, int, int, int, int, int, int)","url":"decompress(byte[],int,int,int,int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress(int, int, int, int, int)","url":"decompress(int,int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress(int, int, int, int)","url":"decompress(int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress(int[], int, int, int, int, int, int, int)","url":"decompress(int[],int,int,int,int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress12(int, int)","url":"decompress12(int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress12(short[], int, int, int, int)","url":"decompress12(short[],int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress16(int, int)","url":"decompress16(int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress16(short[], int, int, int, int)","url":"decompress16(short[],int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress8(BufferedImage)","url":"decompress8(java.awt.image.BufferedImage)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress8(byte[], int, int, int, int)","url":"decompress8(byte[],int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress8(int, int)","url":"decompress8(int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress8(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress8(int[], int, int, int, int)","url":"decompress8(int[],int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompressToYUV(int, int, int, int)","url":"decompressToYUV(int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompressToYUV(int, int[], int, int)","url":"decompressToYUV(int,int[],int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompressToYUV(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompressToYUV(int[])"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompressToYUV(YUVImage, int)","url":"decompressToYUV(org.libjpegturbo.turbojpeg.YUVImage,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompressToYUV(YUVImage)","url":"decompressToYUV(org.libjpegturbo.turbojpeg.YUVImage)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"encodeYUV(int, int)","url":"encodeYUV(int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"encodeYUV(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"encodeYUV(int[], int)","url":"encodeYUV(int[],int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"encodeYUV(int[])"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"encodeYUV(YUVImage, int)","url":"encodeYUV(org.libjpegturbo.turbojpeg.YUVImage,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"encodeYUV(YUVImage)","url":"encodeYUV(org.libjpegturbo.turbojpeg.YUVImage)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJScalingFactor","l":"equals(TJScalingFactor)","url":"equals(org.libjpegturbo.turbojpeg.TJScalingFactor)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"ERR_FATAL"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"ERR_WARNING"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"finalize()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"finalize()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"FLAG_ACCURATEDCT"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"FLAG_BOTTOMUP"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"FLAG_FASTDCT"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"FLAG_FASTUPSAMPLE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"FLAG_LIMITSCANS"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"FLAG_PROGRESSIVE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"FLAG_STOPONWARNING"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"get(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"get(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"getAlphaOffset(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"getBlueOffset(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"getBuf()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"getColorspace()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"getCompressedSize()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJScalingFactor","l":"getDenom()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJException","l":"getErrorCode()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"getGreenOffset(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"getHeight()"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"getHeight()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"getJPEGBuf()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"getJPEGSize()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"getMCUHeight(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"getMCUWidth(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJScalingFactor","l":"getNum()"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"getOffsets()"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"getPad()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"getPixelSize(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"getPlanes()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"getRedOffset(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJScalingFactor","l":"getScaled(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"getScaledHeight(int, int)","url":"getScaledHeight(int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"getScaledWidth(int, int)","url":"getScaledWidth(int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"getScalingFactors()"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"getSize()"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"getStrides()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"getSubsamp()"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"getSubsamp()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransformer","l":"getTransformedSizes()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"getWidth()"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"getWidth()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJScalingFactor","l":"isOne()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"NUMCS"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"NUMERR"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"NUMOP"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"NUMPF"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"NUMSAMP"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"op"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OP_HFLIP"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OP_NONE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OP_ROT180"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OP_ROT270"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OP_ROT90"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OP_TRANSPOSE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OP_TRANSVERSE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OP_VFLIP"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OPT_ARITHMETIC"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OPT_COPYNONE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OPT_CROP"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OPT_GRAY"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OPT_NOOUTPUT"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OPT_OPTIMIZE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OPT_PERFECT"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OPT_PROGRESSIVE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OPT_TRIM"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"options"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_ARITHMETIC"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_BOTTOMUP"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_COLORSPACE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_DENSITYUNITS"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_FASTDCT"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_FASTUPSAMPLE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_JPEGHEIGHT"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_JPEGWIDTH"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_LOSSLESS"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_LOSSLESSPSV"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_LOSSLESSPT"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_OPTIMIZE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_PRECISION"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_PROGRESSIVE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_QUALITY"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_RESTARTBLOCKS"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_RESTARTROWS"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_SCANLIMIT"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_STOPONWARNING"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_SUBSAMP"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_XDENSITY"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_YDENSITY"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_ABGR"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_ARGB"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_BGR"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_BGRA"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_BGRX"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_CMYK"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_GRAY"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_RGB"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_RGBA"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_RGBX"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_XBGR"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_XRGB"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"planeHeight(int, int, int)","url":"planeHeight(int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"planeSizeYUV(int, int, int, int, int)","url":"planeSizeYUV(int,int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"planeWidth(int, int, int)","url":"planeWidth(int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"SAMP_411"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"SAMP_420"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"SAMP_422"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"SAMP_440"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"SAMP_444"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"SAMP_GRAY"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"SAMP_UNKNOWN"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"set(int, int)","url":"set(int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"set(int, int)","url":"set(int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"setBuf(byte[], int, int, int, int)","url":"setBuf(byte[],int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"setBuf(byte[][], int[], int, int[], int, int)","url":"setBuf(byte[][],int[],int,int[],int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"setCroppingRegion(Rectangle)","url":"setCroppingRegion(java.awt.Rectangle)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"setJPEGQuality(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"setScalingFactor(TJScalingFactor)","url":"setScalingFactor(org.libjpegturbo.turbojpeg.TJScalingFactor)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"setSourceImage(BufferedImage, int, int, int, int)","url":"setSourceImage(java.awt.image.BufferedImage,int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"setSourceImage(byte[], int, int, int, int, int, int)","url":"setSourceImage(byte[],int,int,int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"setSourceImage(byte[], int)","url":"setSourceImage(byte[],int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"setSourceImage(YUVImage)","url":"setSourceImage(org.libjpegturbo.turbojpeg.YUVImage)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"setSourceImage(YUVImage)","url":"setSourceImage(org.libjpegturbo.turbojpeg.YUVImage)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"setSourceImage12(short[], int, int, int, int, int, int)","url":"setSourceImage12(short[],int,int,int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"setSourceImage16(short[], int, int, int, int, int, int)","url":"setSourceImage16(short[],int,int,int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"setSubsamp(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"TJCompressor()","url":"%3Cinit%3E()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"TJCompressor(BufferedImage, int, int, int, int)","url":"%3Cinit%3E(java.awt.image.BufferedImage,int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"TJCompressor(byte[], int, int, int, int, int, int)","url":"%3Cinit%3E(byte[],int,int,int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"TJDecompressor()","url":"%3Cinit%3E()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"TJDecompressor(byte[], int)","url":"%3Cinit%3E(byte[],int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"TJDecompressor(byte[])","url":"%3Cinit%3E(byte[])"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"TJDecompressor(YUVImage)","url":"%3Cinit%3E(org.libjpegturbo.turbojpeg.YUVImage)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJException","l":"TJException()","url":"%3Cinit%3E()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJException","l":"TJException(String, int)","url":"%3Cinit%3E(java.lang.String,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJException","l":"TJException(String, Throwable)","url":"%3Cinit%3E(java.lang.String,java.lang.Throwable)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJException","l":"TJException(String)","url":"%3Cinit%3E(java.lang.String)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJException","l":"TJException(Throwable)","url":"%3Cinit%3E(java.lang.Throwable)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJScalingFactor","l":"TJScalingFactor(int, int)","url":"%3Cinit%3E(int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"TJTransform()","url":"%3Cinit%3E()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"TJTransform(int, int, int, int, int, int, TJCustomFilter)","url":"%3Cinit%3E(int,int,int,int,int,int,org.libjpegturbo.turbojpeg.TJCustomFilter)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"TJTransform(Rectangle, int, int, TJCustomFilter)","url":"%3Cinit%3E(java.awt.Rectangle,int,int,org.libjpegturbo.turbojpeg.TJCustomFilter)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransformer","l":"TJTransformer()","url":"%3Cinit%3E()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransformer","l":"TJTransformer(byte[], int)","url":"%3Cinit%3E(byte[],int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransformer","l":"TJTransformer(byte[])","url":"%3Cinit%3E(byte[])"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransformer","l":"transform(byte[][], TJTransform[], int)","url":"transform(byte[][],org.libjpegturbo.turbojpeg.TJTransform[],int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransformer","l":"transform(byte[][], TJTransform[])","url":"transform(byte[][],org.libjpegturbo.turbojpeg.TJTransform[])"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransformer","l":"transform(TJTransform[], int)","url":"transform(org.libjpegturbo.turbojpeg.TJTransform[],int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransformer","l":"transform(TJTransform[])","url":"transform(org.libjpegturbo.turbojpeg.TJTransform[])"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"UNCROPPED"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"UNSCALED"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"YUVImage(byte[], int, int, int, int)","url":"%3Cinit%3E(byte[],int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"YUVImage(byte[][], int[], int, int[], int, int)","url":"%3Cinit%3E(byte[][],int[],int,int[],int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"YUVImage(int, int, int, int)","url":"%3Cinit%3E(int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"YUVImage(int, int[], int, int)","url":"%3Cinit%3E(int,int[],int,int)"}] \ No newline at end of file +memberSearchIndex = [{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"bufSize(int, int, int)","url":"bufSize(int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"bufSizeYUV(int, int, int, int)","url":"bufSizeYUV(int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"cf"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"close()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"close()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"compress()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"compress(byte[], int)","url":"compress(byte[],int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"compress(byte[])"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"compress(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"CS_CMYK"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"CS_GRAY"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"CS_RGB"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"CS_YCbCr"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"CS_YCCK"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCustomFilter","l":"customFilter(ShortBuffer, Rectangle, Rectangle, int, int, TJTransform)","url":"customFilter(java.nio.ShortBuffer,java.awt.Rectangle,java.awt.Rectangle,int,int,org.libjpegturbo.turbojpeg.TJTransform)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress(BufferedImage, int)","url":"decompress(java.awt.image.BufferedImage,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress(byte[], int, int, int, int, int, int, int)","url":"decompress(byte[],int,int,int,int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress(int, int, int, int, int)","url":"decompress(int,int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress(int, int, int, int)","url":"decompress(int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress(int[], int, int, int, int, int, int, int)","url":"decompress(int[],int,int,int,int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress12(int, int)","url":"decompress12(int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress12(short[], int, int, int, int)","url":"decompress12(short[],int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress16(int, int)","url":"decompress16(int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress16(short[], int, int, int, int)","url":"decompress16(short[],int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress8(BufferedImage)","url":"decompress8(java.awt.image.BufferedImage)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress8(byte[], int, int, int, int)","url":"decompress8(byte[],int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress8(int, int)","url":"decompress8(int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress8(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompress8(int[], int, int, int, int)","url":"decompress8(int[],int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompressToYUV(int, int, int, int)","url":"decompressToYUV(int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompressToYUV(int, int[], int, int)","url":"decompressToYUV(int,int[],int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompressToYUV(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompressToYUV(int[])"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompressToYUV(YUVImage, int)","url":"decompressToYUV(org.libjpegturbo.turbojpeg.YUVImage,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"decompressToYUV(YUVImage)","url":"decompressToYUV(org.libjpegturbo.turbojpeg.YUVImage)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"encodeYUV(int, int)","url":"encodeYUV(int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"encodeYUV(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"encodeYUV(int[], int)","url":"encodeYUV(int[],int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"encodeYUV(int[])"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"encodeYUV(YUVImage, int)","url":"encodeYUV(org.libjpegturbo.turbojpeg.YUVImage,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"encodeYUV(YUVImage)","url":"encodeYUV(org.libjpegturbo.turbojpeg.YUVImage)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJScalingFactor","l":"equals(TJScalingFactor)","url":"equals(org.libjpegturbo.turbojpeg.TJScalingFactor)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"ERR_FATAL"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"ERR_WARNING"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"finalize()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"finalize()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"FLAG_ACCURATEDCT"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"FLAG_BOTTOMUP"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"FLAG_FASTDCT"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"FLAG_FASTUPSAMPLE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"FLAG_LIMITSCANS"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"FLAG_PROGRESSIVE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"FLAG_STOPONWARNING"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"get(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"get(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"getAlphaOffset(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"getBlueOffset(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"getBuf()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"getColorspace()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"getCompressedSize()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJScalingFactor","l":"getDenom()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJException","l":"getErrorCode()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"getGreenOffset(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"getHeight()"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"getHeight()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"getJPEGBuf()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"getJPEGSize()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"getMCUHeight(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"getMCUWidth(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJScalingFactor","l":"getNum()"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"getOffsets()"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"getPad()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"getPixelSize(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"getPlanes()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"getRedOffset(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJScalingFactor","l":"getScaled(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"getScaledHeight(int, int)","url":"getScaledHeight(int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"getScaledWidth(int, int)","url":"getScaledWidth(int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"getScalingFactors()"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"getSize()"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"getStrides()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"getSubsamp()"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"getSubsamp()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransformer","l":"getTransformedSizes()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"getWidth()"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"getWidth()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJScalingFactor","l":"isOne()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"NUMCS"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"NUMERR"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"NUMOP"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"NUMPF"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"NUMSAMP"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"op"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OP_HFLIP"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OP_NONE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OP_ROT180"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OP_ROT270"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OP_ROT90"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OP_TRANSPOSE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OP_TRANSVERSE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OP_VFLIP"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OPT_ARITHMETIC"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OPT_COPYNONE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OPT_CROP"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OPT_GRAY"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OPT_NOOUTPUT"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OPT_OPTIMIZE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OPT_PERFECT"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OPT_PROGRESSIVE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"OPT_TRIM"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"options"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_ARITHMETIC"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_BOTTOMUP"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_COLORSPACE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_DENSITYUNITS"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_FASTDCT"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_FASTUPSAMPLE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_JPEGHEIGHT"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_JPEGWIDTH"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_LOSSLESS"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_LOSSLESSPSV"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_LOSSLESSPT"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_OPTIMIZE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_PRECISION"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_PROGRESSIVE"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_QUALITY"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_RESTARTBLOCKS"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_RESTARTROWS"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_SCANLIMIT"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_STOPONWARNING"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_SUBSAMP"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_XDENSITY"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PARAM_YDENSITY"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_ABGR"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_ARGB"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_BGR"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_BGRA"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_BGRX"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_CMYK"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_GRAY"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_RGB"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_RGBA"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_RGBX"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_XBGR"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"PF_XRGB"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"planeHeight(int, int, int)","url":"planeHeight(int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"planeSizeYUV(int, int, int, int, int)","url":"planeSizeYUV(int,int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"planeWidth(int, int, int)","url":"planeWidth(int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"SAMP_411"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"SAMP_420"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"SAMP_422"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"SAMP_440"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"SAMP_441"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"SAMP_444"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"SAMP_GRAY"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"SAMP_UNKNOWN"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"set(int, int)","url":"set(int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"set(int, int)","url":"set(int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"setBuf(byte[], int, int, int, int)","url":"setBuf(byte[],int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"setBuf(byte[][], int[], int, int[], int, int)","url":"setBuf(byte[][],int[],int,int[],int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"setCroppingRegion(Rectangle)","url":"setCroppingRegion(java.awt.Rectangle)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"setJPEGQuality(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"setScalingFactor(TJScalingFactor)","url":"setScalingFactor(org.libjpegturbo.turbojpeg.TJScalingFactor)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"setSourceImage(BufferedImage, int, int, int, int)","url":"setSourceImage(java.awt.image.BufferedImage,int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"setSourceImage(byte[], int, int, int, int, int, int)","url":"setSourceImage(byte[],int,int,int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"setSourceImage(byte[], int)","url":"setSourceImage(byte[],int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"setSourceImage(YUVImage)","url":"setSourceImage(org.libjpegturbo.turbojpeg.YUVImage)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"setSourceImage(YUVImage)","url":"setSourceImage(org.libjpegturbo.turbojpeg.YUVImage)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"setSourceImage12(short[], int, int, int, int, int, int)","url":"setSourceImage12(short[],int,int,int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"setSourceImage16(short[], int, int, int, int, int, int)","url":"setSourceImage16(short[],int,int,int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"setSubsamp(int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"TJCompressor()","url":"%3Cinit%3E()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"TJCompressor(BufferedImage, int, int, int, int)","url":"%3Cinit%3E(java.awt.image.BufferedImage,int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJCompressor","l":"TJCompressor(byte[], int, int, int, int, int, int)","url":"%3Cinit%3E(byte[],int,int,int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"TJDecompressor()","url":"%3Cinit%3E()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"TJDecompressor(byte[], int)","url":"%3Cinit%3E(byte[],int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"TJDecompressor(byte[])","url":"%3Cinit%3E(byte[])"},{"p":"org.libjpegturbo.turbojpeg","c":"TJDecompressor","l":"TJDecompressor(YUVImage)","url":"%3Cinit%3E(org.libjpegturbo.turbojpeg.YUVImage)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJException","l":"TJException()","url":"%3Cinit%3E()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJException","l":"TJException(String, int)","url":"%3Cinit%3E(java.lang.String,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJException","l":"TJException(String, Throwable)","url":"%3Cinit%3E(java.lang.String,java.lang.Throwable)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJException","l":"TJException(String)","url":"%3Cinit%3E(java.lang.String)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJException","l":"TJException(Throwable)","url":"%3Cinit%3E(java.lang.Throwable)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJScalingFactor","l":"TJScalingFactor(int, int)","url":"%3Cinit%3E(int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"TJTransform()","url":"%3Cinit%3E()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"TJTransform(int, int, int, int, int, int, TJCustomFilter)","url":"%3Cinit%3E(int,int,int,int,int,int,org.libjpegturbo.turbojpeg.TJCustomFilter)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransform","l":"TJTransform(Rectangle, int, int, TJCustomFilter)","url":"%3Cinit%3E(java.awt.Rectangle,int,int,org.libjpegturbo.turbojpeg.TJCustomFilter)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransformer","l":"TJTransformer()","url":"%3Cinit%3E()"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransformer","l":"TJTransformer(byte[], int)","url":"%3Cinit%3E(byte[],int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransformer","l":"TJTransformer(byte[])","url":"%3Cinit%3E(byte[])"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransformer","l":"transform(byte[][], TJTransform[], int)","url":"transform(byte[][],org.libjpegturbo.turbojpeg.TJTransform[],int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransformer","l":"transform(byte[][], TJTransform[])","url":"transform(byte[][],org.libjpegturbo.turbojpeg.TJTransform[])"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransformer","l":"transform(TJTransform[], int)","url":"transform(org.libjpegturbo.turbojpeg.TJTransform[],int)"},{"p":"org.libjpegturbo.turbojpeg","c":"TJTransformer","l":"transform(TJTransform[])","url":"transform(org.libjpegturbo.turbojpeg.TJTransform[])"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"UNCROPPED"},{"p":"org.libjpegturbo.turbojpeg","c":"TJ","l":"UNSCALED"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"YUVImage(byte[], int, int, int, int)","url":"%3Cinit%3E(byte[],int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"YUVImage(byte[][], int[], int, int[], int, int)","url":"%3Cinit%3E(byte[][],int[],int,int[],int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"YUVImage(int, int, int, int)","url":"%3Cinit%3E(int,int,int,int)"},{"p":"org.libjpegturbo.turbojpeg","c":"YUVImage","l":"YUVImage(int, int[], int, int)","url":"%3Cinit%3E(int,int[],int,int)"}] \ No newline at end of file diff --git a/java/doc/member-search-index.zip b/java/doc/member-search-index.zip index bb19d4c0507dbe42400a16e94f5203652157d26a..fcc0a1f3eedb4a6e7404660cfd937e76b3c4750e 100644 GIT binary patch delta 558 zcmV+}0@3}44~-8EP)h>@6aWYa2mnE*X|WA81%Dg(SZ{8iqQKB_kae_=n*PYNji}rc z9h1K$H#)Tn$ft@_P)eZ1*|l4ja=Kzy_)=_QaO6Nzp%+}0r3%GX;>xbD8aWDgJ1^Lp zXp2vDgL5f*flH}VwQ!y9CNj=0p+4w!(e1LI-c5 z6o241c`7N{R+A5YrLdWN&aQo)!^$u)nYuUxIJzk1&W06gRd_ZsB$zI?kZ{8`KB`5`ah6qRyc)jRX zAj=l}2)_Nsy<($)DQnk3jjhxH_oBE#r+;95)v!ru6df|lHBm&1fLU!qPjC2xZOekg zXUOgF*pqc;yC}Dxqwt%%^y$W~P{+oTtrPT+NS}Er-f&e5~m2Z%U z76A_3gx(aQ)1UK_zYcC1ZspT>QYfMd8cNL%l3Par{iQMgAW&2t1`_dNYSYluYk#Sm zT2U{hsn^_TwuP)i30m1A8t w>jnS--bVlcP)h*<6aW+e2nYxOK@6s8R+VF2HtPle0N#^C2R#Nc2LJ#70Mt1KFaQ7m delta 552 zcmV+@0@wYG4~Gv8P)h>@6aWYa2mnlHIrKA7mZvqozMH?FA~AJ;w}g z$rnzo0+OO46_m1Iadvmsr6#V}6~3OD7#ul}ROnSxWvN2Nl(^a{tVWK)-OdZPCfed- z+2CAHUf@#dRINVeyH1R=o1{;B*Y}biA`<*@DfwMrxl!raQw;}g&{nvQR_Nd@lq&ir zPk$vP+Y0N!?}awXUCQihSvObNilB z>)wakHr4G0k#SJ#K%MLVu&^kXbHlB3cB@Y7=^TO&@Gq792hZZimO7 ztTWq1x&0i4-`u57H+F?W-arTnrPPUsFr8h84;wA*lE2hSIgb$pAi=794MemEaOfuV zrVyR}CYSt)Z_{uqpT?6y5mnGoYJOGRIs)j=h4}}8qUtb^h!<0vhMrza-PDSDDSuV9 zC1+pAGYUk%%7`ib8)uwE;wSw^I_Yb)yYWField Summary
      + + + + + - + - + - + - + - - - + + +
      Exception Summary 
      Exception
      TJExceptionTJException  
      TJPARAM_COLORSPACE 

      JPEG colorspace.

      The JPEG image uses (decompression) or will use (lossy compression) the specified colorspace.

      @@ -912,7 +912,7 @@

      TJXOPT_PROGRESSIVE. -

      Progressive entropy coding will generally improve compression relative to baseline entropy coding, but it will reduce compression and decompression performance considerably. Implies TJPARAM_OPTIMIZE. Can be combined with TJPARAM_ARITHMETIC.

      +

      Progressive entropy coding will generally improve compression relative to baseline entropy coding, but it will reduce compression and decompression performance considerably. Can be combined with TJPARAM_ARITHMETIC. Implies TJPARAM_OPTIMIZE unless TJPARAM_ARITHMETIC is also set.

      TJPARAM_SCANLIMIT 

      Progressive JPEG scan limit for lossy JPEG images [decompression, lossless transformation].

      Setting this parameter will cause the decompression and transform functions to return an error if the number of scans in a progressive JPEG image exceeds the specified limit. The primary purpose of this is to allow security-critical applications to guard against an exploit of the progressive JPEG format described in this report.

      @@ -926,7 +926,7 @@

      TJXOPT_ARITHMETIC. -

      Arithmetic entropy coding will generally improve compression relative to Huffman entropy coding, but it will reduce compression and decompression performance considerably. Can be combined with TJPARAM_PROGRESSIVE. Arithmetic entropy coding is currently only implemented for 8-bit samples.

      +

      Arithmetic entropy coding will generally improve compression relative to Huffman entropy coding, but it will reduce compression and decompression performance considerably. Can be combined with TJPARAM_PROGRESSIVE.

      TJPARAM_LOSSLESS 

      Lossless JPEG.

      Value

        diff --git a/fuzz/compress12.cc b/fuzz/compress12.cc index ef0813172..8a082999f 100644 --- a/fuzz/compress12.cc +++ b/fuzz/compress12.cc @@ -83,6 +83,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) tj3Set(handle, TJPARAM_BOTTOMUP, ti == 0); tj3Set(handle, TJPARAM_FASTDCT, ti == 0); tj3Set(handle, TJPARAM_PROGRESSIVE, ti == 1 || ti == 3); + tj3Set(handle, TJPARAM_ARITHMETIC, ti == 2 || ti == 3); tj3Set(handle, TJPARAM_NOREALLOC, ti != 2); tj3Set(handle, TJPARAM_RESTARTROWS, ti == 1 || ti == 2 ? 2 : 0); diff --git a/java/TJBench.java b/java/TJBench.java index 39260feb9..0df2c587e 100644 --- a/java/TJBench.java +++ b/java/TJBench.java @@ -775,6 +775,7 @@ static void usage() throws Exception { System.out.println(" (use the CMYK pixel format for packed-pixel source/destination buffers)"); System.out.println("-precision N = Use N-bit data precision when compressing [N is 8, 12, or 16;"); System.out.println(" default = 8; if N is 16, then -lossless must also be specified]"); + System.out.println(" (-precision 12 implies -optimize unless -arithmetic is also specified)"); System.out.println("-quiet = Output results in tabular rather than verbose format"); System.out.println("-restart N = When compressing, add a restart marker every N MCU rows (lossy) or"); System.out.println(" N sample rows (lossless) [default = 0 (no restart markers)]. Append 'B'"); @@ -793,7 +794,6 @@ static void usage() throws Exception { System.out.println("------------------"); System.out.println("-arithmetic = Use arithmetic entropy coding in JPEG images generated by"); System.out.println(" compression and transform operations (can be combined with -progressive)"); - System.out.println(" ** 8-bit data precision only **"); System.out.println("-crop WxH+X+Y = Decompress only the specified region of the JPEG image, where W"); System.out.println(" and H are the width and height of the region (0 = maximum possible width"); System.out.println(" or height) and X and Y are the left and upper boundary of the region, all"); @@ -804,8 +804,8 @@ static void usage() throws Exception { System.out.println("-optimize = Use optimized baseline entropy coding in JPEG images generated by"); System.out.println(" compession and transform operations"); System.out.println("-progressive = Use progressive entropy coding in JPEG images generated by"); - System.out.println(" compression and transform operations (implies -optimize; can be combined"); - System.out.println(" with -arithmetic)"); + System.out.println(" compression and transform operations (can be combined with -arithmetic;"); + System.out.println(" implies -optimize unless -arithmetic is also specified)"); System.out.println("-limitscans = Refuse to decompress or transform progressive JPEG images that"); System.out.println(" have an unreasonably large number of scans"); System.out.println("-scale M/N = When decompressing, scale the width/height of the JPEG image by a"); @@ -1087,8 +1087,6 @@ else if (argv[i].equalsIgnoreCase("-restart") && if (precision == 16 && !lossless) throw new Exception("-lossless must be specified along with -precision 16"); - if (precision != 8 && arithmetic) - throw new Exception("-arithmetic requires 8-bit data precision"); if (precision != 8 && doYUV) throw new Exception("-yuv requires 8-bit data precision"); if (lossless && doYUV) diff --git a/java/doc/member-search-index.zip b/java/doc/member-search-index.zip index 2dc00c8da9ce187a72ac28fcce207cfbe12389cf..bb19d4c0507dbe42400a16e94f5203652157d26a 100644 GIT binary patch delta 30 kcmZqYZ|CO?@MdNaVc_84VDL$|+Q_TL&J3g{Te15B09NV+^#A|> delta 30 kcmZqYZ|CO?@MdNaVc_84U}&Fhv5{Acof$|^wqo}M0AJ_^&j0`b diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJ.html b/java/doc/org/libjpegturbo/turbojpeg/TJ.html index 146508d0d..2b51f985e 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJ.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJ.html @@ -1367,7 +1367,8 @@

        PARAM_PRECISION

      • 8, 12, or 16
      -

      12-bit data precision implies PARAM_OPTIMIZE. +

      12-bit data precision implies PARAM_OPTIMIZE unless + PARAM_ARITHMETIC is set.

      See Also:
      Constant Field Values
      @@ -1511,8 +1512,9 @@

      PARAM_PROGRESSIVE

      Progressive entropy coding will generally improve compression relative to baseline entropy coding, but it will reduce compression and - decompression performance considerably. Implies PARAM_OPTIMIZE. - Can be combined with PARAM_ARITHMETIC. + decompression performance considerably. Can be combined with + PARAM_ARITHMETIC. Implies PARAM_OPTIMIZE unless + PARAM_ARITHMETIC is also set.

      See Also:
      Constant Field Values
      @@ -1572,8 +1574,7 @@

      PARAM_ARITHMETIC

      Arithmetic entropy coding will generally improve compression relative to Huffman entropy coding, but it will reduce compression and decompression performance considerably. Can be combined with - PARAM_PROGRESSIVE. Arithmetic entropy coding is currently only - implemented for 8-bit samples. + PARAM_PROGRESSIVE.

      See Also:
      Constant Field Values
      diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJTransform.html b/java/doc/org/libjpegturbo/turbojpeg/TJTransform.html index fbe0dd8b3..0c2f5cc6d 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJTransform.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJTransform.html @@ -700,8 +700,8 @@

      OPT_PROGRESSIVE

      generated by this particular transform. Progressive entropy coding will generally improve compression relative to baseline entropy coding (the default), but it will reduce decompression performance considerably. - Implies OPT_OPTIMIZE. Can be combined with - OPT_ARITHMETIC. + Can be combined with OPT_ARITHMETIC. Implies + OPT_OPTIMIZE unless OPT_ARITHMETIC is also specified.
      See Also:
      Constant Field Values
      diff --git a/java/doc/package-search-index.zip b/java/doc/package-search-index.zip index 453ee4a263aca02b3e9117b9ed141044b624b466..e6c959be2db77fde6b061acc44969fec7d16dc2a 100644 GIT binary patch delta 28 hcmaFM_?D43z?+#xgn@&DgTW`=Y9enxGl-h+2LNJ!2S5M- delta 28 hcmaFM_?D43z?+#xgn@&DgQ0!4#YEnIW)L;s4*+U52pRwY diff --git a/java/doc/type-search-index.zip b/java/doc/type-search-index.zip index 81bcf27aa0044f80079cf65d7fd56b05578ee707..93006846a794bac2ccb9450ec5748ca50e64b3c8 100644 GIT binary patch delta 28 hcmdnaw4I4Jz?+#xgn@&DgTW`=Y9j9)W)Stp8vt5P2W 8, 12, or 16 * * - *

      12-bit data precision implies {@link #PARAM_OPTIMIZE}. + *

      12-bit data precision implies {@link #PARAM_OPTIMIZE} unless + * {@link #PARAM_ARITHMETIC} is set. */ public static final int PARAM_PRECISION = 7; /** @@ -559,8 +560,9 @@ public static int getAlphaOffset(int pixelFormat) { * *

      Progressive entropy coding will generally improve compression relative * to baseline entropy coding, but it will reduce compression and - * decompression performance considerably. Implies {@link #PARAM_OPTIMIZE}. - * Can be combined with {@link #PARAM_ARITHMETIC}. + * decompression performance considerably. Can be combined with + * {@link #PARAM_ARITHMETIC}. Implies {@link #PARAM_OPTIMIZE} unless + * {@link #PARAM_ARITHMETIC} is also set. */ public static final int PARAM_PROGRESSIVE = 12; /** @@ -601,8 +603,7 @@ public static int getAlphaOffset(int pixelFormat) { *

      Arithmetic entropy coding will generally improve compression relative * to Huffman entropy coding, but it will reduce compression and * decompression performance considerably. Can be combined with - * {@link #PARAM_PROGRESSIVE}. Arithmetic entropy coding is currently only - * implemented for 8-bit samples. + * {@link #PARAM_PROGRESSIVE}. */ public static final int PARAM_ARITHMETIC = 14; /** diff --git a/java/org/libjpegturbo/turbojpeg/TJTransform.java b/java/org/libjpegturbo/turbojpeg/TJTransform.java index dae0c164d..7c32cce7e 100644 --- a/java/org/libjpegturbo/turbojpeg/TJTransform.java +++ b/java/org/libjpegturbo/turbojpeg/TJTransform.java @@ -133,8 +133,8 @@ public class TJTransform extends Rectangle { * generated by this particular transform. Progressive entropy coding will * generally improve compression relative to baseline entropy coding (the * default), but it will reduce decompression performance considerably. - * Implies {@link #OPT_OPTIMIZE}. Can be combined with - * {@link #OPT_ARITHMETIC}. + * Can be combined with {@link #OPT_ARITHMETIC}. Implies + * {@link #OPT_OPTIMIZE} unless {@link #OPT_ARITHMETIC} is also specified. */ public static final int OPT_PROGRESSIVE = (1 << 5); /** diff --git a/jcarith.c b/jcarith.c index 054c7c12e..b1720521b 100644 --- a/jcarith.c +++ b/jcarith.c @@ -914,9 +914,6 @@ jinit_arith_encoder(j_compress_ptr cinfo) arith_entropy_ptr entropy; int i; - if (cinfo->data_precision != 8) - ERREXIT(cinfo, JERR_NOTIMPL); - entropy = (arith_entropy_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, sizeof(arith_entropy_encoder)); diff --git a/jcmaster.c b/jcmaster.c index 68492e93f..7e1408fcc 100644 --- a/jcmaster.c +++ b/jcmaster.c @@ -7,7 +7,7 @@ * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: - * Copyright (C) 2010, 2016, 2018, 2022, D. R. Commander. + * Copyright (C) 2010, 2016, 2018, 2022-2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -646,7 +646,7 @@ jinit_c_master_control(j_compress_ptr cinfo, boolean transcode_only) (cinfo->progressive_mode && !cinfo->arith_code)) cinfo->optimize_coding = TRUE; /* assume default tables no good for progressive mode or lossless mode */ - if (cinfo->data_precision == 12) + if (cinfo->data_precision == 12 && !cinfo->arith_code) cinfo->optimize_coding = TRUE; /* assume default tables no good for 12-bit data precision */ diff --git a/jcparam.c b/jcparam.c index 30bb23b74..d1dee4da3 100644 --- a/jcparam.c +++ b/jcparam.c @@ -7,7 +7,7 @@ * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: - * Copyright (C) 2009-2011, 2018, D. R. Commander. + * Copyright (C) 2009-2011, 2018, 2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -233,7 +233,7 @@ jpeg_set_defaults(j_compress_ptr cinfo) * 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) + if (cinfo->data_precision == 12 && !cinfo->arith_code) cinfo->optimize_coding = TRUE; /* By default, use the simpler non-cosited sampling alignment */ diff --git a/jdarith.c b/jdarith.c index 626f8cb6e..21575e80c 100644 --- a/jdarith.c +++ b/jdarith.c @@ -752,9 +752,6 @@ jinit_arith_decoder(j_decompress_ptr cinfo) arith_entropy_ptr entropy; int i; - if (cinfo->data_precision != 8) - ERREXIT(cinfo, JERR_NOTIMPL); - entropy = (arith_entropy_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, sizeof(arith_entropy_decoder)); diff --git a/tjbench.c b/tjbench.c index 8d09340a1..84fea32bd 100644 --- a/tjbench.c +++ b/tjbench.c @@ -901,6 +901,7 @@ static void usage(char *progName) printf(" (use the CMYK pixel format for packed-pixel source/destination buffers)\n"); printf("-precision N = Use N-bit data precision when compressing [N is 8, 12, or 16;\n"); printf(" default = 8; if N is 16, then -lossless must also be specified]\n"); + printf(" (-precision 12 implies -optimize unless -arithmetic is also specified)\n"); printf("-quiet = Output results in tabular rather than verbose format\n"); printf("-restart N = When compressing, add a restart marker every N MCU rows (lossy) or\n"); printf(" N sample rows (lossless) [default = 0 (no restart markers)]. Append 'B'\n"); @@ -919,7 +920,6 @@ static void usage(char *progName) printf("------------------\n"); printf("-arithmetic = Use arithmetic entropy coding in JPEG images generated by\n"); printf(" compression and transform operations (can be combined with -progressive)\n"); - printf(" ** 8-bit data precision only **\n"); printf("-crop WxH+X+Y = Decompress only the specified region of the JPEG image, where W\n"); printf(" and H are the width and height of the region (0 = maximum possible width\n"); printf(" or height) and X and Y are the left and upper boundary of the region, all\n"); @@ -930,8 +930,8 @@ static void usage(char *progName) printf("-optimize = Use optimized baseline entropy coding in JPEG images generated by\n"); printf(" compession and transform operations\n"); printf("-progressive = Use progressive entropy coding in JPEG images generated by\n"); - printf(" compression and transform operations (implies -optimize; can be combined\n"); - printf(" with -arithmetic)\n"); + printf(" compression and transform operations (can be combined with -arithmetic;\n"); + printf(" implies -optimize unless -arithmetic is also specified)\n"); printf("-limitscans = Refuse to decompress or transform progressive JPEG images that\n"); printf(" have an unreasonably large number of scans\n"); printf("-scale M/N = When decompressing, scale the width/height of the JPEG image by a\n"); @@ -1159,10 +1159,6 @@ int main(int argc, char *argv[]) printf("ERROR: -lossless must be specified along with -precision 16\n"); retval = -1; goto bailout; } - if (precision != 8 && arithmetic) { - printf("ERROR: -arithmetic requires 8-bit data precision\n"); - retval = -1; goto bailout; - } if (precision != 8 && doYUV) { printf("ERROR: -yuv requires 8-bit data precision\n"); retval = -1; goto bailout; diff --git a/tjbenchtest.in b/tjbenchtest.in index 7862906c2..c0f5b891e 100755 --- a/tjbenchtest.in +++ b/tjbenchtest.in @@ -84,10 +84,18 @@ while [ $# -gt 0 ]; do ENTROPYARG=-optimize ;; -progressive) - ENTROPYARG=-progressive + if [ "$ENTROPYARG" = "-arithmetic" ]; then + ENTROPYARG=-progressive-arithmetic + else + ENTROPYARG=-progressive + fi ;; -arithmetic) - ENTROPYARG=-arithmetic + if [ "$ENTROPYARG" = "-progressive" ]; then + ENTROPYARG=-progressive-arithmetic + else + ENTROPYARG=-arithmetic + fi ;; -lossless) LOSSLSARG="-lossless" @@ -115,13 +123,18 @@ if [ $PRECISION = 8 -a "$YUVARG" = "" ]; then IMAGES="vgl_6434_0018a.${EXT}" elif [ "$ENTROPYARG" = "-progressive" ]; then IMAGES="vgl_6548_0026a.${EXT}" - elif [ "$ENTROPYARG" = "-arithmetic" ]; then + elif [ "$ENTROPYARG" = "-arithmetic" -o \ + "$ENTROPYARG" = "-progressive-arithmetic" ]; then IMAGES="big_tree8.${EXT}" fi fi exec >$EXEDIR/tjbenchtest$JAVAARG$YUVARG$ALLOCARG$ENTROPYARG$LOSSLSARG-$PRECISION.log +if [ "$ENTROPYARG" = "-progressive-arithmetic" ]; then + ENTROPYARG="-progressive -arithmetic" +fi + # Standard tests for image in $IMAGES; do diff --git a/turbojpeg.c b/turbojpeg.c index 3c2358616..f77c8688f 100644 --- a/turbojpeg.c +++ b/turbojpeg.c @@ -2728,8 +2728,10 @@ DLLEXPORT int tj3Transform(tjhandle handle, const unsigned char *jpegBuf, if (this->progressive || t[i].options & TJXOPT_PROGRESSIVE) jpeg_simple_progression(cinfo); #endif - if (this->arithmetic || t[i].options & TJXOPT_ARITHMETIC) + if (this->arithmetic || t[i].options & TJXOPT_ARITHMETIC) { cinfo->arith_code = TRUE; + cinfo->optimize_coding = FALSE; + } if (!(t[i].options & TJXOPT_NOOUTPUT)) { jpeg_write_coefficients(cinfo, dstcoefs); jcopy_markers_execute(dinfo, cinfo, t[i].options & TJXOPT_COPYNONE ? diff --git a/turbojpeg.h b/turbojpeg.h index 475282a54..ed2f488dd 100644 --- a/turbojpeg.h +++ b/turbojpeg.h @@ -490,7 +490,8 @@ enum TJPARAM { * **Value** * - `8`, `12`, or `16` * - * 12-bit data precision implies #TJPARAM_OPTIMIZE. + * 12-bit data precision implies #TJPARAM_OPTIMIZE unless #TJPARAM_ARITHMETIC + * is set. */ TJPARAM_PRECISION, /** @@ -566,8 +567,8 @@ enum TJPARAM { * * Progressive entropy coding will generally improve compression relative to * baseline entropy coding, but it will reduce compression and decompression - * performance considerably. Implies #TJPARAM_OPTIMIZE. Can be combined - * with #TJPARAM_ARITHMETIC. + * performance considerably. Can be combined with #TJPARAM_ARITHMETIC. + * Implies #TJPARAM_OPTIMIZE unless #TJPARAM_ARITHMETIC is also set. */ TJPARAM_PROGRESSIVE, /** @@ -602,7 +603,6 @@ enum TJPARAM { * Arithmetic entropy coding will generally improve compression relative to * Huffman entropy coding, but it will reduce compression and decompression * performance considerably. Can be combined with #TJPARAM_PROGRESSIVE. - * Arithmetic entropy coding is currently only implemented for 8-bit samples. */ TJPARAM_ARITHMETIC, /** @@ -863,7 +863,8 @@ enum TJXOP { * generated by this particular transform. Progressive entropy coding will * generally improve compression relative to baseline entropy coding (the * default), but it will reduce decompression performance considerably. - * Implies #TJXOPT_OPTIMIZE. Can be combined with #TJXOPT_ARITHMETIC. + * Can be combined with #TJXOPT_ARITHMETIC. Implies #TJXOPT_OPTIMIZE unless + * #TJXOPT_ARITHMETIC is also specified. */ #define TJXOPT_PROGRESSIVE (1 << 5) /** @@ -877,8 +878,7 @@ enum TJXOP { * generated by this particular transform. Arithmetic entropy coding will * generally improve compression relative to Huffman entropy coding (the * default), but it will reduce decompression performance considerably. Can be - * combined with #TJXOPT_PROGRESSIVE. Arithmetic entropy coding is currently - * only implemented for 8-bit samples. + * combined with #TJXOPT_PROGRESSIVE. */ #define TJXOPT_ARITHMETIC (1 << 7) /** From db9f297f1c577ba314cdb819d4c20e6b25acb28e Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 27 Jan 2023 07:10:49 -0600 Subject: [PATCH 088/162] ChangeLog.md: Document TurboJPEG 3 API overhaul --- ChangeLog.md | 140 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 108 insertions(+), 32 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 598960d92..24f6c38d5 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -11,19 +11,102 @@ tables. 2. All deprecated fields, constructors, and methods in the TurboJPEG Java API have been removed. -3. Added support for 8-bit, 12-bit, and 16-bit lossless JPEG images. A new -libjpeg API function (`jpeg_enable_lossless()`), TurboJPEG API flag -(`TJFLAG_LOSSLESS` in the C API and `TJ.FLAG_LOSSLESS` in the Java API), and -cjpeg/TJBench command-line argument (`-lossless`) can be used to create a -lossless JPEG image. (Decompression of lossless JPEG images is handled -automatically.) Note that the TurboJPEG API and TJBench can currently only be -used to create and decompress 8-bit lossless JPEG images. Refer to +3. Arithmetic entropy coding is now supported with 12-bit-per-component JPEG +images. + +4. Overhauled the TurboJPEG API to address long-standing limitations and to +make the API more extensible and intuitive: + + - All C function names are now prefixed with `tj3`, and all version +suffixes have been removed from the function names. Future API overhauls will +increment the prefix to `tj4`, etc., thus retaining backward API/ABI +compatibility without versioning each individual function. + - Stateless boolean flags have been replaced with stateful integer API +parameters, the values of which persist between function calls. New +functions/methods (`tj3Set()`/`TJCompressor.set()`/`TJDecompressor.set()` and +`tj3Get()`/`TJCompressor.get()`/`TJDecompressor.get()`) can be used to set and +query the value of a particular API parameter. + - The JPEG quality and subsampling are now implemented using API +parameters rather than stateless function arguments (C) or dedicated set/get +methods (Java.) + - `tj3DecompressHeader()` now stores all relevant information about the +JPEG image, including the width, height, subsampling type, entropy coding +algorithm, etc., in API parameters rather than returning that information +through pointer arguments. + - `TJFLAG_LIMITSCANS`/`TJ.FLAG_LIMITSCANS` has been reimplemented as an +API parameter (`TJPARAM_SCANLIMIT`/`TJ.PARAM_SCANLIMIT`) that allows the number +of scans to be specified. + - Optimized baseline entropy coding (the computation of optimal Huffman +tables, as opposed to using the default Huffman tables) can now be specified, +using a new API parameter (`TJPARAM_OPTIMIZE`/`TJ.PARAM_OPTIMIZE`), a new +transform option (`TJXOPT_OPTIMIZE`/`TJTransform.OPT_OPTIMIZE`), and a new +TJBench option (`-optimize`.) + - Arithmetic entropy coding can now be specified or queried, using a new +API parameter (`TJPARAM_ARITHMETIC`/`TJ.PARAM_ARITHMETIC`), a new transform +option (`TJXOPT_ARITHMETIC`/`TJTransform.OPT_ARITHMETIC`), and a new TJBench +option (`-arithmetic`.) + - The restart marker interval can now be specified, using new API +parameters (`TJPARAM_RESTARTROWS`/`TJ.PARAM_RESTARTROWS` and +`TJPARAM_RESTARTBLOCKS`/`TJ.PARAM_RESTARTBLOCKS`) and a new TJBench option +(`-restart`.) + - Pixel density can now be specified or queried, using new API parameters +(`TJPARAM_XDENSITY`/`TJ.PARAM_XDENSITY`, +`TJPARAM_YDENSITY`/`TJ.PARAM_YDENSITY`, and +`TJPARAM_DENSITYUNITS`/`TJ.PARAM_DENSITYUNITS`.) + - The accurate DCT/IDCT algorithms are now the default for both +compression and decompression, since the "fast" algorithms are considered to be +a legacy feature. (The "fast" algorithms do not pass the ISO compliance tests, +and those algorithms are not any faster than the accurate algorithms on modern +x86 CPUs.) + - All C initialization functions have been combined into a single function +(`tj3Init()`) that accepts an integer argument specifying the subsystems to +initialize. + - All C functions now use the `const` keyword for pointer arguments that +point to unmodified buffers (and for both dimensions of pointer arguments that +point to sets of unmodified buffers.) + - All C functions now use `size_t` rather than `unsigned long` to +represent buffer sizes, for compatibility with `malloc()` and to avoid +disparities in the size of `unsigned long` between LP64 (Un*x) and LLP64 +(Windows) operating systems. + - All C buffer size functions now return 0 if an error occurs, rather than +trying to awkwardly return -1 in an unsigned data type (which could easily be +misinterpreted as a very large value.) + - Decompression scaling is now enabled explicitly, using a new +function/method (`tj3SetScalingFactor()`/`TJDecompressor.setScalingFactor()`), +rather than implicitly using awkward "desired width"/"desired height" +arguments. + - Partial image decompression has been implemented, using a new +function/method (`tj3SetCroppingRegion()`/`TJDecompressor.setCroppingRegion()`) +and a new TJBench option (`-crop`.) + - The JPEG colorspace can now be specified explicitly when compressing, +using a new API parameter (`TJPARAM_COLORSPACE`/`TJ.PARAM_COLORSPACE`.) This +allows JPEG images with the RGB and CMYK colorspaces to be created. + - TJBench no longer generates error/difference images, since identical +functionality is already available in ImageMagick. + - JPEG images with unknown subsampling configurations can now be +fully decompressed into packed-pixel images or losslessly transformed (with the +exception of lossless cropping.) They cannot currently be partially +decompressed or decompressed into planar YUV images. + - `tj3Destroy()` now silently accepts a NULL handle. + - `tj3Alloc()` and `tj3Free()` now return/accept void pointers, as +`malloc()` and `free()` do. + - The C image I/O functions now accept a TurboJPEG instance handle, which +is used to transmit/receive API parameter values and to receive error +information. + +5. Added support for 8-bit-per-component, 12-bit-per-component, and +16-bit-per-component lossless JPEG images. A new libjpeg API function +(`jpeg_enable_lossless()`), TurboJPEG API parameters +(`TJPARAM_LOSSLESS`/`TJ.PARAM_LOSSLESS`, +`TJPARAM_LOSSLESSPSV`/`TJ.PARAM_LOSSLESSPSV`, and +`TJPARAM_LOSSLESSPT`/`TJ.PARAM_LOSSLESSPT`), and a cjpeg/TJBench option +(`-lossless`) can be used to create a lossless JPEG image. (Decompression of +lossless JPEG images is handled automatically.) Refer to [libjpeg.txt](libjpeg.txt), [usage.txt](usage.txt), and the TurboJPEG API documentation for more details. -4. 12-bit-per-component (lossy and lossless) and 16-bit-per-component -(lossless) JPEG support is now included in the libjpeg API library, cjpeg, -djpeg, and jpegtran: +6. Added support for 12-bit-per-component (lossy and lossless) and +16-bit-per-component (lossless) JPEG images to the libjpeg and TurboJPEG APIs: - The existing `data_precision` field in `jpeg_compress_struct` and `jpeg_decompress_struct` has been repurposed to enable the creation of @@ -35,27 +118,20 @@ decompressed. 12-bit-per-component versions of `jpeg_write_raw_data()`, `jpeg_skip_scanlines()`, `jpeg_crop_scanline()`, and `jpeg_read_raw_data()`, provide interfaces for compressing from/decompressing to 12-bit-per-component -and 16-bit-per-component uncompressed image buffers. - - A new cjpeg command-line argument (`-precision`) can be used to create -a 12-bit-per-component or 16-bit-per-component JPEG image. (djpeg and jpegtran -handle 12-bit-per-component and 16-bit-per-component JPEG images -automatically.) - - Refer to [libjpeg.txt](libjpeg.txt) and [usage.txt](usage.txt) for more -details. - -5. Introduced a new flag in the TurboJPEG C and Java APIs (`TJFLAG_ARITHMETIC` -and `TJ.FLAG_ARITHMETIC`, respectively) that causes the library to use -arithmetic entropy coding in JPEG images generated by compression and transform -operations. Additionally, a new transform option (`TJXOPT_ARITHMETIC` in the C -API and `TJTransform.OPT_ARITHMETIC` in the Java API) has been introduced, -allowing arithmetic entropy coding to be enabled for selected transforms in a -multi-transform operation. - -6. Added a new TurboJPEG C API function (`tjDecompressHeader4()`) and Java API -method (`TJDecompressor.getFlags()`) that allow calling programs to determine -whether the JPEG image being decompressed uses progressive and/or arithmetic -entropy coding or is a lossless JPEG image. +and 16-bit-per-component packed-pixel and planar YUV image buffers. + - New 12-bit-per-component and 16-bit-per-component compression, +decompression, and image I/O functions/methods have been added to the TurboJPEG +API, and a new API parameter (`TJPARAM_PRECISION`/`TJ.PARAM_PRECISION`) can be +used to query the data precision of a JPEG image. (YUV functions are currently +limited to 8-bit data precision but can be expanded to accommodate 12-bit data +precision in the future, if such is deemed beneficial.) + - A new cjpeg and TJBench command-line argument (`-precision`) can be used +to create a 12-bit-per-component or 16-bit-per-component JPEG image. +(Decompression and transformation of 12-bit-per-component and +16-bit-per-component JPEG images is handled automatically.) + + Refer to [libjpeg.txt](libjpeg.txt), [usage.txt](usage.txt), and the +TurboJPEG API documentation for more details. 2.1.5 @@ -1479,7 +1555,7 @@ features (such as the colorspace extensions), but in general, it performs no faster than libjpeg v6b. 14. Added ARM 64-bit SIMD acceleration for the YCC-to-RGB color conversion -and IDCT algorithms (both are used during JPEG decompression.) For unknown +and IDCT algorithms (both are used during JPEG decompression.) For reasons (probably related to clang), this code cannot currently be compiled for iOS. From b94041390c477a02b3cab79d0cc0ef321dc889e8 Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 27 Jan 2023 07:20:10 -0600 Subject: [PATCH 089/162] TurboJPEG: Robustify JPEG header prefetching In decompression and transform functions, use the libjpeg API state rather than a TurboJPEG instance variable to determine whether jpeg_mem_src_tj() and jpeg_read_header() have already been called by a wrapper function. --- turbojpeg-mp.c | 3 +-- turbojpeg.c | 15 ++++----------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/turbojpeg-mp.c b/turbojpeg-mp.c index b4bf1eda8..1c2f2d6be 100644 --- a/turbojpeg-mp.c +++ b/turbojpeg-mp.c @@ -172,11 +172,10 @@ DLLEXPORT int GET_NAME(tj3Decompress, BITS_IN_JSAMPLE) retval = -1; goto bailout; } - if (!this->headerRead) { + if (dinfo->global_state <= DSTATE_START) { jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize); jpeg_read_header(dinfo, TRUE); } - this->headerRead = FALSE; setDecompParameters(this); this->dinfo.out_color_space = pf2cs[pixelFormat]; #if BITS_IN_JSAMPLE != 16 diff --git a/turbojpeg.c b/turbojpeg.c index f77c8688f..dbbe92c61 100644 --- a/turbojpeg.c +++ b/turbojpeg.c @@ -106,7 +106,6 @@ typedef struct _tjinstance { struct jpeg_decompress_struct dinfo; struct my_error_mgr jerr; int init; - boolean headerRead; char errStr[JMSG_LENGTH_MAX]; boolean isInstanceError; /* Parameters */ @@ -1972,7 +1971,6 @@ DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf, processFlags(handle, flags, DECOMPRESS); - this->headerRead = TRUE; if (tj3SetScalingFactor(handle, sf[i]) == -1) return -1; if (tj3SetCroppingRegion(handle, TJUNCROPPED) == -1) @@ -2321,11 +2319,10 @@ DLLEXPORT int tj3DecompressToYUVPlanes8(tjhandle handle, retval = -1; goto bailout; } - if (!this->headerRead) { + if (dinfo->global_state <= DSTATE_START) { jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize); jpeg_read_header(dinfo, TRUE); } - this->headerRead = FALSE; setDecompParameters(this); if (this->subsamp == TJSAMP_UNKNOWN) THROW("Could not determine subsampling level of JPEG image"); @@ -2477,7 +2474,6 @@ DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle, processFlags(handle, flags, DECOMPRESS); - this->headerRead = TRUE; if (tj3SetScalingFactor(handle, sf[i]) == -1) return -1; return tj3DecompressToYUVPlanes8(handle, jpegBuf, jpegSize, dstPlanes, @@ -2512,7 +2508,7 @@ DLLEXPORT int tj3DecompressToYUV8(tjhandle handle, retval = -1; goto bailout; } - if (!this->headerRead) { + if (dinfo->global_state <= DSTATE_START) { jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize); jpeg_read_header(dinfo, TRUE); } @@ -2539,7 +2535,6 @@ DLLEXPORT int tj3DecompressToYUV8(tjhandle handle, dstPlanes[2] = dstPlanes[1] + strides[1] * ph1; } - this->headerRead = TRUE; return tj3DecompressToYUVPlanes8(handle, jpegBuf, jpegSize, dstPlanes, strides); @@ -2587,7 +2582,6 @@ DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf, processFlags(handle, flags, DECOMPRESS); - this->headerRead = TRUE; if (tj3SetScalingFactor(handle, sf[i]) == -1) return -1; return tj3DecompressToYUV8(handle, jpegBuf, (size_t)jpegSize, dstBuf, align); @@ -2654,7 +2648,7 @@ DLLEXPORT int tj3Transform(tjhandle handle, const unsigned char *jpegBuf, retval = -1; goto bailout; } - if (!this->headerRead) + if (dinfo->global_state <= DSTATE_START) jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize); for (i = 0; i < n; i++) { @@ -2682,7 +2676,7 @@ DLLEXPORT int tj3Transform(tjhandle handle, const unsigned char *jpegBuf, } jcopy_markers_setup(dinfo, saveMarkers ? JCOPYOPT_ALL : JCOPYOPT_NONE); - if (!this->headerRead) + if (dinfo->global_state <= DSTATE_START) jpeg_read_header(dinfo, TRUE); this->subsamp = getSubsamp(&this->dinfo); @@ -2816,7 +2810,6 @@ DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf, THROW("Memory allocation failure"); for (i = 0; i < n; i++) sizes[i] = (size_t)dstSizes[i]; - this->headerRead = TRUE; retval = tj3Transform(handle, jpegBuf, (size_t)jpegSize, n, dstBufs, sizes, t); for (i = 0; i < n; i++) From 8c57aad0a346729bf80223b6d0f968f42e9d2c99 Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 27 Jan 2023 12:19:16 -0600 Subject: [PATCH 090/162] turbojpeg.c: Use THROWG() macro whenever possible --- turbojpeg.c | 65 +++++++++++++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/turbojpeg.c b/turbojpeg.c index dbbe92c61..29049cffd 100644 --- a/turbojpeg.c +++ b/turbojpeg.c @@ -921,14 +921,13 @@ DLLEXPORT tjhandle tjInitCompress(void) /* TurboJPEG 3+ */ DLLEXPORT size_t tj3JPEGBufSize(int width, int height, int jpegSubsamp) { + static const char FUNCTION_NAME[] = "tj3JPEGBufSize"; unsigned long long retval = 0; int mcuw, mcuh, chromasf; if (width < 1 || height < 1 || jpegSubsamp < TJSAMP_UNKNOWN || - jpegSubsamp >= TJ_NUMSAMP) { - SNPRINTF(errStr, JMSG_LENGTH_MAX, "tj3JPEGBufSize(): Invalid argument"); - return 0; - } + jpegSubsamp >= TJ_NUMSAMP) + THROWG("Invalid argument", 0); if (jpegSubsamp == TJSAMP_UNKNOWN) jpegSubsamp = TJSAMP_444; @@ -940,25 +939,25 @@ DLLEXPORT size_t tj3JPEGBufSize(int width, int height, int jpegSubsamp) mcuh = tjMCUHeight[jpegSubsamp]; chromasf = jpegSubsamp == TJSAMP_GRAY ? 0 : 4 * 64 / (mcuw * mcuh); retval = PAD(width, mcuw) * PAD(height, mcuh) * (2ULL + chromasf) + 2048ULL; - if (retval > (unsigned long long)((unsigned long)-1)) { - SNPRINTF(errStr, JMSG_LENGTH_MAX, "tj3JPEGBufSize(): Image is too large"); - return 0; - } + if (retval > (unsigned long long)((unsigned long)-1)) + THROWG("Image is too large", 0); +bailout: return (size_t)retval; } /* TurboJPEG 1.2+ */ DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp) { + static const char FUNCTION_NAME[] = "tjBufSize"; size_t retval; - if (jpegSubsamp < 0) { - SNPRINTF(errStr, JMSG_LENGTH_MAX, "tjBufSize(): Invalid argument"); - return (unsigned long)-1; - } + if (jpegSubsamp < 0) + THROWG("Invalid argument", 0); retval = tj3JPEGBufSize(width, height, jpegSubsamp); + +bailout: return (retval == 0) ? (unsigned long)-1 : (unsigned long)retval; } @@ -986,13 +985,12 @@ DLLEXPORT unsigned long TJBUFSIZE(int width, int height) /* TurboJPEG 3+ */ DLLEXPORT size_t tj3YUVBufSize(int width, int align, int height, int subsamp) { + static const char FUNCTION_NAME[] = "tj3YUVBufSize"; unsigned long long retval = 0; int nc, i; - if (align < 1 || !IS_POW2(align) || subsamp < 0 || subsamp >= TJ_NUMSAMP) { - SNPRINTF(errStr, JMSG_LENGTH_MAX, "tj3YUVBufSize(): Invalid argument"); - return 0; - } + if (align < 1 || !IS_POW2(align) || subsamp < 0 || subsamp >= TJ_NUMSAMP) + THROWG("Invalid argument", 0); nc = (subsamp == TJSAMP_GRAY ? 1 : 3); for (i = 0; i < nc; i++) { @@ -1003,11 +1001,10 @@ DLLEXPORT size_t tj3YUVBufSize(int width, int align, int height, int subsamp) if (pw == 0 || ph == 0) return 0; else retval += (unsigned long long)stride * ph; } - if (retval > (unsigned long long)((unsigned long)-1)) { - SNPRINTF(errStr, JMSG_LENGTH_MAX, "tj3YUVBufSize(): Image is too large"); - return 0; - } + if (retval > (unsigned long long)((unsigned long)-1)) + THROWG("Image is too large", 0); +bailout: return (size_t)retval; } @@ -1104,13 +1101,12 @@ DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp) DLLEXPORT size_t tj3YUVPlaneSize(int componentID, int width, int stride, int height, int subsamp) { + static const char FUNCTION_NAME[] = "tj3YUVPlaneSize"; unsigned long long retval = 0; int pw, ph; - if (width < 1 || height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP) { - SNPRINTF(errStr, JMSG_LENGTH_MAX, "tj3YUVPlaneSize(): Invalid argument"); - return 0; - } + if (width < 1 || height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP) + THROWG("Invalid argument", 0); pw = tj3YUVPlaneWidth(componentID, width, subsamp); ph = tj3YUVPlaneHeight(componentID, height, subsamp); @@ -1120,11 +1116,10 @@ DLLEXPORT size_t tj3YUVPlaneSize(int componentID, int width, int stride, else stride = abs(stride); retval = (unsigned long long)stride * (ph - 1) + pw; - if (retval > (unsigned long long)((unsigned long)-1)) { - SNPRINTF(errStr, JMSG_LENGTH_MAX, "tj3YUVPlaneSize(): Image is too large"); - return 0; - } + if (retval > (unsigned long long)((unsigned long)-1)) + THROWG("Image is too large", 0); +bailout: return (size_t)retval; } @@ -1841,14 +1836,16 @@ DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf, /* TurboJPEG 3+ */ DLLEXPORT tjscalingfactor *tj3GetScalingFactors(int *numScalingFactors) { - if (numScalingFactors == NULL) { - SNPRINTF(errStr, JMSG_LENGTH_MAX, - "tj3GetScalingFactors(): Invalid argument"); - return NULL; - } + static const char FUNCTION_NAME[] = "tj3GetScalingFactors"; + tjscalingfactor *retval = (tjscalingfactor *)sf; + + if (numScalingFactors == NULL) + THROWG("Invalid argument", NULL); *numScalingFactors = NUMSF; - return (tjscalingfactor *)sf; + +bailout: + return retval; } /* TurboJPEG 1.2+ */ From 54f571f8fbd533872d2e8d101cf2281815fc109a Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 27 Jan 2023 12:31:44 -0600 Subject: [PATCH 091/162] TurboJPEG: Clean up/repurpose THROWI() macro We already had a use case for two integer arguments, and the new use cases benefit from two integer arguments as well. --- turbojpeg-mp.c | 19 ++++++++++++------- turbojpeg.c | 23 +++++++++++------------ 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/turbojpeg-mp.c b/turbojpeg-mp.c index 1c2f2d6be..50c220dbf 100644 --- a/turbojpeg-mp.c +++ b/turbojpeg-mp.c @@ -197,11 +197,13 @@ DLLEXPORT int GET_NAME(tj3Decompress, BITS_IN_JSAMPLE) _jpeg_crop_scanline(dinfo, &crop_x, &crop_w); if ((int)crop_x != this->croppingRegion.x) - THROWI("Unexplained mismatch between specified and actual (%d) cropping region left boundary", - (int)crop_x); + THROWI("Unexplained mismatch between specified (%d) and\n" + "actual (%d) cropping region left boundary", + this->croppingRegion.x, (int)crop_x); if ((int)crop_w != this->croppingRegion.w) - THROWI("Unexplained mismatch between specified and actual (%d) cropping region width", - (int)crop_w); + THROWI("Unexplained mismatch between specified (%d) and\n" + "actual (%d) cropping region width", + this->croppingRegion.w, (int)crop_w); } #endif @@ -232,8 +234,9 @@ DLLEXPORT int GET_NAME(tj3Decompress, BITS_IN_JSAMPLE) JDIMENSION lines = _jpeg_skip_scanlines(dinfo, this->croppingRegion.y); if ((int)lines != this->croppingRegion.y) - THROWI("Unexplained mismatch between specified and actual (%d) cropping region upper boundary", - (int)lines); + THROWI("Unexplained mismatch between specified (%d) and\n" + "actual (%d) cropping region upper boundary", + this->croppingRegion.y, (int)lines); } while ((int)dinfo->output_scanline < this->croppingRegion.y + this->croppingRegion.h) @@ -249,7 +252,9 @@ DLLEXPORT int GET_NAME(tj3Decompress, BITS_IN_JSAMPLE) if (lines != dinfo->output_height - this->croppingRegion.y - this->croppingRegion.h) - THROWI("Unexplained mismatch between specified and actual (%d) cropping region lower boundary", + THROWI("Unexplained mismatch between specified (%d) and\n" + "actual (%d) cropping region lower boundary", + this->croppingRegion.y + this->croppingRegion.h, (int)(dinfo->output_height - lines)); } } else diff --git a/turbojpeg.c b/turbojpeg.c index 29049cffd..32344466d 100644 --- a/turbojpeg.c +++ b/turbojpeg.c @@ -249,11 +249,12 @@ static int cs2pf[JPEG_NUMCS] = { SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s(): %s", FUNCTION_NAME, m); \ this->isInstanceError = TRUE; THROWG(m, -1) \ } -#define THROWI(format, val) { \ +#define THROWI(format, val1, val2) { \ SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s(): " format, FUNCTION_NAME, \ - val); \ + val1, val2); \ this->isInstanceError = TRUE; \ - SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): " format, FUNCTION_NAME, val); \ + SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): " format, FUNCTION_NAME, val1, \ + val2); \ retval = -1; goto bailout; \ } @@ -1911,7 +1912,9 @@ DLLEXPORT int tj3SetCroppingRegion(tjhandle handle, tjregion croppingRegion) if (croppingRegion.x % TJSCALED(tjMCUWidth[this->subsamp], this->scalingFactor) != 0) - THROWI("The left boundary of the cropping region is not divisible by the scaled MCU width (%d)", + THROWI("The left boundary of the cropping region (%d) is not\n" + "divisible by the scaled MCU width (%d)", + croppingRegion.x, TJSCALED(tjMCUWidth[this->subsamp], this->scalingFactor)); if (croppingRegion.w == 0) croppingRegion.w = scaledWidth - croppingRegion.x; @@ -2685,14 +2688,10 @@ DLLEXPORT int tj3Transform(tjhandle handle, const unsigned char *jpegBuf, if (this->subsamp == TJSAMP_UNKNOWN) THROW("Could not determine subsampling level of JPEG image"); if ((t[i].r.x % tjMCUWidth[this->subsamp]) != 0 || - (t[i].r.y % tjMCUHeight[this->subsamp]) != 0) { - SNPRINTF(this->errStr, JMSG_LENGTH_MAX, - "To crop this JPEG image, x must be a multiple of %d\n" - "and y must be a multiple of %d.\n", - tjMCUWidth[this->subsamp], tjMCUHeight[this->subsamp]); - this->isInstanceError = TRUE; - retval = -1; goto bailout; - } + (t[i].r.y % tjMCUHeight[this->subsamp]) != 0) + THROWI("To crop this JPEG image, x must be a multiple of %d\n" + "and y must be a multiple of %d.", tjMCUWidth[this->subsamp], + tjMCUHeight[this->subsamp]); } } From d7790789a6c3f0867175d781948e9d10fc55520d Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 27 Jan 2023 12:49:45 -0600 Subject: [PATCH 092/162] tjbench.c: Clean up THROW_TJ*() macro usage - Don't report which function was being called when a TurboJPEG error occurred, because the TurboJPEG error message already contains that information. - Use THROW_TJG() for functions that report errors globally instead of through an instance handle. - Use THROW_TJ() for the image I/O functions. - Formatting tweaks --- tjbench.c | 130 +++++++++++++++++++++++++++--------------------------- 1 file changed, 64 insertions(+), 66 deletions(-) diff --git a/tjbench.c b/tjbench.c index 84fea32bd..9e54eb273 100644 --- a/tjbench.c +++ b/tjbench.c @@ -48,35 +48,31 @@ } #define THROW_UNIX(m) THROW(m, strerror(errno)) -char tjErrorStr[JMSG_LENGTH_MAX] = "\0", tjErrorMsg[JMSG_LENGTH_MAX] = "\0"; +char tjErrorStr[JMSG_LENGTH_MAX] = "\0"; int tjErrorLine = -1, tjErrorCode = -1; #define THROW_TJG(m) { \ - printf("ERROR in line %d while %s:\n%s\n", __LINE__, m, \ - tj3GetErrorStr(NULL)); \ + printf("ERROR in line %d\n%s\n", __LINE__, tj3GetErrorStr(NULL)); \ retval = -1; goto bailout; \ } -#define THROW_TJ(m) { \ +#define THROW_TJ() { \ int _tjErrorCode = tj3GetErrorCode(handle); \ char *_tjErrorStr = tj3GetErrorStr(handle); \ \ if (!tj3Get(handle, TJPARAM_STOPONWARNING) && \ _tjErrorCode == TJERR_WARNING) { \ if (strncmp(tjErrorStr, _tjErrorStr, JMSG_LENGTH_MAX) || \ - strncmp(tjErrorMsg, m, JMSG_LENGTH_MAX) || \ tjErrorCode != _tjErrorCode || tjErrorLine != __LINE__) { \ strncpy(tjErrorStr, _tjErrorStr, JMSG_LENGTH_MAX); \ tjErrorStr[JMSG_LENGTH_MAX - 1] = '\0'; \ - strncpy(tjErrorMsg, m, JMSG_LENGTH_MAX); \ - tjErrorMsg[JMSG_LENGTH_MAX - 1] = '\0'; \ tjErrorCode = _tjErrorCode; \ tjErrorLine = __LINE__; \ - printf("WARNING in line %d while %s:\n%s\n", __LINE__, m, _tjErrorStr); \ + printf("WARNING in line %d:\n%s\n", __LINE__, _tjErrorStr); \ } \ } else { \ - printf("%s in line %d while %s:\n%s\n", \ - _tjErrorCode == TJERR_WARNING ? "WARNING" : "ERROR", __LINE__, m, \ + printf("%s in line %d:\n%s\n", \ + _tjErrorCode == TJERR_WARNING ? "WARNING" : "ERROR", __LINE__, \ _tjErrorStr); \ retval = -1; goto bailout; \ } \ @@ -194,26 +190,26 @@ static int decomp(unsigned char **jpegBufs, size_t *jpegSizes, void *dstBuf, } if ((handle = tj3Init(TJINIT_DECOMPRESS)) == NULL) - THROW_TJ("executing tj3Init()"); + THROW_TJG(); if (tj3Set(handle, TJPARAM_STOPONWARNING, stopOnWarning) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (tj3Set(handle, TJPARAM_BOTTOMUP, bottomUp) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (tj3Set(handle, TJPARAM_FASTUPSAMPLE, fastUpsample) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (tj3Set(handle, TJPARAM_FASTDCT, fastDCT) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (tj3Set(handle, TJPARAM_SCANLIMIT, limitScans ? 500 : 0) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (IS_CROPPED(cr)) { if (tj3DecompressHeader(handle, jpegBufs[0], jpegSizes[0]) == -1) - THROW_TJ("executing tj3DecompressHeader()"); + THROW_TJ(); } if (tj3SetScalingFactor(handle, sf) == -1) - THROW_TJ("executing tj3SetScalingFactor()"); + THROW_TJ(); if (tj3SetCroppingRegion(handle, cr) == -1) - THROW_TJ("executing tj3SetCroppingRegion()"); + THROW_TJ(); if (IS_CROPPED(cr)) { scaledw = cr.w ? cr.w : scaledw - cr.x; scaledh = cr.h ? cr.h : scaledh - cr.y; @@ -247,7 +243,7 @@ static int decomp(unsigned char **jpegBufs, size_t *jpegSizes, void *dstBuf, size_t yuvSize = tj3YUVBufSize(width, yuvAlign, height, subsamp); if (yuvSize == 0) - THROW_TJ("allocating YUV buffer"); + THROW_TJG(); if ((yuvBuf = (unsigned char *)malloc(yuvSize)) == NULL) THROW_UNIX("allocating YUV buffer"); memset(yuvBuf, 127, yuvSize); @@ -272,25 +268,25 @@ static int decomp(unsigned char **jpegBufs, size_t *jpegSizes, void *dstBuf, if (tj3DecompressToYUV8(handle, jpegBufs[tile], jpegSizes[tile], yuvBuf, yuvAlign) == -1) - THROW_TJ("executing tj3DecompressToYUV8()"); + THROW_TJ(); startDecode = getTime(); if (tj3DecodeYUV8(handle, yuvBuf, yuvAlign, dstPtr2, width, pitch, height, pf) == -1) - THROW_TJ("executing tj3DecodeYUV8()"); + THROW_TJ(); if (iter >= 0) elapsedDecode += getTime() - startDecode; } else { if (precision == 8) { if (tj3Decompress8(handle, jpegBufs[tile], jpegSizes[tile], dstPtr2, pitch, pf) == -1) - THROW_TJ("executing tj3Decompress8()"); + THROW_TJ(); } else if (precision == 12) { if (tj3Decompress12(handle, jpegBufs[tile], jpegSizes[tile], (short *)dstPtr2, pitch, pf) == -1) - THROW_TJ("executing tj3Decompress12()"); + THROW_TJ(); } else { if (tj3Decompress16(handle, jpegBufs[tile], jpegSizes[tile], (unsigned short *)dstPtr2, pitch, pf) == -1) - THROW_TJ("executing tj3Decompress16()"); + THROW_TJ(); } } } @@ -345,15 +341,15 @@ static int decomp(unsigned char **jpegBufs, size_t *jpegSizes, void *dstBuf, if (precision == 8) { if (tj3SaveImage8(handle, tempStr, (unsigned char *)dstBuf, scaledw, 0, scaledh, pf) == -1) - THROW_TJG("saving output image"); + THROW_TJ(); } else if (precision == 12) { if (tj3SaveImage12(handle, tempStr, (short *)dstBuf, scaledw, 0, scaledh, pf) == -1) - THROW_TJG("saving output image"); + THROW_TJ(); } else { if (tj3SaveImage16(handle, tempStr, (unsigned short *)dstBuf, scaledw, 0, scaledh, pf) == -1) - THROW_TJG("saving output image"); + THROW_TJ(); } bailout: @@ -411,8 +407,9 @@ static int fullTest(tjhandle handle, void *srcBuf, int w, int h, int subsamp, if (noRealloc) { for (i = 0; i < ntilesw * ntilesh; i++) { size_t jpegBufSize = tj3JPEGBufSize(tilew, tileh, subsamp); + if (jpegBufSize == 0) - THROW_TJ("getting buffer size"); + THROW_TJG(); if (jpegBufSize > (size_t)INT_MAX) THROW("getting buffer size", "Image is too large"); if ((jpegBufs[i] = tj3Alloc(jpegBufSize)) == NULL) @@ -435,35 +432,35 @@ static int fullTest(tjhandle handle, void *srcBuf, int w, int h, int subsamp, } if (tj3Set(handle, TJPARAM_NOREALLOC, noRealloc) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (tj3Set(handle, TJPARAM_SUBSAMP, subsamp) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (tj3Set(handle, TJPARAM_FASTDCT, fastDCT) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (tj3Set(handle, TJPARAM_OPTIMIZE, optimize) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (tj3Set(handle, TJPARAM_PROGRESSIVE, progressive) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (tj3Set(handle, TJPARAM_ARITHMETIC, arithmetic) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (tj3Set(handle, TJPARAM_LOSSLESS, lossless) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (lossless) { if (tj3Set(handle, TJPARAM_LOSSLESSPSV, jpegQual) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); } else { if (tj3Set(handle, TJPARAM_QUALITY, jpegQual) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); } if (tj3Set(handle, TJPARAM_RESTARTBLOCKS, restartIntervalBlocks) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (tj3Set(handle, TJPARAM_RESTARTROWS, restartIntervalRows) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (doYUV) { yuvSize = tj3YUVBufSize(tilew, yuvAlign, tileh, subsamp); if (yuvSize == 0) - THROW_TJ("allocating YUV buffer"); + THROW_TJG(); if ((yuvBuf = (unsigned char *)malloc(yuvSize)) == NULL) THROW_UNIX("allocating YUV buffer"); memset(yuvBuf, 127, yuvSize); @@ -489,25 +486,25 @@ static int fullTest(tjhandle handle, void *srcBuf, int w, int h, int subsamp, if (tj3EncodeYUV8(handle, srcPtr2, width, pitch, height, pf, yuvBuf, yuvAlign) == -1) - THROW_TJ("executing tj3EncodeYUV8()"); + THROW_TJ(); if (iter >= 0) elapsedEncode += getTime() - startEncode; if (tj3CompressFromYUV8(handle, yuvBuf, width, yuvAlign, height, &jpegBufs[tile], &jpegSizes[tile]) == -1) - THROW_TJ("executing tj3CompressFromYUV8()"); + THROW_TJ(); } else { if (precision == 8) { if (tj3Compress8(handle, srcPtr2, width, pitch, height, pf, &jpegBufs[tile], &jpegSizes[tile]) == -1) - THROW_TJ("executing tj3Compress8()"); + THROW_TJ(); } else if (precision == 12) { if (tj3Compress12(handle, (short *)srcPtr2, width, pitch, height, pf, &jpegBufs[tile], &jpegSizes[tile]) == -1) - THROW_TJ("executing tj3Compress12()"); + THROW_TJ(); } else { if (tj3Compress16(handle, (unsigned short *)srcPtr2, width, pitch, height, pf, &jpegBufs[tile], &jpegSizes[tile]) == -1) - THROW_TJ("executing tj3Compress16()"); + THROW_TJ(); } } totalJpegSize += jpegSizes[tile]; @@ -644,22 +641,22 @@ static int decompTest(char *fileName) if (temp != NULL) *temp = '\0'; if ((handle = tj3Init(TJINIT_TRANSFORM)) == NULL) - THROW_TJ("executing tj3Init()"); + THROW_TJG(); if (tj3Set(handle, TJPARAM_STOPONWARNING, stopOnWarning) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (tj3Set(handle, TJPARAM_BOTTOMUP, bottomUp) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (tj3Set(handle, TJPARAM_NOREALLOC, noRealloc) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (tj3Set(handle, TJPARAM_FASTUPSAMPLE, fastUpsample) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (tj3Set(handle, TJPARAM_FASTDCT, fastDCT) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (tj3Set(handle, TJPARAM_SCANLIMIT, limitScans ? 500 : 0) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (tj3DecompressHeader(handle, srcBuf, srcSize) == -1) - THROW_TJ("executing tj3DecompressHeader()"); + THROW_TJ(); w = tj3Get(handle, TJPARAM_JPEGWIDTH); h = tj3Get(handle, TJPARAM_JPEGHEIGHT); subsamp = tj3Get(handle, TJPARAM_SUBSAMP); @@ -669,9 +666,9 @@ static int decompTest(char *fileName) if (tj3Get(handle, TJPARAM_ARITHMETIC) == 1) printf("JPEG image uses arithmetic entropy coding\n\n"); if (tj3Set(handle, TJPARAM_PROGRESSIVE, progressive) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (tj3Set(handle, TJPARAM_ARITHMETIC, arithmetic) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); lossless = tj3Get(handle, TJPARAM_LOSSLESS); sampleSize = (precision == 8 ? sizeof(unsigned char) : sizeof(short)); @@ -684,9 +681,9 @@ static int decompTest(char *fileName) if (lossless) sf = TJUNSCALED; if (tj3SetScalingFactor(handle, sf) == -1) - THROW_TJ("executing tj3SetScalingFactor()"); + THROW_TJ(); if (tj3SetCroppingRegion(handle, cr) == -1) - THROW_TJ("executing tj3SetCroppingRegion()"); + THROW_TJ(); if (quiet == 1) { printf("All performance values in Mpixels/sec\n\n"); @@ -722,8 +719,9 @@ static int decompTest(char *fileName) (doTile || xformOp != TJXOP_NONE || xformOpt != 0 || customFilter)) { for (i = 0; i < ntilesw * ntilesh; i++) { size_t jpegBufSize = tj3JPEGBufSize(tilew, tileh, subsamp); + if (jpegBufSize == 0) - THROW_TJ("getting buffer size"); + THROW_TJG(); if (jpegBufSize > (size_t)INT_MAX) THROW("getting buffer size", "Image is too large"); if ((jpegBufs[i] = tj3Alloc(jpegBufSize)) == NULL) @@ -797,7 +795,7 @@ static int decompTest(char *fileName) start = getTime(); if (tj3Transform(handle, srcBuf, srcSize, tntilesw * tntilesh, jpegBufs, jpegSizes, t) == -1) - THROW_TJ("executing tj3Transform()"); + THROW_TJ(); elapsed += getTime() - start; if (iter >= 0) { iter++; @@ -1199,21 +1197,21 @@ int main(int argc, char *argv[]) if (!decompOnly) { if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL) - THROW_TJ("executing tj3Init()"); + THROW_TJG(); if (tj3Set(handle, TJPARAM_STOPONWARNING, stopOnWarning) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (tj3Set(handle, TJPARAM_BOTTOMUP, bottomUp) == -1) - THROW_TJ("executing tj3Set()"); + THROW_TJ(); if (precision == 8) { if ((srcBuf = tj3LoadImage8(handle, argv[1], &w, 1, &h, &pf)) == NULL) - THROW_TJG("loading input image"); + THROW_TJ(); } else if (precision == 12) { if ((srcBuf = tj3LoadImage12(handle, argv[1], &w, 1, &h, &pf)) == NULL) - THROW_TJG("loading input image"); + THROW_TJ(); } else { if ((srcBuf = tj3LoadImage16(handle, argv[1], &w, 1, &h, &pf)) == NULL) - THROW_TJG("loading input image"); + THROW_TJ(); } temp = strrchr(argv[1], '.'); if (temp != NULL) *temp = '\0'; From d0015876ec798dbf03df23d98b72161704c7c57a Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 27 Jan 2023 13:08:04 -0600 Subject: [PATCH 093/162] Fix build if [CD]_LOSSLESS_SUPPORTED undefined (regression introduced by fc01f4673b71c0b833c59c21e8c4478a9c4bcf21) --- turbojpeg-mp.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/turbojpeg-mp.c b/turbojpeg-mp.c index 50c220dbf..5699869dd 100644 --- a/turbojpeg-mp.c +++ b/turbojpeg-mp.c @@ -283,6 +283,9 @@ DLLEXPORT _JSAMPLE *GET_NAME(tj3LoadImage, BITS_IN_JSAMPLE) { static const char FUNCTION_NAME[] = GET_STRING(tj3LoadImage, BITS_IN_JSAMPLE); + +#if BITS_IN_JSAMPLE != 16 || defined(C_LOSSLESS_SUPPORTED) + int retval = 0, tempc; size_t pitch; tjhandle handle2 = NULL; @@ -392,6 +395,23 @@ DLLEXPORT _JSAMPLE *GET_NAME(tj3LoadImage, BITS_IN_JSAMPLE) if (file) fclose(file); if (retval < 0) { free(dstBuf); dstBuf = NULL; } return dstBuf; + +#else /* BITS_IN_JSAMPLE != 16 || defined(C_LOSSLESS_SUPPORTED) */ + + static const char ERROR_MSG[] = + "16-bit data precision requires lossless JPEG,\n" + "which was disabled at build time."; + _JSAMPLE *retval = NULL; + + GET_TJINSTANCE(handle, NULL) + SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s(): %s", FUNCTION_NAME, + ERROR_MSG); + this->isInstanceError = TRUE; THROWG(ERROR_MSG, NULL) + +bailout: + return retval; + +#endif } @@ -403,6 +423,9 @@ DLLEXPORT int GET_NAME(tj3SaveImage, BITS_IN_JSAMPLE) static const char FUNCTION_NAME[] = GET_STRING(tj3SaveImage, BITS_IN_JSAMPLE); int retval = 0; + +#if BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED) + tjhandle handle2 = NULL; tjinstance *this2; j_decompress_ptr dinfo = NULL; @@ -483,6 +506,16 @@ DLLEXPORT int GET_NAME(tj3SaveImage, BITS_IN_JSAMPLE) tj3Destroy(handle2); if (file) fclose(file); return retval; + +#else /* BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED) */ + + GET_TJINSTANCE(handle, -1) + THROW("16-bit data precision requires lossless JPEG,\n" + "which was disabled at build time.") +bailout: + return retval; + +#endif } From 9b3a8f36418e8b6a601aa683506bb875f6dd9f12 Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 27 Jan 2023 13:38:48 -0600 Subject: [PATCH 094/162] jcapimin.c: Revert changes made in fc01f467 Those changes worked around an innocuous UBSan warning that was exposed by the new TurboJPEG 3 transform fuzz target, due to the fact that tj3Transform() no longer rejects images with unknown subsampling configurations. That UBSan warning was a false positive, and attempting to fix it introduced a buffer overrun triggered by a malformed input image that causes jpeg_write_marker() to be called with datalen == 0. I suspect that the UBSan false positive was only reproducible on my local machine, but I guess we'll see. Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=55413 --- jcapimin.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/jcapimin.c b/jcapimin.c index 25ce9a110..cbb3d13e1 100644 --- a/jcapimin.c +++ b/jcapimin.c @@ -5,7 +5,7 @@ * Copyright (C) 1994-1998, Thomas G. Lane. * Modified 2003-2010 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2022-2023, D. R. Commander. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -240,11 +240,10 @@ jpeg_write_marker(j_compress_ptr cinfo, int marker, const JOCTET *dataptr, (*cinfo->marker->write_marker_header) (cinfo, marker, datalen); write_marker_byte = cinfo->marker->write_marker_byte; /* copy for speed */ - do { + while (datalen--) { (*write_marker_byte) (cinfo, *dataptr); dataptr++; } - while (--datalen); } /* Same, but piecemeal. */ From fd8c4da0ac35c6a79451d9d495eab20fa1486828 Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 27 Jan 2023 14:05:07 -0600 Subject: [PATCH 095/162] Bump revision to 2.1.90 to prepare for beta + acknowledge upcoming 2.1.5 release --- CMakeLists.txt | 2 +- ChangeLog.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b7248ffb..f52458a5d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ if(CMAKE_EXECUTABLE_SUFFIX) endif() project(libjpeg-turbo C) -set(VERSION 2.1.80) +set(VERSION 2.1.90) set(COPYRIGHT_YEAR "1991-2023") string(REPLACE "." ";" VERSION_TRIPLET ${VERSION}) list(GET VERSION_TRIPLET 0 VERSION_MAJOR) diff --git a/ChangeLog.md b/ChangeLog.md index 24f6c38d5..a8c982f30 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,7 +1,7 @@ -2.2 pre-beta -============ +3.0 beta1 +========= -### Significant changes relative to 2.1.4: +### Significant changes relative to 2.1.5: 1. Significantly sped up the computation of optimal Huffman tables. This speeds up the compression of tiny images by as much as 2x and provides a From 427c304595f6d49f5d94d7fdf8bed594705d1b70 Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 27 Jan 2023 14:31:48 -0600 Subject: [PATCH 096/162] tjbench.c: Fix Windows build error (regression introduced by d7790789a6c3f0867175d781948e9d10fc55520d) --- tjbench.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tjbench.c b/tjbench.c index 9e54eb273..f12f5c213 100644 --- a/tjbench.c +++ b/tjbench.c @@ -51,7 +51,7 @@ char tjErrorStr[JMSG_LENGTH_MAX] = "\0"; int tjErrorLine = -1, tjErrorCode = -1; -#define THROW_TJG(m) { \ +#define THROW_TJG() { \ printf("ERROR in line %d\n%s\n", __LINE__, tj3GetErrorStr(NULL)); \ retval = -1; goto bailout; \ } From 3b19db4e6e7493a748369974819b4c5fa84c7614 Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 27 Jan 2023 18:24:41 -0600 Subject: [PATCH 097/162] BUILDING.md: Specify install prefix for MinGW/Un*x The default install prefix when building under MinGW is chosen based on the needs of the official build system, which uses MSYS2 to generate Windows installer packages that install under c:\libjpeg-turbo-gcc[64]. However, attempting to configure the build with that install prefix on a Un*x machine causes a CMake error. Fixes #641 --- BUILDING.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/BUILDING.md b/BUILDING.md index 66d28670a..b965b5e76 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -372,9 +372,13 @@ located (usually **/usr/bin**.) Next, execute the following commands: cd {build_directory} cmake -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake \ + -DCMAKE_INSTALL_PREFIX={install_path} \ [additional CMake flags] {source_directory} make +*{install\_path}* is the path under which the libjpeg-turbo binaries should be +installed. + ### 64-bit MinGW Build on Un*x (including Mac and Cygwin) @@ -391,9 +395,13 @@ located (usually **/usr/bin**.) Next, execute the following commands: cd {build_directory} cmake -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake \ + -DCMAKE_INSTALL_PREFIX={install_path} \ [additional CMake flags] {source_directory} make +*{install\_path}* is the path under which the libjpeg-turbo binaries should be +installed. + Building libjpeg-turbo for iOS ------------------------------ From 0205c6e04c436d322f3cfd1dfe9358f64f6eb7e3 Mon Sep 17 00:00:00 2001 From: DRC Date: Sat, 28 Jan 2023 12:09:43 -0600 Subject: [PATCH 098/162] tjbench.c: Remove vestigial int overflow checks Since tj3Alloc() now accepts a size_t argument rather than an int argument, it is no longer necessary to check for signed integer overflow in the C version of TJBench. --- tjbench.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tjbench.c b/tjbench.c index f12f5c213..e0573b4bd 100644 --- a/tjbench.c +++ b/tjbench.c @@ -410,8 +410,6 @@ static int fullTest(tjhandle handle, void *srcBuf, int w, int h, int subsamp, if (jpegBufSize == 0) THROW_TJG(); - if (jpegBufSize > (size_t)INT_MAX) - THROW("getting buffer size", "Image is too large"); if ((jpegBufs[i] = tj3Alloc(jpegBufSize)) == NULL) THROW_UNIX("allocating JPEG tiles"); } @@ -722,8 +720,6 @@ static int decompTest(char *fileName) if (jpegBufSize == 0) THROW_TJG(); - if (jpegBufSize > (size_t)INT_MAX) - THROW("getting buffer size", "Image is too large"); if ((jpegBufs[i] = tj3Alloc(jpegBufSize)) == NULL) THROW_UNIX("allocating JPEG tiles"); } From fd93d98a959ac3700e2da07310a44867c9c46f03 Mon Sep 17 00:00:00 2001 From: DRC Date: Sat, 28 Jan 2023 12:13:11 -0600 Subject: [PATCH 099/162] Fix i386 transform fuzzer build Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=55447 --- fuzz/transform.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/transform.cc b/fuzz/transform.cc index f45521254..fe4a6a216 100644 --- a/fuzz/transform.cc +++ b/fuzz/transform.cc @@ -39,7 +39,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { tjhandle handle = NULL; unsigned char *dstBufs[NUMXFORMS] = { NULL, NULL, NULL }; - unsigned long dstSizes[NUMXFORMS] = { 0, 0, 0 }, maxBufSize; + size_t dstSizes[NUMXFORMS] = { 0, 0, 0 }, maxBufSize; int width = 0, height = 0, jpegSubsamp, i, t; tjtransform transforms[NUMXFORMS]; #if defined(__has_feature) && __has_feature(memory_sanitizer) From dd89ce6cd6872190c97e0b1389dd5c817c247797 Mon Sep 17 00:00:00 2001 From: DRC Date: Wed, 1 Feb 2023 11:54:09 -0600 Subject: [PATCH 100/162] Build: Define THREAD_LOCAL even if !WITH_TURBOJPEG The SIMD dispatchers use thread-local storage now as well, because of f579cc11b33e5bfeb9931e37cc74b4a33c95d2e6. --- CMakeLists.txt | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f52458a5d..e3528b6b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -468,19 +468,17 @@ if(NOT INLINE_WORKS) endif() message(STATUS "INLINE = ${INLINE} (FORCE_INLINE = ${FORCE_INLINE})") -if(WITH_TURBOJPEG) - if(MSVC) - set(THREAD_LOCAL "__declspec(thread)") - else() - set(THREAD_LOCAL "__thread") - endif() - check_c_source_compiles("${THREAD_LOCAL} int i; int main(void) { i = 0; return i; }" HAVE_THREAD_LOCAL) - if(HAVE_THREAD_LOCAL) - message(STATUS "THREAD_LOCAL = ${THREAD_LOCAL}") - else() - message(WARNING "Thread-local storage is not available. The TurboJPEG API library's global error handler will not be thread-safe.") - unset(THREAD_LOCAL) - endif() +if(MSVC) + set(THREAD_LOCAL "__declspec(thread)") +else() + set(THREAD_LOCAL "__thread") +endif() +check_c_source_compiles("${THREAD_LOCAL} int i; int main(void) { i = 0; return i; }" HAVE_THREAD_LOCAL) +if(HAVE_THREAD_LOCAL) + message(STATUS "THREAD_LOCAL = ${THREAD_LOCAL}") +else() + message(WARNING "Thread-local storage is not available. The TurboJPEG API library's global error handler will not be thread-safe.") + unset(THREAD_LOCAL) endif() if(UNIX AND NOT APPLE) From 89ceac8c859f0ccde21cdff9dbcdee94d96488c8 Mon Sep 17 00:00:00 2001 From: DRC Date: Wed, 1 Feb 2023 12:24:00 -0600 Subject: [PATCH 101/162] Decompress fuzzer: Fix uninitialized memory access (regression introduced by fc01f4673b71c0b833c59c21e8c4478a9c4bcf21) Oops. In the process of migrating the fuzzers to the TurboJPEG 3 API, I accidentally left out the code in decompress.cc that updates the width and height based on the scaling factor (but I apparently included that code in decompress_yuv.cc.) Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=55573 --- fuzz/decompress.cc | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/fuzz/decompress.cc b/fuzz/decompress.cc index 2ef17ba04..1752e3e02 100644 --- a/fuzz/decompress.cc +++ b/fuzz/decompress.cc @@ -71,6 +71,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) tj3Set(handle, TJPARAM_SCANLIMIT, 500); for (pfi = 0; pfi < NUMPF; pfi++) { + int w = width, h = height; int pf = pixelFormats[pfi], i; int64_t sum = 0; @@ -84,20 +85,21 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) if (pfi == 1) { tjscalingfactor sf = { 1, 2 }; tj3SetScalingFactor(handle, sf); + w = TJSCALED(width, sf); + h = TJSCALED(height, sf); } else tj3SetScalingFactor(handle, TJUNSCALED); /* Test partial image decompression on the fourth iteration, if the image is large enough. */ - if (pfi == 3 && width >= 97 && height >= 75) { + if (pfi == 3 && w >= 97 && h >= 75) { tjregion cr = { 32, 16, 65, 59 }; tj3SetCroppingRegion(handle, cr); } else tj3SetCroppingRegion(handle, TJUNCROPPED); } - if ((dstBuf = malloc(width * height * tjPixelSize[pf] * - sampleSize)) == NULL) + if ((dstBuf = malloc(w * h * tjPixelSize[pf] * sampleSize)) == NULL) goto bailout; if (precision == 8) { @@ -105,14 +107,14 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) pf) == 0) { /* Touch all of the output pixels in order to catch uninitialized reads when using MemorySanitizer. */ - for (i = 0; i < width * height * tjPixelSize[pf]; i++) + for (i = 0; i < w * h * tjPixelSize[pf]; i++) sum += ((unsigned char *)dstBuf)[i]; } } else if (precision == 12) { if (tj3Decompress12(handle, data, size, (short *)dstBuf, 0, pf) == 0) { /* Touch all of the output pixels in order to catch uninitialized reads when using MemorySanitizer. */ - for (i = 0; i < width * height * tjPixelSize[pf]; i++) + for (i = 0; i < w * h * tjPixelSize[pf]; i++) sum += ((short *)dstBuf)[i]; } } else { @@ -120,7 +122,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) pf) == 0) { /* Touch all of the output pixels in order to catch uninitialized reads when using MemorySanitizer. */ - for (i = 0; i < width * height * tjPixelSize[pf]; i++) + for (i = 0; i < w * h * tjPixelSize[pf]; i++) sum += ((unsigned short *)dstBuf)[i]; } } From 4e028ecd63aaa13c8a14937f9f1e9a272ed4b543 Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 2 Feb 2023 08:55:37 -0600 Subject: [PATCH 102/162] SIMD/x86: Initialize simd_support before every use As long as a libjpeg instance is only used by one thread at a time, a program is technically within its rights to call jpeg_start_*compress() in one thread and jpeg_(read|write)_*(), with the same libjpeg instance, in a second thread. However, because the various jsimd_can*() functions are called within the body of jpeg_start_*compress() and simd_support is now thread-local (due to f579cc11b33e5bfeb9931e37cc74b4a33c95d2e6), that led to a situation in which simd_support was initialized in the first thread but not the second. The uninitialized value of simd_support is 0xFFFFFFFF, which the second thread interpreted to mean that it could use any instruction set, and when it attempted to use AVX2 instructions on a CPU that didn't support them, an illegal instruction error occurred. This issue was known to affect libvips. This commit modifies the i386 and x86-64 SIMD dispatchers so that the various jsimd_*() functions always call init_simd(), if simd_support is uninitialized, prior to dispatching based on the value of simd_support. Note that the other SIMD dispatchers don't need this, because only the x86 SIMD extensions currently support multiple instruction sets. This patch has been verified to be performance-neutral to within +/- 0.4% with 32-bit and 64-bit code running on a 2.8 GHz Intel Xeon W3530 and a 3.6 GHz Intel Xeon W2123. Fixes #649 --- simd/i386/jsimd.c | 71 ++++++++++++++++++++++++++++++++++++++++++++- simd/x86_64/jsimd.c | 47 +++++++++++++++++++++++++++++- 2 files changed, 116 insertions(+), 2 deletions(-) diff --git a/simd/i386/jsimd.c b/simd/i386/jsimd.c index 7bd61b62f..b429b0a53 100644 --- a/simd/i386/jsimd.c +++ b/simd/i386/jsimd.c @@ -2,7 +2,7 @@ * jsimd_i386.c * * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2009-2011, 2013-2014, 2016, 2018, 2022, D. R. Commander. + * Copyright (C) 2009-2011, 2013-2014, 2016, 2018, 2022-2023, D. R. Commander. * Copyright (C) 2015-2016, 2018, 2022, Matthieu Darbois. * * Based on the x86 SIMD extension for IJG JPEG library, @@ -158,6 +158,9 @@ jsimd_rgb_ycc_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, void (*sse2fct) (JDIMENSION, JSAMPARRAY, JSAMPIMAGE, JDIMENSION, int); void (*mmxfct) (JDIMENSION, JSAMPARRAY, JSAMPIMAGE, JDIMENSION, int); + if (simd_support == ~0U) + init_simd(); + switch (cinfo->in_color_space) { case JCS_EXT_RGB: avx2fct = jsimd_extrgb_ycc_convert_avx2; @@ -217,6 +220,9 @@ jsimd_rgb_gray_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, void (*sse2fct) (JDIMENSION, JSAMPARRAY, JSAMPIMAGE, JDIMENSION, int); void (*mmxfct) (JDIMENSION, JSAMPARRAY, JSAMPIMAGE, JDIMENSION, int); + if (simd_support == ~0U) + init_simd(); + switch (cinfo->in_color_space) { case JCS_EXT_RGB: avx2fct = jsimd_extrgb_gray_convert_avx2; @@ -276,6 +282,9 @@ jsimd_ycc_rgb_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, void (*sse2fct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY, int); void (*mmxfct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY, int); + if (simd_support == ~0U) + init_simd(); + switch (cinfo->out_color_space) { case JCS_EXT_RGB: avx2fct = jsimd_ycc_extrgb_convert_avx2; @@ -379,6 +388,9 @@ GLOBAL(void) jsimd_h2v2_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY output_data) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v2_downsample_avx2(cinfo->image_width, cinfo->max_v_samp_factor, compptr->v_samp_factor, @@ -399,6 +411,9 @@ GLOBAL(void) jsimd_h2v1_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY output_data) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v1_downsample_avx2(cinfo->image_width, cinfo->max_v_samp_factor, compptr->v_samp_factor, @@ -461,6 +476,9 @@ GLOBAL(void) jsimd_h2v2_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v2_upsample_avx2(cinfo->max_v_samp_factor, cinfo->output_width, input_data, output_data_ptr); @@ -476,6 +494,9 @@ GLOBAL(void) jsimd_h2v1_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v1_upsample_avx2(cinfo->max_v_samp_factor, cinfo->output_width, input_data, output_data_ptr); @@ -537,6 +558,9 @@ GLOBAL(void) jsimd_h2v2_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v2_fancy_upsample_avx2(cinfo->max_v_samp_factor, compptr->downsampled_width, input_data, @@ -555,6 +579,9 @@ GLOBAL(void) jsimd_h2v1_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v1_fancy_upsample_avx2(cinfo->max_v_samp_factor, compptr->downsampled_width, input_data, @@ -623,6 +650,9 @@ jsimd_h2v2_merged_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, void (*sse2fct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY); void (*mmxfct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY); + if (simd_support == ~0U) + init_simd(); + switch (cinfo->out_color_space) { case JCS_EXT_RGB: avx2fct = jsimd_h2v2_extrgb_merged_upsample_avx2; @@ -681,6 +711,9 @@ jsimd_h2v1_merged_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, void (*sse2fct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY); void (*mmxfct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY); + if (simd_support == ~0U) + init_simd(); + switch (cinfo->out_color_space) { case JCS_EXT_RGB: avx2fct = jsimd_h2v1_extrgb_merged_upsample_avx2; @@ -785,6 +818,9 @@ GLOBAL(void) jsimd_convsamp(JSAMPARRAY sample_data, JDIMENSION start_col, DCTELEM *workspace) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_convsamp_avx2(sample_data, start_col, workspace); else if (simd_support & JSIMD_SSE2) @@ -797,6 +833,9 @@ GLOBAL(void) jsimd_convsamp_float(JSAMPARRAY sample_data, JDIMENSION start_col, FAST_FLOAT *workspace) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_SSE2) jsimd_convsamp_float_sse2(sample_data, start_col, workspace); else if (simd_support & JSIMD_SSE) @@ -867,6 +906,9 @@ jsimd_can_fdct_float(void) GLOBAL(void) jsimd_fdct_islow(DCTELEM *data) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_fdct_islow_avx2(data); else if (simd_support & JSIMD_SSE2) @@ -878,6 +920,9 @@ jsimd_fdct_islow(DCTELEM *data) GLOBAL(void) jsimd_fdct_ifast(DCTELEM *data) { + if (simd_support == ~0U) + init_simd(); + if ((simd_support & JSIMD_SSE2) && IS_ALIGNED_SSE(jconst_fdct_islow_sse2)) jsimd_fdct_ifast_sse2(data); else @@ -887,6 +932,9 @@ jsimd_fdct_ifast(DCTELEM *data) GLOBAL(void) jsimd_fdct_float(FAST_FLOAT *data) { + if (simd_support == ~0U) + init_simd(); + if ((simd_support & JSIMD_SSE) && IS_ALIGNED_SSE(jconst_fdct_float_sse)) jsimd_fdct_float_sse(data); else if (simd_support & JSIMD_3DNOW) @@ -942,6 +990,9 @@ jsimd_can_quantize_float(void) GLOBAL(void) jsimd_quantize(JCOEFPTR coef_block, DCTELEM *divisors, DCTELEM *workspace) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_quantize_avx2(coef_block, divisors, workspace); else if (simd_support & JSIMD_SSE2) @@ -954,6 +1005,9 @@ GLOBAL(void) jsimd_quantize_float(JCOEFPTR coef_block, FAST_FLOAT *divisors, FAST_FLOAT *workspace) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_SSE2) jsimd_quantize_float_sse2(coef_block, divisors, workspace); else if (simd_support & JSIMD_SSE) @@ -1017,6 +1071,9 @@ jsimd_idct_2x2(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col) { + if (simd_support == ~0U) + init_simd(); + if ((simd_support & JSIMD_SSE2) && IS_ALIGNED_SSE(jconst_idct_red_sse2)) jsimd_idct_2x2_sse2(compptr->dct_table, coef_block, output_buf, output_col); @@ -1029,6 +1086,9 @@ jsimd_idct_4x4(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col) { + if (simd_support == ~0U) + init_simd(); + if ((simd_support & JSIMD_SSE2) && IS_ALIGNED_SSE(jconst_idct_red_sse2)) jsimd_idct_4x4_sse2(compptr->dct_table, coef_block, output_buf, output_col); @@ -1123,6 +1183,9 @@ jsimd_idct_islow(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_idct_islow_avx2(compptr->dct_table, coef_block, output_buf, output_col); @@ -1139,6 +1202,9 @@ jsimd_idct_ifast(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col) { + if (simd_support == ~0U) + init_simd(); + if ((simd_support & JSIMD_SSE2) && IS_ALIGNED_SSE(jconst_idct_ifast_sse2)) jsimd_idct_ifast_sse2(compptr->dct_table, coef_block, output_buf, output_col); @@ -1152,6 +1218,9 @@ jsimd_idct_float(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col) { + if (simd_support == ~0U) + init_simd(); + if ((simd_support & JSIMD_SSE2) && IS_ALIGNED_SSE(jconst_idct_float_sse2)) jsimd_idct_float_sse2(compptr->dct_table, coef_block, output_buf, output_col); diff --git a/simd/x86_64/jsimd.c b/simd/x86_64/jsimd.c index 227e27657..3f5ee77eb 100644 --- a/simd/x86_64/jsimd.c +++ b/simd/x86_64/jsimd.c @@ -2,7 +2,7 @@ * jsimd_x86_64.c * * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2009-2011, 2014, 2016, 2018, 2022, D. R. Commander. + * Copyright (C) 2009-2011, 2014, 2016, 2018, 2022-2023, D. R. Commander. * Copyright (C) 2015-2016, 2018, 2022, Matthieu Darbois. * * Based on the x86 SIMD extension for IJG JPEG library, @@ -145,6 +145,9 @@ jsimd_rgb_ycc_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, void (*avx2fct) (JDIMENSION, JSAMPARRAY, JSAMPIMAGE, JDIMENSION, int); void (*sse2fct) (JDIMENSION, JSAMPARRAY, JSAMPIMAGE, JDIMENSION, int); + if (simd_support == ~0U) + init_simd(); + switch (cinfo->in_color_space) { case JCS_EXT_RGB: avx2fct = jsimd_extrgb_ycc_convert_avx2; @@ -194,6 +197,9 @@ jsimd_rgb_gray_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, void (*avx2fct) (JDIMENSION, JSAMPARRAY, JSAMPIMAGE, JDIMENSION, int); void (*sse2fct) (JDIMENSION, JSAMPARRAY, JSAMPIMAGE, JDIMENSION, int); + if (simd_support == ~0U) + init_simd(); + switch (cinfo->in_color_space) { case JCS_EXT_RGB: avx2fct = jsimd_extrgb_gray_convert_avx2; @@ -243,6 +249,9 @@ jsimd_ycc_rgb_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, void (*avx2fct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY, int); void (*sse2fct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY, int); + if (simd_support == ~0U) + init_simd(); + switch (cinfo->out_color_space) { case JCS_EXT_RGB: avx2fct = jsimd_ycc_extrgb_convert_avx2; @@ -333,6 +342,9 @@ GLOBAL(void) jsimd_h2v2_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY output_data) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v2_downsample_avx2(cinfo->image_width, cinfo->max_v_samp_factor, compptr->v_samp_factor, @@ -349,6 +361,9 @@ GLOBAL(void) jsimd_h2v1_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY output_data) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v1_downsample_avx2(cinfo->image_width, cinfo->max_v_samp_factor, compptr->v_samp_factor, @@ -403,6 +418,9 @@ GLOBAL(void) jsimd_h2v2_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v2_upsample_avx2(cinfo->max_v_samp_factor, cinfo->output_width, input_data, output_data_ptr); @@ -415,6 +433,9 @@ GLOBAL(void) jsimd_h2v1_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v1_upsample_avx2(cinfo->max_v_samp_factor, cinfo->output_width, input_data, output_data_ptr); @@ -469,6 +490,9 @@ GLOBAL(void) jsimd_h2v2_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v2_fancy_upsample_avx2(cinfo->max_v_samp_factor, compptr->downsampled_width, input_data, @@ -483,6 +507,9 @@ GLOBAL(void) jsimd_h2v1_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v1_fancy_upsample_avx2(cinfo->max_v_samp_factor, compptr->downsampled_width, input_data, @@ -542,6 +569,9 @@ jsimd_h2v2_merged_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, void (*avx2fct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY); void (*sse2fct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY); + if (simd_support == ~0U) + init_simd(); + switch (cinfo->out_color_space) { case JCS_EXT_RGB: avx2fct = jsimd_h2v2_extrgb_merged_upsample_avx2; @@ -590,6 +620,9 @@ jsimd_h2v1_merged_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, void (*avx2fct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY); void (*sse2fct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY); + if (simd_support == ~0U) + init_simd(); + switch (cinfo->out_color_space) { case JCS_EXT_RGB: avx2fct = jsimd_h2v1_extrgb_merged_upsample_avx2; @@ -679,6 +712,9 @@ GLOBAL(void) jsimd_convsamp(JSAMPARRAY sample_data, JDIMENSION start_col, DCTELEM *workspace) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_convsamp_avx2(sample_data, start_col, workspace); else @@ -748,6 +784,9 @@ jsimd_can_fdct_float(void) GLOBAL(void) jsimd_fdct_islow(DCTELEM *data) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_fdct_islow_avx2(data); else @@ -809,6 +848,9 @@ jsimd_can_quantize_float(void) GLOBAL(void) jsimd_quantize(JCOEFPTR coef_block, DCTELEM *divisors, DCTELEM *workspace) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_quantize_avx2(coef_block, divisors, workspace); else @@ -963,6 +1005,9 @@ jsimd_idct_islow(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_idct_islow_avx2(compptr->dct_table, coef_block, output_buf, output_col); From 2a5a3c6f0a366f490329ff0b5339435f56412581 Mon Sep 17 00:00:00 2001 From: DRC Date: Tue, 7 Feb 2023 13:13:24 -0600 Subject: [PATCH 103/162] OSS-Fuzz: Bail out immediately on decomp failure Don't keep trying to decompress the same image if tj3Decompress*() has already thrown an error. Otherwise, if the image has an excessive number of scans, then each iteration of the loop will try to decompress up to the scan limit, which may cause the overall test to time out even if one iteration doesn't time out. --- fuzz/decompress.cc | 9 ++++++--- fuzz/decompress_yuv.cc | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/fuzz/decompress.cc b/fuzz/decompress.cc index 1752e3e02..12886f13c 100644 --- a/fuzz/decompress.cc +++ b/fuzz/decompress.cc @@ -109,14 +109,16 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) when using MemorySanitizer. */ for (i = 0; i < w * h * tjPixelSize[pf]; i++) sum += ((unsigned char *)dstBuf)[i]; - } + } else + goto bailout; } else if (precision == 12) { if (tj3Decompress12(handle, data, size, (short *)dstBuf, 0, pf) == 0) { /* Touch all of the output pixels in order to catch uninitialized reads when using MemorySanitizer. */ for (i = 0; i < w * h * tjPixelSize[pf]; i++) sum += ((short *)dstBuf)[i]; - } + } else + goto bailout; } else { if (tj3Decompress16(handle, data, size, (unsigned short *)dstBuf, 0, pf) == 0) { @@ -124,7 +126,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) when using MemorySanitizer. */ for (i = 0; i < w * h * tjPixelSize[pf]; i++) sum += ((unsigned short *)dstBuf)[i]; - } + } else + goto bailout; } free(dstBuf); diff --git a/fuzz/decompress_yuv.cc b/fuzz/decompress_yuv.cc index 125b16322..fc262efe4 100644 --- a/fuzz/decompress_yuv.cc +++ b/fuzz/decompress_yuv.cc @@ -100,7 +100,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) when using MemorySanitizer. */ for (i = 0; i < w * h * tjPixelSize[pf]; i++) sum += dstBuf[i]; - } + } else + goto bailout; free(dstBuf); dstBuf = NULL; From 6c610333497302c52ff36046f9ff72f0c3a6dc2e Mon Sep 17 00:00:00 2001 From: DRC Date: Wed, 8 Feb 2023 09:23:51 -0600 Subject: [PATCH 104/162] ChangeLog.md: Document 4e028ecd + bump version to 3.0 beta2 --- CMakeLists.txt | 2 +- ChangeLog.md | 25 +++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e3528b6b4..d866b8dab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ if(CMAKE_EXECUTABLE_SUFFIX) endif() project(libjpeg-turbo C) -set(VERSION 2.1.90) +set(VERSION 2.1.91) set(COPYRIGHT_YEAR "1991-2023") string(REPLACE "." ";" VERSION_TRIPLET ${VERSION}) list(GET VERSION_TRIPLET 0 VERSION_MAJOR) diff --git a/ChangeLog.md b/ChangeLog.md index a8c982f30..f14819098 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,7 +1,7 @@ -3.0 beta1 +3.0 beta2 ========= -### Significant changes relative to 2.1.5: +### Significant changes relative to 2.1.5.1: 1. Significantly sped up the computation of optimal Huffman tables. This speeds up the compression of tiny images by as much as 2x and provides a @@ -134,6 +134,27 @@ to create a 12-bit-per-component or 16-bit-per-component JPEG image. TurboJPEG API documentation for more details. +2.1.5.1 +======= + +### Significant changes relative to 2.1.5: + +1. The SIMD dispatchers in libjpeg-turbo 2.1.4 and prior stored the list of +supported SIMD instruction sets in a global variable, which caused an innocuous +race condition whereby the variable could have been initialized multiple times +if `jpeg_start_*compress()` was called simultaneously in multiple threads. +libjpeg-turbo 2.1.5 included an undocumented attempt to fix this race condition +by making the SIMD support variable thread-local. However, that caused another +issue whereby, if `jpeg_start_*compress()` was called in one thread and +`jpeg_read_*()` or `jpeg_write_*()` was called in a second thread, the SIMD +support variable was never initialized in the second thread. On x86 systems, +this led the second thread to incorrectly assume that AVX2 instructions were +always available, and when it attempted to use those instructions on older x86 +CPUs that do not support them, an illegal instruction error occurred. The SIMD +dispatchers now ensure that the SIMD support variable is initialized before +dispatching based on its value. + + 2.1.5 ===== From 2af984fdae2e1d48e1e49846e5120d8739fbfe0e Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 10 Feb 2023 09:55:27 -0600 Subject: [PATCH 105/162] Build: Fail if included with add_subdirectory() Even though BUILDING.md and CONTRIBUTING.md explicitly state that the libjpeg-turbo build system does not and will not support being integrated into downstream build systems using add_subdirectory() (see 05655481917a2d2761cf2fe19b76f639b7f159ef), people continue to file bug reports, feature requests, and pull requests regarding that (see #265, #637, and #653 in addition to the issues listed in 05655481917a2d2761cf2fe19b76f639b7f159ef.) Responding to those issues wastes our project's limited resources. Hopefully people will get the hint if the build system explicitly tells them that it can't be included using add_subdirectory(), which will prompt them to read the comments in CMakeLists.txt explaining why. To anyone stumbling upon this commit message, please refer to the discussions under the issues listed above, as well as the issues listed in 05655481917a2d2761cf2fe19b76f639b7f159ef. Our project's position on this has been stated, explained, and defended numerous times. --- CMakeLists.txt | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index d866b8dab..0ff120f3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,34 @@ pad_number(VERSION_MINOR 3) pad_number(VERSION_REVISION 3) set(LIBJPEG_TURBO_VERSION_NUMBER ${VERSION_MAJOR}${VERSION_MINOR}${VERSION_REVISION}) +# The libjpeg-turbo build system has never supported and will never support +# being integrated into another build system using add_subdirectory(), because +# doing so would require that we (minimally): +# +# 1. avoid using certain CMake variables, such as CMAKE_SOURCE_DIR, +# CMAKE_BINARY_DIR, and CMAKE_PROJECT_NAME; +# 2. avoid using implicit include directories and relative paths; +# 3. optionally provide a way to skip the installation of libjpeg-turbo +# components when the 'install' target is built; +# 4. optionally provide a way to postfix target names, to avoid namespace +# conflicts; +# 5. restructure the top-level CMakeLists.txt so that it properly sets the +# PROJECT_VERSION variable; and +# 6. design automated regression tests to ensure that new commits don't break +# any of the above. +# +# Even if we did all of that, issues would still arise, because it is +# impossible for an upstream build system to anticipate the widely varying +# needs of every downstream build system. That's why the CMake +# ExternalProject_Add() function exists. Downstream projects that wish to +# integrate libjpeg-turbo as a subdirectory should either use +# ExternalProject_Add() or make downstream modifications to the libjpeg-turbo +# build system to suit their specific needs. Please do not file bug reports, +# feature requests, or pull requests regarding this. +if(NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + message(FATAL_ERROR "The libjpeg-turbo build system cannot be integrated into another build system using add_subdirectory(). Use ExternalProject_Add() instead.") +endif() + # CMake 3.14 and later sets CMAKE_MACOSX_BUNDLE to TRUE by default when # CMAKE_SYSTEM_NAME is iOS, tvOS, or watchOS, which breaks the libjpeg-turbo # build. (Specifically, when CMAKE_MACOSX_BUNDLE is TRUE, executables for From d67edaff830e0f87a9b53bb4bbaf155bdc7ea499 Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 10 Feb 2023 10:40:56 -0600 Subject: [PATCH 106/162] Ignore 2.1.5.1 ChangeLog header w/git diff --check (Otherwise, checkstyle flags it as a leftover conflict marker.) --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index 6c9660afc..46de94f58 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,3 +2,4 @@ /.gitattributes export-ignore /.github export-ignore *.ppm binary +/ChangeLog.md conflict-marker-size=8 From c13fe15955db9794a07f4643fad4aff9c7d303d3 Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 23 Feb 2023 09:35:12 -0600 Subject: [PATCH 107/162] Build: Clarify CMAKE_OSX_ARCHITECTURES error It's not that the build system doesn't support multiple values in CMAKE_OSX_ARCHITECTURES. It's that libjpeg-turbo, because of its SIMD extensions, *cannot* support multiple values in CMAKE_OSX_ARCHITECTURES. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ff120f3f..f6cc21931 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,7 +98,7 @@ string(TOLOWER ${CMAKE_SYSTEM_PROCESSOR} CMAKE_SYSTEM_PROCESSOR_LC) set(COUNT 1) foreach(ARCH ${CMAKE_OSX_ARCHITECTURES}) if(COUNT GREATER 1) - message(FATAL_ERROR "The libjpeg-turbo build system does not support multiple values in CMAKE_OSX_ARCHITECTURES.") + message(FATAL_ERROR "libjpeg-turbo contains assembly code, so it cannot be built with multiple values in CMAKE_OSX_ARCHITECTURES.") endif() math(EXPR COUNT "${COUNT}+1") endforeach() From 97df8ea9edf0017f891072845f43ed878be4fcef Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 23 Feb 2023 11:40:59 -0600 Subject: [PATCH 108/162] GitHub: Add pull request template --- .github/pull_request_template.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..f1511657f --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,8 @@ +**Complete description of the bug fix or feature that this pull request implements** + + +**Checklist before submitting the pull request, to maximize the chances that the pull request will be accepted** + +- [ ] Read CONTRIBUTING.md, a link to which appears under "Helpful resources" below. That document discusses general guidelines for contributing to libjpeg-turbo, as well as the types of contributions that will not be accepted or are unlikely to be accepted. +- [ ] Search the existing issues and pull requests (both open and closed) to ensure that a similar request has not already been submitted and rejected. +- [ ] Discuss the proposed bug fix or feature in a GitHub issue, through direct e-mail with the project maintainer, or on the libjpeg-turbo-devel mailing list. From 0827eaff11c96ea17c174eceb332140871ad101b Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 9 Mar 2023 21:04:40 -0600 Subject: [PATCH 109/162] ChangeLog.md: Add literal vers # to 3.0 beta2 hdr (per our convention) --- ChangeLog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index f14819098..58a8bd8dd 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,5 @@ -3.0 beta2 -========= +2.1.91 (3.0 beta2) +================== ### Significant changes relative to 2.1.5.1: From 58a3427ffc840db1d2021c16762e54f398a62829 Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 9 Mar 2023 21:07:40 -0600 Subject: [PATCH 110/162] Bump version to 2.1.92 to prepare for new commits --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f6cc21931..9f4681150 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ if(CMAKE_EXECUTABLE_SUFFIX) endif() project(libjpeg-turbo C) -set(VERSION 2.1.91) +set(VERSION 2.1.92) set(COPYRIGHT_YEAR "1991-2023") string(REPLACE "." ";" VERSION_TRIPLET ${VERSION}) list(GET VERSION_TRIPLET 0 VERSION_MAJOR) From fc881ebb211b2ba6292d35691a946e1f9c1374b4 Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 9 Mar 2023 20:55:43 -0600 Subject: [PATCH 111/162] TurboJPEG: Implement 4:4:1 chrominance subsampling This allows losslessly transposed or rotated 4:1:1 JPEG images to be losslessly cropped, partially decompressed, or decompressed to planar YUV images. Because tj3Transform() allows multiple lossless transformations to be chained together, all subsampling options need to have a corresponding transposed subsampling option. (This is why 4:4:0 was originally implemented as well.) Otherwise, the documentation would be technically incorrect. It says that images with unknown subsampling types cannot be losslessly cropped, partially decompressed, or decompressed to planar YUV images, but it doesn't say anything about images with known subsampling types whose subsampling type becomes unknown if the image is rotated or transposed. This is one of those situations in which it is easier to implement a feature that works around the problem than to document the problem. Closes #659 --- ChangeLog.md | 11 ++ cmakescripts/testclean.cmake | 5 + doc/html/group___turbo_j_p_e_g.html | 13 +- doc/html/search/all_6.js | 55 ++++---- doc/html/search/all_7.js | 2 +- doc/html/search/all_8.js | 2 +- doc/html/search/all_9.js | 2 +- doc/html/search/classes_0.js | 6 +- doc/html/search/enums_0.js | 14 +- doc/html/search/enumvalues_0.js | 123 +++++++++--------- doc/html/search/functions_0.js | 76 +++++------ doc/html/search/groups_0.js | 2 +- doc/html/search/typedefs_0.js | 4 +- doc/html/search/variables_0.js | 2 +- doc/html/search/variables_1.js | 4 +- doc/html/search/variables_2.js | 2 +- doc/html/search/variables_3.js | 2 +- doc/html/search/variables_4.js | 4 +- doc/html/search/variables_5.js | 2 +- doc/html/search/variables_6.js | 18 +-- doc/html/search/variables_7.js | 2 +- doc/html/search/variables_8.js | 2 +- doc/html/search/variables_9.js | 2 +- java/TJBench.java | 8 +- java/TJExample.java | 2 +- java/TJUnitTest.java | 16 ++- java/doc/allclasses.html | 4 +- java/doc/constant-values.html | 13 +- java/doc/index-all.html | 4 + java/doc/member-search-index.js | 2 +- java/doc/member-search-index.zip | Bin 1927 -> 1933 bytes java/doc/org/libjpegturbo/turbojpeg/TJ.html | 36 ++++- .../org/libjpegturbo/turbojpeg/YUVImage.html | 20 +-- java/doc/package-search-index.zip | Bin 237 -> 237 bytes java/doc/type-search-index.zip | Bin 311 -> 311 bytes java/org/libjpegturbo/turbojpeg/TJ.java | 17 ++- java/org/libjpegturbo/turbojpeg/YUVImage.java | 20 +-- java/org_libjpegturbo_turbojpeg_TJ.h | 4 +- tjbench.c | 7 +- tjexample.c | 2 +- tjunittest.c | 14 +- turbojpeg.h | 40 ++++-- 42 files changed, 335 insertions(+), 229 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 58a8bd8dd..93b08061e 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,14 @@ +3.0.0 +===== + +### Significant changes relative to 3.0 beta2: + +1. The TurboJPEG API now supports 4:4:1 (transposed 4:1:1) chrominance +subsampling, which allows losslessly transposed or rotated 4:1:1 JPEG images to +be losslessly cropped, partially decompressed, or decompressed to planar YUV +images. + + 2.1.91 (3.0 beta2) ================== diff --git a/cmakescripts/testclean.cmake b/cmakescripts/testclean.cmake index 4d249dee6..6b5a1460f 100644 --- a/cmakescripts/testclean.cmake +++ b/cmakescripts/testclean.cmake @@ -30,6 +30,11 @@ file(GLOB FILES *_411_*.ppm *_411_*.jpg *_411.yuv + *_441_*.bmp + *_441_*.png + *_441_*.ppm + *_441_*.jpg + *_441.yuv *_LOSSL*S_*.bmp *_LOSSL*S_*.ppm *_LOSSL*S_*.jpg diff --git a/doc/html/group___turbo_j_p_e_g.html b/doc/html/group___turbo_j_p_e_g.html index 1d0507f7e..696af8a92 100644 --- a/doc/html/group___turbo_j_p_e_g.html +++ b/doc/html/group___turbo_j_p_e_g.html @@ -169,6 +169,7 @@
        TJSAMP_440, TJSAMP_411, +TJSAMP_441, TJSAMP_UNKNOWN
      }

      TJSAMP_441 

      4:4:1 chrominance subsampling.

      +

      The JPEG or YUV image will contain one chrominance component for every 1x4 block of pixels in the source image. JPEG images compressed with 4:4:1 subsampling will be almost exactly the same size as those compressed with 4:2:0 subsampling, and in the aggregate, both subsampling methods produce approximately the same perceptual quality. However, 4:4:1 is better able to reproduce sharp vertical features.

      +
      Note
      4:4:1 subsampling is not fully accelerated in libjpeg-turbo.
      +
      TJSAMP_UNKNOWN 

      Unknown subsampling.

      The JPEG image uses an unusual type of chrominance subsampling. Such images can be decompressed into packed-pixel images, but they cannot be

      NUMSAMP67
      @@ -508,20 +508,27 @@

      org.libjpegturbo.*

      4
      + +public static final intSAMP_4416
      public static final int SAMP_444 0
      public static final int SAMP_GRAY 3
      public static final int
      static intSAMP_441 +
      4:4:1 chrominance subsampling.
      +
      static int SAMP_444
      4:4:4 chrominance subsampling (no chrominance subsampling).
      static int SAMP_GRAY
      Grayscale.
      static int SAMP_UNKNOWN
      Unknown subsampling.
      static java.awt.Rectangle UNCROPPED
      A java.awt.Rectangle instance that specifies no cropping
      static TJScalingFactor UNSCALED @@ -867,6 +874,27 @@

      SAMP_411

      + + + +
        +
      • +

        SAMP_441

        +
        public static final int SAMP_441
        +
        4:4:1 chrominance subsampling. The JPEG or YUV image will contain one + chrominance component for every 1x4 block of pixels in the source image. + JPEG images compressed with 4:4:1 subsampling will be almost exactly the + same size as those compressed with 4:2:0 subsampling, and in the + aggregate, both subsampling methods produce approximately the same + perceptual quality. However, 4:4:1 is better able to reproduce sharp + vertical features. Note that 4:4:1 subsampling is not fully accelerated + in libjpeg-turbo.
        +
        +
        See Also:
        +
        Constant Field Values
        +
        +
      • +
      diff --git a/java/doc/org/libjpegturbo/turbojpeg/YUVImage.html b/java/doc/org/libjpegturbo/turbojpeg/YUVImage.html index 071b16ce4..2654c21e5 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/YUVImage.html +++ b/java/doc/org/libjpegturbo/turbojpeg/YUVImage.html @@ -150,16 +150,16 @@

      Class YUVImage

      image. The width and height of each plane are determined by the image width, height, and level of chrominance subsampling. The luminance plane width is the image width padded to the nearest multiple of the horizontal - subsampling factor (1 in the case of 4:4:4, grayscale, or 4:4:0; 2 in the - case of 4:2:2 or 4:2:0; 4 in the case of 4:1:1.) Similarly, the luminance - plane height is the image height padded to the nearest multiple of the - vertical subsampling factor (1 in the case of 4:4:4, 4:2:2, grayscale, or - 4:1:1; 2 in the case of 4:2:0 or 4:4:0.) This is irrespective of any - additional padding that may be specified as an argument to the various - YUVImage methods. The chrominance plane width is equal to the luminance - plane width divided by the horizontal subsampling factor, and the - chrominance plane height is equal to the luminance plane height divided by - the vertical subsampling factor. + subsampling factor (1 in the case of 4:4:4, grayscale, 4:4:0, or 4:4:1; 2 in + the case of 4:2:2 or 4:2:0; 4 in the case of 4:1:1.) Similarly, the + luminance plane height is the image height padded to the nearest multiple of + the vertical subsampling factor (1 in the case of 4:4:4, 4:2:2, grayscale, + or 4:1:1; 2 in the case of 4:2:0 or 4:4:0; 4 in the case of 4:4:1.) This is + irrespective of any additional padding that may be specified as an argument + to the various YUVImage methods. The chrominance plane width is equal to + the luminance plane width divided by the horizontal subsampling factor, and + the chrominance plane height is equal to the luminance plane height divided + by the vertical subsampling factor.

      For example, if the source image is 35 x 35 pixels and 4:2:2 subsampling is used, then the luminance plane would be 36 x 35 bytes, and each of the diff --git a/java/doc/package-search-index.zip b/java/doc/package-search-index.zip index e6c959be2db77fde6b061acc44969fec7d16dc2a..b55d555a2ae1dce3fae814b099096f41ddf938ca 100644 GIT binary patch delta 28 hcmaFM_?D43z?+#xgn@&DgTZlG=0x6pW)L;s4*+PM2nGNE delta 28 hcmaFM_?D43z?+#xgn@&DgTW`=Y9enxGl-h+2LNJ!2S5M- diff --git a/java/doc/type-search-index.zip b/java/doc/type-search-index.zip index 93006846a794bac2ccb9450ec5748ca50e64b3c8..3f8aad13e27dce52e0e38ec1e38cbb6d1767969a 100644 GIT binary patch delta 28 hcmdnaw4I4Jz?+#xgn@&DgTZlG=0x5*%pmHGHvnL52r~cx delta 28 hcmdnaw4I4Jz?+#xgn@&DgTW`=Y9j9)W)Stp8vt5P2W * For example, if the source image is 35 x 35 pixels and 4:2:2 subsampling is * used, then the luminance plane would be 36 x 35 bytes, and each of the diff --git a/java/org_libjpegturbo_turbojpeg_TJ.h b/java/org_libjpegturbo_turbojpeg_TJ.h index 5ebaf187d..e27c3b5c1 100644 --- a/java/org_libjpegturbo_turbojpeg_TJ.h +++ b/java/org_libjpegturbo_turbojpeg_TJ.h @@ -8,7 +8,7 @@ extern "C" { #endif #undef org_libjpegturbo_turbojpeg_TJ_NUMSAMP -#define org_libjpegturbo_turbojpeg_TJ_NUMSAMP 6L +#define org_libjpegturbo_turbojpeg_TJ_NUMSAMP 7L #undef org_libjpegturbo_turbojpeg_TJ_SAMP_444 #define org_libjpegturbo_turbojpeg_TJ_SAMP_444 0L #undef org_libjpegturbo_turbojpeg_TJ_SAMP_422 @@ -21,6 +21,8 @@ extern "C" { #define org_libjpegturbo_turbojpeg_TJ_SAMP_440 4L #undef org_libjpegturbo_turbojpeg_TJ_SAMP_411 #define org_libjpegturbo_turbojpeg_TJ_SAMP_411 5L +#undef org_libjpegturbo_turbojpeg_TJ_SAMP_441 +#define org_libjpegturbo_turbojpeg_TJ_SAMP_441 6L #undef org_libjpegturbo_turbojpeg_TJ_SAMP_UNKNOWN #define org_libjpegturbo_turbojpeg_TJ_SAMP_UNKNOWN -1L #undef org_libjpegturbo_turbojpeg_TJ_NUMPF diff --git a/tjbench.c b/tjbench.c index e0573b4bd..e25dd79a5 100644 --- a/tjbench.c +++ b/tjbench.c @@ -98,13 +98,13 @@ const char *pixFormatStr[TJ_NUMPF] = { "RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "GRAY", "", "", "", "", "CMYK" }; const char *subNameLong[TJ_NUMSAMP] = { - "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1" + "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1", "4:4:1" }; const char *csName[TJ_NUMCS] = { "RGB", "YCbCr", "GRAY", "CMYK", "YCCK" }; const char *subName[TJ_NUMSAMP] = { - "444", "422", "420", "GRAY", "440", "411" + "444", "422", "420", "GRAY", "440", "411", "441" }; tjscalingfactor *scalingFactors = NULL, sf = { 1, 1 }; tjregion cr = { 0, 0, 0, 0 }; @@ -941,7 +941,7 @@ static void usage(char *progName) } printf(")\n"); printf("-subsamp S = When compressing, use the specified level of chrominance\n"); - printf(" subsampling (S = 444, 422, 440, 420, 411, or GRAY) [default = test\n"); + printf(" subsampling (S = 444, 422, 440, 420, 411, 441, or GRAY) [default = test\n"); printf(" Grayscale, 4:2:0, 4:2:2, and 4:4:4 in sequence]\n"); printf("-hflip, -vflip, -transpose, -transverse, -rot90, -rot180, -rot270 =\n"); printf(" Perform the specified lossless transform operation on the input image\n"); @@ -1122,6 +1122,7 @@ int main(int argc, char *argv[]) case 440: subsamp = TJSAMP_440; break; case 420: subsamp = TJSAMP_420; break; case 411: subsamp = TJSAMP_411; break; + case 441: subsamp = TJSAMP_441; break; default: usage(argv[0]); } } diff --git a/tjexample.c b/tjexample.c index 1c6c7a15d..cf812db5d 100644 --- a/tjexample.c +++ b/tjexample.c @@ -62,7 +62,7 @@ const char *subsampName[TJ_NUMSAMP] = { - "4:4:4", "4:2:2", "4:2:0", "Grayscale", "4:4:0", "4:1:1" + "4:4:4", "4:2:2", "4:2:0", "Grayscale", "4:4:0", "4:1:1", "4:4:1" }; const char *colorspaceName[TJ_NUMCS] = { diff --git a/tjunittest.c b/tjunittest.c index 4e0a969ae..bbe44bb17 100644 --- a/tjunittest.c +++ b/tjunittest.c @@ -82,10 +82,10 @@ static void usage(char *progName) } const char *subNameLong[TJ_NUMSAMP] = { - "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1" + "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1", "4:4:1" }; const char *subName[TJ_NUMSAMP] = { - "444", "422", "420", "GRAY", "440", "411" + "444", "422", "420", "GRAY", "440", "411", "441" }; const char *pixFormatStr[TJ_NUMPF] = { @@ -575,9 +575,9 @@ static void decompTest(tjhandle handle, unsigned char *jpegBuf, for (i = 0; i < n; i++) { if (subsamp == TJSAMP_444 || subsamp == TJSAMP_GRAY || - (subsamp == TJSAMP_411 && sf[i].num == 1 && + ((subsamp == TJSAMP_411 || subsamp == TJSAMP_441) && sf[i].num == 1 && (sf[i].denom == 2 || sf[i].denom == 1)) || - (subsamp != TJSAMP_411 && sf[i].num == 1 && + (subsamp != TJSAMP_411 && subsamp != TJSAMP_441 && sf[i].num == 1 && (sf[i].denom == 4 || sf[i].denom == 2 || sf[i].denom == 1))) _decompTest(handle, jpegBuf, jpegSize, w, h, pf, basename, subsamp, sf[i]); @@ -617,7 +617,8 @@ static void doTest(int w, int h, const int *formats, int nformats, int subsamp, } else { TRY_TJ(chandle, tj3Set(chandle, TJPARAM_QUALITY, 100)); if (subsamp == TJSAMP_422 || subsamp == TJSAMP_420 || - subsamp == TJSAMP_440 || subsamp == TJSAMP_411) + subsamp == TJSAMP_440 || subsamp == TJSAMP_411 || + subsamp == TJSAMP_441) TRY_TJ(dhandle, tj3Set(dhandle, TJPARAM_FASTUPSAMPLE, 1)); } TRY_TJ(chandle, tj3Set(chandle, TJPARAM_SUBSAMP, subsamp)); @@ -1186,6 +1187,8 @@ int main(int argc, char *argv[]) doTest(39, 41, _4sampleFormats, num4bf, TJSAMP_440, "test"); doTest(41, 35, _3sampleFormats, 2, TJSAMP_411, "test"); doTest(35, 39, _4sampleFormats, num4bf, TJSAMP_411, "test"); + doTest(39, 41, _3sampleFormats, 2, TJSAMP_441, "test"); + doTest(41, 35, _4sampleFormats, num4bf, TJSAMP_441, "test"); } doTest(39, 41, _onlyGray, 1, TJSAMP_GRAY, "test"); if (!lossless) { @@ -1200,6 +1203,7 @@ int main(int argc, char *argv[]) doTest(48, 48, _onlyRGB, 1, TJSAMP_420, "test_yuv0"); doTest(48, 48, _onlyRGB, 1, TJSAMP_440, "test_yuv0"); doTest(48, 48, _onlyRGB, 1, TJSAMP_411, "test_yuv0"); + doTest(48, 48, _onlyRGB, 1, TJSAMP_441, "test_yuv0"); doTest(48, 48, _onlyRGB, 1, TJSAMP_GRAY, "test_yuv0"); doTest(48, 48, _onlyGray, 1, TJSAMP_GRAY, "test_yuv0"); } diff --git a/turbojpeg.h b/turbojpeg.h index ed2f488dd..f392adc41 100644 --- a/turbojpeg.h +++ b/turbojpeg.h @@ -58,16 +58,16 @@ * image. The width and height of each plane are determined by the image * width, height, and level of chrominance subsampling. The luminance plane * width is the image width padded to the nearest multiple of the horizontal - * subsampling factor (1 in the case of 4:4:4, grayscale, or 4:4:0; 2 in the - * case of 4:2:2 or 4:2:0; 4 in the case of 4:1:1.) Similarly, the luminance - * plane height is the image height padded to the nearest multiple of the - * vertical subsampling factor (1 in the case of 4:4:4, 4:2:2, grayscale, or - * 4:1:1; 2 in the case of 4:2:0 or 4:4:0.) This is irrespective of any - * additional padding that may be specified as an argument to the various YUV - * functions. The chrominance plane width is equal to the luminance plane - * width divided by the horizontal subsampling factor, and the chrominance - * plane height is equal to the luminance plane height divided by the vertical - * subsampling factor. + * subsampling factor (1 in the case of 4:4:4, grayscale, 4:4:0, or 4:4:1; 2 in + * the case of 4:2:2 or 4:2:0; 4 in the case of 4:1:1.) Similarly, the + * luminance plane height is the image height padded to the nearest multiple of + * the vertical subsampling factor (1 in the case of 4:4:4, 4:2:2, grayscale, + * or 4:1:1; 2 in the case of 4:2:0 or 4:4:0; 4 in the case of 4:4:1.) This is + * irrespective of any additional padding that may be specified as an argument + * to the various YUV functions. The chrominance plane width is equal to the + * luminance plane width divided by the horizontal subsampling factor, and the + * chrominance plane height is equal to the luminance plane height divided by + * the vertical subsampling factor. * * For example, if the source image is 35 x 35 pixels and 4:2:2 subsampling is * used, then the luminance plane would be 36 x 35 bytes, and each of the @@ -107,7 +107,7 @@ enum TJINIT { /** * The number of chrominance subsampling options */ -#define TJ_NUMSAMP 6 +#define TJ_NUMSAMP 7 /** * Chrominance subsampling options. @@ -158,6 +158,18 @@ enum TJSAMP { * @note 4:1:1 subsampling is not fully accelerated in libjpeg-turbo. */ TJSAMP_411, + /** + * 4:4:1 chrominance subsampling. The JPEG or YUV image will contain one + * chrominance component for every 1x4 block of pixels in the source image. + * JPEG images compressed with 4:4:1 subsampling will be almost exactly the + * same size as those compressed with 4:2:0 subsampling, and in the + * aggregate, both subsampling methods produce approximately the same + * perceptual quality. However, 4:4:1 is better able to reproduce sharp + * vertical features. + * + * @note 4:4:1 subsampling is not fully accelerated in libjpeg-turbo. + */ + TJSAMP_441, /** * Unknown subsampling. The JPEG image uses an unusual type of chrominance * subsampling. Such images can be decompressed into packed-pixel images, @@ -177,8 +189,9 @@ enum TJSAMP { * - 8x16 for 4:4:0 * - 16x16 for 4:2:0 * - 32x8 for 4:1:1 + * - 8x32 for 4:4:1 */ -static const int tjMCUWidth[TJ_NUMSAMP] = { 8, 16, 16, 8, 8, 32 }; +static const int tjMCUWidth[TJ_NUMSAMP] = { 8, 16, 16, 8, 8, 32, 8 }; /** * MCU block height (in pixels) for a given level of chrominance subsampling. @@ -188,8 +201,9 @@ static const int tjMCUWidth[TJ_NUMSAMP] = { 8, 16, 16, 8, 8, 32 }; * - 8x16 for 4:4:0 * - 16x16 for 4:2:0 * - 32x8 for 4:1:1 + * - 8x32 for 4:4:1 */ -static const int tjMCUHeight[TJ_NUMSAMP] = { 8, 8, 16, 8, 16, 8 }; +static const int tjMCUHeight[TJ_NUMSAMP] = { 8, 8, 16, 8, 16, 8, 32 }; /** From 386ec0abc7768922b0c51c3dc2e6efaff6278174 Mon Sep 17 00:00:00 2001 From: DRC Date: Mon, 13 Mar 2023 13:11:19 -0500 Subject: [PATCH 112/162] TJBench: w/JPEG input imgs, set min tile= MCU size When -tile is used with a JPEG input image, TJBench generates the tiles using lossless cropping, which will fail if the cropping region doesn't align with an MCU boundary. Furthermore, there is no reason to avoid 8x8 tiles when decompressing 4:4:4 or grayscale JPEG images. --- java/TJBench.java | 5 +++-- tjbench.c | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/java/TJBench.java b/java/TJBench.java index fad7fd69a..86cdd49c6 100644 --- a/java/TJBench.java +++ b/java/TJBench.java @@ -530,7 +530,7 @@ static void decompTest(String fileName) throws Exception { // Original image int w = 0, h = 0, ntilesw = 1, ntilesh = 1, subsamp = -1, cs = -1; // Transformed image - int tw, th, ttilew, ttileh, tntilesw, tntilesh, tsubsamp; + int minTile, tw, th, ttilew, ttileh, tntilesw, tntilesh, tsubsamp; FileInputStream fis = new FileInputStream(fileName); if (fis.getChannel().size() > (long)Integer.MAX_VALUE) @@ -593,7 +593,8 @@ static void decompTest(String fileName) throws Exception { precision, formatName(subsamp, cs), PIXFORMATSTR[pf], bottomUp ? "Bottom-up" : "Top-down"); - for (int tilew = doTile ? 16 : w, tileh = doTile ? 16 : h; ; + minTile = Math.max(TJ.getMCUWidth(subsamp), TJ.getMCUHeight(subsamp)); + for (int tilew = doTile ? minTile : w, tileh = doTile ? minTile : h; ; tilew *= 2, tileh *= 2) { if (tilew > w) tilew = w; diff --git a/tjbench.c b/tjbench.c index e25dd79a5..8d440f37a 100644 --- a/tjbench.c +++ b/tjbench.c @@ -617,8 +617,8 @@ static int decompTest(char *fileName) int ps = tjPixelSize[pf], tile, row, col, i, iter, retval = 0, decompsrc = 0; char *temp = NULL, tempStr[80], tempStr2[80]; /* Original image */ - int w = 0, h = 0, tilew, tileh, ntilesw = 1, ntilesh = 1, subsamp = -1, - cs = -1; + int w = 0, h = 0, minTile, tilew, tileh, ntilesw = 1, ntilesh = 1, + subsamp = -1, cs = -1; /* Transformed image */ int tw, th, ttilew, ttileh, tntilesw, tntilesh, tsubsamp; @@ -697,7 +697,8 @@ static int decompTest(char *fileName) formatName(subsamp, cs, tempStr), pixFormatStr[pf], bottomUp ? "Bottom-up" : "Top-down"); - for (tilew = doTile ? 16 : w, tileh = doTile ? 16 : h; ; + minTile = max(tjMCUWidth[subsamp], tjMCUHeight[subsamp]); + for (tilew = doTile ? minTile : w, tileh = doTile ? minTile : h; ; tilew *= 2, tileh *= 2) { if (tilew > w) tilew = w; if (tileh > h) tileh = h; From 9d2f189c298ad1fd57be9905a75de2be5d6777dc Mon Sep 17 00:00:00 2001 From: DRC Date: Mon, 13 Mar 2023 16:36:04 -0500 Subject: [PATCH 113/162] TJBench: Change subsamp for transposed 4:*:1 img If we have transformed a 4:1:1 or 4:4:1 JPEG input image in such a way that the horizontal and vertical dimensions are transposed, then we need to change the subsampling type that is passed to the decomp() function. Otherwise, tj3YUVBufSize() may return an incorrect value. (oversight from fc881ebb211b2ba6292d35691a946e1f9c1374b4) --- java/TJBench.java | 4 ++++ tjbench.c | 2 ++ 2 files changed, 6 insertions(+) diff --git a/java/TJBench.java b/java/TJBench.java index 86cdd49c6..23cd88523 100644 --- a/java/TJBench.java +++ b/java/TJBench.java @@ -656,6 +656,10 @@ precision, formatName(subsamp, cs), PIXFORMATSTR[pf], tsubsamp = TJ.SAMP_440; else if (tsubsamp == TJ.SAMP_440) tsubsamp = TJ.SAMP_422; + else if (tsubsamp == TJ.SAMP_411) + tsubsamp = TJ.SAMP_441; + else if (tsubsamp == TJ.SAMP_441) + tsubsamp = TJ.SAMP_411; } TJTransform[] t = new TJTransform[tntilesw * tntilesh]; diff --git a/tjbench.c b/tjbench.c index 8d440f37a..1d601ff96 100644 --- a/tjbench.c +++ b/tjbench.c @@ -769,6 +769,8 @@ static int decompTest(char *fileName) xformOp == TJXOP_ROT90 || xformOp == TJXOP_ROT270) { if (tsubsamp == TJSAMP_422) tsubsamp = TJSAMP_440; else if (tsubsamp == TJSAMP_440) tsubsamp = TJSAMP_422; + else if (tsubsamp == TJSAMP_411) tsubsamp = TJSAMP_441; + else if (tsubsamp == TJSAMP_441) tsubsamp = TJSAMP_411; } for (row = 0, tile = 0; row < tntilesh; row++) { From cc8c6d3667af2d49125ba933f8f8bbf9deac1e16 Mon Sep 17 00:00:00 2001 From: DRC Date: Mon, 13 Mar 2023 14:10:48 -0500 Subject: [PATCH 114/162] tjbenchtest: Test all subsampling types --- tjbenchtest.in | 118 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 90 insertions(+), 28 deletions(-) diff --git a/tjbenchtest.in b/tjbenchtest.in index c0f5b891e..4f5a9729e 100755 --- a/tjbenchtest.in +++ b/tjbenchtest.in @@ -37,9 +37,9 @@ JAVAARG= LOSSLSARG= LOSSLSPSV= TJQUAL=95 -h1SUBSAMP="GRAY 444" -h2SUBSAMP="420 422" -ALLSUBSAMP="GRAY 420 422 444" +x1SUBSAMP="444 GRAY" +x24SUBSAMP="422 440 420 411 441" +ALLSUBSAMP="444 422 440 420 411 441 GRAY" PRECISION=8 if [ "$EXT" = "bmp" ]; then BMPARG=-bmp; fi @@ -101,8 +101,8 @@ while [ $# -gt 0 ]; do LOSSLSARG="-lossless" LOSSLSPSV=4 TJQUAL=4 - h1SUBSAMP=444 - h2SUBSAMP=444 + x1SUBSAMP=444 + x24SUBSAMP=444 ALLSUBSAMP=444 ;; -precision) @@ -141,18 +141,24 @@ for image in $IMAGES; do cp $IMGDIR/$image $OUTDIR basename=`basename $image .${EXT}` runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct fast $ENTROPYARG $LOSSLSARG $LOSSLSPSV -grayscale -outfile $OUTDIR/${basename}_GRAY_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct fast $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 1x4 -outfile $OUTDIR/${basename}_441_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct fast $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 4x1 -outfile $OUTDIR/${basename}_411_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct fast $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 2x2 -outfile $OUTDIR/${basename}_420_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct fast $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 1x2 -outfile $OUTDIR/${basename}_440_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct fast $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 2x1 -outfile $OUTDIR/${basename}_422_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct fast $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 1x1 -outfile $OUTDIR/${basename}_444_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct int $ENTROPYARG $LOSSLSARG $LOSSLSPSV -grayscale -outfile $OUTDIR/${basename}_GRAY_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct int $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 1x4 -outfile $OUTDIR/${basename}_441_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct int $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 4x1 -outfile $OUTDIR/${basename}_411_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct int $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 2x2 -outfile $OUTDIR/${basename}_420_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct int $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 1x2 -outfile $OUTDIR/${basename}_440_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct int $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 2x1 -outfile $OUTDIR/${basename}_422_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct int $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 1x1 -outfile $OUTDIR/${basename}_444_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} for samp in $ALLSUBSAMP; do runme $EXEDIR/djpeg -dct fast -rgb $NSARG $BMPARG -outfile $OUTDIR/${basename}_${samp}_fast_djpeg.${EXT} $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg runme $EXEDIR/djpeg -dct int -rgb $NSARG $BMPARG -outfile $OUTDIR/${basename}_${samp}_accurate_djpeg.${EXT} $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg done - for samp in $h2SUBSAMP; do + for samp in $x24SUBSAMP; do runme $EXEDIR/djpeg -dct fast -nosmooth $BMPARG -outfile $OUTDIR/${basename}_${samp}_fast_nosmooth_djpeg.${EXT} $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg runme $EXEDIR/djpeg -dct int -nosmooth $BMPARG -outfile $OUTDIR/${basename}_${samp}_accurate_nosmooth_djpeg.${EXT} $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg done @@ -164,6 +170,11 @@ for image in $IMAGES; do dctarg=-fastdct fi runme $TJBENCH $OUTDIR/$image $TJQUAL -precision $PRECISION -rgb -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + if [ "$LOSSLSARG" != "-lossless" ]; then + runme $TJBENCH $OUTDIR/$image $TJQUAL -precision $PRECISION -subsamp 440 -rgb -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + runme $TJBENCH $OUTDIR/$image $TJQUAL -precision $PRECISION -subsamp 411 -rgb -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + runme $TJBENCH $OUTDIR/$image $TJQUAL -precision $PRECISION -subsamp 441 -rgb -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + fi for samp in $ALLSUBSAMP; do if [ "$LOSSLSARG" = "-lossless" ]; then runme cmp $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}.jpg $OUTDIR/${basename}_${samp}_${dct}_cjpeg.jpg @@ -181,7 +192,7 @@ for image in $IMAGES; do # Tiled compression & decompression runme $TJBENCH $OUTDIR/$image $TJQUAL -precision $PRECISION -rgb -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG - for samp in $h1SUBSAMP; do + for samp in $x1SUBSAMP; do if [ $ALLOC = 1 ]; then if [ "$LOSSLSARG" = "-lossless" ]; then runme cmp $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}_full.${EXT} $OUTDIR/${basename}_${samp}_${dct}_djpeg.${EXT} @@ -192,13 +203,13 @@ for image in $IMAGES; do fi else if [ "$LOSSLSARG" = "-lossless" ]; then - for i in $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ + for i in $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}_[0-9]*x[0-9]*.${EXT} \ $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}_full.${EXT}; do runme cmp $i $OUTDIR/${basename}_${samp}_${dct}_djpeg.${EXT} rm $i done else - for i in $OUTDIR/${basename}_${samp}_Q${TJQUAL}_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ + for i in $OUTDIR/${basename}_${samp}_Q${TJQUAL}_[0-9]*x[0-9]*.${EXT} \ $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.${EXT}; do runme cmp $i $OUTDIR/${basename}_${samp}_${dct}_djpeg.${EXT} rm $i @@ -207,7 +218,12 @@ for image in $IMAGES; do fi done runme $TJBENCH $OUTDIR/$image $TJQUAL -precision $PRECISION -rgb -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG - for samp in $h2SUBSAMP; do + if [ "$LOSSLSARG" != "-lossless" ]; then + runme $TJBENCH $OUTDIR/$image $TJQUAL -precision $PRECISION -subsamp 440 -rgb -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + runme $TJBENCH $OUTDIR/$image $TJQUAL -precision $PRECISION -subsamp 411 -rgb -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + runme $TJBENCH $OUTDIR/$image $TJQUAL -precision $PRECISION -subsamp 441 -rgb -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + fi + for samp in $x24SUBSAMP; do if [ $ALLOC = 1 ]; then if [ "$LOSSLSARG" = "-lossless" ]; then runme cmp $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}_full.${EXT} $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.${EXT} @@ -218,14 +234,25 @@ for image in $IMAGES; do fi else if [ "$LOSSLSARG" = "-lossless" ]; then - for i in $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ + for i in $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}_[0-9]*x[0-9]*.${EXT} \ $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}_full.${EXT}; do runme cmp $i $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.${EXT} rm $i done else - for i in $OUTDIR/${basename}_${samp}_Q${TJQUAL}_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ + for i in $OUTDIR/${basename}_${samp}_Q${TJQUAL}_[0-9]*x[0-9]*.${EXT} \ $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.${EXT}; do + # If the tile size is smaller than the MCU size, then there will be + # edge artifacts at the tile boundaries, so the decompressed image + # will not be identical to the untiled decompressed image. + TILESIZE=$(basename $(echo $i | sed 's/.*_//g') .${EXT}) + if [ "$TILESIZE" = "8x8" ]; then + continue + fi + if [ "$TILESIZE" = "16x16" -a \ + \( "${samp}" = "411" -o "${samp}" = "441" \) ]; then + continue + fi runme cmp $i $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.${EXT} rm $i done @@ -235,27 +262,35 @@ for image in $IMAGES; do # Tiled decompression if [ "$LOSSLSARG" != "-lossless" ]; then - for samp in GRAY 444; do + for samp in $x1SUBSAMP; do runme $TJBENCH $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG if [ $ALLOC = 1 ]; then runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${dct}_djpeg.${EXT} rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} else - for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ + for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*x[0-9]*.${EXT} \ $OUTDIR/${basename}_${samp}_Q95_full.${EXT}; do runme cmp $i $OUTDIR/${basename}_${samp}_${dct}_djpeg.${EXT} rm $i done fi done - for samp in 420 422; do + for samp in $x24SUBSAMP; do runme $TJBENCH $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG if [ $ALLOC = 1 ]; then runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.${EXT} rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} else - for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ + for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*x[0-9]*.${EXT} \ $OUTDIR/${basename}_${samp}_Q95_full.${EXT}; do + TILESIZE=$(basename $(echo $i | sed 's/.*_//g') .${EXT}) + if [ "$TILESIZE" = "8x8" ]; then + continue + fi + if [ "$TILESIZE" = "16x16" -a \ + \( "${samp}" = "411" -o "${samp}" = "441" \) ]; then + continue + fi runme cmp $i $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.${EXT} rm $i done @@ -267,13 +302,23 @@ for image in $IMAGES; do # Partial decompression if [ "$LOSSLSARG" != "-lossless" -a "$YUVARG" != "-yuv" ]; then for samp in $ALLSUBSAMP; do - runme $EXEDIR/djpeg -rgb -crop 103x90+16+5 $NSARG -outfile $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg - runme $TJBENCH $OUTDIR/${basename}_${samp}_Q${TJQUAL}.jpg -crop 103x90+16+5 -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + CROPW8_8=103 + CROPL8_8=16 + CROPW7_8=91 + CROPL7_8=14 + if [ "${samp}" = "411" ]; then + CROPW8_8=87 + CROPL8_8=32 + CROPW7_8=77 + CROPL7_8=28 + fi + runme $EXEDIR/djpeg -rgb -crop ${CROPW8_8}x90+${CROPL8_8}+5 $NSARG -outfile $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg + runme $TJBENCH $OUTDIR/${basename}_${samp}_Q${TJQUAL}.jpg -crop ${CROPW8_8}x90+${CROPL8_8}+5 -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG runme cmp $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.ppm $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm rm $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.ppm $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm - runme $EXEDIR/djpeg -rgb -scale 7/8 -crop 91x81+14+3 $NSARG -outfile $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg - runme $TJBENCH $OUTDIR/${basename}_${samp}_Q${TJQUAL}.jpg -scale 7/8 -crop 91x81+14+3 -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + runme $EXEDIR/djpeg -rgb -scale 7/8 -crop ${CROPW7_8}x81+${CROPL7_8}+3 $NSARG -outfile $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg + runme $TJBENCH $OUTDIR/${basename}_${samp}_Q${TJQUAL}.jpg -scale 7/8 -crop ${CROPW7_8}x81+${CROPL7_8}+3 -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG runme cmp $OUTDIR/${basename}_${samp}_Q${TJQUAL}_7_8.ppm $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm rm $OUTDIR/${basename}_${samp}_Q${TJQUAL}_7_8.ppm $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm @@ -307,7 +352,7 @@ for image in $IMAGES; do # Transforms if [ "$LOSSLSARG" != "-lossless" ]; then - for samp in GRAY 420 422 444; do + for samp in $ALLSUBSAMP; do runme $EXEDIR/jpegtran -flip horizontal -trim -outfile $OUTDIR/${basename}_${samp}_hflip_jpegtran.jpg $OUTDIR/${basename}_${samp}_Q95.jpg runme $EXEDIR/jpegtran -flip vertical -trim -outfile $OUTDIR/${basename}_${samp}_vflip_jpegtran.jpg $OUTDIR/${basename}_${samp}_Q95.jpg runme $EXEDIR/jpegtran -transpose -trim -outfile $OUTDIR/${basename}_${samp}_transpose_jpegtran.jpg $OUTDIR/${basename}_${samp}_Q95.jpg @@ -317,29 +362,37 @@ for image in $IMAGES; do runme $EXEDIR/jpegtran -rotate 270 -trim -outfile $OUTDIR/${basename}_${samp}_rot270_jpegtran.jpg $OUTDIR/${basename}_${samp}_Q95.jpg done for xform in hflip vflip transpose transverse rot90 rot180 rot270; do - for samp in GRAY 444; do + for samp in $x1SUBSAMP; do runme $EXEDIR/djpeg -rgb $BMPARG -outfile $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg runme $TJBENCH $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -tile -quiet -benchtime 0.01 -warmup 0 $YUVARG $ALLOCARG $ENTROPYARG if [ $ALLOC = 1 ]; then runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} else - for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ + for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*x[0-9]*.${EXT} \ $OUTDIR/${basename}_${samp}_Q95_full.${EXT}; do runme cmp $i $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} rm $i done fi done - for samp in 420 422; do + for samp in $x24SUBSAMP; do runme $EXEDIR/djpeg -nosmooth -rgb $BMPARG -outfile $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg runme $TJBENCH $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample $YUVARG $ALLOCARG $ENTROPYARG if [ $ALLOC = 1 ]; then runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} else - for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ + for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*x[0-9]*.${EXT} \ $OUTDIR/${basename}_${samp}_Q95_full.${EXT}; do + TILESIZE=$(basename $(echo $i | sed 's/.*_//g') .${EXT}) + if [ "$TILESIZE" = "8x8" ]; then + continue + fi + if [ "$TILESIZE" = "16x16" -a \ + \( "${samp}" = "411" -o "${samp}" = "441" \) ]; then + continue + fi runme cmp $i $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} rm $i done @@ -349,14 +402,23 @@ for image in $IMAGES; do # Grayscale transform for xform in hflip vflip transpose transverse rot90 rot180 rot270; do - for samp in GRAY 444 422 420; do + for samp in $ALLSUBSAMP; do runme $TJBENCH $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -tile -quiet -benchtime 0.01 -warmup 0 -grayscale $YUVARG $ALLOCARG $ENTROPYARG if [ $ALLOC = 1 ]; then runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_GRAY_${xform}_jpegtran.${EXT} rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} else - for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ + for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*x[0-9]*.${EXT} \ $OUTDIR/${basename}_${samp}_Q95_full.${EXT}; do + TILESIZE=$(basename $(echo $i | sed 's/.*_//g') .${EXT}) + if [ "$TILESIZE" = "8x8" -a \ + "${samp}" != "444" -a "${samp}" != "GRAY" ]; then + continue + fi + if [ "$TILESIZE" = "16x16" -a \ + \( "${samp}" = "411" -o "${samp}" = "441" \) ]; then + continue + fi runme cmp $i $OUTDIR/${basename}_GRAY_${xform}_jpegtran.${EXT} rm $i done @@ -366,7 +428,7 @@ for image in $IMAGES; do # Transforms with scaling for xform in hflip vflip transpose transverse rot90 rot180 rot270; do - for samp in GRAY 444 422 420; do + for samp in $ALLSUBSAMP; do for scale in 2_1 15_8 7_4 13_8 3_2 11_8 5_4 9_8 7_8 3_4 5_8 1_2 3_8 1_4 1_8; do scalearg=`echo $scale | sed 's/\_/\//g'` runme $EXEDIR/djpeg -rgb -scale ${scalearg} $NSARG $BMPARG -outfile $OUTDIR/${basename}_${samp}_${xform}_${scale}_jpegtran.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg From 0d20aa15ced3b0bb98ff1b01cb12787177ac8493 Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 31 Mar 2023 10:53:29 -0500 Subject: [PATCH 115/162] TJBench: Require known subsamp type w/tiled decomp (oversight from 386ec0abc7768922b0c51c3dc2e6efaff6278174) Tiled decompression will ultimately fail if the subsampling type of the JPEG input image is unknown, but the C version of TJBench needs to fail earlier in order to avoid using -1 (TJSAMP_UNKNOWN) as an array index for tjMCUWidth[]/tjMCUHeight[]. The Java version now fails earlier as well, although there is no benefit to that other than making the error message less cryptic. --- java/TJBench.java | 8 ++++++-- tjbench.c | 9 +++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/java/TJBench.java b/java/TJBench.java index 23cd88523..e653add8f 100644 --- a/java/TJBench.java +++ b/java/TJBench.java @@ -530,7 +530,7 @@ static void decompTest(String fileName) throws Exception { // Original image int w = 0, h = 0, ntilesw = 1, ntilesh = 1, subsamp = -1, cs = -1; // Transformed image - int minTile, tw, th, ttilew, ttileh, tntilesw, tntilesh, tsubsamp; + int minTile = 16, tw, th, ttilew, ttileh, tntilesw, tntilesh, tsubsamp; FileInputStream fis = new FileInputStream(fileName); if (fis.getChannel().size() > (long)Integer.MAX_VALUE) @@ -593,7 +593,11 @@ static void decompTest(String fileName) throws Exception { precision, formatName(subsamp, cs), PIXFORMATSTR[pf], bottomUp ? "Bottom-up" : "Top-down"); - minTile = Math.max(TJ.getMCUWidth(subsamp), TJ.getMCUHeight(subsamp)); + if (doTile) { + if (subsamp == TJ.SAMP_UNKNOWN) + throw new Exception("Could not determine subsampling level of JPEG image"); + minTile = Math.max(TJ.getMCUWidth(subsamp), TJ.getMCUHeight(subsamp)); + } for (int tilew = doTile ? minTile : w, tileh = doTile ? minTile : h; ; tilew *= 2, tileh *= 2) { if (tilew > w) diff --git a/tjbench.c b/tjbench.c index 1d601ff96..db08f060a 100644 --- a/tjbench.c +++ b/tjbench.c @@ -617,7 +617,7 @@ static int decompTest(char *fileName) int ps = tjPixelSize[pf], tile, row, col, i, iter, retval = 0, decompsrc = 0; char *temp = NULL, tempStr[80], tempStr2[80]; /* Original image */ - int w = 0, h = 0, minTile, tilew, tileh, ntilesw = 1, ntilesh = 1, + int w = 0, h = 0, minTile = 16, tilew, tileh, ntilesw = 1, ntilesh = 1, subsamp = -1, cs = -1; /* Transformed image */ int tw, th, ttilew, ttileh, tntilesw, tntilesh, tsubsamp; @@ -697,7 +697,12 @@ static int decompTest(char *fileName) formatName(subsamp, cs, tempStr), pixFormatStr[pf], bottomUp ? "Bottom-up" : "Top-down"); - minTile = max(tjMCUWidth[subsamp], tjMCUHeight[subsamp]); + if (doTile) { + if (subsamp == TJSAMP_UNKNOWN) + THROW("transforming", + "Could not determine subsampling level of JPEG image"); + minTile = max(tjMCUWidth[subsamp], tjMCUHeight[subsamp]); + } for (tilew = doTile ? minTile : w, tileh = doTile ? minTile : h; ; tilew *= 2, tileh *= 2) { if (tilew > w) tilew = w; From d491094b41220a3eb5694311f11b855ad1300c2e Mon Sep 17 00:00:00 2001 From: DRC Date: Mon, 3 Apr 2023 12:31:40 -0500 Subject: [PATCH 116/162] Build separate static/shared jpeg12/16 obj libs If PIC isn't enabled for the entire build (using CMAKE_POSITION_INDEPENDENT_CODE), then we need to enable it for any of the objects in the libjpeg-turbo shared libraries. Ideally, however, we don't want to enable PIC for any of the objects in the libjpeg-turbo static libraries, unless CMAKE_POSITION_INDEPENDENT_CODE is set. Thus, we need to build separate static and shared jpeg12 and jpeg16 object libraries. Fixes #684 --- CMakeLists.txt | 77 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 31 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f4681150..6bfcba1ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -562,14 +562,17 @@ if(CMAKE_EXECUTABLE_SUFFIX_TMP) endif() message(STATUS "CMAKE_EXECUTABLE_SUFFIX = ${CMAKE_EXECUTABLE_SUFFIX}") -set(JPEG_SOURCES jcapimin.c jcapistd.c jccoefct.c jccolor.c jcdctmgr.c - jcdiffct.c jchuff.c jcicc.c jcinit.c jclhuff.c jclossls.c jcmainct.c - jcmarker.c jcmaster.c jcomapi.c jcparam.c jcphuff.c jcprepct.c jcsample.c - jctrans.c jdapimin.c jdapistd.c jdatadst.c jdatasrc.c jdcoefct.c jdcolor.c - jddctmgr.c jddiffct.c jdhuff.c jdicc.c jdinput.c jdlhuff.c jdlossls.c - jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdphuff.c jdpostct.c jdsample.c - jdtrans.c jerror.c jfdctflt.c jfdctfst.c jfdctint.c jidctflt.c jidctfst.c - jidctint.c jidctred.c jquant1.c jquant2.c jutils.c jmemmgr.c jmemnobs.c) +set(JPEG16_SOURCES jcapistd.c jccolor.c jcdiffct.c jclossls.c jcmainct.c + jcprepct.c jcsample.c jdapistd.c jdcolor.c jddiffct.c jdlossls.c jdmainct.c + jdpostct.c jdsample.c jquant1.c jquant2.c jutils.c) +set(JPEG12_SOURCES ${JPEG16_SOURCES} jccoefct.c jcdctmgr.c jdcoefct.c + jddctmgr.c jdmerge.c jdpostct.c jfdctfst.c jfdctint.c jidctflt.c jidctfst.c + jidctint.c jidctred.c) +set(JPEG_SOURCES ${JPEG12_SOURCES} jcapimin.c jchuff.c jcicc.c jcinit.c + jclhuff.c jcmarker.c jcmaster.c jcomapi.c jcparam.c jcphuff.c jctrans.c + jdapimin.c jdatadst.c jdatasrc.c jdhuff.c jdicc.c jdinput.c jdlhuff.c + jdmarker.c jdmaster.c jdphuff.c jdtrans.c jerror.c jfdctflt.c jmemmgr.c + jmemnobs.c) if(WITH_ARITH_ENC OR WITH_ARITH_DEC) set(JPEG_SOURCES ${JPEG_SOURCES} jaricom.c) @@ -583,19 +586,6 @@ if(WITH_ARITH_DEC) set(JPEG_SOURCES ${JPEG_SOURCES} jdarith.c) endif() -# Compile a separate version of these source files with 12-bit and 16-bit data -# precision. -add_library(jpeg12 OBJECT jcapistd.c jccoefct.c jccolor.c jcdctmgr.c jcdiffct.c - jclossls.c jcmainct.c jcprepct.c jcsample.c jdapistd.c jdcoefct.c jdcolor.c - jddctmgr.c jddiffct.c jdlossls.c jdmainct.c jdmerge.c jdpostct.c jdsample.c - jfdctfst.c jfdctint.c jidctflt.c jidctfst.c jidctint.c jidctred.c jquant1.c - jquant2.c jutils.c) -set_property(TARGET jpeg12 PROPERTY COMPILE_FLAGS "-DBITS_IN_JSAMPLE=12") -add_library(jpeg16 OBJECT jcapistd.c jccolor.c jcdiffct.c jclossls.c jcmainct.c - jcprepct.c jcsample.c jdapistd.c jdcolor.c jddiffct.c jdlossls.c jdmainct.c - jdpostct.c jdsample.c jquant1.c jquant2.c jutils.c) -set_property(TARGET jpeg16 PROPERTY COMPILE_FLAGS "-DBITS_IN_JSAMPLE=16") - if(WITH_SIMD) add_subdirectory(simd) if(NEON_INTRINSICS) @@ -623,24 +613,35 @@ if(WITH_JAVA) endif() if(ENABLE_SHARED) + # Compile a separate version of these source files with 12-bit and 16-bit + # data precision. + add_library(jpeg12 OBJECT ${JPEG12_SOURCES}) + set_property(TARGET jpeg12 PROPERTY COMPILE_FLAGS "-DBITS_IN_JSAMPLE=12") + set_target_properties(jpeg12 PROPERTIES POSITION_INDEPENDENT_CODE 1) + add_library(jpeg16 OBJECT ${JPEG16_SOURCES}) + set_property(TARGET jpeg16 PROPERTY COMPILE_FLAGS "-DBITS_IN_JSAMPLE=16") + set_target_properties(jpeg16 PROPERTIES POSITION_INDEPENDENT_CODE 1) add_subdirectory(sharedlib) endif() if(ENABLE_STATIC) + # Compile a separate version of these source files with 12-bit and 16-bit + # data precision. + add_library(jpeg12-static OBJECT ${JPEG12_SOURCES}) + set_property(TARGET jpeg12-static PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=12") + add_library(jpeg16-static OBJECT ${JPEG16_SOURCES}) + set_property(TARGET jpeg16-static PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=16") add_library(jpeg-static STATIC ${JPEG_SOURCES} ${SIMD_TARGET_OBJECTS} - ${SIMD_OBJS} $ $) + ${SIMD_OBJS} $ + $) if(NOT MSVC) set_target_properties(jpeg-static PROPERTIES OUTPUT_NAME jpeg) endif() endif() if(WITH_TURBOJPEG) - add_library(turbojpeg12 OBJECT rdppm.c wrppm.c) - set_property(TARGET turbojpeg12 PROPERTY COMPILE_FLAGS - "-DBITS_IN_JSAMPLE=12 -DPPM_SUPPORTED") - add_library(turbojpeg16 OBJECT rdppm.c wrppm.c) - set_property(TARGET turbojpeg16 PROPERTY COMPILE_FLAGS - "-DBITS_IN_JSAMPLE=16 -DPPM_SUPPORTED") if(ENABLE_SHARED) set(TURBOJPEG_SOURCES ${JPEG_SOURCES} ${SIMD_TARGET_OBJECTS} ${SIMD_OBJS} turbojpeg.c transupp.c jdatadst-tj.c jdatasrc-tj.c rdbmp.c rdppm.c @@ -657,6 +658,14 @@ if(WITH_TURBOJPEG) set(TURBOJPEG_SOURCES ${TURBOJPEG_SOURCES} ${CMAKE_BINARY_DIR}/win/turbojpeg.rc) endif() + add_library(turbojpeg12 OBJECT rdppm.c wrppm.c) + set_property(TARGET turbojpeg12 PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=12 -DPPM_SUPPORTED") + set_target_properties(turbojpeg12 PROPERTIES POSITION_INDEPENDENT_CODE 1) + add_library(turbojpeg16 OBJECT rdppm.c wrppm.c) + set_property(TARGET turbojpeg16 PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=16 -DPPM_SUPPORTED") + set_target_properties(turbojpeg16 PROPERTIES POSITION_INDEPENDENT_CODE 1) add_library(turbojpeg SHARED ${TURBOJPEG_SOURCES} $ $) set_property(TARGET turbojpeg PROPERTY COMPILE_FLAGS @@ -695,11 +704,17 @@ if(WITH_TURBOJPEG) endif() if(ENABLE_STATIC) + add_library(turbojpeg12-static OBJECT rdppm.c wrppm.c) + set_property(TARGET turbojpeg12-static PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=12 -DPPM_SUPPORTED") + add_library(turbojpeg16-static OBJECT rdppm.c wrppm.c) + set_property(TARGET turbojpeg16-static PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=16 -DPPM_SUPPORTED") add_library(turbojpeg-static STATIC ${JPEG_SOURCES} ${SIMD_TARGET_OBJECTS} ${SIMD_OBJS} turbojpeg.c transupp.c jdatadst-tj.c jdatasrc-tj.c rdbmp.c - rdppm.c wrbmp.c wrppm.c $ - $ $ - $) + rdppm.c wrbmp.c wrppm.c $ + $ $ + $) set_property(TARGET turbojpeg-static PROPERTY COMPILE_FLAGS "-DBMP_SUPPORTED -DPPM_SUPPORTED") if(NOT MSVC) From 3f43c6a31080847876cbbca347e9be9df215dba9 Mon Sep 17 00:00:00 2001 From: DRC Date: Tue, 4 Apr 2023 13:27:11 -0500 Subject: [PATCH 117/162] jdlossls.c: Code formatting tweak --- jdlossls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdlossls.c b/jdlossls.c index f03164e49..4d15e6bba 100644 --- a/jdlossls.c +++ b/jdlossls.c @@ -53,7 +53,7 @@ * * The reconstructed sample is supposed to be calculated modulo 2^16, so we * logically AND the result with 0xFFFF. -*/ + */ #define UNDIFFERENCE_1D(INITIAL_PREDICTOR) \ int Ra; \ From 9f756bc67a84d4566bf74a0c2432aa55da404021 Mon Sep 17 00:00:00 2001 From: DRC Date: Tue, 4 Apr 2023 13:53:21 -0500 Subject: [PATCH 118/162] Lossless decomp: Range-limit 12-bit samples 12-bit is the only data precision for which the range of the sample data type exceeds the valid sample range, so it is possible to craft a 12-bit lossless JPEG image that contains out-of-range 12-bit samples. Attempting to decompress such an image using color quantization or merged upsampling (NOTE: libjpeg-turbo cannot generate YCbCr or subsampled lossless JPEG images, but it can decompress them) caused segfaults or buffer overruns when those algorithms attempted to use the out-of-range sample values as array indices. This commit modifies the lossless decompressor so that it range-limits the output of the scaler when using 12-bit samples. Fixes #670 Fixes #672 Fixes #673 Fixes #674 Fixes #675 Fixes #676 Fixes #677 Fixes #678 Fixes #679 Fixes #681 Fixes #683 --- ChangeLog.md | 7 +++++++ jdlossls.c | 14 +++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index 93b08061e..de8e45dfb 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -8,6 +8,13 @@ subsampling, which allows losslessly transposed or rotated 4:1:1 JPEG images to be losslessly cropped, partially decompressed, or decompressed to planar YUV images. +2. Fixed various segfaults and buffer overruns that occurred when attempting to +decompress various specially-crafted malformed 12-bit-per-component lossless +JPEG images. These issues were caused by out-of-range sample values that were +not range-limited before being used as array indices. The issues were specific +to 12-bit data precision, since that is the only data precision for which the +range of the sample data type exceeds the valid sample range. + 2.1.91 (3.0 beta2) ================== diff --git a/jdlossls.c b/jdlossls.c index 4d15e6bba..cfdca7e3a 100644 --- a/jdlossls.c +++ b/jdlossls.c @@ -6,7 +6,7 @@ * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: - * Copyright (C) 2022, D. R. Commander. + * Copyright (C) 2022-2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -217,7 +217,15 @@ simple_upscale(j_decompress_ptr cinfo, JDIFFROW diff_buf, _JSAMPROW output_buf, JDIMENSION width) { do { +#if BITS_IN_JSAMPLE == 12 + /* 12-bit is the only data precision for which the range of the sample data + * type exceeds the valid sample range. Thus, we need to range-limit the + * samples, because other algorithms may try to use them as array indices. + */ + *output_buf++ = (_JSAMPLE)((*diff_buf++ << cinfo->Al) & 0xFFF); +#else *output_buf++ = (_JSAMPLE)(*diff_buf++ << cinfo->Al); +#endif } while (--width); } @@ -226,7 +234,11 @@ noscale(j_decompress_ptr cinfo, JDIFFROW diff_buf, _JSAMPROW output_buf, JDIMENSION width) { do { +#if BITS_IN_JSAMPLE == 12 + *output_buf++ = (_JSAMPLE)((*diff_buf++) & 0xFFF); +#else *output_buf++ = (_JSAMPLE)(*diff_buf++); +#endif } while (--width); } From 62590d428b8c059a4a469de28f36d199f9392192 Mon Sep 17 00:00:00 2001 From: DRC Date: Tue, 4 Apr 2023 19:06:20 -0500 Subject: [PATCH 119/162] Decomp: Don't enable 2-pass color quant w/ RGB565 The 2-pass color quantization algorithm assumes 3-sample pixels. RGB565 is the only 3-component colorspace that doesn't have 3-sample pixels, so we need to treat it as a special case when determining whether to enable 2-pass color quantization. Otherwise, attempting to initialize 2-pass color quantization with an RGB565 output buffer could cause prescan_quantize() to read from uninitialized memory and subsequently underflow/overflow the histogram array. djpeg is supposed to fail gracefully if both -rgb565 and -colors are specified, because none of its destination managers (image writers) support color quantization with RGB565. However, prescan_quantize() was called before that could occur. It is possible but very unlikely that these issues could have been reproduced in applications other than djpeg. The issues involve the use of two features (12-bit precision and RGB565) that are incompatible, and they also involve the use of two rarely-used legacy features (RGB565 and color quantization) that don't make much sense when combined. Fixes #668 Fixes #671 Fixes #680 --- ChangeLog.md | 5 +++++ jdmaster.c | 5 +++-- jquant2.c | 5 +++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index de8e45dfb..4a246a782 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -15,6 +15,11 @@ not range-limited before being used as array indices. The issues were specific to 12-bit data precision, since that is the only data precision for which the range of the sample data type exceeds the valid sample range. +3. Fixed an oversight in 1.4 beta1[8] that caused various segfaults and buffer +overruns when attempting to decompress various specially-crafted malformed +12-bit-per-component JPEG images using djpeg with both color quantization and +RGB565 color conversion enabled. + 2.1.91 (3.0 beta2) ================== diff --git a/jdmaster.c b/jdmaster.c index abf7f51c4..947f20fae 100644 --- a/jdmaster.c +++ b/jdmaster.c @@ -7,7 +7,7 @@ * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: - * Copyright (C) 2009-2011, 2016, 2019, 2022, D. R. Commander. + * Copyright (C) 2009-2011, 2016, 2019, 2022-2023, D. R. Commander. * Copyright (C) 2013, Linaro Limited. * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg @@ -551,7 +551,8 @@ master_selection(j_decompress_ptr cinfo) if (cinfo->raw_data_out) ERREXIT(cinfo, JERR_NOTIMPL); /* 2-pass quantizer only works in 3-component color space. */ - if (cinfo->out_color_components != 3) { + if (cinfo->out_color_components != 3 || + cinfo->out_color_space == JCS_RGB565) { cinfo->enable_1pass_quant = TRUE; cinfo->enable_external_quant = FALSE; cinfo->enable_2pass_quant = FALSE; diff --git a/jquant2.c b/jquant2.c index f0ea63d79..5984ed8cc 100644 --- a/jquant2.c +++ b/jquant2.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2009, 2014-2015, 2020, 2022, D. R. Commander. + * Copyright (C) 2009, 2014-2015, 2020, 2022-2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -1238,7 +1238,8 @@ _jinit_2pass_quantizer(j_decompress_ptr cinfo) cquantize->error_limiter = NULL; /* Make sure jdmaster didn't give me a case I can't handle */ - if (cinfo->out_color_components != 3) + if (cinfo->out_color_components != 3 || + cinfo->out_color_space == JCS_RGB565) ERREXIT(cinfo, JERR_NOTIMPL); /* Allocate the histogram/inverse colormap storage */ From 3a53627306233013dcec61a90f0e9ed302ea5156 Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 6 Apr 2023 18:33:41 -0500 Subject: [PATCH 120/162] jpeg_crop_scanline: Fix calc w/sclg + 2x4,4x2 samp When computing the downsampled width for a particular component, jpeg_crop_scanline() needs to take into account the fact that the libjpeg code uses a combination of IDCT scaling and upsampling to implement 4x2 and 2x4 upsampling with certain decompression scaling factors. Failing to account for that led to incomplete upsampling of 4x2- or 2x4-subsampled components, which caused the color converter to read from uninitialized memory. With 12-bit data precision, this caused a buffer overrun or underrun and subsequent segfault if the uninitialized memory contained a value that was outside of the valid sample range (because the color converter uses the value as an array index.) Fixes #669 --- ChangeLog.md | 8 ++++++++ jdapistd.c | 10 ++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 4a246a782..bd01594ca 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -20,6 +20,14 @@ overruns when attempting to decompress various specially-crafted malformed 12-bit-per-component JPEG images using djpeg with both color quantization and RGB565 color conversion enabled. +4. Fixed an issue whereby `jpeg_crop_scanline()` sometimes miscalculated the +downsampled width for components with 4x2 or 2x4 subsampling factors if +decompression scaling was enabled. This caused the components to be upsampled +incompletely, which caused the color converter to read from uninitialized +memory. With 12-bit data precision, this caused a buffer overrun or underrun +and subsequent segfault if the sample value read from unitialized memory was +outside of the valid sample range. + 2.1.91 (3.0 beta2) ================== diff --git a/jdapistd.c b/jdapistd.c index 245ab2f7a..1f4492723 100644 --- a/jdapistd.c +++ b/jdapistd.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2010, 2015-2020, 2022, D. R. Commander. + * Copyright (C) 2010, 2015-2020, 2022-2023, D. R. Commander. * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -265,9 +265,11 @@ _jpeg_crop_scanline(j_decompress_ptr cinfo, JDIMENSION *xoffset, /* Set downsampled_width to the new output width. */ orig_downsampled_width = compptr->downsampled_width; compptr->downsampled_width = - (JDIMENSION)jdiv_round_up((long)(cinfo->output_width * - compptr->h_samp_factor), - (long)cinfo->max_h_samp_factor); + (JDIMENSION)jdiv_round_up((long)cinfo->output_width * + (long)(compptr->h_samp_factor * + compptr->_DCT_scaled_size), + (long)(cinfo->max_h_samp_factor * + cinfo->_min_DCT_scaled_size)); if (compptr->downsampled_width < 2 && orig_downsampled_width >= 2) reinit_upsampler = TRUE; From 36aaeebb55f702920fdd31a20707aadb2272ea35 Mon Sep 17 00:00:00 2001 From: DRC Date: Tue, 30 May 2023 17:46:58 -0400 Subject: [PATCH 121/162] ChangeLog.md: List CVE ID fixed by 9f756bc6 --- ChangeLog.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index bd01594ca..b9d387a7b 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -8,12 +8,13 @@ subsampling, which allows losslessly transposed or rotated 4:1:1 JPEG images to be losslessly cropped, partially decompressed, or decompressed to planar YUV images. -2. Fixed various segfaults and buffer overruns that occurred when attempting to -decompress various specially-crafted malformed 12-bit-per-component lossless -JPEG images. These issues were caused by out-of-range sample values that were -not range-limited before being used as array indices. The issues were specific -to 12-bit data precision, since that is the only data precision for which the -range of the sample data type exceeds the valid sample range. +2. Fixed various segfaults and buffer overruns (CVE-2023-2804) that occurred +when attempting to decompress various specially-crafted malformed +12-bit-per-component lossless JPEG images. These issues were caused by +out-of-range sample values that were not range-limited before being used as +array indices. The issues were specific to 12-bit data precision, since that +is the only data precision for which the range of the sample data type exceeds +the valid sample range. 3. Fixed an oversight in 1.4 beta1[8] that caused various segfaults and buffer overruns when attempting to decompress various specially-crafted malformed From 10693e64418341e0d60757b7ef403d3ee4d61fd1 Mon Sep 17 00:00:00 2001 From: DRC Date: Tue, 30 May 2023 18:22:50 -0400 Subject: [PATCH 122/162] GitHub: Add security policy --- .github/SECURITY.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .github/SECURITY.md diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 000000000..b0e92e59a --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,9 @@ +# Security Policy + +## Supported Versions + +Any branch/release series that is in the [Next-Gen, Active, Maintenance, or Extended support category](https://libjpeg-turbo.org/DeveloperInfo/Versioning) is eligible for security updates. + +## Reporting a Vulnerability + +To securely report vulnerabilities, [contact the project admin](https://libjpeg-turbo.org/About/Contact) using GPG-encrypted e-mail. From 4e7ff7b9223347c51ebbde43f0dd67f31738869e Mon Sep 17 00:00:00 2001 From: DRC Date: Wed, 31 May 2023 10:24:04 -0400 Subject: [PATCH 123/162] SECURITY.md: Wordsmithing and clarifications - Clarify that encrypted e-mail is optional. - Mention the new GitHub security advisory system. - Clarify that vulnerabilities against new features that are not yet in a Stable release series need not be reported securely. --- .github/SECURITY.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/SECURITY.md b/.github/SECURITY.md index b0e92e59a..0b8fc6b70 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -2,8 +2,21 @@ ## Supported Versions -Any branch/release series that is in the [Next-Gen, Active, Maintenance, or Extended support category](https://libjpeg-turbo.org/DeveloperInfo/Versioning) is eligible for security updates. +Fixes for security vulnerabilities are applied to any applicable branch/release +series that is in the +[Next-Gen, Active, Maintenance, or Extended support category](https://libjpeg-turbo.org/DeveloperInfo/Versioning). ## Reporting a Vulnerability -To securely report vulnerabilities, [contact the project admin](https://libjpeg-turbo.org/About/Contact) using GPG-encrypted e-mail. +Vulnerabilities can be reported in one of the following ways: + +- [E-mail the project admin](https://libjpeg-turbo.org/About/Contact). You can + optionally encrypt the e-mail using the provided public GPG key. +- Open a + [GitHub draft security advisory](https://github.com/libjpeg-turbo/libjpeg-turbo/security/advisories/new). +- [Alpha/Evolving, Beta, and Post-Beta release series](https://libjpeg-turbo.org/DeveloperInfo/Versioning) + are not expected to be free of bugs, so vulnerabilities that affect only + those release series (for example, vulnerabilities introduced by a new + feature that is not present in a Stable release series) can optionally be + reported using a + [GitHub bug report](https://github.com/libjpeg-turbo/libjpeg-turbo/issues/new/choose). From 2192560d74e6e6cf99dd05928885573be00a8208 Mon Sep 17 00:00:00 2001 From: DRC Date: Wed, 31 May 2023 13:02:42 -0400 Subject: [PATCH 124/162] Disallow merged upsampling with lossless decomp Colorspace conversion is explicitly not supported with lossless JPEG images. Merged upsampling implies YCbCr-to-RGB colorspace conversion, so allowing it with lossless decompression was an oversight. 9f756bc67a84d4566bf74a0c2432aa55da404021 eliminated interaction issues between the lossless decompressor and the merged upsampler related to out-of-range 12-bit samples, but referring to #690, other interaction issues apparently still exist. Such issues are likely, given the fact that the merged upsampler was never designed with lossless decompression in mind. This commit also extends the decompress fuzzer so that it catches the issue reported in #690. Fixes #690 Redundantly fixes #670 Redundantly fixes #675 --- fuzz/decompress.cc | 5 +++-- jdmaster.c | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/fuzz/decompress.cc b/fuzz/decompress.cc index 12886f13c..3db992214 100644 --- a/fuzz/decompress.cc +++ b/fuzz/decompress.cc @@ -76,9 +76,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) int64_t sum = 0; /* Test non-default decompression options on the first iteration. */ + tj3Set(handle, TJPARAM_BOTTOMUP, pfi == 0); + tj3Set(handle, TJPARAM_FASTUPSAMPLE, pfi == 0); + if (!tj3Get(handle, TJPARAM_LOSSLESS)) { - tj3Set(handle, TJPARAM_BOTTOMUP, pfi == 0); - tj3Set(handle, TJPARAM_FASTUPSAMPLE, pfi == 0); tj3Set(handle, TJPARAM_FASTDCT, pfi == 0); /* Test IDCT scaling on the second iteration. */ diff --git a/jdmaster.c b/jdmaster.c index 947f20fae..4b3852d9b 100644 --- a/jdmaster.c +++ b/jdmaster.c @@ -35,6 +35,9 @@ LOCAL(boolean) use_merged_upsample(j_decompress_ptr cinfo) { #ifdef UPSAMPLE_MERGING_SUPPORTED + /* Colorspace conversion is not supported with lossless JPEG images */ + if (cinfo->master->lossless) + return FALSE; /* Merging is the equivalent of plain box-filter upsampling */ if (cinfo->do_fancy_upsampling || cinfo->CCIR601_sampling) return FALSE; From 6b506ed3970b98893d8da4b6db72189f39e3ec69 Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 1 Jun 2023 13:11:14 -0400 Subject: [PATCH 125/162] tjexample.c: Prevent integer overflow Because width, height, and tjPixelSize[] are signed integers, signed integer overflow will occur if width * height * tjPixelSize[pixelFormat] > INT_MAX, which would cause an incorrect value to be passed to tj3Alloc(). This commit modifies tjexample.c in the following ways: - Implicitly promote width, height, and tjPixelSize[pixelFormat] to size_t before multiplying them. - Use malloc() rather than tj3Alloc() to allocate the uncompressed image buffer. (tj3Alloc() is only necessary for JPEG buffers that will potentially be reallocated by the TurboJPEG API library.) - If size_t is 32-bit, throw an error if width * height * tjPixelSize[pixelFormat] would overflow the data type. Since tjexample is not installed or packaged, the worst case for this issue was that a downstream application might interpret tjexample.c literally and introduce a similar overflow issue into its own code. However, it's worth noting that such issues could also be introduced when using malloc(). --- tjexample.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tjexample.c b/tjexample.c index cf812db5d..947ef2fed 100644 --- a/tjexample.c +++ b/tjexample.c @@ -338,7 +338,12 @@ int main(int argc, char **argv) outSubsamp = inSubsamp; pixelFormat = TJPF_BGRX; - if ((imgBuf = tj3Alloc(width * height * tjPixelSize[pixelFormat])) == NULL) + if ((unsigned long long)width * height * tjPixelSize[pixelFormat] > + (unsigned long long)((size_t)-1)) + THROW("allocating uncompressed image buffer", "Image is too large"); + if ((imgBuf = + (unsigned char *)malloc(sizeof(unsigned char) * width * height * + tjPixelSize[pixelFormat])) == NULL) THROW_UNIX("allocating uncompressed image buffer"); if (tj3Decompress8(tjInstance, jpegBuf, jpegSize, imgBuf, 0, From 0e9683c4c62eaa71614aa5918a4e4892741610c4 Mon Sep 17 00:00:00 2001 From: DRC Date: Mon, 12 Jun 2023 14:36:18 -0400 Subject: [PATCH 126/162] Bump version to 3.0.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6bfcba1ce..83b4103dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ if(CMAKE_EXECUTABLE_SUFFIX) endif() project(libjpeg-turbo C) -set(VERSION 2.1.92) +set(VERSION 3.0.0) set(COPYRIGHT_YEAR "1991-2023") string(REPLACE "." ";" VERSION_TRIPLET ${VERSION}) list(GET VERSION_TRIPLET 0 VERSION_MAJOR) From 65a85ce34e043d0cb02589ee88ade6759db6e056 Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 16 Jun 2023 11:16:08 -0400 Subject: [PATCH 127/162] GitHub: Fix x32 build 1f55ae7b0fa3acc348a630171617d0e56d922b68 accidentally overrode the value of CMAKE_C_FLAGS, thus eliminating the -mx32 flag that was necessary to enable x32. --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ef86ed505..d08887060 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -124,8 +124,8 @@ jobs: run: | mkdir build pushd build - cmake -G"Unix Makefiles" -DWITH_JPEG7=1 -DCMAKE_C_FLAGS=-mx32 \ - -DCMAKE_C_FLAGS='--std=gnu90 -Wall -Werror -Wextra -Wpedantic -pedantic-errors -Wdouble-promotion -Wformat-overflow=2 -Wformat-security -Wformat-signedness -Wformat-truncation=2 -Wformat-y2k -Wmissing-include-dirs -Wshift-overflow=2 -Wswitch-bool -Wno-unused-parameter -Wuninitialized -Wstrict-overflow=2 -Wstringop-overflow=4 -Wstringop-truncation -Wduplicated-branches -Wduplicated-cond -Wdeclaration-after-statement -Wshadow -Wunsafe-loop-optimizations -Wundef -Wcast-align -Wno-clobbered -Wjump-misses-init -Wno-sign-compare -Wlogical-op -Waggregate-return -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Wpacked -Wredundant-decls -Wnested-externs -Winline -Wno-long-long -Wdisabled-optimization -Wno-overlength-strings' \ + cmake -G"Unix Makefiles" -DWITH_JPEG7=1 \ + -DCMAKE_C_FLAGS='-mx32 --std=gnu90 -Wall -Werror -Wextra -Wpedantic -pedantic-errors -Wdouble-promotion -Wformat-overflow=2 -Wformat-security -Wformat-signedness -Wformat-truncation=2 -Wformat-y2k -Wmissing-include-dirs -Wshift-overflow=2 -Wswitch-bool -Wno-unused-parameter -Wuninitialized -Wstrict-overflow=2 -Wstringop-overflow=4 -Wstringop-truncation -Wduplicated-branches -Wduplicated-cond -Wdeclaration-after-statement -Wshadow -Wunsafe-loop-optimizations -Wundef -Wcast-align -Wno-clobbered -Wjump-misses-init -Wno-sign-compare -Wlogical-op -Waggregate-return -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Wpacked -Wredundant-decls -Wnested-externs -Winline -Wno-long-long -Wdisabled-optimization -Wno-overlength-strings' \ .. export NUMCPUS=`grep -c '^processor' /proc/cpuinfo` make -j$NUMCPUS --load-average=$NUMCPUS From c27695a193a7cb265ef8e45c7b28bda3a71c70ce Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 16 Jun 2023 11:20:15 -0400 Subject: [PATCH 128/162] Fix build warnings/errs w/ -DNO_GETENV/-DNO_PUTENV - strtest.c: Fix unused variable warnings if both -DNO_GETENV and -DNO_PUTENV are specified or if only -DNO_GETENV is specified. - jinclude.h: Fix build error if only -DNO_GETENV is specified. Fixes #697 --- jinclude.h | 4 +++- strtest.c | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/jinclude.h b/jinclude.h index e8d983ac1..56e7a4b29 100644 --- a/jinclude.h +++ b/jinclude.h @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1994, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2022, D. R. Commander. + * Copyright (C) 2022-2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -123,6 +123,8 @@ static INLINE int GETENV_S(char *buffer, size_t buffer_size, const char *name) #else +#include + /* This provides a similar interface to the Microsoft _putenv_s() function, but * other than parameter validation, it has no advantages over setenv(). */ diff --git a/strtest.c b/strtest.c index 88b568c9f..3d5e004c6 100644 --- a/strtest.c +++ b/strtest.c @@ -1,5 +1,5 @@ /* - * Copyright (C)2022 D. R. Commander. All Rights Reserved. + * Copyright (C)2022-2023 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -55,8 +55,12 @@ void invalid_parameter_handler(const wchar_t *expression, int main(int argc, char **argv) { +#if !defined(NO_GETENV) || !defined(NO_PUTENV) int err; +#endif +#ifndef NO_GETENV char env[3]; +#endif #ifdef _MSC_VER _set_invalid_parameter_handler(invalid_parameter_handler); From a403ad5b5227c3e1894a8f469b0e9e12409b34ba Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 16 Jun 2023 15:20:29 -0400 Subject: [PATCH 129/162] AppVeyor: Integrate with SignPath.io --- appveyor.yml | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 9fa3aae58..4d40d0b95 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -32,6 +32,8 @@ install: set MSYSTEM=MINGW32 + bash -c "pacman --noconfirm -S zip" + mklink /d "%ProgramData%\Oracle\Java32" "c:\Program Files (x86)\Java\jdk1.6.0" git clone --depth=1 https://github.com/libjpeg-turbo/buildscripts.git -b %APPVEYOR_REPO_BRANCH% c:/buildscripts @@ -67,6 +69,8 @@ build_script: move c:\ljt.nightly\log-windows.txt . + zip libjpeg-turbo-installers.zip *.exe + artifacts: - path: '*.tar.gz' name: Source tarball @@ -77,6 +81,9 @@ artifacts: - path: '*-vc*.exe' name: SDK for Visual C++ + - path: '*.zip' + name: All libjpeg-turbo installers + - path: 'log-windows.txt' name: Build log @@ -86,14 +93,18 @@ artifacts: test: off deploy: - provider: S3 - access_key_id: - secure: Z74OYogQ6bNV/I+6b5ZEXig74+6MW2WLER0v/bPM/uk= - secret_access_key: - secure: cyGZhHVCFwZ9jgf5lXoW69mVtECmqwx3eLo61ha8ueWbMYlHho7lwDXwVvxOFiCa - bucket: libjpeg-turbo-pr - region: - secure: qSElYBgBRcEUf88M6Osthw== - folder: $(APPVEYOR_REPO_BRANCH)/windows - set_public: true - remove_files: true + - provider: Webhook + url: https://app.signpath.io/API/v1/3bc964ce-257b-4362-829f-1df1f087f5a0/Integrations/AppVeyor?ProjectSlug=libjpeg-turbo&SigningPolicySlug=test-signing + authorization: + secure: rrx5wnu5VWQqrFZJv75WdPc1H5gyYqp7cV/xVhsx1TnVR7VyUaVEiCYm/64Fcx1zmEPHGyEBrlrbjuRHgDjURA== + - provider: S3 + access_key_id: + secure: Z74OYogQ6bNV/I+6b5ZEXig74+6MW2WLER0v/bPM/uk= + secret_access_key: + secure: cyGZhHVCFwZ9jgf5lXoW69mVtECmqwx3eLo61ha8ueWbMYlHho7lwDXwVvxOFiCa + bucket: libjpeg-turbo-pr + region: + secure: qSElYBgBRcEUf88M6Osthw== + folder: $(APPVEYOR_REPO_BRANCH)/windows + set_public: true + remove_files: true From 41b18e8d616a522c87e0b97f50f7cdbb171ec56e Mon Sep 17 00:00:00 2001 From: DRC Date: Sun, 18 Jun 2023 12:50:47 -0400 Subject: [PATCH 130/162] AppVeyor: Only add installers to zip file Oops --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 4d40d0b95..2e110ccb0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -69,7 +69,7 @@ build_script: move c:\ljt.nightly\log-windows.txt . - zip libjpeg-turbo-installers.zip *.exe + zip libjpeg-turbo-installers.zip libjpeg-turbo-*.exe artifacts: - path: '*.tar.gz' From 6b9e3b04008165260a13f77cf235170438d5adf8 Mon Sep 17 00:00:00 2001 From: DRC Date: Mon, 19 Jun 2023 11:11:16 -0400 Subject: [PATCH 131/162] README.md: Include link to project home page (for compliance with SignPath's Code of Conduct for Open Source projects) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 608f587cf..a1eed3df3 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,8 @@ early 2010, libjpeg-turbo spun off into an independent project, with the goal of making high-speed JPEG compression/decompression technology available to a broader range of users and developers. +More information about libjpeg-turbo can be found at +. License ======= From c8d52f1c4c7480277b91420c27b2548d4c8e9043 Mon Sep 17 00:00:00 2001 From: DRC Date: Mon, 26 Jun 2023 11:53:03 -0400 Subject: [PATCH 132/162] tj3Transform: Calc dst buf size from xformed dims When used with TJPARAM_NOREALLOC and with TJXOP_TRANSPOSE, TJXOP_TRANSVERSE, TJXOP_ROT90, or TJXOP_ROT270, tj3Transform() incorrectly based the destination buffer size for a transform on the source image dimensions rather than the transformed image dimensions. This was apparently a long-standing bug that had existed in the tj*Transform() function since its inception. As initially implemented in the evolving libjpeg-turbo v1.2 code base, tjTransform() required dstSizes[i] to be set regardless of whether TJFLAG_NOREALLOC (the predecessor to TJPARAM_NOREALLOC) was set. ff78e37595c8462f64fd100f928aa1d08539527e, which was introduced later in the evolving libjpeg-turbo v1.2 code base, removed that requirement and planted the seed for the bug. However, the bug was not activated until 9b49f0e4c77c727648c6d3a4915eefdf5436de4a was introduced still later in the evolving libjpeg-turbo v1.2 code base, adding a subsampling type argument to the (new at the time) tjBufSize() function and thus making the width and height arguments no longer commutative. The bug opened up the possibility that a JPEG source image could cause tj3Transform() to overflow the destination buffer for a transform if all of the following were true: - The JPEG source image used 4:2:2, 4:4:0, 4:1:1, or 4:4:1 subsampling. (These are the only subsampling types for which the width and height arguments to tj3JPEGBufSize() are not commutative.) - The width and height of the JPEG source image were such that tj3JPEGBufSize(height, width, subsamplingType) returned a smaller value than tj3JPEGBufSize(width, height, subsamplingType). - The JPEG source image contained enough metadata that the size of the transformed image was larger than tj3JPEGBufSize(height, width, subsamplingType). - TJPARAM_NOREALLOC was set. - TJXOP_TRANSPOSE, TJXOP_TRANSVERSE, TJXOP_ROT90, or TJXOP_ROT270 was used. - TJXOPT_COPYNONE was not set. - TJXOPT_CROP was not set. - The calling program allocated tj3JPEGBufSize(height, width, subsamplingType) bytes for the destination buffer, as the API documentation instructs. The API documentation cautions that JPEG source images containing a large amount of extraneous metadata (EXIF, IPTC, ICC, etc.) cannot reliably be transformed if TJPARAM_NOREALLOC is set and TJXOPT_COPYNONE is not set. Irrespective of the bug, there are still cases in which a JPEG source image with a large amount of metadata can, when transformed, exceed the worst-case transformed JPEG image size. For instance, if you try to losslessly crop a JPEG image with 3 kB of EXIF data to 16x16 pixels, then you are guaranteed to exceed the worst-case 16x16 JPEG image size unless you discard the EXIF data. Even without the bug, tj3Transform() will still fail with "Buffer passed to JPEG library is too small" when attempting to transform JPEG source images that meet the aforementioned criteria. The bug is that the function segfaults rather than failing gracefully, but the chances of that occurring in a real-world application are very slim. Any real-world application developers who attempted to transform arbitrary JPEG source images with TJPARAM_NOREALLOC set would very quickly realize that they cannot reliably do that without also setting TJXOPT_COPYNONE. Thus, I posit that the actual risk posed by this bug is low. Applications such as web browsers that are the most exposed to security risks from arbitrary JPEG source images do not use the TurboJPEG lossless transform feature. (None of those applications even use the TurboJPEG API, to the best of my knowledge, and the public libjpeg API has no equivalent transform function.) Our only command-line interface to the tj3Transform() function, TJBench, was not exposed to the bug because it had a compatible bug whereby it allocated the JPEG destination buffer to the same size that tj3Transform() erroneously expected. The TurboJPEG Java API was also not exposed to the bug because of a similar compatible bug in the Java_org_libjpegturbo_turbojpeg_TJTransformer_transform() JNI function. (This commit fixes both compatible bugs.) In short, best practices for tj3Transform() are to use TJPARAM_NOREALLOC only with JPEG source images that are known to be free of metadata (such as images generated by tj3Compress*()) or to use TJXOPT_COPYNONE along with TJPARAM_NOREALLOC. Still, however, the function shouldn't segfault as long as the calling program allocates the suggested amount of space for the JPEG destination buffer. Usability notes: tj3Transform() could hypothetically require dstSizes[i] to be set regardless of the value of TJPARAM_NOREALLOC, but there are usability pitfalls either way. The main pitfall I sought to avoid with ff78e37595c8462f64fd100f928aa1d08539527e was a calling program failing to set dstSizes[i] at all, thus leaving its value undefined. It could be argued that requiring dstSizes[i] to be set in all cases is more consistent, but it could also be argued that not requiring it to be set when TJPARAM_NOREALLOC is set is more user-proof. tj3Transform() could also hypothetically set TJXOPT_COPYNONE automatically when TJPARAM_NOREALLOC is set, but that could lead to user confusion. Ultimately, I would like to address these issues in TurboJPEG v4 by using managed buffer objects, but that would be an extensive overhaul. --- ChangeLog.md | 16 ++++++++ fuzz/transform.cc | 98 +++++++++++++++++++++++++++++------------------ tjbench.c | 7 +++- turbojpeg-jni.c | 4 ++ turbojpeg.c | 4 ++ 5 files changed, 91 insertions(+), 38 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index b9d387a7b..c61565bec 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -29,6 +29,22 @@ memory. With 12-bit data precision, this caused a buffer overrun or underrun and subsequent segfault if the sample value read from unitialized memory was outside of the valid sample range. +5. Fixed a long-standing issue whereby the `tj3Transform()` function, when used +with the `TJXOP_TRANSPOSE`, `TJXOP_TRANSVERSE`, `TJXOP_ROT90`, or +`TJXOP_ROT270` transform operation and without automatic JPEG destination +buffer (re)allocation or lossless cropping, computed the worst-case transformed +JPEG image size based on the source image dimensions rather than the +transformed image dimensions. If a calling program allocated the JPEG +destination buffer based on the transformed image dimensions, as the API +documentation instructs, and attempted to transform a specially-crafted 4:2:2, +4:4:0, 4:1:1, or 4:4:1 JPEG source image containing a large amount of metadata, +the issue caused `tj3Transform()` to overflow the JPEG destination buffer +rather than fail gracefully. The issue could be worked around by setting +`TJXOPT_COPYNONE`. Note that, irrespective of this issue, `tj3Transform()` +cannot reliably transform JPEG source images that contain a large amount of +metadata unless automatic JPEG destination buffer (re)allocation is used or +`TJXOPT_COPYNONE` is set. + 2.1.91 (3.0 beta2) ================== diff --git a/fuzz/transform.cc b/fuzz/transform.cc index fe4a6a216..b3458db57 100644 --- a/fuzz/transform.cc +++ b/fuzz/transform.cc @@ -32,16 +32,13 @@ #include -#define NUMXFORMS 3 - - extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { tjhandle handle = NULL; - unsigned char *dstBufs[NUMXFORMS] = { NULL, NULL, NULL }; - size_t dstSizes[NUMXFORMS] = { 0, 0, 0 }, maxBufSize; - int width = 0, height = 0, jpegSubsamp, i, t; - tjtransform transforms[NUMXFORMS]; + unsigned char *dstBufs[1] = { NULL }; + size_t dstSizes[1] = { 0 }, maxBufSize; + int width = 0, height = 0, jpegSubsamp, i; + tjtransform transforms[1]; #if defined(__has_feature) && __has_feature(memory_sanitizer) char env[18] = "JSIMD_FORCENONE=1"; @@ -69,8 +66,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) if (jpegSubsamp < 0 || jpegSubsamp >= TJ_NUMSAMP) jpegSubsamp = TJSAMP_444; - for (t = 0; t < NUMXFORMS; t++) - memset(&transforms[t], 0, sizeof(tjtransform)); + memset(&transforms[0], 0, sizeof(tjtransform)); transforms[0].op = TJXOP_NONE; transforms[0].options = TJXOPT_PROGRESSIVE | TJXOPT_COPYNONE; @@ -79,44 +75,73 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) if (!dstBufs[0]) goto bailout; - transforms[1].r.w = (width + 1) / 2; - transforms[1].r.h = (height + 1) / 2; - transforms[1].op = TJXOP_TRANSPOSE; - transforms[1].options = TJXOPT_GRAY | TJXOPT_CROP | TJXOPT_COPYNONE; - dstBufs[1] = - (unsigned char *)malloc(tj3JPEGBufSize((width + 1) / 2, (height + 1) / 2, + maxBufSize = tj3JPEGBufSize(width, height, jpegSubsamp); + + tj3Set(handle, TJPARAM_NOREALLOC, 1); + if (tj3Transform(handle, data, size, 1, dstBufs, dstSizes, + transforms) == 0) { + /* Touch all of the output pixels in order to catch uninitialized reads + when using MemorySanitizer. */ + int sum = 0; + + for (i = 0; i < dstSizes[0]; i++) + sum += dstBufs[0][i]; + + /* Prevent the code above from being optimized out. This test should + never be true, but the compiler doesn't know that. */ + if (sum > 255 * maxBufSize) + goto bailout; + } + + free(dstBufs[0]); + dstBufs[0] = NULL; + + transforms[0].r.w = (height + 1) / 2; + transforms[0].r.h = (width + 1) / 2; + transforms[0].op = TJXOP_TRANSPOSE; + transforms[0].options = TJXOPT_GRAY | TJXOPT_CROP | TJXOPT_COPYNONE; + dstBufs[0] = + (unsigned char *)malloc(tj3JPEGBufSize((height + 1) / 2, (width + 1) / 2, TJSAMP_GRAY)); - if (!dstBufs[1]) + if (!dstBufs[0]) goto bailout; - transforms[2].op = TJXOP_ROT90; - transforms[2].options = TJXOPT_TRIM | TJXOPT_COPYNONE | TJXOPT_ARITHMETIC; - dstBufs[2] = + maxBufSize = tj3JPEGBufSize((height + 1) / 2, (width + 1) / 2, TJSAMP_GRAY); + + if (tj3Transform(handle, data, size, 1, dstBufs, dstSizes, + transforms) == 0) { + int sum = 0; + + for (i = 0; i < dstSizes[0]; i++) + sum += dstBufs[0][i]; + + if (sum > 255 * maxBufSize) + goto bailout; + } + + free(dstBufs[0]); + dstBufs[0] = NULL; + + transforms[0].op = TJXOP_ROT90; + transforms[0].options = TJXOPT_TRIM | TJXOPT_ARITHMETIC; + dstBufs[0] = (unsigned char *)malloc(tj3JPEGBufSize(height, width, jpegSubsamp)); - if (!dstBufs[2]) + if (!dstBufs[0]) goto bailout; - maxBufSize = tj3JPEGBufSize(width, height, jpegSubsamp); + maxBufSize = tj3JPEGBufSize(height, width, jpegSubsamp); - tj3Set(handle, TJPARAM_NOREALLOC, 1); - if (tj3Transform(handle, data, size, NUMXFORMS, dstBufs, dstSizes, + if (tj3Transform(handle, data, size, 1, dstBufs, dstSizes, transforms) == 0) { - /* Touch all of the output pixels in order to catch uninitialized reads - when using MemorySanitizer. */ - for (t = 0; t < NUMXFORMS; t++) { - int sum = 0; + int sum = 0; - for (i = 0; i < dstSizes[t]; i++) - sum += dstBufs[t][i]; + for (i = 0; i < dstSizes[0]; i++) + sum += dstBufs[0][i]; - /* Prevent the code above from being optimized out. This test should - never be true, but the compiler doesn't know that. */ - if (sum > 255 * maxBufSize) - goto bailout; - } + if (sum > 255 * maxBufSize) + goto bailout; } - transforms[0].options &= ~TJXOPT_COPYNONE; transforms[0].options |= TJXOPT_OPTIMIZE; free(dstBufs[0]); dstBufs[0] = NULL; @@ -135,8 +160,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) } bailout: - for (t = 0; t < NUMXFORMS; t++) - free(dstBufs[t]); + free(dstBufs[0]); tj3Destroy(handle); return 0; } diff --git a/tjbench.c b/tjbench.c index db08f060a..e053dc6d9 100644 --- a/tjbench.c +++ b/tjbench.c @@ -722,8 +722,13 @@ static int decompTest(char *fileName) if (noRealloc && (doTile || xformOp != TJXOP_NONE || xformOpt != 0 || customFilter)) { for (i = 0; i < ntilesw * ntilesh; i++) { - size_t jpegBufSize = tj3JPEGBufSize(tilew, tileh, subsamp); + size_t jpegBufSize; + if (xformOp == TJXOP_TRANSPOSE || xformOp == TJXOP_TRANSVERSE || + xformOp == TJXOP_ROT90 || xformOp == TJXOP_ROT270) + jpegBufSize = tj3JPEGBufSize(tileh, tilew, subsamp); + else + jpegBufSize = tj3JPEGBufSize(tilew, tileh, subsamp); if (jpegBufSize == 0) THROW_TJG(); if ((jpegBufs[i] = tj3Alloc(jpegBufSize)) == NULL) diff --git a/turbojpeg-jni.c b/turbojpeg-jni.c index d9531a254..32186f3fa 100644 --- a/turbojpeg-jni.c +++ b/turbojpeg-jni.c @@ -1213,6 +1213,10 @@ JNIEXPORT jintArray JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_transf for (i = 0; i < n; i++) { int w = jpegWidth, h = jpegHeight; + if (t[i].op == TJXOP_TRANSPOSE || t[i].op == TJXOP_TRANSVERSE || + t[i].op == TJXOP_ROT90 || t[i].op == TJXOP_ROT270) { + w = jpegHeight; h = jpegWidth; + } if (t[i].r.w != 0) w = t[i].r.w; if (t[i].r.h != 0) h = t[i].r.h; BAILIF0(jdstBufs[i] = (*env)->GetObjectArrayElement(env, dstobjs, i)); diff --git a/turbojpeg.c b/turbojpeg.c index 32344466d..b75d41484 100644 --- a/turbojpeg.c +++ b/turbojpeg.c @@ -2702,6 +2702,10 @@ DLLEXPORT int tj3Transform(tjhandle handle, const unsigned char *jpegBuf, if (!xinfo[i].crop) { w = dinfo->image_width; h = dinfo->image_height; + if (t[i].op == TJXOP_TRANSPOSE || t[i].op == TJXOP_TRANSVERSE || + t[i].op == TJXOP_ROT90 || t[i].op == TJXOP_ROT270) { + w = dinfo->image_height; h = dinfo->image_width; + } } else { w = xinfo[i].crop_width; h = xinfo[i].crop_height; } From bf9f319cb4b86b130bee16af19fee95a1cdb5ef2 Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 29 Jun 2023 16:07:42 -0400 Subject: [PATCH 133/162] Disallow color quantization with lossless decomp Color quantization is a legacy feature that serves little or no purpose with lossless JPEG images. 9f756bc67a84d4566bf74a0c2432aa55da404021 eliminated interaction issues between the lossless decompressor and the color quantizers related to out-of-range 12-bit samples, but referring to #701, other interaction issues apparently still exist. Such issues are likely, given the fact that the color quantizers were not designed with lossless decompression in mind. This commit reverts 9f756bc67a84d4566bf74a0c2432aa55da404021, since the issues it fixed are no longer relevant because of this commit and 2192560d74e6e6cf99dd05928885573be00a8208. Fixed #672 Fixes #673 Fixes #674 Fixes #676 Fixes #677 Fixes #678 Fixes #679 Fixes #681 Fixes #683 Fixes #701 --- CMakeLists.txt | 10 +++++----- ChangeLog.md | 16 +++++++++++----- cdjpeg.h | 7 ------- cjpeg.1 | 4 +++- djpeg.c | 14 ++++---------- jdlossls.c | 14 +------------- jdmaster.c | 8 -------- jdpostct.c | 27 ++++++++++++++++++++------- jpegint.h | 6 ------ jquant1.c | 12 +++++++----- jquant2.c | 8 +++----- jsamplecomp.h | 7 ------- libjpeg.txt | 7 ++++--- sharedlib/CMakeLists.txt | 4 ++-- structure.txt | 5 ++--- usage.txt | 1 + wrgif.c | 8 +++----- 17 files changed, 66 insertions(+), 92 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 83b4103dd..efd101a07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -564,10 +564,10 @@ message(STATUS "CMAKE_EXECUTABLE_SUFFIX = ${CMAKE_EXECUTABLE_SUFFIX}") set(JPEG16_SOURCES jcapistd.c jccolor.c jcdiffct.c jclossls.c jcmainct.c jcprepct.c jcsample.c jdapistd.c jdcolor.c jddiffct.c jdlossls.c jdmainct.c - jdpostct.c jdsample.c jquant1.c jquant2.c jutils.c) + jdpostct.c jdsample.c jutils.c) set(JPEG12_SOURCES ${JPEG16_SOURCES} jccoefct.c jcdctmgr.c jdcoefct.c - jddctmgr.c jdmerge.c jdpostct.c jfdctfst.c jfdctint.c jidctflt.c jidctfst.c - jidctint.c jidctred.c) + jddctmgr.c jdmerge.c jfdctfst.c jfdctint.c jidctflt.c jidctfst.c jidctint.c + jidctred.c jquant1.c jquant2.c) set(JPEG_SOURCES ${JPEG12_SOURCES} jcapimin.c jchuff.c jcicc.c jcinit.c jclhuff.c jcmarker.c jcmaster.c jcomapi.c jcparam.c jcphuff.c jctrans.c jdapimin.c jdatadst.c jdatasrc.c jdhuff.c jdicc.c jdinput.c jdlhuff.c @@ -760,9 +760,9 @@ if(ENABLE_STATIC) add_library(djpeg12-static OBJECT rdcolmap.c wrgif.c wrppm.c) set_property(TARGET djpeg12-static PROPERTY COMPILE_FLAGS "-DBITS_IN_JSAMPLE=12 -DGIF_SUPPORTED -DPPM_SUPPORTED") - add_library(djpeg16-static OBJECT rdcolmap.c wrgif.c wrppm.c) + add_library(djpeg16-static OBJECT wrppm.c) set_property(TARGET djpeg16-static PROPERTY COMPILE_FLAGS - "-DBITS_IN_JSAMPLE=16 -DGIF_SUPPORTED -DPPM_SUPPORTED") + "-DBITS_IN_JSAMPLE=16 -DPPM_SUPPORTED") add_executable(djpeg-static djpeg.c cdjpeg.c rdcolmap.c rdswitch.c wrbmp.c wrgif.c wrppm.c wrtarga.c $ $) diff --git a/ChangeLog.md b/ChangeLog.md index c61565bec..aadd79071 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -10,11 +10,17 @@ images. 2. Fixed various segfaults and buffer overruns (CVE-2023-2804) that occurred when attempting to decompress various specially-crafted malformed -12-bit-per-component lossless JPEG images. These issues were caused by -out-of-range sample values that were not range-limited before being used as -array indices. The issues were specific to 12-bit data precision, since that -is the only data precision for which the range of the sample data type exceeds -the valid sample range. +12-bit-per-component and 16-bit-per-component lossless JPEG images using color +quantization or merged chroma upsampling/color conversion. The underlying +cause of these issues was that the color quantization and merged chroma +upsampling/color conversion algorithms were not designed with lossless +decompression in mind. Since libjpeg-turbo explicitly does not support color +conversion when compressing or decompressing lossless JPEG images, merged +chroma upsampling/color conversion never should have been enabled for such +images. Color quantization is a legacy feature that serves little or no +purpose with lossless JPEG images, so it is also now disabled when +decompressing such images. (As a result, djpeg can no longer decompress a +lossless JPEG image into a GIF image.) 3. Fixed an oversight in 1.4 beta1[8] that caused various segfaults and buffer overruns when attempting to decompress various specially-crafted malformed diff --git a/cdjpeg.h b/cdjpeg.h index 13609f66b..471b9a3fc 100644 --- a/cdjpeg.h +++ b/cdjpeg.h @@ -123,10 +123,6 @@ EXTERN(cjpeg_source_ptr) j16init_read_gif(j_compress_ptr cinfo); EXTERN(djpeg_dest_ptr) jinit_write_gif(j_decompress_ptr cinfo, boolean is_lzw); EXTERN(djpeg_dest_ptr) j12init_write_gif(j_decompress_ptr cinfo, boolean is_lzw); -#ifdef D_LOSSLESS_SUPPORTED -EXTERN(djpeg_dest_ptr) j16init_write_gif(j_decompress_ptr cinfo, - boolean is_lzw); -#endif EXTERN(cjpeg_source_ptr) jinit_read_ppm(j_compress_ptr cinfo); EXTERN(cjpeg_source_ptr) j12init_read_ppm(j_compress_ptr cinfo); #ifdef C_LOSSLESS_SUPPORTED @@ -154,9 +150,6 @@ EXTERN(boolean) set_sample_factors(j_compress_ptr cinfo, char *arg); EXTERN(void) read_color_map(j_decompress_ptr cinfo, FILE *infile); EXTERN(void) read_color_map_12(j_decompress_ptr cinfo, FILE *infile); -#ifdef D_LOSSLESS_SUPPORTED -EXTERN(void) read_color_map_16(j_decompress_ptr cinfo, FILE *infile); -#endif /* common support routines (in cdjpeg.c) */ diff --git a/cjpeg.1 b/cjpeg.1 index 7ddce3c0d..0815ca0c7 100644 --- a/cjpeg.1 +++ b/cjpeg.1 @@ -1,4 +1,4 @@ -.TH CJPEG 1 "30 November 2022" +.TH CJPEG 1 "29 June 2023" .SH NAME cjpeg \- compress an image file to a JPEG file .SH SYNOPSIS @@ -182,6 +182,8 @@ unavailable when compressing or decompressing a lossless JPEG file: - Color space conversion (the JPEG image will use the same color space as the input image) .IP +- Color quantization +.IP - DCT/IDCT algorithm selection .IP - Smoothing diff --git a/djpeg.c b/djpeg.c index 1f7153167..f42d35432 100644 --- a/djpeg.c +++ b/djpeg.c @@ -662,14 +662,9 @@ main(int argc, char **argv) #endif #ifdef GIF_SUPPORTED case FMT_GIF: - if (cinfo.data_precision == 16) { -#ifdef D_LOSSLESS_SUPPORTED - dest_mgr = j16init_write_gif(&cinfo, TRUE); -#else + if (cinfo.data_precision == 16) ERREXIT1(&cinfo, JERR_BAD_PRECISION, cinfo.data_precision); - break; -#endif - } else if (cinfo.data_precision == 12) + else if (cinfo.data_precision == 12) dest_mgr = j12init_write_gif(&cinfo, TRUE); else dest_mgr = jinit_write_gif(&cinfo, TRUE); @@ -680,14 +675,13 @@ main(int argc, char **argv) #endif #ifdef PPM_SUPPORTED case FMT_PPM: - if (cinfo.data_precision == 16) { + if (cinfo.data_precision == 16) #ifdef D_LOSSLESS_SUPPORTED dest_mgr = j16init_write_ppm(&cinfo); #else ERREXIT1(&cinfo, JERR_BAD_PRECISION, cinfo.data_precision); - break; #endif - } else if (cinfo.data_precision == 12) + else if (cinfo.data_precision == 12) dest_mgr = j12init_write_ppm(&cinfo); else dest_mgr = jinit_write_ppm(&cinfo); diff --git a/jdlossls.c b/jdlossls.c index cfdca7e3a..4d15e6bba 100644 --- a/jdlossls.c +++ b/jdlossls.c @@ -6,7 +6,7 @@ * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: - * Copyright (C) 2022-2023, D. R. Commander. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -217,15 +217,7 @@ simple_upscale(j_decompress_ptr cinfo, JDIFFROW diff_buf, _JSAMPROW output_buf, JDIMENSION width) { do { -#if BITS_IN_JSAMPLE == 12 - /* 12-bit is the only data precision for which the range of the sample data - * type exceeds the valid sample range. Thus, we need to range-limit the - * samples, because other algorithms may try to use them as array indices. - */ - *output_buf++ = (_JSAMPLE)((*diff_buf++ << cinfo->Al) & 0xFFF); -#else *output_buf++ = (_JSAMPLE)(*diff_buf++ << cinfo->Al); -#endif } while (--width); } @@ -234,11 +226,7 @@ noscale(j_decompress_ptr cinfo, JDIFFROW diff_buf, _JSAMPROW output_buf, JDIMENSION width) { do { -#if BITS_IN_JSAMPLE == 12 - *output_buf++ = (_JSAMPLE)((*diff_buf++) & 0xFFF); -#else *output_buf++ = (_JSAMPLE)(*diff_buf++); -#endif } while (--width); } diff --git a/jdmaster.c b/jdmaster.c index 4b3852d9b..80a4842ac 100644 --- a/jdmaster.c +++ b/jdmaster.c @@ -571,11 +571,7 @@ master_selection(j_decompress_ptr cinfo) if (cinfo->enable_1pass_quant) { #ifdef QUANT_1PASS_SUPPORTED if (cinfo->data_precision == 16) -#ifdef D_LOSSLESS_SUPPORTED - j16init_1pass_quantizer(cinfo); -#else ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); -#endif else if (cinfo->data_precision == 12) j12init_1pass_quantizer(cinfo); else @@ -590,11 +586,7 @@ master_selection(j_decompress_ptr cinfo) if (cinfo->enable_2pass_quant || cinfo->enable_external_quant) { #ifdef QUANT_2PASS_SUPPORTED if (cinfo->data_precision == 16) -#ifdef D_LOSSLESS_SUPPORTED - j16init_2pass_quantizer(cinfo); -#else ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); -#endif else if (cinfo->data_precision == 12) j12init_2pass_quantizer(cinfo); else diff --git a/jdpostct.c b/jdpostct.c index e85007485..d38495f5f 100644 --- a/jdpostct.c +++ b/jdpostct.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2022, D. R. Commander. + * Copyright (C) 2022-2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -49,6 +49,7 @@ typedef my_post_controller *my_post_ptr; /* Forward declarations */ +#if BITS_IN_JSAMPLE != 16 METHODDEF(void) post_process_1pass(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, @@ -56,7 +57,8 @@ METHODDEF(void) post_process_1pass(j_decompress_ptr cinfo, _JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail); -#ifdef QUANT_2PASS_SUPPORTED +#endif +#if defined(QUANT_2PASS_SUPPORTED) && BITS_IN_JSAMPLE != 16 METHODDEF(void) post_process_prepass(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, @@ -85,6 +87,7 @@ start_pass_dpost(j_decompress_ptr cinfo, J_BUF_MODE pass_mode) switch (pass_mode) { case JBUF_PASS_THRU: +#if BITS_IN_JSAMPLE != 16 if (cinfo->quantize_colors) { /* Single-pass processing with color quantization. */ post->pub._post_process_data = post_process_1pass; @@ -97,14 +100,16 @@ start_pass_dpost(j_decompress_ptr cinfo, J_BUF_MODE pass_mode) ((j_common_ptr)cinfo, post->whole_image, (JDIMENSION)0, post->strip_height, TRUE); } - } else { + } else +#endif + { /* For single-pass processing without color quantization, * I have no work to do; just call the upsampler directly. */ post->pub._post_process_data = cinfo->upsample->_upsample; } break; -#ifdef QUANT_2PASS_SUPPORTED +#if defined(QUANT_2PASS_SUPPORTED) && BITS_IN_JSAMPLE != 16 case JBUF_SAVE_AND_PASS: /* First pass of 2-pass quantization */ if (post->whole_image == NULL) @@ -117,7 +122,7 @@ start_pass_dpost(j_decompress_ptr cinfo, J_BUF_MODE pass_mode) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); post->pub._post_process_data = post_process_2pass; break; -#endif /* QUANT_2PASS_SUPPORTED */ +#endif /* defined(QUANT_2PASS_SUPPORTED) && BITS_IN_JSAMPLE != 16 */ default: ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); break; @@ -131,6 +136,8 @@ start_pass_dpost(j_decompress_ptr cinfo, J_BUF_MODE pass_mode) * This is used for color precision reduction as well as one-pass quantization. */ +#if BITS_IN_JSAMPLE != 16 + METHODDEF(void) post_process_1pass(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, @@ -156,8 +163,10 @@ post_process_1pass(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, *out_row_ctr += num_rows; } +#endif -#ifdef QUANT_2PASS_SUPPORTED + +#if defined(QUANT_2PASS_SUPPORTED) && BITS_IN_JSAMPLE != 16 /* * Process some data in the first pass of 2-pass quantization. @@ -246,7 +255,7 @@ post_process_2pass(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, } } -#endif /* QUANT_2PASS_SUPPORTED */ +#endif /* defined(QUANT_2PASS_SUPPORTED) && BITS_IN_JSAMPLE != 16 */ /* @@ -271,6 +280,7 @@ _jinit_d_post_controller(j_decompress_ptr cinfo, boolean need_full_buffer) /* Create the quantization buffer, if needed */ if (cinfo->quantize_colors) { +#if BITS_IN_JSAMPLE != 16 /* 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?) @@ -296,6 +306,9 @@ _jinit_d_post_controller(j_decompress_ptr cinfo, boolean need_full_buffer) cinfo->output_width * cinfo->out_color_components, post->strip_height); } +#else + ERREXIT(cinfo, JERR_NOTIMPL); +#endif } } diff --git a/jpegint.h b/jpegint.h index 747b54645..654142014 100644 --- a/jpegint.h +++ b/jpegint.h @@ -431,10 +431,6 @@ struct jpeg_color_quantizer { JSAMPARRAY output_buf, int num_rows); void (*color_quantize_12) (j_decompress_ptr cinfo, J12SAMPARRAY input_buf, J12SAMPARRAY output_buf, int num_rows); -#ifdef D_LOSSLESS_SUPPORTED - void (*color_quantize_16) (j_decompress_ptr cinfo, J16SAMPARRAY input_buf, - J16SAMPARRAY output_buf, int num_rows); -#endif void (*finish_pass) (j_decompress_ptr cinfo); void (*new_color_map) (j_decompress_ptr cinfo); }; @@ -553,8 +549,6 @@ EXTERN(void) j16init_d_post_controller(j_decompress_ptr cinfo, boolean need_full_buffer); EXTERN(void) j16init_upsampler(j_decompress_ptr cinfo); EXTERN(void) j16init_color_deconverter(j_decompress_ptr cinfo); -EXTERN(void) j16init_1pass_quantizer(j_decompress_ptr cinfo); -EXTERN(void) j16init_2pass_quantizer(j_decompress_ptr cinfo); EXTERN(void) jinit_d_diff_controller(j_decompress_ptr cinfo, boolean need_full_buffer); EXTERN(void) j12init_d_diff_controller(j_decompress_ptr cinfo, diff --git a/jquant1.c b/jquant1.c index 67c1ae365..2e914b919 100644 --- a/jquant1.c +++ b/jquant1.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2009, 2015, 2022, D. R. Commander. + * Copyright (C) 2009, 2015, 2022-2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -18,8 +18,7 @@ #include "jpeglib.h" #include "jsamplecomp.h" -#if defined(QUANT_1PASS_SUPPORTED) && \ - (BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED)) +#if defined(QUANT_1PASS_SUPPORTED) && BITS_IN_JSAMPLE != 16 /* @@ -827,6 +826,10 @@ _jinit_1pass_quantizer(j_decompress_ptr cinfo) if (cinfo->data_precision != BITS_IN_JSAMPLE) ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + /* Color quantization is not supported with lossless JPEG images */ + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); + cquantize = (my_cquantize_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, sizeof(my_cquantizer)); @@ -858,5 +861,4 @@ _jinit_1pass_quantizer(j_decompress_ptr cinfo) alloc_fs_workspace(cinfo); } -#endif /* defined(QUANT_1PASS_SUPPORTED) && - (BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED)) */ +#endif /* defined(QUANT_1PASS_SUPPORTED) && BITS_IN_JSAMPLE != 16 */ diff --git a/jquant2.c b/jquant2.c index 5984ed8cc..9ba51fa88 100644 --- a/jquant2.c +++ b/jquant2.c @@ -25,8 +25,7 @@ #include "jpeglib.h" #include "jsamplecomp.h" -#if defined(QUANT_2PASS_SUPPORTED) && \ - (BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED)) +#if defined(QUANT_2PASS_SUPPORTED) && BITS_IN_JSAMPLE != 16 /* @@ -1239,7 +1238,7 @@ _jinit_2pass_quantizer(j_decompress_ptr cinfo) /* Make sure jdmaster didn't give me a case I can't handle */ if (cinfo->out_color_components != 3 || - cinfo->out_color_space == JCS_RGB565) + cinfo->out_color_space == JCS_RGB565 || cinfo->master->lossless) ERREXIT(cinfo, JERR_NOTIMPL); /* Allocate the histogram/inverse colormap storage */ @@ -1291,5 +1290,4 @@ _jinit_2pass_quantizer(j_decompress_ptr cinfo) } } -#endif /* defined(QUANT_2PASS_SUPPORTED) && - (BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED)) */ +#endif /* defined(QUANT_2PASS_SUPPORTED) && BITS_IN_JSAMPLE != 16 */ diff --git a/jsamplecomp.h b/jsamplecomp.h index 6594480f9..f3f275e6e 100644 --- a/jsamplecomp.h +++ b/jsamplecomp.h @@ -57,8 +57,6 @@ #define _upsample upsample_16 /* Use the 16-bit method in the jpeg_color_converter structure. */ #define _color_convert color_convert_16 -/* Use the 16-bit method in the jpeg_color_quantizer structure. */ -#define _color_quantize color_quantize_16 #endif /* Global internal functions (jpegint.h) */ @@ -76,8 +74,6 @@ #define _jinit_d_post_controller j16init_d_post_controller #define _jinit_upsampler j16init_upsampler #define _jinit_color_deconverter j16init_color_deconverter -#define _jinit_1pass_quantizer j16init_1pass_quantizer -#define _jinit_2pass_quantizer j16init_2pass_quantizer #define _jinit_merged_upsampler j16init_merged_upsampler #define _jinit_d_diff_controller j16init_d_diff_controller #define _jinit_lossless_decompressor j16init_lossless_decompressor @@ -102,10 +98,7 @@ #endif #ifdef D_LOSSLESS_SUPPORTED -#define _jinit_write_gif j16init_write_gif #define _jinit_write_ppm j16init_write_ppm - -#define _read_color_map read_color_map_16 #endif #elif BITS_IN_JSAMPLE == 12 diff --git a/libjpeg.txt b/libjpeg.txt index a695f6d0e..2dae2d204 100644 --- a/libjpeg.txt +++ b/libjpeg.txt @@ -5,7 +5,7 @@ Copyright (C) 1994-2013, Thomas G. Lane, Guido Vollbeding. Lossless JPEG Modifications: Copyright (C) 1999, Ken Murchison. libjpeg-turbo Modifications: -Copyright (C) 2010, 2014-2018, 2020, 2022, D. R. Commander. +Copyright (C) 2010, 2014-2018, 2020, 2022-2023, D. R. Commander. Copyright (C) 2015, Google, Inc. For conditions of distribution and use, see the accompanying README.ijg file. @@ -120,8 +120,8 @@ supports 12-bit-per-component lossy or lossless JPEG if you set cinfo->data_precision to 12 and 16-bit-per-component lossless JPEG if you set cinfo->data_precision to 16. Note that this causes the sample size to be larger than a char, so it affects the surrounding application's image data. -The sample applications cjpeg and djpeg can support 12-bit and 16-bit mode only -for PPM and GIF file formats. +The sample applications cjpeg and djpeg can support 12-bit mode only for PPM, +PGM, and GIF file formats and 16-bit mode only for PPM and PGM file formats. Note that, when 12-bit data precision is enabled, the library always compresses in Huffman optimization mode, in order to generate valid Huffman tables. This @@ -1037,6 +1037,7 @@ jpeg_enable_lossless (j_compress_ptr cinfo, int predictor_selection_value, * Downsampling/upsampling * Color space conversion (the JPEG image will use the same color space as the input image) + * Color quantization * IDCT scaling * Raw (downsampled) data input/output * Transcoding of DCT coefficients diff --git a/sharedlib/CMakeLists.txt b/sharedlib/CMakeLists.txt index 589230573..8e9425696 100644 --- a/sharedlib/CMakeLists.txt +++ b/sharedlib/CMakeLists.txt @@ -87,9 +87,9 @@ target_link_libraries(cjpeg jpeg) add_library(djpeg12 OBJECT ../rdcolmap.c ../wrgif.c ../wrppm.c) set_property(TARGET djpeg12 PROPERTY COMPILE_FLAGS "-DBITS_IN_JSAMPLE=12 -DGIF_SUPPORTED -DPPM_SUPPORTED") -add_library(djpeg16 OBJECT ../rdcolmap.c ../wrgif.c ../wrppm.c) +add_library(djpeg16 OBJECT ../wrppm.c) set_property(TARGET djpeg16 PROPERTY COMPILE_FLAGS - "-DBITS_IN_JSAMPLE=16 -DGIF_SUPPORTED -DPPM_SUPPORTED") + "-DBITS_IN_JSAMPLE=16 -DPPM_SUPPORTED") add_executable(djpeg ../djpeg.c ../cdjpeg.c ../rdcolmap.c ../rdswitch.c ../wrbmp.c ../wrgif.c ../wrppm.c ../wrtarga.c $ $) diff --git a/structure.txt b/structure.txt index c0c6d8e42..030b8e880 100644 --- a/structure.txt +++ b/structure.txt @@ -5,7 +5,7 @@ Copyright (C) 1991-2012, Thomas G. Lane, Guido Vollbeding. Lossless JPEG Modifications: Copyright (C) 1999, Ken Murchison. libjpeg-turbo Modifications: -Copyright (C) 2022, D. R. Commander. +Copyright (C) 2022-2023, D. R. Commander. For conditions of distribution and use, see the accompanying README.ijg file. @@ -443,8 +443,7 @@ Main controller --| | scaling Main controller --| | |-- Upsampling - |-- Postprocessing controller --| |-- Colorspace conversion - |-- Color quantization + |-- Postprocessing controller --| |-- Color precision reduction As before, this diagram also represents typical control flow. The objects diff --git a/usage.txt b/usage.txt index ddd0d1f5c..c1bba4ae3 100644 --- a/usage.txt +++ b/usage.txt @@ -183,6 +183,7 @@ Switches for advanced users: * Quality/quantization table selection * Color space conversion (the JPEG image will use the same color space as the input image) + * Color quantization * DCT/IDCT algorithm selection * Smoothing * Downsampling/upsampling diff --git a/wrgif.c b/wrgif.c index c0fb10c14..23773573b 100644 --- a/wrgif.c +++ b/wrgif.c @@ -5,7 +5,7 @@ * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 2015-2019 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2015, 2017, 2022, D. R. Commander. + * Copyright (C) 2015, 2017, 2022-2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -33,8 +33,7 @@ #include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ #include "jsamplecomp.h" -#if defined(GIF_SUPPORTED) && \ - (BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED)) +#if defined(GIF_SUPPORTED) && BITS_IN_JSAMPLE != 16 #define MAX_LZW_BITS 12 /* maximum LZW code size (4096 symbols) */ @@ -583,5 +582,4 @@ _jinit_write_gif(j_decompress_ptr cinfo, boolean is_lzw) return (djpeg_dest_ptr)dest; } -#endif /* defined(GIF_SUPPORTED) && - (BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED)) */ +#endif /* defined(GIF_SUPPORTED) && BITS_IN_JSAMPLE != 16 */ From 717388922347f31854cfa153db0549b33f195413 Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 29 Jun 2023 16:31:37 -0400 Subject: [PATCH 134/162] djpeg: Fix -map option with 12-bit data precision --- ChangeLog.md | 4 ++++ djpeg.c | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index aadd79071..a55cebaa0 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -51,6 +51,10 @@ cannot reliably transform JPEG source images that contain a large amount of metadata unless automatic JPEG destination buffer (re)allocation is used or `TJXOPT_COPYNONE` is set. +6. Fixed a regression introduced by 3.0 beta2[6] that prevented the djpeg +`-map` option from working when decompressing 12-bit-per-component lossy JPEG +images. + 2.1.91 (3.0 beta2) ================== diff --git a/djpeg.c b/djpeg.c index f42d35432..4ea5c4bd7 100644 --- a/djpeg.c +++ b/djpeg.c @@ -5,7 +5,7 @@ * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 2013-2019 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2010-2011, 2013-2017, 2019-2020, 2022, D. R. Commander. + * Copyright (C) 2010-2011, 2013-2017, 2019-2020, 2022-2023, D. R. Commander. * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -330,7 +330,10 @@ parse_switches(j_decompress_ptr cinfo, int argc, char **argv, fprintf(stderr, "%s: can't open %s\n", progname, argv[argn]); exit(EXIT_FAILURE); } - read_color_map(cinfo, mapfile); + if (cinfo->data_precision == 12) + read_color_map_12(cinfo, mapfile); + else + read_color_map(cinfo, mapfile); fclose(mapfile); cinfo->quantize_colors = TRUE; #else From 240a5a5cbb1c92db833e4a8fc833f05c1d590851 Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 29 Jun 2023 17:45:07 -0400 Subject: [PATCH 135/162] ChangeLog.md: Fix typo --- ChangeLog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index a55cebaa0..3f9363c4e 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -32,7 +32,7 @@ downsampled width for components with 4x2 or 2x4 subsampling factors if decompression scaling was enabled. This caused the components to be upsampled incompletely, which caused the color converter to read from uninitialized memory. With 12-bit data precision, this caused a buffer overrun or underrun -and subsequent segfault if the sample value read from unitialized memory was +and subsequent segfault if the sample value read from uninitialized memory was outside of the valid sample range. 5. Fixed a long-standing issue whereby the `tj3Transform()` function, when used From 4a831d6e482fabe84fba9c403ec2e9cb24b30fb7 Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 30 Jun 2023 10:15:40 -0400 Subject: [PATCH 136/162] turbojpeg.h: Make customFilter() proto match doc Closes #704 --- doc/html/functions.html | 2 +- doc/html/functions_vars.html | 2 +- doc/html/search/all_0.js | 2 +- doc/html/search/variables_0.js | 2 +- doc/html/structtjtransform.html | 12 ++++++------ turbojpeg.h | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/html/functions.html b/doc/html/functions.html index a3456d705..22c164b85 100644 --- a/doc/html/functions.html +++ b/doc/html/functions.html @@ -65,7 +65,7 @@

      Here is a list of all documented struct and union fields with links to the struct/union documentation for each field:
      • customFilter -: tjtransform +: tjtransform
      • data : tjtransform diff --git a/doc/html/functions_vars.html b/doc/html/functions_vars.html index 409cbd599..0e3897e76 100644 --- a/doc/html/functions_vars.html +++ b/doc/html/functions_vars.html @@ -65,7 +65,7 @@
         
        • customFilter -: tjtransform +: tjtransform
        • data : tjtransform diff --git a/doc/html/search/all_0.js b/doc/html/search/all_0.js index 54c735614..07cc58c7c 100644 --- a/doc/html/search/all_0.js +++ b/doc/html/search/all_0.js @@ -1,4 +1,4 @@ var searchData= [ - ['customfilter_0',['customFilter',['../structtjtransform.html#afd7fc262df33f741e120ef4183202ef5',1,'tjtransform']]] + ['customfilter_0',['customFilter',['../structtjtransform.html#a0dc7697d59a7abe48afc629e96cbc1d2',1,'tjtransform']]] ]; diff --git a/doc/html/search/variables_0.js b/doc/html/search/variables_0.js index 7585db6ed..40a95a8e1 100644 --- a/doc/html/search/variables_0.js +++ b/doc/html/search/variables_0.js @@ -1,4 +1,4 @@ var searchData= [ - ['customfilter_190',['customFilter',['../structtjtransform.html#afd7fc262df33f741e120ef4183202ef5',1,'tjtransform']]] + ['customfilter_190',['customFilter',['../structtjtransform.html#a0dc7697d59a7abe48afc629e96cbc1d2',1,'tjtransform']]] ]; diff --git a/doc/html/structtjtransform.html b/doc/html/structtjtransform.html index bcb6a0ac1..9facd8523 100644 --- a/doc/html/structtjtransform.html +++ b/doc/html/structtjtransform.html @@ -89,21 +89,21 @@
      void * data
       Arbitrary data that can be accessed within the body of the callback function. More...
       
      int(* customFilter )(short *coeffs, tjregion arrayRegion, tjregion planeRegion, int componentIndex, int transformIndex, struct tjtransform *transform)
       A callback function that can be used to modify the DCT coefficients after they are losslessly transformed but before they are transcoded to a new JPEG image. More...
       
      int(* customFilter )(short *coeffs, tjregion arrayRegion, tjregion planeRegion, int componentID, int transformID, struct tjtransform *transform)
       A callback function that can be used to modify the DCT coefficients after they are losslessly transformed but before they are transcoded to a new JPEG image. More...
       

      Detailed Description

      Lossless transform.

      Field Documentation

      - -

      ◆ customFilter

      + +

      ◆ customFilter

      - +
      int(* tjtransform::customFilter) (short *coeffs, tjregion arrayRegion, tjregion planeRegion, int componentIndex, int transformIndex, struct tjtransform *transform)int(* tjtransform::customFilter) (short *coeffs, tjregion arrayRegion, tjregion planeRegion, int componentID, int transformID, struct tjtransform *transform)
      diff --git a/turbojpeg.h b/turbojpeg.h index f392adc41..976b98b62 100644 --- a/turbojpeg.h +++ b/turbojpeg.h @@ -1004,8 +1004,8 @@ typedef struct tjtransform { * @return 0 if the callback was successful, or -1 if an error occurred. */ int (*customFilter) (short *coeffs, tjregion arrayRegion, - tjregion planeRegion, int componentIndex, - int transformIndex, struct tjtransform *transform); + tjregion planeRegion, int componentID, int transformID, + struct tjtransform *transform); } tjtransform; /** From e0c53aa38f65915661fcb7d448d50d1c3c7b8391 Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 30 Jun 2023 17:58:45 -0400 Subject: [PATCH 137/162] jchuff.c: Test for out-of-range coefficients Restore two coefficient range checks from libjpeg to the C baseline Huffman encoder. This fixes an issue (https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=60253) whereby the encoder could read from uninitialized memory when attempting to transform a specially-crafted malformed arithmetic-coded JPEG source image into a baseline Huffman-coded JPEG destination image with default Huffman tables. More specifically, the out-of-range coefficients caused r to equal 256, which overflowed the actbl->ehufsi[] array. Because the overflow was contained within the huff_entropy_encoder structure, this issue was not exploitable (nor was it observable at all on x86 or Arm CPUs unless JSIMD_NOHUFFENC=1 or JSIMD_FORCENONE=1 was set in the environment or unless libjpeg-turbo was built with WITH_SIMD=0.) The fix is performance-neutral (+/- 1-2%) for x86-64 code and causes a 0-4% (avg. 1-2%, +/- 1-2%) compression regression for i386 code on Intel CPUs when the C baseline Huffman encoder is used (JSIMD_NOHUFFENC=1). The fix is performance-neutral (+/- 1-2%) on Intel CPUs when all of the libjpeg-turbo SIMD extensions are disabled (JSIMD_FORCENONE=1). The fix causes a 0-2% (avg. <1%, +/- 1%) compression regression for PowerPC code. --- ChangeLog.md | 5 +++++ jchuff.c | 11 ++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index 3f9363c4e..e12b61786 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -55,6 +55,11 @@ metadata unless automatic JPEG destination buffer (re)allocation is used or `-map` option from working when decompressing 12-bit-per-component lossy JPEG images. +7. Fixed an issue that caused the C Huffman encoder (which is not used by +default on x86 and Arm CPUs) to read from uninitialized memory when attempting +to transform a specially-crafted malformed arithmetic-coded JPEG source image +into a baseline Huffman-coded JPEG destination image. + 2.1.91 (3.0 beta2) ================== diff --git a/jchuff.c b/jchuff.c index dac832e99..b879506d5 100644 --- a/jchuff.c +++ b/jchuff.c @@ -6,7 +6,7 @@ * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: - * Copyright (C) 2009-2011, 2014-2016, 2018-2022, D. R. Commander. + * Copyright (C) 2009-2011, 2014-2016, 2018-2023, D. R. Commander. * Copyright (C) 2015, Matthieu Darbois. * Copyright (C) 2018, Matthias Räncker. * Copyright (C) 2020, Arm Limited. @@ -584,6 +584,7 @@ encode_one_block(working_state *state, JCOEFPTR block, int last_dc_val, bit_buf_type put_buffer; JOCTET _buffer[BUFSIZE], *buffer; int localbuf = 0; + int max_coef_bits = state->cinfo->data_precision + 2; free_bits = state->cur.free_bits; put_buffer = state->cur.put_buffer.c; @@ -604,6 +605,11 @@ encode_one_block(working_state *state, JCOEFPTR block, int last_dc_val, /* Find the number of bits needed for the magnitude of the coefficient */ nbits = JPEG_NBITS(nbits); + /* Check for out-of-range coefficient values. + * Since we're encoding a difference, the range limit is twice as much. + */ + if (nbits > max_coef_bits + 1) + ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); /* Emit the Huffman-coded symbol for the number of bits. * Emit that number of bits of the value, if positive, @@ -629,6 +635,9 @@ encode_one_block(working_state *state, JCOEFPTR block, int last_dc_val, temp += nbits; \ nbits ^= temp; \ nbits = JPEG_NBITS_NONZERO(nbits); \ + /* Check for out-of-range coefficient values */ \ + if (nbits > max_coef_bits) \ + ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); \ /* if run length > 15, must emit special run-length-16 codes (0xF0) */ \ while (r >= 16 * 16) { \ r -= 16 * 16; \ From 02b074fd90aa31e3eab3b36353af31b04a409624 Mon Sep 17 00:00:00 2001 From: DRC Date: Sat, 1 Jul 2023 08:09:23 -0400 Subject: [PATCH 138/162] xform fuzz: Use only xform opts to set entropy alg This is subtle, but the tj3DecompressHeader() function sets the values of TJPARAM_ARITHMETIC, TJPARAM_OPTIMIZE, and TJPARAM_PROGRESSIVE. Unless we unset those values, the entropy algorithm used in the transformed JPEG image will be determined by the union of the parameter values and the transform options, which isn't what we want. --- fuzz/transform.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fuzz/transform.cc b/fuzz/transform.cc index b3458db57..06493883c 100644 --- a/fuzz/transform.cc +++ b/fuzz/transform.cc @@ -55,6 +55,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) width = tj3Get(handle, TJPARAM_JPEGWIDTH); height = tj3Get(handle, TJPARAM_JPEGHEIGHT); jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP); + /* Let the transform options dictate the entropy coding algorithm. */ + tj3Set(handle, TJPARAM_ARITHMETIC, 0); + tj3Set(handle, TJPARAM_PROGRESSIVE, 0); + tj3Set(handle, TJPARAM_OPTIMIZE, 0); /* Ignore 0-pixel images and images larger than 1 Megapixel. Casting width to (uint64_t) prevents integer overflow if width * height > INT_MAX. */ From 6c87537f60941f3c265c339fe60d1e31d2a42ccf Mon Sep 17 00:00:00 2001 From: DRC Date: Mon, 3 Jul 2023 10:25:24 -0400 Subject: [PATCH 139/162] AppVeyor: Use SignPath release cert/only sign tags The SignPath release certificate for our project is not yet available as of this writing, but this commit prepares the CI system to use the release certificate whenever it becomes available. It also restricts signing only to tags, which correspond to official releases. (That mimics our traditional policy of not signing pre-release builds.) --- appveyor.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 2e110ccb0..a04b4a479 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -94,9 +94,11 @@ test: off deploy: - provider: Webhook - url: https://app.signpath.io/API/v1/3bc964ce-257b-4362-829f-1df1f087f5a0/Integrations/AppVeyor?ProjectSlug=libjpeg-turbo&SigningPolicySlug=test-signing + url: https://app.signpath.io/API/v1/3bc964ce-257b-4362-829f-1df1f087f5a0/Integrations/AppVeyor?ProjectSlug=libjpeg-turbo&SigningPolicySlug=release-signing authorization: secure: rrx5wnu5VWQqrFZJv75WdPc1H5gyYqp7cV/xVhsx1TnVR7VyUaVEiCYm/64Fcx1zmEPHGyEBrlrbjuRHgDjURA== + on: + appveyor_repo_tag: true - provider: S3 access_key_id: secure: Z74OYogQ6bNV/I+6b5ZEXig74+6MW2WLER0v/bPM/uk= From 762f8b4f21716648cfaf36d63bde6bcddf2f82fa Mon Sep 17 00:00:00 2001 From: DRC Date: Wed, 5 Jul 2023 10:55:07 -0400 Subject: [PATCH 140/162] Doc: Mention that we are a JPEG ref implementation --- README.md | 4 +++- release/ReadMe.txt | 2 +- release/deb-control.in | 3 ++- release/rpm.spec.in | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a1eed3df3..119213f5f 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,13 @@ derivative of libjpeg v6b developed by Miyasaka Masaru. The TigerVNC and VirtualGL projects made numerous enhancements to the codec in 2009, and in early 2010, libjpeg-turbo spun off into an independent project, with the goal of making high-speed JPEG compression/decompression technology available to a -broader range of users and developers. +broader range of users and developers. libjpeg-turbo is an ISO/IEC and ITU-T +reference implementation of the JPEG standard. More information about libjpeg-turbo can be found at . + License ======= diff --git a/release/ReadMe.txt b/release/ReadMe.txt index 446ce46cf..a71d15c2f 100644 --- a/release/ReadMe.txt +++ b/release/ReadMe.txt @@ -2,4 +2,4 @@ libjpeg-turbo is a JPEG image codec that uses SIMD instructions to accelerate ba libjpeg-turbo implements both the traditional libjpeg API as well as the less powerful but more straightforward TurboJPEG API. libjpeg-turbo also features colorspace extensions that allow it to compress from/decompress to 32-bit and big-endian pixel buffers (RGBX, XBGR, etc.), as well as a full-featured Java interface. -libjpeg-turbo was originally based on libjpeg/SIMD, an MMX-accelerated derivative of libjpeg v6b developed by Miyasaka Masaru. The TigerVNC and VirtualGL projects made numerous enhancements to the codec in 2009, and in early 2010, libjpeg-turbo spun off into an independent project, with the goal of making high-speed JPEG compression/decompression technology available to a broader range of users and developers. +libjpeg-turbo was originally based on libjpeg/SIMD, an MMX-accelerated derivative of libjpeg v6b developed by Miyasaka Masaru. The TigerVNC and VirtualGL projects made numerous enhancements to the codec in 2009, and in early 2010, libjpeg-turbo spun off into an independent project, with the goal of making high-speed JPEG compression/decompression technology available to a broader range of users and developers. libjpeg-turbo is an ISO/IEC and ITU-T reference implementation of the JPEG standard. diff --git a/release/deb-control.in b/release/deb-control.in index 72bceec49..2c2ae3817 100644 --- a/release/deb-control.in +++ b/release/deb-control.in @@ -28,4 +28,5 @@ Description: A SIMD-accelerated JPEG codec that provides both the libjpeg and Tu VirtualGL projects made numerous enhancements to the codec in 2009, and in early 2010, libjpeg-turbo spun off into an independent project, with the goal of making high-speed JPEG compression/decompression technology available to a - broader range of users and developers. + broader range of users and developers. libjpeg-turbo is an ISO/IEC and ITU-T + reference implementation of the JPEG standard. diff --git a/release/rpm.spec.in b/release/rpm.spec.in index 33a365189..24872dbcd 100644 --- a/release/rpm.spec.in +++ b/release/rpm.spec.in @@ -77,7 +77,8 @@ derivative of libjpeg v6b developed by Miyasaka Masaru. The TigerVNC and VirtualGL projects made numerous enhancements to the codec in 2009, and in early 2010, libjpeg-turbo spun off into an independent project, with the goal of making high-speed JPEG compression/decompression technology available to a -broader range of users and developers. +broader range of users and developers. libjpeg-turbo is an ISO/IEC and ITU-T +reference implementation of the JPEG standard. #-->%prep #-->%setup -q -n @CMAKE_PROJECT_NAME@-%{version} From 926f1f3daf41148caa090f4476760e68f6aa2a59 Mon Sep 17 00:00:00 2001 From: DRC Date: Wed, 5 Jul 2023 12:41:32 -0400 Subject: [PATCH 141/162] README.md: Include GitHub Sponsors link/button Closes #706 --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 119213f5f..5e5416aff 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,21 @@ More information about libjpeg-turbo can be found at . +Funding +======= + +libjpeg-turbo is an independent open source project, but we rely on patronage +and funded development in order to maintain that independence. The easiest way +to ensure that libjpeg-turbo remains community-focused and free of any one +organization's agenda is to +[sponsor our project through GitHub](https://github.com/sponsors/libjpeg-turbo). +All sponsorship money goes directly toward funding the labor necessary to +maintain libjpeg-turbo, support the user community, and implement bug fixes and +strategically important features. + +[![Sponsor libjpeg-turbo](https://img.shields.io/github/sponsors/libjpeg-turbo?label=Sponsor&logo=GitHub)](https://github.com/sponsors/libjpeg-turbo) + + License ======= From 30c21e559d61a5060a8c4d8e235457379088327b Mon Sep 17 00:00:00 2001 From: DRC Date: Wed, 5 Jul 2023 13:51:35 -0400 Subject: [PATCH 142/162] OSS-Fuzz: Ignore tj3DecompressHeader() return val Unlike its predecessors, tj3DecompressHeader() does not fail if passed a JPEG image with an unknown subsampling type. This led me to believe that it was OK for the fuzzers to abort if tj3DecompressHeader() returned an error. However, there are apparently some malformed JPEG images that can expose issues in libjpeg-turbo while also causing tj3DecompressHeader() to complain about header errors. Thus, it is best to ignore the return value of tj3DecompressHeader(), as the fuzzers in libjpeg-turbo 2.1.x and prior did. --- fuzz/decompress.cc | 6 ++++-- fuzz/decompress_yuv.cc | 6 ++++-- fuzz/transform.cc | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/fuzz/decompress.cc b/fuzz/decompress.cc index 3db992214..eadc7342b 100644 --- a/fuzz/decompress.cc +++ b/fuzz/decompress.cc @@ -55,8 +55,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) if ((handle = tj3Init(TJINIT_DECOMPRESS)) == NULL) goto bailout; - if (tj3DecompressHeader(handle, data, size) < 0) - goto bailout; + /* We ignore the return value of tj3DecompressHeader(), because malformed + JPEG images that might expose issues in libjpeg-turbo might also have + header errors that cause tj3DecompressHeader() to fail. */ + tj3DecompressHeader(handle, data, size); width = tj3Get(handle, TJPARAM_JPEGWIDTH); height = tj3Get(handle, TJPARAM_JPEGHEIGHT); precision = tj3Get(handle, TJPARAM_PRECISION); diff --git a/fuzz/decompress_yuv.cc b/fuzz/decompress_yuv.cc index fc262efe4..4e869df62 100644 --- a/fuzz/decompress_yuv.cc +++ b/fuzz/decompress_yuv.cc @@ -55,8 +55,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) if ((handle = tj3Init(TJINIT_DECOMPRESS)) == NULL) goto bailout; - if (tj3DecompressHeader(handle, data, size) < 0) - goto bailout; + /* We ignore the return value of tj3DecompressHeader(), because malformed + JPEG images that might expose issues in libjpeg-turbo might also have + header errors that cause tj3DecompressHeader() to fail. */ + tj3DecompressHeader(handle, data, size); width = tj3Get(handle, TJPARAM_JPEGWIDTH); height = tj3Get(handle, TJPARAM_JPEGHEIGHT); jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP); diff --git a/fuzz/transform.cc b/fuzz/transform.cc index 06493883c..4cb585000 100644 --- a/fuzz/transform.cc +++ b/fuzz/transform.cc @@ -50,8 +50,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) if ((handle = tj3Init(TJINIT_TRANSFORM)) == NULL) goto bailout; - if (tj3DecompressHeader(handle, data, size) < 0) - goto bailout; + /* We ignore the return value of tj3DecompressHeader(), because malformed + JPEG images that might expose issues in libjpeg-turbo might also have + header errors that cause tj3DecompressHeader() to fail. */ + tj3DecompressHeader(handle, data, size); width = tj3Get(handle, TJPARAM_JPEGWIDTH); height = tj3Get(handle, TJPARAM_JPEGHEIGHT); jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP); From 895287572db62318332a78395a0c07eb86137fd8 Mon Sep 17 00:00:00 2001 From: DRC Date: Wed, 5 Jul 2023 15:35:21 -0400 Subject: [PATCH 143/162] xform fuzz: Use src subsamp to calc dst buf size Referring to https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=60379 there are some specially-crafted malformed JPEG images that, when transformed to grayscale, will exceed the worst-case transformed grayscale JPEG image size. This is similar in nature to the issue fixed by c8d52f1c4c7480277b91420c27b2548d4c8e9043, except that in this case, the issue occurs regardless of the amount of metadata in the source image. Also, the tj3Transform() function, the Java_org_libjpegturbo_turbojpeg_TJTransformer_transform() JNI function, and TJBench were behaving correctly in this case, because the TurboJPEG API documentation specifies that the source image's subsampling type should be used when computing the worst-case transformed JPEG image size. (However, only the Java API documentation specified that. Oops. The C API documentation now does as well.) The documented usage mitigates the issue, and only the transform fuzzer did not adhere to that. Thus, this was an issue with the fuzzer itself rather than an issue with the library. --- doc/html/group___turbo_j_p_e_g.html | 2 +- fuzz/transform.cc | 4 ++-- turbojpeg.h | 15 ++++++++------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/doc/html/group___turbo_j_p_e_g.html b/doc/html/group___turbo_j_p_e_g.html index 696af8a92..04285c254 100644 --- a/doc/html/group___turbo_j_p_e_g.html +++ b/doc/html/group___turbo_j_p_e_g.html @@ -3149,7 +3149,7 @@

      dstBufspointer to an array of n byte buffers. dstBufs[i] will receive a JPEG image that has been transformed using the parameters in transforms[i]. TurboJPEG has the ability to reallocate the JPEG destination buffer to accommodate the size of the transformed JPEG image. Thus, you can choose to:
      1. pre-allocate the JPEG destination buffer with an arbitrary size using tj3Alloc() and let TurboJPEG grow the buffer as needed,
      2. set dstBufs[i] to NULL to tell TurboJPEG to allocate the buffer for you, or
      3. -
      4. pre-allocate the buffer to a "worst case" size determined by calling tj3JPEGBufSize() with the transformed or cropped width and height. Under normal circumstances, this should ensure that the buffer never has to be re-allocated. (Setting TJPARAM_NOREALLOC guarantees that it won't be.) Note, however, that there are some rare cases (such as transforming images with a large amount of embedded EXIF or ICC profile data) in which the transformed JPEG image will be larger than the worst-case size, and TJPARAM_NOREALLOC cannot be used in those cases.
      5. +
      6. pre-allocate the buffer to a "worst case" size determined by calling tj3JPEGBufSize() with the transformed or cropped width and height and the level of subsampling used in the source image. Under normal circumstances, this should ensure that the buffer never has to be re-allocated. (Setting TJPARAM_NOREALLOC guarantees that it won't be.) Note, however, that there are some rare cases (such as transforming images with a large amount of embedded EXIF or ICC profile data) in which the transformed JPEG image will be larger than the worst-case size, and TJPARAM_NOREALLOC cannot be used in those cases.
      If you choose option 1, then dstSizes[i] should be set to the size of your pre-allocated buffer. In any case, unless you have set TJPARAM_NOREALLOC, you should always check dstBufs[i] upon return from this function, as it may have changed. dstSizespointer to an array of n size_t variables that will receive the actual sizes (in bytes) of each transformed JPEG image. If dstBufs[i] points to a pre-allocated buffer, then dstSizes[i] should be set to the size of the buffer. Upon return, dstSizes[i] will contain the size of the transformed JPEG image (in bytes.) diff --git a/fuzz/transform.cc b/fuzz/transform.cc index 4cb585000..65e24a42b 100644 --- a/fuzz/transform.cc +++ b/fuzz/transform.cc @@ -108,11 +108,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) transforms[0].options = TJXOPT_GRAY | TJXOPT_CROP | TJXOPT_COPYNONE; dstBufs[0] = (unsigned char *)malloc(tj3JPEGBufSize((height + 1) / 2, (width + 1) / 2, - TJSAMP_GRAY)); + jpegSubsamp)); if (!dstBufs[0]) goto bailout; - maxBufSize = tj3JPEGBufSize((height + 1) / 2, (width + 1) / 2, TJSAMP_GRAY); + maxBufSize = tj3JPEGBufSize((height + 1) / 2, (width + 1) / 2, jpegSubsamp); if (tj3Transform(handle, data, size, 1, dstBufs, dstSizes, transforms) == 0) { diff --git a/turbojpeg.h b/turbojpeg.h index 976b98b62..12efcbc76 100644 --- a/turbojpeg.h +++ b/turbojpeg.h @@ -1862,13 +1862,14 @@ DLLEXPORT int tj3DecodeYUVPlanes8(tjhandle handle, * -# set `dstBufs[i]` to NULL to tell TurboJPEG to allocate the buffer for * you, or * -# pre-allocate the buffer to a "worst case" size determined by calling - * #tj3JPEGBufSize() with the transformed or cropped width and height. Under - * normal circumstances, this should ensure that the buffer never has to be - * re-allocated. (Setting #TJPARAM_NOREALLOC guarantees that it won't be.) - * Note, however, that there are some rare cases (such as transforming images - * with a large amount of embedded EXIF or ICC profile data) in which the - * transformed JPEG image will be larger than the worst-case size, and - * #TJPARAM_NOREALLOC cannot be used in those cases. + * #tj3JPEGBufSize() with the transformed or cropped width and height and the + * level of subsampling used in the source image. Under normal circumstances, + * this should ensure that the buffer never has to be re-allocated. (Setting + * #TJPARAM_NOREALLOC guarantees that it won't be.) Note, however, that there + * are some rare cases (such as transforming images with a large amount of + * embedded EXIF or ICC profile data) in which the transformed JPEG image will + * be larger than the worst-case size, and #TJPARAM_NOREALLOC cannot be used in + * those cases. * . * If you choose option 1, then `dstSizes[i]` should be set to the size of your * pre-allocated buffer. In any case, unless you have set #TJPARAM_NOREALLOC, From d011622f4b5b2c3f0141e93fc3e1da6169915c18 Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 6 Jul 2023 10:29:27 -0400 Subject: [PATCH 144/162] Restore xform fuzzer behavior from before c8d52f1c The intent was for the final transform operation to be the same as the first transform operation but without TJXOPT_COPYNONE or TJPARAM_NOREALLOC. Unrolling the transform operations in c8d52f1c4c7480277b91420c27b2548d4c8e9043 accidentally changed that. --- fuzz/transform.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fuzz/transform.cc b/fuzz/transform.cc index 65e24a42b..351fa9c36 100644 --- a/fuzz/transform.cc +++ b/fuzz/transform.cc @@ -151,6 +151,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) transforms[0].options |= TJXOPT_OPTIMIZE; free(dstBufs[0]); dstBufs[0] = NULL; + + transforms[0].op = TJXOP_NONE; + transforms[0].options = TJXOPT_PROGRESSIVE; dstSizes[0] = 0; tj3Set(handle, TJPARAM_NOREALLOC, 0); From 035ea386d1b6a99a8a1e2ab57cc1fc903569136c Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 6 Jul 2023 12:04:22 -0400 Subject: [PATCH 145/162] Build: Fix regression test concurrency issues - The example-*bit-*-decompress test must run after the example-*bit-*-compress test, since the latter generates testout*-example.jpg. - Add -static to the filenames of all output files generated by the "static" regression tests, to avoid conflicts with the "shared" regression tests. - Add the PID to the filenames of all files generated by the tjunittest packed-pixel image I/O tests. - Check the return value of MD5File() in tjunittest to avoid a segfault if the file doesn't exist. (Prior to the fix described above, that could occur if two instances of tjunittest ran concurrently from the same directory with the same -bmp and -precision arguments.) Fixes #705 --- CMakeLists.txt | 10 ++++++---- tjunittest.c | 8 ++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index efd101a07..f0b9e9377 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1004,7 +1004,7 @@ foreach(libtype ${TEST_LIBTYPES}) if(sample_bits EQUAL 12) set(tjbench tjbench12) - set(testout testout12) + set(testout testout12${suffix}) set(MD5_PPM_GRAY_TILE 2f799249148b1a9d0e61fa4408f6c397) set(MD5_PPM_420_8x8_TILE b25684e1af37be504ee3fd137757353f) @@ -1024,7 +1024,7 @@ foreach(libtype ${TEST_LIBTYPES}) set(MD5_PPM_444_TILE 2f571a032e4dbc8ef40f75219d336b0b) else() set(tjbench tjbench) - set(testout testout) + set(testout testout${suffix}) set(MD5_PPM_GRAY_TILE 2c3b567086e6ca0c5e6d34ad8d6f6fe8) set(MD5_PPM_420_8x8_TILE efca1bdf0226df01777137778cf986ec) @@ -1160,7 +1160,7 @@ foreach(libtype ${TEST_LIBTYPES}) set(cjpeg cjpeg12) set(djpeg djpeg12) set(jpegtran jpegtran12) - set(testout testout12) + set(testout testout12${suffix}) set(TESTORIG testorig12.jpg) set(MD5_JPEG_RGB_ISLOW 9d7369207c520d37f2c1cbfcb82b2964) @@ -1222,7 +1222,7 @@ foreach(libtype ${TEST_LIBTYPES}) set(cjpeg cjpeg) set(djpeg djpeg) set(jpegtran jpegtran) - set(testout testout) + set(testout testout${suffix}) set(TESTORIG testorig.jpg) set(MD5_JPEG_RGB_ISLOW 1d44a406f61da743b5fd31c0a9abdca3) @@ -1632,6 +1632,8 @@ foreach(libtype ${TEST_LIBTYPES}) add_test(example-${sample_bits}bit-${libtype}-decompress ${CMAKE_CROSSCOMPILING_EMULATOR} example${suffix} decompress ${EXAMPLE_12BIT_ARG} ${testout}-example.jpg ${testout}-example.ppm) + set_tests_properties(example-${sample_bits}bit-${libtype}-decompress + PROPERTIES DEPENDS example-${sample_bits}bit-${libtype}-compress) add_test(example-${sample_bits}bit-${libtype}-decompress-cmp ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} ${MD5_PPM_EXAMPLE_DECOMPRESS} ${testout}-example.ppm) diff --git a/tjunittest.c b/tjunittest.c index bbe44bb17..b033c4f17 100644 --- a/tjunittest.c +++ b/tjunittest.c @@ -977,8 +977,8 @@ static int doBmpTest(const char *ext, int width, int align, int height, int pf, THROW("Could not allocate memory"); initBitmap(buf, width, pitch, height, pf, bottomUp); - SNPRINTF(filename, 80, "test_bmp%d_%s_%d_%s.%s", precision, pixFormatStr[pf], - align, bottomUp ? "bu" : "td", ext); + SNPRINTF(filename, 80, "test_bmp%d_%s_%d_%s_%d.%s", precision, pixFormatStr[pf], + align, bottomUp ? "bu" : "td", getpid(), ext); if (precision == 8) { TRY_TJ(handle, tj3SaveImage8(handle, filename, (unsigned char *)buf, width, pitch, height, pf)); @@ -990,6 +990,10 @@ static int doBmpTest(const char *ext, int width, int align, int height, int pf, width, pitch, height, pf)); } md5sum = MD5File(filename, md5buf); + if (!md5sum) { + printf("\n Could not determine MD5 sum of %s\n", filename); + retval = -1; goto bailout; + } if (strcasecmp(md5sum, md5ref)) THROW_MD5(filename, md5sum, md5ref); From e429e379b01dde9d1d92b5bbabd2a18ee75d7eaa Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 6 Jul 2023 16:58:20 -0400 Subject: [PATCH 146/162] tjunittest.c: Use _getpid() on Windows --- tjunittest.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tjunittest.c b/tjunittest.c index b033c4f17..dcd5006e9 100644 --- a/tjunittest.c +++ b/tjunittest.c @@ -46,7 +46,9 @@ #include "jconfigint.h" #ifdef _WIN32 #include +#include #define random() rand() +#define getpid() _getpid() #else #include #endif From d6914b6b1eb9be1cb4af7f42a7ccc116cbe6d297 Mon Sep 17 00:00:00 2001 From: DRC Date: Mon, 24 Jul 2023 16:41:18 -0400 Subject: [PATCH 147/162] CMakeLists.txt: Fix comment buglet --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f0b9e9377..2ddd0f1c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -895,7 +895,7 @@ if(CPU_TYPE STREQUAL "x86_64" OR CPU_TYPE STREQUAL "i386") set(DEFAULT_FLOATTEST8 no-fp-contract) # else we can't really set an intelligent default for i386. The appropriate # value could be no-fp-contract, fp-contract, 387, or msvc, depending on the - # compiler and compiler options. We leave it to the user to set FLOATTEST + # compiler and compiler options. We leave it to the user to set FLOATTEST8 # manually. endif() else() From 63bd71885f566dc65db9c9481fc1445f720451a7 Mon Sep 17 00:00:00 2001 From: DRC Date: Tue, 25 Jul 2023 10:01:42 -0400 Subject: [PATCH 148/162] Build: Unset FLOATTEST* by default for non-x86 Because libjpeg-turbo 3.0.x now supports multiple data precisions in the same build, the regression test system can test the 8-bit and 12-bit floating point DCT/IDCT algorithms separately. The expected MD5 sums for those tests are communicated to the test system using the FLOATTEST8 and FLOATTEST12 CMake variables. Whereas it is possible to intelligently set a default value for FLOATTEST8 when building for x86[-64] and a default value for FLOATTEST12 when building for x86-64, it is not possible with other architectures. (Refer to #705, #709, and #710.) Clang 14, for example, now enables FMA (fused multiply-add) instructions by default on architectures that support them, but with AArch64 builds, the results are not the same as when using GCC/AArch64 with FMA instructions enabled. Thus, setting FLOATTEST12=fp-contract doesn't make the tests pass. It was already impossible to intelligently set a default for FLOATTEST8 with i386 builds, but referring to #710, that appears to be the case with other non-x86-64 builds as well. Back in 1991, when Tom Lane first released libjpeg, some CPUs had floating point units and some didn't. It could take minutes to compress or decompress a 1-megapixel JPEG image using the "slow" integer DCT/IDCT algorithms, and the floating point algorithms were significantly faster on systems that had an FPU. On systems without FPUs, floating point math was emulated and really slow, so Tom also developed "fast" integer DCT/IDCT algorithms to speed up JPEG performance, at the expense of accuracy, on those systems. Because of libjpeg-turbo's SIMD extensions, the floating point algorithms are now significantly slower than the "slow" integer algorithms without being significantly more accurate, and the "fast" integer algorithms fail the ISO/ITU-T conformance tests without being any faster than the "slow" integer algorithms on x86 systems. Thus, the floating point and "fast" integer algorithms are considered legacy features. In order for the floating point regression tests to be useful, the results of the tests must be validated against an independent metric. (In other words, it wouldn't be useful to use the floating point DCT/IDCT algorithms to determine the expected results of the floating point DCT/IDCT algorithms.) In the past, I attempted without success to develop a low-level floating point test that would run at configure time and determine the appropriate default value of FLOATTEST*. Barring that approach, the only other possibilities would be: 1. Develop a test framework that compares the floating point results with a margin of error, as TJUnitTest does. However, that effort isn't justified unless it could also benefit non-legacy features. 2. Compare the floating point results against an expected MD5 sum, as we currently do. However, as previously described, it isn't possible in most cases to determine an appropriate default value for the expected MD5 sum. For the moment, it makes the most sense to disable the 8-bit floating point tests by default except with x86[-64] builds and to disable the 12-bit floating point tests by default except with x86-64 builds. That means that the floating point algorithms will still be regression tested when performing x86[-64] builds, but other types of builds will have to opt in to the same regression tests. Since the floating point DCT/IDCT algorithms are unlikely to change ever again (the only reason they still exist at all is to maintain backward compatibility with libjpeg), this seems like a reasonable tradeoff. --- CMakeLists.txt | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ddd0f1c8..c0f46375b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -893,18 +893,11 @@ if(CPU_TYPE STREQUAL "x86_64" OR CPU_TYPE STREQUAL "i386") set(DEFAULT_FLOATTEST8 sse) elseif(CPU_TYPE STREQUAL "x86_64") set(DEFAULT_FLOATTEST8 no-fp-contract) - # else we can't really set an intelligent default for i386. The appropriate - # value could be no-fp-contract, fp-contract, 387, or msvc, depending on the - # compiler and compiler options. We leave it to the user to set FLOATTEST8 - # manually. - endif() -else() - if((CPU_TYPE STREQUAL "powerpc" OR CPU_TYPE STREQUAL "arm64") AND - NOT CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT MSVC) - set(DEFAULT_FLOATTEST8 fp-contract) - else() - set(DEFAULT_FLOATTEST8 no-fp-contract) endif() +# else we can't really set an intelligent default for FLOATTEST8. The +# appropriate value could be no-fp-contract, fp-contract, 387, or msvc, +# depending on the compiler and compiler options. We leave it to the user to +# set FLOATTEST8 manually. endif() # This causes FLOATTEST8 to reset to the default value if WITH_SIMD has @@ -932,13 +925,10 @@ endif() if(CPU_TYPE STREQUAL "x86_64") set(DEFAULT_FLOATTEST12 no-fp-contract) -elseif(NOT CPU_TYPE STREQUAL "i386") - if((CPU_TYPE STREQUAL "powerpc" OR CPU_TYPE STREQUAL "arm64") AND - NOT CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT MSVC) - set(DEFAULT_FLOATTEST12 fp-contract) - else() - set(DEFAULT_FLOATTEST12 no-fp-contract) - endif() +# else we can't really set an intelligent default for FLOATTEST12. The +# appropriate value could be no-fp-contract, fp-contract, or something else, +# depending on the compiler and compiler options. We leave it to the user to +# set FLOATTEST12 manually. endif() set(FLOATTEST12 ${DEFAULT_FLOATTEST12} CACHE STRING From ebca79d508d424687707462675a4a5f09ab4429c Mon Sep 17 00:00:00 2001 From: DRC Date: Tue, 25 Jul 2023 16:46:07 -0400 Subject: [PATCH 149/162] xform fuzz: Test optimized baseline entropy coding Because of d011622f4b5b2c3f0141e93fc3e1da6169915c18, optimized baseline entropy coding wasn't actually being tested. --- fuzz/transform.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzz/transform.cc b/fuzz/transform.cc index 351fa9c36..8d2e6acaa 100644 --- a/fuzz/transform.cc +++ b/fuzz/transform.cc @@ -105,7 +105,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) transforms[0].r.w = (height + 1) / 2; transforms[0].r.h = (width + 1) / 2; transforms[0].op = TJXOP_TRANSPOSE; - transforms[0].options = TJXOPT_GRAY | TJXOPT_CROP | TJXOPT_COPYNONE; + transforms[0].options = TJXOPT_GRAY | TJXOPT_CROP | TJXOPT_COPYNONE | + TJXOPT_OPTIMIZE; dstBufs[0] = (unsigned char *)malloc(tj3JPEGBufSize((height + 1) / 2, (width + 1) / 2, jpegSubsamp)); @@ -148,7 +149,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) goto bailout; } - transforms[0].options |= TJXOPT_OPTIMIZE; free(dstBufs[0]); dstBufs[0] = NULL; From 300a344d653d4a8779706e42828d945c6a53ff9d Mon Sep 17 00:00:00 2001 From: DRC Date: Sun, 30 Jul 2023 11:02:29 -0400 Subject: [PATCH 150/162] tjexample.c: Fix error when recompressing (regression introduced by fc01f4673b71c0b833c59c21e8c4478a9c4bcf21) Because of the TurboJPEG 3 API overhaul, the pure compression code path in tjexample.c now creates a TurboJPEG compression instance prior to calling tj3LoadImage(). However, due to an oversight, a compression instance was no longer created in the recompression code path. Thus, that code path attempted to reuse the TurboJPEG decompression instance, which caused an error ("tj3Set(): TJPARAM_QUALITY is not applicable to decompression instances.") This commit modifies tjexample.c so that the recompression code path creates a new TurboJPEG compression instance if one has not already been created. Fixes #716 --- tjexample.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tjexample.c b/tjexample.c index 947ef2fed..579ebe537 100644 --- a/tjexample.c +++ b/tjexample.c @@ -350,6 +350,7 @@ int main(int argc, char **argv) pixelFormat) < 0) THROW_TJ("decompressing JPEG image"); tj3Free(jpegBuf); jpegBuf = NULL; + tj3Destroy(tjInstance); tjInstance = NULL; } else { /* Input image is not a JPEG image. Load it into memory. */ if ((tjInstance = tj3Init(TJINIT_COMPRESS)) == NULL) @@ -379,6 +380,8 @@ int main(int argc, char **argv) printf(", %s subsampling, quality = %d\n", subsampName[outSubsamp], outQual); + if (!tjInstance && (tjInstance = tj3Init(TJINIT_COMPRESS)) == NULL) + THROW_TJ("initializing compressor"); if (tj3Set(tjInstance, TJPARAM_SUBSAMP, outSubsamp) < 0) THROW_TJ("setting TJPARAM_SUBSAMP"); if (tj3Set(tjInstance, TJPARAM_QUALITY, outQual) < 0) From dbae59281fdc6b3a6304a40134e8576d50d662c0 Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 3 Aug 2023 14:42:30 -0400 Subject: [PATCH 151/162] Fix interblock smoothing with narrow prog. JPEGs Due to an oversight, the assignment of DC05, DC10, DC15, DC20, and DC25 (the right edge coefficients in the 5x5 interblock smoothing window) in decompress_smooth_data() was incorrect for images exactly two MCU blocks wide. For such images, DC04, DC09, DC14, DC19, and DC24 were assigned values based on the last MCU column, but DC05, DC10, DC15, DC20, and DC25 were assigned values based on the first MCU column (because block_num + 1 was never less than last_block_column.) This commit modifies jdcoefct.c so that, for images at least two MCU blocks wide, DC05, DC10, DC15, DC20, and DC25 are assigned the same values as DC04, DC09, DC14, DC19, and DC24 (respectively.) DC05, DC10, DC15, DC20, and DC25 are then immediately overwritten for images more than two MCU blocks wide. Since this issue was minor and not likely obvious to an end user, the fix is undocumented. Fixes #700 --- jdcoefct.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/jdcoefct.c b/jdcoefct.c index 85cbcb797..e68f68455 100644 --- a/jdcoefct.c +++ b/jdcoefct.c @@ -584,11 +584,11 @@ decompress_smooth_data(j_decompress_ptr cinfo, _JSAMPIMAGE output_buf) /* Update DC values */ if (block_num == cinfo->master->first_MCU_col[ci] && block_num < last_block_column) { - DC04 = (int)prev_prev_block_row[1][0]; - DC09 = (int)prev_block_row[1][0]; - DC14 = (int)buffer_ptr[1][0]; - DC19 = (int)next_block_row[1][0]; - DC24 = (int)next_next_block_row[1][0]; + DC04 = DC05 = (int)prev_prev_block_row[1][0]; + DC09 = DC10 = (int)prev_block_row[1][0]; + DC14 = DC15 = (int)buffer_ptr[1][0]; + DC19 = DC20 = (int)next_block_row[1][0]; + DC24 = DC25 = (int)next_next_block_row[1][0]; } if (block_num + 1 < last_block_column) { DC05 = (int)prev_prev_block_row[2][0]; From e17fa3a271bfc0d8ab4687f02fabb7d2c3bebd20 Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 27 Jul 2023 13:11:39 -0400 Subject: [PATCH 152/162] Bump version to 3.0.1 to prepare for new commits --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c0f46375b..3c3b040c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ if(CMAKE_EXECUTABLE_SUFFIX) endif() project(libjpeg-turbo C) -set(VERSION 3.0.0) +set(VERSION 3.0.1) set(COPYRIGHT_YEAR "1991-2023") string(REPLACE "." ";" VERSION_TRIPLET ${VERSION}) list(GET VERSION_TRIPLET 0 VERSION_MAJOR) From 7b844bfda67f68b7bd61a6394815b5a1628ee096 Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 28 Jul 2023 11:46:10 -0400 Subject: [PATCH 153/162] x86-64 SIMD: Use std stack frame/prologue/epilogue This allows debuggers and profilers to reliably capture backtraces from within the x86-64 SIMD functions. In places where rbp was previously used to access temporary variables (after stack alignment), we now use r15 and save/restore it accordingly. The total amount of work is approximately the same, because the previous code pushed the pre-alignment stack pointer to the aligned stack. The new prologue and epilogue actually have fewer instructions. Also note that the {un}collect_args macros now use rbp instead of rax to access arguments passed on the stack, so we save a few instructions there as well. Based on: https://github.com/alk/libjpeg-turbo/commit/debcc7c3b436467aea8d02c66a514c5099d0ad37 Closes #707 Closes #708 --- ChangeLog.md | 10 +++++++ simd/nasm/jsimdext.inc | 5 ++-- simd/x86_64/jccolext-avx2.asm | 17 ++++++----- simd/x86_64/jccolext-sse2.asm | 17 ++++++----- simd/x86_64/jcgryext-avx2.asm | 17 ++++++----- simd/x86_64/jcgryext-sse2.asm | 17 ++++++----- simd/x86_64/jchuff-sse2.asm | 56 +++++++++++++++++------------------ simd/x86_64/jcphuff-sse2.asm | 31 +++++++------------ simd/x86_64/jcsample-avx2.asm | 2 -- simd/x86_64/jcsample-sse2.asm | 2 -- simd/x86_64/jdcolext-avx2.asm | 17 ++++++----- simd/x86_64/jdcolext-sse2.asm | 17 ++++++----- simd/x86_64/jdmrgext-avx2.asm | 18 +++++------ simd/x86_64/jdmrgext-sse2.asm | 18 +++++------ simd/x86_64/jdsample-avx2.asm | 22 +++++++------- simd/x86_64/jdsample-sse2.asm | 20 ++++++------- simd/x86_64/jfdctflt-sse.asm | 17 ++++++----- simd/x86_64/jfdctfst-sse2.asm | 17 ++++++----- simd/x86_64/jfdctint-avx2.asm | 1 - simd/x86_64/jfdctint-sse2.asm | 17 ++++++----- simd/x86_64/jidctflt-sse2.asm | 17 +++++------ simd/x86_64/jidctfst-sse2.asm | 19 ++++++------ simd/x86_64/jidctint-avx2.asm | 1 - simd/x86_64/jidctint-sse2.asm | 19 ++++++------ simd/x86_64/jidctred-sse2.asm | 20 ++++++------- simd/x86_64/jquantf-sse2.asm | 2 -- simd/x86_64/jquanti-avx2.asm | 2 -- simd/x86_64/jquanti-sse2.asm | 2 -- simd/x86_64/jsimdcpu.asm | 4 +++ 29 files changed, 209 insertions(+), 215 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index e12b61786..8bccc1aad 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,13 @@ +3.0.1 +===== + +### Significant changes relative to 3.0.0: + +1. The x86-64 SIMD functions now use a standard stack frame, prologue, and +epilogue so that debuggers and profilers can reliably capture backtraces from +within the functions. + + 3.0.0 ===== diff --git a/simd/nasm/jsimdext.inc b/simd/nasm/jsimdext.inc index e8d50b034..bebcb2004 100644 --- a/simd/nasm/jsimdext.inc +++ b/simd/nasm/jsimdext.inc @@ -5,6 +5,7 @@ ; Copyright (C) 2010, 2016, 2018-2019, D. R. Commander. ; Copyright (C) 2018, Matthieu Darbois. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library - version 1.02 ; @@ -397,11 +398,11 @@ const_base: %endif %if %1 > 4 push r14 - mov r14, [rax+48] + mov r14, [rbp+48] %endif %if %1 > 5 push r15 - mov r15, [rax+56] + mov r15, [rbp+56] %endif push rsi push rdi diff --git a/simd/x86_64/jccolext-avx2.asm b/simd/x86_64/jccolext-avx2.asm index ffb527db0..dd7ea3985 100644 --- a/simd/x86_64/jccolext-avx2.asm +++ b/simd/x86_64/jccolext-avx2.asm @@ -4,6 +4,7 @@ ; Copyright (C) 2009, 2016, D. R. Commander. ; Copyright (C) 2015, Intel Corporation. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -33,7 +34,7 @@ ; r13d = JDIMENSION output_row ; r14d = int num_rows -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_YMMWORD ; ymmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_YMMWORD ; ymmword wk[WK_NUM] %define WK_NUM 8 align 32 @@ -41,12 +42,12 @@ EXTN(jsimd_rgb_ycc_convert_avx2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_YMMWORD) ; align to 256 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, (SIZEOF_YMMWORD * WK_NUM) collect_args 5 push rbx @@ -549,8 +550,8 @@ EXTN(jsimd_rgb_ycc_convert_avx2): pop rbx vzeroupper uncollect_args 5 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jccolext-sse2.asm b/simd/x86_64/jccolext-sse2.asm index af70ed601..bc1e8175e 100644 --- a/simd/x86_64/jccolext-sse2.asm +++ b/simd/x86_64/jccolext-sse2.asm @@ -3,6 +3,7 @@ ; ; Copyright (C) 2009, 2016, D. R. Commander. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -32,7 +33,7 @@ ; r13d = JDIMENSION output_row ; r14d = int num_rows -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 8 align 32 @@ -40,12 +41,12 @@ EXTN(jsimd_rgb_ycc_convert_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, (SIZEOF_XMMWORD * WK_NUM) collect_args 5 push rbx @@ -474,8 +475,8 @@ EXTN(jsimd_rgb_ycc_convert_sse2): .return: pop rbx uncollect_args 5 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jcgryext-avx2.asm b/simd/x86_64/jcgryext-avx2.asm index ddcc2c0a2..c8c8d12a2 100644 --- a/simd/x86_64/jcgryext-avx2.asm +++ b/simd/x86_64/jcgryext-avx2.asm @@ -4,6 +4,7 @@ ; Copyright (C) 2011, 2016, D. R. Commander. ; Copyright (C) 2015, Intel Corporation. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -33,7 +34,7 @@ ; r13d = JDIMENSION output_row ; r14d = int num_rows -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_YMMWORD ; ymmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_YMMWORD ; ymmword wk[WK_NUM] %define WK_NUM 2 align 32 @@ -41,12 +42,12 @@ EXTN(jsimd_rgb_gray_convert_avx2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_YMMWORD) ; align to 256 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (SIZEOF_YMMWORD * WK_NUM) collect_args 5 push rbx @@ -428,8 +429,8 @@ EXTN(jsimd_rgb_gray_convert_avx2): pop rbx vzeroupper uncollect_args 5 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jcgryext-sse2.asm b/simd/x86_64/jcgryext-sse2.asm index f1d399a63..7e5a0f2c3 100644 --- a/simd/x86_64/jcgryext-sse2.asm +++ b/simd/x86_64/jcgryext-sse2.asm @@ -3,6 +3,7 @@ ; ; Copyright (C) 2011, 2016, D. R. Commander. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -32,7 +33,7 @@ ; r13d = JDIMENSION output_row ; r14d = int num_rows -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 2 align 32 @@ -40,12 +41,12 @@ EXTN(jsimd_rgb_gray_convert_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (SIZEOF_XMMWORD * WK_NUM) collect_args 5 push rbx @@ -353,8 +354,8 @@ EXTN(jsimd_rgb_gray_convert_sse2): .return: pop rbx uncollect_args 5 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jchuff-sse2.asm b/simd/x86_64/jchuff-sse2.asm index 9ea6df946..a0eb9ce2d 100644 --- a/simd/x86_64/jchuff-sse2.asm +++ b/simd/x86_64/jchuff-sse2.asm @@ -1,9 +1,10 @@ ; ; jchuff-sse2.asm - Huffman entropy encoding (64-bit SSE2) ; -; Copyright (C) 2009-2011, 2014-2016, 2019, 2021, D. R. Commander. +; Copyright (C) 2009-2011, 2014-2016, 2019, 2021, 2023, D. R. Commander. ; Copyright (C) 2015, Matthieu Darbois. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -208,15 +209,15 @@ times 1 << 15 db 16 ; rax - buffer ; rbx - temp ; rcx - nbits -; rdx - block --> free_bits +; rdx - code ; rsi - nbits_base ; rdi - t -; rbp - code ; r8 - dctbl --> code_temp ; r9 - actbl ; r10 - state ; r11 - index ; r12 - put_buffer +; r15 - block --> free_bits %define buffer rax %ifdef WIN64 @@ -231,12 +232,11 @@ times 1 << 15 db 16 %define nbitsq rcx %define nbits ecx %define nbitsb cl -%define block rdx +%define codeq rdx +%define code edx %define nbits_base rsi %define t rdi %define td edi -%define codeq rbp -%define code ebp %define dctbl r8 %define actbl r9 %define state r10 @@ -244,6 +244,7 @@ times 1 << 15 db 16 %define indexd r11d %define put_buffer r12 %define put_bufferd r12d +%define block r15 ; Step 1: Re-arrange input data according to jpeg_natural_order ; xx 01 02 03 04 05 06 07 xx 01 08 16 09 02 03 10 @@ -259,6 +260,8 @@ times 1 << 15 db 16 GLOBAL_FUNCTION(jsimd_huff_encode_one_block_sse2) EXTN(jsimd_huff_encode_one_block_sse2): + push rbp + mov rbp, rsp %ifdef WIN64 @@ -266,15 +269,15 @@ EXTN(jsimd_huff_encode_one_block_sse2): ; rdx = JOCTET *buffer ; r8 = JCOEFPTR block ; r9 = int last_dc_val -; [rax+48] = c_derived_tbl *dctbl -; [rax+56] = c_derived_tbl *actbl +; [rbp+48] = c_derived_tbl *dctbl +; [rbp+56] = c_derived_tbl *actbl ;X: X = code stream mov buffer, rdx + push r15 mov block, r8 movups xmm3, XMMWORD [block + 0 * SIZEOF_WORD] ;D: w3 = xx 01 02 03 04 05 06 07 push rbx - push rbp movdqa xmm0, xmm3 ;A: w0 = xx 01 02 03 04 05 06 07 push rsi push rdi @@ -284,12 +287,10 @@ EXTN(jsimd_huff_encode_one_block_sse2): movsx code, word [block] ;Z: code = block[0]; pxor xmm4, xmm4 ;A: w4[i] = 0; sub code, r9d ;Z: code -= last_dc_val; - mov dctbl, POINTER [rsp+6*8+4*8] - mov actbl, POINTER [rsp+6*8+5*8] + mov dctbl, POINTER [rbp+48] + mov actbl, POINTER [rbp+56] punpckldq xmm0, xmm1 ;A: w0 = xx 01 08 09 02 03 10 11 lea nbits_base, [rel jpeg_nbits_table] - add rsp, -DCTSIZE2 * SIZEOF_WORD - mov t, rsp %else @@ -301,9 +302,10 @@ EXTN(jsimd_huff_encode_one_block_sse2): ; r9 = c_derived_tbl *actbl ;X: X = code stream + push r15 + mov block, rdx movups xmm3, XMMWORD [block + 0 * SIZEOF_WORD] ;D: w3 = xx 01 02 03 04 05 06 07 push rbx - push rbp movdqa xmm0, xmm3 ;A: w0 = xx 01 02 03 04 05 06 07 push r12 mov state, rdi @@ -314,10 +316,13 @@ EXTN(jsimd_huff_encode_one_block_sse2): pxor xmm4, xmm4 ;A: w4[i] = 0; sub codeq, rcx ;Z: code -= last_dc_val; punpckldq xmm0, xmm1 ;A: w0 = xx 01 08 09 02 03 10 11 - lea t, [rsp - DCTSIZE2 * SIZEOF_WORD] ; use red zone for t_ %endif + ; Allocate stack space for t array, and realign stack. + add rsp, -DCTSIZE2 * SIZEOF_WORD - 8 + mov t, rsp + pshuflw xmm0, xmm0, 11001001b ;A: w0 = 01 08 xx 09 02 03 10 11 pinsrw xmm0, word [block + 16 * SIZEOF_WORD], 2 ;A: w0 = 01 08 16 09 02 03 10 11 punpckhdq xmm3, xmm1 ;D: w3 = 04 05 12 13 06 07 14 15 @@ -443,9 +448,9 @@ EXTN(jsimd_huff_encode_one_block_sse2): pinsrw xmm5, word [block + 29 * SIZEOF_WORD], 7 ;E: w5 = 42 49 56 57 50 43 36 29 ; (Row 4, offset 1) %undef block -%define free_bitsq rdx -%define free_bitsd edx -%define free_bitsb dl +%define free_bitsq r15 +%define free_bitsd r15d +%define free_bitsb r15b pcmpeqw xmm1, xmm0 ;F: w1[i] = (w1[i] == 0 ? -1 : 0); shl tempq, 48 ;Z: temp <<= 48; pxor xmm2, xmm2 ;E: w2[i] = 0; @@ -534,12 +539,8 @@ EXTN(jsimd_huff_encode_one_block_sse2): test index, index jnz .BLOOP ; } while (index != 0); .ELOOP: ; } /* index != 0 */ - sub td, esp ; t -= (WIN64: &t_[0], UNIX: &t_[64]); -%ifdef WIN64 + sub td, esp ; t -= &t_[0]; cmp td, (DCTSIZE2 - 2) * SIZEOF_WORD ; if (t != 62) -%else - cmp td, -2 * SIZEOF_WORD ; if (t != -2) -%endif je .EFN ; { movzx nbits, byte [actbl + c_derived_tbl.ehufsi + 0] ; nbits = actbl->ehufsi[0]; @@ -556,18 +557,17 @@ EXTN(jsimd_huff_encode_one_block_sse2): ; state->cur.put_buffer.simd = put_buffer; mov byte [state + working_state.cur.free_bits], free_bitsb ; state->cur.free_bits = free_bits; -%ifdef WIN64 - sub rsp, -DCTSIZE2 * SIZEOF_WORD + sub rsp, -DCTSIZE2 * SIZEOF_WORD - 8 pop r12 +%ifdef WIN64 pop rdi pop rsi - pop rbp pop rbx %else - pop r12 - pop rbp pop rbx %endif + pop r15 + pop rbp ret ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/simd/x86_64/jcphuff-sse2.asm b/simd/x86_64/jcphuff-sse2.asm index 01b5c0235..11db4b2d9 100644 --- a/simd/x86_64/jcphuff-sse2.asm +++ b/simd/x86_64/jcphuff-sse2.asm @@ -3,6 +3,7 @@ ; (64-bit SSE2) ; ; Copyright (C) 2016, 2018, Matthieu Darbois +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -282,16 +283,12 @@ EXTN(jsimd_encode_mcu_AC_first_prepare_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [rbp - 16] + sub rsp, SIZEOF_XMMWORD + movdqa XMMWORD [rsp], ZERO collect_args 6 - movdqa XMMWORD [rbp - 16], ZERO - movd AL, r13d pxor ZERO, ZERO mov K, LEN @@ -384,10 +381,9 @@ EXTN(jsimd_encode_mcu_AC_first_prepare_sse2): REDUCE0 - movdqa ZERO, XMMWORD [rbp - 16] uncollect_args 6 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + movdqa ZERO, XMMWORD [rsp] + mov rsp, rbp pop rbp ret @@ -450,16 +446,12 @@ EXTN(jsimd_encode_mcu_AC_first_prepare_sse2): EXTN(jsimd_encode_mcu_AC_refine_prepare_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [rbp - 16] + sub rsp, SIZEOF_XMMWORD + movdqa XMMWORD [rsp], ZERO collect_args 6 - movdqa XMMWORD [rbp - 16], ZERO - xor SIGN, SIGN xor EOB, EOB xor KK, KK @@ -606,10 +598,9 @@ EXTN(jsimd_encode_mcu_AC_refine_prepare_sse2): REDUCE0 mov eax, EOB - movdqa ZERO, XMMWORD [rbp - 16] uncollect_args 6 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + movdqa ZERO, XMMWORD [rsp] + mov rsp, rbp pop rbp ret diff --git a/simd/x86_64/jcsample-avx2.asm b/simd/x86_64/jcsample-avx2.asm index b32527aeb..589c52bd1 100644 --- a/simd/x86_64/jcsample-avx2.asm +++ b/simd/x86_64/jcsample-avx2.asm @@ -45,7 +45,6 @@ EXTN(jsimd_h2v1_downsample_avx2): push rbp - mov rax, rsp mov rbp, rsp collect_args 6 @@ -207,7 +206,6 @@ EXTN(jsimd_h2v1_downsample_avx2): EXTN(jsimd_h2v2_downsample_avx2): push rbp - mov rax, rsp mov rbp, rsp collect_args 6 diff --git a/simd/x86_64/jcsample-sse2.asm b/simd/x86_64/jcsample-sse2.asm index 2fcfe4567..7a4f1bc37 100644 --- a/simd/x86_64/jcsample-sse2.asm +++ b/simd/x86_64/jcsample-sse2.asm @@ -44,7 +44,6 @@ EXTN(jsimd_h2v1_downsample_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 6 @@ -189,7 +188,6 @@ EXTN(jsimd_h2v1_downsample_sse2): EXTN(jsimd_h2v2_downsample_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 6 diff --git a/simd/x86_64/jdcolext-avx2.asm b/simd/x86_64/jdcolext-avx2.asm index 2370fda64..070436c24 100644 --- a/simd/x86_64/jdcolext-avx2.asm +++ b/simd/x86_64/jdcolext-avx2.asm @@ -5,6 +5,7 @@ ; Copyright (C) 2009, 2012, 2016, D. R. Commander. ; Copyright (C) 2015, Intel Corporation. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -34,7 +35,7 @@ ; r13 = JSAMPARRAY output_buf ; r14d = int num_rows -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_YMMWORD ; ymmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_YMMWORD ; ymmword wk[WK_NUM] %define WK_NUM 2 align 32 @@ -42,12 +43,12 @@ EXTN(jsimd_ycc_rgb_convert_avx2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_YMMWORD) ; align to 256 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (WK_NUM * SIZEOF_YMMWORD) collect_args 5 push rbx @@ -486,8 +487,8 @@ EXTN(jsimd_ycc_rgb_convert_avx2): pop rbx vzeroupper uncollect_args 5 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jdcolext-sse2.asm b/simd/x86_64/jdcolext-sse2.asm index e07c8d751..bba3a30c7 100644 --- a/simd/x86_64/jdcolext-sse2.asm +++ b/simd/x86_64/jdcolext-sse2.asm @@ -4,6 +4,7 @@ ; Copyright 2009, 2012 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2012, 2016, D. R. Commander. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -33,7 +34,7 @@ ; r13 = JSAMPARRAY output_buf ; r14d = int num_rows -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 2 align 32 @@ -41,12 +42,12 @@ EXTN(jsimd_ycc_rgb_convert_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (SIZEOF_XMMWORD * WK_NUM) collect_args 5 push rbx @@ -429,8 +430,8 @@ EXTN(jsimd_ycc_rgb_convert_sse2): .return: pop rbx uncollect_args 5 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jdmrgext-avx2.asm b/simd/x86_64/jdmrgext-avx2.asm index 8b264b4f0..11916455c 100644 --- a/simd/x86_64/jdmrgext-avx2.asm +++ b/simd/x86_64/jdmrgext-avx2.asm @@ -5,6 +5,7 @@ ; Copyright (C) 2009, 2012, 2016, D. R. Commander. ; Copyright (C) 2015, Intel Corporation. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -34,7 +35,7 @@ ; r12d = JDIMENSION in_row_group_ctr ; r13 = JSAMPARRAY output_buf -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_YMMWORD ; ymmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_YMMWORD ; ymmword wk[WK_NUM] %define WK_NUM 3 align 32 @@ -42,12 +43,12 @@ EXTN(jsimd_h2v1_merged_upsample_avx2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_YMMWORD) ; align to 256 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, SIZEOF_YMMWORD * WK_NUM collect_args 4 push rbx @@ -480,8 +481,8 @@ EXTN(jsimd_h2v1_merged_upsample_avx2): pop rbx vzeroupper uncollect_args 4 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret @@ -506,7 +507,6 @@ EXTN(jsimd_h2v1_merged_upsample_avx2): EXTN(jsimd_h2v2_merged_upsample_avx2): push rbp - mov rax, rsp mov rbp, rsp collect_args 4 push rbx diff --git a/simd/x86_64/jdmrgext-sse2.asm b/simd/x86_64/jdmrgext-sse2.asm index eb3ab9dbd..8988dd0bd 100644 --- a/simd/x86_64/jdmrgext-sse2.asm +++ b/simd/x86_64/jdmrgext-sse2.asm @@ -4,6 +4,7 @@ ; Copyright 2009, 2012 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2012, 2016, D. R. Commander. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -33,7 +34,7 @@ ; r12d = JDIMENSION in_row_group_ctr ; r13 = JSAMPARRAY output_buf -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 3 align 32 @@ -41,12 +42,12 @@ EXTN(jsimd_h2v1_merged_upsample_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (SIZEOF_XMMWORD * WK_NUM) collect_args 4 push rbx @@ -422,8 +423,8 @@ EXTN(jsimd_h2v1_merged_upsample_sse2): .return: pop rbx uncollect_args 4 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret @@ -448,7 +449,6 @@ EXTN(jsimd_h2v1_merged_upsample_sse2): EXTN(jsimd_h2v2_merged_upsample_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 4 push rbx diff --git a/simd/x86_64/jdsample-avx2.asm b/simd/x86_64/jdsample-avx2.asm index 1e4979f93..c6ddbb5ed 100644 --- a/simd/x86_64/jdsample-avx2.asm +++ b/simd/x86_64/jdsample-avx2.asm @@ -5,6 +5,7 @@ ; Copyright (C) 2009, 2016, D. R. Commander. ; Copyright (C) 2015, Intel Corporation. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -62,7 +63,6 @@ PW_EIGHT times 16 dw 8 EXTN(jsimd_h2v1_fancy_upsample_avx2): push rbp - mov rax, rsp mov rbp, rsp push_xmm 3 collect_args 4 @@ -208,7 +208,7 @@ EXTN(jsimd_h2v1_fancy_upsample_avx2): ; r12 = JSAMPARRAY input_data ; r13 = JSAMPARRAY *output_data_ptr -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_YMMWORD ; ymmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_YMMWORD ; ymmword wk[WK_NUM] %define WK_NUM 4 align 32 @@ -216,12 +216,12 @@ EXTN(jsimd_h2v1_fancy_upsample_avx2): EXTN(jsimd_h2v2_fancy_upsample_avx2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 - and rsp, byte (-SIZEOF_YMMWORD) ; align to 256 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + mov rbp, rsp + push r15 + and rsp, byte (-SIZEOF_YMMWORD) ; align to 128 bits + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, (SIZEOF_YMMWORD * WK_NUM) push_xmm 3 collect_args 4 push rbx @@ -500,8 +500,8 @@ EXTN(jsimd_h2v2_fancy_upsample_avx2): vzeroupper uncollect_args 4 pop_xmm 3 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret @@ -525,7 +525,6 @@ EXTN(jsimd_h2v2_fancy_upsample_avx2): EXTN(jsimd_h2v1_upsample_avx2): push rbp - mov rax, rsp mov rbp, rsp collect_args 4 @@ -614,7 +613,6 @@ EXTN(jsimd_h2v1_upsample_avx2): EXTN(jsimd_h2v2_upsample_avx2): push rbp - mov rax, rsp mov rbp, rsp collect_args 4 push rbx diff --git a/simd/x86_64/jdsample-sse2.asm b/simd/x86_64/jdsample-sse2.asm index 38dbceec2..24cd38964 100644 --- a/simd/x86_64/jdsample-sse2.asm +++ b/simd/x86_64/jdsample-sse2.asm @@ -4,6 +4,7 @@ ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, D. R. Commander. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -61,7 +62,6 @@ PW_EIGHT times 8 dw 8 EXTN(jsimd_h2v1_fancy_upsample_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 4 @@ -195,7 +195,7 @@ EXTN(jsimd_h2v1_fancy_upsample_sse2): ; r12 = JSAMPARRAY input_data ; r13 = JSAMPARRAY *output_data_ptr -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 4 align 32 @@ -203,12 +203,12 @@ EXTN(jsimd_h2v1_fancy_upsample_sse2): EXTN(jsimd_h2v2_fancy_upsample_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (SIZEOF_XMMWORD * WK_NUM) collect_args 4 push rbx @@ -473,8 +473,8 @@ EXTN(jsimd_h2v2_fancy_upsample_sse2): .return: pop rbx uncollect_args 4 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret @@ -498,7 +498,6 @@ EXTN(jsimd_h2v2_fancy_upsample_sse2): EXTN(jsimd_h2v1_upsample_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 4 @@ -585,7 +584,6 @@ EXTN(jsimd_h2v1_upsample_sse2): EXTN(jsimd_h2v2_upsample_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 4 push rbx diff --git a/simd/x86_64/jfdctflt-sse.asm b/simd/x86_64/jfdctflt-sse.asm index ef2796649..359549641 100644 --- a/simd/x86_64/jfdctflt-sse.asm +++ b/simd/x86_64/jfdctflt-sse.asm @@ -3,6 +3,7 @@ ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, D. R. Commander. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -58,7 +59,7 @@ PD_1_306 times 4 dd 1.306562964876376527856643 ; r10 = FAST_FLOAT *data -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 2 align 32 @@ -66,12 +67,12 @@ PD_1_306 times 4 dd 1.306562964876376527856643 EXTN(jsimd_fdct_float_sse): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (SIZEOF_XMMWORD * WK_NUM) collect_args 1 ; ---- Pass 1: process rows. @@ -345,8 +346,8 @@ EXTN(jsimd_fdct_float_sse): jnz near .columnloop uncollect_args 1 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jfdctfst-sse2.asm b/simd/x86_64/jfdctfst-sse2.asm index 2e1bfe6e8..d33c58a4f 100644 --- a/simd/x86_64/jfdctfst-sse2.asm +++ b/simd/x86_64/jfdctfst-sse2.asm @@ -3,6 +3,7 @@ ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, D. R. Commander. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -73,7 +74,7 @@ PW_F1306 times 8 dw F_1_306 << CONST_SHIFT ; r10 = DCTELEM *data -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 2 align 32 @@ -81,12 +82,12 @@ PW_F1306 times 8 dw F_1_306 << CONST_SHIFT EXTN(jsimd_fdct_ifast_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (SIZEOF_XMMWORD * WK_NUM) collect_args 1 ; ---- Pass 1: process rows. @@ -379,8 +380,8 @@ EXTN(jsimd_fdct_ifast_sse2): movdqa XMMWORD [XMMBLOCK(1,0,rdx,SIZEOF_DCTELEM)], xmm2 uncollect_args 1 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jfdctint-avx2.asm b/simd/x86_64/jfdctint-avx2.asm index e56258b48..d0afe5e81 100644 --- a/simd/x86_64/jfdctint-avx2.asm +++ b/simd/x86_64/jfdctint-avx2.asm @@ -261,7 +261,6 @@ PW_1_NEG1 times 8 dw 1 EXTN(jsimd_fdct_islow_avx2): push rbp - mov rax, rsp mov rbp, rsp collect_args 1 diff --git a/simd/x86_64/jfdctint-sse2.asm b/simd/x86_64/jfdctint-sse2.asm index ec1f383cc..024ce9001 100644 --- a/simd/x86_64/jfdctint-sse2.asm +++ b/simd/x86_64/jfdctint-sse2.asm @@ -3,6 +3,7 @@ ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2020, D. R. Commander. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -94,7 +95,7 @@ PW_DESCALE_P2X times 8 dw 1 << (PASS1_BITS - 1) ; r10 = DCTELEM *data -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 6 align 32 @@ -102,12 +103,12 @@ PW_DESCALE_P2X times 8 dw 1 << (PASS1_BITS - 1) EXTN(jsimd_fdct_islow_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (SIZEOF_XMMWORD * WK_NUM) collect_args 1 ; ---- Pass 1: process rows. @@ -609,8 +610,8 @@ EXTN(jsimd_fdct_islow_sse2): movdqa XMMWORD [XMMBLOCK(3,0,rdx,SIZEOF_DCTELEM)], xmm3 uncollect_args 1 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jidctflt-sse2.asm b/simd/x86_64/jidctflt-sse2.asm index 60bf96189..952fbe30f 100644 --- a/simd/x86_64/jidctflt-sse2.asm +++ b/simd/x86_64/jidctflt-sse2.asm @@ -4,6 +4,7 @@ ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, D. R. Commander. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -65,8 +66,7 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE ; r12 = JSAMPARRAY output_buf ; r13d = JDIMENSION output_col -%define original_rbp rbp + 0 -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 2 %define workspace wk(0) - DCTSIZE2 * SIZEOF_FAST_FLOAT @@ -77,11 +77,11 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE EXTN(jsimd_idct_float_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp lea rsp, [workspace] collect_args 4 push rbx @@ -322,7 +322,6 @@ EXTN(jsimd_idct_float_sse2): ; ---- Pass 2: process rows from work array, store into output array. - mov rax, [original_rbp] lea rsi, [workspace] ; FAST_FLOAT *wsptr mov rdi, r12 ; (JSAMPROW *) mov eax, r13d @@ -472,8 +471,8 @@ EXTN(jsimd_idct_float_sse2): pop rbx uncollect_args 4 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jidctfst-sse2.asm b/simd/x86_64/jidctfst-sse2.asm index cb97fdfbb..a3da8d87f 100644 --- a/simd/x86_64/jidctfst-sse2.asm +++ b/simd/x86_64/jidctfst-sse2.asm @@ -4,6 +4,7 @@ ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, D. R. Commander. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -86,8 +87,7 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE ; r12 = JSAMPARRAY output_buf ; r13d = JDIMENSION output_col -%define original_rbp rbp + 0 -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 2 @@ -96,12 +96,12 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE EXTN(jsimd_idct_ifast_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (SIZEOF_XMMWORD * WK_NUM) collect_args 4 ; ---- Pass 1: process columns from input. @@ -320,7 +320,6 @@ EXTN(jsimd_idct_ifast_sse2): ; ---- Pass 2: process rows from work array, store into output array. - mov rax, [original_rbp] mov rdi, r12 ; (JSAMPROW *) mov eax, r13d @@ -480,8 +479,8 @@ EXTN(jsimd_idct_ifast_sse2): movq XMM_MMWORD [rsi+rax*SIZEOF_JSAMPLE], xmm2 uncollect_args 4 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret ret diff --git a/simd/x86_64/jidctint-avx2.asm b/simd/x86_64/jidctint-avx2.asm index ca7e317f6..528da012e 100644 --- a/simd/x86_64/jidctint-avx2.asm +++ b/simd/x86_64/jidctint-avx2.asm @@ -283,7 +283,6 @@ PW_1_NEG1 times 8 dw 1 EXTN(jsimd_idct_islow_avx2): push rbp - mov rax, rsp ; rax = original rbp mov rbp, rsp ; rbp = aligned rbp push_xmm 4 collect_args 4 diff --git a/simd/x86_64/jidctint-sse2.asm b/simd/x86_64/jidctint-sse2.asm index 7aa869bc0..92f633e99 100644 --- a/simd/x86_64/jidctint-sse2.asm +++ b/simd/x86_64/jidctint-sse2.asm @@ -4,6 +4,7 @@ ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2020, D. R. Commander. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -99,8 +100,7 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE ; r12 = JSAMPARRAY output_buf ; r13d = JDIMENSION output_col -%define original_rbp rbp + 0 -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 12 @@ -109,12 +109,12 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE EXTN(jsimd_idct_islow_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, (SIZEOF_XMMWORD * WK_NUM) collect_args 4 ; ---- Pass 1: process columns from input. @@ -512,7 +512,6 @@ EXTN(jsimd_idct_islow_sse2): ; ---- Pass 2: process rows from work array, store into output array. - mov rax, [original_rbp] mov rdi, r12 ; (JSAMPROW *) mov eax, r13d @@ -837,8 +836,8 @@ EXTN(jsimd_idct_islow_sse2): movq XMM_MMWORD [rsi+rax*SIZEOF_JSAMPLE], xmm5 uncollect_args 4 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jidctred-sse2.asm b/simd/x86_64/jidctred-sse2.asm index 4ece9d891..1ec500c98 100644 --- a/simd/x86_64/jidctred-sse2.asm +++ b/simd/x86_64/jidctred-sse2.asm @@ -4,6 +4,7 @@ ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, D. R. Commander. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -107,8 +108,7 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE ; r12 = JSAMPARRAY output_buf ; r13d = JDIMENSION output_col -%define original_rbp rbp + 0 -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 2 @@ -117,12 +117,12 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE EXTN(jsimd_idct_4x4_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (SIZEOF_XMMWORD * WK_NUM) collect_args 4 ; ---- Pass 1: process columns from input. @@ -309,7 +309,6 @@ EXTN(jsimd_idct_4x4_sse2): ; ---- Pass 2: process rows, store into output array. - mov rax, [original_rbp] mov rdi, r12 ; (JSAMPROW *) mov eax, r13d @@ -390,8 +389,8 @@ EXTN(jsimd_idct_4x4_sse2): movd XMM_DWORD [rsi+rax*SIZEOF_JSAMPLE], xmm3 uncollect_args 4 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret @@ -415,7 +414,6 @@ EXTN(jsimd_idct_4x4_sse2): EXTN(jsimd_idct_2x2_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 4 push rbx diff --git a/simd/x86_64/jquantf-sse2.asm b/simd/x86_64/jquantf-sse2.asm index ab2e3954f..232bbb2dc 100644 --- a/simd/x86_64/jquantf-sse2.asm +++ b/simd/x86_64/jquantf-sse2.asm @@ -38,7 +38,6 @@ EXTN(jsimd_convsamp_float_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 3 push rbx @@ -111,7 +110,6 @@ EXTN(jsimd_convsamp_float_sse2): EXTN(jsimd_quantize_float_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 3 diff --git a/simd/x86_64/jquanti-avx2.asm b/simd/x86_64/jquanti-avx2.asm index 70fe81139..66104d7d4 100644 --- a/simd/x86_64/jquanti-avx2.asm +++ b/simd/x86_64/jquanti-avx2.asm @@ -39,7 +39,6 @@ EXTN(jsimd_convsamp_avx2): push rbp - mov rax, rsp mov rbp, rsp collect_args 3 @@ -117,7 +116,6 @@ EXTN(jsimd_convsamp_avx2): EXTN(jsimd_quantize_avx2): push rbp - mov rax, rsp mov rbp, rsp collect_args 3 diff --git a/simd/x86_64/jquanti-sse2.asm b/simd/x86_64/jquanti-sse2.asm index 3ee442027..11e9f4c42 100644 --- a/simd/x86_64/jquanti-sse2.asm +++ b/simd/x86_64/jquanti-sse2.asm @@ -38,7 +38,6 @@ EXTN(jsimd_convsamp_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 3 push rbx @@ -117,7 +116,6 @@ EXTN(jsimd_convsamp_sse2): EXTN(jsimd_quantize_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 3 diff --git a/simd/x86_64/jsimdcpu.asm b/simd/x86_64/jsimdcpu.asm index 705f813d7..251bc4cda 100644 --- a/simd/x86_64/jsimdcpu.asm +++ b/simd/x86_64/jsimdcpu.asm @@ -3,6 +3,7 @@ ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, D. R. Commander. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on ; x86 SIMD extension for IJG JPEG library @@ -31,6 +32,8 @@ GLOBAL_FUNCTION(jpeg_simd_cpu_support) EXTN(jpeg_simd_cpu_support): + push rbp + mov rbp, rsp push rbx push rdi @@ -79,6 +82,7 @@ EXTN(jpeg_simd_cpu_support): pop rdi pop rbx + pop rbp ret ; For some reason, the OS X linker does not honor the request to align the From 9b704f96b2dccc54363ad7a2fe8e378fc1a2893b Mon Sep 17 00:00:00 2001 From: DRC Date: Tue, 15 Aug 2023 11:03:57 -0400 Subject: [PATCH 154/162] Fix block smoothing w/vert.-subsampled prog. JPEGs The 5x5 interblock smoothing implementation, introduced in libjpeg-turbo 2.1, improperly extended the logic from the traditional 3x3 smoothing implementation. Both implementations point prev_block_row and next_block_row to the current block row when processing, respectively, the first and the last block row in the image: if (block_row > 0 || cinfo->output_iMCU_row > 0) prev_block_row = buffer[block_row - 1] + cinfo->master->first_MCU_col[ci]; else prev_block_row = buffer_ptr; if (block_row < block_rows - 1 || cinfo->output_iMCU_row < last_iMCU_row) next_block_row = buffer[block_row + 1] + cinfo->master->first_MCU_col[ci]; else next_block_row = buffer_ptr; 6d91e950c871103a11bac2f10c63bf998796c719 naively extended that logic to accommodate a 5x5 smoothing window: if (block_row > 1 || cinfo->output_iMCU_row > 1) prev_prev_block_row = buffer[block_row - 2] + cinfo->master->first_MCU_col[ci]; else prev_prev_block_row = prev_block_row; if (block_row < block_rows - 2 || cinfo->output_iMCU_row + 1 < last_iMCU_row) next_next_block_row = buffer[block_row + 2] + cinfo->master->first_MCU_col[ci]; else next_next_block_row = next_block_row; However, this new logic was only correct if block_rows == 1, so the values of prev_prev_block_row and next_next_block_row were incorrect when processing, respectively, the second and second to last iMCU rows in a vertically-subsampled progressive JPEG image. The intent was to: - point prev_block_row to the current block row when processing the first block row in the image, - point prev_prev_block_row to prev_block_row when processing the first two block rows in the image, - point next_block_row to the current block row when processing the last block row in the image, and - point next_next_block_row to next_block_row when processing the last two block rows in the image. This commit modifies decompress_smooth_data() so that it computes the current block row's position relative to the whole image and sets the block row pointers based on that value. This commit also restores a line of code that was accidentally deleted by 6d91e950c871103a11bac2f10c63bf998796c719: access_rows += compptr->v_samp_factor; /* prior iMCU row too */ access_rows is merely a sanity check that tells the access_virt_barray() method to generate an error if accessing the specified number of rows would cause a buffer overrun. Essentially, it is a belt-and-suspenders measure to ensure that j*init_d_coef_controller() allocated enough rows for the full-image virtual array. Thus, excluding that line of code did not cause an observable issue. This commit also documents dbae59281fdc6b3a6304a40134e8576d50d662c0 in the change log. Fixes #721 --- ChangeLog.md | 5 +++++ jdcoefct.c | 17 ++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 8bccc1aad..2f3ebd546 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -7,6 +7,11 @@ epilogue so that debuggers and profilers can reliably capture backtraces from within the functions. +2. Fixed two minor issues in the interblock smoothing algorithm that caused +mathematical (but not necessarily perceptible) edge block errors when +decompressing progressive JPEG images exactly two MCU blocks in width or that +use vertical chrominance subsampling. + 3.0.0 ===== diff --git a/jdcoefct.c b/jdcoefct.c index e68f68455..40ce27259 100644 --- a/jdcoefct.c +++ b/jdcoefct.c @@ -5,7 +5,7 @@ * Copyright (C) 1994-1997, Thomas G. Lane. * libjpeg-turbo Modifications: * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2010, 2015-2016, 2019-2020, 2022, D. R. Commander. + * Copyright (C) 2010, 2015-2016, 2019-2020, 2022-2023, D. R. Commander. * Copyright (C) 2015, 2020, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -431,7 +431,8 @@ decompress_smooth_data(j_decompress_ptr cinfo, _JSAMPIMAGE output_buf) my_coef_ptr coef = (my_coef_ptr)cinfo->coef; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; JDIMENSION block_num, last_block_column; - int ci, block_row, block_rows, access_rows; + int ci, block_row, block_rows, access_rows, image_block_row, + image_block_rows; JBLOCKARRAY buffer; JBLOCKROW buffer_ptr, prev_prev_block_row, prev_block_row; JBLOCKROW next_block_row, next_next_block_row; @@ -497,6 +498,7 @@ decompress_smooth_data(j_decompress_ptr cinfo, _JSAMPIMAGE output_buf) (JDIMENSION)access_rows, FALSE); buffer += 2 * compptr->v_samp_factor; /* point to current iMCU row */ } else if (cinfo->output_iMCU_row > 0) { + access_rows += compptr->v_samp_factor; /* prior iMCU row too */ buffer = (*cinfo->mem->access_virt_barray) ((j_common_ptr)cinfo, coef->whole_image[ci], (cinfo->output_iMCU_row - 1) * compptr->v_samp_factor, @@ -539,29 +541,30 @@ decompress_smooth_data(j_decompress_ptr cinfo, _JSAMPIMAGE output_buf) inverse_DCT = cinfo->idct->_inverse_DCT[ci]; output_ptr = output_buf[ci]; /* Loop over all DCT blocks to be processed. */ + image_block_rows = block_rows * cinfo->total_iMCU_rows; for (block_row = 0; block_row < block_rows; block_row++) { + image_block_row = cinfo->output_iMCU_row * block_rows + block_row; buffer_ptr = buffer[block_row] + cinfo->master->first_MCU_col[ci]; - if (block_row > 0 || cinfo->output_iMCU_row > 0) + if (image_block_row > 0) prev_block_row = buffer[block_row - 1] + cinfo->master->first_MCU_col[ci]; else prev_block_row = buffer_ptr; - if (block_row > 1 || cinfo->output_iMCU_row > 1) + if (image_block_row > 1) prev_prev_block_row = buffer[block_row - 2] + cinfo->master->first_MCU_col[ci]; else prev_prev_block_row = prev_block_row; - if (block_row < block_rows - 1 || cinfo->output_iMCU_row < last_iMCU_row) + if (image_block_row < image_block_rows - 1) next_block_row = buffer[block_row + 1] + cinfo->master->first_MCU_col[ci]; else next_block_row = buffer_ptr; - if (block_row < block_rows - 2 || - cinfo->output_iMCU_row + 1 < last_iMCU_row) + if (image_block_row < image_block_rows - 2) next_next_block_row = buffer[block_row + 2] + cinfo->master->first_MCU_col[ci]; else From f3c7116eaf3eed9af081150a4a3353f105c15389 Mon Sep 17 00:00:00 2001 From: DRC Date: Mon, 11 Sep 2023 12:46:13 -0400 Subject: [PATCH 155/162] jpeglib.h: Document that JCS_RGB565 is decomp-only Closes #723 --- jpeglib.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/jpeglib.h b/jpeglib.h index 8c8130973..a59e98c25 100644 --- a/jpeglib.h +++ b/jpeglib.h @@ -7,7 +7,8 @@ * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: - * Copyright (C) 2009-2011, 2013-2014, 2016-2017, 2020, 2022, D. R. Commander. + * Copyright (C) 2009-2011, 2013-2014, 2016-2017, 2020, 2022-2023, + D. R. Commander. * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -270,7 +271,8 @@ typedef enum { JCS_EXT_BGRA, /* blue/green/red/alpha */ JCS_EXT_ABGR, /* alpha/blue/green/red */ JCS_EXT_ARGB, /* alpha/red/green/blue */ - JCS_RGB565 /* 5-bit red/6-bit green/5-bit blue */ + JCS_RGB565 /* 5-bit red/6-bit green/5-bit blue + [decompression only] */ } J_COLOR_SPACE; /* DCT/IDCT algorithm options. */ From c0412b56d66b287817009afca7c105feb674cd7c Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 14 Sep 2023 17:19:36 -0400 Subject: [PATCH 156/162] ChangeLog.md: List CVE ID fixed by ccaba5d7 Closes #724 --- ChangeLog.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 2f3ebd546..ad0dc95b3 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -413,9 +413,9 @@ transform a specially-crafted malformed JPEG image. ### Significant changes relative to 2.1 beta1: -1. Fixed a regression introduced by 2.1 beta1[6(b)] whereby attempting to -decompress certain progressive JPEG images with one or more component planes of -width 8 or less caused a buffer overrun. +1. Fixed a regression (CVE-2021-29390) introduced by 2.1 beta1[6(b)] whereby +attempting to decompress certain progressive JPEG images with one or more +component planes of width 8 or less caused a buffer overrun. 2. Fixed a regression introduced by 2.1 beta1[6(b)] whereby attempting to decompress a specially-crafted malformed progressive JPEG image caused the From 47656a082091f9c9efda054674522513f4768c6c Mon Sep 17 00:00:00 2001 From: DRC Date: Mon, 2 Oct 2023 18:03:50 -0400 Subject: [PATCH 157/162] Test: Fix float test errors w/ Clang & fp-contract The MD5 sums associated with FLOATTEST8=fp-contract and FLOATTEST12=fp-contract are appropriate for GCC (tested v5 through v13) with -ffp-contract=fast, which is the default when compiling for an architecture that has fused multiply-add (FMA) instructions. However, different MD5 sums are needed for Clang (tested v5 through v14) with -ffp-contract=on, which is now the default in Clang 14 when compiling for an architecture that has FMA instructions. Refer to #705, #709, #710 --- CMakeLists.txt | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c3b040c9..deb948fac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -874,14 +874,14 @@ endif() # extensions # no-fp-contract = validate against the expected results from the C code when # floating point expression contraction is disabled (the -# default with Clang, with GCC when building for platforms -# that lack fused multiply-add [FMA] instructions, or when -# passing -ffp-contract=off to the compiler) +# default with Clang 13 and earlier, when building for +# platforms that lack fused multiply-add [FMA] instructions, +# or when passing -ffp-contract=off to GCC or Clang) # fp-contract = validate against the expected results from the C code when # floating point expression contraction is enabled (the default -# with GCC when building for platforms that have fused multiply- -# add [FMA] instructions or when passing -ffp-contract=fast to -# the compiler) +# with Clang 14 and later, with GCC when building for platforms +# that have fused multiply-add [FMA] instructions, or when +# passing -ffp-contract=fast to GCC or -ffp-contract=on to Clang) # 387 = validate against the expected results from the C code when the 387 FPU # is being used for floating point math (which is generally the default # with x86 compilers) @@ -1176,7 +1176,11 @@ foreach(libtype ${TEST_LIBTYPES}) set(MD5_PPM_3x2_FLOAT_NO_FP_CONTRACT ${MD5_PPM_3x2_FLOAT_SSE}) set(MD5_JPEG_3x2_FLOAT_PROG_FP_CONTRACT ${MD5_JPEG_3x2_FLOAT_PROG_NO_FP_CONTRACT}) - set(MD5_PPM_3x2_FLOAT_FP_CONTRACT ${MD5_PPM_3x2_FLOAT_SSE}) + if(CMAKE_C_COMPILER_ID STREQUAL "Clang") + set(MD5_PPM_3x2_FLOAT_FP_CONTRACT 2da9de6ae869e88b8372de815d366b03) + else() + set(MD5_PPM_3x2_FLOAT_FP_CONTRACT ${MD5_PPM_3x2_FLOAT_SSE}) + endif() set(MD5_JPEG_3x2_FLOAT_PROG_387 bc6dbbefac2872f6b9d6c4a0ae60c3c0) set(MD5_PPM_3x2_FLOAT_387 bcc5723c61560463ac60f772e742d092) set(MD5_JPEG_3x2_FLOAT_PROG_MSVC e27840755870fa849872e58aa0cd1400) @@ -1244,7 +1248,11 @@ foreach(libtype ${TEST_LIBTYPES}) set(MD5_PPM_3x2_FLOAT_NO_FP_CONTRACT f6bfab038438ed8f5522fbd33595dcdc) set(MD5_JPEG_3x2_FLOAT_PROG_FP_CONTRACT ${MD5_JPEG_3x2_FLOAT_PROG_NO_FP_CONTRACT}) - set(MD5_PPM_3x2_FLOAT_FP_CONTRACT 0e917a34193ef976b679a6b069b1be26) + if(CMAKE_C_COMPILER_ID STREQUAL "Clang") + set(MD5_PPM_3x2_FLOAT_FP_CONTRACT ${MD5_PPM_3x2_FLOAT_NO_FP_CONTRACT}) + else() + set(MD5_PPM_3x2_FLOAT_FP_CONTRACT 0e917a34193ef976b679a6b069b1be26) + endif() set(MD5_JPEG_3x2_FLOAT_PROG_387 1657664a410e0822c924b54f6f65e6e9) set(MD5_PPM_3x2_FLOAT_387 cb0a1f027f3d2917c902b5640214e025) set(MD5_JPEG_3x2_FLOAT_PROG_MSVC 7999ce9cd0ee9b6c7043b7351ab7639d) From 2c97a1ff07ccffe7c4b6bc96c7a94afbc32c3575 Mon Sep 17 00:00:00 2001 From: DRC Date: Tue, 3 Oct 2023 12:07:40 -0400 Subject: [PATCH 158/162] GitHub: Use Ubuntu 20.04 runner for x32 build/test The Ubuntu 22.04 kernel no longer supports the x32 ABI. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d08887060..5633792bc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -110,7 +110,7 @@ jobs: JSIMD_FORCENONE=1 make test popd linux-jpeg7-x32: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - name: Check out code uses: actions/checkout@v3 From 3d1d68cf967aec4aba34cd51e89a13fc2f0f3d9b Mon Sep 17 00:00:00 2001 From: DRC Date: Wed, 4 Oct 2023 13:20:38 -0400 Subject: [PATCH 159/162] README.md: Mention 4:4:0 math. incomp. vs. jpeg-6b libjpeg-turbo implements 4:4:0 "fancy" (smooth) upsampling, which is enabled by default when decompressing JPEG images that use 4:4:0 chrominance subsampling. libjpeg did not and does not implement fancy 4:4:0 upsampling. --- README.md | 53 +++++++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 5e5416aff..923e61d23 100644 --- a/README.md +++ b/README.md @@ -283,30 +283,35 @@ Mathematical Compatibility ========================== For the most part, libjpeg-turbo should produce identical output to libjpeg -v6b. The one exception to this is when using the floating point DCT/IDCT, in -which case the outputs of libjpeg v6b and libjpeg-turbo can differ for the -following reasons: - -- The SSE/SSE2 floating point DCT implementation in libjpeg-turbo is ever so - slightly more accurate than the implementation in libjpeg v6b, but not by - any amount perceptible to human vision (generally in the range of 0.01 to - 0.08 dB gain in PNSR.) - -- When not using the SIMD extensions, libjpeg-turbo uses the more accurate - (and slightly faster) floating point IDCT algorithm introduced in libjpeg - v8a as opposed to the algorithm used in libjpeg v6b. It should be noted, - however, that this algorithm basically brings the accuracy of the floating - point IDCT in line with the accuracy of the accurate integer IDCT. The - floating point DCT/IDCT algorithms are mainly a legacy feature, and they do - not produce significantly more accuracy than the accurate integer algorithms - (to put numbers on this, the typical difference in PNSR between the two - algorithms is less than 0.10 dB, whereas changing the quality level by 1 in - the upper range of the quality scale is typically more like a 1.0 dB - difference.) - -- If the floating point algorithms in libjpeg-turbo are not implemented using - SIMD instructions on a particular platform, then the accuracy of the - floating point DCT/IDCT can depend on the compiler settings. +v6b. There are two exceptions: + +1. When decompressing a JPEG image that uses 4:4:0 chrominance subsampling, the +outputs of libjpeg v6b and libjpeg-turbo can differ because libjpeg-turbo +implements a "fancy" (smooth) 4:4:0 upsampling algorithm and libjpeg did not. + +2. When using the floating point DCT/IDCT, the outputs of libjpeg v6b and +libjpeg-turbo can differ for the following reasons: + + - The SSE/SSE2 floating point DCT implementation in libjpeg-turbo is ever + so slightly more accurate than the implementation in libjpeg v6b, but not + by any amount perceptible to human vision (generally in the range of 0.01 + to 0.08 dB gain in PNSR.) + + - When not using the SIMD extensions, libjpeg-turbo uses the more accurate + (and slightly faster) floating point IDCT algorithm introduced in libjpeg + v8a as opposed to the algorithm used in libjpeg v6b. It should be noted, + however, that this algorithm basically brings the accuracy of the + floating point IDCT in line with the accuracy of the accurate integer + IDCT. The floating point DCT/IDCT algorithms are mainly a legacy + feature, and they do not produce significantly more accuracy than the + accurate integer algorithms. (To put numbers on this, the typical + difference in PNSR between the two algorithms is less than 0.10 dB, + whereas changing the quality level by 1 in the upper range of the quality + scale is typically more like a 1.0 dB difference.) + + - If the floating point algorithms in libjpeg-turbo are not implemented + using SIMD instructions on a particular platform, then the accuracy of + the floating point DCT/IDCT can depend on the compiler settings. While libjpeg-turbo does emulate the libjpeg v8 API/ABI, under the hood it is still using the same algorithms as libjpeg v6b, so there are several specific From da48edfc49508574d844fa14f91de89b79e5a5ce Mon Sep 17 00:00:00 2001 From: DRC Date: Mon, 9 Oct 2023 14:13:55 -0400 Subject: [PATCH 160/162] jchuff.c: Fix uninit read w/ AArch64, WITH_SIMD=0 Because of bf01ed2fbc02c15e86f414ff4946b66b4e5a00f1, the simd field in huff_entropy_encoder (and, by extension, the simd field in savable_state) is only initialized if WITH_SIMD is defined. Due to an oversight, the simd field in savable_state was queried in flush_bits() regardless of whether WITH_SIMD was defined. In most cases, both branches of the query have identical code, and the optimizer removes the branch. However, because the legacy Neon GAS Huffman encoder uses the older bit buffer logic from libjpeg-turbo 2.0.x and prior (refer to 087c29e07f7533ec82fd7eb1dafc84c29e7870ec), the branches do not have identical code when building for AArch64 with NEON_INTRINSICS undefined (which will be the case if WITH_SIMD is undefined.) Thus, if libjpeg-turbo was built for AArch64 with the SIMD extensions disabled at build time, it was possible for the Neon GAS branch in flush_bits() to be taken, which would have set put_bits to a value that is incorrect for the C Huffman encoder. Referring to #728, a user reported that this issue sometimes caused libjpeg-turbo to generate bogus JPEG images if it was built for AArch64 without SIMD extensions and subsequently used through the Qt framework. (It should be noted, however, that disabling the SIMD extensions in AArch64 builds of libjpeg-turbo is inadvisable for performance reasons.) I was unable to reproduce the issue on Linux/AArch64 using libjpeg-turbo alone, despite testing various versions of GCC and Clang and various optimization levels. However, the issue is reproducible using MSan with -O0, so this commit also modifies the GitHub Actions workflow so that compiler optimization is disabled in the linux-msan job. That should prevent the issue or similar issues from re-emerging. Fixes #728 --- .github/workflows/build.yml | 2 +- ChangeLog.md | 6 ++++++ jchuff.c | 20 ++++++++++++++++++-- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5633792bc..22b6607e7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -169,7 +169,7 @@ jobs: run: | mkdir build pushd build - cmake -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=clang -DCMAKE_C_FLAGS_RELWITHDEBINFO="-O3 -g -fsanitize=memory -fno-sanitize-recover=all -fPIE" -DWITH_SIMD=0 .. + cmake -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=clang -DCMAKE_C_FLAGS_RELWITHDEBINFO="-O0 -g -fsanitize=memory -fno-sanitize-recover=all -fPIE" -DWITH_SIMD=0 .. export NUMCPUS=`grep -c '^processor' /proc/cpuinfo` make -j$NUMCPUS --load-average=$NUMCPUS make test diff --git a/ChangeLog.md b/ChangeLog.md index ad0dc95b3..a46678513 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -12,6 +12,12 @@ mathematical (but not necessarily perceptible) edge block errors when decompressing progressive JPEG images exactly two MCU blocks in width or that use vertical chrominance subsampling. +3. Fixed a regression introduced by 3.0 beta2[6] that, in rare cases, caused +the C Huffman encoder (which is not used by default on x86 and Arm CPUs) to +generate incorrect results if the Neon SIMD extensions were explicitly disabled +at build time (by setting the `WITH_SIMD` CMake variable to `0`) in an AArch64 +build of libjpeg-turbo. + 3.0.0 ===== diff --git a/jchuff.c b/jchuff.c index b879506d5..3fede05f8 100644 --- a/jchuff.c +++ b/jchuff.c @@ -108,7 +108,9 @@ typedef bit_buf_type simd_bit_buf_type; typedef struct { union { bit_buf_type c; +#ifdef WITH_SIMD simd_bit_buf_type simd; +#endif } put_buffer; /* current bit accumulation buffer */ int free_bits; /* # of bits available in it */ /* (Neon GAS: # of bits now in it) */ @@ -133,7 +135,9 @@ typedef struct { long *ac_count_ptrs[NUM_HUFF_TBLS]; #endif +#ifdef WITH_SIMD int simd; +#endif } huff_entropy_encoder; typedef huff_entropy_encoder *huff_entropy_ptr; @@ -147,7 +151,9 @@ typedef struct { size_t free_in_buffer; /* # of byte spaces remaining in buffer */ savable_state cur; /* Current bit buffer & DC state */ j_compress_ptr cinfo; /* dump_buffer needs access to this */ +#ifdef WITH_SIMD int simd; +#endif } working_state; @@ -511,6 +517,7 @@ flush_bits(working_state *state) simd_bit_buf_type put_buffer; int put_bits; int localbuf = 0; +#ifdef WITH_SIMD if (state->simd) { #if defined(__aarch64__) && !defined(NEON_INTRINSICS) put_bits = state->cur.free_bits; @@ -518,7 +525,9 @@ flush_bits(working_state *state) put_bits = SIMD_BIT_BUF_SIZE - state->cur.free_bits; #endif put_buffer = state->cur.put_buffer.simd; - } else { + } else +#endif + { put_bits = BIT_BUF_SIZE - state->cur.free_bits; put_buffer = state->cur.put_buffer.c; } @@ -536,6 +545,7 @@ flush_bits(working_state *state) EMIT_BYTE(temp) } +#ifdef WITH_SIMD if (state->simd) { /* and reset bit buffer to empty */ state->cur.put_buffer.simd = 0; #if defined(__aarch64__) && !defined(NEON_INTRINSICS) @@ -543,7 +553,9 @@ flush_bits(working_state *state) #else state->cur.free_bits = SIMD_BIT_BUF_SIZE; #endif - } else { + } else +#endif + { state->cur.put_buffer.c = 0; state->cur.free_bits = BIT_BUF_SIZE; } @@ -719,7 +731,9 @@ encode_mcu_huff(j_compress_ptr cinfo, JBLOCKROW *MCU_data) state.free_in_buffer = cinfo->dest->free_in_buffer; state.cur = entropy->saved; state.cinfo = cinfo; +#ifdef WITH_SIMD state.simd = entropy->simd; +#endif /* Emit restart marker if needed */ if (cinfo->restart_interval) { @@ -792,7 +806,9 @@ finish_pass_huff(j_compress_ptr cinfo) state.free_in_buffer = cinfo->dest->free_in_buffer; state.cur = entropy->saved; state.cinfo = cinfo; +#ifdef WITH_SIMD state.simd = entropy->simd; +#endif /* Flush out the last data */ if (!flush_bits(&state)) From 5b2beb4bc4f41dd9dd2a905cb931b8d5054d909b Mon Sep 17 00:00:00 2001 From: DRC Date: Tue, 10 Oct 2023 16:44:59 -0400 Subject: [PATCH 161/162] Build: Set FLOATTEST* by default for AArch64, PPC Because of 47656a082091f9c9efda054674522513f4768c6c, we can now reliably determine the correct default values for FLOATTEST8 and FLOATTEST12 when using Clang or GCC to build for AArch64 or PowerPC platforms. (Testing confirms that this is the case with GCC 5-13 and Clang 5-14 on Ubuntu/AArch64, GCC 4 on CentOS 7/PPC, and GCC 8-10 and Clang 6-12 on Ubuntu/PPCLE.) Other CPU architectures and compilers can be added on a case-by-case basis as they are tested. --- CMakeLists.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index deb948fac..5ad071832 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -894,6 +894,17 @@ if(CPU_TYPE STREQUAL "x86_64" OR CPU_TYPE STREQUAL "i386") elseif(CPU_TYPE STREQUAL "x86_64") set(DEFAULT_FLOATTEST8 no-fp-contract) endif() +elseif(CPU_TYPE STREQUAL "powerpc" OR CPU_TYPE STREQUAL "arm64") + if(CMAKE_C_COMPILER_ID STREQUAL "Clang") + if(CMAKE_C_COMPILER_VERSION VERSION_EQUAL 14.0.0 OR + CMAKE_C_COMPILER_VERSION VERSION_GREATER 14.0.0) + set(DEFAULT_FLOATTEST8 fp-contract) + else() + set(DEFAULT_FLOATTEST8 no-fp-contract) + endif() + elseif(CMAKE_COMPILER_IS_GNUCC) + set(DEFAULT_FLOATTEST8 fp-contract) + endif() # else we can't really set an intelligent default for FLOATTEST8. The # appropriate value could be no-fp-contract, fp-contract, 387, or msvc, # depending on the compiler and compiler options. We leave it to the user to @@ -925,6 +936,17 @@ endif() if(CPU_TYPE STREQUAL "x86_64") set(DEFAULT_FLOATTEST12 no-fp-contract) +elseif(CPU_TYPE STREQUAL "powerpc" OR CPU_TYPE STREQUAL "arm64") + if(CMAKE_C_COMPILER_ID STREQUAL "Clang") + if(CMAKE_C_COMPILER_VERSION VERSION_EQUAL 14.0.0 OR + CMAKE_C_COMPILER_VERSION VERSION_GREATER 14.0.0) + set(DEFAULT_FLOATTEST12 fp-contract) + else() + set(DEFAULT_FLOATTEST12 no-fp-contract) + endif() + elseif(CMAKE_COMPILER_IS_GNUCC) + set(DEFAULT_FLOATTEST12 fp-contract) + endif() # else we can't really set an intelligent default for FLOATTEST12. The # appropriate value could be no-fp-contract, fp-contract, or something else, # depending on the compiler and compiler options. We leave it to the user to From ec32420f6b5dfa4e86883d42b209e8371e55aeb5 Mon Sep 17 00:00:00 2001 From: DRC Date: Wed, 11 Oct 2023 15:02:33 -0400 Subject: [PATCH 162/162] example.c: Fix 12-bit PPM write w/ big endian CPUs --- example.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/example.c b/example.c index 837254c21..4a826955b 100644 --- a/example.c +++ b/example.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software. * Copyright (C) 1992-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2017, 2019, 2022, D. R. Commander. + * Copyright (C) 2017, 2019, 2022-2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -398,6 +398,7 @@ do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, char *infilename, J12SAMPARRAY buffer12 = NULL; /* 12-bit output row buffer */ int col; int row_stride; /* physical row width in output buffer */ + int little_endian = 1; /* In this example we want to open the input and output files before doing * anything else, so that the setjmp() error recovery below can assume the @@ -494,10 +495,12 @@ do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, char *infilename, * more than one scanline at a time if that's more convenient. */ (void)jpeg12_read_scanlines(cinfo, buffer12, 1); - /* Swap MSB and LSB in each sample */ - for (col = 0; col < row_stride; col++) - buffer12[0][col] = ((buffer12[0][col] & 0xFF) << 8) | - ((buffer12[0][col] >> 8) & 0xFF); + if (*(char *)&little_endian == 1) { + /* Swap MSB and LSB in each sample */ + for (col = 0; col < row_stride; col++) + buffer12[0][col] = ((buffer12[0][col] & 0xFF) << 8) | + ((buffer12[0][col] >> 8) & 0xFF); + } fwrite(buffer12[0], 1, row_stride * sizeof(J12SAMPLE), outfile); } } else {