From fc01f4673b71c0b833c59c21e8c4478a9c4bcf21 Mon Sep 17 00:00:00 2001 From: DRC Date: Thu, 5 Jan 2023 06:36:46 -0600 Subject: [PATCH] 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 | 311 +- 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 | 4325 +++--- 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 | 49 +- fuzz/compress12.cc | 133 + fuzz/compress12_lossless.cc | 131 + fuzz/compress16_lossless.cc | 131 + fuzz/compress_lossless.cc | 45 +- fuzz/compress_yuv.cc | 62 +- fuzz/decompress.cc | 85 +- fuzz/decompress_yuv.cc | 44 +- fuzz/transform.cc | 43 +- java/TJBench.java | 663 +- java/TJExample.java | 40 +- java/TJUnitTest.java | 425 +- 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 | 569 +- java/doc/deprecated-list.html | 240 +- java/doc/{package-list => element-list} | 0 java/doc/help-doc.html | 154 +- java/doc/index-all.html | 995 +- java/doc/index.html | 80 +- 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 | 1996 ++- .../libjpegturbo/turbojpeg/TJCompressor.html | 1102 +- .../turbojpeg/TJCustomFilter.html | 169 +- .../turbojpeg/TJDecompressor.html | 1921 ++- .../libjpegturbo/turbojpeg/TJException.html | 214 +- .../turbojpeg/TJScalingFactor.html | 241 +- .../libjpegturbo/turbojpeg/TJTransform.html | 528 +- .../libjpegturbo/turbojpeg/TJTransformer.html | 389 +- .../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 | 748 +- java/doc/type-search-index.js | 1 + java/doc/type-search-index.zip | Bin 0 -> 311 bytes java/org/libjpegturbo/turbojpeg/TJ.java | 610 +- .../libjpegturbo/turbojpeg/TJCompressor.java | 527 +- .../turbojpeg/TJCustomFilter.java | 4 +- .../turbojpeg/TJDecompressor.java | 1172 +- .../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 | 766 +- tjbenchtest.in | 206 +- tjbenchtest.java.in | 241 - tjexample.c | 101 +- tjexampletest.in | 57 +- tjexampletest.java.in | 151 - tjunittest.c | 849 +- turbojpeg-jni.c | 842 +- turbojpeg-mapfile | 41 +- turbojpeg-mapfile.jni | 87 +- turbojpeg-mp.c | 493 + turbojpeg.c | 2119 +-- turbojpeg.h | 1698 ++- 132 files changed, 41404 insertions(+), 10163 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 5920cb52..9b7248ff 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) - 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) + foreach(sample_bits 8 12) - # 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) + if(sample_bits EQUAL 12) + set(tjbench tjbench12) + set(testout testout12) - 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) + 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() + + # 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) + + 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() + 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() - 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) - 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) - 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 fc3fc25e..4d249dee 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 00000000..1f28b8a6 --- /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 de0462ee..d7e29877 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 ac3c535b..78730e6b 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 68fcde3f..a3456d70 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 d918435d..409cbd59 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 4c39201f..1e96c474 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 @@ Data Structures + + + @@ -101,36 +104,9 @@ Macros - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + @@ -138,10 +114,10 @@ Macros - + - + @@ -150,20 +126,20 @@ Macros - + - + - - - + + + @@ -179,6 +155,12 @@ Typedefs

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...
 
+ + +TJSAMP_GRAY,
  TJSAMP_440, -TJSAMP_411 +TJSAMP_411, +TJSAMP_UNKNOWN
} @@ -223,6 +206,39 @@ Enumerations } + + + @@ -240,95 +256,125 @@ EnumerationsTJXOP_ROT270
} - +

Enumerations

enum  TJINIT { TJINIT_COMPRESS, +TJINIT_DECOMPRESS, +TJINIT_TRANSFORM + }
 Initialization options. More...
 
enum  TJSAMP {
  TJSAMP_444, TJSAMP_422, @@ -186,7 +168,8 @@ Enumerations
 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 - + - + - + - + - + + + + + + +

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.

@@ -394,6 +446,38 @@ YUV Image Format Notes

The number of error codes.

+ + + +

◆ TJ_NUMINIT

+ +
+
+ + + + +
#define TJ_NUMINIT
+
+ +

The number of initialization options.

+ +
+
+ +

◆ TJ_NUMPARAM

+ +
+
+ + + + +
#define TJ_NUMPARAM
+
+ +

The number of parameters.

+
@@ -442,205 +526,6 @@ YUV Image Format Notes

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

- -
-
- - - - -
#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 planar YUV images
  • -
  • Decompression scaling
  • -
  • Lossless transformations
  • -
- -
-
- -

◆ TJFLAG_NOREALLOC

- -
-
- - - - -
#define TJFLAG_NOREALLOC
-
- -

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.

- -
-
- -

◆ TJFLAG_PROGRESSIVE

- -
-
- - - - -
#define TJFLAG_PROGRESSIVE
-
- -

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.

- -
-
- -

◆ TJFLAG_STOPONWARNING

- -
-
- - - - -
#define TJFLAG_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.

- -
-
- -

◆ TJPAD

- -
-
- - - - - - - - -
#define TJPAD( width)
-
- -

Pad the given width to the nearest multiple of 4.

-
@@ -687,7 +572,7 @@ YUV Image Format Notes

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 @@ YUV Image Format Notes
-

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 @@ YUV Image Format Notes

This option will enable lossless cropping.

-

See tjTransform() for more information.

+

See tj3Transform() for more information.

@@ -752,9 +637,26 @@ YUV Image Format Notes
-

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 @@ YUV Image Format Notes
-

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 @@ YUV Image Format Notes

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 @@ YUV Image Format Notes
-

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 @@ YUV Image Format Notes

JPEG colorspaces.

Enumerator
TJCS_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 @@ YUV Image Format Notes

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 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 @@ YUV Image Format Notes
+ + + +

◆ 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 @@ YUV Image Format Notes

Pixel formats.

Enumerator
TJPF_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 @@ YUV Image Format Notes

The JPEG or YUV image will contain one chrominance component for every 4x1 block of pixels in the source image. JPEG images compressed with 4:1: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:1:1 is better able to reproduce sharp horizontal features.

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 @@ YUV Image Format Notes
-

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 @@ YUV Image Format Notes

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,18 +1168,1321 @@ YUV Image Format Notes
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
- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DLLEXPORT unsigned long tjBufSize 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.

+
Parameters
+ + + + + + + + + +
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
0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
+ +
+
+ +

◆ tj3Compress16()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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.

+
Parameters
+ + + + + + + + + +
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
0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
+ +
+
+ +

◆ tj3Compress8()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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.

+
Parameters
+ + + + + + + + + +
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
0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
+ +
+
+ +

◆ tj3CompressFromYUV8()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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.

+
Parameters
+ + + + + + + + +
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.
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
0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
+ +
+
+ +

◆ tj3CompressFromYUVPlanes8()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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.

+
Parameters
+ + + + + + + + +
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.
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
0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
+ +
+
+ +

◆ tj3DecodeYUV8()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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.

+

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
+ + + + + + + + + +
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.)
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. (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.)
+
+
+
Returns
0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
+ +
+
+ +

◆ tj3DecodeYUVPlanes8()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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.

+

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
+ + + + + + + + + +
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.
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. (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.)
+
+
+
Returns
0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
+ +
+
+ +

◆ tj3Decompress12()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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.

+

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)
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 tj3GetErrorStr() and tj3GetErrorCode().)
+ +
+
+ +

◆ tj3Decompress16()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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.

+

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)
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 tj3GetErrorStr() and tj3GetErrorCode().)
+ +
+
+ +

◆ tj3Decompress8()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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.

+

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)
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 tj3GetErrorStr() and tj3GetErrorCode().)
+ +
+
+ +

◆ tj3DecompressHeader()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
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.

+

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()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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.

+

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)
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 tj3GetErrorStr() and tj3GetErrorCode().)
+ +
+
+ +

◆ tj3DecompressToYUVPlanes8()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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.

+

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 instance.

+
Parameters
+ + +
handlehandle to a TurboJPEG instance. If the handle is NULL, then this function has no effect.
+
+
+ +
+
+ +

◆ tj3EncodeYUV8()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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.

+

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
+ + + + + + + + + +
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. (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 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.
+
+
+
Returns
0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
+ +
+
+ +

◆ tj3EncodeYUVPlanes8()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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.

+

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
+ + + + + + + + + +
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. (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 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.
+
+
+
Returns
0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
+ +
+
+ +

◆ tj3Free()

+ +
+
+ + + + + + + + +
DLLEXPORT void tj3Free (void * buffer)
+
+ +

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 tj3Alloc().

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

◆ tj3Get()

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

Get the value of a parameter.

+
Parameters
+ + + +
handlehandle to a TurboJPEG instance
paramone of the parameters
+
+
+
Returns
the value of the specified parameter, or -1 if the value is unknown.
+ +
+
+ +

◆ tj3GetErrorCode()

+ +
+
+ + + + + + + + +
DLLEXPORT int tj3GetErrorCode (tjhandle handle)
+
+ +

Returns a code indicating the severity of the last error.

+

See Error codes.

+
Parameters
+ + +
handlehandle to a TurboJPEG instance
+
+
+
Returns
a code indicating the severity of the last error. See Error codes.
+ +
+
+ +

◆ tj3GetErrorStr()

+ +
+
+ + + + + + + + +
DLLEXPORT char* tj3GetErrorStr (tjhandle handle)
+
+ +

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

+
Parameters
+ + +
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 descriptive error message explaining why the last command failed.
+ +
+
+ +

◆ tj3GetScalingFactors()

+ +
+
+ + + + + + + + +
DLLEXPORT tjscalingfactor* tj3GetScalingFactors (int * numScalingFactors)
+
+ +

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().)
+ +
+
+ +

◆ tj3Init()

+ +
+
+ + + + + + + + +
DLLEXPORT tjhandle tj3Init (int initType)
+
+ +

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().)
+ +
+
+ +

◆ tj3JPEGBufSize()

+ +
+
+ + + @@ -1099,22 +2513,657 @@ YUV Image Format Notes
DLLEXPORT size_t tj3JPEGBufSize ( int  width,
- +
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.)
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 -1 if the arguments are out of bounds.
+
Returns
the maximum size of the buffer (in bytes) required to hold the image, or 0 if the arguments are out of bounds.
- -

◆ tjBufSizeYUV2()

+ +

◆ tj3LoadImage12()

- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DLLEXPORT unsigned long tjBufSizeYUV2 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.

+
Parameters
+ + + + + + + +
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
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().
+ +
+
+ +

◆ tj3LoadImage16()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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.

+
Parameters
+ + + + + + + +
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
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().
+ +
+
+ +

◆ tj3LoadImage8()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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.

+
Parameters
+ + + + + + + +
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
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().
+ +
+
+ +

◆ tj3SaveImage12()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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.

+
Parameters
+ + + + + + + + +
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().)
+ +
+
+ +

◆ tj3SaveImage16()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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.

+
Parameters
+ + + + + + + + +
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().)
+ +
+
+ +

◆ tj3SaveImage8()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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.

+
Parameters
+ + + + + + + + +
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().)
+ +
+
+ +

◆ 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().)
+ +
+
+ +

◆ tj3Transform()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DLLEXPORT int tj3Transform (tjhandle handle,
const unsigned char * jpegBuf,
size_t jpegSize,
int n,
unsigned char ** dstBufs,
size_t * dstSizes,
const tjtransformtransforms 
)
+
+ +

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 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
+ + + + + + + + +
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 tj3Alloc() and let TurboJPEG grow the buffer as needed,
  2. +
  3. set dstBufs[i] 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() 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.
  6. +
+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.)
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.
+
+
+
Returns
0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
+ +
+
+ +

◆ tj3YUVBufSize()

+ +
+
+ + + @@ -1155,1303 +3204,18 @@ YUV Image Format Notes
DLLEXPORT size_t tj3YUVBufSize ( int  width,
-
Returns
the size of the buffer (in bytes) required to hold the image, or -1 if the arguments are out of bounds.
+
Returns
the size of the buffer (in bytes) required to hold the image, or 0 if the arguments are out of bounds.
- -

◆ tjCompress2()

+ +

◆ tj3YUVPlaneHeight()

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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.

-
Parameters
- - - - - - - - - - - - -
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
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].
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. 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 tjBufSize(). This should ensure that the buffer never has to be re-allocated. (Setting TJFLAG_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 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
-
-
-
Returns
0 if successful, or -1 if an error occurred (see tjGetErrorStr2() and tjGetErrorCode().)
- -
-
- -

◆ tjCompressFromYUV()

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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.

-
Parameters
- - - - - - - - - - - -
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.)
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. 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 tjBufSize(). This should ensure that the buffer never has to be re-allocated. (Setting TJFLAG_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 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
-
-
-
Returns
0 if successful, or -1 if an error occurred (see tjGetErrorStr2() and tjGetErrorCode().)
- -
-
- -

◆ tjCompressFromYUVPlanes()

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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.

-
Parameters
- - - - - - - - - - - -
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.
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. 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 tjBufSize(). This should ensure that the buffer never has to be re-allocated. (Setting TJFLAG_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 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
-
-
-
Returns
0 if successful, or -1 if an error occurred (see tjGetErrorStr2() and tjGetErrorCode().)
- -
-
- -

◆ tjDecodeYUV()

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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.

-

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 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 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 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
-
-
-
Returns
0 if successful, or -1 if an error occurred (see tjGetErrorStr2() and tjGetErrorCode().)
- -
-
- -

◆ tjDecodeYUVPlanes()

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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.

-

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
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 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.
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].
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().)
- -
-
- -

◆ tjDecompress2()

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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.

-
Parameters
- - - - - - - - - - -
handlea handle to a TurboJPEG decompressor or transformer instance
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.
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().)
- -
-
- -

◆ tjDecompressHeader4()

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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.

-
Parameters
- - - - - - - - - -
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.
-
-
-
Returns
0 if successful, or -1 if an error occurred (see tjGetErrorStr2() and tjGetErrorCode().)
- -
-
- -

◆ tjDecompressToYUV2()

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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.

-

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 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
-
-
-
Returns
0 if successful, or -1 if an error occurred (see tjGetErrorStr2() and tjGetErrorCode().)
- -
-
- -

◆ tjDecompressToYUVPlanes()

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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.

-

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 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
-
-
-
Returns
0 if successful, or -1 if an error occurred (see tjGetErrorStr2() and tjGetErrorCode().)
- -
-
- -

◆ tjDestroy()

- -
-
- - - - - - - - -
DLLEXPORT int tjDestroy (tjhandle handle)
-
- -

Destroy a TurboJPEG compressor, decompressor, or transformer instance.

-
Parameters
- - -
handlea handle to a TurboJPEG compressor, decompressor or transformer instance
-
-
-
Returns
0 if successful, or -1 if an error occurred (see tjGetErrorStr2().)
- -
-
- -

◆ tjEncodeYUV3()

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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.

-

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 a buffer containing a packed-pixel RGB or grayscale source image to be encoded
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].
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.)
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().)
- -
-
- -

◆ tjEncodeYUVPlanes()

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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.

-

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 a buffer containing a packed-pixel RGB or grayscale source image to be encoded
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].
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 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().)
- -
-
- -

◆ tjFree()

- -
-
- - - - - - - - -
DLLEXPORT void tjFree (unsigned char * buffer)
-
- -

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()
- -
-
- -

◆ tjGetErrorCode()

- -
-
- - - - - - - - -
DLLEXPORT int tjGetErrorCode (tjhandle handle)
-
- -

Returns a code indicating the severity of the last error.

-

See Error codes.

-
Parameters
- - -
handlea handle to a TurboJPEG compressor, decompressor or transformer instance
-
-
-
Returns
a code indicating the severity of the last error. See Error codes.
- -
-
- -

◆ tjGetErrorStr2()

- -
-
- - - - - - - - -
DLLEXPORT char* tjGetErrorStr2 (tjhandle handle)
-
- -

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

-
Parameters
- - -
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.)
-
-
-
Returns
a descriptive error message explaining why the last command failed.
- -
-
- -

◆ tjGetScalingFactors()

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

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 tjGetErrorStr2().)
- -
-
- -

◆ tjInitCompress()

- -
-
- - - - - - - - -
DLLEXPORT tjhandle tjInitCompress (void )
-
- -

Create a TurboJPEG compressor instance.

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

◆ tjInitDecompress()

- -
-
- - - - - - - - -
DLLEXPORT tjhandle tjInitDecompress (void )
-
- -

Create a TurboJPEG decompressor instance.

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

◆ tjInitTransform()

- -
-
- - - - - - - - -
DLLEXPORT tjhandle tjInitTransform (void )
-
- -

Create a new TurboJPEG transformer instance.

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

◆ tjLoadImage()

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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.

-
Parameters
- - - - - - - -
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 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().
- -
-
- -

◆ tjPlaneHeight()

- -
-
- - - + @@ -2486,18 +3250,18 @@ If you choose option 1, then *jpegSize should be set to the size of
DLLEXPORT int tjPlaneHeight DLLEXPORT int tj3YUVPlaneHeight ( int  componentID,
-
Returns
the plane height of a YUV image plane with the given parameters, or -1 if the arguments are out of bounds.
+
Returns
the plane height of a YUV image plane with the given parameters, or 0 if the arguments are out of bounds.
- -

◆ tjPlaneSizeYUV()

+ +

◆ tj3YUVPlaneSize()

- + @@ -2545,18 +3309,18 @@ If you choose option 1, then *jpegSize should be set to the size of
DLLEXPORT unsigned long tjPlaneSizeYUV DLLEXPORT size_t tj3YUVPlaneSize ( int  componentID,
-
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
the size of the buffer (in bytes) required to hold the YUV image plane, or 0 if the arguments are out of bounds.
- -

◆ tjPlaneWidth()

+ +

◆ tj3YUVPlaneWidth()

- + @@ -2591,166 +3355,7 @@ If you choose option 1, then *jpegSize should be set to the size of
DLLEXPORT int tjPlaneWidth DLLEXPORT int tj3YUVPlaneWidth ( int  componentID,
-
Returns
the plane width of a YUV image plane with the given parameters, or -1 if the arguments are out of bounds.
- -
-
- -

◆ tjSaveImage()

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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.

-
Parameters
- - - - - - - - -
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.
-
-
-
Returns
0 if successful, or -1 if an error occurred (see tjGetErrorStr2().)
- -
-
- -

◆ tjTransform()

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DLLEXPORT int tjTransform (tjhandle handle,
const unsigned char * jpegBuf,
unsigned long jpegSize,
int n,
unsigned char ** dstBufs,
unsigned long * dstSizes,
tjtransformtransforms,
int flags 
)
-
- -

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.

-
Parameters
- - - - - - - - - -
handlea handle to a TurboJPEG transformer instance
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. set dstBufs[i] 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 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.
  6. -
-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.)
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
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 @@ If you choose option 1, then dstSizes[i] should be set to the size
-

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 @@ If you choose option 1, then dstSizes[i] should be set to the size
-

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 @@ If you choose option 1, then dstSizes[i] should be set to the size
-

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 @@ If you choose option 1, then dstSizes[i] should be set to the size
-

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

+

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

@@ -2936,8 +3541,56 @@ If you choose option 1, then dstSizes[i] should be set to the size
-

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 8d06e0bc..7c9ccd28 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 f0d4eb56..b6c30d89 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 7e2ed520..55306648 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 2f57008a..1093ee35 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 98e47c02..fa87fc98 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 8c5b0555..192181cd 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 39971e8e..5460dc5d 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 6d5b80ad..df0c9f0a 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 ff1d8a7c..f5b344d1 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 e134e670..3a066fe6 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 dc5404ce..46ff898c 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 6b01cef3..927d7d32 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 eb7bc5af..b438812c 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 cd6ee7e3..e9a35d29 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 5be414e8..80319f85 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 586307e7..e04ac9a0 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 7023c9ca..7b86b6e4 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 ca1e0cd2..7abd5f5e 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 f7a582c4..35ad31f5 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 fe5bba70..ae8d2cde 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 5e101601..9baab871 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 1010f64a..d52ead7e 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 fc398900..e6785846 100644 --- a/doc/html/structtjregion.html +++ b/doc/html/structtjregion.html @@ -23,7 +23,7 @@
TurboJPEG -  2.2 +  3
@@ -157,7 +157,7 @@ Data Fields

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 6b8b1ffd..818dfbd0 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 7a243520..bcb6a0ac 100644 --- a/doc/html/structtjtransform.html +++ b/doc/html/structtjtransform.html @@ -23,7 +23,7 @@
TurboJPEG -  2.2 +  3
@@ -116,7 +116,7 @@ Data Fields 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 b051aa4c..1309a059 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 1c7d68bf..a08cb46a 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 8e88b4d0..d87cbdff 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 ca3ec7c1..00000000 --- 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 6f1d20c3..00000000 --- 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 539932f1..f59f66d1 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; + 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); - /* 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_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 00000000..ef081317 --- /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 00000000..76a0a0d9 --- /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 00000000..aa6037ec --- /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 4ba15669..7c2bb790 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; + tj3Set(handle, TJPARAM_BOTTOMUP, ti == 0); + tj3Set(handle, TJPARAM_NOREALLOC, ti != 2); + tj3Set(handle, TJPARAM_RESTARTROWS, ti == 0 || ti == 6 ? 1 : 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_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 ef41fc2d..0b12e0ce 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'; + 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); - /* 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_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 b6604224..2ef17ba0 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 4b2c89de..125b1632 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 4511102b..f4552125 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 f1f3854b..39260feb 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 @@ final class 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 @@ final class TJBench { 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 @@ final class TJBench { 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 @@ final class TJBench { } - 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 @@ final class TJBench { 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 @@ final class TJBench { 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 @@ final class TJBench { 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 @@ final class TJBench { 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"); + tempStr = new String(fileName + "_" + + (lossless ? "LOSSLS" : SUBNAME[subsamp]) + qualStr + + "_" + sizeStr + (bmp ? ".bmp" : ".ppm")); - 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); - } + 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 @@ final class TJBench { 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); + 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 = new TJCompressor(); + 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 @@ final class TJBench { 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 @@ final class TJBench { 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 @@ final class TJBench { (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 @@ final class TJBench { /* 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 @@ final class TJBench { 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 @@ final class TJBench { 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 ((jpegFlags & TJ.FLAG_LOSSLESS) != 0) - sf = new TJScalingFactor(1, 1); + if (cs == TJ.CS_YCCK || cs == TJ.CS_CMYK) { + pf = TJ.PF_CMYK; ps = TJ.getPixelSize(pf); + } + + 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 @@ final class TJBench { 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 @@ final class TJBench { 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 @@ final class TJBench { } 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 @@ final class TJBench { 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 @@ final class TJBench { 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 @@ final class TJBench { 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 @@ final class TJBench { } 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 @@ final class TJBench { 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 @@ final class TJBench { 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 @@ final class TJBench { 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 @@ final class TJBench { 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 @@ final class TJBench { 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 @@ final class TJBench { 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 @@ final class TJBench { 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 @@ final class TJBench { 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 @@ final class TJBench { 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 @@ final class TJBench { 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 @@ final class TJBench { 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 @@ final class TJBench { 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 @@ final class TJBench { xformOpt &= (~TJTransform.OPT_CROP); } - if (!decompOnly) { - int[] width = new int[1], height = new int[1]; + 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"); + } - srcBuf = loadImage(argv[0], width, height, pf); - w = width[0]; h = height[0]; + if (!decompOnly) { + 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); + + 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 @@ final class TJBench { 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 @@ final class TJBench { 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 @@ final class TJBench { } 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]); + 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_420, 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_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 64e7ad13..cffe9c84 100644 --- a/java/TJExample.java +++ b/java/TJExample.java @@ -140,8 +140,6 @@ class TJExample implements TJCustomFilter { 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 @@ class TJExample implements TJCustomFilter { 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 @@ class TJExample implements TJCustomFilter { 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 @@ class TJExample implements TJCustomFilter { 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 @@ class TJExample implements TJCustomFilter { /* 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 @@ class TJExample implements TJCustomFilter { " 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 c64c21f0..fff4a061 100644 --- a/java/TJUnitTest.java +++ b/java/TJUnitTest.java @@ -49,10 +49,13 @@ final class TJUnitTest { 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 @@ final class TJUnitTest { "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 @@ final class TJUnitTest { 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 @@ final class TJUnitTest { } } - 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 @@ final class TJUnitTest { 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 @@ final class TJUnitTest { 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 @@ final class TJUnitTest { } } - 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 @@ final class TJUnitTest { 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 @@ final class TJUnitTest { 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 @@ final class TJUnitTest { 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 @@ final class TJUnitTest { 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 @@ final class TJUnitTest { } 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 @@ final class TJUnitTest { 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 @@ final class TJUnitTest { } 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 @@ final class TJUnitTest { 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 @@ final class TJUnitTest { 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 @@ final class TJUnitTest { 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 @@ final class TJUnitTest { } 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 @@ final class TJUnitTest { 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 @@ final class TJUnitTest { 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 @@ final class TJUnitTest { } 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 @@ final class TJUnitTest { } 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 @@ final class TJUnitTest { 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 @@ final class TJUnitTest { 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 @@ final class TJUnitTest { 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 @@ final class TJUnitTest { } 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 @@ final class TJUnitTest { 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 @@ final class TJUnitTest { } 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 @@ final class TJUnitTest { (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 @@ final class TJUnitTest { 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 @@ final class TJUnitTest { 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 @@ final class TJUnitTest { } 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 { - if (lossless) { - flags |= TJ.FLAG_LOSSLESS; - quality = (((psv++ - 1) % 7) + 1) * 10; - numSamp = 1; - } - tjc = new TJCompressor(); + + if (lossless) { + tjc.set(TJ.PARAM_LOSSLESS, 1); + tjc.set(TJ.PARAM_LOSSLESSPSV, ((psv++ - 1) % 7) + 1); + numSamp = 1; + } else + tjc.set(TJ.PARAM_QUALITY, 100); + 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 @@ final class TJUnitTest { 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 @@ final class TJUnitTest { } 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 @@ final class TJUnitTest { 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 fecac06d..00000000 --- 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 00000000..f3179c5f --- /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 1f7fd3c6..2703a3c4 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 00000000..8ad354d7 --- /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 b4ddce95..5b1d2385 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 b8726bcb..3f4f34a0 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 6645d957..6d9362c9 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 @@
  • +

    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 b770db6a..22c93057 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. + +
-
CS_CMYK - Static variable in class org.libjpegturbo.turbojpeg.TJ
+
compress(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
+
+
Deprecated. + +
+
+
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 + +
+
decompress(int[], int, int, int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
+
+ +
+
decompress(int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
+
+
Deprecated. + +
+
+
decompress(int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
+
+
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(int, int, int, int, 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 return a buffer containing - the packed-pixel decompressed image.
+
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.
-
decompress(int[], int, int, int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
+
decompress16(short[], 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 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.
-
decompress(BufferedImage, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
+
decompress8(byte[], 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 - 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 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 packed-pixel source image associated with this compressor - instance into a planar YUV image and store it in the given - YUVImage instance.
+
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, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
+
encodeYUV(int[]) - 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 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
+
encodeYUV(int[], 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
+
encodeYUV(int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
+
+
Deprecated. + +
+
+
encodeYUV(YUVImage) - 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 planar YUV image and store it + in the given YUVImage instance.
+
+
encodeYUV(YUVImage, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
+
+
Deprecated. + +
+
+
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.
+
Deprecated. +
Use TJ.PARAM_FASTDCT instead.
+
-
FLAG_ARITHMETIC - Static variable in class org.libjpegturbo.turbojpeg.TJ
+
FLAG_BOTTOMUP - Static variable in class org.libjpegturbo.turbojpeg.TJ
-
Use arithmetic entropy coding in JPEG images generated by compression and - transform operations.
+
Deprecated. +
Use TJ.PARAM_BOTTOMUP instead.
+
-
FLAG_BOTTOMUP - Static variable in class org.libjpegturbo.turbojpeg.TJ
+
FLAG_FASTDCT - 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_FASTUPSAMPLE - Static variable in class org.libjpegturbo.turbojpeg.TJ
-
Use the fastest DCT/IDCT algorithm available.
+
Deprecated. + +
-
FLAG_FASTUPSAMPLE - Static variable in class org.libjpegturbo.turbojpeg.TJ
+
FLAG_LIMITSCANS - 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_SCANLIMIT instead.
+
-
FLAG_LIMITSCANS - Static variable in class org.libjpegturbo.turbojpeg.TJ
+
FLAG_PROGRESSIVE - 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_STOPONWARNING - 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 - transform operations.
-
-
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
-
For the given pixel format, returns the number of bytes that the alpha +
Get the value of a compression parameter.
+
+
get(int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
+
+
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 cause TJTransformer.transform() to throw an exception if the transform is not +
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 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
+
+
Unknown subsampling.
+
+
set(int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
+
+
Set the value of a compression parameter.
+
+
set(int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
+
+
Set the value of a decompression parameter.
+
+
setBuf(byte[][], int[], int, int[], int, int) - Method in class org.libjpegturbo.turbojpeg.YUVImage
Assign a set of image planes to this YUVImage instance.
-
setBuf(byte[], int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.YUVImage
+
setBuf(byte[], int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.YUVImage
Assign a unified buffer to this YUVImage instance.
-
setJPEGQuality(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
+
setCroppingRegion(Rectangle) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
-
Set the JPEG image quality level for subsequent compress operations.
+
Set the cropping region for partially decompressing a lossy JPEG image + into a packed-pixel image.
-
setSourceImage(byte[], int, int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
+
setJPEGQuality(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
-
Associate a packed-pixel RGB, grayscale, or CMYK source image with this - compressor instance.
+
Deprecated. +
Use + set(TJ.PARAM_QUALITY, ...) instead.
+
-
setSourceImage(BufferedImage, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
+
setScalingFactor(TJScalingFactor) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
-
Associate a packed-pixel RGB or grayscale source image with this - compressor instance.
+
Set the scaling factor for subsequent lossy decompression operations.
-
setSourceImage(YUVImage) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
-
-
Associate a planar YUV source image with this compressor instance.
-
-
setSourceImage(byte[], int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
+
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
-
Set the level of chrominance subsampling for subsequent compress/encode - operations.
+
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
+
+
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
+
+ +
- + + + +

U

+
+
UNCROPPED - Static variable in class org.libjpegturbo.turbojpeg.TJ
+
+
A java.awt.Rectangle instance that specifies no cropping
+
+
UNSCALED - Static variable in class org.libjpegturbo.turbojpeg.TJ
+
+
A TJScalingFactor instance that specifies a scaling factor of 1/1 + (no scaling)
+
+
+

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
+
YUVImage - Class in org.libjpegturbo.turbojpeg
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
-
-
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.
-
-
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 4e210754..cae2fdd8 100644 --- a/java/doc/index.html +++ b/java/doc/index.html @@ -1,71 +1,23 @@ - + + Generated Documentation (Untitled) - - - - - - +<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> +<script type="text/javascript">window.location.replace('org/libjpegturbo/turbojpeg/package-summary.html')</script> <noscript> -<div>JavaScript is disabled on your browser.</div> +<meta http-equiv="Refresh" content="0;org/libjpegturbo/turbojpeg/package-summary.html"> </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 00000000..facf852c --- /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 00000000..50937333 --- /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 00000000..93d8bc8e --- /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 00000000..775895ec --- /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 @@

  • -
    -
    public final class TJ
    +
    public final class TJ
     extends java.lang.Object
    TurboJPEG utility class (cannot be instantiated)
  • @@ -106,372 +140,601 @@ extends java.lang.Object
    • +
      +
      +
        -
      • +
      • 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 @@ extends java.lang.Object
      +
@@ -486,12 +750,13 @@ extends java.lang.Object
  • +
      -
    • +
    • Field Detail

      - + - +
        @@ -512,10 +780,13 @@ extends java.lang.Object
        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 @@ extends java.lang.Object
        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 @@ extends java.lang.Object
        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 @@ extends java.lang.Object

        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 @@ extends java.lang.Object
        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 @@ extends java.lang.Object 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 @@ extends java.lang.Object

        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 @@ extends java.lang.Object

        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 @@ extends java.lang.Object

        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 @@ extends java.lang.Object

        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 @@ extends java.lang.Object

        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 @@ extends java.lang.Object

        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 @@ extends java.lang.Object 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 @@ extends java.lang.Object
        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 @@ extends java.lang.Object 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 @@ extends java.lang.Object 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 @@ extends java.lang.Object
        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 @@ extends java.lang.Object 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
        -
      • -
      - - - -
        -
      • -

        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
        -
      • -
      - - - -
        -
      • -

        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
        -
      • -
      - - - -
        -
      • -

        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
        -
      • -
      - - - -
        -
      • -

        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
        -
      • -
      - - - -
        -
      • -

        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
        -
      • -
      - - - -
        -
      • -

        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
        -
      • -
      - - - -
        -
      • -

        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
        -
      • -
      - - - -
        -
      • -

        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_STOPONWARNING

        +
        public static final int PARAM_STOPONWARNING
        +
        Error handling behavior + +

        Value

          -
        • Colorspace conversion -
        • Chrominance subsampling +
        • 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
        +
        +
      • +
      + + + +
        +
      • +

        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
        +
        +
      • +
      + + + +
        +
      • +

        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
        +
        +
      • +
      + + + +
        +
      • +

        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
        +
        +
      • +
      + + + +
        +
      • +

        PARAM_JPEGWIDTH

        +
        public static final int PARAM_JPEGWIDTH
        +
        JPEG width (in pixels) [decompression only, read-only]
        +
        +
        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
        +
        +
      • +
      + + + +
        +
      • +

        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
        +
        +
      • +
      + + + +
        +
      • +

        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
        +
        +
      • +
      + + + +
        +
      • +

        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 +

          +
        • 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:
        Constant Field Values
        +
        +
        See Also:
        +
        PARAM_LOSSLESSPSV, +PARAM_LOSSLESSPT, +Constant Field Values
        +
      - + + + +
        +
      • +

        PARAM_LOSSLESSPSV

        +
        public static final int PARAM_LOSSLESSPSV
        +
        Lossless JPEG predictor selection value (PSV) + +

        Value +

          +
        • 1-7 [default for compression: + 1] +
        +
        +
        See Also:
        +
        PARAM_LOSSLESS, +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 69b7a3e5..a6e6dfcb 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 @@
  • 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 @@ implements java.io.Closeable
    • +
        -
      • +
      • Constructor Summary

        - +
        - + + - + - - - - + + + + +
        Constructors 
        Constructor and DescriptionConstructorDescription
        TJCompressor() +TJCompressor()
        Create a TurboJPEG compressor instance.
        TJCompressor(java.awt.image.BufferedImage 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.
        -
        TJCompressor(byte[] srcImage, +TJCompressor​(byte[] 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 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​(java.awt.image.BufferedImage srcImage, + int x, + int y, + int width, + 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.
        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.
        -
        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, - 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.
        -
        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.
        +
        compress​(byte[] dstBuf, + int flags) +
        Deprecated. +
        Use set() and compress(byte[]) instead.
        +
        protected voidfinalize() 
        byte[]compress​(int flags) +
        Deprecated. +
        Use set() and compress() instead.
        +
        +
        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[] 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) +
        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() 
        intgetCompressedSize() +get​(int param) +
        Get the value of a compression parameter.
        +
        intgetCompressedSize()
        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, - int x, - int y, - int width, - int height) -
        Associate a packed-pixel RGB or grayscale source image with this - compressor instance.
        +
        setJPEGQuality​(int quality) +
        Deprecated. +
        Use + set(TJ.PARAM_QUALITY, ...) instead.
        +
        voidsetSourceImage(byte[] srcImage, +setSourceImage​(byte[] 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 pixelFormat)
        +
        Associate an 8-bit-per-sample packed-pixel RGB, grayscale, or CMYK source + image with this compressor instance.
        voidsetSourceImage(YUVImage srcImage) -
        Associate a planar YUV source image with this compressor instance.
        +
        setSourceImage​(java.awt.image.BufferedImage srcImage, + int x, + int y, + int width, + int height) +
        Associate an 8-bit-per-pixel packed-pixel RGB or grayscale source image + with this compressor instance.
        voidsetSubsamp(int newSubsamp) -
        Set the level of chrominance subsampling for subsequent compress/encode - operations.
        +
        setSourceImage​(YUVImage srcImage) +
        Associate an 8-bit-per-sample planar YUV source image with this compressor + instance.
        +
        voidsetSourceImage12​(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 @@ implements java.io.Closeable
      +
  • @@ -285,380 +437,643 @@ implements java.io.Closeable
    • +
      +
      +
        -
      • +
      • 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).
          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/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).) 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)
          +
          Deprecated. +
          Use + set(TJ.PARAM_SUBSAMP, ...) instead.
          +
        - +
        • 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
          -
        • -
        - - - -
          -
        • -

          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 - 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 YUVImage instance containing the encoded image planes
          -
          Throws:
          -
          TJException
          +
          +
          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 + 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.
          +
          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 982079c4..536f4004 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 @@

    • -
      -
      public interface TJCustomFilter
      +
      public interface TJCustomFilter
      Custom filter callback interface
    @@ -97,25 +131,28 @@
    • +
        -
      • +
      • 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 @@
      +
    @@ -131,62 +169,78 @@
    • +
        -
      • +
      • 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 57b300d9..06de168b 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 @@
  • 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 @@ implements java.io.Closeable
    • - - +
        -
      • +
      • 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 @@ implements java.io.Closeable
        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 @@ implements java.io.Closeable
      +
    • - -
        -
      • - - -

        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
          -
        • -
        -
      • -
      +
        -
      • +
      • Constructor Detail

        - + - +
        • 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 @@ implements java.io.Closeable 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.
        +
      - + + + +
        +
      • +

        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)
        +
        +
      • +
      + + + +
        +
      • +

        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.
        +
        +
      • +
      + + + +
        +
      • +

        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

        -
        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.
        +
        @Deprecated
        +public int getSubsamp()
        +
        Deprecated. +
        Use get(TJ.PARAM_SUBSAMP) + instead.
        +
      - +
      • 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.
        +
        @Deprecated
        +public int getColorspace()
        +
        Deprecated. +
        Use get(TJ.PARAM_COLORSPACE) + instead.
        +
      - - - -
        -
      • -

        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.
        -
      • -
      - +
      • 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. + +
      - + + + +
        +
      • +

        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.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. 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
        +
        +
      • +
      +
      • 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 +
        @Deprecated
        +public void decompress​(byte[] dstBuf,
        +                       int x,
        +                       int y,
        +                       int desiredWidth,
        +                       int pitch,
        +                       int desiredHeight,
        +                       int pixelFormat,
        +                       int flags)
        +                throws TJException
        + +
        +
        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.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 - 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
        + 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
        +
      - +
      • -

        decompress

        -
        public byte[] decompress(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 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 - decompress(byte[], int, int, int, int, int, int, int) - for description
        desiredHeight - see - decompress(byte[], int, int, int, int, int, int, int) - 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 packed-pixel decompressed image.
        -
        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
        +
      - +
      • -

        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. +

        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 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 - 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
        + 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 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 - 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 - 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 - planes
        -
        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 +
        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.
        -
        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 + 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.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 the + scaled JPEG width and height (see setScalingFactor(), TJScalingFactor.getScaled(), getWidth(), and getHeight().)
        +
        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 + 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.
        +
        Returns:
        +
        a YUVImage instance containing the decompressed image + planes
        +
        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
        +
      - + + + + + + + +
        +
      • +

        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.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(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. -

        - 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
        +
        @Deprecated
        +public void decompress​(int[] dstBuf,
        +                       int x,
        +                       int y,
        +                       int desiredWidth,
        +                       int stride,
        +                       int desiredHeight,
        +                       int pixelFormat,
        +                       int flags)
        +                throws TJException
        + +
        +
        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. +

        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
        +
      - +
      • decompress

        -
        public java.awt.image.BufferedImage decompress(int desiredWidth,
        -                                      int desiredHeight,
        -                                      int bufferedImageType,
        -                                      int flags)
        -                                        throws TJException
        -
        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 - decompress(byte[], int, int, int, int, int, int, int) for - description
        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
        +
        @Deprecated
        +public void decompress​(java.awt.image.BufferedImage dstImage,
        +                       int flags)
        +                throws TJException
        +
        Deprecated. + +
        +
        +
        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)
        +
        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 66d73e74..5c2905d2 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 @@
  • 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 @@ extends java.io.IOException
    • +
        -
      • +
      • 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 @@ extends java.io.IOException
      +
    @@ -198,12 +247,13 @@ extends java.io.IOException
    • +
        -
      • +
      • Constructor Detail

        - +
          @@ -212,80 +262,91 @@ extends java.io.IOException
          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 4006baca..caa5c589 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 @@

    • -
      -
      public class TJScalingFactor
      +
      public class TJScalingFactor
       extends java.lang.Object
      Fractional scaling factor
    • @@ -106,72 +140,83 @@ extends java.lang.Object
      • +
          -
        • +
        • 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 @@ extends java.lang.Object
        +
    @@ -186,109 +232,139 @@ extends 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 40265efa..fbe0dd8b 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 @@
  • 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 @@ extends java.awt.Rectangle
    • +
        -
      • +
      • Nested Class Summary

          -
        • +
        • Nested classes/interfaces inherited from class java.awt.geom.Rectangle2D

          @@ -140,157 +172,188 @@ extends java.awt.Rectangle
      +
      +
        -
      • +
      • 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 -
        This option will enable progressive entropy coding in the JPEG image - generated by this particular transform.
        +
        OPT_PERFECT +
        This option will cause TJTransformer.transform() to throw an exception if the transform is not + perfect.
        static intOPT_TRIM +OPT_PROGRESSIVE +
        This option will enable progressive entropy coding in the JPEG image + generated by this particular transform.
        +
        static intOPT_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 @@ extends java.awt.Rectangle
      +
      +
      +
      +
        -
      • +
      • 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 @@ extends java.awt.Rectangle
      +
    @@ -386,12 +458,13 @@ extends java.awt.Rectangle
    • +
        -
      • +
      • Field Detail

        - + - +
          @@ -410,10 +486,13 @@ extends java.awt.Rectangle

          OP_NONE

          public static final int OP_NONE
          Do not transform the position of the image pixels.
          -
          See Also:
          Constant Field Values
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - + - + - + - + - + - + - + - +
        • 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 @@ extends java.awt.Rectangle
          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 @@ extends java.awt.Rectangle
          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 @@ extends java.awt.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 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 @@ extends java.awt.Rectangle 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 @@ extends java.awt.Rectangle

          options

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

        - +
          @@ -665,62 +808,86 @@ extends java.awt.Rectangle
          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 a95dcca7..83836eac 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 @@
  • java.lang.Object
    • -
    • org.libjpegturbo.turbojpeg.TJDecompressor
    • +
    • org.libjpegturbo.turbojpeg.TJDecompressor
      • org.libjpegturbo.turbojpeg.TJTransformer
      • @@ -101,12 +136,11 @@
      • All Implemented Interfaces:
        -
        java.io.Closeable, java.lang.AutoCloseable
        +
        java.io.Closeable, java.lang.AutoCloseable

        -
        -
        public class TJTransformer
        -extends TJDecompressor
        +
        public class TJTransformer
        +extends TJDecompressor
        TurboJPEG lossless transformer
      @@ -114,47 +148,37 @@ extends
      • - - +
          -
        • +
        • Constructor Summary

          - +
          - + + - + - + - +
          Constructors 
          Constructor and DescriptionConstructorDescription
          TJTransformer() +TJTransformer()
          Create a TurboJPEG lossless transformer instance.
          TJTransformer(byte[] jpegImage) +TJTransformer​(byte[] jpegImage)
          Create a TurboJPEG lossless transformer instance and associate the JPEG source image stored in jpegImage with the newly created instance.
          TJTransformer(byte[] jpegImage, - int imageSize) +TJTransformer​(byte[] jpegImage, + int imageSize)
          Create a TurboJPEG lossless transformer instance and associate the JPEG source image of length imageSize bytes stored in jpegImage with the newly created instance.
          @@ -163,54 +187,81 @@ extends + +
          @@ -225,78 +277,89 @@ extends
        • +
            -
          • +
          • Constructor Detail

            - + - +
            • TJTransformer

              -
              public TJTransformer(byte[] jpegImage)
              -              throws TJException
              +
              public TJTransformer​(byte[] jpegImage)
              +              throws TJException
              Create a TurboJPEG lossless transformer instance and associate the JPEG source image stored in jpegImage with the newly created instance.
              -
              Parameters:
              jpegImage - buffer containing the JPEG source image to transform. +
              +
              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
              +
              Throws:
              +
              TJException
              +
            - +
            • TJTransformer

              -
              public TJTransformer(byte[] jpegImage,
              -             int imageSize)
              -              throws TJException
              +
              public TJTransformer​(byte[] jpegImage,
              +                     int imageSize)
              +              throws TJException
              Create a TurboJPEG lossless transformer instance and associate the JPEG source image of length imageSize bytes stored in jpegImage with the newly created instance.
              -
              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
              +
              +
              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
              +
          +
          +
          +
        • + + diff --git a/java/doc/org/libjpegturbo/turbojpeg/YUVImage.html b/java/doc/org/libjpegturbo/turbojpeg/YUVImage.html index b08fcb3d..071b16ce 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 @@

          • -
            -
            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 @@ extends java.lang.Object
            • - - +
                -
              • +
              • Constructor Summary

                - +
                - + + - + - + - + - + @@ -238,100 +231,114 @@ extends java.lang.Object
                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 @@ extends 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 08a8bf83..00000000 --- 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 89dbe05b..0a027c3a 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 @@ - + @@ -85,7 +114,7 @@
            Interface Summary 
            Interface
            TJCustomFilterTJCustomFilter
            Custom filter callback interface
          • - +
            @@ -93,43 +122,43 @@ - + - + - + - + - + - + - +
            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 @@
          • - +
            @@ -147,7 +176,7 @@ - + @@ -155,14 +184,19 @@ + + diff --git a/java/doc/org/libjpegturbo/turbojpeg/package-tree.html b/java/doc/org/libjpegturbo/turbojpeg/package-tree.html index 5f0f8c3e..ca8d36a8 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 b6599954..d2d6d53e 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 00000000..7f0700b4 --- /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 b3463569..7dc93c48 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 00000000..8492271e --- /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 e123f318..33cde533 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 0aeaa97f..de945eda 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.subNavList li{ +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.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; + * 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 ul li table, .classUseContainer ul li table, .constantValuesContainer ul li table { - width:100%; +.overviewSummary, .memberSummary, .requiresSummary, .packagesSummary, .providesSummary, .usesSummary { + padding:0px; } -.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 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; + white-space:pre; } -caption a:link, caption a:hover, caption a:active, caption a:visited { +.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; +.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; +} +.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; +} +.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; } -ul.blockList ul.blockList li.blockList table { - margin:0 0 12px 0px; - width:100%; +.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; } -.tableSubHeadingColor { - background-color: #EEEEFF; +.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; } -.altColor { - background-color:#eeeeef; +.rowColor th, .altColor th { + font-weight:normal; } -.rowColor { - background-color:#ffffff; -} -.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.colLast, th.colOne, .constantValuesContainer th { +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.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; + font-size:13px; } -td.colLast, th.colLast { - border-right:1px solid #9eadc0; +td.colSecond, th.colSecond, td.colLast, th.colConstructorName, th.colDeprecatedItemName, th.colLast { + font-size:13px; } -td.colOne, th.colOne { - border-right:1px solid #9eadc0; - border-left:1px solid #9eadc0; +.constantsSummary th, .packagesSummary th { + font-size:13px; } -table.overviewSummary { - padding:0px; - margin-left:0px; +.providesSummary th.colFirst, .providesSummary th.colLast, .providesSummary td.colFirst, +.providesSummary td.colLast { + white-space:normal; + font-size:13px; } -table.overviewSummary td.colFirst, table.overviewSummary th.colFirst, -table.overviewSummary td.colOne, table.overviewSummary th.colOne { - width:25%; - vertical-align:middle; +.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; } -table.packageSummary td.colFirst, table.overviewSummary th.colFirst { - width:25%; - vertical-align:middle; +.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; +} +.tableSubHeadingColor { + background-color:#EEEEFF; +} +.altColor, .altColor th { + background-color:#FFFFFF; +} +.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; } -.strong { +.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; +} +.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 00000000..d4bc5641 --- /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 final class TJ { 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 final class TJ { /** - * 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 final class TJ { /** - * 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 final class TJ { /** - * 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 final class TJ { /** - * 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 final class TJ { /** - * 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 final class TJ { * 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 final class TJ { * 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 final class TJ { /** - * 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 = (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. - */ - public static final int FLAG_FASTUPSAMPLE = (1 << 8); - /** - * 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 = (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. - */ - public static final int FLAG_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. - *

            - * 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. - */ - 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 - * compression relative to baseline entropy coding (the default), but it will - * reduce compression and decompression performance considerably. Can be - * combined with {@link #FLAG_ARITHMETIC}. - */ - 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 - * 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. - */ - 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 - * 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 = (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: + * Error handling behavior + * + *

            Value *

              - *
            • Colorspace conversion - *
            • Chrominance subsampling + *
            • 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 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; + /** + * 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 PARAM_SUBSAMP = 4; + /** + * JPEG width (in pixels) [decompression only, read-only] + */ + public static final int PARAM_JPEGWIDTH = 5; + /** + * JPEG height (in pixels) [decompression only, read-only] + */ + public static final int PARAM_JPEGHEIGHT = 6; + /** + * 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 PARAM_OPTIMIZE = 11; + /** + * 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 PARAM_PROGRESSIVE = 12; + /** + * 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 PARAM_SCANLIMIT = 13; + /** + * 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 PARAM_ARITHMETIC = 14; + /** + * 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 + * {@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 FLAG_LOSSLESS = (1 << 17); + 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 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 final class TJ { * @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 final class TJ { /** * 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 final class TJ { /** * 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 final class TJ { */ 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 c6f9d149..fed47de6 100644 --- a/java/org/libjpegturbo/turbojpeg/TJCompressor.java +++ b/java/org/libjpegturbo/turbojpeg/TJCompressor.java @@ -39,9 +39,6 @@ import java.io.*; */ 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 class TJCompressor implements Closeable { } /** - * 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 class TJCompressor implements Closeable { } /** - * 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 class TJCompressor implements Closeable { } /** - * 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 class TJCompressor implements Closeable { * 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,7 @@ public class TJCompressor implements Closeable { 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); @@ -149,13 +144,127 @@ public class TJCompressor implements Closeable { srcPixelFormat = pixelFormat; srcX = x; srcY = y; + srcBuf12 = null; + srcBuf16 = null; srcBufInt = null; srcYUVImage = null; } /** - * Associate a packed-pixel RGB or grayscale source image with this - * compressor instance. + * 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); + else + srcPitch = pitch; + srcHeight = height; + srcPixelFormat = pixelFormat; + srcX = x; + srcY = y; + srcBuf8 = null; + srcBuf12 = null; + srcBufInt = null; + srcYUVImage = null; + } + + /** + * 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 class TJCompressor implements Closeable { 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 class TJCompressor implements Closeable { 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 class TJCompressor implements Closeable { 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. + * Set the value of a compression parameter. * - * @param newSubsamp the level of chrominance subsampling to use in - * subsequent compress/encode oeprations (one of - * {@link TJ#SAMP_444 TJ.SAMP_*}) + * @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 void setSubsamp(int newSubsamp) { - if (newSubsamp < 0 || newSubsamp >= TJ.NUMSAMP) + 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. + */ + @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 class TJCompressor implements Closeable { * 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 class TJCompressor implements Closeable { * 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 @@ public class TJCompressor implements Closeable { } }; + @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 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 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 compressFromYUV(byte[][] srcPlanes, int[] srcOffsets, - int width, int[] srcStrides, int height, int subsamp, byte[] jpegBuf, - int jpegQual, int flags) + 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 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, + private native int compressFromYUV8(byte[][] srcPlanes, int[] srcOffsets, + int width, int[] srcStrides, int height, byte[] jpegBuf) + throws TJException; + + @SuppressWarnings("checkstyle:HiddenField") + private native void encodeYUV8(byte[] srcBuf, int x, int y, int width, int pitch, int height, int pixelFormat, byte[][] dstPlanes, - int[] dstOffsets, int[] dstStrides, int subsamp, int flags) - throws TJException; + int[] dstOffsets, int[] dstStrides) throws TJException; @SuppressWarnings("checkstyle:HiddenField") - private native void encodeYUV(int[] srcBuf, int x, int y, int width, + 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 @@ public class TJCompressor implements Closeable { 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 3a66fd9e..78e6e4e1 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 976d86a6..d8c05459 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 class TJDecompressor implements Closeable { /** * 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 class TJDecompressor implements Closeable { * 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 class TJDecompressor implements Closeable { } /** - * 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 class TJDecompressor implements Closeable { * 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 class TJDecompressor implements Closeable { /** * 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 class TJDecompressor implements Closeable { 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 class TJDecompressor implements Closeable { 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 class TJDecompressor implements Closeable { 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. * - * @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_*} + * + * @param value value of the decompression parameter (refer to + * {@link TJ#PARAM_STOPONWARNING parameter documentation}) */ - public int getSubsamp() { - if (yuvImage != null) - return yuvImage.getSubsamp(); - if (jpegSubsamp < 0) - throw new IllegalStateException(NO_ASSOC_ERROR); - if (jpegSubsamp >= TJ.NUMSAMP) - throw new IllegalStateException("JPEG header information is invalid"); - return jpegSubsamp; + public native void set(int param, int value); + + /** + * Get the value of a decompression parameter. + * + * @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; } /** - * 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}. + * Set the cropping region for partially decompressing a lossy JPEG image + * into a packed-pixel image. * - * @return the colorspace used in the source image (JPEG or YUV) associated - * with this decompressor instance. + * @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() { + int subsamp = get(TJ.PARAM_SUBSAMP); + if (subsamp == TJ.SAMP_UNKNOWN) + throw new IllegalStateException(NO_ASSOC_ERROR); + if (subsamp >= TJ.NUMSAMP) + throw new IllegalStateException("JPEG header information is invalid"); + return subsamp; + } + + /** + * @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 class TJDecompressor implements Closeable { 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 class TJDecompressor implements Closeable { } /** - * 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 class TJDecompressor implements Closeable { * @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 decompress(byte[] dstBuf, int x, int y, int desiredWidth, - int pitch, int desiredHeight, int pixelFormat, - int flags) throws TJException { + 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 || - (yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) || - pixelFormat < 0 || pixelFormat >= TJ.NUMPF || 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 (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); } /** - * 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 + * @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 ((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); + decompress8(dstBuf, x, y, pitch, 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. * * @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[] 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) - throw new IllegalArgumentException("Invalid argument in decompress()"); + 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 = 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); + decompress8(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. + * @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 ((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 = scalingFactor.getScaled(getJPEGWidth()); + int scaledHeight = scalingFactor.getScaled(getJPEGHeight()); + if (pitch == 0) + pitch = scaledWidth * pixelSize; + short[] buf = new short[pitch * scaledHeight]; + decompress12(buf, 0, 0, pitch, pixelFormat); + return buf; + } + + /** + * 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 class TJDecompressor implements Closeable { * 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 class TJDecompressor implements Closeable { * @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. + * 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 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. - * - * @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) - 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); + 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); } /** - * 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 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) { + TJScalingFactor sf = getScalingFactor(desiredWidth, desiredHeight); + setScalingFactor(sf); + } + processFlags(flags); + decompress8(dstBuf, x, y, stride, 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 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 class TJDecompressor implements Closeable { 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 class TJDecompressor implements Closeable { 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 @@ public class TJDecompressor implements Closeable { } }; + @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 @@ public class TJDecompressor implements Closeable { 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 d02d258f..dae0c164 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 class TJTransform extends Rectangle { * 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 class TJTransform extends Rectangle { * @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 class TJTransform extends Rectangle { /** * 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 2cbf0bfb..4d46b5a1 100644 --- a/java/org/libjpegturbo/turbojpeg/TJTransformer.java +++ b/java/org/libjpegturbo/turbojpeg/TJTransformer.java @@ -87,23 +87,30 @@ public class TJTransformer extends TJDecompressor { * @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 class TJTransformer extends TJDecompressor { * 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 class TJTransformer extends TJDecompressor { 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 94830464..60ace66a 100644 --- a/java/org/libjpegturbo/turbojpeg/YUVImage.java +++ b/java/org/libjpegturbo/turbojpeg/YUVImage.java @@ -121,8 +121,8 @@ public class YUVImage { * @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 class YUVImage { * 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 class YUVImage { * @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 @@ public class YUVImage { * 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 @@ public class YUVImage { 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 e1a46cfd..5ebaf187 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 eeb2d6ad..24bc5215 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_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___3BIIIIII_3B + (JNIEnv *, jobject, jbyteArray, jint, jint, jint, jint, jint, jint, jbyteArray); /* * Class: org_libjpegturbo_turbojpeg_TJCompressor - * Method: compress - * Signature: ([IIIIIII[BIII)I + * Method: compress12 + * Signature: ([SIIIIII[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_compress12 + (JNIEnv *, jobject, jshortArray, jint, jint, jint, jint, jint, jint, jbyteArray); /* * Class: org_libjpegturbo_turbojpeg_TJCompressor - * Method: compressFromYUV - * Signature: ([[B[II[III[BII)I + * Method: compress16 + * Signature: ([SIIIIII[B)I */ -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 jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress16 + (JNIEnv *, jobject, jshortArray, jint, jint, jint, jint, jint, jint, jbyteArray); /* * Class: org_libjpegturbo_turbojpeg_TJCompressor - * Method: encodeYUV - * Signature: ([BIIIIII[[B[I[III)V + * Method: compress8 + * Signature: ([IIIIIII[B)I */ -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 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: encodeYUV - * Signature: ([IIIIIII[[B[I[III)V + * Method: compressFromYUV8 + * Signature: ([[B[II[II[B)I */ -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 jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFromYUV8 + (JNIEnv *, jobject, jobjectArray, jintArray, jint, jintArray, jint, jbyteArray); + +/* + * Class: org_libjpegturbo_turbojpeg_TJCompressor + * Method: encodeYUV8 + * Signature: ([BIIIIII[[B[I[I)V + */ +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: encodeYUV8 + * Signature: ([IIIIIII[[B[I[I)V + */ +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: loadImage + * Signature: (ILjava/lang/String;[II[I[I)Ljava/lang/Object; + */ +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 96af6a15..621ad5fc 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_decompress___3BI_3BIIIIIII - (JNIEnv *, jobject, jbyteArray, jint, jbyteArray, jint, jint, jint, jint, jint, jint, jint); +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_setCroppingRegion + (JNIEnv *, jobject); /* * Class: org_libjpegturbo_turbojpeg_TJDecompressor - * Method: decompress - * Signature: ([BI[IIIIIIII)V + * Method: decompress8 + * Signature: ([BI[BIIII)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_decompress8___3BI_3BIIII + (JNIEnv *, jobject, jbyteArray, jint, jbyteArray, jint, jint, jint, jint); /* * Class: org_libjpegturbo_turbojpeg_TJDecompressor - * Method: decompressToYUV - * Signature: ([BI[[B[II[III)V + * Method: decompress12 + * Signature: ([BI[SIIII)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_decompress12 + (JNIEnv *, jobject, jbyteArray, jint, jshortArray, jint, jint, jint, jint); /* * Class: org_libjpegturbo_turbojpeg_TJDecompressor - * Method: decodeYUV - * Signature: ([[B[I[II[BIIIIIII)V + * Method: decompress16 + * Signature: ([BI[SIIII)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_decompress16 + (JNIEnv *, jobject, jbyteArray, jint, jshortArray, jint, jint, jint, jint); /* * Class: org_libjpegturbo_turbojpeg_TJDecompressor - * Method: decodeYUV - * Signature: ([[B[I[II[IIIIIIII)V + * Method: decompress8 + * Signature: ([BI[IIIII)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_decompress8___3BI_3IIIII + (JNIEnv *, jobject, jbyteArray, jint, jintArray, jint, jint, jint, jint); + +/* + * Class: org_libjpegturbo_turbojpeg_TJDecompressor + * Method: decompressToYUV8 + * Signature: ([BI[[B[I[I)V + */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressToYUV8 + (JNIEnv *, jobject, jbyteArray, jint, jobjectArray, jintArray, jintArray); + +/* + * Class: org_libjpegturbo_turbojpeg_TJDecompressor + * Method: decodeYUV8 + * Signature: ([[B[I[I[BIIIIII)V + */ +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: decodeYUV8 + * Signature: ([[B[I[I[IIIIIII)V + */ +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: saveImage + * Signature: (ILjava/lang/String;Ljava/lang/Object;IIII)V + */ +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 a9dad4d6..5d860335 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 cbb3d13e..25ce9a11 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 e10d9812..cce263af 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 69fb5eaa..a5970b53 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 5aaee041..57058069 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); + SNPRINTF(tempStr, 1024, "%s_%s%s_%s.%s", fileName, + lossless ? "LOSSLS" : 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) + 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 9b847cd0..7862906c 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 f99c5ce7..00000000 --- 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 857a7f63..1c6c7a15 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 0d3047e2..c56fc429 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 d4b63bc5..00000000 --- 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 98f270d8..4e0a969a 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 @@ bailout: 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 @@ bailout: } -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 @@ bailout: } -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"; + 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 = (unsigned char *)malloc(w * h * tjPixelSize[pf])) == NULL) - THROW("Memory allocation failure"); - initBuf(srcBuf, w, h, pf, flags); + 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 @@ bailout: 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(tjDecompressHeader2(handle, jpegBuf, jpegSize, &_hdrw, &_hdrh, - &_hdrsubsamp)); + TRY_TJ(handle, tj3SetScalingFactor(handle, sf)); + + 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 @@ bailout: 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 @@ bailout: 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; + 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); - 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; + 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); + initBitmap(buf, width, pitch, height, pf, bottomUp); - 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)); + 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) ... ", + 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 samples) ... ", pixFormatStr[format], align); - if (doBmpTest("bmp", width, align, height, format, 0) == -1) + if (doBmpTest("ppm", width, align, height, format, 1) == -1) return -1; printf("OK.\n"); - printf("%s Top-Down PPM (row alignment = %d bytes) ... ", - pixFormatStr[format], align); - if (doBmpTest("ppm", width, align, height, format, - TJFLAG_BOTTOMUP) == -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 BMP (row alignment = %d bytes) ... ", + printf("%s Bottom-Up PPM (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) ... ", - 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 aa6611ef..d9531a25 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 @@ bailout: 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 @@ bailout: 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 @@ bailout: 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 @@ bailout: return; } -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) +/* TurboJPEG 3: TJCompressor::set() */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_set + (JNIEnv *env, jobject obj, jint param, jint value) { tjhandle handle = 0; - unsigned long jpegSize = 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 precision, + jint x, jint y, jint width, jint pitch, jint height, jint pf, + jbyteArray dst) +{ + tjhandle handle = 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 @@ bailout: 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 @@ bailout: 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 @@ bailout: 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 @@ bailout: 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(); } - 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); - bailout: SAFE_RELEASE(src, jpegBuf); } +/* 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: + 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 @@ bailout: 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, x, y, width, - pitch, height, pf, flags); + TJDecompressor_decompress(env, obj, src, jpegSize, dst, 1, 8, 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::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, 16, x, y, pitch, + pf); +} + +/* 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); + 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"); - nc = (jpegSubsamp == org_libjpegturbo_turbojpeg_TJ_SAMP_GRAY ? 1 : 3); + 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); - 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 (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 @@ bailout: 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 @@ bailout: 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 @@ bailout: /* 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 9d2b7daf..6aab8713 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 5e301138..31be7508 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 00000000..b4bf1eda --- /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 fa25a3ac..3c235861 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); + this->cinfo.in_color_space = pf2cs[pixelFormat]; + this->cinfo.input_components = tjPixelSize[pixelFormat]; + jpeg_set_defaults(&this->cinfo); - cinfo->in_color_space = pf2cs[pixelFormat]; - cinfo->input_components = tjPixelSize[pixelFormat]; - jpeg_set_defaults(cinfo); + 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; -#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 - - 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"); + 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; } - 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); + 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); + } + + 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); -#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; + if (this->progressive) jpeg_simple_progression(&this->cinfo); #endif + this->cinfo.arith_code = this->arithmetic; - 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.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 3+ */ +DLLEXPORT void tj3Destroy(tjhandle 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; + if (this->init & COMPRESS) jpeg_destroy_compress(cinfo); + if (this->init & DECOMPRESS) jpeg_destroy_decompress(dinfo); + free(this); +} /* TurboJPEG 1.0+ */ DLLEXPORT int tjDestroy(tjhandle handle) { - GET_INSTANCE(handle); + static const char FUNCTION_NAME[] = "tjDestroy"; + int retval = 0; - if (setjmp(this->jerr.setjmp_buffer)) return -1; - if (this->init & COMPRESS) jpeg_destroy_compress(cinfo); - if (this->init & DECOMPRESS) jpeg_destroy_decompress(dinfo); - free(this); - return 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 1.2+ */ -DLLEXPORT void tjFree(unsigned char *buf) +/* TurboJPEG 3+ */ +DLLEXPORT void tj3Free(void *buf) { free(buf); } +/* TurboJPEG 1.2+ */ +DLLEXPORT void tjFree(unsigned char *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,38 +1087,67 @@ 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, @@ -722,71 +1155,26 @@ DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf, 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"); + THROW("Invalid argument"); - if (pitch == 0) pitch = width * tjPixelSize[pixelFormat]; + this->quality = jpegQual; + this->subsamp = jpegSubsamp; + processFlags(handle, flags, COMPRESS); - if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * height)) == NULL) - THROW("tjCompress2(): 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; - -#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,7 +1354,72 @@ bailout: free(outbuf[i]); } if (this->jerr.warning) retval = -1; - this->jerr.stopOnWarning = FALSE; + return retval; +} + +/* 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) +{ + 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; + + GET_TJINSTANCE(handle, -1); + + if (width <= 0 || height <= 0 || dstBuf == NULL || align < 1 || + !IS_POW2(align)) + THROW("Invalid argument"); + + 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 (this->subsamp == TJSAMP_GRAY) { + strides[1] = strides[2] = 0; + dstPlanes[1] = dstPlanes[2] = NULL; + } else { + 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 tj3EncodeYUVPlanes8(handle, srcBuf, width, pitch, height, pixelFormat, + dstPlanes, strides); + +bailout: return retval; } @@ -981,35 +1429,19 @@ DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf, unsigned char *dstBuf, int align, int subsamp, int flags) { - unsigned char *dstPlanes[3]; - int pw0, ph0, strides[3], retval = -1; - tjinstance *this = (tjinstance *)handle; + static const char FUNCTION_NAME[] = "tjEncodeYUV3"; + int retval = 0; - 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"); + if (subsamp < 0 || subsamp >= TJ_NUMSAMP) + THROW("Invalid argument"); - pw0 = tjPlaneWidth(0, width, subsamp); - ph0 = tjPlaneHeight(0, height, subsamp); - dstPlanes[0] = dstBuf; - strides[0] = PAD(pw0, align); - if (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); + this->subsamp = subsamp; + processFlags(handle, flags, COMPRESS); - 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 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,7 +1599,81 @@ bailout: } free(_tmpbuf); if (this->jerr.warning) retval = -1; - this->jerr.stopOnWarning = FALSE; + return retval; +} + +/* 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) +{ + 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; + + GET_TJINSTANCE(handle, -1); + + if (srcBuf == NULL || width <= 0 || align < 1 || !IS_POW2(align) || + height <= 0) + THROW("Invalid argument"); + + 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 (this->subsamp == TJSAMP_GRAY) { + strides[1] = strides[2] = 0; + srcPlanes[1] = srcPlanes[2] = NULL; + } else { + 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 tj3CompressFromYUVPlanes8(handle, srcPlanes, width, strides, height, + jpegBuf, jpegSize); + +bailout: return retval; } @@ -1183,35 +1684,23 @@ DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf, unsigned long *jpegSize, int jpegQual, int flags) { - const unsigned char *srcPlanes[3]; - int pw0, ph0, strides[3], retval = -1; - tjinstance *this = (tjinstance *)handle; + static const char FUNCTION_NAME[] = "tjCompressFromYUV"; + int retval = -1; + size_t size; - 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"); + if (subsamp < 0 || subsamp >= TJ_NUMSAMP) + THROW("Invalid argument"); - pw0 = tjPlaneWidth(0, width, subsamp); - ph0 = tjPlaneHeight(0, height, subsamp); - srcPlanes[0] = srcBuf; - strides[0] = PAD(pw0, align); - if (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); + this->quality = jpegQual; + this->subsamp = subsamp; + processFlags(handle, flags, COMPRESS); - 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); + 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; - return tjDecompressHeader4(handle, jpegBuf, jpegSize, width, height, - jpegSubsamp, jpegColorspace, &flags); + GET_TJINSTANCE(handle, -1); + + 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"); + THROW("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 - - 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,7 +2185,73 @@ bailout: free(inbuf[i]); } if (this->jerr.warning) retval = -1; - this->jerr.stopOnWarning = FALSE; + return retval; +} + +/* 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) +{ + 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; + + GET_TJINSTANCE(handle, -1); + + if (srcBuf == NULL || align < 1 || !IS_POW2(align) || width <= 0 || + height <= 0) + THROW("Invalid argument"); + + 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 (this->subsamp == TJSAMP_GRAY) { + strides[1] = strides[2] = 0; + srcPlanes[1] = srcPlanes[2] = NULL; + } else { + 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 tj3DecodeYUVPlanes8(handle, srcPlanes, strides, dstBuf, width, pitch, + height, pixelFormat); + +bailout: return retval; } @@ -1678,49 +2261,34 @@ DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf, int width, int pitch, int height, int pixelFormat, int flags) { - const unsigned char *srcPlanes[3]; - int pw0, ph0, strides[3], retval = -1; - tjinstance *this = (tjinstance *)handle; + static const char FUNCTION_NAME[] = "tjDecodeYUV"; + int retval = -1; - 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 (subsamp < 0 || subsamp >= TJ_NUMSAMP) + THROW("Invalid argument"); - pw0 = tjPlaneWidth(0, width, subsamp); - ph0 = tjPlaneHeight(0, height, subsamp); - srcPlanes[0] = srcBuf; - strides[0] = PAD(pw0, align); - if (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); + this->subsamp = subsamp; + processFlags(handle, flags, DECOMPRESS); - 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 tj3DecodeYUV8(handle, srcBuf, align, 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) + +/* 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"); + if (jpegBuf == NULL || jpegSize <= 0 || !dstPlanes || !dstPlanes[0]) + THROW("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 (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 @@ bailout: } 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); - pw0 = tjPlaneWidth(0, width, jpegSubsamp); - ph0 = tjPlaneHeight(0, height, jpegSubsamp); + this->headerRead = TRUE; + if (tj3SetScalingFactor(handle, sf[i]) == -1) + return -1; + 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 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: - this->jerr.stopOnWarning = FALSE; + 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: + 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"); + dstSizes == NULL || t == NULL) + THROW("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 (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 @@ bailout: if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo); free(xinfo); if (this->jerr.warning) retval = -1; - this->jerr.stopOnWarning = FALSE; + return retval; +} + +/* 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) +{ + static const char FUNCTION_NAME[] = "tjTransform"; + int i, retval = 0; + size_t *sizes = NULL; + + GET_DINSTANCE(handle); + if ((this->init & DECOMPRESS) == 0) + THROW("Instance has not been initialized for decompression"); + + 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; + } + + 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]; + +bailout: + free(sizes); return retval; } /*************************** Packed-Pixel Image I/O **************************/ +/* tj3LoadImage*() is implemented in turbojpeg-mp.c */ + /* TurboJPEG 2.0+ */ DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width, - int align, int *height, int *pixelFormat, - int flags) + int align, int *height, + int *pixelFormat, 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 = tj3Init(TJINIT_COMPRESS)) == NULL) return NULL; - if ((handle = tjInitCompress()) == NULL) return NULL; - this = (tjinstance *)handle; - cinfo = &this->cinfo; + processFlags(handle, flags, COMPRESS); -#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"); + dstBuf = tj3LoadImage8(handle, filename, width, align, height, pixelFormat); - 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 (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"); - - 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); - - *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"); - - if (setjmp(this->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++) { - unsigned char *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]); - } - cinfo->next_scanline += nlines; - } - - (*src->finish_input) (cinfo, src); - -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; + int retval = -1; - if (!filename || !buffer || width < 1 || pitch < 0 || height < 1 || - pixelFormat < 0 || pixelFormat >= TJ_NUMPF) - THROWG("tjSaveImage(): Invalid argument"); + if ((handle = tj3Init(TJINIT_DECOMPRESS)) == NULL) return -1; - if ((handle = tjInitDecompress()) == NULL) - return -1; - this = (tjinstance *)handle; - dinfo = &this->dinfo; + processFlags(handle, flags, DECOMPRESS); -#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"); + retval = tj3SaveImage8(handle, filename, buffer, width, pitch, height, + pixelFormat); - 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]; - - while (dinfo->output_scanline < dinfo->output_height) { - unsigned char *rowptr; - - 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++; - } - - (*dst->finish_output) (dinfo, dst); - -bailout: - if (handle) tjDestroy(handle); - if (file) fclose(file); + tj3Destroy(handle); return retval; } diff --git a/turbojpeg.h b/turbojpeg.h index 5f1fdd82..475282a5 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. + * The number of parameters */ -#define TJFLAG_BOTTOMUP (1 << 1) +#define TJ_NUMPARAM + /** - * 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. + * Parameters */ -#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. - */ -#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) -/** - * 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 - */ -#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); /** - * Compress a packed-pixel RGB, grayscale, or CMYK image into a JPEG image. + * Set the value of a parameter. * - * @param handle a handle to a TurboJPEG compressor or transformer instance + * @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 an 8-bit-per-sample packed-pixel RGB, grayscale, or CMYK image into + * an 8-bit-per-sample JPEG image. + * + * @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.) - * - * @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 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 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. + * + * \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. + * + * \details \copydetails tj3Compress8() + */ +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 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 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" - * - * @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 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 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" - * - * @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().) + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) */ -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().) - */ -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 *tjGetScalingFactors(int *numScalingFactors); +DLLEXPORT tjscalingfactor *tj3GetScalingFactors(int *numScalingFactors); /** - * Decompress a JPEG image into a packed-pixel RGB, grayscale, or CMYK image. + * Set the scaling factor for subsequent lossy decompression operations. * - * @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 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 int tj3SetCroppingRegion(tjhandle handle, tjregion croppingRegion); + + +/** + * 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 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. + * 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 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 + * @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 #tjGetErrorStr2() - * and #tjGetErrorCode().) + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) */ -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 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. + * + * \details \copydetails tj3Decompress8() + */ +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().) + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) */ -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().) - */ -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. + * Destroy a TurboJPEG instance. * - * @param handle a handle to a TurboJPEG compressor, decompressor or - * transformer 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 #tjGetErrorStr2().) + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr().) */ -DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer, - int width, int pitch, int height, int pixelFormat, - int flags); +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. + * + * \details \copydetails tj3SaveImage8() + */ +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 + /** * @} */

          • Exception Summary 
            Exception
            TJExceptionTJException