From 4a0335d3c5292496cb1f03107695c2db09c3723a Mon Sep 17 00:00:00 2001 From: Jiyong Date: Fri, 8 Dec 2023 14:49:28 +0900 Subject: [PATCH] Imported Upstream version 3.0.1 Change-Id: Icfb189b1b0e53fb5a140cb8d0f64bd2a489850dd --- BUILDING.md | 27 +- CMakeLists.txt | 1573 +-- ChangeLog.md | 332 +- LICENSE.md | 2 +- README.ijg | 26 +- README.md | 84 +- cdjpeg.h | 28 +- cjpeg.1 | 56 +- cjpeg.c | 110 +- cmakescripts/BuildPackages.cmake | 2 +- cmakescripts/testclean.cmake | 8 + cmakescripts/tjbenchtest.cmake | 77 + cmyk.h | 32 +- djpeg.c | 190 +- doc/html/annotated.html | 2 +- doc/html/classes.html | 2 +- doc/html/functions.html | 4 +- doc/html/functions_vars.html | 4 +- doc/html/group___turbo_j_p_e_g.html | 2556 +++-- doc/html/index.html | 2 +- doc/html/modules.html | 2 +- doc/html/search/all_0.js | 2 +- doc/html/search/all_6.js | 238 +- 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 | 96 +- 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 | 22 +- doxygen.config | 2 +- example.txt => example.c | 338 +- fuzz/CMakeLists.txt | 8 + fuzz/build.sh | 4 + fuzz/compress.cc | 51 +- fuzz/compress12.cc | 134 + fuzz/compress12_lossless.cc | 131 + fuzz/compress16_lossless.cc | 131 + fuzz/compress_lossless.cc | 130 + fuzz/compress_yuv.cc | 68 +- fuzz/decompress.cc | 91 +- fuzz/decompress_yuv.cc | 51 +- fuzz/transform.cc | 138 +- java/TJBench.java | 741 +- java/TJExample.java | 51 +- java/TJUnitTest.java | 533 +- java/doc/allclasses-frame.html | 24 - java/doc/allclasses-index.html | 215 + .../{allclasses-noframe.html => allclasses.html} | 20 +- java/doc/allpackages-index.html | 162 + java/doc/constant-values.html | 501 +- java/doc/deprecated-list.html | 245 +- java/doc/{package-list => element-list} | 0 java/doc/help-doc.html | 154 +- java/doc/index-all.html | 1105 +- java/doc/index.html | 78 +- java/doc/jquery-ui.overrides.css | 35 + java/doc/jquery/external/jquery/jquery.js | 10872 ++++++++++++++++++ java/doc/jquery/jquery-3.6.0.min.js | 2 + java/doc/jquery/jquery-ui.min.css | 6 + java/doc/jquery/jquery-ui.min.js | 6 + java/doc/jquery/jszip-utils/dist/jszip-utils-ie.js | 56 + .../jquery/jszip-utils/dist/jszip-utils-ie.min.js | 10 + java/doc/jquery/jszip-utils/dist/jszip-utils.js | 118 + .../doc/jquery/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 -> 1933 bytes java/doc/org/libjpegturbo/turbojpeg/TJ.html | 1978 +++- .../org/libjpegturbo/turbojpeg/TJCompressor.html | 1191 +- .../org/libjpegturbo/turbojpeg/TJCustomFilter.html | 171 +- .../org/libjpegturbo/turbojpeg/TJDecompressor.html | 1930 ++-- .../org/libjpegturbo/turbojpeg/TJException.html | 214 +- .../libjpegturbo/turbojpeg/TJScalingFactor.html | 241 +- .../org/libjpegturbo/turbojpeg/TJTransform.html | 554 +- .../org/libjpegturbo/turbojpeg/TJTransformer.html | 438 +- java/doc/org/libjpegturbo/turbojpeg/YUVImage.html | 743 +- .../org/libjpegturbo/turbojpeg/package-frame.html | 31 - .../libjpegturbo/turbojpeg/package-summary.html | 113 +- .../org/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 | 114 +- java/doc/stylesheet.css | 752 +- java/doc/type-search-index.js | 1 + java/doc/type-search-index.zip | Bin 0 -> 311 bytes java/org/libjpegturbo/turbojpeg/TJ.java | 683 +- java/org/libjpegturbo/turbojpeg/TJCompressor.java | 665 +- .../org/libjpegturbo/turbojpeg/TJCustomFilter.java | 8 +- .../org/libjpegturbo/turbojpeg/TJDecompressor.java | 1254 +- java/org/libjpegturbo/turbojpeg/TJTransform.java | 94 +- java/org/libjpegturbo/turbojpeg/TJTransformer.java | 115 +- java/org/libjpegturbo/turbojpeg/YUVImage.java | 163 +- java/org_libjpegturbo_turbojpeg_TJ.h | 66 +- java/org_libjpegturbo_turbojpeg_TJCompressor.h | 88 +- java/org_libjpegturbo_turbojpeg_TJDecompressor.h | 88 +- java/org_libjpegturbo_turbojpeg_TJTransformer.h | 4 +- jcapimin.c | 27 +- jcapistd.c | 53 +- jccoefct.c | 47 +- jccolext.c | 64 +- jccolor.c | 240 +- jcdctmgr.c | 94 +- jcdiffct.c | 411 + jchuff.c | 168 +- jchuff.h | 16 +- jcinit.c | 111 +- jclhuff.c | 587 + jclossls.c | 319 + jcmainct.c | 45 +- jcmarker.c | 36 +- jcmaster.c | 194 +- jcmaster.h | 43 + jconfig.h.in | 47 +- jconfigint.h.in | 29 + jcparam.c | 52 +- jcphuff.c | 53 +- jcprepct.c | 88 +- jcsample.c | 103 +- jctrans.c | 16 +- jdapimin.c | 10 +- jdapistd.c | 149 +- jdatadst-tj.c | 10 +- jdatadst.c | 10 - jdatasrc-tj.c | 8 +- jdatasrc.c | 6 - jdcoefct.c | 75 +- jdcoefct.h | 5 + jdcol565.c | 62 +- jdcolext.c | 48 +- jdcolor.c | 193 +- jdct.h | 135 +- jddctmgr.c | 61 +- jddiffct.c | 403 + jdhuff.c | 20 +- jdhuff.h | 7 +- jdinput.c | 63 +- jdlhuff.c | 302 + jdlossls.c | 289 + jdmainct.c | 112 +- jdmainct.h | 15 +- jdmarker.c | 26 +- jdmaster.c | 635 +- jdmerge.c | 72 +- jdmerge.h | 9 +- jdmrg565.c | 43 +- jdmrgext.c | 40 +- jdphuff.c | 4 +- jdpostct.c | 109 +- jdsample.c | 128 +- jdsample.h | 9 +- jdtrans.c | 12 +- jerror.h | 13 +- jfdctfst.c | 2 +- jfdctint.c | 4 +- jidctflt.c | 14 +- jidctfst.c | 18 +- jidctint.c | 136 +- jidctred.c | 38 +- jinclude.h | 4 +- jlossls.h | 101 + jmemmgr.c | 181 +- jmorecfg.h | 39 +- jpegcomp.h => jpegapicomp.h | 2 +- jpegint.h | 226 +- jpeglib.h | 153 +- jquant1.c | 164 +- jquant2.c | 130 +- jsamplecomp.h | 336 + jsimd.h | 12 +- jsimd_none.c | 431 - jutils.c | 25 +- jversion.h.in | 5 +- libjpeg.txt | 355 +- rdbmp.c | 3 + rdcolmap.c | 34 +- rdgif.c | 48 +- rdppm.c | 299 +- rdtarga.c | 3 + release/ReadMe.txt | 2 +- release/deb-control.in | 3 +- release/installer.nsi.in | 4 +- release/rpm.spec.in | 10 +- sharedlib/CMakeLists.txt | 53 +- simd/arm/aarch32/jsimd.c | 12 +- simd/arm/aarch64/jsimd.c | 17 +- simd/arm/jcphuff-neon.c | 187 +- simd/arm/jdcolor-neon.c | 1 - simd/arm/jdmerge-neon.c | 1 - simd/arm/jidctint-neon.c | 1 - simd/i386/jsimd.c | 84 +- simd/jsimd.h | 12 +- simd/mips/jsimd.c | 12 +- simd/mips64/jsimd.c | 12 +- simd/nasm/jsimdext.inc | 5 +- simd/powerpc/jsimd.c | 12 +- simd/x86_64/jccolext-avx2.asm | 17 +- simd/x86_64/jccolext-sse2.asm | 17 +- simd/x86_64/jcgryext-avx2.asm | 17 +- simd/x86_64/jcgryext-sse2.asm | 17 +- simd/x86_64/jchuff-sse2.asm | 56 +- simd/x86_64/jcphuff-sse2.asm | 31 +- simd/x86_64/jcsample-avx2.asm | 2 - simd/x86_64/jcsample-sse2.asm | 2 - simd/x86_64/jdcolext-avx2.asm | 17 +- simd/x86_64/jdcolext-sse2.asm | 17 +- simd/x86_64/jdmrgext-avx2.asm | 18 +- simd/x86_64/jdmrgext-sse2.asm | 18 +- simd/x86_64/jdsample-avx2.asm | 22 +- simd/x86_64/jdsample-sse2.asm | 20 +- simd/x86_64/jfdctflt-sse.asm | 17 +- simd/x86_64/jfdctfst-sse2.asm | 17 +- simd/x86_64/jfdctint-avx2.asm | 1 - simd/x86_64/jfdctint-sse2.asm | 17 +- simd/x86_64/jidctflt-sse2.asm | 17 +- simd/x86_64/jidctfst-sse2.asm | 19 +- simd/x86_64/jidctint-avx2.asm | 1 - simd/x86_64/jidctint-sse2.asm | 19 +- simd/x86_64/jidctred-sse2.asm | 20 +- simd/x86_64/jquantf-sse2.asm | 2 - simd/x86_64/jquanti-avx2.asm | 2 - simd/x86_64/jquanti-sse2.asm | 2 - simd/x86_64/jsimd.c | 60 +- simd/x86_64/jsimdcpu.asm | 4 + strtest.c | 6 +- structure.txt | 163 +- 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 | 861 +- tjbenchtest.in | 475 +- tjbenchtest.java.in | 215 - tjexample.c | 115 +- tjexampletest.in | 57 +- tjexampletest.java.in | 151 - tjunittest.c | 889 +- transupp.c | 4 +- turbojpeg-jni.c | 994 +- turbojpeg-mapfile | 51 +- turbojpeg-mapfile.jni | 97 +- turbojpeg-mp.c | 530 + turbojpeg.c | 2126 ++-- turbojpeg.h | 2235 ++-- usage.txt | 38 +- win/jconfig.h.in | 25 - win/jpeg.rc.in | 2 +- win/jpeg62-memsrcdst.def | 108 - win/jpeg62.def | 35 +- win/jpeg7-memsrcdst.def | 110 - win/jpeg7.def | 35 +- win/jpeg8.def | 25 + win/turbojpeg.rc.in | 2 +- wizard.txt | 49 +- wrbmp.c | 3 + wrgif.c | 31 +- wrppm.c | 57 +- wrtarga.c | 3 + 283 files changed, 50545 insertions(+), 14164 deletions(-) create mode 100644 cmakescripts/tjbenchtest.cmake rename example.txt => example.c (59%) create mode 100644 fuzz/compress12.cc create mode 100644 fuzz/compress12_lossless.cc create mode 100644 fuzz/compress16_lossless.cc create mode 100644 fuzz/compress_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} (57%) 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 jcdiffct.c create mode 100644 jclhuff.c create mode 100644 jclossls.c create mode 100644 jcmaster.h create mode 100644 jddiffct.c create mode 100644 jdlhuff.c create mode 100644 jdlossls.c create mode 100644 jlossls.h rename jpegcomp.h => jpegapicomp.h (98%) create mode 100644 jsamplecomp.h delete mode 100644 jsimd_none.c 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 delete mode 100644 win/jconfig.h.in delete mode 100644 win/jpeg62-memsrcdst.def delete mode 100644 win/jpeg7-memsrcdst.def diff --git a/BUILDING.md b/BUILDING.md index 2ce65d6..6b484a1 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -25,9 +25,9 @@ Build Requirements variable or the `ASM_NASM` environment variable. On Windows, use forward slashes rather than backslashes in the path (for example, **c:/nasm/nasm.exe**). - * NASM and Yasm are located in the CRB (Code Ready Builder) repository on - Red Hat Enterprise Linux 8 and in the PowerTools repository on RHEL - derivatives, which is not enabled by default. + * NASM and Yasm are located in the CRB (Code Ready Builder) or PowerTools + repository on Red Hat Enterprise Linux 8+ and derivatives, which is not + enabled by default. ### Un*x Platforms (including Linux, Mac, FreeBSD, Solaris, and Cygwin) @@ -288,15 +288,6 @@ API/ABI-compatible with libjpeg v8. See [README.md](README.md) for more information about libjpeg v7 and v8 emulation. -### In-Memory Source/Destination Managers - -When using libjpeg v6b or v7 API/ABI emulation, add `-DWITH_MEM_SRCDST=0` to -the CMake command line to build a version of libjpeg-turbo that lacks the -`jpeg_mem_src()` and `jpeg_mem_dest()` functions. These functions were not -part of the original libjpeg v6b and v7 APIs, so removing them ensures strict -conformance with those APIs. See [README.md](README.md) for more information. - - ### Arithmetic Coding Support Since the patent on arithmetic coding has expired, this functionality has been @@ -372,9 +363,13 @@ located (usually **/usr/bin**.) Next, execute the following commands: cd {build_directory} cmake -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake \ + -DCMAKE_INSTALL_PREFIX={install_path} \ [additional CMake flags] {source_directory} make +*{install\_path}* is the path under which the libjpeg-turbo binaries should be +installed. + ### 64-bit MinGW Build on Un*x (including Mac and Cygwin) @@ -391,9 +386,13 @@ located (usually **/usr/bin**.) Next, execute the following commands: cd {build_directory} cmake -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake \ + -DCMAKE_INSTALL_PREFIX={install_path} \ [additional CMake flags] {source_directory} make +*{install\_path}* is the path under which the libjpeg-turbo binaries should be +installed. + Building libjpeg-turbo for iOS ------------------------------ @@ -429,6 +428,10 @@ iPhone 5S/iPad Mini 2/iPad Air and newer. [additional CMake flags] {source_directory} make +Replace `iPhoneOS` with `iPhoneSimulator` and `-miphoneos-version-min` with +`-miphonesimulator-version-min` to build libjpeg-turbo for the iOS simulator on +Macs with Apple silicon CPUs. + Building libjpeg-turbo for Android ---------------------------------- diff --git a/CMakeLists.txt b/CMakeLists.txt index cca2966..5ad0718 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,8 +10,8 @@ if(CMAKE_EXECUTABLE_SUFFIX) endif() project(libjpeg-turbo C) -set(VERSION 2.1.4) -set(COPYRIGHT_YEAR "1991-2022") +set(VERSION 3.0.1) +set(COPYRIGHT_YEAR "1991-2023") string(REPLACE "." ";" VERSION_TRIPLET ${VERSION}) list(GET VERSION_TRIPLET 0 VERSION_MAJOR) list(GET VERSION_TRIPLET 1 VERSION_MINOR) @@ -31,6 +31,34 @@ pad_number(VERSION_MINOR 3) pad_number(VERSION_REVISION 3) set(LIBJPEG_TURBO_VERSION_NUMBER ${VERSION_MAJOR}${VERSION_MINOR}${VERSION_REVISION}) +# The libjpeg-turbo build system has never supported and will never support +# being integrated into another build system using add_subdirectory(), because +# doing so would require that we (minimally): +# +# 1. avoid using certain CMake variables, such as CMAKE_SOURCE_DIR, +# CMAKE_BINARY_DIR, and CMAKE_PROJECT_NAME; +# 2. avoid using implicit include directories and relative paths; +# 3. optionally provide a way to skip the installation of libjpeg-turbo +# components when the 'install' target is built; +# 4. optionally provide a way to postfix target names, to avoid namespace +# conflicts; +# 5. restructure the top-level CMakeLists.txt so that it properly sets the +# PROJECT_VERSION variable; and +# 6. design automated regression tests to ensure that new commits don't break +# any of the above. +# +# Even if we did all of that, issues would still arise, because it is +# impossible for an upstream build system to anticipate the widely varying +# needs of every downstream build system. That's why the CMake +# ExternalProject_Add() function exists. Downstream projects that wish to +# integrate libjpeg-turbo as a subdirectory should either use +# ExternalProject_Add() or make downstream modifications to the libjpeg-turbo +# build system to suit their specific needs. Please do not file bug reports, +# feature requests, or pull requests regarding this. +if(NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + message(FATAL_ERROR "The libjpeg-turbo build system cannot be integrated into another build system using add_subdirectory(). Use ExternalProject_Add() instead.") +endif() + # CMake 3.14 and later sets CMAKE_MACOSX_BUNDLE to TRUE by default when # CMAKE_SYSTEM_NAME is iOS, tvOS, or watchOS, which breaks the libjpeg-turbo # build. (Specifically, when CMAKE_MACOSX_BUNDLE is TRUE, executables for @@ -40,6 +68,15 @@ set(LIBJPEG_TURBO_VERSION_NUMBER ${VERSION_MAJOR}${VERSION_MINOR}${VERSION_REVIS # application bundles would break our iOS packages.) set(CMAKE_MACOSX_BUNDLE FALSE) +get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY + GENERATOR_IS_MULTI_CONFIG) +# If the GENERATOR_IS_MULTI_CONFIG property doesn't exist (CMake < 3.9), then +# set the GENERATOR_IS_MULTI_CONFIG variable manually if the generator is +# Visual Studio or Xcode (the only multi-config generators in CMake < 3.9). +if(NOT GENERATOR_IS_MULTI_CONFIG AND (MSVC_IDE OR XCODE)) + set(GENERATOR_IS_MULTI_CONFIG TRUE) +endif() + string(TIMESTAMP DEFAULT_BUILD "%Y%m%d") set(BUILD ${DEFAULT_BUILD} CACHE STRING "Build string (default: ${DEFAULT_BUILD})") @@ -61,7 +98,7 @@ string(TOLOWER ${CMAKE_SYSTEM_PROCESSOR} CMAKE_SYSTEM_PROCESSOR_LC) set(COUNT 1) foreach(ARCH ${CMAKE_OSX_ARCHITECTURES}) if(COUNT GREATER 1) - message(FATAL_ERROR "The libjpeg-turbo build system does not support multiple values in CMAKE_OSX_ARCHITECTURES.") + message(FATAL_ERROR "libjpeg-turbo contains assembly code, so it cannot be built with multiple values in CMAKE_OSX_ARCHITECTURES.") endif() math(EXPR COUNT "${COUNT}+1") endforeach() @@ -187,8 +224,6 @@ option(ENABLE_STATIC "Build static libraries" TRUE) boolean_number(ENABLE_STATIC) option(REQUIRE_SIMD "Generate a fatal error if SIMD extensions are not available for this platform (default is to fall back to a non-SIMD build)" FALSE) boolean_number(REQUIRE_SIMD) -option(WITH_12BIT "Encode/decode JPEG images with 12-bit samples (implies WITH_ARITH_DEC=0 WITH_ARITH_ENC=0 WITH_JAVA=0 WITH_SIMD=0 WITH_TURBOJPEG=0 )" FALSE) -boolean_number(WITH_12BIT) option(WITH_ARITH_DEC "Include arithmetic decoding support when emulating the libjpeg v6b API/ABI" TRUE) boolean_number(WITH_ARITH_DEC) option(WITH_ARITH_ENC "Include arithmetic encoding support when emulating the libjpeg v6b API/ABI" TRUE) @@ -203,8 +238,6 @@ option(WITH_JPEG7 "Emulate libjpeg v7 API/ABI (this makes ${CMAKE_PROJECT_NAME} boolean_number(WITH_JPEG7) option(WITH_JPEG8 "Emulate libjpeg v8 API/ABI (this makes ${CMAKE_PROJECT_NAME} backward-incompatible with libjpeg v6b)" FALSE) boolean_number(WITH_JPEG8) -option(WITH_MEM_SRCDST "Include in-memory source/destination manager functions when emulating the libjpeg v6b or v7 API/ABI" TRUE) -boolean_number(WITH_MEM_SRCDST) option(WITH_SIMD "Include SIMD extensions, if available for this platform" TRUE) boolean_number(WITH_SIMD) option(WITH_TURBOJPEG "Include the TurboJPEG API library and associated test programs" TRUE) @@ -244,52 +277,39 @@ if(WITH_JPEG8 OR WITH_JPEG7) set(WITH_ARITH_ENC 1) set(WITH_ARITH_DEC 1) endif() -if(WITH_JPEG8) - set(WITH_MEM_SRCDST 0) -endif() - -if(WITH_12BIT) - set(WITH_ARITH_DEC 0) - set(WITH_ARITH_ENC 0) - set(WITH_JAVA 0) - set(WITH_SIMD 0) - set(WITH_TURBOJPEG 0) - set(BITS_IN_JSAMPLE 12) -else() - set(BITS_IN_JSAMPLE 8) -endif() -report_option(WITH_12BIT "12-bit JPEG support") if(WITH_ARITH_DEC) set(D_ARITH_CODING_SUPPORTED 1) endif() -if(NOT WITH_12BIT) - report_option(WITH_ARITH_DEC "Arithmetic decoding support") -endif() +report_option(WITH_ARITH_DEC "Arithmetic decoding support") if(WITH_ARITH_ENC) set(C_ARITH_CODING_SUPPORTED 1) endif() -if(NOT WITH_12BIT) - report_option(WITH_ARITH_ENC "Arithmetic encoding support") -endif() +report_option(WITH_ARITH_ENC "Arithmetic encoding support") -if(NOT WITH_12BIT) - report_option(WITH_TURBOJPEG "TurboJPEG API library") - report_option(WITH_JAVA "TurboJPEG Java wrapper") -endif() +report_option(WITH_TURBOJPEG "TurboJPEG API library") +report_option(WITH_JAVA "TurboJPEG Java wrapper") -if(WITH_MEM_SRCDST) - set(MEM_SRCDST_SUPPORTED 1) - set(MEM_SRCDST_FUNCTIONS "global: jpeg_mem_dest; jpeg_mem_src;") -endif() if(NOT WITH_JPEG8) - report_option(WITH_MEM_SRCDST "In-memory source/destination managers") + set(MEM_SRCDST_FUNCTIONS "global: jpeg_mem_dest; jpeg_mem_src;") endif() -set(SO_AGE 2) -if(WITH_MEM_SRCDST) - set(SO_AGE 3) +# 0: Original libjpeg v6b/v7/v8 API/ABI +# +# libjpeg v6b/v7 API/ABI emulation: +# 1: + In-memory source/destination managers (libjpeg-turbo 1.3.x) +# 2: + Partial image decompression functions (libjpeg-turbo 1.5.x) +# 3: + ICC functions (libjpeg-turbo 2.0.x) +# 4: + 12-bit-per-component and lossless functions (libjpeg-turbo 2.2.x) +# +# libjpeg v8 API/ABI emulation: +# 1: + Partial image decompression functions (libjpeg-turbo 1.5.x) +# 2: + ICC functions (libjpeg-turbo 2.0.x) +# 3: + 12-bit-per-component and lossless functions (libjpeg-turbo 2.2.x) +set(SO_AGE 3) +if(NOT WITH_JPEG8) + set(SO_AGE 4) endif() if(WITH_JPEG8) @@ -337,9 +357,21 @@ message(STATUS "libjpeg API shared library version = ${SO_MAJOR_VERSION}.${SO_AG # names of functions whenever they are modified in a backward-incompatible # manner, it is always backward-ABI-compatible with itself, so the major and # minor SO versions don't change. However, we increase the middle number (the -# SO "age") whenever functions are added to the API. +# SO "age") whenever functions are added to the API, because adding functions +# affects forward API/ABI compatibility. set(TURBOJPEG_SO_MAJOR_VERSION 0) -set(TURBOJPEG_SO_AGE 2) +# 0: TurboJPEG 1.3.x API +# 1: TurboJPEG 1.4.x API +# The TurboJPEG 1.5.x API modified some of the function prototypes, adding +# the const keyword in front of pointers to unmodified buffers, but that did +# not affect forward API/ABI compatibility. +# 2: TurboJPEG 2.0.x API +# The TurboJPEG 2.1.x API modified the behavior of the tjDecompressHeader3() +# 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. +# 3: TurboJPEG 3 API +set(TURBOJPEG_SO_AGE 3) set(TURBOJPEG_SO_VERSION 0.${TURBOJPEG_SO_AGE}.0) @@ -464,19 +496,17 @@ if(NOT INLINE_WORKS) endif() message(STATUS "INLINE = ${INLINE} (FORCE_INLINE = ${FORCE_INLINE})") -if(WITH_TURBOJPEG) - if(MSVC) - set(THREAD_LOCAL "__declspec(thread)") - else() - set(THREAD_LOCAL "__thread") - endif() - check_c_source_compiles("${THREAD_LOCAL} int i; int main(void) { i = 0; return i; }" HAVE_THREAD_LOCAL) - if(HAVE_THREAD_LOCAL) - message(STATUS "THREAD_LOCAL = ${THREAD_LOCAL}") - else() - message(WARNING "Thread-local storage is not available. The TurboJPEG API library's global error handler will not be thread-safe.") - unset(THREAD_LOCAL) - endif() +if(MSVC) + set(THREAD_LOCAL "__declspec(thread)") +else() + set(THREAD_LOCAL "__thread") +endif() +check_c_source_compiles("${THREAD_LOCAL} int i; int main(void) { i = 0; return i; }" HAVE_THREAD_LOCAL) +if(HAVE_THREAD_LOCAL) + message(STATUS "THREAD_LOCAL = ${THREAD_LOCAL}") +else() + message(WARNING "Thread-local storage is not available. The TurboJPEG API library's global error handler will not be thread-safe.") + unset(THREAD_LOCAL) endif() if(UNIX AND NOT APPLE) @@ -514,12 +544,6 @@ if(UNIX AND NOT APPLE) endif() # Generate files -if(WIN32) - configure_file(win/jconfig.h.in jconfig.h) -else() - configure_file(jconfig.h.in jconfig.h) -endif() -configure_file(jconfigint.h.in jconfigint.h) configure_file(jversion.h.in jversion.h) if(UNIX) configure_file(libjpeg.map.in libjpeg.map) @@ -538,13 +562,17 @@ if(CMAKE_EXECUTABLE_SUFFIX_TMP) endif() message(STATUS "CMAKE_EXECUTABLE_SUFFIX = ${CMAKE_EXECUTABLE_SUFFIX}") -set(JPEG_SOURCES jcapimin.c jcapistd.c jccoefct.c jccolor.c jcdctmgr.c jchuff.c - jcicc.c jcinit.c jcmainct.c jcmarker.c jcmaster.c jcomapi.c jcparam.c - jcphuff.c jcprepct.c jcsample.c jctrans.c jdapimin.c jdapistd.c jdatadst.c - jdatasrc.c jdcoefct.c jdcolor.c jddctmgr.c jdhuff.c jdicc.c jdinput.c - jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdphuff.c jdpostct.c jdsample.c - jdtrans.c jerror.c jfdctflt.c jfdctfst.c jfdctint.c jidctflt.c jidctfst.c - jidctint.c jidctred.c jquant1.c jquant2.c jutils.c jmemmgr.c jmemnobs.c) +set(JPEG16_SOURCES jcapistd.c jccolor.c jcdiffct.c jclossls.c jcmainct.c + jcprepct.c jcsample.c jdapistd.c jdcolor.c jddiffct.c jdlossls.c jdmainct.c + jdpostct.c jdsample.c jutils.c) +set(JPEG12_SOURCES ${JPEG16_SOURCES} jccoefct.c jcdctmgr.c jdcoefct.c + jddctmgr.c jdmerge.c jfdctfst.c jfdctint.c jidctflt.c jidctfst.c jidctint.c + jidctred.c jquant1.c jquant2.c) +set(JPEG_SOURCES ${JPEG12_SOURCES} jcapimin.c jchuff.c jcicc.c jcinit.c + jclhuff.c jcmarker.c jcmaster.c jcomapi.c jcparam.c jcphuff.c jctrans.c + jdapimin.c jdatadst.c jdatasrc.c jdhuff.c jdicc.c jdinput.c jdlhuff.c + jdmarker.c jdmaster.c jdphuff.c jdtrans.c jerror.c jfdctflt.c jmemmgr.c + jmemnobs.c) if(WITH_ARITH_ENC OR WITH_ARITH_DEC) set(JPEG_SOURCES ${JPEG_SOURCES} jaricom.c) @@ -563,19 +591,21 @@ if(WITH_SIMD) if(NEON_INTRINSICS) add_definitions(-DNEON_INTRINSICS) endif() -elseif(NOT WITH_12BIT) +else() message(STATUS "SIMD extensions: None (WITH_SIMD = ${WITH_SIMD})") endif() + +# We have to generate these here, because if the build system tries and fails +# to enable the SIMD extensions, the value of WITH_SIMD will have changed. +configure_file(jconfig.h.in jconfig.h) +configure_file(jconfigint.h.in jconfigint.h) + if(WITH_SIMD) message(STATUS "SIMD extensions: ${CPU_TYPE} (WITH_SIMD = ${WITH_SIMD})") if(MSVC_IDE OR XCODE) set_source_files_properties(${SIMD_OBJS} PROPERTIES GENERATED 1) endif() -else() - add_library(simd OBJECT jsimd_none.c) - if(NOT WIN32 AND (CMAKE_POSITION_INDEPENDENT_CODE OR ENABLE_SHARED)) - set_target_properties(simd PROPERTIES POSITION_INDEPENDENT_CODE 1) - endif() + set(SIMD_TARGET_OBJECTS $) endif() if(WITH_JAVA) @@ -583,12 +613,29 @@ if(WITH_JAVA) endif() if(ENABLE_SHARED) + # Compile a separate version of these source files with 12-bit and 16-bit + # data precision. + add_library(jpeg12 OBJECT ${JPEG12_SOURCES}) + set_property(TARGET jpeg12 PROPERTY COMPILE_FLAGS "-DBITS_IN_JSAMPLE=12") + set_target_properties(jpeg12 PROPERTIES POSITION_INDEPENDENT_CODE 1) + add_library(jpeg16 OBJECT ${JPEG16_SOURCES}) + set_property(TARGET jpeg16 PROPERTY COMPILE_FLAGS "-DBITS_IN_JSAMPLE=16") + set_target_properties(jpeg16 PROPERTIES POSITION_INDEPENDENT_CODE 1) add_subdirectory(sharedlib) endif() if(ENABLE_STATIC) - add_library(jpeg-static STATIC ${JPEG_SOURCES} $ - ${SIMD_OBJS}) + # Compile a separate version of these source files with 12-bit and 16-bit + # data precision. + add_library(jpeg12-static OBJECT ${JPEG12_SOURCES}) + set_property(TARGET jpeg12-static PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=12") + add_library(jpeg16-static OBJECT ${JPEG16_SOURCES}) + set_property(TARGET jpeg16-static PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=16") + add_library(jpeg-static STATIC ${JPEG_SOURCES} ${SIMD_TARGET_OBJECTS} + ${SIMD_OBJS} $ + $) if(NOT MSVC) set_target_properties(jpeg-static PROPERTIES OUTPUT_NAME jpeg) endif() @@ -596,9 +643,9 @@ endif() if(WITH_TURBOJPEG) if(ENABLE_SHARED) - set(TURBOJPEG_SOURCES ${JPEG_SOURCES} $ ${SIMD_OBJS} + set(TURBOJPEG_SOURCES ${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) + wrbmp.c wrppm.c $ $) set(TJMAPFILE ${CMAKE_CURRENT_SOURCE_DIR}/turbojpeg-mapfile) if(WITH_JAVA) set(TURBOJPEG_SOURCES ${TURBOJPEG_SOURCES} turbojpeg-jni.c) @@ -611,7 +658,16 @@ if(WITH_TURBOJPEG) set(TURBOJPEG_SOURCES ${TURBOJPEG_SOURCES} ${CMAKE_BINARY_DIR}/win/turbojpeg.rc) endif() - add_library(turbojpeg SHARED ${TURBOJPEG_SOURCES}) + add_library(turbojpeg12 OBJECT rdppm.c wrppm.c) + set_property(TARGET turbojpeg12 PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=12 -DPPM_SUPPORTED") + set_target_properties(turbojpeg12 PROPERTIES POSITION_INDEPENDENT_CODE 1) + add_library(turbojpeg16 OBJECT rdppm.c wrppm.c) + set_property(TARGET turbojpeg16 PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=16 -DPPM_SUPPORTED") + set_target_properties(turbojpeg16 PROPERTIES POSITION_INDEPENDENT_CODE 1) + add_library(turbojpeg SHARED ${TURBOJPEG_SOURCES} + $ $) set_property(TARGET turbojpeg PROPERTY COMPILE_FLAGS "-DBMP_SUPPORTED -DPPM_SUPPORTED") if(WIN32) @@ -648,9 +704,17 @@ if(WITH_TURBOJPEG) endif() if(ENABLE_STATIC) - add_library(turbojpeg-static STATIC ${JPEG_SOURCES} $ + add_library(turbojpeg12-static OBJECT rdppm.c wrppm.c) + set_property(TARGET turbojpeg12-static PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=12 -DPPM_SUPPORTED") + add_library(turbojpeg16-static OBJECT rdppm.c wrppm.c) + set_property(TARGET turbojpeg16-static PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=16 -DPPM_SUPPORTED") + add_library(turbojpeg-static STATIC ${JPEG_SOURCES} ${SIMD_TARGET_OBJECTS} ${SIMD_OBJS} turbojpeg.c transupp.c jdatadst-tj.c jdatasrc-tj.c rdbmp.c - rdppm.c wrbmp.c wrppm.c) + rdppm.c wrbmp.c wrppm.c $ + $ $ + $) set_property(TARGET turbojpeg-static PROPERTY COMPILE_FLAGS "-DBMP_SUPPORTED -DPPM_SUPPORTED") if(NOT MSVC) @@ -672,28 +736,46 @@ endif() if(WIN32) set(USE_SETMODE "-DUSE_SETMODE") endif() -if(WITH_12BIT) - set(COMPILE_FLAGS "-DGIF_SUPPORTED -DPPM_SUPPORTED ${USE_SETMODE}") -else() - set(COMPILE_FLAGS "-DBMP_SUPPORTED -DGIF_SUPPORTED -DPPM_SUPPORTED -DTARGA_SUPPORTED ${USE_SETMODE}") - set(CJPEG_BMP_SOURCES rdbmp.c rdtarga.c) - set(DJPEG_BMP_SOURCES wrbmp.c wrtarga.c) -endif() +set(CDJPEG_COMPILE_FLAGS + "-DBMP_SUPPORTED -DGIF_SUPPORTED -DPPM_SUPPORTED -DTARGA_SUPPORTED ${USE_SETMODE}") if(ENABLE_STATIC) - add_executable(cjpeg-static cjpeg.c cdjpeg.c rdgif.c rdppm.c rdswitch.c - ${CJPEG_BMP_SOURCES}) - set_property(TARGET cjpeg-static PROPERTY COMPILE_FLAGS ${COMPILE_FLAGS}) + # Compile a separate version of these source files with 12-bit and 16-bit + # data precision. + add_library(cjpeg12-static OBJECT rdgif.c rdppm.c) + set_property(TARGET cjpeg12-static PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=12 -DGIF_SUPPORTED -DPPM_SUPPORTED") + add_library(cjpeg16-static OBJECT rdgif.c rdppm.c) + set_property(TARGET cjpeg16-static PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=16 -DGIF_SUPPORTED -DPPM_SUPPORTED") + add_executable(cjpeg-static cjpeg.c cdjpeg.c rdbmp.c rdgif.c rdppm.c + rdswitch.c rdtarga.c $ + $) + set_property(TARGET cjpeg-static PROPERTY COMPILE_FLAGS + ${CDJPEG_COMPILE_FLAGS}) target_link_libraries(cjpeg-static jpeg-static) - add_executable(djpeg-static djpeg.c cdjpeg.c rdcolmap.c rdswitch.c wrgif.c - wrppm.c ${DJPEG_BMP_SOURCES}) - set_property(TARGET djpeg-static PROPERTY COMPILE_FLAGS ${COMPILE_FLAGS}) + # Compile a separate version of these source files with 12-bit and 16-bit + # data precision. + add_library(djpeg12-static OBJECT rdcolmap.c wrgif.c wrppm.c) + set_property(TARGET djpeg12-static PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=12 -DGIF_SUPPORTED -DPPM_SUPPORTED") + add_library(djpeg16-static OBJECT wrppm.c) + set_property(TARGET djpeg16-static PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=16 -DPPM_SUPPORTED") + add_executable(djpeg-static djpeg.c cdjpeg.c rdcolmap.c rdswitch.c wrbmp.c + wrgif.c wrppm.c wrtarga.c $ + $) + set_property(TARGET djpeg-static PROPERTY COMPILE_FLAGS + ${CDJPEG_COMPILE_FLAGS}) target_link_libraries(djpeg-static jpeg-static) add_executable(jpegtran-static jpegtran.c cdjpeg.c rdswitch.c transupp.c) target_link_libraries(jpegtran-static jpeg-static) set_property(TARGET jpegtran-static PROPERTY COMPILE_FLAGS "${USE_SETMODE}") + + add_executable(example-static example.c) + target_link_libraries(example-static jpeg-static) endif() add_executable(rdjpgcom rdjpgcom.c) @@ -713,7 +795,7 @@ add_executable(strtest strtest.c) add_subdirectory(md5) -if(MSVC_IDE OR XCODE) +if(GENERATOR_IS_MULTI_CONFIG) set(OBJDIR "\${CTEST_CONFIGURATION_TYPE}/") else() set(OBJDIR "") @@ -721,124 +803,6 @@ endif() enable_testing() -if(WITH_12BIT) - set(TESTORIG testorig12.jpg) - set(MD5_JPEG_RGB_ISLOW 9d7369207c520d37f2c1cbfcb82b2964) - set(MD5_JPEG_RGB_ISLOW2 a00bd20d8ae49684640ef7177d2e0b64) - set(MD5_PPM_RGB_ISLOW f3301d2219783b8b3d942b7239fa50c0) - set(MD5_JPEG_422_IFAST_OPT 7322e3bd2f127f7de4b40d4480ce60e4) - set(MD5_PPM_422_IFAST 79807fa552899e66a04708f533e16950) - set(MD5_JPEG_440_ISLOW e25c1912e38367be505a89c410c1c2d2) - set(MD5_PPM_440_ISLOW e7d2e26288870cfcb30f3114ad01e380) - set(MD5_PPM_422M_IFAST 07737bfe8a7c1c87aaa393a0098d16b0) - set(MD5_JPEG_420_IFAST_Q100_PROG 9447cef4803d9b0f74bcf333cc710a29) - set(MD5_PPM_420_Q100_IFAST 1b3730122709f53d007255e8dfd3305e) - set(MD5_PPM_420M_Q100_IFAST 980a1a3c5bf9510022869d30b7d26566) - set(MD5_JPEG_GRAY_ISLOW 235c90707b16e2e069f37c888b2636d9) - set(MD5_PPM_GRAY_ISLOW 7213c10af507ad467da5578ca5ee1fca) - set(MD5_PPM_GRAY_ISLOW_RGB e96ee81c30a6ed422d466338bd3de65d) - set(MD5_JPEG_420S_IFAST_OPT 7af8e60be4d9c227ec63ac9b6630855e) - - set(MD5_JPEG_3x2_FLOAT_PROG_SSE a8c17daf77b457725ec929e215b603f8) - set(MD5_PPM_3x2_FLOAT_SSE 42876ab9e5c2f76a87d08db5fbd57956) - set(MD5_JPEG_3x2_FLOAT_PROG_NO_FP_CONTRACT a8c17daf77b457725ec929e215b603f8) - set(MD5_PPM_3x2_FLOAT_NO_FP_CONTRACT ${MD5_PPM_3x2_FLOAT_SSE}) - set(MD5_JPEG_3x2_FLOAT_PROG_FP_CONTRACT - ${MD5_JPEG_3x2_FLOAT_PROG_NO_FP_CONTRACT}) - set(MD5_PPM_3x2_FLOAT_FP_CONTRACT ${MD5_PPM_3x2_FLOAT_SSE}) - set(MD5_JPEG_3x2_FLOAT_PROG_387 bc6dbbefac2872f6b9d6c4a0ae60c3c0) - set(MD5_PPM_3x2_FLOAT_387 bcc5723c61560463ac60f772e742d092) - set(MD5_JPEG_3x2_FLOAT_PROG_MSVC e27840755870fa849872e58aa0cd1400) - set(MD5_PPM_3x2_FLOAT_MSVC 6c2880b83bb1aa41dfe330e7a9768690) - - set(MD5_JPEG_3x2_IFAST_PROG 1396cc2b7185cfe943d408c9d305339e) - set(MD5_PPM_3x2_IFAST 3975985ef6eeb0a2cdc58daa651ccc00) - set(MD5_PPM_420M_ISLOW_2_1 4ca6be2a6f326ff9eaab63e70a8259c0) - set(MD5_PPM_420M_ISLOW_15_8 12aa9f9534c1b3d7ba047322226365eb) - set(MD5_PPM_420M_ISLOW_13_8 f7e22817c7b25e1393e4ec101e9d4e96) - set(MD5_PPM_420M_ISLOW_11_8 800a16f9f4dc9b293197bfe11be10a82) - set(MD5_PPM_420M_ISLOW_9_8 06b7a92a9bc69f4dc36ec40f1937d55c) - set(MD5_PPM_420M_ISLOW_7_8 3ec444a14a4ab4eab88ffc49c48eca43) - set(MD5_PPM_420M_ISLOW_3_4 3e726b7ea872445b19437d1c1d4f0d93) - set(MD5_PPM_420M_ISLOW_5_8 a8a771abdc94301d20ffac119b2caccd) - set(MD5_PPM_420M_ISLOW_1_2 b419124dd5568b085787234866102866) - set(MD5_PPM_420M_ISLOW_3_8 343d19015531b7bbe746124127244fa8) - set(MD5_PPM_420M_ISLOW_1_4 35fd59d866e44659edfa3c18db2a3edb) - set(MD5_PPM_420M_ISLOW_1_8 ccaed48ac0aedefda5d4abe4013f4ad7) - set(MD5_PPM_420_ISLOW_SKIP15_31 86664cd9dc956536409e44e244d20a97) - set(MD5_PPM_420_ISLOW_PROG_CROP62x62_71_71 452a21656115a163029cfba5c04fa76a) - set(MD5_PPM_444_ISLOW_SKIP1_6 ef63901f71ef7a75cd78253fc0914f84) - set(MD5_PPM_444_ISLOW_PROG_CROP98x98_13_13 15b173fb5872d9575572fbcc1b05956f) - set(MD5_JPEG_CROP cdb35ff4b4519392690ea040c56ea99c) -else() - set(TESTORIG testorig.jpg) - set(MD5_JPEG_RGB_ISLOW 1d44a406f61da743b5fd31c0a9abdca3) - set(MD5_JPEG_RGB_ISLOW2 31d121e57b6c2934c890a7fc7763bcd4) - set(MD5_PPM_RGB_ISLOW 00a257f5393fef8821f2b88ac7421291) - set(MD5_BMP_RGB_ISLOW_565 f07d2e75073e4bb10f6c6f4d36e2e3be) - set(MD5_BMP_RGB_ISLOW_565D 4cfa0928ef3e6bb626d7728c924cfda4) - set(MD5_JPEG_422_IFAST_OPT 2540287b79d913f91665e660303ab2c8) - set(MD5_PPM_422_IFAST 35bd6b3f833bad23de82acea847129fa) - set(MD5_JPEG_440_ISLOW 538bc02bd4b4658fd85de6ece6cbeda6) - set(MD5_PPM_440_ISLOW 11e7eab7ef7ef3276934bb7e7b6bb377) - set(MD5_PPM_422M_IFAST 8dbc65323d62cca7c91ba02dd1cfa81d) - set(MD5_BMP_422M_IFAST_565 3294bd4d9a1f2b3d08ea6020d0db7065) - set(MD5_BMP_422M_IFAST_565D da98c9c7b6039511be4a79a878a9abc1) - set(MD5_JPEG_420_IFAST_Q100_PROG 0ba15f9dab81a703505f835f9dbbac6d) - set(MD5_PPM_420_Q100_IFAST 5a732542015c278ff43635e473a8a294) - set(MD5_PPM_420M_Q100_IFAST ff692ee9323a3b424894862557c092f1) - set(MD5_JPEG_GRAY_ISLOW 72b51f894b8f4a10b3ee3066770aa38d) - set(MD5_PPM_GRAY_ISLOW 8d3596c56eace32f205deccc229aa5ed) - set(MD5_PPM_GRAY_ISLOW_RGB 116424ac07b79e5e801f00508eab48ec) - set(MD5_BMP_GRAY_ISLOW_565 12f78118e56a2f48b966f792fedf23cc) - set(MD5_BMP_GRAY_ISLOW_565D bdbbd616441a24354c98553df5dc82db) - set(MD5_JPEG_420S_IFAST_OPT 388708217ac46273ca33086b22827ed8) - - set(MD5_JPEG_3x2_FLOAT_PROG_SSE 343e3f8caf8af5986ebaf0bdc13b5c71) - set(MD5_PPM_3x2_FLOAT_SSE 1a75f36e5904d6fc3a85a43da9ad89bb) - set(MD5_JPEG_3x2_FLOAT_PROG_NO_FP_CONTRACT 9bca803d2042bd1eb03819e2bf92b3e5) - set(MD5_PPM_3x2_FLOAT_NO_FP_CONTRACT f6bfab038438ed8f5522fbd33595dcdc) - set(MD5_JPEG_3x2_FLOAT_PROG_FP_CONTRACT - ${MD5_JPEG_3x2_FLOAT_PROG_NO_FP_CONTRACT}) - set(MD5_PPM_3x2_FLOAT_FP_CONTRACT 0e917a34193ef976b679a6b069b1be26) - set(MD5_JPEG_3x2_FLOAT_PROG_387 1657664a410e0822c924b54f6f65e6e9) - set(MD5_PPM_3x2_FLOAT_387 cb0a1f027f3d2917c902b5640214e025) - set(MD5_JPEG_3x2_FLOAT_PROG_MSVC 7999ce9cd0ee9b6c7043b7351ab7639d) - set(MD5_PPM_3x2_FLOAT_MSVC 28cdc448a6b75e97892f0e0f8d4b21f3) - - set(MD5_JPEG_3x2_IFAST_PROG 1ee5d2c1a77f2da495f993c8c7cceca5) - set(MD5_PPM_3x2_IFAST fd283664b3b49127984af0a7f118fccd) - set(MD5_JPEG_420_ISLOW_ARI e986fb0a637a8d833d96e8a6d6d84ea1) - set(MD5_JPEG_444_ISLOW_PROGARI 0a8f1c8f66e113c3cf635df0a475a617) - set(MD5_PPM_420M_IFAST_ARI 57251da28a35b46eecb7177d82d10e0e) - set(MD5_JPEG_420_ISLOW 9a68f56bc76e466aa7e52f415d0f4a5f) - set(MD5_PPM_420M_ISLOW_2_1 9f9de8c0612f8d06869b960b05abf9c9) - set(MD5_PPM_420M_ISLOW_15_8 b6875bc070720b899566cc06459b63b7) - set(MD5_PPM_420M_ISLOW_13_8 bc3452573c8152f6ae552939ee19f82f) - set(MD5_PPM_420M_ISLOW_11_8 d8cc73c0aaacd4556569b59437ba00a5) - set(MD5_PPM_420M_ISLOW_9_8 d25e61bc7eac0002f5b393aa223747b6) - set(MD5_PPM_420M_ISLOW_7_8 ddb564b7c74a09494016d6cd7502a946) - set(MD5_PPM_420M_ISLOW_3_4 8ed8e68808c3fbc4ea764fc9d2968646) - set(MD5_PPM_420M_ISLOW_5_8 a3363274999da2366a024efae6d16c9b) - set(MD5_PPM_420M_ISLOW_1_2 e692a315cea26b988c8e8b29a5dbcd81) - set(MD5_PPM_420M_ISLOW_3_8 79eca9175652ced755155c90e785a996) - set(MD5_PPM_420M_ISLOW_1_4 79cd778f8bf1a117690052cacdd54eca) - set(MD5_PPM_420M_ISLOW_1_8 391b3d4aca640c8567d6f8745eb2142f) - set(MD5_BMP_420_ISLOW_256 4980185e3776e89bd931736e1cddeee6) - set(MD5_BMP_420_ISLOW_565 bf9d13e16c4923b92e1faa604d7922cb) - set(MD5_BMP_420_ISLOW_565D 6bde71526acc44bcff76f696df8638d2) - set(MD5_BMP_420M_ISLOW_565 8dc0185245353cfa32ad97027342216f) - set(MD5_BMP_420M_ISLOW_565D ce034037d212bc403330df6f915c161b) - set(MD5_PPM_420_ISLOW_SKIP15_31 c4c65c1e43d7275cd50328a61e6534f0) - set(MD5_PPM_420_ISLOW_ARI_SKIP16_139 087c6b123db16ac00cb88c5b590bb74a) - set(MD5_PPM_420_ISLOW_PROG_CROP62x62_71_71 26eb36ccc7d1f0cb80cdabb0ac8b5d99) - set(MD5_PPM_420_ISLOW_ARI_CROP53x53_4_4 886c6775af22370257122f8b16207e6d) - set(MD5_PPM_444_ISLOW_SKIP1_6 5606f86874cf26b8fcee1117a0a436a6) - set(MD5_PPM_444_ISLOW_PROG_CROP98x98_13_13 db87dc7ce26bcdc7a6b56239ce2b9d6c) - set(MD5_PPM_444_ISLOW_ARI_CROP37x37_0_0 cb57b32bd6d03e35432362f7bf184b6d) - set(MD5_JPEG_CROP b4197f377e621c4e9b1d20471432610d) -endif() - if(WITH_JAVA) add_test(TJUnitTest ${Java_JAVA_EXECUTABLE} ${JAVAARGS} -cp java/turbojpeg.jar @@ -852,6 +816,10 @@ if(WITH_JAVA) ${Java_JAVA_EXECUTABLE} ${JAVAARGS} -cp java/turbojpeg.jar -Djava.library.path=${CMAKE_CURRENT_BINARY_DIR}/${OBJDIR} TJUnitTest -yuv -noyuvpad) + add_test(TJUnitTest-lossless + ${Java_JAVA_EXECUTABLE} ${JAVAARGS} -cp java/turbojpeg.jar + -Djava.library.path=${CMAKE_CURRENT_BINARY_DIR}/${OBJDIR} + TJUnitTest -lossless) add_test(TJUnitTest-bi ${Java_JAVA_EXECUTABLE} ${JAVAARGS} -cp java/turbojpeg.jar -Djava.library.path=${CMAKE_CURRENT_BINARY_DIR}/${OBJDIR} @@ -864,6 +832,22 @@ if(WITH_JAVA) ${Java_JAVA_EXECUTABLE} ${JAVAARGS} -cp java/turbojpeg.jar -Djava.library.path=${CMAKE_CURRENT_BINARY_DIR}/${OBJDIR} TJUnitTest -bi -yuv -noyuvpad) + add_test(TJUnitTest-bi-lossless + ${Java_JAVA_EXECUTABLE} ${JAVAARGS} -cp java/turbojpeg.jar + -Djava.library.path=${CMAKE_CURRENT_BINARY_DIR}/${OBJDIR} + TJUnitTest -bi -lossless) + 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 "") @@ -882,22 +866,22 @@ if(CMAKE_CROSSCOMPILING) endif() # The output of the floating point DCT/IDCT algorithms differs depending on the -# type of floating point math used, so the FLOATTEST CMake variable must be -# set in order to tell the testing system which floating point results it -# should expect: +# type of floating point math used, so the FLOATTEST8 and FLOATTEST12 CMake +# variables must be set in order to tell the testing system which floating +# point results it should expect: # # sse = validate against the expected results from the libjpeg-turbo SSE SIMD # extensions # no-fp-contract = validate against the expected results from the C code when # floating point expression contraction is disabled (the -# default with Clang, with GCC when building for platforms -# that lack fused multiply-add [FMA] instructions, or when -# passing -ffp-contract=off to the compiler) +# default with Clang 13 and earlier, when building for +# platforms that lack fused multiply-add [FMA] instructions, +# or when passing -ffp-contract=off to GCC or Clang) # fp-contract = validate against the expected results from the C code when # floating point expression contraction is enabled (the default -# with GCC when building for platforms that have fused multiply- -# add [FMA] instructions or when passing -ffp-contract=fast to -# the compiler) +# with Clang 14 and later, with GCC when building for platforms +# that have fused multiply-add [FMA] instructions, or when +# passing -ffp-contract=fast to GCC or -ffp-contract=on to Clang) # 387 = validate against the expected results from the C code when the 387 FPU # is being used for floating point math (which is generally the default # with x86 compilers) @@ -906,43 +890,83 @@ endif() if(CPU_TYPE STREQUAL "x86_64" OR CPU_TYPE STREQUAL "i386") if(WITH_SIMD) - set(DEFAULT_FLOATTEST sse) + set(DEFAULT_FLOATTEST8 sse) elseif(CPU_TYPE STREQUAL "x86_64") - set(DEFAULT_FLOATTEST no-fp-contract) - # else we can't really set an intelligent default for i386. The appropriate - # value could be no-fp-contract, fp-contract, 387, or msvc, depending on the - # compiler and compiler options. We leave it to the user to set FLOATTEST - # manually. + set(DEFAULT_FLOATTEST8 no-fp-contract) endif() -else() - if((CPU_TYPE STREQUAL "powerpc" OR CPU_TYPE STREQUAL "arm64") AND - NOT CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT MSVC) - set(DEFAULT_FLOATTEST fp-contract) - else() - set(DEFAULT_FLOATTEST no-fp-contract) +elseif(CPU_TYPE STREQUAL "powerpc" OR CPU_TYPE STREQUAL "arm64") + if(CMAKE_C_COMPILER_ID STREQUAL "Clang") + if(CMAKE_C_COMPILER_VERSION VERSION_EQUAL 14.0.0 OR + CMAKE_C_COMPILER_VERSION VERSION_GREATER 14.0.0) + set(DEFAULT_FLOATTEST8 fp-contract) + else() + set(DEFAULT_FLOATTEST8 no-fp-contract) + endif() + elseif(CMAKE_COMPILER_IS_GNUCC) + set(DEFAULT_FLOATTEST8 fp-contract) endif() +# else we can't really set an intelligent default for FLOATTEST8. The +# appropriate value could be no-fp-contract, fp-contract, 387, or msvc, +# depending on the compiler and compiler options. We leave it to the user to +# set FLOATTEST8 manually. endif() -# This causes FLOATTEST to reset to the default value if WITH_SIMD has +# This causes FLOATTEST8 to reset to the default value if WITH_SIMD has # changed. if(DEFINED WITH_SIMD_INT AND NOT WITH_SIMD EQUAL WITH_SIMD_INT) - set(FORCE_FLOATTEST "FORCE") + set(FORCE_FLOATTEST8 "FORCE") endif() set(WITH_SIMD_INT ${WITH_SIMD} CACHE INTERNAL "") -set(FLOATTEST ${DEFAULT_FLOATTEST} CACHE STRING - "The type of floating point math used by the floating point DCT/IDCT algorithms. This tells the testing system which numerical results it should expect from those tests. [sse = libjpeg-turbo x86/x86-64 SIMD extensions, no-fp-contract = generic FPU with floating point expression contraction disabled, fp-contract = generic FPU with floating point expression contraction enabled, 387 = 387 FPU, msvc = 32-bit Visual Studio] (default = ${DEFAULT_FLOATTEST})" - ${FORCE_FLOATTEST}) -message(STATUS "FLOATTEST = ${FLOATTEST}") - -if(FLOATTEST) - string(TOUPPER ${FLOATTEST} FLOATTEST_UC) - string(REGEX REPLACE "-" "_" FLOATTEST_UC ${FLOATTEST_UC}) - string(TOLOWER ${FLOATTEST} FLOATTEST) - if(NOT FLOATTEST STREQUAL "sse" AND - NOT FLOATTEST STREQUAL "no-fp-contract" AND - NOT FLOATTEST STREQUAL "fp-contract" AND NOT FLOATTEST STREQUAL "387" AND - NOT FLOATTEST STREQUAL "msvc") - message(FATAL_ERROR "\"${FLOATTEST}\" is not a valid value for FLOATTEST.") +set(FLOATTEST8 ${DEFAULT_FLOATTEST8} CACHE STRING + "The type of floating point math used by the 8-bit-per-sample floating point DCT/IDCT algorithms. This tells the testing system which numerical results it should expect from those tests. [sse = libjpeg-turbo x86/x86-64 SIMD extensions, no-fp-contract = generic FPU with floating point expression contraction disabled, fp-contract = generic FPU with floating point expression contraction enabled, 387 = 387 FPU, msvc = 32-bit Visual Studio] (default = ${DEFAULT_FLOATTEST8})" + ${FORCE_FLOATTEST8}) +message(STATUS "FLOATTEST8 = ${FLOATTEST8}") + +if(FLOATTEST8) + string(TOUPPER ${FLOATTEST8} FLOATTEST8_UC) + string(REGEX REPLACE "-" "_" FLOATTEST8_UC ${FLOATTEST8_UC}) + string(TOLOWER ${FLOATTEST8} FLOATTEST8) + if(NOT FLOATTEST8 STREQUAL "sse" AND + NOT FLOATTEST8 STREQUAL "no-fp-contract" AND + NOT FLOATTEST8 STREQUAL "fp-contract" AND NOT FLOATTEST8 STREQUAL "387" AND + NOT FLOATTEST8 STREQUAL "msvc") + message(FATAL_ERROR "\"${FLOATTEST8}\" is not a valid value for FLOATTEST8.") + endif() +endif() + +if(CPU_TYPE STREQUAL "x86_64") + set(DEFAULT_FLOATTEST12 no-fp-contract) +elseif(CPU_TYPE STREQUAL "powerpc" OR CPU_TYPE STREQUAL "arm64") + if(CMAKE_C_COMPILER_ID STREQUAL "Clang") + if(CMAKE_C_COMPILER_VERSION VERSION_EQUAL 14.0.0 OR + CMAKE_C_COMPILER_VERSION VERSION_GREATER 14.0.0) + set(DEFAULT_FLOATTEST12 fp-contract) + else() + set(DEFAULT_FLOATTEST12 no-fp-contract) + endif() + elseif(CMAKE_COMPILER_IS_GNUCC) + set(DEFAULT_FLOATTEST12 fp-contract) + endif() +# else we can't really set an intelligent default for FLOATTEST12. The +# appropriate value could be no-fp-contract, fp-contract, or something else, +# depending on the compiler and compiler options. We leave it to the user to +# set FLOATTEST12 manually. +endif() + +set(FLOATTEST12 ${DEFAULT_FLOATTEST12} CACHE STRING + "The type of floating point math used by the 12-bit-per-sample floating point DCT/IDCT algorithms. This tells the testing system which numerical results it should expect from those tests. [sse = libjpeg-turbo x86/x86-64 SIMD extensions, no-fp-contract = generic FPU with floating point expression contraction disabled, fp-contract = generic FPU with floating point expression contraction enabled, 387 = 387 FPU, msvc = 32-bit Visual Studio] (default = ${DEFAULT_FLOATTEST12})") +message(STATUS "FLOATTEST12 = ${FLOATTEST12}") + +if(FLOATTEST12) + string(TOUPPER ${FLOATTEST12} FLOATTEST12_UC) + string(REGEX REPLACE "-" "_" FLOATTEST12_UC ${FLOATTEST12_UC}) + string(TOLOWER ${FLOATTEST12} FLOATTEST12) + if(NOT FLOATTEST12 STREQUAL "sse" AND + NOT FLOATTEST12 STREQUAL "no-fp-contract" AND + NOT FLOATTEST12 STREQUAL "fp-contract" AND + NOT FLOATTEST12 STREQUAL "387" AND + NOT FLOATTEST12 STREQUAL "msvc") + message(FATAL_ERROR "\"${FLOATTEST12}\" is not a valid value for FLOATTEST12.") endif() endif() @@ -961,85 +985,143 @@ foreach(libtype ${TEST_LIBTYPES}) ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -yuv -alloc) add_test(tjunittest-${libtype}-yuv-nopad ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -yuv -noyuvpad) + add_test(tjunittest-${libtype}-lossless + ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -lossless) + add_test(tjunittest-${libtype}-lossless-alloc + ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -lossless -alloc) add_test(tjunittest-${libtype}-bmp ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -bmp) + add_test(tjunittest12-${libtype} + ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -precision 12) + add_test(tjunittest12-${libtype}-alloc + ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -precision 12 + -alloc) + add_test(tjunittest12-${libtype}-lossless + ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -precision 12 + -lossless) + add_test(tjunittest12-${libtype}-lossless-alloc + ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -precision 12 + -lossless -alloc) + add_test(tjunittest12-${libtype}-bmp + ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -precision 12 -bmp) + add_test(tjunittest16-${libtype}-lossless + ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -precision 16) + add_test(tjunittest16-${libtype}-lossless-alloc + ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -precision 16 + -alloc) + add_test(tjunittest16-${libtype}-bmp + ${CMAKE_CROSSCOMPILING_EMULATOR} tjunittest${suffix} -precision 16 -bmp) + + foreach(sample_bits 8 12) + + if(sample_bits EQUAL 12) + set(tjbench tjbench12) + set(testout testout12${suffix}) + + 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${suffix}) + + set(MD5_PPM_GRAY_TILE 2c3b567086e6ca0c5e6d34ad8d6f6fe8) + set(MD5_PPM_420_8x8_TILE efca1bdf0226df01777137778cf986ec) + set(MD5_PPM_420_16x16_TILE 8c92c7453870d9e11c6d1dec3a8c9101) + set(MD5_PPM_420_32x32_TILE 3f7651872a95e469d1c7115f1b11ecef) + set(MD5_PPM_420_64x64_TILE f64c71af03fdea12363b62f1a3096aab) + set(MD5_PPM_420_128x128_TILE 5a5ef57517558c671bf5e75793588d69) + set(MD5_PPM_420M_8x8_TILE 66bd869b315a32a00fef1a025661ce72) + set(MD5_PPM_420M_TILE bf9ec2ab4875abb2efcce8f876fe2c2a) + set(MD5_PPM_422_8x8_TILE c300553ce1b3b90fd414ec96b62fe988) + set(MD5_PPM_422_16x16_TILE 6559ddb1191f5b2d3eb41081b254c4e0) + set(MD5_PPM_422_32x32_TILE 58691797f4584c4c5ed5965a6bb9aec0) + set(MD5_PPM_422_64x64_TILE 7f9e34942ae46af7b784f459ec133f5e) + set(MD5_PPM_422_128x128_TILE 6afcb77580d85dd3eacb04b3c2bc7710) + set(MD5_PPM_422M_8x8_TILE 55df1f96bcfb631aedeb940cf3f011f5) + set(MD5_PPM_422M_TILE 6502031018c2d2f69bc6353347f8df4d) + set(MD5_PPM_444_TILE 87bd58005eec73f0f313c8e38d0d793c) + endif() - set(MD5_PPM_GRAY_TILE 89d3ca21213d9d864b50b4e4e7de4ca6) - set(MD5_PPM_420_8x8_TILE 847fceab15c5b7b911cb986cf0f71de3) - set(MD5_PPM_420_16x16_TILE ca45552a93687e078f7137cc4126a7b0) - set(MD5_PPM_420_32x32_TILE d8676f1d6b68df358353bba9844f4a00) - set(MD5_PPM_420_64x64_TILE 4e4c1a3d7ea4bace4f868bcbe83b7050) - set(MD5_PPM_420_128x128_TILE f24c3429c52265832beab9df72a0ceae) - set(MD5_PPM_420M_8x8_TILE bc25320e1f4c31ce2e610e43e9fd173c) - set(MD5_PPM_420M_TILE 75ffdf14602258c5c189522af57fa605) - set(MD5_PPM_422_8x8_TILE d83dacd9fc73b0a6f10c09acad64eb1e) - set(MD5_PPM_422_16x16_TILE 35077fb610d72dd743b1eb0cbcfe10fb) - set(MD5_PPM_422_32x32_TILE e6902ed8a449ecc0f0d6f2bf945f65f7) - set(MD5_PPM_422_64x64_TILE 2b4502a8f316cedbde1da7bce3d2231e) - set(MD5_PPM_422_128x128_TILE f0b5617d578f5e13c8eee215d64d4877) - set(MD5_PPM_422M_8x8_TILE 828941d7f41cd6283abd6beffb7fd51d) - set(MD5_PPM_422M_TILE e877ae1324c4a280b95376f7f018172f) - set(MD5_PPM_444_TILE 7964e41e67cfb8d0a587c0aa4798f9c3) - - # Test compressing from/decompressing to an arbitrary subregion of a larger - # image buffer - add_test(tjbench-${libtype}-tile-cp - ${CMAKE_COMMAND} -E copy_if_different ${TESTIMAGES}/testorig.ppm - testout_tile.ppm) - add_test(tjbench-${libtype}-tile - ${CMAKE_CROSSCOMPILING_EMULATOR} tjbench${suffix} testout_tile.ppm 95 - -rgb -quiet -tile -benchtime 0.01 -warmup 0) - set_tests_properties(tjbench-${libtype}-tile - PROPERTIES DEPENDS tjbench-${libtype}-tile-cp) - - foreach(tile 8 16 32 64 128) - add_test(tjbench-${libtype}-tile-gray-${tile}x${tile}-cmp - ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} ${MD5_PPM_GRAY_TILE} - testout_tile_GRAY_Q95_${tile}x${tile}.ppm) - foreach(subsamp 420 422) - add_test(tjbench-${libtype}-tile-${subsamp}-${tile}x${tile}-cmp - ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} - ${MD5_PPM_${subsamp}_${tile}x${tile}_TILE} - testout_tile_${subsamp}_Q95_${tile}x${tile}.ppm) - 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) + # 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() - endforeach() - add_test(tjbench-${libtype}-tilem-cp - ${CMAKE_COMMAND} -E copy_if_different ${TESTIMAGES}/testorig.ppm - testout_tilem.ppm) - add_test(tjbench-${libtype}-tilem - ${CMAKE_CROSSCOMPILING_EMULATOR} tjbench${suffix} testout_tilem.ppm 95 - -rgb -fastupsample -quiet -tile -benchtime 0.01 -warmup 0) - set_tests_properties(tjbench-${libtype}-tilem - PROPERTIES DEPENDS tjbench-${libtype}-tilem-cp) - - add_test(tjbench-${libtype}-tile-420m-8x8-cmp - ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} ${MD5_PPM_420M_8x8_TILE} - testout_tilem_420_Q95_8x8.ppm) - add_test(tjbench-${libtype}-tile-422m-8x8-cmp - ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} ${MD5_PPM_422M_8x8_TILE} - testout_tilem_422_Q95_8x8.ppm) - foreach(tile 16 32 64 128) - foreach(subsamp 420 422) - add_test(tjbench-${libtype}-tile-${subsamp}m-${tile}x${tile}-cmp - ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} - ${MD5_PPM_${subsamp}M_TILE} - testout_tilem_${subsamp}_Q95_${tile}x${tile}.ppm) + add_test(${tjbench}-${libtype}-tilem-cp + ${CMAKE_COMMAND} -E copy_if_different ${TESTIMAGES}/testorig.ppm + ${testout}_tilem.ppm) + add_test(${tjbench}-${libtype}-tilem + ${CMAKE_CROSSCOMPILING_EMULATOR} tjbench${suffix} ${testout}_tilem.ppm + 95 -precision ${sample_bits} -rgb -fastupsample -quiet -tile + -benchtime 0.01 -warmup 0) + set_tests_properties(${tjbench}-${libtype}-tilem + PROPERTIES DEPENDS ${tjbench}-${libtype}-tilem-cp) + + add_test(${tjbench}-${libtype}-tile-420m-8x8-cmp + ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} ${MD5_PPM_420M_8x8_TILE} + ${testout}_tilem_420_Q95_8x8.ppm) + add_test(${tjbench}-${libtype}-tile-422m-8x8-cmp + ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} ${MD5_PPM_422M_8x8_TILE} + ${testout}_tilem_422_Q95_8x8.ppm) + foreach(tile 16 32 64 128) + foreach(subsamp 420 422) + add_test(${tjbench}-${libtype}-tile-${subsamp}m-${tile}x${tile}-cmp + ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} + ${MD5_PPM_${subsamp}M_TILE} + ${testout}_tilem_${subsamp}_Q95_${tile}x${tile}.ppm) + endforeach() endforeach() - endforeach() - foreach(tile 8 16 32 64 128) - foreach(subsamp 420 422) - set_tests_properties(tjbench-${libtype}-tile-${subsamp}m-${tile}x${tile}-cmp - PROPERTIES DEPENDS tjbench-${libtype}-tilem) + foreach(tile 8 16 32 64 128) + foreach(subsamp 420 422) + set_tests_properties( + ${tjbench}-${libtype}-tile-${subsamp}m-${tile}x${tile}-cmp + PROPERTIES DEPENDS ${tjbench}-${libtype}-tilem) + endforeach() endforeach() + endforeach() + endif() # These tests are carefully crafted to provide full coverage of as many of @@ -1047,8 +1129,17 @@ foreach(libtype ${TEST_LIBTYPES}) # SIMD-accelerated ones.) macro(add_bittest PROG NAME ARGS OUTFILE INFILE MD5SUM) + if(${PROG} STREQUAL "cjpeg16") + set(ACTUAL_ARGS "${ARGS};-precision;16") + elseif(${PROG} STREQUAL "cjpeg12") + set(ACTUAL_ARGS "${ARGS};-precision;12") + else() + set(ACTUAL_ARGS ${ARGS}) + endif() + string(REGEX REPLACE "16" "" ACTUAL_PROG ${PROG}) + string(REGEX REPLACE "12" "" ACTUAL_PROG ${ACTUAL_PROG}) add_test(${PROG}-${libtype}-${NAME} - ${CMAKE_CROSSCOMPILING_EMULATOR} ${PROG}${suffix} ${ARGS} + ${CMAKE_CROSSCOMPILING_EMULATOR} ${ACTUAL_PROG}${suffix} ${ACTUAL_ARGS} -outfile ${OUTFILE} ${INFILE}) add_test(${PROG}-${libtype}-${NAME}-cmp ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} ${MD5SUM} ${OUTFILE}) @@ -1061,296 +1152,515 @@ foreach(libtype ${TEST_LIBTYPES}) endif() endmacro() - # CC: null SAMP: fullsize FDCT: islow ENT: huff - add_bittest(cjpeg rgb-islow "-rgb;-dct;int;-icc;${TESTIMAGES}/test1.icc" - testout_rgb_islow.jpg ${TESTIMAGES}/testorig.ppm - ${MD5_JPEG_RGB_ISLOW}) - - # CC: null SAMP: fullsize IDCT: islow ENT: huff - add_bittest(djpeg rgb-islow "-dct;int;-ppm;-icc;testout_rgb_islow.icc" - testout_rgb_islow.ppm testout_rgb_islow.jpg - ${MD5_PPM_RGB_ISLOW} cjpeg-${libtype}-rgb-islow) - - add_test(djpeg-${libtype}-rgb-islow-icc-cmp - ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} - b06a39d730129122e85c1363ed1bbc9e testout_rgb_islow.icc) - set_tests_properties(djpeg-${libtype}-rgb-islow-icc-cmp PROPERTIES - DEPENDS djpeg-${libtype}-rgb-islow) - - add_bittest(jpegtran icc "-copy;all;-icc;${TESTIMAGES}/test2.icc" - testout_rgb_islow2.jpg testout_rgb_islow.jpg - ${MD5_JPEG_RGB_ISLOW2} cjpeg-${libtype}-rgb-islow) - - if(NOT WITH_12BIT) - # CC: RGB->RGB565 SAMP: fullsize IDCT: islow ENT: huff - add_bittest(djpeg rgb-islow-565 "-dct;int;-rgb565;-dither;none;-bmp" - testout_rgb_islow_565.bmp testout_rgb_islow.jpg - ${MD5_BMP_RGB_ISLOW_565} cjpeg-${libtype}-rgb-islow) - - # CC: RGB->RGB565 (dithered) SAMP: fullsize IDCT: islow ENT: huff - add_bittest(djpeg rgb-islow-565D "-dct;int;-rgb565;-bmp" - testout_rgb_islow_565D.bmp testout_rgb_islow.jpg - ${MD5_BMP_RGB_ISLOW_565D} cjpeg-${libtype}-rgb-islow) - endif() + set(MD5_JPEG_LOSSLESS fe99437df4e9976fe5e841969242b208) + set(MD5_PPM_LOSSLESS 01d9642a2b8723fefebbe9cb074ccd02) + + # Lossless (all arguments other than -lossless and -restart should have no + # effect) + add_bittest(cjpeg16 lossless + "-lossless;4;-restart;1;-quality;1;-grayscale;-optimize;-dct;float;-smooth;100;-baseline;-qslots;1,0,0;-sample;1x2,3x4,2x1" + testout16_lossless.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_LOSSLESS}) + add_bittest(djpeg16 lossless + "-fast;-scale;1/8;-dct;float;-dither;none;-nosmooth;-onepass" + testout16_lossless.ppm testout16_lossless.jpg + ${MD5_PPM_LOSSLESS} cjpeg16-${libtype}-lossless) + + foreach(sample_bits 8 12) + + if(sample_bits EQUAL 12) + set(cjpeg cjpeg12) + set(djpeg djpeg12) + set(jpegtran jpegtran12) + set(testout testout12${suffix}) + + set(TESTORIG testorig12.jpg) + set(MD5_JPEG_RGB_ISLOW 9d7369207c520d37f2c1cbfcb82b2964) + set(MD5_JPEG_RGB_ISLOW2 a00bd20d8ae49684640ef7177d2e0b64) + set(MD5_PPM_RGB_ISLOW f3301d2219783b8b3d942b7239fa50c0) + set(MD5_JPEG_422_IFAST_OPT 7322e3bd2f127f7de4b40d4480ce60e4) + set(MD5_PPM_422_IFAST 79807fa552899e66a04708f533e16950) + set(MD5_JPEG_440_ISLOW e25c1912e38367be505a89c410c1c2d2) + set(MD5_PPM_440_ISLOW e7d2e26288870cfcb30f3114ad01e380) + set(MD5_PPM_422M_IFAST 07737bfe8a7c1c87aaa393a0098d16b0) + set(MD5_JPEG_420_IFAST_Q100_PROG 9447cef4803d9b0f74bcf333cc710a29) + set(MD5_PPM_420_Q100_IFAST 1b3730122709f53d007255e8dfd3305e) + set(MD5_PPM_420M_Q100_IFAST 980a1a3c5bf9510022869d30b7d26566) + set(MD5_JPEG_GRAY_ISLOW 235c90707b16e2e069f37c888b2636d9) + set(MD5_PPM_GRAY_ISLOW 7213c10af507ad467da5578ca5ee1fca) + set(MD5_PPM_GRAY_ISLOW_RGB e96ee81c30a6ed422d466338bd3de65d) + set(MD5_JPEG_420S_IFAST_OPT 7af8e60be4d9c227ec63ac9b6630855e) + + set(MD5_JPEG_3x2_FLOAT_PROG_SSE a8c17daf77b457725ec929e215b603f8) + set(MD5_PPM_3x2_FLOAT_SSE 42876ab9e5c2f76a87d08db5fbd57956) + set(MD5_JPEG_3x2_FLOAT_PROG_NO_FP_CONTRACT + a8c17daf77b457725ec929e215b603f8) + set(MD5_PPM_3x2_FLOAT_NO_FP_CONTRACT ${MD5_PPM_3x2_FLOAT_SSE}) + set(MD5_JPEG_3x2_FLOAT_PROG_FP_CONTRACT + ${MD5_JPEG_3x2_FLOAT_PROG_NO_FP_CONTRACT}) + if(CMAKE_C_COMPILER_ID STREQUAL "Clang") + set(MD5_PPM_3x2_FLOAT_FP_CONTRACT 2da9de6ae869e88b8372de815d366b03) + else() + set(MD5_PPM_3x2_FLOAT_FP_CONTRACT ${MD5_PPM_3x2_FLOAT_SSE}) + endif() + set(MD5_JPEG_3x2_FLOAT_PROG_387 bc6dbbefac2872f6b9d6c4a0ae60c3c0) + set(MD5_PPM_3x2_FLOAT_387 bcc5723c61560463ac60f772e742d092) + set(MD5_JPEG_3x2_FLOAT_PROG_MSVC e27840755870fa849872e58aa0cd1400) + set(MD5_PPM_3x2_FLOAT_MSVC 6c2880b83bb1aa41dfe330e7a9768690) + + set(MD5_JPEG_3x2_IFAST_PROG 1396cc2b7185cfe943d408c9d305339e) + set(MD5_PPM_3x2_IFAST 3975985ef6eeb0a2cdc58daa651ccc00) + set(MD5_PPM_420M_ISLOW_2_1 4ca6be2a6f326ff9eaab63e70a8259c0) + set(MD5_PPM_420M_ISLOW_15_8 12aa9f9534c1b3d7ba047322226365eb) + set(MD5_PPM_420M_ISLOW_13_8 f7e22817c7b25e1393e4ec101e9d4e96) + set(MD5_PPM_420M_ISLOW_11_8 800a16f9f4dc9b293197bfe11be10a82) + set(MD5_PPM_420M_ISLOW_9_8 06b7a92a9bc69f4dc36ec40f1937d55c) + set(MD5_PPM_420M_ISLOW_7_8 3ec444a14a4ab4eab88ffc49c48eca43) + set(MD5_PPM_420M_ISLOW_3_4 3e726b7ea872445b19437d1c1d4f0d93) + set(MD5_PPM_420M_ISLOW_5_8 a8a771abdc94301d20ffac119b2caccd) + set(MD5_PPM_420M_ISLOW_1_2 b419124dd5568b085787234866102866) + set(MD5_PPM_420M_ISLOW_3_8 343d19015531b7bbe746124127244fa8) + set(MD5_PPM_420M_ISLOW_1_4 35fd59d866e44659edfa3c18db2a3edb) + set(MD5_PPM_420M_ISLOW_1_8 ccaed48ac0aedefda5d4abe4013f4ad7) + set(MD5_JPEG_LOSSLESS 8473501f5bb7c826524472c858bf4fcd) + set(MD5_PPM_LOSSLESS 1da3fb2620e5a4e258e0fcb891bc67e8) + set(MD5_PPM_420_ISLOW_SKIP15_31 86664cd9dc956536409e44e244d20a97) + set(MD5_PPM_420_ISLOW_PROG_CROP62x62_71_71 + 452a21656115a163029cfba5c04fa76a) + set(MD5_PPM_444_ISLOW_SKIP1_6 ef63901f71ef7a75cd78253fc0914f84) + set(MD5_PPM_444_ISLOW_PROG_CROP98x98_13_13 + 15b173fb5872d9575572fbcc1b05956f) + set(MD5_JPEG_CROP cdb35ff4b4519392690ea040c56ea99c) + + set(MD5_JPEG_EXAMPLE_COMPRESS 5e502da0c3c0f957a58c536f31e973dc) + set(MD5_PPM_EXAMPLE_DECOMPRESS 2ff0e8505ee6e0ffaeb24037d5650b57) + else() + set(cjpeg cjpeg) + set(djpeg djpeg) + set(jpegtran jpegtran) + set(testout testout${suffix}) + + set(TESTORIG testorig.jpg) + set(MD5_JPEG_RGB_ISLOW 1d44a406f61da743b5fd31c0a9abdca3) + set(MD5_JPEG_RGB_ISLOW2 31d121e57b6c2934c890a7fc7763bcd4) + set(MD5_PPM_RGB_ISLOW 00a257f5393fef8821f2b88ac7421291) + set(MD5_BMP_RGB_ISLOW_565 f07d2e75073e4bb10f6c6f4d36e2e3be) + set(MD5_BMP_RGB_ISLOW_565D 4cfa0928ef3e6bb626d7728c924cfda4) + set(MD5_JPEG_422_IFAST_OPT 2540287b79d913f91665e660303ab2c8) + set(MD5_PPM_422_IFAST 35bd6b3f833bad23de82acea847129fa) + set(MD5_JPEG_440_ISLOW 538bc02bd4b4658fd85de6ece6cbeda6) + set(MD5_PPM_440_ISLOW 11e7eab7ef7ef3276934bb7e7b6bb377) + set(MD5_PPM_422M_IFAST 8dbc65323d62cca7c91ba02dd1cfa81d) + set(MD5_BMP_422M_IFAST_565 3294bd4d9a1f2b3d08ea6020d0db7065) + set(MD5_BMP_422M_IFAST_565D da98c9c7b6039511be4a79a878a9abc1) + set(MD5_JPEG_420_IFAST_Q100_PROG 0ba15f9dab81a703505f835f9dbbac6d) + set(MD5_PPM_420_Q100_IFAST 5a732542015c278ff43635e473a8a294) + set(MD5_PPM_420M_Q100_IFAST ff692ee9323a3b424894862557c092f1) + set(MD5_JPEG_GRAY_ISLOW 72b51f894b8f4a10b3ee3066770aa38d) + set(MD5_PPM_GRAY_ISLOW 8d3596c56eace32f205deccc229aa5ed) + set(MD5_PPM_GRAY_ISLOW_RGB 116424ac07b79e5e801f00508eab48ec) + set(MD5_BMP_GRAY_ISLOW_565 12f78118e56a2f48b966f792fedf23cc) + set(MD5_BMP_GRAY_ISLOW_565D bdbbd616441a24354c98553df5dc82db) + set(MD5_JPEG_420S_IFAST_OPT 388708217ac46273ca33086b22827ed8) + + set(MD5_JPEG_3x2_FLOAT_PROG_SSE 343e3f8caf8af5986ebaf0bdc13b5c71) + set(MD5_PPM_3x2_FLOAT_SSE 1a75f36e5904d6fc3a85a43da9ad89bb) + set(MD5_JPEG_3x2_FLOAT_PROG_NO_FP_CONTRACT + 9bca803d2042bd1eb03819e2bf92b3e5) + set(MD5_PPM_3x2_FLOAT_NO_FP_CONTRACT f6bfab038438ed8f5522fbd33595dcdc) + set(MD5_JPEG_3x2_FLOAT_PROG_FP_CONTRACT + ${MD5_JPEG_3x2_FLOAT_PROG_NO_FP_CONTRACT}) + if(CMAKE_C_COMPILER_ID STREQUAL "Clang") + set(MD5_PPM_3x2_FLOAT_FP_CONTRACT ${MD5_PPM_3x2_FLOAT_NO_FP_CONTRACT}) + else() + set(MD5_PPM_3x2_FLOAT_FP_CONTRACT 0e917a34193ef976b679a6b069b1be26) + endif() + set(MD5_JPEG_3x2_FLOAT_PROG_387 1657664a410e0822c924b54f6f65e6e9) + set(MD5_PPM_3x2_FLOAT_387 cb0a1f027f3d2917c902b5640214e025) + set(MD5_JPEG_3x2_FLOAT_PROG_MSVC 7999ce9cd0ee9b6c7043b7351ab7639d) + set(MD5_PPM_3x2_FLOAT_MSVC 28cdc448a6b75e97892f0e0f8d4b21f3) + + set(MD5_JPEG_3x2_IFAST_PROG 1ee5d2c1a77f2da495f993c8c7cceca5) + set(MD5_PPM_3x2_IFAST fd283664b3b49127984af0a7f118fccd) + set(MD5_JPEG_420_ISLOW_ARI e986fb0a637a8d833d96e8a6d6d84ea1) + set(MD5_JPEG_444_ISLOW_PROGARI 0a8f1c8f66e113c3cf635df0a475a617) + set(MD5_PPM_420M_IFAST_ARI 57251da28a35b46eecb7177d82d10e0e) + set(MD5_JPEG_420_ISLOW 9a68f56bc76e466aa7e52f415d0f4a5f) + set(MD5_PPM_420M_ISLOW_2_1 9f9de8c0612f8d06869b960b05abf9c9) + set(MD5_PPM_420M_ISLOW_15_8 b6875bc070720b899566cc06459b63b7) + set(MD5_PPM_420M_ISLOW_13_8 bc3452573c8152f6ae552939ee19f82f) + set(MD5_PPM_420M_ISLOW_11_8 d8cc73c0aaacd4556569b59437ba00a5) + set(MD5_PPM_420M_ISLOW_9_8 d25e61bc7eac0002f5b393aa223747b6) + set(MD5_PPM_420M_ISLOW_7_8 ddb564b7c74a09494016d6cd7502a946) + set(MD5_PPM_420M_ISLOW_3_4 8ed8e68808c3fbc4ea764fc9d2968646) + set(MD5_PPM_420M_ISLOW_5_8 a3363274999da2366a024efae6d16c9b) + set(MD5_PPM_420M_ISLOW_1_2 e692a315cea26b988c8e8b29a5dbcd81) + set(MD5_PPM_420M_ISLOW_3_8 79eca9175652ced755155c90e785a996) + set(MD5_PPM_420M_ISLOW_1_4 79cd778f8bf1a117690052cacdd54eca) + set(MD5_PPM_420M_ISLOW_1_8 391b3d4aca640c8567d6f8745eb2142f) + set(MD5_BMP_420_ISLOW_256 4980185e3776e89bd931736e1cddeee6) + set(MD5_BMP_420_ISLOW_565 bf9d13e16c4923b92e1faa604d7922cb) + set(MD5_BMP_420_ISLOW_565D 6bde71526acc44bcff76f696df8638d2) + set(MD5_BMP_420M_ISLOW_565 8dc0185245353cfa32ad97027342216f) + set(MD5_BMP_420M_ISLOW_565D ce034037d212bc403330df6f915c161b) + set(MD5_JPEG_LOSSLESS fc777b82d42d835ae1282ba1ee87c209) + set(MD5_PPM_LOSSLESS 64072f1dbdc5b3a187777788604971a5) + set(MD5_PPM_420_ISLOW_SKIP15_31 c4c65c1e43d7275cd50328a61e6534f0) + set(MD5_PPM_420_ISLOW_ARI_SKIP16_139 087c6b123db16ac00cb88c5b590bb74a) + set(MD5_PPM_420_ISLOW_PROG_CROP62x62_71_71 + 26eb36ccc7d1f0cb80cdabb0ac8b5d99) + set(MD5_PPM_420_ISLOW_ARI_CROP53x53_4_4 886c6775af22370257122f8b16207e6d) + set(MD5_PPM_444_ISLOW_SKIP1_6 5606f86874cf26b8fcee1117a0a436a6) + set(MD5_PPM_444_ISLOW_PROG_CROP98x98_13_13 + db87dc7ce26bcdc7a6b56239ce2b9d6c) + set(MD5_PPM_444_ISLOW_ARI_CROP37x37_0_0 cb57b32bd6d03e35432362f7bf184b6d) + set(MD5_JPEG_CROP b4197f377e621c4e9b1d20471432610d) + + set(MD5_JPEG_EXAMPLE_COMPRESS 95d4d72e2ef127332654c2599afb47bf) + set(MD5_PPM_EXAMPLE_DECOMPRESS 6fdde7301575bfd711e295b969b6b3de) + endif() - # CC: RGB->YCC SAMP: fullsize/h2v1 FDCT: ifast ENT: 2-pass huff - add_bittest(cjpeg 422-ifast-opt "-sample;2x1;-dct;fast;-opt" - testout_422_ifast_opt.jpg ${TESTIMAGES}/testorig.ppm - ${MD5_JPEG_422_IFAST_OPT}) - - # CC: YCC->RGB SAMP: fullsize/h2v1 fancy IDCT: ifast ENT: huff - add_bittest(djpeg 422-ifast "-dct;fast" - testout_422_ifast.ppm testout_422_ifast_opt.jpg - ${MD5_PPM_422_IFAST} cjpeg-${libtype}-422-ifast-opt) - - # CC: RGB->YCC SAMP: fullsize/h1v2 FDCT: islow ENT: huff - add_bittest(cjpeg 440-islow "-sample;1x2;-dct;int" - testout_440_islow.jpg ${TESTIMAGES}/testorig.ppm - ${MD5_JPEG_440_ISLOW}) - - # CC: YCC->RGB SAMP: fullsize/h1v2 fancy IDCT: islow ENT: huff - add_bittest(djpeg 440-islow "-dct;int" - testout_440_islow.ppm testout_440_islow.jpg - ${MD5_PPM_440_ISLOW} cjpeg-${libtype}-440-islow) - - # CC: YCC->RGB SAMP: h2v1 merged IDCT: ifast ENT: huff - add_bittest(djpeg 422m-ifast "-dct;fast;-nosmooth" - testout_422m_ifast.ppm testout_422_ifast_opt.jpg - ${MD5_PPM_422M_IFAST} cjpeg-${libtype}-422-ifast-opt) - - if(NOT WITH_12BIT) - # CC: YCC->RGB565 SAMP: h2v1 merged IDCT: ifast ENT: huff - add_bittest(djpeg 422m-ifast-565 - "-dct;int;-nosmooth;-rgb565;-dither;none;-bmp" - testout_422m_ifast_565.bmp testout_422_ifast_opt.jpg - ${MD5_BMP_422M_IFAST_565} cjpeg-${libtype}-422-ifast-opt) - - # CC: YCC->RGB565 (dithered) SAMP: h2v1 merged IDCT: ifast ENT: huff - add_bittest(djpeg 422m-ifast-565D "-dct;int;-nosmooth;-rgb565;-bmp" - testout_422m_ifast_565D.bmp testout_422_ifast_opt.jpg - ${MD5_BMP_422M_IFAST_565D} cjpeg-${libtype}-422-ifast-opt) - endif() + # CC: null SAMP: fullsize FDCT: islow ENT: huff + add_bittest(${cjpeg} rgb-islow "-rgb;-dct;int;-icc;${TESTIMAGES}/test1.icc" + ${testout}_rgb_islow.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_RGB_ISLOW}) + + # CC: null SAMP: fullsize IDCT: islow ENT: huff + add_bittest(${djpeg} rgb-islow + "-dct;int;-ppm;-icc;${testout}_rgb_islow.icc" + ${testout}_rgb_islow.ppm ${testout}_rgb_islow.jpg + ${MD5_PPM_RGB_ISLOW} ${cjpeg}-${libtype}-rgb-islow) + + add_test(${djpeg}-${libtype}-rgb-islow-icc-cmp + ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} + b06a39d730129122e85c1363ed1bbc9e ${testout}_rgb_islow.icc) + set_tests_properties(${djpeg}-${libtype}-rgb-islow-icc-cmp PROPERTIES + DEPENDS ${djpeg}-${libtype}-rgb-islow) + + add_bittest(${jpegtran} icc "-copy;all;-icc;${TESTIMAGES}/test2.icc" + ${testout}_rgb_islow2.jpg ${testout}_rgb_islow.jpg + ${MD5_JPEG_RGB_ISLOW2} ${cjpeg}-${libtype}-rgb-islow) + + if(sample_bits EQUAL 8) + # CC: RGB->RGB565 SAMP: fullsize IDCT: islow ENT: huff + add_bittest(${djpeg} rgb-islow-565 "-dct;int;-rgb565;-dither;none;-bmp" + ${testout}_rgb_islow_565.bmp ${testout}_rgb_islow.jpg + ${MD5_BMP_RGB_ISLOW_565} ${cjpeg}-${libtype}-rgb-islow) + + # CC: RGB->RGB565 (dithered) SAMP: fullsize IDCT: islow ENT: huff + add_bittest(${djpeg} rgb-islow-565D "-dct;int;-rgb565;-bmp" + ${testout}_rgb_islow_565D.bmp ${testout}_rgb_islow.jpg + ${MD5_BMP_RGB_ISLOW_565D} ${cjpeg}-${libtype}-rgb-islow) + endif() - # CC: RGB->YCC SAMP: fullsize/h2v2 FDCT: ifast ENT: prog huff - add_bittest(cjpeg 420-q100-ifast-prog - "-sample;2x2;-quality;100;-dct;fast;-scans;${TESTIMAGES}/test.scan" - testout_420_q100_ifast_prog.jpg ${TESTIMAGES}/testorig.ppm - ${MD5_JPEG_420_IFAST_Q100_PROG}) - - # CC: YCC->RGB SAMP: fullsize/h2v2 fancy IDCT: ifast ENT: prog huff - add_bittest(djpeg 420-q100-ifast-prog "-dct;fast" - testout_420_q100_ifast.ppm testout_420_q100_ifast_prog.jpg - ${MD5_PPM_420_Q100_IFAST} cjpeg-${libtype}-420-q100-ifast-prog) - - # CC: YCC->RGB SAMP: h2v2 merged IDCT: ifast ENT: prog huff - add_bittest(djpeg 420m-q100-ifast-prog "-dct;fast;-nosmooth" - testout_420m_q100_ifast.ppm testout_420_q100_ifast_prog.jpg - ${MD5_PPM_420M_Q100_IFAST} cjpeg-${libtype}-420-q100-ifast-prog) - - # CC: RGB->Gray SAMP: fullsize FDCT: islow ENT: huff - add_bittest(cjpeg gray-islow "-gray;-dct;int" - testout_gray_islow.jpg ${TESTIMAGES}/testorig.ppm - ${MD5_JPEG_GRAY_ISLOW}) - - # CC: Gray->Gray SAMP: fullsize IDCT: islow ENT: huff - add_bittest(djpeg gray-islow "-dct;int" - testout_gray_islow.ppm testout_gray_islow.jpg - ${MD5_PPM_GRAY_ISLOW} cjpeg-${libtype}-gray-islow) - - # CC: Gray->RGB SAMP: fullsize IDCT: islow ENT: huff - add_bittest(djpeg gray-islow-rgb "-dct;int;-rgb" - testout_gray_islow_rgb.ppm testout_gray_islow.jpg - ${MD5_PPM_GRAY_ISLOW_RGB} cjpeg-${libtype}-gray-islow) - - if(NOT WITH_12BIT) - # CC: Gray->RGB565 SAMP: fullsize IDCT: islow ENT: huff - add_bittest(djpeg gray-islow-565 "-dct;int;-rgb565;-dither;none;-bmp" - testout_gray_islow_565.bmp testout_gray_islow.jpg - ${MD5_BMP_GRAY_ISLOW_565} cjpeg-${libtype}-gray-islow) - - # CC: Gray->RGB565 (dithered) SAMP: fullsize IDCT: islow ENT: huff - add_bittest(djpeg gray-islow-565D "-dct;int;-rgb565;-bmp" - testout_gray_islow_565D.bmp testout_gray_islow.jpg - ${MD5_BMP_GRAY_ISLOW_565D} cjpeg-${libtype}-gray-islow) - endif() + # CC: RGB->YCC SAMP: fullsize/h2v1 FDCT: ifast ENT: 2-pass huff + add_bittest(${cjpeg} 422-ifast-opt "-sample;2x1;-dct;fast;-opt" + ${testout}_422_ifast_opt.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_422_IFAST_OPT}) + + # CC: YCC->RGB SAMP: fullsize/h2v1 fancy IDCT: ifast ENT: huff + add_bittest(${djpeg} 422-ifast "-dct;fast" + ${testout}_422_ifast.ppm ${testout}_422_ifast_opt.jpg + ${MD5_PPM_422_IFAST} ${cjpeg}-${libtype}-422-ifast-opt) + + # CC: RGB->YCC SAMP: fullsize/h1v2 FDCT: islow ENT: huff + add_bittest(${cjpeg} 440-islow "-sample;1x2;-dct;int" + ${testout}_440_islow.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_440_ISLOW}) + + # CC: YCC->RGB SAMP: fullsize/h1v2 fancy IDCT: islow ENT: huff + add_bittest(${djpeg} 440-islow "-dct;int" + ${testout}_440_islow.ppm ${testout}_440_islow.jpg + ${MD5_PPM_440_ISLOW} ${cjpeg}-${libtype}-440-islow) + + # CC: YCC->RGB SAMP: h2v1 merged IDCT: ifast ENT: huff + add_bittest(${djpeg} 422m-ifast "-dct;fast;-nosmooth" + ${testout}_422m_ifast.ppm ${testout}_422_ifast_opt.jpg + ${MD5_PPM_422M_IFAST} ${cjpeg}-${libtype}-422-ifast-opt) + + if(sample_bits EQUAL 8) + # CC: YCC->RGB565 SAMP: h2v1 merged IDCT: ifast ENT: huff + add_bittest(${djpeg} 422m-ifast-565 + "-dct;int;-nosmooth;-rgb565;-dither;none;-bmp" + ${testout}_422m_ifast_565.bmp ${testout}_422_ifast_opt.jpg + ${MD5_BMP_422M_IFAST_565} ${cjpeg}-${libtype}-422-ifast-opt) + + # CC: YCC->RGB565 (dithered) SAMP: h2v1 merged IDCT: ifast ENT: huff + add_bittest(${djpeg} 422m-ifast-565D "-dct;int;-nosmooth;-rgb565;-bmp" + ${testout}_422m_ifast_565D.bmp ${testout}_422_ifast_opt.jpg + ${MD5_BMP_422M_IFAST_565D} ${cjpeg}-${libtype}-422-ifast-opt) + endif() - # CC: RGB->YCC SAMP: fullsize smooth/h2v2 smooth FDCT: islow - # ENT: 2-pass huff - add_bittest(cjpeg 420s-ifast-opt "-sample;2x2;-smooth;1;-dct;int;-opt" - testout_420s_ifast_opt.jpg ${TESTIMAGES}/testorig.ppm - ${MD5_JPEG_420S_IFAST_OPT}) - - if(FLOATTEST) - # CC: RGB->YCC SAMP: fullsize/int FDCT: float ENT: prog huff - add_bittest(cjpeg 3x2-float-prog "-sample;3x2;-dct;float;-prog" - testout_3x2_float_prog.jpg ${TESTIMAGES}/testorig.ppm - ${MD5_JPEG_3x2_FLOAT_PROG_${FLOATTEST_UC}}) - - # CC: YCC->RGB SAMP: fullsize/int IDCT: float ENT: prog huff - add_bittest(djpeg 3x2-float-prog "-dct;float" - testout_3x2_float.ppm testout_3x2_float_prog.jpg - ${MD5_PPM_3x2_FLOAT_${FLOATTEST_UC}} cjpeg-${libtype}-3x2-float-prog) - endif() + # CC: RGB->YCC SAMP: fullsize/h2v2 FDCT: ifast ENT: prog huff + add_bittest(${cjpeg} 420-q100-ifast-prog + "-sample;2x2;-quality;100;-dct;fast;-scans;${TESTIMAGES}/test.scan" + ${testout}_420_q100_ifast_prog.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_420_IFAST_Q100_PROG}) + + # CC: YCC->RGB SAMP: fullsize/h2v2 fancy IDCT: ifast ENT: prog huff + add_bittest(${djpeg} 420-q100-ifast-prog "-dct;fast" + ${testout}_420_q100_ifast.ppm ${testout}_420_q100_ifast_prog.jpg + ${MD5_PPM_420_Q100_IFAST} ${cjpeg}-${libtype}-420-q100-ifast-prog) + + # CC: YCC->RGB SAMP: h2v2 merged IDCT: ifast ENT: prog huff + add_bittest(${djpeg} 420m-q100-ifast-prog "-dct;fast;-nosmooth" + ${testout}_420m_q100_ifast.ppm ${testout}_420_q100_ifast_prog.jpg + ${MD5_PPM_420M_Q100_IFAST} ${cjpeg}-${libtype}-420-q100-ifast-prog) + + # CC: RGB->Gray SAMP: fullsize FDCT: islow ENT: huff + add_bittest(${cjpeg} gray-islow "-gray;-dct;int" + ${testout}_gray_islow.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_GRAY_ISLOW}) + + # CC: Gray->Gray SAMP: fullsize IDCT: islow ENT: huff + add_bittest(${djpeg} gray-islow "-dct;int" + ${testout}_gray_islow.ppm ${testout}_gray_islow.jpg + ${MD5_PPM_GRAY_ISLOW} ${cjpeg}-${libtype}-gray-islow) + + # CC: Gray->RGB SAMP: fullsize IDCT: islow ENT: huff + add_bittest(${djpeg} gray-islow-rgb "-dct;int;-rgb" + ${testout}_gray_islow_rgb.ppm ${testout}_gray_islow.jpg + ${MD5_PPM_GRAY_ISLOW_RGB} ${cjpeg}-${libtype}-gray-islow) + + if(sample_bits EQUAL 8) + # CC: Gray->RGB565 SAMP: fullsize IDCT: islow ENT: huff + add_bittest(${djpeg} gray-islow-565 "-dct;int;-rgb565;-dither;none;-bmp" + ${testout}_gray_islow_565.bmp ${testout}_gray_islow.jpg + ${MD5_BMP_GRAY_ISLOW_565} ${cjpeg}-${libtype}-gray-islow) + + # CC: Gray->RGB565 (dithered) SAMP: fullsize IDCT: islow ENT: huff + add_bittest(${djpeg} gray-islow-565D "-dct;int;-rgb565;-bmp" + ${testout}_gray_islow_565D.bmp ${testout}_gray_islow.jpg + ${MD5_BMP_GRAY_ISLOW_565D} ${cjpeg}-${libtype}-gray-islow) + endif() + + # CC: RGB->YCC SAMP: fullsize smooth/h2v2 smooth FDCT: islow + # ENT: 2-pass huff + add_bittest(${cjpeg} 420s-ifast-opt "-sample;2x2;-smooth;1;-dct;int;-opt" + ${testout}_420s_ifast_opt.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_420S_IFAST_OPT}) + + if(FLOATTEST${sample_bits}) + # CC: RGB->YCC SAMP: fullsize/int FDCT: float ENT: prog huff + add_bittest(${cjpeg} 3x2-float-prog "-sample;3x2;-dct;float;-prog" + ${testout}_3x2_float_prog.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_3x2_FLOAT_PROG_${FLOATTEST${sample_bits}_UC}}) + + # CC: YCC->RGB SAMP: fullsize/int IDCT: float ENT: prog huff + add_bittest(${djpeg} 3x2-float-prog "-dct;float" + ${testout}_3x2_float.ppm ${testout}_3x2_float_prog.jpg + ${MD5_PPM_3x2_FLOAT_${FLOATTEST${sample_bits}_UC}} + ${cjpeg}-${libtype}-3x2-float-prog) + endif() # CC: RGB->YCC SAMP: fullsize/int FDCT: ifast ENT: prog huff - add_bittest(cjpeg 3x2-ifast-prog "-sample;3x2;-dct;fast;-prog" - testout_3x2_ifast_prog.jpg ${TESTIMAGES}/testorig.ppm - ${MD5_JPEG_3x2_IFAST_PROG}) - - # CC: YCC->RGB SAMP: fullsize/int IDCT: ifast ENT: prog huff - add_bittest(djpeg 3x2-ifast-prog "-dct;fast" - testout_3x2_ifast.ppm testout_3x2_ifast_prog.jpg - ${MD5_PPM_3x2_IFAST} cjpeg-${libtype}-3x2-ifast-prog) - - if(WITH_ARITH_ENC) - # CC: YCC->RGB SAMP: fullsize/h2v2 FDCT: islow ENT: arith - add_bittest(cjpeg 420-islow-ari "-dct;int;-arithmetic" - testout_420_islow_ari.jpg ${TESTIMAGES}/testorig.ppm - ${MD5_JPEG_420_ISLOW_ARI}) - - add_bittest(jpegtran 420-islow-ari "-arithmetic" - testout_420_islow_ari2.jpg ${TESTIMAGES}/testimgint.jpg - ${MD5_JPEG_420_ISLOW_ARI}) - - # CC: YCC->RGB SAMP: fullsize FDCT: islow ENT: prog arith - add_bittest(cjpeg 444-islow-progari - "-sample;1x1;-dct;int;-prog;-arithmetic" - testout_444_islow_progari.jpg ${TESTIMAGES}/testorig.ppm - ${MD5_JPEG_444_ISLOW_PROGARI}) - endif() + add_bittest(${cjpeg} 3x2-ifast-prog "-sample;3x2;-dct;fast;-prog" + ${testout}_3x2_ifast_prog.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_3x2_IFAST_PROG}) + + # CC: YCC->RGB SAMP: fullsize/int IDCT: ifast ENT: prog huff + add_bittest(${djpeg} 3x2-ifast-prog "-dct;fast" + ${testout}_3x2_ifast.ppm ${testout}_3x2_ifast_prog.jpg + ${MD5_PPM_3x2_IFAST} ${cjpeg}-${libtype}-3x2-ifast-prog) + + if(WITH_ARITH_ENC AND sample_bits EQUAL 8) + # CC: YCC->RGB SAMP: fullsize/h2v2 FDCT: islow ENT: arith + add_bittest(${cjpeg} 420-islow-ari "-dct;int;-arithmetic" + ${testout}_420_islow_ari.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_420_ISLOW_ARI}) + + add_bittest(${jpegtran} 420-islow-ari "-arithmetic" + ${testout}_420_islow_ari2.jpg ${TESTIMAGES}/testimgint.jpg + ${MD5_JPEG_420_ISLOW_ARI}) + + # CC: YCC->RGB SAMP: fullsize FDCT: islow ENT: prog arith + add_bittest(${cjpeg} 444-islow-progari + "-sample;1x1;-dct;int;-prog;-arithmetic" + ${testout}_444_islow_progari.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_444_ISLOW_PROGARI}) + endif() - if(WITH_ARITH_DEC) - # CC: RGB->YCC SAMP: h2v2 merged IDCT: ifast ENT: arith - add_bittest(djpeg 420m-ifast-ari "-fast;-skip;1,20;-ppm" - testout_420m_ifast_ari.ppm ${TESTIMAGES}/testimgari.jpg - ${MD5_PPM_420M_IFAST_ARI}) + if(WITH_ARITH_DEC AND sample_bits EQUAL 8) + # CC: RGB->YCC SAMP: h2v2 merged IDCT: ifast ENT: arith + add_bittest(${djpeg} 420m-ifast-ari "-fast;-skip;1,20;-ppm" + ${testout}_420m_ifast_ari.ppm ${TESTIMAGES}/testimgari.jpg + ${MD5_PPM_420M_IFAST_ARI}) - add_bittest(jpegtran 420-islow "" - testout_420_islow.jpg ${TESTIMAGES}/testimgari.jpg - ${MD5_JPEG_420_ISLOW}) - endif() + add_bittest(${jpegtran} 420-islow "" + ${testout}_420_islow.jpg ${TESTIMAGES}/testimgari.jpg + ${MD5_JPEG_420_ISLOW}) + endif() - # 2/1-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 16x16 islow ENT: huff - # 15/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 15x15 islow ENT: huff - # 13/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 13x13 islow ENT: huff - # 11/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 11x11 islow ENT: huff - # 9/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 9x9 islow ENT: huff - # 7/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 7x7 islow/14x14 islow - # ENT: huff - # 3/4-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 6x6 islow/12x12 islow - # ENT: huff - # 5/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 5x5 islow/10x10 islow - # ENT: huff - # 1/2-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 4x4 islow/8x8 islow - # ENT: huff - # 3/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 3x3 islow/6x6 islow - # ENT: huff - # 1/4-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 2x2 islow/4x4 islow - # ENT: huff - # 1/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 1x1 islow/2x2 islow - # ENT: huff - foreach(scale 2_1 15_8 13_8 11_8 9_8 7_8 3_4 5_8 1_2 3_8 1_4 1_8) - string(REGEX REPLACE "_" "/" scalearg ${scale}) - add_bittest(djpeg 420m-islow-${scale} - "-dct;int;-scale;${scalearg};-nosmooth;-ppm" - testout_420m_islow_${scale}.ppm ${TESTIMAGES}/${TESTORIG} - ${MD5_PPM_420M_ISLOW_${scale}}) - endforeach() + # 2/1-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 16x16 islow ENT: huff + # 15/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 15x15 islow ENT: huff + # 13/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 13x13 islow ENT: huff + # 11/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 11x11 islow ENT: huff + # 9/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 9x9 islow ENT: huff + # 7/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 7x7 islow/14x14 islow + # ENT: huff + # 3/4-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 6x6 islow/12x12 islow + # ENT: huff + # 5/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 5x5 islow/10x10 islow + # ENT: huff + # 1/2-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 4x4 islow/8x8 islow + # ENT: huff + # 3/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 3x3 islow/6x6 islow + # ENT: huff + # 1/4-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 2x2 islow/4x4 islow + # ENT: huff + # 1/8-- CC: YCC->RGB SAMP: h2v2 merged IDCT: 1x1 islow/2x2 islow + # ENT: huff + foreach(scale 2_1 15_8 13_8 11_8 9_8 7_8 3_4 5_8 1_2 3_8 1_4 1_8) + string(REGEX REPLACE "_" "/" scalearg ${scale}) + add_bittest(${djpeg} 420m-islow-${scale} + "-dct;int;-scale;${scalearg};-nosmooth;-ppm" + ${testout}_420m_islow_${scale}.ppm ${TESTIMAGES}/${TESTORIG} + ${MD5_PPM_420M_ISLOW_${scale}}) + endforeach() - if(NOT WITH_12BIT) - # CC: YCC->RGB (dithered) SAMP: h2v2 fancy IDCT: islow ENT: huff - add_bittest(djpeg 420-islow-256 "-dct;int;-colors;256;-bmp" - testout_420_islow_256.bmp ${TESTIMAGES}/${TESTORIG} - ${MD5_BMP_420_ISLOW_256}) - - # CC: YCC->RGB565 SAMP: h2v2 fancy IDCT: islow ENT: huff - add_bittest(djpeg 420-islow-565 "-dct;int;-rgb565;-dither;none;-bmp" - testout_420_islow_565.bmp ${TESTIMAGES}/${TESTORIG} - ${MD5_BMP_420_ISLOW_565}) - - # CC: YCC->RGB565 (dithered) SAMP: h2v2 fancy IDCT: islow ENT: huff - add_bittest(djpeg 420-islow-565D "-dct;int;-rgb565;-bmp" - testout_420_islow_565D.bmp ${TESTIMAGES}/${TESTORIG} - ${MD5_BMP_420_ISLOW_565D}) - - # CC: YCC->RGB565 SAMP: h2v2 merged IDCT: islow ENT: huff - add_bittest(djpeg 420m-islow-565 - "-dct;int;-nosmooth;-rgb565;-dither;none;-bmp" - testout_420m_islow_565.bmp ${TESTIMAGES}/${TESTORIG} - ${MD5_BMP_420M_ISLOW_565}) - - # CC: YCC->RGB565 (dithered) SAMP: h2v2 merged IDCT: islow ENT: huff - add_bittest(djpeg 420m-islow-565D "-dct;int;-nosmooth;-rgb565;-bmp" - testout_420m_islow_565D.bmp ${TESTIMAGES}/${TESTORIG} - ${MD5_BMP_420M_ISLOW_565D}) - endif() + if(sample_bits EQUAL 8) + # CC: YCC->RGB (dithered) SAMP: h2v2 fancy IDCT: islow ENT: huff + add_bittest(${djpeg} 420-islow-256 "-dct;int;-colors;256;-bmp" + ${testout}_420_islow_256.bmp ${TESTIMAGES}/${TESTORIG} + ${MD5_BMP_420_ISLOW_256}) + + # CC: YCC->RGB565 SAMP: h2v2 fancy IDCT: islow ENT: huff + add_bittest(${djpeg} 420-islow-565 "-dct;int;-rgb565;-dither;none;-bmp" + ${testout}_420_islow_565.bmp ${TESTIMAGES}/${TESTORIG} + ${MD5_BMP_420_ISLOW_565}) + + # CC: YCC->RGB565 (dithered) SAMP: h2v2 fancy IDCT: islow ENT: huff + add_bittest(${djpeg} 420-islow-565D "-dct;int;-rgb565;-bmp" + ${testout}_420_islow_565D.bmp ${TESTIMAGES}/${TESTORIG} + ${MD5_BMP_420_ISLOW_565D}) + + # CC: YCC->RGB565 SAMP: h2v2 merged IDCT: islow ENT: huff + add_bittest(${djpeg} 420m-islow-565 + "-dct;int;-nosmooth;-rgb565;-dither;none;-bmp" + ${testout}_420m_islow_565.bmp ${TESTIMAGES}/${TESTORIG} + ${MD5_BMP_420M_ISLOW_565}) + + # CC: YCC->RGB565 (dithered) SAMP: h2v2 merged IDCT: islow ENT: huff + add_bittest(${djpeg} 420m-islow-565D "-dct;int;-nosmooth;-rgb565;-bmp" + ${testout}_420m_islow_565D.bmp ${TESTIMAGES}/${TESTORIG} + ${MD5_BMP_420M_ISLOW_565D}) + endif() - # Partial decode tests. These tests are designed to cover all of the - # possible code paths in jpeg_skip_scanlines(). + # Lossless (all arguments other than -lossless and -restart should have no + # effect) + add_bittest(${cjpeg} lossless + "-lossless;4;-restart;1;-quality;1;-grayscale;-optimize;-dct;float;-smooth;100;-baseline;-qslots;1,0,0;-sample;1x2,3x4,2x1" + ${testout}_lossless.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_LOSSLESS}) + add_bittest(${djpeg} lossless + "-fast;-scale;1/8;-dct;float;-dither;none;-nosmooth;-onepass" + ${testout}_lossless.ppm ${testout}_lossless.jpg + ${MD5_PPM_LOSSLESS} ${cjpeg}-${libtype}-lossless) + + # Partial decode tests. These tests are designed to cover all of the + # possible code paths in jpeg_skip_scanlines(). + + # Context rows: Yes Intra-iMCU row: Yes iMCU row prefetch: No + # ENT: huff + add_bittest(${djpeg} 420-islow-skip15_31 "-dct;int;-skip;15,31;-ppm" + ${testout}_420_islow_skip15,31.ppm ${TESTIMAGES}/${TESTORIG} + ${MD5_PPM_420_ISLOW_SKIP15_31}) + + # Context rows: Yes Intra-iMCU row: No iMCU row prefetch: Yes + # ENT: arith + if(WITH_ARITH_DEC AND sample_bits EQUAL 8) + add_bittest(${djpeg} 420-islow-ari-skip16_139 + "-dct;int;-skip;16,139;-ppm" + ${testout}_420_islow_ari_skip16,139.ppm ${TESTIMAGES}/testimgari.jpg + ${MD5_PPM_420_ISLOW_ARI_SKIP16_139}) + endif() - # Context rows: Yes Intra-iMCU row: Yes iMCU row prefetch: No ENT: huff - add_bittest(djpeg 420-islow-skip15_31 "-dct;int;-skip;15,31;-ppm" - testout_420_islow_skip15,31.ppm ${TESTIMAGES}/${TESTORIG} - ${MD5_PPM_420_ISLOW_SKIP15_31}) + # Context rows: Yes Intra-iMCU row: No iMCU row prefetch: No + # ENT: prog huff + add_test(${cjpeg}-${libtype}-420-islow-prog + ${CMAKE_CROSSCOMPILING_EMULATOR} cjpeg${suffix} -dct int -prog + -precision ${sample_bits} -outfile ${testout}_420_islow_prog.jpg + ${TESTIMAGES}/testorig.ppm) + add_bittest(${djpeg} 420-islow-prog-crop62x62_71_71 + "-dct;int;-crop;62x62+71+71;-ppm" + ${testout}_420_islow_prog_crop62x62,71,71.ppm + ${testout}_420_islow_prog.jpg ${MD5_PPM_420_ISLOW_PROG_CROP62x62_71_71} + ${cjpeg}-${libtype}-420-islow-prog) + + # Context rows: Yes Intra-iMCU row: No iMCU row prefetch: No + # ENT: arith + if(WITH_ARITH_DEC AND sample_bits EQUAL 8) + add_bittest(${djpeg} 420-islow-ari-crop53x53_4_4 + "-dct;int;-crop;53x53+4+4;-ppm" + ${testout}_420_islow_ari_crop53x53,4,4.ppm ${TESTIMAGES}/testimgari.jpg + ${MD5_PPM_420_ISLOW_ARI_CROP53x53_4_4}) + endif() - # Context rows: Yes Intra-iMCU row: No iMCU row prefetch: Yes ENT: arith - if(WITH_ARITH_DEC) - add_bittest(djpeg 420-islow-ari-skip16_139 "-dct;int;-skip;16,139;-ppm" - testout_420_islow_ari_skip16,139.ppm ${TESTIMAGES}/testimgari.jpg - ${MD5_PPM_420_ISLOW_ARI_SKIP16_139}) - endif() + # Context rows: No Intra-iMCU row: Yes ENT: huff + add_test(${cjpeg}-${libtype}-444-islow + ${CMAKE_CROSSCOMPILING_EMULATOR} cjpeg${suffix} -dct int -sample 1x1 + -precision ${sample_bits} -outfile ${testout}_444_islow.jpg + ${TESTIMAGES}/testorig.ppm) + add_bittest(${djpeg} 444-islow-skip1_6 "-dct;int;-skip;1,6;-ppm" + ${testout}_444_islow_skip1,6.ppm ${testout}_444_islow.jpg + ${MD5_PPM_444_ISLOW_SKIP1_6} ${cjpeg}-${libtype}-444-islow) + + # Context rows: No Intra-iMCU row: No ENT: prog huff + add_test(${cjpeg}-${libtype}-444-islow-prog + ${CMAKE_CROSSCOMPILING_EMULATOR} cjpeg${suffix} -dct int -prog + -precision ${sample_bits} -sample 1x1 + -outfile ${testout}_444_islow_prog.jpg ${TESTIMAGES}/testorig.ppm) + add_bittest(${djpeg} 444-islow-prog-crop98x98_13_13 + "-dct;int;-crop;98x98+13+13;-ppm" + ${testout}_444_islow_prog_crop98x98,13,13.ppm + ${testout}_444_islow_prog.jpg ${MD5_PPM_444_ISLOW_PROG_CROP98x98_13_13} + ${cjpeg}-${libtype}-444-islow-prog) + + # Context rows: No Intra-iMCU row: No ENT: arith + if(WITH_ARITH_ENC AND sample_bits EQUAL 8) + add_test(${cjpeg}-${libtype}-444-islow-ari + ${CMAKE_CROSSCOMPILING_EMULATOR} cjpeg${suffix} -dct int -arithmetic + -sample 1x1 -precision ${sample_bits} + -outfile ${testout}_444_islow_ari.jpg ${TESTIMAGES}/testorig.ppm) + if(WITH_ARITH_DEC) + add_bittest(${djpeg} 444-islow-ari-crop37x37_0_0 + "-dct;int;-crop;37x37+0+0;-ppm" + ${testout}_444_islow_ari_crop37x37,0,0.ppm + ${testout}_444_islow_ari.jpg ${MD5_PPM_444_ISLOW_ARI_CROP37x37_0_0} + ${cjpeg}-${libtype}-444-islow-ari) + endif() + endif() - # Context rows: Yes Intra-iMCU row: No iMCU row prefetch: No ENT: prog huff - add_test(cjpeg-${libtype}-420-islow-prog - ${CMAKE_CROSSCOMPILING_EMULATOR} cjpeg${suffix} -dct int -prog - -outfile testout_420_islow_prog.jpg ${TESTIMAGES}/testorig.ppm) - add_bittest(djpeg 420-islow-prog-crop62x62_71_71 - "-dct;int;-crop;62x62+71+71;-ppm" - testout_420_islow_prog_crop62x62,71,71.ppm testout_420_islow_prog.jpg - ${MD5_PPM_420_ISLOW_PROG_CROP62x62_71_71} cjpeg-${libtype}-420-islow-prog) - - # Context rows: Yes Intra-iMCU row: No iMCU row prefetch: No ENT: arith - if(WITH_ARITH_DEC) - add_bittest(djpeg 420-islow-ari-crop53x53_4_4 - "-dct;int;-crop;53x53+4+4;-ppm" - testout_420_islow_ari_crop53x53,4,4.ppm ${TESTIMAGES}/testimgari.jpg - ${MD5_PPM_420_ISLOW_ARI_CROP53x53_4_4}) - endif() + add_bittest(${jpegtran} crop "-crop;120x90+20+50;-transpose;-perfect" + ${testout}_crop.jpg ${TESTIMAGES}/${TESTORIG} + ${MD5_JPEG_CROP}) - # Context rows: No Intra-iMCU row: Yes ENT: huff - add_test(cjpeg-${libtype}-444-islow - ${CMAKE_CROSSCOMPILING_EMULATOR} cjpeg${suffix} -dct int -sample 1x1 - -outfile testout_444_islow.jpg ${TESTIMAGES}/testorig.ppm) - add_bittest(djpeg 444-islow-skip1_6 "-dct;int;-skip;1,6;-ppm" - testout_444_islow_skip1,6.ppm testout_444_islow.jpg - ${MD5_PPM_444_ISLOW_SKIP1_6} cjpeg-${libtype}-444-islow) - - # Context rows: No Intra-iMCU row: No ENT: prog huff - add_test(cjpeg-${libtype}-444-islow-prog - ${CMAKE_CROSSCOMPILING_EMULATOR} cjpeg${suffix} -dct int -prog -sample 1x1 - -outfile testout_444_islow_prog.jpg ${TESTIMAGES}/testorig.ppm) - add_bittest(djpeg 444-islow-prog-crop98x98_13_13 - "-dct;int;-crop;98x98+13+13;-ppm" - testout_444_islow_prog_crop98x98,13,13.ppm testout_444_islow_prog.jpg - ${MD5_PPM_444_ISLOW_PROG_CROP98x98_13_13} cjpeg-${libtype}-444-islow-prog) - - # Context rows: No Intra-iMCU row: No ENT: arith - if(WITH_ARITH_ENC) - add_test(cjpeg-${libtype}-444-islow-ari - ${CMAKE_CROSSCOMPILING_EMULATOR} cjpeg${suffix} -dct int -arithmetic - -sample 1x1 -outfile testout_444_islow_ari.jpg - ${TESTIMAGES}/testorig.ppm) - if(WITH_ARITH_DEC) - add_bittest(djpeg 444-islow-ari-crop37x37_0_0 - "-dct;int;-crop;37x37+0+0;-ppm" - testout_444_islow_ari_crop37x37,0,0.ppm testout_444_islow_ari.jpg - ${MD5_PPM_444_ISLOW_ARI_CROP37x37_0_0} cjpeg-${libtype}-444-islow-ari) + unset(EXAMPLE_12BIT_ARG) + if(sample_bits EQUAL 12) + set(EXAMPLE_12BIT_ARG "-precision;12") endif() - endif() - add_bittest(jpegtran crop "-crop;120x90+20+50;-transpose;-perfect" - testout_crop.jpg ${TESTIMAGES}/${TESTORIG} - ${MD5_JPEG_CROP}) + add_test(example-${sample_bits}bit-${libtype}-compress + ${CMAKE_CROSSCOMPILING_EMULATOR} example${suffix} compress -q 95 + ${EXAMPLE_12BIT_ARG} ${testout}-example.jpg) + add_test(example-${sample_bits}bit-${libtype}-compress-cmp + ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} ${MD5_JPEG_EXAMPLE_COMPRESS} + ${testout}-example.jpg) + set_tests_properties(example-${sample_bits}bit-${libtype}-compress-cmp + PROPERTIES DEPENDS example-${sample_bits}bit-${libtype}-compress) + + add_test(example-${sample_bits}bit-${libtype}-decompress + ${CMAKE_CROSSCOMPILING_EMULATOR} example${suffix} decompress + ${EXAMPLE_12BIT_ARG} ${testout}-example.jpg ${testout}-example.ppm) + set_tests_properties(example-${sample_bits}bit-${libtype}-decompress + PROPERTIES DEPENDS example-${sample_bits}bit-${libtype}-compress) + add_test(example-${sample_bits}bit-${libtype}-decompress-cmp + ${CMAKE_CROSSCOMPILING_EMULATOR} ${MD5CMP} ${MD5_PPM_EXAMPLE_DECOMPRESS} + ${testout}-example.ppm) + set_tests_properties(example-${sample_bits}bit-${libtype}-decompress-cmp + PROPERTIES DEPENDS example-${sample_bits}bit-${libtype}-decompress) + + endforeach() endforeach() @@ -1368,56 +1678,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 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 tjexampletest.java -progressive -yuv - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest.java - -progressive -yuv - 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) - 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 tjexampletest - COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest) - 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() @@ -1447,7 +1722,7 @@ if(WITH_TURBOJPEG) INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) if(NOT ENABLE_SHARED) - if(MSVC_IDE OR XCODE) + if(GENERATOR_IS_MULTI_CONFIG) set(DIR "${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_INSTALL_CONFIG_NAME}") else() set(DIR ${CMAKE_CURRENT_BINARY_DIR}) @@ -1465,7 +1740,7 @@ if(ENABLE_STATIC) INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) if(NOT ENABLE_SHARED) - if(MSVC_IDE OR XCODE) + if(GENERATOR_IS_MULTI_CONFIG) set(DIR "${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_INSTALL_CONFIG_NAME}") else() set(DIR ${CMAKE_CURRENT_BINARY_DIR}) @@ -1482,7 +1757,7 @@ endif() install(TARGETS rdjpgcom wrjpgcom RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/README.ijg - ${CMAKE_CURRENT_SOURCE_DIR}/README.md ${CMAKE_CURRENT_SOURCE_DIR}/example.txt + ${CMAKE_CURRENT_SOURCE_DIR}/README.md ${CMAKE_CURRENT_SOURCE_DIR}/example.c ${CMAKE_CURRENT_SOURCE_DIR}/tjexample.c ${CMAKE_CURRENT_SOURCE_DIR}/libjpeg.txt ${CMAKE_CURRENT_SOURCE_DIR}/structure.txt diff --git a/ChangeLog.md b/ChangeLog.md index b0d166e..a466785 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,7 +1,303 @@ +3.0.1 +===== + +### Significant changes relative to 3.0.0: + +1. The x86-64 SIMD functions now use a standard stack frame, prologue, and +epilogue so that debuggers and profilers can reliably capture backtraces from +within the functions. + +2. Fixed two minor issues in the interblock smoothing algorithm that caused +mathematical (but not necessarily perceptible) edge block errors when +decompressing progressive JPEG images exactly two MCU blocks in width or that +use vertical chrominance subsampling. + +3. Fixed a regression introduced by 3.0 beta2[6] that, in rare cases, caused +the C Huffman encoder (which is not used by default on x86 and Arm CPUs) to +generate incorrect results if the Neon SIMD extensions were explicitly disabled +at build time (by setting the `WITH_SIMD` CMake variable to `0`) in an AArch64 +build of libjpeg-turbo. + + +3.0.0 +===== + +### Significant changes relative to 3.0 beta2: + +1. The TurboJPEG API now supports 4:4:1 (transposed 4:1:1) chrominance +subsampling, which allows losslessly transposed or rotated 4:1:1 JPEG images to +be losslessly cropped, partially decompressed, or decompressed to planar YUV +images. + +2. Fixed various segfaults and buffer overruns (CVE-2023-2804) that occurred +when attempting to decompress various specially-crafted malformed +12-bit-per-component and 16-bit-per-component lossless JPEG images using color +quantization or merged chroma upsampling/color conversion. The underlying +cause of these issues was that the color quantization and merged chroma +upsampling/color conversion algorithms were not designed with lossless +decompression in mind. Since libjpeg-turbo explicitly does not support color +conversion when compressing or decompressing lossless JPEG images, merged +chroma upsampling/color conversion never should have been enabled for such +images. Color quantization is a legacy feature that serves little or no +purpose with lossless JPEG images, so it is also now disabled when +decompressing such images. (As a result, djpeg can no longer decompress a +lossless JPEG image into a GIF image.) + +3. Fixed an oversight in 1.4 beta1[8] that caused various segfaults and buffer +overruns when attempting to decompress various specially-crafted malformed +12-bit-per-component JPEG images using djpeg with both color quantization and +RGB565 color conversion enabled. + +4. Fixed an issue whereby `jpeg_crop_scanline()` sometimes miscalculated the +downsampled width for components with 4x2 or 2x4 subsampling factors if +decompression scaling was enabled. This caused the components to be upsampled +incompletely, which caused the color converter to read from uninitialized +memory. With 12-bit data precision, this caused a buffer overrun or underrun +and subsequent segfault if the sample value read from uninitialized memory was +outside of the valid sample range. + +5. Fixed a long-standing issue whereby the `tj3Transform()` function, when used +with the `TJXOP_TRANSPOSE`, `TJXOP_TRANSVERSE`, `TJXOP_ROT90`, or +`TJXOP_ROT270` transform operation and without automatic JPEG destination +buffer (re)allocation or lossless cropping, computed the worst-case transformed +JPEG image size based on the source image dimensions rather than the +transformed image dimensions. If a calling program allocated the JPEG +destination buffer based on the transformed image dimensions, as the API +documentation instructs, and attempted to transform a specially-crafted 4:2:2, +4:4:0, 4:1:1, or 4:4:1 JPEG source image containing a large amount of metadata, +the issue caused `tj3Transform()` to overflow the JPEG destination buffer +rather than fail gracefully. The issue could be worked around by setting +`TJXOPT_COPYNONE`. Note that, irrespective of this issue, `tj3Transform()` +cannot reliably transform JPEG source images that contain a large amount of +metadata unless automatic JPEG destination buffer (re)allocation is used or +`TJXOPT_COPYNONE` is set. + +6. Fixed a regression introduced by 3.0 beta2[6] that prevented the djpeg +`-map` option from working when decompressing 12-bit-per-component lossy JPEG +images. + +7. Fixed an issue that caused the C Huffman encoder (which is not used by +default on x86 and Arm CPUs) to read from uninitialized memory when attempting +to transform a specially-crafted malformed arithmetic-coded JPEG source image +into a baseline Huffman-coded JPEG destination image. + + +2.1.91 (3.0 beta2) +================== + +### Significant changes relative to 2.1.5.1: + +1. Significantly sped up the computation of optimal Huffman tables. This +speeds up the compression of tiny images by as much as 2x and provides a +noticeable speedup for images as large as 256x256 when using optimal Huffman +tables. + +2. All deprecated fields, constructors, and methods in the TurboJPEG Java API +have been removed. + +3. Arithmetic entropy coding is now supported with 12-bit-per-component JPEG +images. + +4. Overhauled the TurboJPEG API to address long-standing limitations and to +make the API more extensible and intuitive: + + - All C function names are now prefixed with `tj3`, and all version +suffixes have been removed from the function names. Future API overhauls will +increment the prefix to `tj4`, etc., thus retaining backward API/ABI +compatibility without versioning each individual function. + - Stateless boolean flags have been replaced with stateful integer API +parameters, the values of which persist between function calls. New +functions/methods (`tj3Set()`/`TJCompressor.set()`/`TJDecompressor.set()` and +`tj3Get()`/`TJCompressor.get()`/`TJDecompressor.get()`) can be used to set and +query the value of a particular API parameter. + - The JPEG quality and subsampling are now implemented using API +parameters rather than stateless function arguments (C) or dedicated set/get +methods (Java.) + - `tj3DecompressHeader()` now stores all relevant information about the +JPEG image, including the width, height, subsampling type, entropy coding +algorithm, etc., in API parameters rather than returning that information +through pointer arguments. + - `TJFLAG_LIMITSCANS`/`TJ.FLAG_LIMITSCANS` has been reimplemented as an +API parameter (`TJPARAM_SCANLIMIT`/`TJ.PARAM_SCANLIMIT`) that allows the number +of scans to be specified. + - Optimized baseline entropy coding (the computation of optimal Huffman +tables, as opposed to using the default Huffman tables) can now be specified, +using a new API parameter (`TJPARAM_OPTIMIZE`/`TJ.PARAM_OPTIMIZE`), a new +transform option (`TJXOPT_OPTIMIZE`/`TJTransform.OPT_OPTIMIZE`), and a new +TJBench option (`-optimize`.) + - Arithmetic entropy coding can now be specified or queried, using a new +API parameter (`TJPARAM_ARITHMETIC`/`TJ.PARAM_ARITHMETIC`), a new transform +option (`TJXOPT_ARITHMETIC`/`TJTransform.OPT_ARITHMETIC`), and a new TJBench +option (`-arithmetic`.) + - The restart marker interval can now be specified, using new API +parameters (`TJPARAM_RESTARTROWS`/`TJ.PARAM_RESTARTROWS` and +`TJPARAM_RESTARTBLOCKS`/`TJ.PARAM_RESTARTBLOCKS`) and a new TJBench option +(`-restart`.) + - Pixel density can now be specified or queried, using new API parameters +(`TJPARAM_XDENSITY`/`TJ.PARAM_XDENSITY`, +`TJPARAM_YDENSITY`/`TJ.PARAM_YDENSITY`, and +`TJPARAM_DENSITYUNITS`/`TJ.PARAM_DENSITYUNITS`.) + - The accurate DCT/IDCT algorithms are now the default for both +compression and decompression, since the "fast" algorithms are considered to be +a legacy feature. (The "fast" algorithms do not pass the ISO compliance tests, +and those algorithms are not any faster than the accurate algorithms on modern +x86 CPUs.) + - All C initialization functions have been combined into a single function +(`tj3Init()`) that accepts an integer argument specifying the subsystems to +initialize. + - All C functions now use the `const` keyword for pointer arguments that +point to unmodified buffers (and for both dimensions of pointer arguments that +point to sets of unmodified buffers.) + - All C functions now use `size_t` rather than `unsigned long` to +represent buffer sizes, for compatibility with `malloc()` and to avoid +disparities in the size of `unsigned long` between LP64 (Un*x) and LLP64 +(Windows) operating systems. + - All C buffer size functions now return 0 if an error occurs, rather than +trying to awkwardly return -1 in an unsigned data type (which could easily be +misinterpreted as a very large value.) + - Decompression scaling is now enabled explicitly, using a new +function/method (`tj3SetScalingFactor()`/`TJDecompressor.setScalingFactor()`), +rather than implicitly using awkward "desired width"/"desired height" +arguments. + - Partial image decompression has been implemented, using a new +function/method (`tj3SetCroppingRegion()`/`TJDecompressor.setCroppingRegion()`) +and a new TJBench option (`-crop`.) + - The JPEG colorspace can now be specified explicitly when compressing, +using a new API parameter (`TJPARAM_COLORSPACE`/`TJ.PARAM_COLORSPACE`.) This +allows JPEG images with the RGB and CMYK colorspaces to be created. + - TJBench no longer generates error/difference images, since identical +functionality is already available in ImageMagick. + - JPEG images with unknown subsampling configurations can now be +fully decompressed into packed-pixel images or losslessly transformed (with the +exception of lossless cropping.) They cannot currently be partially +decompressed or decompressed into planar YUV images. + - `tj3Destroy()` now silently accepts a NULL handle. + - `tj3Alloc()` and `tj3Free()` now return/accept void pointers, as +`malloc()` and `free()` do. + - The C image I/O functions now accept a TurboJPEG instance handle, which +is used to transmit/receive API parameter values and to receive error +information. + +5. Added support for 8-bit-per-component, 12-bit-per-component, and +16-bit-per-component lossless JPEG images. A new libjpeg API function +(`jpeg_enable_lossless()`), TurboJPEG API parameters +(`TJPARAM_LOSSLESS`/`TJ.PARAM_LOSSLESS`, +`TJPARAM_LOSSLESSPSV`/`TJ.PARAM_LOSSLESSPSV`, and +`TJPARAM_LOSSLESSPT`/`TJ.PARAM_LOSSLESSPT`), and a cjpeg/TJBench option +(`-lossless`) can be used to create a lossless JPEG image. (Decompression of +lossless JPEG images is handled automatically.) Refer to +[libjpeg.txt](libjpeg.txt), [usage.txt](usage.txt), and the TurboJPEG API +documentation for more details. + +6. Added support for 12-bit-per-component (lossy and lossless) and +16-bit-per-component (lossless) JPEG images to the libjpeg and TurboJPEG APIs: + + - The existing `data_precision` field in `jpeg_compress_struct` and +`jpeg_decompress_struct` has been repurposed to enable the creation of +12-bit-per-component and 16-bit-per-component JPEG images or to detect whether +a 12-bit-per-component or 16-bit-per-component JPEG image is being +decompressed. + - New 12-bit-per-component and 16-bit-per-component versions of +`jpeg_write_scanlines()` and `jpeg_read_scanlines()`, as well as new +12-bit-per-component versions of `jpeg_write_raw_data()`, +`jpeg_skip_scanlines()`, `jpeg_crop_scanline()`, and `jpeg_read_raw_data()`, +provide interfaces for compressing from/decompressing to 12-bit-per-component +and 16-bit-per-component packed-pixel and planar YUV image buffers. + - New 12-bit-per-component and 16-bit-per-component compression, +decompression, and image I/O functions/methods have been added to the TurboJPEG +API, and a new API parameter (`TJPARAM_PRECISION`/`TJ.PARAM_PRECISION`) can be +used to query the data precision of a JPEG image. (YUV functions are currently +limited to 8-bit data precision but can be expanded to accommodate 12-bit data +precision in the future, if such is deemed beneficial.) + - A new cjpeg and TJBench command-line argument (`-precision`) can be used +to create a 12-bit-per-component or 16-bit-per-component JPEG image. +(Decompression and transformation of 12-bit-per-component and +16-bit-per-component JPEG images is handled automatically.) + + Refer to [libjpeg.txt](libjpeg.txt), [usage.txt](usage.txt), and the +TurboJPEG API documentation for more details. + + +2.1.5.1 +======= + +### Significant changes relative to 2.1.5: + +1. The SIMD dispatchers in libjpeg-turbo 2.1.4 and prior stored the list of +supported SIMD instruction sets in a global variable, which caused an innocuous +race condition whereby the variable could have been initialized multiple times +if `jpeg_start_*compress()` was called simultaneously in multiple threads. +libjpeg-turbo 2.1.5 included an undocumented attempt to fix this race condition +by making the SIMD support variable thread-local. However, that caused another +issue whereby, if `jpeg_start_*compress()` was called in one thread and +`jpeg_read_*()` or `jpeg_write_*()` was called in a second thread, the SIMD +support variable was never initialized in the second thread. On x86 systems, +this led the second thread to incorrectly assume that AVX2 instructions were +always available, and when it attempted to use those instructions on older x86 +CPUs that do not support them, an illegal instruction error occurred. The SIMD +dispatchers now ensure that the SIMD support variable is initialized before +dispatching based on its value. + + +2.1.5 +===== + +### Significant changes relative to 2.1.4: + +1. Fixed issues in the build system whereby, when using the Ninja Multi-Config +CMake generator, a static build of libjpeg-turbo (a build in which +`ENABLE_SHARED` is `0`) could not be installed, a Windows installer could not +be built, and the Java regression tests failed. + +2. Fixed a regression introduced by 2.0 beta1[15] that caused a buffer overrun +in the progressive Huffman encoder when attempting to transform a +specially-crafted malformed 12-bit-per-component JPEG image into a progressive +12-bit-per-component JPEG image using a 12-bit-per-component build of +libjpeg-turbo (`-DWITH_12BIT=1`.) Given that the buffer overrun was fully +contained within the progressive Huffman encoder structure and did not cause a +segfault or other user-visible errant behavior, given that the lossless +transformer (unlike the decompressor) is not generally exposed to arbitrary +data exploits, and given that 12-bit-per-component builds of libjpeg-turbo are +uncommon, this issue did not likely pose a security risk. + +3. Fixed an issue whereby, when using a 12-bit-per-component build of +libjpeg-turbo (`-DWITH_12BIT=1`), passing samples with values greater than 4095 +or less than 0 to `jpeg_write_scanlines()` caused a buffer overrun or underrun +in the RGB-to-YCbCr color converter. + +4. Fixed a floating point exception that occurred when attempting to use the +jpegtran `-drop` and `-trim` options to losslessly transform a +specially-crafted malformed JPEG image. + +5. Fixed an issue in `tjBufSizeYUV2()` whereby it returned a bogus result, +rather than throwing an error, if the `align` parameter was not a power of 2. +Fixed a similar issue in `tjCompressFromYUV()` whereby it generated a corrupt +JPEG image in certain cases, rather than throwing an error, if the `align` +parameter was not a power of 2. + +6. Fixed an issue whereby `tjDecompressToYUV2()`, which is a wrapper for +`tjDecompressToYUVPlanes()`, used the desired YUV image dimensions rather than +the actual scaled image dimensions when computing the plane pointers and +strides to pass to `tjDecompressToYUVPlanes()`. This caused a buffer overrun +and subsequent segfault if the desired image dimensions exceeded the scaled +image dimensions. + +7. Fixed an issue whereby, when decompressing a 12-bit-per-component JPEG image +(`-DWITH_12BIT=1`) using an alpha-enabled output color space such as +`JCS_EXT_RGBA`, the alpha channel was set to 255 rather than 4095. + +8. Fixed an issue whereby the Java version of TJBench did not accept a range of +quality values. + +9. Fixed an issue whereby, when `-progressive` was passed to TJBench, the JPEG +input image was not transformed into a progressive JPEG image prior to +decompression. + + 2.1.4 ===== -### Significant changes relative to 2.1.3 +### Significant changes relative to 2.1.3: 1. Fixed a regression introduced in 2.1.3 that caused build failures with Visual Studio 2010. @@ -36,7 +332,7 @@ virtual array access") under certain circumstances. 2.1.3 ===== -### Significant changes relative to 2.1.2 +### Significant changes relative to 2.1.2: 1. Fixed a regression introduced by 2.0 beta1[7] whereby cjpeg compressed PGM input files into full-color JPEG images unless the `-grayscale` option was @@ -60,7 +356,7 @@ be reproduced using the libjpeg API, not using djpeg. 2.1.2 ===== -### Significant changes relative to 2.1.1 +### Significant changes relative to 2.1.1: 1. Fixed a regression introduced by 2.1 beta1[13] that caused the remaining GAS implementations of AArch64 (Arm 64-bit) Neon SIMD functions (which are used @@ -92,7 +388,7 @@ image contains incomplete or corrupt image data. 2.1.1 ===== -### Significant changes relative to 2.1.0 +### Significant changes relative to 2.1.0: 1. Fixed a regression introduced in 2.1.0 that caused build failures with non-GCC-compatible compilers for Un*x/Arm platforms. @@ -121,11 +417,11 @@ transform a specially-crafted malformed JPEG image. 2.1.0 ===== -### Significant changes relative to 2.1 beta1 +### Significant changes relative to 2.1 beta1: -1. Fixed a regression introduced by 2.1 beta1[6(b)] whereby attempting to -decompress certain progressive JPEG images with one or more component planes of -width 8 or less caused a buffer overrun. +1. Fixed a regression (CVE-2021-29390) introduced by 2.1 beta1[6(b)] whereby +attempting to decompress certain progressive JPEG images with one or more +component planes of width 8 or less caused a buffer overrun. 2. Fixed a regression introduced by 2.1 beta1[6(b)] whereby attempting to decompress a specially-crafted malformed progressive JPEG image caused the @@ -156,10 +452,10 @@ progressive JPEG format described in the report ["Two Issues with the JPEG Standard"](https://libjpeg-turbo.org/pmwiki/uploads/About/TwoIssueswiththeJPEGStandard.pdf). 7. The PPM reader now throws an error, rather than segfaulting (due to a buffer -overrun) or generating incorrect pixels, if an application attempts to use the -`tjLoadImage()` function to load a 16-bit binary PPM file (a binary PPM file -with a maximum value greater than 255) into a grayscale image buffer or to load -a 16-bit binary PGM file into an RGB image buffer. +overrun, CVE-2021-46822) or generating incorrect pixels, if an application +attempts to use the `tjLoadImage()` function to load a 16-bit binary PPM file +(a binary PPM file with a maximum value greater than 255) into a grayscale +image buffer or to load a 16-bit binary PGM file into an RGB image buffer. 8. Fixed an issue in the PPM reader that caused incorrect pixels to be generated when using the `tjLoadImage()` function to load a 16-bit binary PPM @@ -325,11 +621,11 @@ methods in the TurboJPEG Java API. 2. Fixed or worked around multiple issues with `jpeg_skip_scanlines()`: - - Fixed segfaults or "Corrupt JPEG data: premature end of data segment" -errors in `jpeg_skip_scanlines()` that occurred when decompressing 4:2:2 or -4:2:0 JPEG images using merged (non-fancy) upsampling/color conversion (that -is, when setting `cinfo.do_fancy_upsampling` to `FALSE`.) 2.0.0[6] was a -similar fix, but it did not cover all cases. + - Fixed segfaults (CVE-2020-35538) or "Corrupt JPEG data: premature end of +data segment" errors in `jpeg_skip_scanlines()` that occurred when +decompressing 4:2:2 or 4:2:0 JPEG images using merged (non-fancy) +upsampling/color conversion (that is, when setting `cinfo.do_fancy_upsampling` +to `FALSE`.) 2.0.0[6] was a similar fix, but it did not cover all cases. - `jpeg_skip_scanlines()` now throws an error if two-pass color quantization is enabled. Two-pass color quantization never worked properly with `jpeg_skip_scanlines()`, and the issues could not readily be fixed. @@ -1364,7 +1660,7 @@ features (such as the colorspace extensions), but in general, it performs no faster than libjpeg v6b. 14. Added ARM 64-bit SIMD acceleration for the YCC-to-RGB color conversion -and IDCT algorithms (both are used during JPEG decompression.) For unknown +and IDCT algorithms (both are used during JPEG decompression.) For reasons (probably related to clang), this code cannot currently be compiled for iOS. diff --git a/LICENSE.md b/LICENSE.md index d753e1d..bf8a7fd 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -91,7 +91,7 @@ best of our understanding. The Modified (3-clause) BSD License =================================== -Copyright (C)2009-2022 D. R. Commander. All Rights Reserved.
+Copyright (C)2009-2023 D. R. Commander. All Rights Reserved.
Copyright (C)2015 Viktor Szathmáry. All Rights Reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.ijg b/README.ijg index 9453c19..8f37682 100644 --- a/README.ijg +++ b/README.ijg @@ -43,7 +43,7 @@ User documentation: change.log Version-to-version change highlights. Programmer and internal documentation: libjpeg.txt How to use the JPEG library in your own programs. - example.txt Sample code for calling the JPEG library. + example.c Sample code for calling the JPEG library. structure.txt Overview of the JPEG library's internal structure. coderules.txt Coding style rules --- please read if you contribute code. @@ -68,17 +68,17 @@ other abrupt features may not compress well with JPEG, and a higher JPEG quality may have to be used to avoid visible compression artifacts with such images. -JPEG is lossy, meaning that the output pixels are not necessarily identical to -the input pixels. However, on photographic content and other "smooth" images, -very good compression ratios can be obtained with no visible compression -artifacts, and extremely high compression ratios are possible if you are -willing to sacrifice image quality (by reducing the "quality" setting in the -compressor.) - -This software implements JPEG baseline, extended-sequential, and progressive -compression processes. Provision is made for supporting all variants of these -processes, although some uncommon parameter settings aren't implemented yet. -We have made no provision for supporting the hierarchical or lossless +JPEG is normally lossy, meaning that the output pixels are not necessarily +identical to the input pixels. However, on photographic content and other +"smooth" images, very good compression ratios can be obtained with no visible +compression artifacts, and extremely high compression ratios are possible if +you are willing to sacrifice image quality (by reducing the "quality" setting +in the compressor.) + +This software implements JPEG baseline, extended-sequential, progressive, and +lossless compression processes. Provision is made for supporting all variants +of these processes, although some uncommon parameter settings aren't +implemented yet. We have made no provision for supporting the hierarchical processes defined in the standard. We provide a set of library routines for reading and writing JPEG image files, @@ -241,7 +241,7 @@ This software implements ITU T.81 | ISO/IEC 10918 with some extensions from ITU T.871 | ISO/IEC 10918-5 (JPEG File Interchange Format-- see REFERENCES). Informally, the term "JPEG image" or "JPEG file" most often refers to JFIF or a subset thereof, but there are other formats containing the name "JPEG" that -are incompatible with the DCT-based JPEG standard or with JFIF (for instance, +are incompatible with the original JPEG standard or with JFIF (for instance, JPEG 2000 and JPEG XR). This software therefore does not support these formats. Indeed, one of the original reasons for developing this free software was to help force convergence on a common, interoperable format standard for diff --git a/README.md b/README.md index 01e391e..923e61d 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,26 @@ derivative of libjpeg v6b developed by Miyasaka Masaru. The TigerVNC and VirtualGL projects made numerous enhancements to the codec in 2009, and in early 2010, libjpeg-turbo spun off into an independent project, with the goal of making high-speed JPEG compression/decompression technology available to a -broader range of users and developers. +broader range of users and developers. libjpeg-turbo is an ISO/IEC and ITU-T +reference implementation of the JPEG standard. + +More information about libjpeg-turbo can be found at +. + + +Funding +======= + +libjpeg-turbo is an independent open source project, but we rely on patronage +and funded development in order to maintain that independence. The easiest way +to ensure that libjpeg-turbo remains community-focused and free of any one +organization's agenda is to +[sponsor our project through GitHub](https://github.com/sponsors/libjpeg-turbo). +All sponsorship money goes directly toward funding the labor necessary to +maintain libjpeg-turbo, support the user community, and implement bug fixes and +strategically important features. + +[![Sponsor libjpeg-turbo](https://img.shields.io/github/sponsors/libjpeg-turbo?label=Sponsor&logo=GitHub)](https://github.com/sponsors/libjpeg-turbo) License @@ -245,16 +264,6 @@ programs that need them, without breaking ABI compatibility for programs that don't, and it allows those functions to be provided in the "official" libjpeg-turbo binaries. -Those who are concerned about maintaining strict conformance with the libjpeg -v6b or v7 API can pass an argument of `-DWITH_MEM_SRCDST=0` to `cmake` prior to -building libjpeg-turbo. This will restore the pre-1.3 behavior, in which -`jpeg_mem_src()` and `jpeg_mem_dest()` are only included when emulating the -libjpeg v8 API/ABI. - -On Un*x systems, including the in-memory source/destination managers changes -the dynamic library version from 62.2.0 to 62.3.0 if using libjpeg v6b API/ABI -emulation and from 7.2.0 to 7.3.0 if using libjpeg v7 API/ABI emulation. - Note that, on most Un*x systems, the dynamic linker will not look for a function in a library until that function is actually used. Thus, if a program is built against libjpeg-turbo 1.3+ and uses `jpeg_mem_src()` or @@ -274,30 +283,35 @@ Mathematical Compatibility ========================== For the most part, libjpeg-turbo should produce identical output to libjpeg -v6b. The one exception to this is when using the floating point DCT/IDCT, in -which case the outputs of libjpeg v6b and libjpeg-turbo can differ for the -following reasons: - -- The SSE/SSE2 floating point DCT implementation in libjpeg-turbo is ever so - slightly more accurate than the implementation in libjpeg v6b, but not by - any amount perceptible to human vision (generally in the range of 0.01 to - 0.08 dB gain in PNSR.) - -- When not using the SIMD extensions, libjpeg-turbo uses the more accurate - (and slightly faster) floating point IDCT algorithm introduced in libjpeg - v8a as opposed to the algorithm used in libjpeg v6b. It should be noted, - however, that this algorithm basically brings the accuracy of the floating - point IDCT in line with the accuracy of the accurate integer IDCT. The - floating point DCT/IDCT algorithms are mainly a legacy feature, and they do - not produce significantly more accuracy than the accurate integer algorithms - (to put numbers on this, the typical difference in PNSR between the two - algorithms is less than 0.10 dB, whereas changing the quality level by 1 in - the upper range of the quality scale is typically more like a 1.0 dB - difference.) - -- If the floating point algorithms in libjpeg-turbo are not implemented using - SIMD instructions on a particular platform, then the accuracy of the - floating point DCT/IDCT can depend on the compiler settings. +v6b. There are two exceptions: + +1. When decompressing a JPEG image that uses 4:4:0 chrominance subsampling, the +outputs of libjpeg v6b and libjpeg-turbo can differ because libjpeg-turbo +implements a "fancy" (smooth) 4:4:0 upsampling algorithm and libjpeg did not. + +2. When using the floating point DCT/IDCT, the outputs of libjpeg v6b and +libjpeg-turbo can differ for the following reasons: + + - The SSE/SSE2 floating point DCT implementation in libjpeg-turbo is ever + so slightly more accurate than the implementation in libjpeg v6b, but not + by any amount perceptible to human vision (generally in the range of 0.01 + to 0.08 dB gain in PNSR.) + + - When not using the SIMD extensions, libjpeg-turbo uses the more accurate + (and slightly faster) floating point IDCT algorithm introduced in libjpeg + v8a as opposed to the algorithm used in libjpeg v6b. It should be noted, + however, that this algorithm basically brings the accuracy of the + floating point IDCT in line with the accuracy of the accurate integer + IDCT. The floating point DCT/IDCT algorithms are mainly a legacy + feature, and they do not produce significantly more accuracy than the + accurate integer algorithms. (To put numbers on this, the typical + difference in PNSR between the two algorithms is less than 0.10 dB, + whereas changing the quality level by 1 in the upper range of the quality + scale is typically more like a 1.0 dB difference.) + + - If the floating point algorithms in libjpeg-turbo are not implemented + using SIMD instructions on a particular platform, then the accuracy of + the floating point DCT/IDCT can depend on the compiler settings. While libjpeg-turbo does emulate the libjpeg v8 API/ABI, under the hood it is still using the same algorithms as libjpeg v6b, so there are several specific diff --git a/cdjpeg.h b/cdjpeg.h index 082687c..471b9a3 100644 --- a/cdjpeg.h +++ b/cdjpeg.h @@ -5,7 +5,7 @@ * Copyright (C) 1994-1997, Thomas G. Lane. * Modified 2019 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2017, 2019, 2021, D. R. Commander. + * Copyright (C) 2017, 2019, 2021-2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -35,6 +35,10 @@ struct cjpeg_source_struct { FILE *input_file; JSAMPARRAY buffer; + J12SAMPARRAY buffer12; +#ifdef C_LOSSLESS_SUPPORTED + J16SAMPARRAY buffer16; +#endif JDIMENSION buffer_height; #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION JDIMENSION max_pixels; @@ -75,6 +79,10 @@ struct djpeg_dest_struct { * height is buffer_height. */ JSAMPARRAY buffer; + J12SAMPARRAY buffer12; +#ifdef D_LOSSLESS_SUPPORTED + J16SAMPARRAY buffer16; +#endif JDIMENSION buffer_height; }; @@ -108,9 +116,23 @@ EXTERN(cjpeg_source_ptr) jinit_read_bmp(j_compress_ptr cinfo, EXTERN(djpeg_dest_ptr) jinit_write_bmp(j_decompress_ptr cinfo, boolean is_os2, boolean use_inversion_array); EXTERN(cjpeg_source_ptr) jinit_read_gif(j_compress_ptr cinfo); +EXTERN(cjpeg_source_ptr) j12init_read_gif(j_compress_ptr cinfo); +#ifdef C_LOSSLESS_SUPPORTED +EXTERN(cjpeg_source_ptr) j16init_read_gif(j_compress_ptr cinfo); +#endif EXTERN(djpeg_dest_ptr) jinit_write_gif(j_decompress_ptr cinfo, boolean is_lzw); +EXTERN(djpeg_dest_ptr) j12init_write_gif(j_decompress_ptr cinfo, + boolean is_lzw); EXTERN(cjpeg_source_ptr) jinit_read_ppm(j_compress_ptr cinfo); +EXTERN(cjpeg_source_ptr) j12init_read_ppm(j_compress_ptr cinfo); +#ifdef C_LOSSLESS_SUPPORTED +EXTERN(cjpeg_source_ptr) j16init_read_ppm(j_compress_ptr cinfo); +#endif EXTERN(djpeg_dest_ptr) jinit_write_ppm(j_decompress_ptr cinfo); +EXTERN(djpeg_dest_ptr) j12init_write_ppm(j_decompress_ptr cinfo); +#ifdef D_LOSSLESS_SUPPORTED +EXTERN(djpeg_dest_ptr) j16init_write_ppm(j_decompress_ptr cinfo); +#endif EXTERN(cjpeg_source_ptr) jinit_read_targa(j_compress_ptr cinfo); EXTERN(djpeg_dest_ptr) jinit_write_targa(j_decompress_ptr cinfo); @@ -127,6 +149,7 @@ EXTERN(boolean) set_sample_factors(j_compress_ptr cinfo, char *arg); /* djpeg support routines (in rdcolmap.c) */ EXTERN(void) read_color_map(j_decompress_ptr cinfo, FILE *infile); +EXTERN(void) read_color_map_12(j_decompress_ptr cinfo, FILE *infile); /* common support routines (in cdjpeg.c) */ @@ -156,6 +179,3 @@ EXTERN(FILE *) write_stdout(void); #ifndef EXIT_WARNING #define EXIT_WARNING 2 #endif - -#define IsExtRGB(cs) \ - (cs == JCS_RGB || (cs >= JCS_EXT_RGB && cs <= JCS_EXT_ARGB)) diff --git a/cjpeg.1 b/cjpeg.1 index 4bc4c8f..0815ca0 100644 --- a/cjpeg.1 +++ b/cjpeg.1 @@ -1,4 +1,4 @@ -.TH CJPEG 1 "30 November 2021" +.TH CJPEG 1 "29 June 2023" .SH NAME cjpeg \- compress an image file to a JPEG file .SH SYNOPSIS @@ -149,6 +149,56 @@ about the same --- often a little smaller. .PP Switches for advanced users: .TP +.BI \-precision " N" +Create JPEG file with N-bit data precision. N is 8, 12, or 16; default is 8. +If N is 16, then +.B -lossless +must also be specified. +.B Caution: +12-bit and 16-bit JPEG is not yet widely implemented, so many decoders will be +unable to view a 12-bit or 16-bit JPEG file at all. +.TP +.BI \-lossless " psv[,Pt]" +Create a lossless JPEG file using the specified predictor selection value +(1 through 7) and optional point transform (0 through +.nh +.I precision +.hy +- 1, where +.nh +.I precision +.hy +is the JPEG data precision in bits). A point transform value of 0 (the +default) is necessary in order to create a fully lossless JPEG file. (A +non-zero point transform value right-shifts the input samples by the specified +number of bits, which is effectively a form of lossy color quantization.) +.B Caution: +lossless JPEG is not yet widely implemented, so many decoders will be unable to +view a lossless JPEG file at all. Note that the following features will be +unavailable when compressing or decompressing a lossless JPEG file: +.IP +- Quality/quantization table selection +.IP +- Color space conversion (the JPEG image will use the same color space as the +input image) +.IP +- Color quantization +.IP +- DCT/IDCT algorithm selection +.IP +- Smoothing +.IP +- Downsampling/upsampling +.IP +- IDCT scaling +.IP +- Partial image decompression +.IP +- Transformations using +.B jpegtran +.IP +Any switches used to enable or configure those features will be ignored. +.TP .B \-arithmetic Use arithmetic coding. .B Caution: @@ -195,8 +245,8 @@ machines. Embed ICC color management profile contained in the specified file. .TP .BI \-restart " N" -Emit a JPEG restart marker every N MCU rows, or every N MCU blocks if "B" is -attached to the number. +Emit a JPEG restart marker every N MCU rows, or every N MCU blocks (samples in +lossless mode) if "B" is attached to the number. .B \-restart 0 (the default) means no restart markers. .TP diff --git a/cjpeg.c b/cjpeg.c index dae18a3..ed649d2 100644 --- a/cjpeg.c +++ b/cjpeg.c @@ -4,6 +4,8 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1998, Thomas G. Lane. * Modified 2003-2011 by Guido Vollbeding. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2010, 2013-2014, 2017, 2019-2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg @@ -103,11 +105,31 @@ select_file_type(j_compress_ptr cinfo, FILE *infile) #endif #ifdef GIF_SUPPORTED case 'G': - return jinit_read_gif(cinfo); + if (cinfo->data_precision == 16) { +#ifdef C_LOSSLESS_SUPPORTED + return j16init_read_gif(cinfo); +#else + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + break; +#endif + } else if (cinfo->data_precision == 12) + return j12init_read_gif(cinfo); + else + return jinit_read_gif(cinfo); #endif #ifdef PPM_SUPPORTED case 'P': - return jinit_read_ppm(cinfo); + if (cinfo->data_precision == 16) { +#ifdef C_LOSSLESS_SUPPORTED + return j16init_read_ppm(cinfo); +#else + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + break; +#endif + } else if (cinfo->data_precision == 12) + return j12init_read_ppm(cinfo); + else + return jinit_read_ppm(cinfo); #endif #ifdef TARGA_SUPPORTED case 0x00: @@ -204,6 +226,16 @@ usage(void) fprintf(stderr, " -targa Input file is Targa format (usually not needed)\n"); #endif fprintf(stderr, "Switches for advanced users:\n"); + fprintf(stderr, " -precision N Create JPEG file with N-bit data precision\n"); +#ifdef C_LOSSLESS_SUPPORTED + fprintf(stderr, " (N is 8, 12, or 16; default is 8; if N is 16, then -lossless\n"); + fprintf(stderr, " must also be specified)\n"); +#else + fprintf(stderr, " (N is 8 or 12; default is 8)\n"); +#endif +#ifdef C_LOSSLESS_SUPPORTED + fprintf(stderr, " -lossless psv[,Pt] Create lossless JPEG file\n"); +#endif #ifdef C_ARITH_CODING_SUPPORTED fprintf(stderr, " -arithmetic Use arithmetic coding\n"); #endif @@ -226,9 +258,7 @@ usage(void) #endif fprintf(stderr, " -maxmemory N Maximum memory to use (in kbytes)\n"); fprintf(stderr, " -outfile name Specify name for output file\n"); -#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) fprintf(stderr, " -memdst Compress to memory instead of file (useful for benchmarking)\n"); -#endif fprintf(stderr, " -report Report compression progress\n"); fprintf(stderr, " -strict Treat all warnings as fatal\n"); fprintf(stderr, " -verbose or -debug Emit debug output\n"); @@ -259,6 +289,9 @@ parse_switches(j_compress_ptr cinfo, int argc, char **argv, { int argn; char *arg; +#ifdef C_LOSSLESS_SUPPORTED + int psv, pt = 0; +#endif boolean force_baseline; boolean simple_progressive; char *qualityarg = NULL; /* saves -quality parm if any */ @@ -355,6 +388,27 @@ parse_switches(j_compress_ptr cinfo, int argc, char **argv, usage(); icc_filename = argv[argn]; + } else if (keymatch(arg, "lossless", 1)) { + /* Enable lossless mode. */ +#ifdef C_LOSSLESS_SUPPORTED + char ch = ',', *ptr; + + if (++argn >= argc) /* advance to next argument */ + usage(); + if (sscanf(argv[argn], "%d%c", &psv, &ch) < 1 || ch != ',') + usage(); + ptr = argv[argn]; + while (*ptr && *ptr++ != ','); /* advance to next segment of arg + string */ + if (*ptr) + sscanf(ptr, "%d", &pt); + jpeg_enable_lossless(cinfo, psv, pt); +#else + fprintf(stderr, "%s: sorry, lossless output was not compiled\n", + progname); + exit(EXIT_FAILURE); +#endif + } else if (keymatch(arg, "maxmemory", 3)) { /* Maximum memory in Kb (or Mb with 'm'). */ long lval; @@ -384,7 +438,23 @@ parse_switches(j_compress_ptr cinfo, int argc, char **argv, usage(); outfilename = argv[argn]; /* save it away for later use */ - } else if (keymatch(arg, "progressive", 1)) { + } else if (keymatch(arg, "precision", 3)) { + /* Set data precision. */ + int val; + + if (++argn >= argc) /* advance to next argument */ + usage(); + if (sscanf(argv[argn], "%d", &val) != 1) + usage(); +#ifdef C_LOSSLESS_SUPPORTED + if (val != 8 && val != 12 && val != 16) +#else + if (val != 8 && val != 12) +#endif + usage(); + cinfo->data_precision = val; + + } else if (keymatch(arg, "progressive", 3)) { /* Select simple progressive mode. */ #ifdef C_PROGRESSIVE_SUPPORTED simple_progressive = TRUE; @@ -397,13 +467,7 @@ parse_switches(j_compress_ptr cinfo, int argc, char **argv, } else if (keymatch(arg, "memdst", 2)) { /* Use in-memory destination manager */ -#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) memdst = TRUE; -#else - fprintf(stderr, "%s: sorry, in-memory destination manager was not compiled in\n", - progname); - exit(EXIT_FAILURE); -#endif } else if (keymatch(arg, "quality", 1)) { /* Quality ratings (quantization table scaling factors). */ @@ -710,11 +774,9 @@ main(int argc, char **argv) file_index = parse_switches(&cinfo, argc, argv, 0, TRUE); /* Specify data destination for compression */ -#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) if (memdst) jpeg_mem_dest(&cinfo, &outbuffer, &outsize); else -#endif jpeg_stdio_dest(&cinfo, output_file); #ifdef CJPEG_FUZZER @@ -729,9 +791,25 @@ main(int argc, char **argv) jpeg_write_icc_profile(&cinfo, icc_profile, (unsigned int)icc_len); /* Process data */ - while (cinfo.next_scanline < cinfo.image_height) { - num_scanlines = (*src_mgr->get_pixel_rows) (&cinfo, src_mgr); - (void)jpeg_write_scanlines(&cinfo, src_mgr->buffer, num_scanlines); + if (cinfo.data_precision == 16) { +#ifdef C_LOSSLESS_SUPPORTED + while (cinfo.next_scanline < cinfo.image_height) { + num_scanlines = (*src_mgr->get_pixel_rows) (&cinfo, src_mgr); + (void)jpeg16_write_scanlines(&cinfo, src_mgr->buffer16, num_scanlines); + } +#else + ERREXIT1(&cinfo, JERR_BAD_PRECISION, cinfo.data_precision); +#endif + } else if (cinfo.data_precision == 12) { + while (cinfo.next_scanline < cinfo.image_height) { + num_scanlines = (*src_mgr->get_pixel_rows) (&cinfo, src_mgr); + (void)jpeg12_write_scanlines(&cinfo, src_mgr->buffer12, num_scanlines); + } + } else { + while (cinfo.next_scanline < cinfo.image_height) { + num_scanlines = (*src_mgr->get_pixel_rows) (&cinfo, src_mgr); + (void)jpeg_write_scanlines(&cinfo, src_mgr->buffer, num_scanlines); + } } /* Finish compression and release memory */ diff --git a/cmakescripts/BuildPackages.cmake b/cmakescripts/BuildPackages.cmake index 7d6fa2c..2e0170f 100644 --- a/cmakescripts/BuildPackages.cmake +++ b/cmakescripts/BuildPackages.cmake @@ -90,7 +90,7 @@ if(WITH_JAVA) set(INST_DEFS ${INST_DEFS} -DJAVA) endif() -if(MSVC_IDE) +if(GENERATOR_IS_MULTI_CONFIG) set(INST_DEFS ${INST_DEFS} "-DBUILDDIR=${CMAKE_CFG_INTDIR}\\") else() set(INST_DEFS ${INST_DEFS} "-DBUILDDIR=") diff --git a/cmakescripts/testclean.cmake b/cmakescripts/testclean.cmake index fc3fc25..6b5a146 100644 --- a/cmakescripts/testclean.cmake +++ b/cmakescripts/testclean.cmake @@ -30,6 +30,14 @@ file(GLOB FILES *_411_*.ppm *_411_*.jpg *_411.yuv + *_441_*.bmp + *_441_*.png + *_441_*.ppm + *_441_*.jpg + *_441.yuv + *_LOSSL*S_*.bmp + *_LOSSL*S_*.ppm + *_LOSSL*S_*.jpg tjbenchtest*.log tjexampletest*.log) diff --git a/cmakescripts/tjbenchtest.cmake b/cmakescripts/tjbenchtest.cmake new file mode 100644 index 0000000..facb203 --- /dev/null +++ b/cmakescripts/tjbenchtest.cmake @@ -0,0 +1,77 @@ +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") +endif() +if(NOT PRECISION EQUAL 16) + run_test(tjbenchtest "-precision;${PRECISION};-arithmetic") + run_test(tjbenchtest "-precision;${PRECISION};-progressive;-arithmetic") +endif() +if(PRECISION EQUAL 8) + run_test(tjbenchtest "-precision;${PRECISION};-arithmetic;-yuv") +endif() +run_test(tjbenchtest "-precision;${PRECISION};-lossless") +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") + endif() + if(NOT PRECISION EQUAL 16) + run_test(tjbenchtest "-java;-precision;${PRECISION};-arithmetic") + run_test(tjbenchtest "-java;-precision;${PRECISION};-progressive;-arithmetic") + endif() + if(PRECISION EQUAL 8) + run_test(tjbenchtest "-java;-precision;${PRECISION};-arithmetic;-yuv") + endif() + run_test(tjbenchtest "-java;-precision;${PRECISION};-lossless") + if(PRECISION EQUAL 8) + run_test(tjexampletest "-java") + endif() +endif() diff --git a/cmyk.h b/cmyk.h index 48187a8..2389124 100644 --- a/cmyk.h +++ b/cmyk.h @@ -1,7 +1,7 @@ /* * cmyk.h * - * Copyright (C) 2017-2018, D. R. Commander. + * Copyright (C) 2017-2018, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -17,19 +17,19 @@ #include #define JPEG_INTERNALS #include -#include "jconfigint.h" +#include "jsamplecomp.h" /* Fully reversible */ INLINE LOCAL(void) -rgb_to_cmyk(JSAMPLE r, JSAMPLE g, JSAMPLE b, JSAMPLE *c, JSAMPLE *m, - JSAMPLE *y, JSAMPLE *k) +rgb_to_cmyk(_JSAMPLE r, _JSAMPLE g, _JSAMPLE b, + _JSAMPLE *c, _JSAMPLE *m, _JSAMPLE *y, _JSAMPLE *k) { - double ctmp = 1.0 - ((double)r / 255.0); - double mtmp = 1.0 - ((double)g / 255.0); - double ytmp = 1.0 - ((double)b / 255.0); + double ctmp = 1.0 - ((double)r / (double)_MAXJSAMPLE); + double mtmp = 1.0 - ((double)g / (double)_MAXJSAMPLE); + double ytmp = 1.0 - ((double)b / (double)_MAXJSAMPLE); double ktmp = MIN(MIN(ctmp, mtmp), ytmp); if (ktmp == 1.0) ctmp = mtmp = ytmp = 0.0; @@ -38,10 +38,10 @@ rgb_to_cmyk(JSAMPLE r, JSAMPLE g, JSAMPLE b, JSAMPLE *c, JSAMPLE *m, mtmp = (mtmp - ktmp) / (1.0 - ktmp); ytmp = (ytmp - ktmp) / (1.0 - ktmp); } - *c = (JSAMPLE)(255.0 - ctmp * 255.0 + 0.5); - *m = (JSAMPLE)(255.0 - mtmp * 255.0 + 0.5); - *y = (JSAMPLE)(255.0 - ytmp * 255.0 + 0.5); - *k = (JSAMPLE)(255.0 - ktmp * 255.0 + 0.5); + *c = (_JSAMPLE)((double)_MAXJSAMPLE - ctmp * (double)_MAXJSAMPLE + 0.5); + *m = (_JSAMPLE)((double)_MAXJSAMPLE - mtmp * (double)_MAXJSAMPLE + 0.5); + *y = (_JSAMPLE)((double)_MAXJSAMPLE - ytmp * (double)_MAXJSAMPLE + 0.5); + *k = (_JSAMPLE)((double)_MAXJSAMPLE - ktmp * (double)_MAXJSAMPLE + 0.5); } @@ -49,12 +49,12 @@ rgb_to_cmyk(JSAMPLE r, JSAMPLE g, JSAMPLE b, JSAMPLE *c, JSAMPLE *m, INLINE LOCAL(void) -cmyk_to_rgb(JSAMPLE c, JSAMPLE m, JSAMPLE y, JSAMPLE k, JSAMPLE *r, JSAMPLE *g, - JSAMPLE *b) +cmyk_to_rgb(_JSAMPLE c, _JSAMPLE m, _JSAMPLE y, _JSAMPLE k, + _JSAMPLE *r, _JSAMPLE *g, _JSAMPLE *b) { - *r = (JSAMPLE)((double)c * (double)k / 255.0 + 0.5); - *g = (JSAMPLE)((double)m * (double)k / 255.0 + 0.5); - *b = (JSAMPLE)((double)y * (double)k / 255.0 + 0.5); + *r = (_JSAMPLE)((double)c * (double)k / (double)_MAXJSAMPLE + 0.5); + *g = (_JSAMPLE)((double)m * (double)k / (double)_MAXJSAMPLE + 0.5); + *b = (_JSAMPLE)((double)y * (double)k / (double)_MAXJSAMPLE + 0.5); } diff --git a/djpeg.c b/djpeg.c index 7666e3f..4ea5c4b 100644 --- a/djpeg.c +++ b/djpeg.c @@ -5,7 +5,7 @@ * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 2013-2019 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2010-2011, 2013-2017, 2019-2020, 2022, D. R. Commander. + * Copyright (C) 2010-2011, 2013-2017, 2019-2020, 2022-2023, D. R. Commander. * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -164,9 +164,7 @@ usage(void) fprintf(stderr, " -maxmemory N Maximum memory to use (in kbytes)\n"); fprintf(stderr, " -maxscans N Maximum number of scans to allow in input file\n"); fprintf(stderr, " -outfile name Specify name for output file\n"); -#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) fprintf(stderr, " -memsrc Load input file into memory before decompressing\n"); -#endif fprintf(stderr, " -report Report decompression progress\n"); fprintf(stderr, " -skip Y0,Y1 Decompress all rows except those between Y0 and Y1 (inclusive)\n"); fprintf(stderr, " -crop WxH+X+Y Decompress only a rectangular subregion of the image\n"); @@ -316,7 +314,9 @@ parse_switches(j_decompress_ptr cinfo, int argc, char **argv, if (++argn >= argc) /* advance to next argument */ usage(); icc_filename = argv[argn]; +#ifdef SAVE_MARKERS_SUPPORTED jpeg_save_markers(cinfo, JPEG_APP0 + 2, 0xFFFF); +#endif } else if (keymatch(arg, "map", 3)) { /* Quantize to a color map taken from an input file. */ @@ -330,7 +330,10 @@ parse_switches(j_decompress_ptr cinfo, int argc, char **argv, fprintf(stderr, "%s: can't open %s\n", progname, argv[argn]); exit(EXIT_FAILURE); } - read_color_map(cinfo, mapfile); + if (cinfo->data_precision == 12) + read_color_map_12(cinfo, mapfile); + else + read_color_map(cinfo, mapfile); fclose(mapfile); cinfo->quantize_colors = TRUE; #else @@ -377,13 +380,7 @@ parse_switches(j_decompress_ptr cinfo, int argc, char **argv, } else if (keymatch(arg, "memsrc", 2)) { /* Use in-memory source manager */ -#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) memsrc = TRUE; -#else - fprintf(stderr, "%s: sorry, in-memory source manager was not compiled in\n", - progname); - exit(EXIT_FAILURE); -#endif } else if (keymatch(arg, "pnm", 1) || keymatch(arg, "ppm", 1)) { /* PPM/PGM output format. */ @@ -535,9 +532,7 @@ main(int argc, char **argv) FILE *input_file; FILE *output_file; unsigned char *inbuffer = NULL; -#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) unsigned long insize = 0; -#endif JDIMENSION num_scanlines; progname = argv[0]; @@ -627,7 +622,6 @@ main(int argc, char **argv) } /* Specify data source for decompression */ -#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) if (memsrc) { size_t nbytes; do { @@ -649,7 +643,6 @@ main(int argc, char **argv) fprintf(stderr, "Compressed size: %lu bytes\n", insize); jpeg_mem_src(&cinfo, inbuffer, insize); } else -#endif jpeg_stdio_src(&cinfo, input_file); /* Read file header, set default decompression parameters */ @@ -672,7 +665,12 @@ main(int argc, char **argv) #endif #ifdef GIF_SUPPORTED case FMT_GIF: - dest_mgr = jinit_write_gif(&cinfo, TRUE); + if (cinfo.data_precision == 16) + ERREXIT1(&cinfo, JERR_BAD_PRECISION, cinfo.data_precision); + else if (cinfo.data_precision == 12) + dest_mgr = j12init_write_gif(&cinfo, TRUE); + else + dest_mgr = jinit_write_gif(&cinfo, TRUE); break; case FMT_GIF0: dest_mgr = jinit_write_gif(&cinfo, FALSE); @@ -680,7 +678,16 @@ main(int argc, char **argv) #endif #ifdef PPM_SUPPORTED case FMT_PPM: - dest_mgr = jinit_write_ppm(&cinfo); + if (cinfo.data_precision == 16) +#ifdef D_LOSSLESS_SUPPORTED + dest_mgr = j16init_write_ppm(&cinfo); +#else + ERREXIT1(&cinfo, JERR_BAD_PRECISION, cinfo.data_precision); +#endif + else if (cinfo.data_precision == 12) + dest_mgr = j12init_write_ppm(&cinfo); + else + dest_mgr = jinit_write_ppm(&cinfo); break; #endif #ifdef TARGA_SUPPORTED @@ -719,22 +726,44 @@ main(int argc, char **argv) (*dest_mgr->start_output) (&cinfo, dest_mgr); cinfo.output_height = tmp; - /* Process data */ - while (cinfo.output_scanline < skip_start) { - num_scanlines = jpeg_read_scanlines(&cinfo, dest_mgr->buffer, - dest_mgr->buffer_height); - (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); - } - if ((tmp = jpeg_skip_scanlines(&cinfo, skip_end - skip_start + 1)) != - skip_end - skip_start + 1) { - fprintf(stderr, "%s: jpeg_skip_scanlines() returned %u rather than %u\n", - progname, tmp, skip_end - skip_start + 1); - exit(EXIT_FAILURE); - } - while (cinfo.output_scanline < cinfo.output_height) { - num_scanlines = jpeg_read_scanlines(&cinfo, dest_mgr->buffer, - dest_mgr->buffer_height); - (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); + if (cinfo.data_precision == 16) + ERREXIT(&cinfo, JERR_NOTIMPL); + else if (cinfo.data_precision == 12) { + /* Process data */ + while (cinfo.output_scanline < skip_start) { + num_scanlines = jpeg12_read_scanlines(&cinfo, dest_mgr->buffer12, + dest_mgr->buffer_height); + (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); + } + if ((tmp = jpeg12_skip_scanlines(&cinfo, skip_end - skip_start + 1)) != + skip_end - skip_start + 1) { + fprintf(stderr, "%s: jpeg12_skip_scanlines() returned %u rather than %u\n", + progname, tmp, skip_end - skip_start + 1); + exit(EXIT_FAILURE); + } + while (cinfo.output_scanline < cinfo.output_height) { + num_scanlines = jpeg12_read_scanlines(&cinfo, dest_mgr->buffer12, + dest_mgr->buffer_height); + (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); + } + } else { + /* Process data */ + while (cinfo.output_scanline < skip_start) { + num_scanlines = jpeg_read_scanlines(&cinfo, dest_mgr->buffer, + dest_mgr->buffer_height); + (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); + } + if ((tmp = jpeg_skip_scanlines(&cinfo, skip_end - skip_start + 1)) != + skip_end - skip_start + 1) { + fprintf(stderr, "%s: jpeg_skip_scanlines() returned %u rather than %u\n", + progname, tmp, skip_end - skip_start + 1); + exit(EXIT_FAILURE); + } + while (cinfo.output_scanline < cinfo.output_height) { + num_scanlines = jpeg_read_scanlines(&cinfo, dest_mgr->buffer, + dest_mgr->buffer_height); + (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); + } } /* Decompress a subregion */ @@ -751,7 +780,12 @@ main(int argc, char **argv) exit(EXIT_FAILURE); } - jpeg_crop_scanline(&cinfo, &crop_x, &crop_width); + if (cinfo.data_precision == 16) + ERREXIT(&cinfo, JERR_NOTIMPL); + else if (cinfo.data_precision == 12) + jpeg12_crop_scanline(&cinfo, &crop_x, &crop_width); + else + jpeg_crop_scanline(&cinfo, &crop_x, &crop_width); if (dest_mgr->calc_buffer_dimensions) (*dest_mgr->calc_buffer_dimensions) (&cinfo, dest_mgr); else @@ -765,24 +799,48 @@ main(int argc, char **argv) (*dest_mgr->start_output) (&cinfo, dest_mgr); cinfo.output_height = tmp; - /* Process data */ - if ((tmp = jpeg_skip_scanlines(&cinfo, crop_y)) != crop_y) { - fprintf(stderr, "%s: jpeg_skip_scanlines() returned %u rather than %u\n", - progname, tmp, crop_y); - exit(EXIT_FAILURE); - } - while (cinfo.output_scanline < crop_y + crop_height) { - num_scanlines = jpeg_read_scanlines(&cinfo, dest_mgr->buffer, - dest_mgr->buffer_height); - (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); - } - if ((tmp = - jpeg_skip_scanlines(&cinfo, - cinfo.output_height - crop_y - crop_height)) != - cinfo.output_height - crop_y - crop_height) { - fprintf(stderr, "%s: jpeg_skip_scanlines() returned %u rather than %u\n", - progname, tmp, cinfo.output_height - crop_y - crop_height); - exit(EXIT_FAILURE); + if (cinfo.data_precision == 16) + ERREXIT(&cinfo, JERR_NOTIMPL); + else if (cinfo.data_precision == 12) { + /* Process data */ + if ((tmp = jpeg12_skip_scanlines(&cinfo, crop_y)) != crop_y) { + fprintf(stderr, "%s: jpeg12_skip_scanlines() returned %u rather than %u\n", + progname, tmp, crop_y); + exit(EXIT_FAILURE); + } + while (cinfo.output_scanline < crop_y + crop_height) { + num_scanlines = jpeg12_read_scanlines(&cinfo, dest_mgr->buffer12, + dest_mgr->buffer_height); + (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); + } + if ((tmp = + jpeg12_skip_scanlines(&cinfo, cinfo.output_height - crop_y - + crop_height)) != + cinfo.output_height - crop_y - crop_height) { + fprintf(stderr, "%s: jpeg12_skip_scanlines() returned %u rather than %u\n", + progname, tmp, cinfo.output_height - crop_y - crop_height); + exit(EXIT_FAILURE); + } + } else { + /* Process data */ + if ((tmp = jpeg_skip_scanlines(&cinfo, crop_y)) != crop_y) { + fprintf(stderr, "%s: jpeg_skip_scanlines() returned %u rather than %u\n", + progname, tmp, crop_y); + exit(EXIT_FAILURE); + } + while (cinfo.output_scanline < crop_y + crop_height) { + num_scanlines = jpeg_read_scanlines(&cinfo, dest_mgr->buffer, + dest_mgr->buffer_height); + (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); + } + if ((tmp = + jpeg_skip_scanlines(&cinfo, + cinfo.output_height - crop_y - crop_height)) != + cinfo.output_height - crop_y - crop_height) { + fprintf(stderr, "%s: jpeg_skip_scanlines() returned %u rather than %u\n", + progname, tmp, cinfo.output_height - crop_y - crop_height); + exit(EXIT_FAILURE); + } } /* Normal full-image decompress */ @@ -790,11 +848,31 @@ main(int argc, char **argv) /* Write output file header */ (*dest_mgr->start_output) (&cinfo, dest_mgr); - /* Process data */ - while (cinfo.output_scanline < cinfo.output_height) { - num_scanlines = jpeg_read_scanlines(&cinfo, dest_mgr->buffer, - dest_mgr->buffer_height); - (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); + if (cinfo.data_precision == 16) { +#ifdef D_LOSSLESS_SUPPORTED + /* Process data */ + while (cinfo.output_scanline < cinfo.output_height) { + num_scanlines = jpeg16_read_scanlines(&cinfo, dest_mgr->buffer16, + dest_mgr->buffer_height); + (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); + } +#else + ERREXIT1(&cinfo, JERR_BAD_PRECISION, cinfo.data_precision); +#endif + } else if (cinfo.data_precision == 12) { + /* Process data */ + while (cinfo.output_scanline < cinfo.output_height) { + num_scanlines = jpeg12_read_scanlines(&cinfo, dest_mgr->buffer12, + dest_mgr->buffer_height); + (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); + } + } else { + /* Process data */ + while (cinfo.output_scanline < cinfo.output_height) { + num_scanlines = jpeg_read_scanlines(&cinfo, dest_mgr->buffer, + dest_mgr->buffer_height); + (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); + } } } diff --git a/doc/html/annotated.html b/doc/html/annotated.html index c735a5a..d7e2987 100644 --- a/doc/html/annotated.html +++ b/doc/html/annotated.html @@ -23,7 +23,7 @@
TurboJPEG -  2.1.4 +  3
diff --git a/doc/html/classes.html b/doc/html/classes.html index 42f38f6..78730e6 100644 --- a/doc/html/classes.html +++ b/doc/html/classes.html @@ -23,7 +23,7 @@
TurboJPEG -  2.1.4 +  3
diff --git a/doc/html/functions.html b/doc/html/functions.html index 5de4c6f..22c164b 100644 --- a/doc/html/functions.html +++ b/doc/html/functions.html @@ -23,7 +23,7 @@
TurboJPEG -  2.1.4 +  3
@@ -65,7 +65,7 @@ $(function() {
Here is a list of all documented struct and union fields with links to the struct/union documentation for each field:
  • customFilter -: tjtransform +: tjtransform
  • data : tjtransform diff --git a/doc/html/functions_vars.html b/doc/html/functions_vars.html index 2b010f4..0e3897e 100644 --- a/doc/html/functions_vars.html +++ b/doc/html/functions_vars.html @@ -23,7 +23,7 @@
    TurboJPEG -  2.1.4 +  3
    @@ -65,7 +65,7 @@ $(function() {
     
    • customFilter -: tjtransform +: tjtransform
    • data : tjtransform diff --git a/doc/html/group___turbo_j_p_e_g.html b/doc/html/group___turbo_j_p_e_g.html index 63d4791..04285c2 100644 --- a/doc/html/group___turbo_j_p_e_g.html +++ b/doc/html/group___turbo_j_p_e_g.html @@ -23,7 +23,7 @@
      TurboJPEG -  2.1.4 +  3
      @@ -92,6 +92,9 @@ Data Structures + + + @@ -101,30 +104,9 @@ Macros - - - - - - - - - - - - - - - - - - - - - - - - + + + @@ -132,29 +114,32 @@ Macros - + - + - + - + - + - + - - - + + + + + + @@ -170,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
       The uncompressed source/destination image is stored in bottom-up (Windows, OpenGL) order, not 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 in the underlying codec. More...
       
      #define TJFLAG_NOREALLOC
       Disable buffer (re)allocation. More...
       
      #define TJFLAG_FASTDCT
       Use the fastest DCT/IDCT algorithm available in the underlying codec. More...
       
      #define TJFLAG_ACCURATEDCT
       Use the most accurate DCT/IDCT algorithm available in the underlying codec. More...
       
      #define TJFLAG_STOPONWARNING
       Immediately discontinue the current compression/decompression/transform operation if the underlying codec throws a warning (non-fatal error). 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 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...
       
      #define TJXOPT_GRAY
       This option will discard the color data in the input image and produce a grayscale output image. 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 (this can be used in conjunction with a custom filter to capture the transformed DCT coefficients without transcoding them.) 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 output image generated by this particular transform. More...
       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 output 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 TJPAD(width)
       Pad the given width to the nearest 32-bit boundary. More...
       
      #define TJXOPT_ARITHMETIC
       This option will enable arithmetic entropy coding in the JPEG image generated by this particular transform. 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_441, +TJSAMP_UNKNOWN
      } @@ -214,6 +207,39 @@ Enumerations } + + + @@ -231,95 +257,125 @@ EnumerationsTJXOP_ROT270
      } - +

      Enumerations

      enum  TJINIT { TJINIT_COMPRESS, +TJINIT_DECOMPRESS, +TJINIT_TRANSFORM + }
       Initialization options. More...
       
      enum  TJSAMP {
        TJSAMP_444, TJSAMP_422, @@ -177,7 +168,9 @@ 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 an RGB, grayscale, or CMYK image into a JPEG image. More...
       
      DLLEXPORT int tjCompressFromYUV (tjhandle handle, const unsigned char *srcBuf, int width, int pad, int height, int subsamp, unsigned char **jpegBuf, unsigned long *jpegSize, int jpegQual, int flags)
       Compress a YUV planar 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 pad, int height, int subsamp)
       The size of the buffer (in bytes) required to hold a YUV planar 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 pad, int subsamp, int flags)
       Encode an RGB or grayscale image into a YUV planar 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 an 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 tjDecompressHeader3 (tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height, int *jpegSubsamp, int *jpegColorspace)
       Retrieve information about a JPEG image without decompressing it, or prime the decompressor with quantization and Huffman tables. More...
       
      DLLEXPORT tjscalingfactortjGetScalingFactors (int *numscalingfactors)
       Returns a list of fractional scaling factors that the JPEG decompressor in this implementation of TurboJPEG 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 to an RGB, grayscale, or CMYK image. More...
       
      DLLEXPORT int tjDecompressToYUV2 (tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf, int width, int pad, int height, int flags)
       Decompress a JPEG image to a YUV planar 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 pad, int subsamp, unsigned char *dstBuf, int width, int pitch, int height, int pixelFormat, int flags)
       Decode a YUV planar image into an 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 an 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 an image buffer for use with TurboJPEG. More...
       
      DLLEXPORT unsigned char * tjLoadImage (const char *filename, int *width, int align, int *height, int *pixelFormat, int flags)
       Load an uncompressed 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 an uncompressed image from memory to disk. More...
       
      DLLEXPORT void tjFree (unsigned char *buffer)
       Free an image 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...
       
      @@ -330,20 +386,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.

      @@ -352,8 +414,8 @@ Variables

      YUV Image Format Notes

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

      -

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

      -

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

      +

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

      +

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

      Macro Definition Documentation

      ◆ TJ_NUMCS

      @@ -387,206 +449,83 @@ YUV Image Format Notes
- -

◆ TJ_NUMPF

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

The number of pixel formats.

- -
-
- -

◆ TJ_NUMSAMP

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

The number of chrominance subsampling options.

- -
-
- -

◆ TJ_NUMXOP

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

The number of transform operations.

- -
-
- -

◆ TJFLAG_ACCURATEDCT

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

Use the most accurate DCT/IDCT algorithm available in the underlying codec.

-

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

- -
-
- -

◆ TJFLAG_BOTTOMUP

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

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

- -
-
- -

◆ TJFLAG_FASTDCT

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

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

-

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

- -
-
- -

◆ TJFLAG_FASTUPSAMPLE

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

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

-

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

- -
-
- -

◆ TJFLAG_LIMITSCANS

+ +

◆ TJ_NUMINIT

- +
#define TJFLAG_LIMITSCANS#define TJ_NUMINIT
-

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.

+

The number of initialization options.

- -

◆ TJFLAG_NOREALLOC

+ +

◆ TJ_NUMPARAM

- +
#define TJFLAG_NOREALLOC#define TJ_NUMPARAM
-

Disable buffer (re)allocation.

-

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

+

The number of parameters.

- -

◆ TJFLAG_PROGRESSIVE

+ +

◆ TJ_NUMPF

- +
#define TJFLAG_PROGRESSIVE#define TJ_NUMPF
-

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

-

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

+

The number of pixel formats.

- -

◆ TJFLAG_STOPONWARNING

+ +

◆ TJ_NUMSAMP

- +
#define TJFLAG_STOPONWARNING#define TJ_NUMSAMP
-

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

-

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

+

The number of chrominance subsampling options.

- -

◆ TJPAD

+ +

◆ TJ_NUMXOP

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

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

+

The number of transform operations.

@@ -621,6 +560,23 @@ YUV Image Format Notes + +

◆ TJXOPT_ARITHMETIC

+ +
+
+ + + + +
#define TJXOPT_ARITHMETIC
+
+ +

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.

+ +
+

◆ TJXOPT_COPYNONE

@@ -633,7 +589,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 output 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.

@@ -650,7 +606,7 @@ YUV Image Format Notes

This option will enable lossless cropping.

-

See tjTransform() for more information.

+

See tj3Transform() for more information.

@@ -666,7 +622,7 @@ YUV Image Format Notes
-

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

+

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

@@ -682,7 +638,25 @@ YUV Image Format Notes
-

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

+

This option will prevent 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.)

@@ -698,7 +672,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.

@@ -715,8 +689,8 @@ YUV Image Format Notes
-

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

-

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

+

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. Implies TJXOPT_OPTIMIZE unless TJXOPT_ARITHMETIC is also specified.

@@ -732,7 +706,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.

@@ -785,19 +759,19 @@ 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 any of the extended RGB pixel formats or grayscale, but they cannot be decompressed to YUV images.

+

When compressing the JPEG image, the R, G, and B components in the source image are reordered into image planes, but no colorspace conversion or subsampling is performed. RGB JPEG images can be 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 bandwidth or disk space. YCbCr is the most common JPEG colorspace, and YCbCr JPEG images can be compressed from and decompressed to any of the extended RGB pixel formats or grayscale, or they can be decompressed to YUV planar images.

+

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

TJCS_GRAY 

Grayscale colorspace.

-

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

+

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

TJCS_CMYK 

CMYK colorspace.

-

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

+

When compressing the JPEG image, the C, M, Y, and K components in the source image are reordered into image planes, but no colorspace conversion or subsampling is performed. CMYK JPEG images can only be 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 CMYK pixels.

+

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

@@ -817,7 +791,7 @@ YUV Image Format Notes

Error codes.

- @@ -825,6 +799,208 @@ YUV Image Format Notes + +

◆ TJINIT

+ +
+
+
Enumerator
TJERR_WARNING 

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

+
Enumerator
TJERR_WARNING 

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

TJERR_FATAL 

The error was fatal and non-recoverable.

+ + + +
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 unless TJPARAM_ARITHMETIC is set.

+
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. Can be combined with TJPARAM_ARITHMETIC. Implies TJPARAM_OPTIMIZE unless TJPARAM_ARITHMETIC is also set.

+
TJPARAM_SCANLIMIT 

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

+

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

+

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.

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

◆ TJPF

@@ -840,43 +1016,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 CMYK pixels into a YCCK JPEG image (see TJCS_YCCK) and decompressing YCCK JPEG images into CMYK pixels.

+

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

TJPF_UNKNOWN 

Unknown pixel format.

-

Currently this is only used by tjLoadImage().

+

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

@@ -895,7 +1071,7 @@ YUV Image Format Notes

Chrominance subsampling options.

-

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

+

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

+ +
Enumerator
TJSAMP_444 

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

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

@@ -917,6 +1093,17 @@ 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_441 

4:4:1 chrominance subsampling.

+

The JPEG or YUV image will contain one chrominance component for every 1x4 block of pixels in the source image. JPEG images compressed with 4:4:1 subsampling will be almost exactly the same size as those compressed with 4:2:0 subsampling, and in the aggregate, both subsampling methods produce approximately the same perceptual quality. However, 4:4:1 is better able to reproduce sharp vertical features.

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

Unknown subsampling.

+

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

    +
  • decompressed into planar YUV images,
  • +
  • losslessly transformed if TJXOPT_CROP is specified, or
  • +
  • partially decompressed using a cropping region.
  • +
+
@@ -933,52 +1120,52 @@ 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()

- + - +
DLLEXPORT unsigned char* tjAlloc DLLEXPORT void* tj3Alloc (int size_t  bytes)
-

Allocate an image buffer for use with TurboJPEG.

-

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

+

Allocate a byte buffer for use with TurboJPEG.

+

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

Parameters
@@ -986,19 +1173,31 @@ 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
- + + + + + + + + + + + + + @@ -1006,13 +1205,31 @@ YUV Image Format Notes + + + + + + - + + + + + + + + + + + + + @@ -1022,29 +1239,50 @@ YUV Image Format Notes
DLLEXPORT unsigned long tjBufSize DLLEXPORT int tj3Compress12 (tjhandle handle,
const short * srcBuf,
int  width,
int pitch,
int  height,
int jpegSubsamp pixelFormat,
unsigned char ** jpegBuf,
size_t * jpegSize 
-

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

-

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

+

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

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

◆ tjBufSizeYUV2()

+ +

◆ tj3Compress16()

- + + + + + + + + + + + + + @@ -1052,7 +1290,7 @@ YUV Image Format Notes - + @@ -1064,7 +1302,19 @@ YUV Image Format Notes - + + + + + + + + + + + + + @@ -1074,28 +1324,37 @@ YUV Image Format Notes
DLLEXPORT unsigned long tjBufSizeYUV2 DLLEXPORT int tj3Compress16 (tjhandle handle,
const unsigned short * srcBuf,
int  width,
int pad, pitch,
int subsamp pixelFormat,
unsigned char ** jpegBuf,
size_t * jpegSize 
-

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

+

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

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

◆ tjCompress2()

+ +

◆ tj3Compress8()

- + @@ -1139,26 +1398,8 @@ YUV Image Format Notes - - - - - - - - - - - - - - - - - - - - + + @@ -1168,40 +1409,37 @@ YUV Image Format Notes
DLLEXPORT int tjCompress2 DLLEXPORT int tj3Compress8 ( tjhandle  handle,
unsigned long * jpegSize,
int jpegSubsamp,
int jpegQual,
int flags size_t * jpegSize 
-

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

+

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

Parameters
- - + + - + - - - - - +If you choose option 1, then *jpegSize should be set to the size of your pre-allocated buffer. In any case, unless you have set TJPARAM_NOREALLOC, you should always check *jpegBuf upon return from this function, as it may have changed. +
handlea handle to a TurboJPEG compressor or transformer instance
srcBufpointer to an image buffer containing RGB, grayscale, or CMYK pixels to be compressed
handlehandle to a TurboJPEG instance that has been initialized for compression
srcBufpointer to a buffer containing a packed-pixel RGB, grayscale, or CMYK source image to be compressed. This buffer should normally be pitch * height samples in size. However, you can also use this parameter to compress from a specific region of a larger buffer.
widthwidth (in pixels) of the source image
pitchbytes per line in the source image. Normally, this should be width * tjPixelSize[pixelFormat] if the image is unpadded, or TJPAD(width * tjPixelSize[pixelFormat]) if each line of the image is padded to the nearest 32-bit boundary, as is the case for Windows bitmaps. You can also be clever and use this parameter to skip lines, etc. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
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 an image buffer that will receive the JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to accommodate the size of the JPEG image. Thus, you can choose to:
    -
  1. pre-allocate the JPEG buffer with an arbitrary size using tjAlloc() and let TurboJPEG grow the buffer as needed,
  2. +
jpegBufaddress of a pointer to a byte buffer that will receive the JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to accommodate the size of the JPEG image. Thus, you can choose to:
    +
  1. pre-allocate the JPEG buffer with an arbitrary size using tj3Alloc() and let TurboJPEG grow the buffer as needed,
  2. set *jpegBuf to NULL to tell TurboJPEG to allocate the buffer for you, or
  3. -
  4. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize(). This should ensure that the buffer never has to be re-allocated (setting TJFLAG_NOREALLOC guarantees that it won't be.)
  5. +
  6. pre-allocate the buffer to a "worst case" size determined by calling 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, *jpegSize should be set to the size of your pre-allocated buffer. In any case, unless you have set TJFLAG_NOREALLOC, you should always check *jpegBuf upon return from this function, as it may have changed.
jpegSizepointer to an unsigned long variable that holds the size of the JPEG image buffer. If *jpegBuf points to a pre-allocated buffer, then *jpegSize should be set to the size of the buffer. Upon return, *jpegSize will contain the size of the JPEG image (in bytes.) If *jpegBuf points to a JPEG image buffer that is being reused from a previous call to one of the JPEG compression functions, then *jpegSize is ignored.
jpegSubsampthe level of chrominance subsampling to be used when generating the JPEG image (see Chrominance subsampling options.)
jpegQualthe image quality of the generated JPEG image (1 = worst, 100 = best)
flagsthe bitwise OR of one or more of the flags
jpegSizepointer to a size_t variable that holds the size of the JPEG buffer. If *jpegBuf points to a pre-allocated buffer, then *jpegSize should be set to the size of the buffer. Upon return, *jpegSize will contain the size of the JPEG image (in bytes.) If *jpegBuf points to a JPEG buffer that is being reused from a previous call to one of the JPEG compression functions, then *jpegSize is ignored.
-
Returns
0 if successful, or -1 if an error occurred (see tjGetErrorStr2() and tjGetErrorCode().)
+
Returns
0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
- -

◆ tjCompressFromYUV()

+ +

◆ tj3CompressFromYUV8()

- + @@ -1222,7 +1460,7 @@ If you choose option 1, *jpegSize should be set to the size of your - + @@ -1233,32 +1471,14 @@ If you choose option 1, *jpegSize should be set to the size of your - - - - - - - - - - - - - - - - - - - - + + @@ -1268,39 +1488,36 @@ If you choose option 1, *jpegSize should be set to the size of your
DLLEXPORT int tjCompressFromYUV DLLEXPORT int tj3CompressFromYUV8 ( tjhandle  handle, int pad, align,
int subsamp,
unsigned char **  jpegBuf,
unsigned long * jpegSize,
int jpegQual,
int flags size_t * jpegSize 
-

Compress a YUV planar image into a JPEG image.

+

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

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

◆ tjCompressFromYUVPlanes()

+ +

◆ tj3CompressFromYUVPlanes8()

- + @@ -1308,7 +1525,7 @@ If you choose option 1, *jpegSize should be set to the size of your - + @@ -1332,32 +1549,14 @@ If you choose option 1, *jpegSize should be set to the size of your - - - - - - - - - - - - - - - - - - - - + + @@ -1367,39 +1566,36 @@ If you choose option 1, *jpegSize should be set to the size of your
DLLEXPORT int tjCompressFromYUVPlanes DLLEXPORT int tj3CompressFromYUVPlanes8 ( tjhandle  handle,
const unsigned char ** const unsigned char *const *  srcPlanes,
int subsamp,
unsigned char **  jpegBuf,
unsigned long * jpegSize,
int jpegQual,
int flags 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.

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

◆ tjDecodeYUV()

+ +

◆ tj3DecodeYUV8()

- + @@ -1414,13 +1610,7 @@ If you choose option 1, *jpegSize should be set to the size of your - - - - - - - + @@ -1450,13 +1640,7 @@ If you choose option 1, *jpegSize should be set to the size of your - - - - - - - + @@ -1466,35 +1650,33 @@ If you choose option 1, *jpegSize should be set to the size of your
DLLEXPORT int tjDecodeYUV DLLEXPORT int tj3DecodeYUV8 ( tjhandle  handle, int pad,
int subsamp, align,
int pixelFormat,
int flags pixelFormat 
-

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

-

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

+

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

◆ tjDecodeYUVPlanes()

+ +

◆ tj3DecodeYUVPlanes8()

- + @@ -1502,7 +1684,7 @@ If you choose option 1, *jpegSize should be set to the size of your - + @@ -1514,12 +1696,6 @@ If you choose option 1, *jpegSize should be set to the size of your - - - - - - @@ -1545,13 +1721,7 @@ If you choose option 1, *jpegSize should be set to the size of your - - - - - - - + @@ -1561,35 +1731,33 @@ If you choose option 1, *jpegSize should be set to the size of your
DLLEXPORT int tjDecodeYUVPlanes DLLEXPORT int tj3DecodeYUVPlanes8 ( tjhandle  handle,
const unsigned char ** const unsigned char *const *  srcPlanes,
int subsamp,
unsigned char *  dstBuf,
int pixelFormat,
int flags pixelFormat 
-

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

-

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

+

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

◆ tjDecompress2()

+ +

◆ tj3Decompress12()

- + @@ -1603,44 +1771,26 @@ If you choose option 1, *jpegSize should be set to the size of your - + - + - - - - - - - - - - - - - - - - - - - + @@ -1650,33 +1800,31 @@ If you choose option 1, *jpegSize should be set to the size of your
DLLEXPORT int tjDecompress2 DLLEXPORT int tj3Decompress12 ( tjhandle  handle,
unsigned long size_t  jpegSize,
unsigned char * short *  dstBuf,
int width,
int  pitch,
int height,
int pixelFormat,
int flags pixelFormat 
-

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

+

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

+

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

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

◆ tjDecompressHeader3()

+ +

◆ tj3Decompress16()

- + @@ -1690,32 +1838,26 @@ If you choose option 1, *jpegSize should be set to the size of your - + - - - - - - - - + + - - + + - - + + @@ -1725,31 +1867,31 @@ If you choose option 1, *jpegSize should be set to the size of your
DLLEXPORT int tjDecompressHeader3 DLLEXPORT int tj3Decompress16 ( tjhandle  handle,
unsigned long size_t  jpegSize,
int * width,
int * height, unsigned short * dstBuf,
int * jpegSubsamp, int pitch,
int * jpegColorspace int pixelFormat 
-

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

+

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

+

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

Parameters
- - - - - - - + + + + + +
handlea handle to a TurboJPEG decompressor or transformer instance
jpegBufpointer to a buffer containing a JPEG image or an "abbreviated table specification" (AKA "tables-only") datastream. Passing a tables-only datastream to this function primes the decompressor with quantization and Huffman tables that can be used when decompressing subsequent "abbreviated image" datastreams. This is useful, for instance, when decompressing video streams in which all frames share the same quantization and Huffman tables.
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.
handlehandle to a TurboJPEG instance that has been initialized for decompression
jpegBufpointer to a byte buffer containing the JPEG image to decompress
jpegSizesize of the JPEG image (in bytes)
dstBufpointer to a buffer that will receive the packed-pixel decompressed image. This buffer should normally be pitch * destinationHeight samples in size. However, you can also use this parameter to decompress into a specific region of a larger buffer. NOTE: If the JPEG image is lossy, then destinationHeight is either the scaled JPEG height (see TJSCALED(), TJPARAM_JPEGHEIGHT, and tj3SetScalingFactor()) or the height of the cropping region (see tj3SetCroppingRegion().) If the JPEG image is lossless, then destinationHeight is the JPEG height.
pitchsamples per row in the destination image. Normally this should be set to destinationWidth * tjPixelSize[pixelFormat], if the destination image should be unpadded. (Setting this parameter to 0 is the equivalent of setting it to destinationWidth * tjPixelSize[pixelFormat].) However, you can also use this parameter to specify the row alignment/padding of the destination image, to skip rows, or to decompress into a specific region of a larger buffer. NOTE: If the JPEG image is lossy, then destinationWidth is either the scaled JPEG width (see TJSCALED(), TJPARAM_JPEGWIDTH, and tj3SetScalingFactor()) or the width of the cropping region (see tj3SetCroppingRegion().) If the JPEG image is lossless, then destinationWidth is the JPEG width.
pixelFormatpixel format of the destination image (see Pixel formats.)
-
Returns
0 if successful, or -1 if an error occurred (see tjGetErrorStr2() and tjGetErrorCode().)
+
Returns
0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
- -

◆ tjDecompressToYUV2()

+ +

◆ tj3Decompress8()

- + @@ -1763,7 +1905,7 @@ If you choose option 1, *jpegSize should be set to the size of your - + @@ -1776,25 +1918,62 @@ If you choose option 1, *jpegSize should be set to the size of your - + - + + + + + + + +
DLLEXPORT int tjDecompressToYUV2 DLLEXPORT int tj3Decompress8 ( tjhandle  handle,
unsigned long size_t  jpegSize,
int width, pitch,
int pad, 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()

+ +
+
+ + + + + + - - + + - - + + @@ -1804,33 +1983,28 @@ If you choose option 1, *jpegSize should be set to the size of your
DLLEXPORT int tj3DecompressHeader (tjhandle handle,
int height, const unsigned char * jpegBuf,
int flags size_t jpegSize 
-

Decompress a JPEG image to a YUV planar image.

-

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

+

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
- - - - - - - - + + +
handlea handle to a TurboJPEG decompressor or transformer instance
jpegBufpointer to a buffer containing the JPEG image to decompress
jpegSizesize of the JPEG image (in bytes)
dstBufpointer to an image buffer that will receive the YUV image. Use tjBufSizeYUV2() to determine the appropriate size for this buffer based on the image width, height, padding, and level of subsampling. The Y, U (Cb), and V (Cr) image planes will be stored sequentially in the buffer (refer to YUV Image Format Notes.)
widthdesired width (in pixels) of the YUV image. If this is different than the width of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired width. If width is set to 0, then only the height will be considered when determining the scaled image size. If the scaled width is not an even multiple of the MCU block width (see tjMCUWidth), then an intermediate buffer copy will be performed within TurboJPEG.
padthe width of each line in each plane of the YUV image will be padded to the nearest multiple of this number of bytes (must be a power of 2.) To generate images suitable for X Video, pad should be set to 4.
heightdesired height (in pixels) of the YUV image. If this is different than the height of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired height. If height is set to 0, then only the width will be considered when determining the scaled image size. If the scaled height is not an even multiple of the MCU block height (see tjMCUHeight), then an intermediate buffer copy will be performed within TurboJPEG.
flagsthe bitwise OR of one or more of the flags
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 tjGetErrorStr2() and tjGetErrorCode().)
+
Returns
0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
- -

◆ tjDecompressToYUVPlanes()

+ +

◆ tj3DecompressToYUV8()

- + @@ -1844,38 +2018,80 @@ If you choose option 1, *jpegSize should be set to the size of your - + - - + + - + + + + + + + +
DLLEXPORT int tjDecompressToYUVPlanes DLLEXPORT int tj3DecompressToYUV8 ( tjhandle  handle,
unsigned long size_t  jpegSize,
unsigned char ** dstPlanes, unsigned char * dstBuf,
int width, 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()

+ +
+
+ + + + + + - - + + - - + + - - + + + + + + + + @@ -1885,33 +2101,30 @@ If you choose option 1, *jpegSize should be set to the size of your
DLLEXPORT int tj3DecompressToYUVPlanes8 (tjhandle handle,
int * strides, const unsigned char * jpegBuf,
int height, size_t jpegSize,
int flags unsigned char ** dstPlanes,
int * strides 
-

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

-

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

+

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
- - + + - - - - - + +
handlea handle to a TurboJPEG decompressor or transformer instance
jpegBufpointer to a buffer containing the JPEG image to decompress
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 YUV image. These planes can be contiguous or non-contiguous in memory. Use tjPlaneSizeYUV() to determine the appropriate size for each plane based on the scaled image width, scaled image height, strides, and level of chrominance subsampling. Refer to YUV Image Format Notes for more details.
widthdesired width (in pixels) of the YUV image. If this is different than the width of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired width. If width is set to 0, then only the height will be considered when determining the scaled image size. If the scaled width is not an even multiple of the MCU block width (see tjMCUWidth), then an intermediate buffer copy will be performed within TurboJPEG.
stridesan array of integers, each specifying the number of bytes per line in the corresponding plane of the output image. Setting the stride for any plane to 0 is the same as setting it to the scaled plane width (see YUV Image Format Notes.) If strides is NULL, then the strides for all planes will be set to their respective scaled plane widths. You can adjust the strides in order to add an arbitrary amount of line padding to each plane or to decompress the JPEG image into a subregion of a larger YUV planar image.
heightdesired height (in pixels) of the YUV image. If this is different than the height of the JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired height. If height is set to 0, then only the width will be considered when determining the scaled image size. If the scaled height is not an even multiple of the MCU block height (see tjMCUHeight), then an intermediate buffer copy will be performed within TurboJPEG.
flagsthe bitwise OR of one or more of the flags
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 tjGetErrorStr2() and tjGetErrorCode().)
+
Returns
0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
- -

◆ tjDestroy()

+ +

◆ tj3Destroy()

- + @@ -1920,25 +2133,24 @@ If you choose option 1, *jpegSize should be set to the size of your
DLLEXPORT int tjDestroy DLLEXPORT void tj3Destroy ( tjhandle  handle)
-

Destroy a TurboJPEG compressor, decompressor, or transformer instance.

+

Destroy a TurboJPEG instance.

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

◆ tjEncodeYUV3()

+ +

◆ tj3EncodeYUV8()

- + @@ -1983,19 +2195,7 @@ If you choose option 1, *jpegSize should be set to the size of your - - - - - - - - - - - - - + @@ -2005,35 +2205,33 @@ If you choose option 1, *jpegSize should be set to the size of your
DLLEXPORT int tjEncodeYUV3 DLLEXPORT int tj3EncodeYUV8 ( tjhandle  handle, int pad,
int subsamp,
int flags align 
-

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

-

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

+

Encode 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
- - + + - + - - - - + +
handlea handle to a TurboJPEG compressor or transformer instance
srcBufpointer to an image buffer containing RGB or grayscale pixels to be encoded
handlehandle to a TurboJPEG instance that has been initialized for compression
srcBufpointer to a buffer containing a packed-pixel RGB or grayscale source image to be encoded. This buffer should normally be pitch * height bytes in size. However, you can also use this parameter to encode from a specific region of a larger buffer.
widthwidth (in pixels) of the source image
pitchbytes per line in the source image. Normally, this should be width * tjPixelSize[pixelFormat] if the image is unpadded, or TJPAD(width * tjPixelSize[pixelFormat]) if each line of the image is padded to the nearest 32-bit boundary, as is the case for Windows bitmaps. You can also be clever and use this parameter to skip lines, etc. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
pitchbytes per row in the source image. Normally this should be width * tjPixelSize[pixelFormat], if the image is unpadded. (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 an image buffer that will receive the YUV image. Use tjBufSizeYUV2() to determine the appropriate size for this buffer based on the image width, height, padding, and level of chrominance subsampling. The Y, U (Cb), and V (Cr) image planes will be stored sequentially in the buffer (refer to YUV Image Format Notes.)
padthe width of each line in each plane of the YUV image will be padded to the nearest multiple of this number of bytes (must be a power of 2.) To generate images suitable for X Video, pad should be set to 4.
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
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 tjGetErrorStr2() and tjGetErrorCode().)
+
Returns
0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
- -

◆ tjEncodeYUVPlanes()

+ +

◆ tj3EncodeYUVPlanes8()

- + @@ -2078,19 +2276,7 @@ If you choose option 1, *jpegSize should be set to the size of your - - - - - - - - - - - - - + @@ -2100,76 +2286,112 @@ If you choose option 1, *jpegSize should be set to the size of your
DLLEXPORT int tjEncodeYUVPlanes DLLEXPORT int tj3EncodeYUVPlanes8 ( tjhandle  handle, int * strides,
int subsamp,
int flags strides 
-

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

-

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

+

Encode 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
- - + + - + - - - - + +
handlea handle to a TurboJPEG compressor or transformer instance
srcBufpointer to an image buffer containing RGB or grayscale pixels to be encoded
handlehandle to a TurboJPEG instance that has been initialized for compression
srcBufpointer to a buffer containing a packed-pixel RGB or grayscale source image to be encoded. This buffer should normally be pitch * height bytes in size. However, you can also use this parameter to encode from a specific region of a larger buffer.
widthwidth (in pixels) of the source image
pitchbytes per line in the source image. Normally, this should be width * tjPixelSize[pixelFormat] if the image is unpadded, or TJPAD(width * tjPixelSize[pixelFormat]) if each line of the image is padded to the nearest 32-bit boundary, as is the case for Windows bitmaps. You can also be clever and use this parameter to skip lines, etc. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
pitchbytes per row in the source image. Normally this should be width * tjPixelSize[pixelFormat], if the image is unpadded. (Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].) However, you can also use this parameter to specify the row alignment/padding of the source image, to skip rows, or to encode from a specific region of a larger packed-pixel image.
heightheight (in pixels) of the source image
pixelFormatpixel format of the source image (see Pixel formats.)
dstPlanesan array of pointers to Y, U (Cb), and V (Cr) image planes (or just a Y plane, if generating a grayscale image) that will receive the encoded image. These planes can be contiguous or non-contiguous in memory. Use tjPlaneSizeYUV() to determine the appropriate size for each plane based on the image width, height, strides, and level of chrominance subsampling. Refer to YUV Image Format Notes for more details.
stridesan array of integers, each specifying the number of bytes per line in the corresponding plane of the output image. Setting the stride for any plane to 0 is the same as setting it to the plane width (see YUV Image Format Notes.) If strides is NULL, then the strides for all planes will be set to their respective plane widths. You can adjust the strides in order to add an arbitrary amount of line padding to each plane or to encode an RGB or grayscale image into a subregion of a larger YUV planar image.
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
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 tjGetErrorStr2() and tjGetErrorCode().)
+
Returns
0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
- -

◆ tjFree()

+ +

◆ tj3Free()

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

Free an image buffer previously allocated by TurboJPEG.

-

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

+

Free a byte buffer previously allocated by TurboJPEG.

+

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

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

◆ tjGetErrorCode()

+ +

◆ tj3Get()

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

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
- +
handlea handle to a TurboJPEG compressor, decompressor or transformer instance
handlehandle to a TurboJPEG instance
@@ -2177,14 +2399,14 @@ If you choose option 1, *jpegSize should be set to the size of your
- -

◆ tjGetErrorStr2()

+ +

◆ tj3GetErrorStr()

- + @@ -2193,148 +2415,652 @@ If you choose option 1, *jpegSize should be set to the size of your
DLLEXPORT char* tjGetErrorStr2 DLLEXPORT char* tj3GetErrorStr ( tjhandle  handle)
-

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

+

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

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

+

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

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

◆ tj3LoadImage12()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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
- + + + + + + +
handlea handle to a TurboJPEG compressor, decompressor, or transformer instance, or NULL if the error was generated by a global function (but note that retrieving the error message for a global function is thread-safe only on platforms that support thread-local storage.)
handlehandle to a TurboJPEG instance
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
a descriptive error message explaining why the last command failed.
+
Returns
0 if successful, or -1 if an error occurred (see tj3GetErrorStr().)
- -

◆ tjGetScalingFactors()

+ +

◆ tj3Set()

- + - - + + + + + + + + + + + + + + + + + +
DLLEXPORT tjscalingfactor* tjGetScalingFactors DLLEXPORT int tj3Set (int * numscalingfactors)tjhandle handle,
int param,
int value 
)
-

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

+

Set the value of a parameter.

Parameters
- + + +
numscalingfactorspointer to an integer variable that will receive the number of elements in the list
handlehandle to a TurboJPEG instance
paramone of the parameters
valuevalue of the parameter (refer to parameter documentation)
-
Returns
a pointer to a list of fractional scaling factors, or NULL if an error is encountered (see tjGetErrorStr2().)
+
Returns
0 if successful, or -1 if an error occurred (see tj3GetErrorStr().)
- -

◆ tjInitCompress()

+ +

◆ tj3SetCroppingRegion()

- + - - - + + -
DLLEXPORT tjhandle tjInitCompress DLLEXPORT int tj3SetCroppingRegion (void )tjhandle handle,
-
- -

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 ) tjregion croppingRegion 
-
- -

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

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

◆ tjLoadImage()

+ +

◆ tj3SetScalingFactor()

- + - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + @@ -2344,51 +3070,65 @@ If you choose option 1, *jpegSize should be set to the size of your
DLLEXPORT unsigned char* tjLoadImage DLLEXPORT int tj3SetScalingFactor (const char * filename,
int * width,
int align,
int * height,
int * pixelFormat, tjhandle handle,
int flags tjscalingfactor scalingFactor 
-

Load an uncompressed image from disk into memory.

+

Set the scaling factor for subsequent lossy decompression operations.

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

◆ tjPlaneHeight()

+ +

◆ tj3Transform()

- + - - + + - - + + + + + + + + - + + + + + + + + + + + + + + + + + + + @@ -2398,43 +3138,46 @@ If you choose option 1, *jpegSize should be set to the size of your
DLLEXPORT int tjPlaneHeight DLLEXPORT int tj3Transform (int componentID, tjhandle handle,
int height, const unsigned char * jpegBuf,
size_t jpegSize,
int subsamp n,
unsigned char ** dstBufs,
size_t * dstSizes,
const tjtransformtransforms 
-

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

-

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

+

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
- - - + + + + + + +
componentIDID number of the image plane (0 = Y, 1 = U/Cb, 2 = V/Cr)
heightheight (in pixels) of the YUV image
subsamplevel of chrominance subsampling in the image (see Chrominance subsampling options.)
handlehandle to a TurboJPEG instance 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 and the level of subsampling used in the source image. Under normal circumstances, this should ensure that the buffer never has to be re-allocated. (Setting TJPARAM_NOREALLOC guarantees that it won't be.) Note, however, that there are some rare cases (such as transforming images with a large amount of embedded EXIF or ICC profile data) in which the transformed JPEG image will be larger than the worst-case size, and TJPARAM_NOREALLOC cannot be used in those cases.
  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
the plane height of a YUV image plane with the given parameters, or -1 if the arguments are out of bounds.
+
Returns
0 if successful, or -1 if an error occurred (see tj3GetErrorStr() and tj3GetErrorCode().)
- -

◆ tjPlaneSizeYUV()

+ +

◆ tj3YUVBufSize()

- + - - - - - - - + @@ -2456,29 +3199,28 @@ If you choose option 1, *jpegSize should be set to the size of your
DLLEXPORT unsigned long tjPlaneSizeYUV DLLEXPORT size_t tj3YUVBufSize ( int componentID,
int  width,
int stride, align,
-

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

+

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

Parameters
- - - - + + +
componentIDID number of the image plane (0 = Y, 1 = U/Cb, 2 = V/Cr)
widthwidth (in pixels) of the YUV image. NOTE: this is the width of the whole image, not the plane width.
stridebytes per line in the image plane. Setting this to 0 is the equivalent of setting it to the plane width.
heightheight (in pixels) of the YUV image. NOTE: this is the height of the whole image, not the plane height.
widthwidth (in pixels) of the image
alignrow alignment (in bytes) of the image (must be a power of 2.) Setting this parameter to n specifies that each row in each plane of the image will be padded to the nearest multiple of n bytes (1 = unpadded.)
heightheight (in pixels) of the image
subsamplevel of chrominance subsampling in the image (see Chrominance subsampling options.)
-
Returns
the size of the buffer (in bytes) required to hold the YUV image plane, 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.
- -

◆ tjPlaneWidth()

+ +

◆ tj3YUVPlaneHeight()

- + @@ -2487,7 +3229,7 @@ If you choose option 1, *jpegSize should be set to the size of your - + @@ -2503,37 +3245,31 @@ If you choose option 1, *jpegSize should be set to the size of your
DLLEXPORT int tjPlaneWidth DLLEXPORT int tj3YUVPlaneHeight ( int  componentID, int width, height,
-

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

-

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

+

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

+

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

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

◆ tjSaveImage()

+ +

◆ tj3YUVPlaneSize()

- + - - - - - - - - + + @@ -2545,7 +3281,7 @@ If you choose option 1, *jpegSize should be set to the size of your - + @@ -2557,13 +3293,7 @@ If you choose option 1, *jpegSize should be set to the size of your - - - - - - - + @@ -2573,76 +3303,44 @@ If you choose option 1, *jpegSize should be set to the size of your
DLLEXPORT int tjSaveImage DLLEXPORT size_t tj3YUVPlaneSize (const char * filename,
unsigned char * buffer, int componentID,
int pitch, stride,
int pixelFormat,
int flags subsamp 
-

Save an uncompressed image from memory to disk.

+

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

Parameters
- - - - - - - + + + + +
filenamename of a file to which to save the uncompressed image. The image will be stored in Windows BMP or PBMPLUS (PPM/PGM) format, depending on the file extension.
bufferpointer to an image buffer containing RGB, grayscale, or CMYK pixels to be saved
widthwidth (in pixels) of the uncompressed image
pitchbytes per line in the image buffer. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
heightheight (in pixels) of the uncompressed image
pixelFormatpixel format of the image buffer (see Pixel formats.) If this parameter is set to TJPF_GRAY, then the image will be stored in PGM or 8-bit (indexed color) BMP format. Otherwise, the image will be stored in PPM or 24-bit BMP format. If this parameter is set to TJPF_CMYK, then the CMYK pixels will be converted to RGB using a quick & dirty algorithm that is suitable only for testing (proper conversion between CMYK and other formats requires a color management system.)
flagsthe bitwise OR of one or more of the flags.
componentIDID number of the image plane (0 = Y, 1 = U/Cb, 2 = V/Cr)
widthwidth (in pixels) of the YUV image. NOTE: this is the width of the whole image, not the plane width.
stridebytes per row in the image plane. Setting this to 0 is the equivalent of setting it to the plane width.
heightheight (in pixels) of the YUV image. NOTE: this is the height of the whole image, not the plane height.
subsamplevel of chrominance subsampling in the image (see Chrominance subsampling options.)
-
Returns
0 if successful, or -1 if an error occurred (see tjGetErrorStr2().)
+
Returns
the size of the buffer (in bytes) required to hold the YUV image plane, or 0 if the arguments are out of bounds.
- -

◆ tjTransform()

+ +

◆ tj3YUVPlaneWidth()

- + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - + @@ -2652,27 +3350,17 @@ If you choose option 1, *jpegSize should be set to the size of your
DLLEXPORT int tjTransform DLLEXPORT int tj3YUVPlaneWidth (tjhandle handle,
const unsigned char * jpegBuf,
unsigned long jpegSize,
int n,
unsigned char ** dstBufs,
unsigned long * dstSizes, componentID,
tjtransformtransforms, int width,
int flags subsamp 
-

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.

+

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

+

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

Parameters
- - - - - - - - + + +
handlea handle to a TurboJPEG transformer instance
jpegBufpointer to a buffer containing the JPEG source image to transform
jpegSizesize of the JPEG source image (in bytes)
nthe number of transformed JPEG images to generate
dstBufspointer to an array of n image buffers. dstBufs[i] will receive a JPEG image that has been transformed using the parameters in transforms[i]. TurboJPEG has the ability to reallocate the JPEG buffer to accommodate the size of the JPEG image. Thus, you can choose to:
    -
  1. pre-allocate the JPEG buffer with an arbitrary size using tjAlloc() and let TurboJPEG grow the buffer as needed,
  2. -
  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 output image will be larger than the worst-case size, and TJFLAG_NOREALLOC cannot be used in those cases.
  6. -
-If you choose option 1, dstSizes[i] should be set to the size of your pre-allocated buffer. In any case, unless you have set TJFLAG_NOREALLOC, you should always check dstBufs[i] upon return from this function, as it may have changed.
dstSizespointer to an array of n unsigned long variables that will receive the actual sizes (in bytes) of each transformed JPEG image. If dstBufs[i] points to a pre-allocated buffer, then dstSizes[i] should be set to the size of the buffer. Upon return, dstSizes[i] will contain the size of the JPEG image (in bytes.)
transformspointer to an array of n tjtransform structures, each of which specifies the transform parameters and/or cropping region for the corresponding transformed output image.
flagsthe bitwise OR of one or more of the flags
componentIDID number of the image plane (0 = Y, 1 = U/Cb, 2 = V/Cr)
widthwidth (in pixels) of the YUV image
subsamplevel of chrominance subsampling in the image (see Chrominance subsampling options.)
-
Returns
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.
@@ -2697,8 +3385,8 @@ If you choose option 1, dstSizes[i] should be set to the size of yo
-

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

-

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

+

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.

@@ -2722,8 +3410,8 @@ If you choose option 1, dstSizes[i] should be set to the size of yo
-

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

-

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

+

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.

@@ -2747,8 +3435,8 @@ If you choose option 1, dstSizes[i] should be set to the size of yo
-

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

-

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

+

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.

@@ -2778,7 +3466,8 @@ If you choose option 1, dstSizes[i] should be set to the size of yo
  • 16x8 for 4:2:2
  • 8x16 for 4:4:0
  • 16x16 for 4:2:0
  • -
  • 32x8 for 4:1:1
  • +
  • 32x8 for 4:1:1
  • +
  • 8x32 for 4:4:1
  • @@ -2809,7 +3498,8 @@ If you choose option 1, dstSizes[i] should be set to the size of yo
  • 16x8 for 4:2:2
  • 8x16 for 4:4:0
  • 16x16 for 4:2:0
  • -
  • 32x8 for 4:1:1
  • +
  • 32x8 for 4:1:1
  • +
  • 8x32 for 4:4:1
  • @@ -2834,7 +3524,7 @@ If you choose option 1, dstSizes[i] should be set to the size of yo
    -

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

    +

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

    @@ -2858,8 +3548,56 @@ If you choose option 1, dstSizes[i] should be set to the size of yo
    -

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

    -

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

    +

    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 a6f272a..7c9ccd2 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -23,7 +23,7 @@
    TurboJPEG -  2.1.4 +  3
    diff --git a/doc/html/modules.html b/doc/html/modules.html index d48980a..b6c30d8 100644 --- a/doc/html/modules.html +++ b/doc/html/modules.html @@ -23,7 +23,7 @@
    TurboJPEG -  2.1.4 +  3
    diff --git a/doc/html/search/all_0.js b/doc/html/search/all_0.js index 54c7356..07cc58c 100644 --- a/doc/html/search/all_0.js +++ b/doc/html/search/all_0.js @@ -1,4 +1,4 @@ var searchData= [ - ['customfilter_0',['customFilter',['../structtjtransform.html#afd7fc262df33f741e120ef4183202ef5',1,'tjtransform']]] + ['customfilter_0',['customFilter',['../structtjtransform.html#a0dc7697d59a7abe48afc629e96cbc1d2',1,'tjtransform']]] ]; diff --git a/doc/html/search/all_6.js b/doc/html/search/all_6.js index aa31107..a3fa784 100644 --- a/doc/html/search/all_6.js +++ b/doc/html/search/all_6.js @@ -1,103 +1,141 @@ 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#ga2be2b9969d4df9ecce9b05deed273194',1,'turbojpeg.h']]], - ['tjcompress2_18',['tjCompress2',['../group___turbo_j_p_e_g.html#gafbdce0112fd78fd38efae841443a9bcf',1,'turbojpeg.h']]], - ['tjcompressfromyuv_19',['tjCompressFromYUV',['../group___turbo_j_p_e_g.html#ga7622a459b79aa1007e005b58783f875b',1,'turbojpeg.h']]], - ['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#ga70abbf38f77a26fd6da8813bef96f695',1,'turbojpeg.h']]], - ['tjdecodeyuvplanes_28',['tjDecodeYUVPlanes',['../group___turbo_j_p_e_g.html#ga10e837c07fa9d25770565b237d3898d9',1,'turbojpeg.h']]], - ['tjdecompress2_29',['tjDecompress2',['../group___turbo_j_p_e_g.html#gae9eccef8b682a48f43a9117c231ed013',1,'turbojpeg.h']]], - ['tjdecompressheader3_30',['tjDecompressHeader3',['../group___turbo_j_p_e_g.html#ga0595681096bba7199cc6f3533cb25f77',1,'turbojpeg.h']]], - ['tjdecompresstoyuv2_31',['tjDecompressToYUV2',['../group___turbo_j_p_e_g.html#ga04d1e839ff9a0860dd1475cff78d3364',1,'turbojpeg.h']]], - ['tjdecompresstoyuvplanes_32',['tjDecompressToYUVPlanes',['../group___turbo_j_p_e_g.html#gaa59f901a5258ada5bd0185ad59368540',1,'turbojpeg.h']]], - ['tjdestroy_33',['tjDestroy',['../group___turbo_j_p_e_g.html#ga75f355fa27225ba1a4ee392c852394d2',1,'turbojpeg.h']]], - ['tjencodeyuv3_34',['tjEncodeYUV3',['../group___turbo_j_p_e_g.html#gac519b922cdf446e97d0cdcba513636bf',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_5fbottomup_40',['TJFLAG_BOTTOMUP',['../group___turbo_j_p_e_g.html#ga72ecf4ebe6eb702d3c6f5ca27455e1ec',1,'turbojpeg.h']]], - ['tjflag_5ffastdct_41',['TJFLAG_FASTDCT',['../group___turbo_j_p_e_g.html#gaabce235db80d3f698b27f36cbd453da2',1,'turbojpeg.h']]], - ['tjflag_5ffastupsample_42',['TJFLAG_FASTUPSAMPLE',['../group___turbo_j_p_e_g.html#ga4ee4506c81177a06f77e2504a22efd2d',1,'turbojpeg.h']]], - ['tjflag_5flimitscans_43',['TJFLAG_LIMITSCANS',['../group___turbo_j_p_e_g.html#ga163e6482dc5096831feef9c79ff3f805',1,'turbojpeg.h']]], - ['tjflag_5fnorealloc_44',['TJFLAG_NOREALLOC',['../group___turbo_j_p_e_g.html#ga8808d403c68b62aaa58a4c1e58e98963',1,'turbojpeg.h']]], - ['tjflag_5fprogressive_45',['TJFLAG_PROGRESSIVE',['../group___turbo_j_p_e_g.html#ga43b426750b46190a25d34a67ef76df1b',1,'turbojpeg.h']]], - ['tjflag_5fstoponwarning_46',['TJFLAG_STOPONWARNING',['../group___turbo_j_p_e_g.html#ga519cfa4ef6c18d9e5b455fdf59306a3a',1,'turbojpeg.h']]], - ['tjfree_47',['tjFree',['../group___turbo_j_p_e_g.html#gaea863d2da0cdb609563aabdf9196514b',1,'turbojpeg.h']]], - ['tjgeterrorcode_48',['tjGetErrorCode',['../group___turbo_j_p_e_g.html#ga414feeffbf860ebd31c745df203de410',1,'turbojpeg.h']]], - ['tjgeterrorstr2_49',['tjGetErrorStr2',['../group___turbo_j_p_e_g.html#ga1ead8574f9f39fbafc6b497124e7aafa',1,'turbojpeg.h']]], - ['tjgetscalingfactors_50',['tjGetScalingFactors',['../group___turbo_j_p_e_g.html#gac3854476006b10787bd128f7ede48057',1,'turbojpeg.h']]], - ['tjgreenoffset_51',['tjGreenOffset',['../group___turbo_j_p_e_g.html#ga82d6e35da441112a411da41923c0ba2f',1,'turbojpeg.h']]], - ['tjhandle_52',['tjhandle',['../group___turbo_j_p_e_g.html#ga758d2634ecb4949de7815cba621f5763',1,'turbojpeg.h']]], - ['tjinitcompress_53',['tjInitCompress',['../group___turbo_j_p_e_g.html#ga9d63a05fc6d813f4aae06107041a37e8',1,'turbojpeg.h']]], - ['tjinitdecompress_54',['tjInitDecompress',['../group___turbo_j_p_e_g.html#ga52300eac3f3d9ef4bab303bc244f62d3',1,'turbojpeg.h']]], - ['tjinittransform_55',['tjInitTransform',['../group___turbo_j_p_e_g.html#ga928beff6ac248ceadf01089fc6b41957',1,'turbojpeg.h']]], - ['tjloadimage_56',['tjLoadImage',['../group___turbo_j_p_e_g.html#gaffbd83c375e79f5db4b5c5d8ad4466e7',1,'turbojpeg.h']]], - ['tjmcuheight_57',['tjMCUHeight',['../group___turbo_j_p_e_g.html#gabd247bb9fecb393eca57366feb8327bf',1,'turbojpeg.h']]], - ['tjmcuwidth_58',['tjMCUWidth',['../group___turbo_j_p_e_g.html#ga9e61e7cd47a15a173283ba94e781308c',1,'turbojpeg.h']]], - ['tjpad_59',['TJPAD',['../group___turbo_j_p_e_g.html#ga0aba955473315e405295d978f0c16511',1,'turbojpeg.h']]], - ['tjpf_60',['TJPF',['../group___turbo_j_p_e_g.html#gac916144e26c3817ac514e64ae5d12e2a',1,'turbojpeg.h']]], - ['tjpf_5fabgr_61',['TJPF_ABGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa1ba1a7f1631dbeaa49a0a85fc4a40081',1,'turbojpeg.h']]], - ['tjpf_5fargb_62',['TJPF_ARGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aae8f846ed9d9de99b6e1dfe448848765c',1,'turbojpeg.h']]], - ['tjpf_5fbgr_63',['TJPF_BGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aab10624437fb8ef495a0b153e65749839',1,'turbojpeg.h']]], - ['tjpf_5fbgra_64',['TJPF_BGRA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aac037ff1845cf9b74bb81a3659c2b9fb4',1,'turbojpeg.h']]], - ['tjpf_5fbgrx_65',['TJPF_BGRX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa2a1fbf569ca79897eae886e3376ca4c8',1,'turbojpeg.h']]], - ['tjpf_5fcmyk_66',['TJPF_CMYK',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7f5100ec44c91994e243f1cf55553f8b',1,'turbojpeg.h']]], - ['tjpf_5fgray_67',['TJPF_GRAY',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa5431b54b015337705f13118073711a1a',1,'turbojpeg.h']]], - ['tjpf_5frgb_68',['TJPF_RGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7ce93230bff449518ce387c17e6ed37c',1,'turbojpeg.h']]], - ['tjpf_5frgba_69',['TJPF_RGBA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa88d2e88fab67f6503cf972e14851cc12',1,'turbojpeg.h']]], - ['tjpf_5frgbx_70',['TJPF_RGBX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa83973bebb7e2dc6fa8bae89ff3f42e01',1,'turbojpeg.h']]], - ['tjpf_5funknown_71',['TJPF_UNKNOWN',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa84c1a6cead7952998e2fb895844a21ed',1,'turbojpeg.h']]], - ['tjpf_5fxbgr_72',['TJPF_XBGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aaf6603b27147de47e212e75dac027b2af',1,'turbojpeg.h']]], - ['tjpf_5fxrgb_73',['TJPF_XRGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aadae996905efcfa3b42a0bb3bea7f9d84',1,'turbojpeg.h']]], - ['tjpixelsize_74',['tjPixelSize',['../group___turbo_j_p_e_g.html#gad77cf8fe5b2bfd3cb3f53098146abb4c',1,'turbojpeg.h']]], - ['tjplaneheight_75',['tjPlaneHeight',['../group___turbo_j_p_e_g.html#ga1a209696c6a80748f20e134b3c64789f',1,'turbojpeg.h']]], - ['tjplanesizeyuv_76',['tjPlaneSizeYUV',['../group___turbo_j_p_e_g.html#gab4ab7b24f6e797d79abaaa670373961d',1,'turbojpeg.h']]], - ['tjplanewidth_77',['tjPlaneWidth',['../group___turbo_j_p_e_g.html#ga63fb66bb1e36c74008c4634360becbb1',1,'turbojpeg.h']]], - ['tjredoffset_78',['tjRedOffset',['../group___turbo_j_p_e_g.html#gadd9b446742ac8a3923f7992c7988fea8',1,'turbojpeg.h']]], - ['tjregion_79',['tjregion',['../structtjregion.html',1,'']]], - ['tjsamp_80',['TJSAMP',['../group___turbo_j_p_e_g.html#ga1d047060ea80bb9820d540bb928e9074',1,'turbojpeg.h']]], - ['tjsamp_5f411_81',['TJSAMP_411',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a28ec62575e5ea295c3fde3001dc628e2',1,'turbojpeg.h']]], - ['tjsamp_5f420_82',['TJSAMP_420',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a63085dbf683cfe39e513cdb6343e3737',1,'turbojpeg.h']]], - ['tjsamp_5f422_83',['TJSAMP_422',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a136130902cc578f11f32429b59368404',1,'turbojpeg.h']]], - ['tjsamp_5f440_84',['TJSAMP_440',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074accf740e6f3aa6ba20ba922cad13cb974',1,'turbojpeg.h']]], - ['tjsamp_5f444_85',['TJSAMP_444',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074afb8da4f44197837bdec0a4f593dacae3',1,'turbojpeg.h']]], - ['tjsamp_5fgray_86',['TJSAMP_GRAY',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a3f1c9504842ddc7a48d0f690754b6248',1,'turbojpeg.h']]], - ['tjsaveimage_87',['tjSaveImage',['../group___turbo_j_p_e_g.html#ga6f445b22d8933ae4815b3370a538d879',1,'turbojpeg.h']]], - ['tjscaled_88',['TJSCALED',['../group___turbo_j_p_e_g.html#ga84878bb65404204743aa18cac02781df',1,'turbojpeg.h']]], - ['tjscalingfactor_89',['tjscalingfactor',['../structtjscalingfactor.html',1,'']]], - ['tjtransform_90',['tjtransform',['../structtjtransform.html',1,'tjtransform'],['../group___turbo_j_p_e_g.html#ga504805ec0161f1b505397ca0118bf8fd',1,'tjtransform(): turbojpeg.h'],['../group___turbo_j_p_e_g.html#ga9cb8abf4cc91881e04a0329b2270be25',1,'tjTransform(tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, int n, unsigned char **dstBufs, unsigned long *dstSizes, tjtransform *transforms, int flags): turbojpeg.h']]], - ['tjxop_91',['TJXOP',['../group___turbo_j_p_e_g.html#ga2de531af4e7e6c4f124908376b354866',1,'turbojpeg.h']]], - ['tjxop_5fhflip_92',['TJXOP_HFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aa0df69776caa30f0fa28e26332d311ce',1,'turbojpeg.h']]], - ['tjxop_5fnone_93',['TJXOP_NONE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aad88c0366cd3f7d0eac9d7a3fa1c2c27',1,'turbojpeg.h']]], - ['tjxop_5frot180_94',['TJXOP_ROT180',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a140952eb8dd0300accfcc22726d69692',1,'turbojpeg.h']]], - ['tjxop_5frot270_95',['TJXOP_ROT270',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a3064ee5dfb7f032df332818587567a08',1,'turbojpeg.h']]], - ['tjxop_5frot90_96',['TJXOP_ROT90',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a43b2bbb23bc4bd548422d43fbe9af128',1,'turbojpeg.h']]], - ['tjxop_5ftranspose_97',['TJXOP_TRANSPOSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a31060aed199f886afdd417f80499c32d',1,'turbojpeg.h']]], - ['tjxop_5ftransverse_98',['TJXOP_TRANSVERSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866af3b14d488aea6ece9e5b3df73a74d6a4',1,'turbojpeg.h']]], - ['tjxop_5fvflip_99',['TJXOP_VFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a324eddfbec53b7e691f61e56929d0d5d',1,'turbojpeg.h']]], - ['tjxopt_5fcopynone_100',['TJXOPT_COPYNONE',['../group___turbo_j_p_e_g.html#ga153b468cfb905d0de61706c838986fe8',1,'turbojpeg.h']]], - ['tjxopt_5fcrop_101',['TJXOPT_CROP',['../group___turbo_j_p_e_g.html#ga9c771a757fc1294add611906b89ab2d2',1,'turbojpeg.h']]], - ['tjxopt_5fgray_102',['TJXOPT_GRAY',['../group___turbo_j_p_e_g.html#ga3acee7b48ade1b99e5588736007c2589',1,'turbojpeg.h']]], - ['tjxopt_5fnooutput_103',['TJXOPT_NOOUTPUT',['../group___turbo_j_p_e_g.html#gafbf992bbf6e006705886333703ffab31',1,'turbojpeg.h']]], - ['tjxopt_5fperfect_104',['TJXOPT_PERFECT',['../group___turbo_j_p_e_g.html#ga50e03cb5ed115330e212417429600b00',1,'turbojpeg.h']]], - ['tjxopt_5fprogressive_105',['TJXOPT_PROGRESSIVE',['../group___turbo_j_p_e_g.html#gad2371c80674584ecc1a7d75e564cf026',1,'turbojpeg.h']]], - ['tjxopt_5ftrim_106',['TJXOPT_TRIM',['../group___turbo_j_p_e_g.html#ga319826b7eb1583c0595bbe7b95428709',1,'turbojpeg.h']]], - ['turbojpeg_107',['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_5f441_118',['TJSAMP_441',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a3351696e1dd34a083a35b6be8b90122d',1,'turbojpeg.h']]], + ['tjsamp_5f444_119',['TJSAMP_444',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074afb8da4f44197837bdec0a4f593dacae3',1,'turbojpeg.h']]], + ['tjsamp_5fgray_120',['TJSAMP_GRAY',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a3f1c9504842ddc7a48d0f690754b6248',1,'turbojpeg.h']]], + ['tjsamp_5funknown_121',['TJSAMP_UNKNOWN',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074ac124fa8f6cb41147e3d670dfbdfb7173',1,'turbojpeg.h']]], + ['tjscaled_122',['TJSCALED',['../group___turbo_j_p_e_g.html#ga84878bb65404204743aa18cac02781df',1,'turbojpeg.h']]], + ['tjscalingfactor_123',['tjscalingfactor',['../structtjscalingfactor.html',1,'']]], + ['tjtransform_124',['tjtransform',['../structtjtransform.html',1,'tjtransform'],['../group___turbo_j_p_e_g.html#ga504805ec0161f1b505397ca0118bf8fd',1,'tjtransform(): turbojpeg.h']]], + ['tjuncropped_125',['TJUNCROPPED',['../group___turbo_j_p_e_g.html#ga6f192ad58a5a5802e145149d83c643bf',1,'turbojpeg.h']]], + ['tjunscaled_126',['TJUNSCALED',['../group___turbo_j_p_e_g.html#ga7880644a0849161ad20933536169ee19',1,'turbojpeg.h']]], + ['tjxop_127',['TJXOP',['../group___turbo_j_p_e_g.html#ga2de531af4e7e6c4f124908376b354866',1,'turbojpeg.h']]], + ['tjxop_5fhflip_128',['TJXOP_HFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aa0df69776caa30f0fa28e26332d311ce',1,'turbojpeg.h']]], + ['tjxop_5fnone_129',['TJXOP_NONE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aad88c0366cd3f7d0eac9d7a3fa1c2c27',1,'turbojpeg.h']]], + ['tjxop_5frot180_130',['TJXOP_ROT180',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a140952eb8dd0300accfcc22726d69692',1,'turbojpeg.h']]], + ['tjxop_5frot270_131',['TJXOP_ROT270',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a3064ee5dfb7f032df332818587567a08',1,'turbojpeg.h']]], + ['tjxop_5frot90_132',['TJXOP_ROT90',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a43b2bbb23bc4bd548422d43fbe9af128',1,'turbojpeg.h']]], + ['tjxop_5ftranspose_133',['TJXOP_TRANSPOSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a31060aed199f886afdd417f80499c32d',1,'turbojpeg.h']]], + ['tjxop_5ftransverse_134',['TJXOP_TRANSVERSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866af3b14d488aea6ece9e5b3df73a74d6a4',1,'turbojpeg.h']]], + ['tjxop_5fvflip_135',['TJXOP_VFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a324eddfbec53b7e691f61e56929d0d5d',1,'turbojpeg.h']]], + ['tjxopt_5farithmetic_136',['TJXOPT_ARITHMETIC',['../group___turbo_j_p_e_g.html#gaecaaa3b7e2af812592c015d83207f010',1,'turbojpeg.h']]], + ['tjxopt_5fcopynone_137',['TJXOPT_COPYNONE',['../group___turbo_j_p_e_g.html#ga153b468cfb905d0de61706c838986fe8',1,'turbojpeg.h']]], + ['tjxopt_5fcrop_138',['TJXOPT_CROP',['../group___turbo_j_p_e_g.html#ga9c771a757fc1294add611906b89ab2d2',1,'turbojpeg.h']]], + ['tjxopt_5fgray_139',['TJXOPT_GRAY',['../group___turbo_j_p_e_g.html#ga3acee7b48ade1b99e5588736007c2589',1,'turbojpeg.h']]], + ['tjxopt_5fnooutput_140',['TJXOPT_NOOUTPUT',['../group___turbo_j_p_e_g.html#gafbf992bbf6e006705886333703ffab31',1,'turbojpeg.h']]], + ['tjxopt_5foptimize_141',['TJXOPT_OPTIMIZE',['../group___turbo_j_p_e_g.html#ga6bedf37aa9e1122f3ec9f7302ca59117',1,'turbojpeg.h']]], + ['tjxopt_5fperfect_142',['TJXOPT_PERFECT',['../group___turbo_j_p_e_g.html#ga50e03cb5ed115330e212417429600b00',1,'turbojpeg.h']]], + ['tjxopt_5fprogressive_143',['TJXOPT_PROGRESSIVE',['../group___turbo_j_p_e_g.html#gad2371c80674584ecc1a7d75e564cf026',1,'turbojpeg.h']]], + ['tjxopt_5ftrim_144',['TJXOPT_TRIM',['../group___turbo_j_p_e_g.html#ga319826b7eb1583c0595bbe7b95428709',1,'turbojpeg.h']]], + ['turbojpeg_145',['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 46e4994..7d2132b 100644 --- a/doc/html/search/all_7.js +++ b/doc/html/search/all_7.js @@ -1,4 +1,4 @@ var searchData= [ - ['w_108',['w',['../structtjregion.html#ab6eb73ceef584fc23c8c8097926dce42',1,'tjregion']]] + ['w_146',['w',['../structtjregion.html#ab6eb73ceef584fc23c8c8097926dce42',1,'tjregion']]] ]; diff --git a/doc/html/search/all_8.js b/doc/html/search/all_8.js index 157ee98..4ef2012 100644 --- a/doc/html/search/all_8.js +++ b/doc/html/search/all_8.js @@ -1,4 +1,4 @@ var searchData= [ - ['x_109',['x',['../structtjregion.html#a4b6a37a93997091b26a75831fa291ad9',1,'tjregion']]] + ['x_147',['x',['../structtjregion.html#a4b6a37a93997091b26a75831fa291ad9',1,'tjregion']]] ]; diff --git a/doc/html/search/all_9.js b/doc/html/search/all_9.js index 80ac522..3a72aaf 100644 --- a/doc/html/search/all_9.js +++ b/doc/html/search/all_9.js @@ -1,4 +1,4 @@ var searchData= [ - ['y_110',['y',['../structtjregion.html#a7b3e0c24cfe87acc80e334cafdcf22c2',1,'tjregion']]] + ['y_148',['y',['../structtjregion.html#a7b3e0c24cfe87acc80e334cafdcf22c2',1,'tjregion']]] ]; diff --git a/doc/html/search/classes_0.js b/doc/html/search/classes_0.js index ed057f2..ff3fb9a 100644 --- a/doc/html/search/classes_0.js +++ b/doc/html/search/classes_0.js @@ -1,6 +1,6 @@ var searchData= [ - ['tjregion_111',['tjregion',['../structtjregion.html',1,'']]], - ['tjscalingfactor_112',['tjscalingfactor',['../structtjscalingfactor.html',1,'']]], - ['tjtransform_113',['tjtransform',['../structtjtransform.html',1,'']]] + ['tjregion_149',['tjregion',['../structtjregion.html',1,'']]], + ['tjscalingfactor_150',['tjscalingfactor',['../structtjscalingfactor.html',1,'']]], + ['tjtransform_151',['tjtransform',['../structtjtransform.html',1,'']]] ]; diff --git a/doc/html/search/enums_0.js b/doc/html/search/enums_0.js index 0e15c9a..2f372f6 100644 --- a/doc/html/search/enums_0.js +++ b/doc/html/search/enums_0.js @@ -1,8 +1,10 @@ var searchData= [ - ['tjcs_162',['TJCS',['../group___turbo_j_p_e_g.html#ga4f83ad3368e0e29d1957be0efa7c3720',1,'turbojpeg.h']]], - ['tjerr_163',['TJERR',['../group___turbo_j_p_e_g.html#gafbc17cfa57d0d5d11fea35ac025950fe',1,'turbojpeg.h']]], - ['tjpf_164',['TJPF',['../group___turbo_j_p_e_g.html#gac916144e26c3817ac514e64ae5d12e2a',1,'turbojpeg.h']]], - ['tjsamp_165',['TJSAMP',['../group___turbo_j_p_e_g.html#ga1d047060ea80bb9820d540bb928e9074',1,'turbojpeg.h']]], - ['tjxop_166',['TJXOP',['../group___turbo_j_p_e_g.html#ga2de531af4e7e6c4f124908376b354866',1,'turbojpeg.h']]] + ['tjcs_212',['TJCS',['../group___turbo_j_p_e_g.html#ga4f83ad3368e0e29d1957be0efa7c3720',1,'turbojpeg.h']]], + ['tjerr_213',['TJERR',['../group___turbo_j_p_e_g.html#gafbc17cfa57d0d5d11fea35ac025950fe',1,'turbojpeg.h']]], + ['tjinit_214',['TJINIT',['../group___turbo_j_p_e_g.html#ga3850bbee1313e752e667b4eb08b1e086',1,'turbojpeg.h']]], + ['tjparam_215',['TJPARAM',['../group___turbo_j_p_e_g.html#gaa0f6be63ba78278299c9f5c12031fe82',1,'turbojpeg.h']]], + ['tjpf_216',['TJPF',['../group___turbo_j_p_e_g.html#gac916144e26c3817ac514e64ae5d12e2a',1,'turbojpeg.h']]], + ['tjsamp_217',['TJSAMP',['../group___turbo_j_p_e_g.html#ga1d047060ea80bb9820d540bb928e9074',1,'turbojpeg.h']]], + ['tjxop_218',['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 67c78fe..4baa519 100644 --- a/doc/html/search/enumvalues_0.js +++ b/doc/html/search/enumvalues_0.js @@ -1,37 +1,65 @@ var searchData= [ - ['tjcs_5fcmyk_167',['TJCS_CMYK',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a6c8b636152ac8195b869587db315ee53',1,'turbojpeg.h']]], - ['tjcs_5fgray_168',['TJCS_GRAY',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720ab3e7d6a87f695e45b81c1b5262b5a50a',1,'turbojpeg.h']]], - ['tjcs_5frgb_169',['TJCS_RGB',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a677cb7ccb85c4038ac41964a2e09e555',1,'turbojpeg.h']]], - ['tjcs_5fycbcr_170',['TJCS_YCbCr',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a7389b8f65bb387ffedce3efd0d78ec75',1,'turbojpeg.h']]], - ['tjcs_5fycck_171',['TJCS_YCCK',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a53839e0fe867b76b58d16b0a1a7c598e',1,'turbojpeg.h']]], - ['tjerr_5ffatal_172',['TJERR_FATAL',['../group___turbo_j_p_e_g.html#ggafbc17cfa57d0d5d11fea35ac025950feafc9cceeada13122b09e4851e3788039a',1,'turbojpeg.h']]], - ['tjerr_5fwarning_173',['TJERR_WARNING',['../group___turbo_j_p_e_g.html#ggafbc17cfa57d0d5d11fea35ac025950fea342dd6e2aedb47bb257b4e7568329b59',1,'turbojpeg.h']]], - ['tjpf_5fabgr_174',['TJPF_ABGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa1ba1a7f1631dbeaa49a0a85fc4a40081',1,'turbojpeg.h']]], - ['tjpf_5fargb_175',['TJPF_ARGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aae8f846ed9d9de99b6e1dfe448848765c',1,'turbojpeg.h']]], - ['tjpf_5fbgr_176',['TJPF_BGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aab10624437fb8ef495a0b153e65749839',1,'turbojpeg.h']]], - ['tjpf_5fbgra_177',['TJPF_BGRA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aac037ff1845cf9b74bb81a3659c2b9fb4',1,'turbojpeg.h']]], - ['tjpf_5fbgrx_178',['TJPF_BGRX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa2a1fbf569ca79897eae886e3376ca4c8',1,'turbojpeg.h']]], - ['tjpf_5fcmyk_179',['TJPF_CMYK',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7f5100ec44c91994e243f1cf55553f8b',1,'turbojpeg.h']]], - ['tjpf_5fgray_180',['TJPF_GRAY',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa5431b54b015337705f13118073711a1a',1,'turbojpeg.h']]], - ['tjpf_5frgb_181',['TJPF_RGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7ce93230bff449518ce387c17e6ed37c',1,'turbojpeg.h']]], - ['tjpf_5frgba_182',['TJPF_RGBA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa88d2e88fab67f6503cf972e14851cc12',1,'turbojpeg.h']]], - ['tjpf_5frgbx_183',['TJPF_RGBX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa83973bebb7e2dc6fa8bae89ff3f42e01',1,'turbojpeg.h']]], - ['tjpf_5funknown_184',['TJPF_UNKNOWN',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa84c1a6cead7952998e2fb895844a21ed',1,'turbojpeg.h']]], - ['tjpf_5fxbgr_185',['TJPF_XBGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aaf6603b27147de47e212e75dac027b2af',1,'turbojpeg.h']]], - ['tjpf_5fxrgb_186',['TJPF_XRGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aadae996905efcfa3b42a0bb3bea7f9d84',1,'turbojpeg.h']]], - ['tjsamp_5f411_187',['TJSAMP_411',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a28ec62575e5ea295c3fde3001dc628e2',1,'turbojpeg.h']]], - ['tjsamp_5f420_188',['TJSAMP_420',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a63085dbf683cfe39e513cdb6343e3737',1,'turbojpeg.h']]], - ['tjsamp_5f422_189',['TJSAMP_422',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a136130902cc578f11f32429b59368404',1,'turbojpeg.h']]], - ['tjsamp_5f440_190',['TJSAMP_440',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074accf740e6f3aa6ba20ba922cad13cb974',1,'turbojpeg.h']]], - ['tjsamp_5f444_191',['TJSAMP_444',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074afb8da4f44197837bdec0a4f593dacae3',1,'turbojpeg.h']]], - ['tjsamp_5fgray_192',['TJSAMP_GRAY',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a3f1c9504842ddc7a48d0f690754b6248',1,'turbojpeg.h']]], - ['tjxop_5fhflip_193',['TJXOP_HFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aa0df69776caa30f0fa28e26332d311ce',1,'turbojpeg.h']]], - ['tjxop_5fnone_194',['TJXOP_NONE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aad88c0366cd3f7d0eac9d7a3fa1c2c27',1,'turbojpeg.h']]], - ['tjxop_5frot180_195',['TJXOP_ROT180',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a140952eb8dd0300accfcc22726d69692',1,'turbojpeg.h']]], - ['tjxop_5frot270_196',['TJXOP_ROT270',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a3064ee5dfb7f032df332818587567a08',1,'turbojpeg.h']]], - ['tjxop_5frot90_197',['TJXOP_ROT90',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a43b2bbb23bc4bd548422d43fbe9af128',1,'turbojpeg.h']]], - ['tjxop_5ftranspose_198',['TJXOP_TRANSPOSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a31060aed199f886afdd417f80499c32d',1,'turbojpeg.h']]], - ['tjxop_5ftransverse_199',['TJXOP_TRANSVERSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866af3b14d488aea6ece9e5b3df73a74d6a4',1,'turbojpeg.h']]], - ['tjxop_5fvflip_200',['TJXOP_VFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a324eddfbec53b7e691f61e56929d0d5d',1,'turbojpeg.h']]] + ['tjcs_5fcmyk_219',['TJCS_CMYK',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a6c8b636152ac8195b869587db315ee53',1,'turbojpeg.h']]], + ['tjcs_5fgray_220',['TJCS_GRAY',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720ab3e7d6a87f695e45b81c1b5262b5a50a',1,'turbojpeg.h']]], + ['tjcs_5frgb_221',['TJCS_RGB',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a677cb7ccb85c4038ac41964a2e09e555',1,'turbojpeg.h']]], + ['tjcs_5fycbcr_222',['TJCS_YCbCr',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a7389b8f65bb387ffedce3efd0d78ec75',1,'turbojpeg.h']]], + ['tjcs_5fycck_223',['TJCS_YCCK',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a53839e0fe867b76b58d16b0a1a7c598e',1,'turbojpeg.h']]], + ['tjerr_5ffatal_224',['TJERR_FATAL',['../group___turbo_j_p_e_g.html#ggafbc17cfa57d0d5d11fea35ac025950feafc9cceeada13122b09e4851e3788039a',1,'turbojpeg.h']]], + ['tjerr_5fwarning_225',['TJERR_WARNING',['../group___turbo_j_p_e_g.html#ggafbc17cfa57d0d5d11fea35ac025950fea342dd6e2aedb47bb257b4e7568329b59',1,'turbojpeg.h']]], + ['tjinit_5fcompress_226',['TJINIT_COMPRESS',['../group___turbo_j_p_e_g.html#gga3850bbee1313e752e667b4eb08b1e086aa45ac279e3dc6ffabc4b0f45864da796',1,'turbojpeg.h']]], + ['tjinit_5fdecompress_227',['TJINIT_DECOMPRESS',['../group___turbo_j_p_e_g.html#gga3850bbee1313e752e667b4eb08b1e086a4b8ca1ef700699b71350700bf95c2167',1,'turbojpeg.h']]], + ['tjinit_5ftransform_228',['TJINIT_TRANSFORM',['../group___turbo_j_p_e_g.html#gga3850bbee1313e752e667b4eb08b1e086a8d58a2a4c45b3e0cd349746544a6e0c2',1,'turbojpeg.h']]], + ['tjparam_5farithmetic_229',['TJPARAM_ARITHMETIC',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a1c756757384308145602c040524aebf7',1,'turbojpeg.h']]], + ['tjparam_5fbottomup_230',['TJPARAM_BOTTOMUP',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a924657172695ed6cb0b128219546fcce',1,'turbojpeg.h']]], + ['tjparam_5fcolorspace_231',['TJPARAM_COLORSPACE',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a46a10d46309514907d0c39fcd86c324c',1,'turbojpeg.h']]], + ['tjparam_5fdensityunits_232',['TJPARAM_DENSITYUNITS',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a4c045981bd8a303521a401dbbe1df208',1,'turbojpeg.h']]], + ['tjparam_5ffastdct_233',['TJPARAM_FASTDCT',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a6914692ac6ec5567787d592b7563f627',1,'turbojpeg.h']]], + ['tjparam_5ffastupsample_234',['TJPARAM_FASTUPSAMPLE',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a0e051ac106f7b7402b690a5daf4869c0',1,'turbojpeg.h']]], + ['tjparam_5fjpegheight_235',['TJPARAM_JPEGHEIGHT',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a8f76673be73f2b659440a9572a65a95f',1,'turbojpeg.h']]], + ['tjparam_5fjpegwidth_236',['TJPARAM_JPEGWIDTH',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a02ab77fb294a0c9061a78cd424c82dd8',1,'turbojpeg.h']]], + ['tjparam_5flossless_237',['TJPARAM_LOSSLESS',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a249f35f0770792b19f995e603bb17c6f',1,'turbojpeg.h']]], + ['tjparam_5flosslesspsv_238',['TJPARAM_LOSSLESSPSV',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82abcc997d40e5bec84817c12b76ef84159',1,'turbojpeg.h']]], + ['tjparam_5flosslesspt_239',['TJPARAM_LOSSLESSPT',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a4a6c6f25764ecaf4231a36bff844e46a',1,'turbojpeg.h']]], + ['tjparam_5fnorealloc_240',['TJPARAM_NOREALLOC',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82ae64ffb358bc7b194fd48e0f27750b29b',1,'turbojpeg.h']]], + ['tjparam_5foptimize_241',['TJPARAM_OPTIMIZE',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a8f0af9afc0b36443751f9ee82b760aa6',1,'turbojpeg.h']]], + ['tjparam_5fprecision_242',['TJPARAM_PRECISION',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a781db82741934e8cd008d308597c59d8',1,'turbojpeg.h']]], + ['tjparam_5fprogressive_243',['TJPARAM_PROGRESSIVE',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a1716f242b3859905b4a317dae8cfb75f',1,'turbojpeg.h']]], + ['tjparam_5fquality_244',['TJPARAM_QUALITY',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a0467e8792621f2d817dc2af563d3186c',1,'turbojpeg.h']]], + ['tjparam_5frestartblocks_245',['TJPARAM_RESTARTBLOCKS',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a343c72883b7160f23f3ef46fc548a0ec',1,'turbojpeg.h']]], + ['tjparam_5frestartrows_246',['TJPARAM_RESTARTROWS',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a714367585952fe5c863f0dba5bd37e5c',1,'turbojpeg.h']]], + ['tjparam_5fscanlimit_247',['TJPARAM_SCANLIMIT',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82ac478910e20ecf61b914f9824d80f8167',1,'turbojpeg.h']]], + ['tjparam_5fstoponwarning_248',['TJPARAM_STOPONWARNING',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a555e2212079fa49b30bcd2879c6c8ddb',1,'turbojpeg.h']]], + ['tjparam_5fsubsamp_249',['TJPARAM_SUBSAMP',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a2a3494a8215d3de4fdbaeb2ba6f6b03a',1,'turbojpeg.h']]], + ['tjparam_5fxdensity_250',['TJPARAM_XDENSITY',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82a4de5c9d7cab5be806143a43c3b0e0877',1,'turbojpeg.h']]], + ['tjparam_5fydensity_251',['TJPARAM_YDENSITY',['../group___turbo_j_p_e_g.html#ggaa0f6be63ba78278299c9f5c12031fe82abda48f2df7eb9b88e2b7621efb017eba',1,'turbojpeg.h']]], + ['tjpf_5fabgr_252',['TJPF_ABGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa1ba1a7f1631dbeaa49a0a85fc4a40081',1,'turbojpeg.h']]], + ['tjpf_5fargb_253',['TJPF_ARGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aae8f846ed9d9de99b6e1dfe448848765c',1,'turbojpeg.h']]], + ['tjpf_5fbgr_254',['TJPF_BGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aab10624437fb8ef495a0b153e65749839',1,'turbojpeg.h']]], + ['tjpf_5fbgra_255',['TJPF_BGRA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aac037ff1845cf9b74bb81a3659c2b9fb4',1,'turbojpeg.h']]], + ['tjpf_5fbgrx_256',['TJPF_BGRX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa2a1fbf569ca79897eae886e3376ca4c8',1,'turbojpeg.h']]], + ['tjpf_5fcmyk_257',['TJPF_CMYK',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7f5100ec44c91994e243f1cf55553f8b',1,'turbojpeg.h']]], + ['tjpf_5fgray_258',['TJPF_GRAY',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa5431b54b015337705f13118073711a1a',1,'turbojpeg.h']]], + ['tjpf_5frgb_259',['TJPF_RGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7ce93230bff449518ce387c17e6ed37c',1,'turbojpeg.h']]], + ['tjpf_5frgba_260',['TJPF_RGBA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa88d2e88fab67f6503cf972e14851cc12',1,'turbojpeg.h']]], + ['tjpf_5frgbx_261',['TJPF_RGBX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa83973bebb7e2dc6fa8bae89ff3f42e01',1,'turbojpeg.h']]], + ['tjpf_5funknown_262',['TJPF_UNKNOWN',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa84c1a6cead7952998e2fb895844a21ed',1,'turbojpeg.h']]], + ['tjpf_5fxbgr_263',['TJPF_XBGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aaf6603b27147de47e212e75dac027b2af',1,'turbojpeg.h']]], + ['tjpf_5fxrgb_264',['TJPF_XRGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aadae996905efcfa3b42a0bb3bea7f9d84',1,'turbojpeg.h']]], + ['tjsamp_5f411_265',['TJSAMP_411',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a28ec62575e5ea295c3fde3001dc628e2',1,'turbojpeg.h']]], + ['tjsamp_5f420_266',['TJSAMP_420',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a63085dbf683cfe39e513cdb6343e3737',1,'turbojpeg.h']]], + ['tjsamp_5f422_267',['TJSAMP_422',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a136130902cc578f11f32429b59368404',1,'turbojpeg.h']]], + ['tjsamp_5f440_268',['TJSAMP_440',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074accf740e6f3aa6ba20ba922cad13cb974',1,'turbojpeg.h']]], + ['tjsamp_5f441_269',['TJSAMP_441',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a3351696e1dd34a083a35b6be8b90122d',1,'turbojpeg.h']]], + ['tjsamp_5f444_270',['TJSAMP_444',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074afb8da4f44197837bdec0a4f593dacae3',1,'turbojpeg.h']]], + ['tjsamp_5fgray_271',['TJSAMP_GRAY',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a3f1c9504842ddc7a48d0f690754b6248',1,'turbojpeg.h']]], + ['tjsamp_5funknown_272',['TJSAMP_UNKNOWN',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074ac124fa8f6cb41147e3d670dfbdfb7173',1,'turbojpeg.h']]], + ['tjxop_5fhflip_273',['TJXOP_HFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aa0df69776caa30f0fa28e26332d311ce',1,'turbojpeg.h']]], + ['tjxop_5fnone_274',['TJXOP_NONE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aad88c0366cd3f7d0eac9d7a3fa1c2c27',1,'turbojpeg.h']]], + ['tjxop_5frot180_275',['TJXOP_ROT180',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a140952eb8dd0300accfcc22726d69692',1,'turbojpeg.h']]], + ['tjxop_5frot270_276',['TJXOP_ROT270',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a3064ee5dfb7f032df332818587567a08',1,'turbojpeg.h']]], + ['tjxop_5frot90_277',['TJXOP_ROT90',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a43b2bbb23bc4bd548422d43fbe9af128',1,'turbojpeg.h']]], + ['tjxop_5ftranspose_278',['TJXOP_TRANSPOSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866a31060aed199f886afdd417f80499c32d',1,'turbojpeg.h']]], + ['tjxop_5ftransverse_279',['TJXOP_TRANSVERSE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866af3b14d488aea6ece9e5b3df73a74d6a4',1,'turbojpeg.h']]], + ['tjxop_5fvflip_280',['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 4a9ea5b..18f7e3f 100644 --- a/doc/html/search/functions_0.js +++ b/doc/html/search/functions_0.js @@ -1,31 +1,41 @@ var searchData= [ - ['tjalloc_114',['tjAlloc',['../group___turbo_j_p_e_g.html#gaec627dd4c5f30b7a775a7aea3bec5d83',1,'turbojpeg.h']]], - ['tjbufsize_115',['tjBufSize',['../group___turbo_j_p_e_g.html#ga67ac12fee79073242cb216e07c9f1f90',1,'turbojpeg.h']]], - ['tjbufsizeyuv2_116',['tjBufSizeYUV2',['../group___turbo_j_p_e_g.html#ga2be2b9969d4df9ecce9b05deed273194',1,'turbojpeg.h']]], - ['tjcompress2_117',['tjCompress2',['../group___turbo_j_p_e_g.html#gafbdce0112fd78fd38efae841443a9bcf',1,'turbojpeg.h']]], - ['tjcompressfromyuv_118',['tjCompressFromYUV',['../group___turbo_j_p_e_g.html#ga7622a459b79aa1007e005b58783f875b',1,'turbojpeg.h']]], - ['tjcompressfromyuvplanes_119',['tjCompressFromYUVPlanes',['../group___turbo_j_p_e_g.html#ga29ec5dfbd2d84b8724e951d6fa0d5d9e',1,'turbojpeg.h']]], - ['tjdecodeyuv_120',['tjDecodeYUV',['../group___turbo_j_p_e_g.html#ga70abbf38f77a26fd6da8813bef96f695',1,'turbojpeg.h']]], - ['tjdecodeyuvplanes_121',['tjDecodeYUVPlanes',['../group___turbo_j_p_e_g.html#ga10e837c07fa9d25770565b237d3898d9',1,'turbojpeg.h']]], - ['tjdecompress2_122',['tjDecompress2',['../group___turbo_j_p_e_g.html#gae9eccef8b682a48f43a9117c231ed013',1,'turbojpeg.h']]], - ['tjdecompressheader3_123',['tjDecompressHeader3',['../group___turbo_j_p_e_g.html#ga0595681096bba7199cc6f3533cb25f77',1,'turbojpeg.h']]], - ['tjdecompresstoyuv2_124',['tjDecompressToYUV2',['../group___turbo_j_p_e_g.html#ga04d1e839ff9a0860dd1475cff78d3364',1,'turbojpeg.h']]], - ['tjdecompresstoyuvplanes_125',['tjDecompressToYUVPlanes',['../group___turbo_j_p_e_g.html#gaa59f901a5258ada5bd0185ad59368540',1,'turbojpeg.h']]], - ['tjdestroy_126',['tjDestroy',['../group___turbo_j_p_e_g.html#ga75f355fa27225ba1a4ee392c852394d2',1,'turbojpeg.h']]], - ['tjencodeyuv3_127',['tjEncodeYUV3',['../group___turbo_j_p_e_g.html#gac519b922cdf446e97d0cdcba513636bf',1,'turbojpeg.h']]], - ['tjencodeyuvplanes_128',['tjEncodeYUVPlanes',['../group___turbo_j_p_e_g.html#gae2d04c72457fe7f4d60cf78ab1b1feb1',1,'turbojpeg.h']]], - ['tjfree_129',['tjFree',['../group___turbo_j_p_e_g.html#gaea863d2da0cdb609563aabdf9196514b',1,'turbojpeg.h']]], - ['tjgeterrorcode_130',['tjGetErrorCode',['../group___turbo_j_p_e_g.html#ga414feeffbf860ebd31c745df203de410',1,'turbojpeg.h']]], - ['tjgeterrorstr2_131',['tjGetErrorStr2',['../group___turbo_j_p_e_g.html#ga1ead8574f9f39fbafc6b497124e7aafa',1,'turbojpeg.h']]], - ['tjgetscalingfactors_132',['tjGetScalingFactors',['../group___turbo_j_p_e_g.html#gac3854476006b10787bd128f7ede48057',1,'turbojpeg.h']]], - ['tjinitcompress_133',['tjInitCompress',['../group___turbo_j_p_e_g.html#ga9d63a05fc6d813f4aae06107041a37e8',1,'turbojpeg.h']]], - ['tjinitdecompress_134',['tjInitDecompress',['../group___turbo_j_p_e_g.html#ga52300eac3f3d9ef4bab303bc244f62d3',1,'turbojpeg.h']]], - ['tjinittransform_135',['tjInitTransform',['../group___turbo_j_p_e_g.html#ga928beff6ac248ceadf01089fc6b41957',1,'turbojpeg.h']]], - ['tjloadimage_136',['tjLoadImage',['../group___turbo_j_p_e_g.html#gaffbd83c375e79f5db4b5c5d8ad4466e7',1,'turbojpeg.h']]], - ['tjplaneheight_137',['tjPlaneHeight',['../group___turbo_j_p_e_g.html#ga1a209696c6a80748f20e134b3c64789f',1,'turbojpeg.h']]], - ['tjplanesizeyuv_138',['tjPlaneSizeYUV',['../group___turbo_j_p_e_g.html#gab4ab7b24f6e797d79abaaa670373961d',1,'turbojpeg.h']]], - ['tjplanewidth_139',['tjPlaneWidth',['../group___turbo_j_p_e_g.html#ga63fb66bb1e36c74008c4634360becbb1',1,'turbojpeg.h']]], - ['tjsaveimage_140',['tjSaveImage',['../group___turbo_j_p_e_g.html#ga6f445b22d8933ae4815b3370a538d879',1,'turbojpeg.h']]], - ['tjtransform_141',['tjTransform',['../group___turbo_j_p_e_g.html#ga9cb8abf4cc91881e04a0329b2270be25',1,'turbojpeg.h']]] + ['tj3alloc_152',['tj3Alloc',['../group___turbo_j_p_e_g.html#gab40a0b231122f536e503e3394569a68d',1,'turbojpeg.h']]], + ['tj3compress12_153',['tj3Compress12',['../group___turbo_j_p_e_g.html#ga9a1968c384ec7abb6122830253ebf570',1,'turbojpeg.h']]], + ['tj3compress16_154',['tj3Compress16',['../group___turbo_j_p_e_g.html#ga77901b71d0471784f318ada31ff4e7bd',1,'turbojpeg.h']]], + ['tj3compress8_155',['tj3Compress8',['../group___turbo_j_p_e_g.html#ga2cc418a2dab709ad7f30f5b25905f138',1,'turbojpeg.h']]], + ['tj3compressfromyuv8_156',['tj3CompressFromYUV8',['../group___turbo_j_p_e_g.html#ga041c870d9c669eb3f385c78f4346c43f',1,'turbojpeg.h']]], + ['tj3compressfromyuvplanes8_157',['tj3CompressFromYUVPlanes8',['../group___turbo_j_p_e_g.html#gac9f5ace3e73805b476c95dda9f8d0cd0',1,'turbojpeg.h']]], + ['tj3decodeyuv8_158',['tj3DecodeYUV8',['../group___turbo_j_p_e_g.html#gaa1eb574f38b1c1de43a6c7aafcf68d8c',1,'turbojpeg.h']]], + ['tj3decodeyuvplanes8_159',['tj3DecodeYUVPlanes8',['../group___turbo_j_p_e_g.html#gad366f1915f82c1ad4e7e37ebe073ca89',1,'turbojpeg.h']]], + ['tj3decompress12_160',['tj3Decompress12',['../group___turbo_j_p_e_g.html#ga39b848f01781ad74a5b3941c012b6199',1,'turbojpeg.h']]], + ['tj3decompress16_161',['tj3Decompress16',['../group___turbo_j_p_e_g.html#gaa074e63f9beb0b3ff42b833a4049df6e',1,'turbojpeg.h']]], + ['tj3decompress8_162',['tj3Decompress8',['../group___turbo_j_p_e_g.html#ga1169c7c1a26ec18c9e6122cb8ae64013',1,'turbojpeg.h']]], + ['tj3decompressheader_163',['tj3DecompressHeader',['../group___turbo_j_p_e_g.html#ga96d2c4b3432f9d88ad14758ae240b8d1',1,'turbojpeg.h']]], + ['tj3decompresstoyuv8_164',['tj3DecompressToYUV8',['../group___turbo_j_p_e_g.html#ga1e6bf6a19fec3f9fa7534348879d8320',1,'turbojpeg.h']]], + ['tj3decompresstoyuvplanes8_165',['tj3DecompressToYUVPlanes8',['../group___turbo_j_p_e_g.html#ga934373482dbbf257f2280505b6ff4fb5',1,'turbojpeg.h']]], + ['tj3destroy_166',['tj3Destroy',['../group___turbo_j_p_e_g.html#ga53fbadf4560e95a65b8f5ab81703fe82',1,'turbojpeg.h']]], + ['tj3encodeyuv8_167',['tj3EncodeYUV8',['../group___turbo_j_p_e_g.html#ga2a8d50f130bde10f0a04030f8cc59936',1,'turbojpeg.h']]], + ['tj3encodeyuvplanes8_168',['tj3EncodeYUVPlanes8',['../group___turbo_j_p_e_g.html#gae2e9df38790e9bddc249d04cb158a4cf',1,'turbojpeg.h']]], + ['tj3free_169',['tj3Free',['../group___turbo_j_p_e_g.html#gaddb84fb6c81769e9faa0f5a63b296606',1,'turbojpeg.h']]], + ['tj3get_170',['tj3Get',['../group___turbo_j_p_e_g.html#ga34af9ba3183bdf0ec7c8f47bb9a4c84f',1,'turbojpeg.h']]], + ['tj3geterrorcode_171',['tj3GetErrorCode',['../group___turbo_j_p_e_g.html#gab8c8279f1415fe425ff30dbbc56013bd',1,'turbojpeg.h']]], + ['tj3geterrorstr_172',['tj3GetErrorStr',['../group___turbo_j_p_e_g.html#gaf2aab0e6dbb3edc57646b0fec25e8bb2',1,'turbojpeg.h']]], + ['tj3getscalingfactors_173',['tj3GetScalingFactors',['../group___turbo_j_p_e_g.html#ga74397f8e0587d4233182c72f085aaf04',1,'turbojpeg.h']]], + ['tj3init_174',['tj3Init',['../group___turbo_j_p_e_g.html#ga69c09d39f97ec30250ad3605ace7e5df',1,'turbojpeg.h']]], + ['tj3jpegbufsize_175',['tj3JPEGBufSize',['../group___turbo_j_p_e_g.html#gac6285e58e35a35d871d7162ec5a929c4',1,'turbojpeg.h']]], + ['tj3loadimage12_176',['tj3LoadImage12',['../group___turbo_j_p_e_g.html#ga1f03c26892a26d4ce077ed6a4ac40e8f',1,'turbojpeg.h']]], + ['tj3loadimage16_177',['tj3LoadImage16',['../group___turbo_j_p_e_g.html#ga638aeba63e0ccb89d472fdbf34224cfc',1,'turbojpeg.h']]], + ['tj3loadimage8_178',['tj3LoadImage8',['../group___turbo_j_p_e_g.html#ga565aaae7be3f8ca9099b56655c893251',1,'turbojpeg.h']]], + ['tj3saveimage12_179',['tj3SaveImage12',['../group___turbo_j_p_e_g.html#ga7c64b5106d04267a46aad85f9714ad90',1,'turbojpeg.h']]], + ['tj3saveimage16_180',['tj3SaveImage16',['../group___turbo_j_p_e_g.html#ga0fd87851f4266aca24bf4594dd0c0e71',1,'turbojpeg.h']]], + ['tj3saveimage8_181',['tj3SaveImage8',['../group___turbo_j_p_e_g.html#gaa4ec838988e469cc15618e4690cc8722',1,'turbojpeg.h']]], + ['tj3set_182',['tj3Set',['../group___turbo_j_p_e_g.html#gaddf92640bfee3e8622218c713e77e7db',1,'turbojpeg.h']]], + ['tj3setcroppingregion_183',['tj3SetCroppingRegion',['../group___turbo_j_p_e_g.html#gaa49c7bd4c9431667a043cfc93388ba1c',1,'turbojpeg.h']]], + ['tj3setscalingfactor_184',['tj3SetScalingFactor',['../group___turbo_j_p_e_g.html#ga89da17ee1e43ff423382cbc145803c75',1,'turbojpeg.h']]], + ['tj3transform_185',['tj3Transform',['../group___turbo_j_p_e_g.html#gaff23ba1dcabed456794b844791613920',1,'turbojpeg.h']]], + ['tj3yuvbufsize_186',['tj3YUVBufSize',['../group___turbo_j_p_e_g.html#gaaebaa16973a0f550a66eca5765ed0546',1,'turbojpeg.h']]], + ['tj3yuvplaneheight_187',['tj3YUVPlaneHeight',['../group___turbo_j_p_e_g.html#ga969767ec8180cc3edd99cf507f87299b',1,'turbojpeg.h']]], + ['tj3yuvplanesize_188',['tj3YUVPlaneSize',['../group___turbo_j_p_e_g.html#gacc19d265edce76b46146f59579f9438d',1,'turbojpeg.h']]], + ['tj3yuvplanewidth_189',['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 b4e000d..97bded0 100644 --- a/doc/html/search/groups_0.js +++ b/doc/html/search/groups_0.js @@ -1,4 +1,4 @@ var searchData= [ - ['turbojpeg_201',['TurboJPEG',['../group___turbo_j_p_e_g.html',1,'']]] + ['turbojpeg_281',['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 bad1a20..968490f 100644 --- a/doc/html/search/typedefs_0.js +++ b/doc/html/search/typedefs_0.js @@ -1,5 +1,5 @@ var searchData= [ - ['tjhandle_160',['tjhandle',['../group___turbo_j_p_e_g.html#ga758d2634ecb4949de7815cba621f5763',1,'turbojpeg.h']]], - ['tjtransform_161',['tjtransform',['../group___turbo_j_p_e_g.html#ga504805ec0161f1b505397ca0118bf8fd',1,'turbojpeg.h']]] + ['tjhandle_210',['tjhandle',['../group___turbo_j_p_e_g.html#ga758d2634ecb4949de7815cba621f5763',1,'turbojpeg.h']]], + ['tjtransform_211',['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 9ed036c..40a95a8 100644 --- a/doc/html/search/variables_0.js +++ b/doc/html/search/variables_0.js @@ -1,4 +1,4 @@ var searchData= [ - ['customfilter_142',['customFilter',['../structtjtransform.html#afd7fc262df33f741e120ef4183202ef5',1,'tjtransform']]] + ['customfilter_190',['customFilter',['../structtjtransform.html#a0dc7697d59a7abe48afc629e96cbc1d2',1,'tjtransform']]] ]; diff --git a/doc/html/search/variables_1.js b/doc/html/search/variables_1.js index 3dc2108..9113a24 100644 --- a/doc/html/search/variables_1.js +++ b/doc/html/search/variables_1.js @@ -1,5 +1,5 @@ var searchData= [ - ['data_143',['data',['../structtjtransform.html#a688fe8f1a8ecc12a538d9e561cf338e3',1,'tjtransform']]], - ['denom_144',['denom',['../structtjscalingfactor.html#aefbcdf3e9e62274b2d312c695f133ce3',1,'tjscalingfactor']]] + ['data_191',['data',['../structtjtransform.html#a688fe8f1a8ecc12a538d9e561cf338e3',1,'tjtransform']]], + ['denom_192',['denom',['../structtjscalingfactor.html#aefbcdf3e9e62274b2d312c695f133ce3',1,'tjscalingfactor']]] ]; diff --git a/doc/html/search/variables_2.js b/doc/html/search/variables_2.js index f0d8327..a907b81 100644 --- a/doc/html/search/variables_2.js +++ b/doc/html/search/variables_2.js @@ -1,4 +1,4 @@ var searchData= [ - ['h_145',['h',['../structtjregion.html#aecefc45a26f4d8b60dd4d825c1710115',1,'tjregion']]] + ['h_193',['h',['../structtjregion.html#aecefc45a26f4d8b60dd4d825c1710115',1,'tjregion']]] ]; diff --git a/doc/html/search/variables_3.js b/doc/html/search/variables_3.js index e719258..7b5a616 100644 --- a/doc/html/search/variables_3.js +++ b/doc/html/search/variables_3.js @@ -1,4 +1,4 @@ var searchData= [ - ['num_146',['num',['../structtjscalingfactor.html#a9b011e57f981ee23083e2c1aa5e640ec',1,'tjscalingfactor']]] + ['num_194',['num',['../structtjscalingfactor.html#a9b011e57f981ee23083e2c1aa5e640ec',1,'tjscalingfactor']]] ]; diff --git a/doc/html/search/variables_4.js b/doc/html/search/variables_4.js index 2650623..c2bfaa8 100644 --- a/doc/html/search/variables_4.js +++ b/doc/html/search/variables_4.js @@ -1,5 +1,5 @@ var searchData= [ - ['op_147',['op',['../structtjtransform.html#a2525aab4ba6978a1c273f74fef50e498',1,'tjtransform']]], - ['options_148',['options',['../structtjtransform.html#ac0e74655baa4402209a21e1ae481c8f6',1,'tjtransform']]] + ['op_195',['op',['../structtjtransform.html#a2525aab4ba6978a1c273f74fef50e498',1,'tjtransform']]], + ['options_196',['options',['../structtjtransform.html#ac0e74655baa4402209a21e1ae481c8f6',1,'tjtransform']]] ]; diff --git a/doc/html/search/variables_5.js b/doc/html/search/variables_5.js index 2639dfd..0ae92ea 100644 --- a/doc/html/search/variables_5.js +++ b/doc/html/search/variables_5.js @@ -1,4 +1,4 @@ var searchData= [ - ['r_149',['r',['../structtjtransform.html#ac324e5e442abec8a961e5bf219db12cf',1,'tjtransform']]] + ['r_197',['r',['../structtjtransform.html#ac324e5e442abec8a961e5bf219db12cf',1,'tjtransform']]] ]; diff --git a/doc/html/search/variables_6.js b/doc/html/search/variables_6.js index 50b3fd7..2e7c0b3 100644 --- a/doc/html/search/variables_6.js +++ b/doc/html/search/variables_6.js @@ -1,10 +1,12 @@ var searchData= [ - ['tjalphaoffset_150',['tjAlphaOffset',['../group___turbo_j_p_e_g.html#ga5af0ab065feefd526debf1e20c43e837',1,'turbojpeg.h']]], - ['tjblueoffset_151',['tjBlueOffset',['../group___turbo_j_p_e_g.html#ga84e2e35d3f08025f976ec1ec53693dea',1,'turbojpeg.h']]], - ['tjgreenoffset_152',['tjGreenOffset',['../group___turbo_j_p_e_g.html#ga82d6e35da441112a411da41923c0ba2f',1,'turbojpeg.h']]], - ['tjmcuheight_153',['tjMCUHeight',['../group___turbo_j_p_e_g.html#gabd247bb9fecb393eca57366feb8327bf',1,'turbojpeg.h']]], - ['tjmcuwidth_154',['tjMCUWidth',['../group___turbo_j_p_e_g.html#ga9e61e7cd47a15a173283ba94e781308c',1,'turbojpeg.h']]], - ['tjpixelsize_155',['tjPixelSize',['../group___turbo_j_p_e_g.html#gad77cf8fe5b2bfd3cb3f53098146abb4c',1,'turbojpeg.h']]], - ['tjredoffset_156',['tjRedOffset',['../group___turbo_j_p_e_g.html#gadd9b446742ac8a3923f7992c7988fea8',1,'turbojpeg.h']]] + ['tjalphaoffset_198',['tjAlphaOffset',['../group___turbo_j_p_e_g.html#ga5af0ab065feefd526debf1e20c43e837',1,'turbojpeg.h']]], + ['tjblueoffset_199',['tjBlueOffset',['../group___turbo_j_p_e_g.html#ga84e2e35d3f08025f976ec1ec53693dea',1,'turbojpeg.h']]], + ['tjgreenoffset_200',['tjGreenOffset',['../group___turbo_j_p_e_g.html#ga82d6e35da441112a411da41923c0ba2f',1,'turbojpeg.h']]], + ['tjmcuheight_201',['tjMCUHeight',['../group___turbo_j_p_e_g.html#gabd247bb9fecb393eca57366feb8327bf',1,'turbojpeg.h']]], + ['tjmcuwidth_202',['tjMCUWidth',['../group___turbo_j_p_e_g.html#ga9e61e7cd47a15a173283ba94e781308c',1,'turbojpeg.h']]], + ['tjpixelsize_203',['tjPixelSize',['../group___turbo_j_p_e_g.html#gad77cf8fe5b2bfd3cb3f53098146abb4c',1,'turbojpeg.h']]], + ['tjredoffset_204',['tjRedOffset',['../group___turbo_j_p_e_g.html#gadd9b446742ac8a3923f7992c7988fea8',1,'turbojpeg.h']]], + ['tjuncropped_205',['TJUNCROPPED',['../group___turbo_j_p_e_g.html#ga6f192ad58a5a5802e145149d83c643bf',1,'turbojpeg.h']]], + ['tjunscaled_206',['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 cd4a680..c8c66b6 100644 --- a/doc/html/search/variables_7.js +++ b/doc/html/search/variables_7.js @@ -1,4 +1,4 @@ var searchData= [ - ['w_157',['w',['../structtjregion.html#ab6eb73ceef584fc23c8c8097926dce42',1,'tjregion']]] + ['w_207',['w',['../structtjregion.html#ab6eb73ceef584fc23c8c8097926dce42',1,'tjregion']]] ]; diff --git a/doc/html/search/variables_8.js b/doc/html/search/variables_8.js index 61434f0..5f81e56 100644 --- a/doc/html/search/variables_8.js +++ b/doc/html/search/variables_8.js @@ -1,4 +1,4 @@ var searchData= [ - ['x_158',['x',['../structtjregion.html#a4b6a37a93997091b26a75831fa291ad9',1,'tjregion']]] + ['x_208',['x',['../structtjregion.html#a4b6a37a93997091b26a75831fa291ad9',1,'tjregion']]] ]; diff --git a/doc/html/search/variables_9.js b/doc/html/search/variables_9.js index 32719d4..bb57e20 100644 --- a/doc/html/search/variables_9.js +++ b/doc/html/search/variables_9.js @@ -1,4 +1,4 @@ var searchData= [ - ['y_159',['y',['../structtjregion.html#a7b3e0c24cfe87acc80e334cafdcf22c2',1,'tjregion']]] + ['y_209',['y',['../structtjregion.html#a7b3e0c24cfe87acc80e334cafdcf22c2',1,'tjregion']]] ]; diff --git a/doc/html/structtjregion.html b/doc/html/structtjregion.html index 72d49d2..e678584 100644 --- a/doc/html/structtjregion.html +++ b/doc/html/structtjregion.html @@ -23,7 +23,7 @@
    TurboJPEG -  2.1.4 +  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 1606a02..818dfbd 100644 --- a/doc/html/structtjscalingfactor.html +++ b/doc/html/structtjscalingfactor.html @@ -23,7 +23,7 @@
    TurboJPEG -  2.1.4 +  3
    diff --git a/doc/html/structtjtransform.html b/doc/html/structtjtransform.html index ba78980..9facd85 100644 --- a/doc/html/structtjtransform.html +++ b/doc/html/structtjtransform.html @@ -23,7 +23,7 @@
    TurboJPEG -  2.1.4 +  3
    @@ -84,26 +84,26 @@ Data Fields  One of the transform operations. More...
      int options - The bitwise OR of one of more of the transform options. More...
    + The bitwise OR of one of more of the transform options. More...
      void * data  Arbitrary data that can be accessed within the body of the callback function. More...
      -int(* customFilter )(short *coeffs, tjregion arrayRegion, tjregion planeRegion, int componentIndex, int transformIndex, struct tjtransform *transform) - A callback function that can be used to modify the DCT coefficients after they are losslessly transformed but before they are transcoded to a new JPEG image. More...
    -  +int(* customFilter )(short *coeffs, tjregion arrayRegion, tjregion planeRegion, int componentID, int transformID, struct tjtransform *transform) + A callback function that can be used to modify the DCT coefficients after they are losslessly transformed but before they are transcoded to a new JPEG image. More...

    Detailed Description

    Lossless transform.

    Field Documentation

    - -

    ◆ customFilter

    + +

    ◆ customFilter

    - +
    int(* tjtransform::customFilter) (short *coeffs, tjregion arrayRegion, tjregion planeRegion, int componentIndex, int transformIndex, struct tjtransform *transform)int(* tjtransform::customFilter) (short *coeffs, tjregion arrayRegion, tjregion planeRegion, int componentID, int transformID, struct tjtransform *transform)
    @@ -115,8 +115,8 @@ Data Fields coeffspointer to an array of transformed DCT coefficients. (NOTE: this pointer is not guaranteed to be valid once the callback returns, so applications wishing to hand off the DCT coefficients to another function or library should make a copy of them within the body of the callback.) arrayRegiontjregion structure containing the width and height of the array pointed to by coeffs as well as its offset relative to the component plane. TurboJPEG implementations may choose to split each component plane into multiple DCT coefficient arrays and call the callback function once for each array. planeRegiontjregion structure containing the width and height of the component plane to which coeffs belongs - componentIDID number of the component plane to which coeffs belongs (Y, Cb, and Cr have, respectively, ID's of 0, 1, and 2 in typical JPEG images.) - 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(). + 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 tj3Transform(). transforma pointer to a tjtransform structure that specifies the parameters and/or cropping region for this transform @@ -169,7 +169,7 @@ Data Fields
    -

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

    +

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

    diff --git a/doxygen.config b/doxygen.config index 16708b0..1309a05 100644 --- a/doxygen.config +++ b/doxygen.config @@ -1,5 +1,5 @@ PROJECT_NAME = TurboJPEG -PROJECT_NUMBER = 2.1.4 +PROJECT_NUMBER = 3 OUTPUT_DIRECTORY = doc/ USE_WINDOWS_ENCODING = NO OPTIMIZE_OUTPUT_FOR_C = YES diff --git a/example.txt b/example.c similarity index 59% rename from example.txt rename to example.c index d473aed..4a82695 100644 --- a/example.txt +++ b/example.c @@ -1,33 +1,41 @@ /* - * example.txt + * example.c * - * This file illustrates how to use the IJG code as a subroutine library - * to read or write JPEG image files. You should look at this code in - * conjunction with the documentation file libjpeg.txt. + * This file was part of the Independent JPEG Group's software. + * Copyright (C) 1992-1996, Thomas G. Lane. + * libjpeg-turbo Modifications: + * Copyright (C) 2017, 2019, 2022-2023, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * - * This code will not do anything useful as-is, but it may be helpful as a - * skeleton for constructing routines that call the JPEG library. + * This file illustrates how to use the IJG code as a subroutine library + * to read or write JPEG image files with 8-bit or 12-bit data precision. You + * should look at this code in conjunction with the documentation file + * libjpeg.txt. * * We present these routines in the same coding style used in the JPEG code * (ANSI function definitions, etc); but you are of course free to code your * routines in a different style if you prefer. */ -/* This example was part of the original libjpeg documentation and has been - * unchanged since 1994. It is, as described in libjpeg.txt, "heavily - * commented skeleton code for calling the JPEG library." It is not meant to - * be compiled as a standalone program, since it has no main() function and - * does not compress from/decompress to a real image buffer (corollary: - * put_scanline_someplace() is not a real function.) First-time users of - * libjpeg-turbo would be better served by looking at tjexample.c, which uses - * the more straightforward TurboJPEG API, or at cjpeg.c and djpeg.c, which are - * examples of libjpeg API usage that can be (and are) compiled into standalone - * programs. Note that this example, as well as the examples in cjpeg.c and - * djpeg.c, interleave disk I/O with JPEG compression/decompression, so none of - * these examples is suitable for benchmarking purposes. +/* First-time users of libjpeg-turbo might be better served by looking at + * tjexample.c, which uses the more straightforward TurboJPEG API. Note that + * this example, like cjpeg and djpeg, interleaves disk I/O with JPEG + * compression/decompression, so it is not suitable for benchmarking purposes. */ +#ifdef _MSC_VER +#define _CRT_SECURE_NO_DEPRECATE +#endif + #include +#include +#include + +#ifdef _WIN32 +#define strcasecmp stricmp +#define strncasecmp strnicmp +#endif /* * Include file for users of JPEG library. @@ -38,6 +46,7 @@ */ #include "jpeglib.h" +#include "jerror.h" /* * is used for the optional error recovery mechanism shown in @@ -61,10 +70,10 @@ * * The standard input image format is a rectangular array of pixels, with * each pixel having the same number of "component" values (color channels). - * Each pixel row is an array of JSAMPLEs (which typically are unsigned chars). - * If you are working with color data, then the color values for each pixel - * must be adjacent in the row; for example, R,G,B,R,G,B,R,G,B,... for 24-bit - * RGB color. + * Each pixel row is an array of JSAMPLEs (which typically are unsigned chars) + * or J12SAMPLEs (which typically are shorts). If you are working with color + * data, then the color values for each pixel must be adjacent in the row; for + * example, R,G,B,R,G,B,R,G,B,... for 24-bit RGB color. * * For this example, we'll assume that this data structure matches the way * our application has stored the image in memory, so we can just pass a @@ -72,18 +81,17 @@ * RGB color and is described by: */ -extern JSAMPLE *image_buffer; /* Points to large array of R,G,B-order data */ -extern int image_height; /* Number of rows in image */ -extern int image_width; /* Number of columns in image */ +#define WIDTH 640 /* Number of columns in image */ +#define HEIGHT 480 /* Number of rows in image */ /* - * Sample routine for JPEG compression. We assume that the target file name - * and a compression quality factor are passed in. + * Sample routine for JPEG compression. We assume that the target file name, + * a compression quality factor, and a data precision are passed in. */ -GLOBAL(void) -write_JPEG_file(char *filename, int quality) +METHODDEF(void) +write_JPEG_file(char *filename, int quality, int data_precision) { /* This struct contains the JPEG compression parameters and pointers to * working space (which is allocated as needed by the JPEG library). @@ -103,8 +111,15 @@ write_JPEG_file(char *filename, int quality) struct jpeg_error_mgr jerr; /* More stuff */ FILE *outfile; /* target file */ + JSAMPARRAY image_buffer = NULL; + /* Points to large array of R,G,B-order data */ JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ + J12SAMPARRAY image_buffer12 = NULL; + /* Points to large array of R,G,B-order 12-bit + data */ + J12SAMPROW row_pointer12[1]; /* pointer to J12SAMPLE row[s] */ int row_stride; /* physical row width in image buffer */ + int row, col; /* Step 1: allocate and initialize JPEG compression object */ @@ -125,10 +140,8 @@ write_JPEG_file(char *filename, int quality) * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that * requires it in order to write binary files. */ - if ((outfile = fopen(filename, "wb")) == NULL) { - fprintf(stderr, "can't open %s\n", filename); - exit(1); - } + if ((outfile = fopen(filename, "wb")) == NULL) + ERREXIT(&cinfo, JERR_FILE_WRITE); jpeg_stdio_dest(&cinfo, outfile); /* Step 3: set parameters for compression */ @@ -136,10 +149,11 @@ write_JPEG_file(char *filename, int quality) /* First we supply a description of the input image. * Four fields of the cinfo struct must be filled in: */ - cinfo.image_width = image_width; /* image width and height, in pixels */ - cinfo.image_height = image_height; + cinfo.image_width = WIDTH; /* image width and height, in pixels */ + cinfo.image_height = HEIGHT; cinfo.input_components = 3; /* # of color components per pixel */ cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ + cinfo.data_precision = data_precision; /* data precision of input image */ /* Now use the library's routine to set default compression parameters. * (You must set at least cinfo.in_color_space before calling this, * since the defaults depend on the source color space.) @@ -149,6 +163,8 @@ write_JPEG_file(char *filename, int quality) * Here we just illustrate the use of quality (quantization table) scaling: */ jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); + /* Use 4:4:4 subsampling (default is 4:2:0) */ + cinfo.comp_info[0].h_samp_factor = cinfo.comp_info[0].v_samp_factor = 1; /* Step 4: Start compressor */ @@ -157,7 +173,48 @@ write_JPEG_file(char *filename, int quality) */ jpeg_start_compress(&cinfo, TRUE); - /* Step 5: while (scan lines remain to be written) */ + /* Step 5: allocate and initialize image buffer */ + + row_stride = WIDTH * 3; /* J[12]SAMPLEs per row in image_buffer */ + /* Make a sample array that will go away when done with image. Note that, + * for the purposes of this example, we could also create a one-row-high + * sample array and initialize it for each successive scanline written in the + * scanline loop below. + */ + if (cinfo.data_precision == 12) { + image_buffer12 = (J12SAMPARRAY)(*cinfo.mem->alloc_sarray) + ((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, HEIGHT); + + /* Initialize image buffer with a repeating pattern */ + for (row = 0; row < HEIGHT; row++) { + for (col = 0; col < WIDTH; col++) { + image_buffer12[row][col * 3] = + (col * (MAXJ12SAMPLE + 1) / WIDTH) % (MAXJ12SAMPLE + 1); + image_buffer12[row][col * 3 + 1] = + (row * (MAXJ12SAMPLE + 1) / HEIGHT) % (MAXJ12SAMPLE + 1); + image_buffer12[row][col * 3 + 2] = + (row * (MAXJ12SAMPLE + 1) / HEIGHT + + col * (MAXJ12SAMPLE + 1) / WIDTH) % (MAXJ12SAMPLE + 1); + } + } + } else { + image_buffer = (*cinfo.mem->alloc_sarray) + ((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, HEIGHT); + + for (row = 0; row < HEIGHT; row++) { + for (col = 0; col < WIDTH; col++) { + image_buffer[row][col * 3] = + (col * (MAXJSAMPLE + 1) / WIDTH) % (MAXJSAMPLE + 1); + image_buffer[row][col * 3 + 1] = + (row * (MAXJSAMPLE + 1) / HEIGHT) % (MAXJSAMPLE + 1); + image_buffer[row][col * 3 + 2] = + (row * (MAXJSAMPLE + 1) / HEIGHT + col * (MAXJSAMPLE + 1) / WIDTH) % + (MAXJSAMPLE + 1); + } + } + } + + /* Step 6: while (scan lines remain to be written) */ /* jpeg_write_scanlines(...); */ /* Here we use the library's state variable cinfo.next_scanline as the @@ -165,24 +222,33 @@ write_JPEG_file(char *filename, int quality) * To keep things simple, we pass one scanline per call; you can pass * more if you wish, though. */ - row_stride = image_width * 3; /* JSAMPLEs per row in image_buffer */ - - while (cinfo.next_scanline < cinfo.image_height) { - /* jpeg_write_scanlines expects an array of pointers to scanlines. - * Here the array is only one element long, but you could pass - * more than one scanline at a time if that's more convenient. - */ - row_pointer[0] = &image_buffer[cinfo.next_scanline * row_stride]; - (void)jpeg_write_scanlines(&cinfo, row_pointer, 1); + if (cinfo.data_precision == 12) { + while (cinfo.next_scanline < cinfo.image_height) { + /* jpeg12_write_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could pass + * more than one scanline at a time if that's more convenient. + */ + row_pointer12[0] = image_buffer12[cinfo.next_scanline]; + (void)jpeg12_write_scanlines(&cinfo, row_pointer12, 1); + } + } else { + while (cinfo.next_scanline < cinfo.image_height) { + /* jpeg_write_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could pass + * more than one scanline at a time if that's more convenient. + */ + row_pointer[0] = image_buffer[cinfo.next_scanline]; + (void)jpeg_write_scanlines(&cinfo, row_pointer, 1); + } } - /* Step 6: Finish compression */ + /* Step 7: Finish compression */ jpeg_finish_compress(&cinfo); /* After finish_compress, we can close the output file. */ fclose(outfile); - /* Step 7: release JPEG compression object */ + /* Step 8: release JPEG compression object */ /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_compress(&cinfo); @@ -231,10 +297,11 @@ write_JPEG_file(char *filename, int quality) * Just to make this example a little different from the first one, we'll * assume that we do not intend to put the whole image into an in-memory * buffer, but to send it line-by-line someplace else. We need a one- - * scanline-high JSAMPLE array as a work buffer, and we will let the JPEG - * memory manager allocate it for us. This approach is actually quite useful - * because we don't need to remember to deallocate the buffer separately: it - * will go away automatically when the JPEG object is cleaned up. + * scanline-high JSAMPLE or J12SAMPLE array as a work buffer, and we will let + * the JPEG memory manager allocate it for us. This approach is actually quite + * useful because we don't need to remember to deallocate the buffer + * separately: it will go away automatically when the JPEG object is cleaned + * up. */ @@ -289,22 +356,22 @@ my_error_exit(j_common_ptr cinfo) METHODDEF(int) do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, - char *filename); + char *infilename, char *outfilename); /* * Sample routine for JPEG decompression. We assume that the source file name * is passed in. We want to return 1 on success, 0 on error. */ -GLOBAL(int) -read_JPEG_file(char *filename) +METHODDEF(int) +read_JPEG_file(char *infilename, char *outfilename) { /* This struct contains the JPEG decompression parameters and pointers to * working space (which is allocated as needed by the JPEG library). */ struct jpeg_decompress_struct cinfo; - return do_read_JPEG_file(&cinfo, filename); + return do_read_JPEG_file(&cinfo, infilename, outfilename); } /* @@ -316,7 +383,8 @@ read_JPEG_file(char *filename) */ METHODDEF(int) -do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, char *filename) +do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, char *infilename, + char *outfilename) { /* We use our private extension JPEG error handler. * Note that this struct must live as long as the main JPEG parameter @@ -325,17 +393,28 @@ do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, char *filename) struct my_error_mgr jerr; /* More stuff */ FILE *infile; /* source file */ - JSAMPARRAY buffer; /* Output row buffer */ + FILE *outfile; /* output file */ + JSAMPARRAY buffer = NULL; /* Output row buffer */ + J12SAMPARRAY buffer12 = NULL; /* 12-bit output row buffer */ + int col; int row_stride; /* physical row width in output buffer */ + int little_endian = 1; - /* In this example we want to open the input file before doing anything else, - * so that the setjmp() error recovery below can assume the file is open. + /* In this example we want to open the input and output files before doing + * anything else, so that the setjmp() error recovery below can assume the + * files are open. + * * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that - * requires it in order to read binary files. + * requires it in order to read/write binary files. */ - if ((infile = fopen(filename, "rb")) == NULL) { - fprintf(stderr, "can't open %s\n", filename); + if ((infile = fopen(infilename, "rb")) == NULL) { + fprintf(stderr, "can't open %s\n", infilename); + return 0; + } + if ((outfile = fopen(outfilename, "wb")) == NULL) { + fprintf(stderr, "can't open %s\n", outfilename); + fclose(infile); return 0; } @@ -351,6 +430,7 @@ do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, char *filename) */ jpeg_destroy_decompress(cinfo); fclose(infile); + fclose(outfile); return 0; } /* Now we can initialize the JPEG decompression object. */ @@ -369,6 +449,10 @@ do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, char *filename) * See libjpeg.txt for more info. */ + /* emit header for raw PPM format */ + fprintf(outfile, "P6\n%d %d\n%d\n", WIDTH, HEIGHT, + cinfo->data_precision == 12 ? MAXJ12SAMPLE : MAXJSAMPLE); + /* Step 4: set parameters for decompression */ /* In this example, we don't need to change any of the defaults set by @@ -388,11 +472,15 @@ do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, char *filename) * if we asked for color quantization. * In this example, we need to make an output work buffer of the right size. */ - /* JSAMPLEs per row in output buffer */ + /* Samples per row in output buffer */ row_stride = cinfo->output_width * cinfo->output_components; /* Make a one-row-high sample array that will go away when done with image */ - buffer = (*cinfo->mem->alloc_sarray) - ((j_common_ptr)cinfo, JPOOL_IMAGE, row_stride, 1); + if (cinfo->data_precision == 12) + buffer12 = (J12SAMPARRAY)(*cinfo->mem->alloc_sarray) + ((j_common_ptr)cinfo, JPOOL_IMAGE, row_stride, 1); + else + buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr)cinfo, JPOOL_IMAGE, row_stride, 1); /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ @@ -400,14 +488,30 @@ do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, char *filename) /* Here we use the library's state variable cinfo->output_scanline as the * loop counter, so that we don't have to keep track ourselves. */ - while (cinfo->output_scanline < cinfo->output_height) { - /* jpeg_read_scanlines expects an array of pointers to scanlines. - * Here the array is only one element long, but you could ask for - * more than one scanline at a time if that's more convenient. - */ - (void)jpeg_read_scanlines(cinfo, buffer, 1); - /* Assume put_scanline_someplace wants a pointer and sample count. */ - put_scanline_someplace(buffer[0], row_stride); + if (cinfo->data_precision == 12) { + while (cinfo->output_scanline < cinfo->output_height) { + /* jpeg12_read_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could ask for + * more than one scanline at a time if that's more convenient. + */ + (void)jpeg12_read_scanlines(cinfo, buffer12, 1); + if (*(char *)&little_endian == 1) { + /* Swap MSB and LSB in each sample */ + for (col = 0; col < row_stride; col++) + buffer12[0][col] = ((buffer12[0][col] & 0xFF) << 8) | + ((buffer12[0][col] >> 8) & 0xFF); + } + fwrite(buffer12[0], 1, row_stride * sizeof(J12SAMPLE), outfile); + } + } else { + while (cinfo->output_scanline < cinfo->output_height) { + /* jpeg_read_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could ask for + * more than one scanline at a time if that's more convenient. + */ + (void)jpeg_read_scanlines(cinfo, buffer, 1); + fwrite(buffer[0], 1, row_stride, outfile); + } } /* Step 7: Finish decompression */ @@ -422,12 +526,13 @@ do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, char *filename) /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_decompress(cinfo); - /* After finish_decompress, we can close the input file. + /* After finish_decompress, we can close the input and output files. * Here we postpone it until after no more JPEG errors are possible, * so as to simplify the setjmp error logic above. (Actually, I don't * think that jpeg_destroy can do an error exit, but why assume anything...) */ fclose(infile); + fclose(outfile); /* At this point you may want to check to see whether any corrupt-data * warnings occurred (test whether jerr.pub.num_warnings is nonzero). @@ -462,3 +567,88 @@ do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, char *filename) * On some systems you may need to set up a signal handler to ensure that * temporary files are deleted if the program is interrupted. See libjpeg.txt. */ + + +LOCAL(void) +usage(const char *progname) +{ + fprintf(stderr, "usage: %s compress [switches] outputfile[.jpg]\n", + progname); + fprintf(stderr, " %s decompress inputfile[.jpg] outputfile[.ppm]\n", + progname); + fprintf(stderr, "Switches (names may be abbreviated):\n"); + fprintf(stderr, " -precision N Create JPEG file with N-bit data precision\n"); + fprintf(stderr, " (N is 8 or 12; default is 8)\n"); + fprintf(stderr, " -quality N Compression quality (0..100; 5-95 is most useful range,\n"); + fprintf(stderr, " default is 75)\n"); + + exit(EXIT_FAILURE); +} + + +typedef enum { + COMPRESS, + DECOMPRESS +} EXAMPLE_MODE; + + +int +main(int argc, char **argv) +{ + int argn, quality = 75; + int data_precision = 8; + EXAMPLE_MODE mode = -1; + char *arg, *filename = NULL; + + if (argc < 3) + usage(argv[0]); + + if (!strcasecmp(argv[1], "compress")) + mode = COMPRESS; + else if (!strcasecmp(argv[1], "decompress")) + mode = DECOMPRESS; + else + usage(argv[0]); + + for (argn = 2; argn < argc; argn++) { + arg = argv[argn]; + if (*arg != '-') { + filename = arg; + /* Not a switch, must be a file name argument */ + break; /* done parsing switches */ + } + arg++; /* advance past switch marker character */ + + if (!strncasecmp(arg, "p", 1)) { + /* Set data precision. */ + if (++argn >= argc) /* advance to next argument */ + usage(argv[0]); + if (sscanf(argv[argn], "%d", &data_precision) < 1 || + (data_precision != 8 && data_precision != 12)) + usage(argv[0]); + } else if (!strncasecmp(arg, "q", 1)) { + /* Quality rating (quantization table scaling factor). */ + if (++argn >= argc) /* advance to next argument */ + usage(argv[0]); + if (sscanf(argv[argn], "%d", &quality) < 1 || quality < 0 || + quality > 100) + usage(argv[0]); + if (quality < 1) + quality = 1; + } + } + + if (!filename) + usage(argv[0]); + + if (mode == COMPRESS) + write_JPEG_file(filename, quality, data_precision); + else if (mode == DECOMPRESS) { + if (argc - argn < 2) + usage(argv[0]); + + read_JPEG_file(argv[argn], argv[argn + 1]); + } + + return 0; +} diff --git a/fuzz/CMakeLists.txt b/fuzz/CMakeLists.txt index 9f044c6..a08cb46 100644 --- a/fuzz/CMakeLists.txt +++ b/fuzz/CMakeLists.txt @@ -45,6 +45,14 @@ add_fuzz_target(compress compress.cc) 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 7033022..d87cbdf 100644 --- a/fuzz/build.sh +++ b/fuzz/build.sh @@ -20,6 +20,10 @@ 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/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/compress.cc b/fuzz/compress.cc index 539932f..f59f66d 100644 --- a/fuzz/compress.cc +++ b/fuzz/compress.cc @@ -1,5 +1,5 @@ /* - * Copyright (C)2021 D. R. Commander. All Rights Reserved. + * Copyright (C)2021, 2023 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -35,8 +35,6 @@ #define NUMTESTS 7 -/* Private flag that triggers different TurboJPEG API behavior when fuzzing */ -#define TJFLAG_FUZZING (1 << 30) struct test { @@ -73,37 +71,40 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) if ((fd = mkstemp(filename)) < 0 || write(fd, data, size) < 0) goto bailout; - if ((handle = tjInitCompress()) == NULL) + if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL) goto bailout; for (ti = 0; ti < NUMTESTS; ti++) { - int flags = TJFLAG_FUZZING, sum = 0, pf = tests[ti].pf; - unsigned long dstSize = 0, maxBufSize; + int sum = 0, pf = tests[ti].pf; + size_t dstSize = 0, maxBufSize; /* Test non-default compression options on specific iterations. */ - if (ti == 0) - flags |= TJFLAG_BOTTOMUP | TJFLAG_ACCURATEDCT; - else if (ti == 1) - flags |= TJFLAG_PROGRESSIVE; - if (ti != 2) - flags |= TJFLAG_NOREALLOC; - - /* tjLoadImage() refuses to load images larger than 1 Megapixel when - FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION is defined (yes, that's a dirty - hack), so we don't need to check the width and height here. */ - if ((srcBuf = tjLoadImage(filename, &width, 1, &height, &pf, - flags)) == NULL) + tj3Set(handle, TJPARAM_BOTTOMUP, ti == 0); + tj3Set(handle, TJPARAM_FASTDCT, ti == 1); + tj3Set(handle, TJPARAM_OPTIMIZE, ti == 6); + tj3Set(handle, TJPARAM_PROGRESSIVE, ti == 1 || ti == 3); + tj3Set(handle, TJPARAM_ARITHMETIC, ti == 2 || ti == 3); + tj3Set(handle, TJPARAM_NOREALLOC, ti != 2); + tj3Set(handle, TJPARAM_RESTARTROWS, ti == 1 || ti == 2 ? 2 : 0); + + tj3Set(handle, TJPARAM_MAXPIXELS, 1048576); + /* tj3LoadImage8() will refuse to load images larger than 1 Megapixel, so + we don't need to check the width and height here. */ + if ((srcBuf = tj3LoadImage8(handle, filename, &width, 1, &height, + &pf)) == NULL) continue; - maxBufSize = tjBufSize(width, height, tests[ti].subsamp); - if (flags & TJFLAG_NOREALLOC) { + maxBufSize = tj3JPEGBufSize(width, height, tests[ti].subsamp); + if (tj3Get(handle, TJPARAM_NOREALLOC)) { if ((dstBuf = (unsigned char *)malloc(maxBufSize)) == NULL) goto bailout; } else dstBuf = NULL; - if (tjCompress2(handle, srcBuf, width, 0, height, pf, &dstBuf, &dstSize, - tests[ti].subsamp, tests[ti].quality, flags) == 0) { + tj3Set(handle, TJPARAM_SUBSAMP, tests[ti].subsamp); + tj3Set(handle, TJPARAM_QUALITY, tests[ti].quality); + if (tj3Compress8(handle, srcBuf, width, 0, height, pf, &dstBuf, + &dstSize) == 0) { /* Touch all of the output pixels in order to catch uninitialized reads when using MemorySanitizer. */ for (i = 0; i < dstSize; i++) @@ -112,7 +113,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) free(dstBuf); dstBuf = NULL; - tjFree(srcBuf); + tj3Free(srcBuf); srcBuf = NULL; /* Prevent the code above from being optimized out. This test should never @@ -123,11 +124,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) bailout: free(dstBuf); - tjFree(srcBuf); + tj3Free(srcBuf); if (fd >= 0) { close(fd); if (strlen(filename) > 0) unlink(filename); } - if (handle) tjDestroy(handle); + tj3Destroy(handle); return 0; } diff --git a/fuzz/compress12.cc b/fuzz/compress12.cc new file mode 100644 index 0000000..8a08299 --- /dev/null +++ b/fuzz/compress12.cc @@ -0,0 +1,134 @@ +/* + * 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_ARITHMETIC, ti == 2 || ti == 3); + tj3Set(handle, TJPARAM_NOREALLOC, ti != 2); + tj3Set(handle, TJPARAM_RESTARTROWS, ti == 1 || ti == 2 ? 2 : 0); + + tj3Set(handle, TJPARAM_MAXPIXELS, 1048576); + /* 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 0000000..76a0a0d --- /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 0000000..aa6037e --- /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 new file mode 100644 index 0000000..7c2bb79 --- /dev/null +++ b/fuzz/compress_lossless.cc @@ -0,0 +1,130 @@ +/* + * 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 char *srcBuf = NULL, *dstBuf = NULL; + int width = 0, height = 0, fd = -1, i, ti; + char filename[FILENAME_MAX] = { 0 }; + struct test tests[NUMTESTS] = { + { TJPF_RGB, 1, 0 }, + { TJPF_BGR, 2, 2 }, + { TJPF_RGBX, 3, 4 }, + { TJPF_BGRA, 4, 7 }, + { TJPF_XRGB, 5, 5 }, + { TJPF_GRAY, 6, 3 }, + { TJPF_CMYK, 7, 0 } + }; +#if defined(__has_feature) && __has_feature(memory_sanitizer) + char env[18] = "JSIMD_FORCENONE=1"; + + /* The libjpeg-turbo SIMD extensions produce false positives with + MemorySanitizer. */ + putenv(env); +#endif + + snprintf(filename, FILENAME_MAX, "/tmp/libjpeg-turbo_compress_fuzz.XXXXXX"); + if ((fd = mkstemp(filename)) < 0 || write(fd, data, size) < 0) + goto bailout; + + if ((handle = 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); + /* 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 = 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 (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++) + 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_yuv.cc b/fuzz/compress_yuv.cc index 021d661..0b12e0c 100644 --- a/fuzz/compress_yuv.cc +++ b/fuzz/compress_yuv.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 6 -/* Private flag that triggers different TurboJPEG API behavior when fuzzing */ -#define TJFLAG_FUZZING (1 << 30) struct test { @@ -60,62 +58,54 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { TJPF_BGR, TJSAMP_GRAY, 60 }, { TJPF_GRAY, TJSAMP_GRAY, 50 } }; - char arithEnv[16] = "TJ_ARITHMETIC=0"; - 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(arithEnv); - 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) - arithEnv[14] = '1'; - else - arithEnv[14] = '0'; - if (ti == 1 || ti == 2) - restartEnv[11] = '2'; - else - restartEnv[11] = '0'; - - /* tjLoadImage() refuses to load images larger than 1 Megapixel when - FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION is defined (yes, that's a dirty - hack), so we don't need to check the width and height here. */ - if ((srcBuf = tjLoadImage(filename, &width, 1, &height, &pf, - flags)) == NULL) + tj3Set(handle, TJPARAM_BOTTOMUP, ti == 0); + tj3Set(handle, TJPARAM_FASTDCT, ti == 1); + tj3Set(handle, TJPARAM_OPTIMIZE, ti == 4); + tj3Set(handle, TJPARAM_PROGRESSIVE, ti == 1 || ti == 3); + tj3Set(handle, TJPARAM_ARITHMETIC, ti == 2 || ti == 3); + tj3Set(handle, TJPARAM_NOREALLOC, 1); + tj3Set(handle, TJPARAM_RESTARTBLOCKS, ti == 3 || ti == 4 ? 4 : 0); + + tj3Set(handle, TJPARAM_MAXPIXELS, 1048576); + /* tj3LoadImage8() will refuse to load images larger than 1 Megapixel, so + we don't need to check the width and height here. */ + if ((srcBuf = tj3LoadImage8(handle, filename, &width, 1, &height, + &pf)) == NULL) continue; - maxBufSize = tjBufSize(width, height, tests[ti].subsamp); + maxBufSize = tj3JPEGBufSize(width, height, tests[ti].subsamp); if ((dstBuf = (unsigned char *)malloc(maxBufSize)) == NULL) goto bailout; if ((yuvBuf = - (unsigned char *)malloc(tjBufSizeYUV2(width, 1, height, + (unsigned char *)malloc(tj3YUVBufSize(width, 1, height, tests[ti].subsamp))) == NULL) goto bailout; - if (tjEncodeYUV3(handle, srcBuf, width, 0, height, pf, yuvBuf, 1, - tests[ti].subsamp, flags) == 0 && - tjCompressFromYUV(handle, yuvBuf, width, 1, height, tests[ti].subsamp, - &dstBuf, &dstSize, tests[ti].quality, flags) == 0) { + tj3Set(handle, TJPARAM_SUBSAMP, tests[ti].subsamp); + tj3Set(handle, TJPARAM_QUALITY, tests[ti].quality); + if (tj3EncodeYUV8(handle, srcBuf, width, 0, height, pf, yuvBuf, 1) == 0 && + tj3CompressFromYUV8(handle, yuvBuf, width, 1, height, &dstBuf, + &dstSize) == 0) { /* Touch all of the output pixels in order to catch uninitialized reads when using MemorySanitizer. */ for (i = 0; i < dstSize; i++) @@ -126,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 @@ -138,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 c7fcb50..eadc734 100644 --- a/fuzz/decompress.cc +++ b/fuzz/decompress.cc @@ -1,5 +1,5 @@ /* - * Copyright (C)2021 D. R. Commander. All Rights Reserved. + * Copyright (C)2021-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, 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,17 @@ 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. */ - tjDecompressHeader3(handle, data, size, &width, &height, &jpegSubsamp, - &jpegColorspace); + /* We ignore the return value of tj3DecompressHeader(), because malformed + JPEG images that might expose issues in libjpeg-turbo might also have + header errors that cause tj3DecompressHeader() to fail. */ + tj3DecompressHeader(handle, data, size); + width = tj3Get(handle, TJPARAM_JPEGWIDTH); + height = tj3Get(handle, TJPARAM_JPEGHEIGHT); + precision = tj3Get(handle, TJPARAM_PRECISION); + 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 +70,67 @@ 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) { - w = (width + 1) / 2; - h = (height + 1) / 2; + tj3Set(handle, TJPARAM_BOTTOMUP, pfi == 0); + tj3Set(handle, TJPARAM_FASTUPSAMPLE, pfi == 0); + + if (!tj3Get(handle, TJPARAM_LOSSLESS)) { + tj3Set(handle, TJPARAM_FASTDCT, pfi == 0); + + /* Test IDCT scaling on the second iteration. */ + if (pfi == 1) { + tjscalingfactor sf = { 1, 2 }; + tj3SetScalingFactor(handle, sf); + w = TJSCALED(width, sf); + h = TJSCALED(height, sf); + } else + tj3SetScalingFactor(handle, TJUNSCALED); + + /* Test partial image decompression on the fourth iteration, if the image + is large enough. */ + if (pfi == 3 && w >= 97 && h >= 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(w * h * 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 < w * h * tjPixelSize[pf]; i++) + sum += ((unsigned char *)dstBuf)[i]; + } else + goto bailout; + } else if (precision == 12) { + if (tj3Decompress12(handle, data, size, (short *)dstBuf, 0, pf) == 0) { + /* Touch all of the output pixels in order to catch uninitialized reads + when using MemorySanitizer. */ + for (i = 0; i < w * h * tjPixelSize[pf]; i++) + sum += ((short *)dstBuf)[i]; + } else + goto bailout; + } else { + if (tj3Decompress16(handle, data, size, (unsigned short *)dstBuf, 0, + pf) == 0) { + /* 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 += ((unsigned short *)dstBuf)[i]; + } else + goto bailout; } free(dstBuf); @@ -95,12 +138,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 d603fd8..4e869df 100644 --- a/fuzz/decompress_yuv.cc +++ b/fuzz/decompress_yuv.cc @@ -1,5 +1,5 @@ /* - * Copyright (C)2021 D. R. Commander. All Rights Reserved. + * Copyright (C)2021-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, 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,45 +52,58 @@ 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 (tjDecompressHeader3(handle, data, size, &width, &height, &jpegSubsamp, - &jpegColorspace) < 0) - goto bailout; + /* We ignore the return value of tj3DecompressHeader(), because malformed + JPEG images that might expose issues in libjpeg-turbo might also have + header errors that cause tj3DecompressHeader() to fail. */ + tj3DecompressHeader(handle, data, size); + width = tj3Get(handle, TJPARAM_JPEGWIDTH); + height = tj3Get(handle, TJPARAM_JPEGHEIGHT); + jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP); /* 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) { - 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++) sum += dstBuf[i]; - } + } else + goto bailout; free(dstBuf); dstBuf = NULL; @@ -106,6 +119,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 b8a7516..8d2e6ac 100644 --- a/fuzz/transform.cc +++ b/fuzz/transform.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: @@ -32,16 +32,13 @@ #include -#define NUMXFORMS 3 - - extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { tjhandle handle = NULL; - unsigned char *dstBufs[NUMXFORMS] = { NULL, NULL, NULL }; - unsigned long dstSizes[NUMXFORMS] = { 0, 0, 0 }, maxBufSize; - int width = 0, height = 0, jpegSubsamp, jpegColorspace, i, t; - tjtransform transforms[NUMXFORMS]; + unsigned char *dstBufs[1] = { NULL }; + size_t dstSizes[1] = { 0 }, maxBufSize; + int width = 0, height = 0, jpegSubsamp, i; + tjtransform transforms[1]; #if defined(__has_feature) && __has_feature(memory_sanitizer) char env[18] = "JSIMD_FORCENONE=1"; @@ -50,74 +47,118 @@ 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); + /* We ignore the return value of tj3DecompressHeader(), because malformed + JPEG images that might expose issues in libjpeg-turbo might also have + header errors that cause tj3DecompressHeader() to fail. */ + tj3DecompressHeader(handle, data, size); + width = tj3Get(handle, TJPARAM_JPEGWIDTH); + height = tj3Get(handle, TJPARAM_JPEGHEIGHT); + jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP); + /* Let the transform options dictate the entropy coding algorithm. */ + tj3Set(handle, TJPARAM_ARITHMETIC, 0); + tj3Set(handle, TJPARAM_PROGRESSIVE, 0); + tj3Set(handle, TJPARAM_OPTIMIZE, 0); /* Ignore 0-pixel images and images larger than 1 Megapixel. Casting width to (uint64_t) prevents integer overflow if width * height > INT_MAX. */ 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; - for (t = 0; t < NUMXFORMS; t++) - memset(&transforms[t], 0, sizeof(tjtransform)); + memset(&transforms[0], 0, sizeof(tjtransform)); transforms[0].op = TJXOP_NONE; transforms[0].options = TJXOPT_PROGRESSIVE | TJXOPT_COPYNONE; - dstBufs[0] = (unsigned char *)malloc(tjBufSize(width, height, jpegSubsamp)); + dstBufs[0] = + (unsigned char *)malloc(tj3JPEGBufSize(width, height, jpegSubsamp)); if (!dstBufs[0]) goto bailout; - transforms[1].r.w = (width + 1) / 2; - transforms[1].r.h = (height + 1) / 2; - transforms[1].op = TJXOP_TRANSPOSE; - transforms[1].options = TJXOPT_GRAY | TJXOPT_CROP | TJXOPT_COPYNONE; - dstBufs[1] = - (unsigned char *)malloc(tjBufSize((width + 1) / 2, (height + 1) / 2, - TJSAMP_GRAY)); - if (!dstBufs[1]) + maxBufSize = tj3JPEGBufSize(width, height, jpegSubsamp); + + tj3Set(handle, TJPARAM_NOREALLOC, 1); + if (tj3Transform(handle, data, size, 1, dstBufs, dstSizes, + transforms) == 0) { + /* Touch all of the output pixels in order to catch uninitialized reads + when using MemorySanitizer. */ + int sum = 0; + + for (i = 0; i < dstSizes[0]; i++) + sum += dstBufs[0][i]; + + /* Prevent the code above from being optimized out. This test should + never be true, but the compiler doesn't know that. */ + if (sum > 255 * maxBufSize) + goto bailout; + } + + free(dstBufs[0]); + dstBufs[0] = NULL; + + transforms[0].r.w = (height + 1) / 2; + transforms[0].r.h = (width + 1) / 2; + transforms[0].op = TJXOP_TRANSPOSE; + transforms[0].options = TJXOPT_GRAY | TJXOPT_CROP | TJXOPT_COPYNONE | + TJXOPT_OPTIMIZE; + dstBufs[0] = + (unsigned char *)malloc(tj3JPEGBufSize((height + 1) / 2, (width + 1) / 2, + jpegSubsamp)); + if (!dstBufs[0]) goto bailout; - transforms[2].op = TJXOP_ROT90; - transforms[2].options = TJXOPT_TRIM | TJXOPT_COPYNONE; - dstBufs[2] = (unsigned char *)malloc(tjBufSize(height, width, jpegSubsamp)); - if (!dstBufs[2]) + maxBufSize = tj3JPEGBufSize((height + 1) / 2, (width + 1) / 2, jpegSubsamp); + + if (tj3Transform(handle, data, size, 1, dstBufs, dstSizes, + transforms) == 0) { + int sum = 0; + + for (i = 0; i < dstSizes[0]; i++) + sum += dstBufs[0][i]; + + if (sum > 255 * maxBufSize) + goto bailout; + } + + free(dstBufs[0]); + dstBufs[0] = NULL; + + transforms[0].op = TJXOP_ROT90; + transforms[0].options = TJXOPT_TRIM | TJXOPT_ARITHMETIC; + dstBufs[0] = + (unsigned char *)malloc(tj3JPEGBufSize(height, width, jpegSubsamp)); + if (!dstBufs[0]) goto bailout; - maxBufSize = tjBufSize(width, height, jpegSubsamp); + maxBufSize = tj3JPEGBufSize(height, width, jpegSubsamp); - if (tjTransform(handle, data, size, NUMXFORMS, dstBufs, dstSizes, transforms, - TJFLAG_LIMITSCANS | TJFLAG_NOREALLOC) == 0) { - /* Touch all of the output pixels in order to catch uninitialized reads - when using MemorySanitizer. */ - for (t = 0; t < NUMXFORMS; t++) { - int sum = 0; + if (tj3Transform(handle, data, size, 1, dstBufs, dstSizes, + transforms) == 0) { + int sum = 0; - for (i = 0; i < dstSizes[t]; i++) - sum += dstBufs[t][i]; + for (i = 0; i < dstSizes[0]; i++) + sum += dstBufs[0][i]; - /* Prevent the code above from being optimized out. This test should - never be true, but the compiler doesn't know that. */ - if (sum > 255 * maxBufSize) - goto bailout; - } + if (sum > 255 * maxBufSize) + goto bailout; } - transforms[0].options &= ~TJXOPT_COPYNONE; free(dstBufs[0]); dstBufs[0] = NULL; + + transforms[0].op = TJXOP_NONE; + transforms[0].options = TJXOPT_PROGRESSIVE; 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++) @@ -128,8 +169,7 @@ 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); + free(dstBufs[0]); + tj3Destroy(handle); return 0; } diff --git a/java/TJBench.java b/java/TJBench.java index 3a061d8..e653add 100644 --- a/java/TJBench.java +++ b/java/TJBench.java @@ -1,6 +1,6 @@ /* - * Copyright (C)2009-2014, 2016-2019, 2021 D. R. Commander. - * All Rights Reserved. + * Copyright (C)2009-2014, 2016-2019, 2021-2023 D. R. Commander. + * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -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,30 +39,68 @@ final class TJBench { private TJBench() {} - private static int flags = 0, quiet = 0, pf = TJ.PF_BGR, yuvPad = 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 = { - "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1" + "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1", "4:4:1" }; static final String[] SUBNAME = { - "444", "422", "420", "GRAY", "440", "411" + "444", "422", "420", "GRAY", "440", "411", "441" }; static final String[] CSNAME = { "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,91 +157,69 @@ 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; int ps = TJ.getPixelSize(pf), i, iter = 0; - int scaledw = sf.getScaled(w); - int scaledh = sf.getScaled(h); - int pitch = scaledw * ps; + int scaledw, scaledh, pitch; YUVImage yuvImage = null; + if (lossless) + sf = TJ.UNSCALED; + + scaledw = sf.getScaled(w); + scaledh = sf.getScaled(h); + 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; int height = doTile ? tileh : scaledh; - yuvImage = new YUVImage(width, yuvPad, height, subsamp); + yuvImage = new YUVImage(width, yuvAlign, height, subsamp); Arrays.fill(yuvImage.getBuf(), (byte)127); } @@ -209,23 +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, yuvPad, height, subsamp); + 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); } } } @@ -243,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) { @@ -284,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"); - - saveImage(tempStr, dstBuf, scaledw, scaledh, pf); - int ndx = tempStr.lastIndexOf('.'); - tempStr = new String(tempStr.substring(0, ndx) + "-err.bmp"); - if (srcBuf != null && sf.getNum() == 1 && sf.getDenom() == 1) { - if (quiet == 0) - System.out.println("Compression error written to " + tempStr + "."); - if (subsamp == TJ.SAMP_GRAY) { - for (int y = 0, index = 0; y < h; y++, index += pitch) { - for (int x = 0, index2 = index; x < w; x++, index2 += ps) { - int rindex = index2 + TJ.getRedOffset(pf); - int gindex = index2 + TJ.getGreenOffset(pf); - int bindex = index2 + TJ.getBlueOffset(pf); - int lum = (int)((double)(srcBuf[rindex] & 0xff) * 0.299 + - (double)(srcBuf[gindex] & 0xff) * 0.587 + - (double)(srcBuf[bindex] & 0xff) * 0.114 + 0.5); - - if (lum > 255) lum = 255; - if (lum < 0) lum = 0; - dstBuf[rindex] = (byte)Math.abs((dstBuf[rindex] & 0xff) - lum); - dstBuf[gindex] = (byte)Math.abs((dstBuf[gindex] & 0xff) - lum); - dstBuf[bindex] = (byte)Math.abs((dstBuf[bindex] & 0xff) - lum); - } - } - } else { - for (int y = 0; y < h; y++) - for (int x = 0; x < w * ps; x++) - dstBuf[pitch * y + x] = - (byte)Math.abs((dstBuf[pitch * y + x] & 0xff) - - (srcBuf[pitch * y + x] & 0xff)); - } - saveImage(tempStr, dstBuf, w, h, pf); - } + tempStr = new String(fileName + "_" + + (lossless ? "LOSSLS" : SUBNAME[subsamp]) + qualStr + + "_" + sizeStr + (bmp ? ".bmp" : ".ppm")); + + tjd.saveImage(precision, tempStr, dstBuf, scaledw, 0, scaledh, pf); } - static void fullTest(byte[] srcBuf, int w, int h, int subsamp, int jpegQual, - String fileName) throws Exception { - TJCompressor tjc; - byte[] tmpBuf; - byte[][] jpegBuf; - int[] jpegSize; + static void fullTest(TJCompressor tjc, Object srcBuf, int w, int h, + int subsamp, int jpegQual, String fileName) + throws Exception { + Object tmpBuf; + byte[][] jpegBufs; + int[] jpegSizes; double start, elapsed, elapsedEncode; int totalJpegSize = 0, tilew, tileh, i, iter; int ps = TJ.getPixelSize(pf); @@ -339,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); - - tjc = new TJCompressor(); + System.out.format(">>>>> %s (%s) <--> %d-bit JPEG (%s %s%d) <<<<<\n", + pfStr, bottomUp ? "Bottom-up" : "Top-down", precision, + lossless ? "Lossless" : SUBNAME_LONG[subsamp], + lossless ? "PSV" : "Q", jpegQual); + + tjc.set(TJ.PARAM_SUBSAMP, subsamp); + tjc.set(TJ.PARAM_FASTDCT, fastDCT ? 1 : 0); + tjc.set(TJ.PARAM_OPTIMIZE, optimize ? 1 : 0); + tjc.set(TJ.PARAM_PROGRESSIVE, progressive ? 1 : 0); + tjc.set(TJ.PARAM_ARITHMETIC, arithmetic ? 1 : 0); + tjc.set(TJ.PARAM_LOSSLESS, lossless ? 1 : 0); + if (lossless) + tjc.set(TJ.PARAM_LOSSLESSPSV, jpegQual); + else + tjc.set(TJ.PARAM_QUALITY, jpegQual); + tjc.set(TJ.PARAM_RESTARTBLOCKS, restartIntervalBlocks); + tjc.set(TJ.PARAM_RESTARTROWS, restartIntervalRows); for (tilew = doTile ? 8 : w, tileh = doTile ? 8 : h; ; tilew *= 2, tileh *= 2) { @@ -358,21 +374,28 @@ 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, yuvPad, tileh, subsamp); + yuvImage = new YUVImage(tilew, yuvAlign, tileh, subsamp); Arrays.fill(yuvImage.getBuf(), (byte)127); } @@ -389,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, yuvPad, height, + 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; @@ -465,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); @@ -477,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"); @@ -489,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; + int minTile = 16, tw, th, ttilew, ttileh, tntilesw, tntilesh, tsubsamp; FileInputStream fis = new FileInputStream(fileName); if (fis.getChannel().size() > (long)Integer.MAX_VALUE) @@ -513,34 +545,60 @@ 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(); + subsamp = tjt.get(TJ.PARAM_SUBSAMP); + precision = tjt.get(TJ.PARAM_PRECISION); + cs = tjt.get(TJ.PARAM_COLORSPACE); + if (tjt.get(TJ.PARAM_PROGRESSIVE) == 1) + System.out.println("JPEG image uses progressive entropy coding\n"); + if (tjt.get(TJ.PARAM_ARITHMETIC) == 1) + System.out.println("JPEG image uses arithmetic entropy coding\n"); + tjt.set(TJ.PARAM_PROGRESSIVE, progressive ? 1 : 0); + tjt.set(TJ.PARAM_ARITHMETIC, arithmetic ? 1 : 0); + + if (cs == TJ.CS_YCCK || cs == TJ.CS_CMYK) { + pf = TJ.PF_CMYK; ps = TJ.getPixelSize(pf); + } + + if (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("Bitmap 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"); - - for (int tilew = doTile ? 16 : w, tileh = doTile ? 16 : h; ; + System.out.format(">>>>> %d-bit JPEG (%s) --> %s (%s) <<<<<\n", + precision, formatName(subsamp, cs), PIXFORMATSTR[pf], + bottomUp ? "Bottom-up" : "Top-down"); + + if (doTile) { + if (subsamp == TJ.SAMP_UNKNOWN) + throw new Exception("Could not determine subsampling level of JPEG image"); + minTile = Math.max(TJ.getMCUWidth(subsamp), TJ.getMCUHeight(subsamp)); + } + for (int tilew = doTile ? minTile : w, tileh = doTile ? minTile : h; ; tilew *= 2, tileh *= 2) { if (tilew > w) tilew = w; @@ -553,19 +611,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 || @@ -573,6 +632,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 || @@ -598,10 +660,14 @@ final class TJBench { tsubsamp = TJ.SAMP_440; else if (tsubsamp == TJ.SAMP_440) tsubsamp = TJ.SAMP_422; + else if (tsubsamp == TJ.SAMP_411) + tsubsamp = TJ.SAMP_441; + else if (tsubsamp == TJ.SAMP_441) + tsubsamp = TJ.SAMP_411; } TJTransform[] t = new TJTransform[tntilesw * tntilesh]; - jpegBuf = + jpegBufs = new byte[tntilesw * tntilesh][TJ.bufSize(ttilew, ttileh, subsamp)]; for (y = 0, tile = 0; y < th; y += ttileh) { @@ -613,9 +679,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; } } @@ -624,9 +691,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++; @@ -640,7 +707,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", @@ -664,10 +731,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) @@ -675,13 +742,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; } @@ -695,34 +762,62 @@ 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("-alloc = Dynamically allocate JPEG image buffers"); - System.out.println("-bottomup = Test bottom-up compression/decompression"); - System.out.println("-tile = Test performance of the codec when the image is encoded as separate"); - System.out.println(" tiles of varying sizes."); + System.out.println(" [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("-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(" Test the specified color conversion path in the codec (default = BGR)"); - System.out.println("-fastupsample = Use the fastest chrominance upsampling algorithm available in"); - System.out.println(" the underlying codec"); - System.out.println("-fastdct = Use the fastest DCT/IDCT algorithms available in the underlying"); - System.out.println(" codec"); - System.out.println("-accuratedct = Use the most accurate DCT/IDCT algorithms available in the"); - System.out.println(" underlying codec"); - System.out.println("-progressive = Use progressive entropy coding in JPEG images generated by"); - System.out.println(" compression and transform operations."); - System.out.println("-subsamp = When testing JPEG compression, this option specifies the level"); - System.out.println(" of chrominance subsampling to use ( = 444, 422, 440, 420, 411, or"); - System.out.println(" GRAY). The default is to test Grayscale, 4:2:0, 4:2:2, and 4:4:4 in"); - System.out.println(" sequence."); + System.out.println(" Use the specified pixel format for packed-pixel source/destination buffers"); + System.out.println(" [default = BGR]"); + 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(" (-precision 12 implies -optimize unless -arithmetic is also specified)"); System.out.println("-quiet = Output results in tabular rather than verbose format"); - System.out.println("-yuv = Test YUV encoding/decoding functions"); - System.out.println("-yuvpad

    = If testing YUV encoding/decoding, this specifies the number of"); - System.out.println(" bytes to which each row of each plane in the intermediate YUV image is"); - System.out.println(" padded (default = 1)"); - System.out.println("-scale M/N = Scale down the width/height of the decompressed JPEG image by a"); + 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("-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 (can be combined with -arithmetic;"); + System.out.println(" implies -optimize unless -arithmetic is also specified)"); + System.out.println("-limitscans = Refuse to decompress or transform progressive JPEG images that"); + System.out.println(" have an unreasonably large number of scans"); + System.out.println("-scale M/N = When decompressing, scale the width/height of the JPEG image by a"); System.out.print(" factor of M/N (M/N = "); for (i = 0; i < nsf; i++) { System.out.format("%d/%d", scalingFactors[i].getNum(), @@ -739,36 +834,34 @@ 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, 441, 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 corresponding lossless transform prior to"); - System.out.println(" decompression (these options are mutually exclusive)"); - System.out.println("-grayscale = Perform lossless grayscale conversion prior to decompression"); - System.out.println(" test (can be combined with the other transforms above)"); + System.out.println(" Perform the specified lossless transform operation on the input image"); + System.out.println(" prior to decompression (these operations are mutually exclusive)"); + System.out.println("-grayscale = Transform the input image into a grayscale JPEG image prior to"); + System.out.println(" decompression (can be combined with the other transform operations above)"); System.out.println("-copynone = Do not copy any extra markers (including EXIF and ICC profile data)"); - System.out.println(" when transforming the image."); - System.out.println("-benchtime = Run each benchmark for at least seconds (default = 5.0)"); - System.out.println("-warmup = Run each benchmark for seconds (default = 1.0) prior to"); - System.out.println(" starting the timer, in order to prime the caches and thus improve the"); - System.out.println(" consistency of the results."); - System.out.println("-componly = Stop after running compression tests. Do not test decompression."); - System.out.println("-nowrite = Do not write reference or output images (improves consistency"); - System.out.println(" of performance measurements.)"); - System.out.println("-limitscans = Refuse to decompress or transform progressive JPEG images that"); - System.out.println(" have an unreasonably large number of scans"); - System.out.println("-stoponwarning = Immediately discontinue the current"); - System.out.println(" compression/decompression/transform operation if the underlying codec"); - System.out.println(" throws a warning (non-fatal error)\n"); - System.out.println("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(" when transforming the input image"); + 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 { @@ -778,6 +871,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(""); @@ -785,18 +880,16 @@ final class TJBench { minArg = 2; if (argv.length < minArg) usage(); + String[] quals = argv[1].split("-", 2); try { - minQual = Integer.parseInt(argv[1]); + minQual = Integer.parseInt(quals[0]); } catch (NumberFormatException e) {} - if (minQual < 1 || minQual > 100) - throw new Exception("Quality must be between 1 and 100."); - int dashIndex = argv[1].indexOf('-'); - if (dashIndex > 0 && argv[1].length() > dashIndex + 1) { + if (quals.length > 1) { try { - maxQual = Integer.parseInt(argv[1].substring(dashIndex + 1)); + maxQual = Integer.parseInt(quals[1]); } catch (NumberFormatException e) {} } - if (maxQual < 1 || maxQual > 100) + if (maxQual < minQual) maxQual = minQual; } @@ -804,18 +897,38 @@ 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 fast upsampling code\n"); - flags |= TJ.FLAG_FASTUPSAMPLE; + System.out.println("Using fastest upsampling algorithm\n"); + 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"); + arithmetic = true; + xformOpt |= TJTransform.OPT_ARITHMETIC; + } else if (argv[i].equalsIgnoreCase("-lossless")) { + lossless = true; + subsamp = TJ.SAMP_444; } else if (argv[i].equalsIgnoreCase("-rgb")) pf = TJ.PF_RGB; else if (argv[i].equalsIgnoreCase("-rgbx")) @@ -828,8 +941,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")) @@ -858,6 +973,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")) @@ -874,6 +1004,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")) @@ -901,8 +1033,10 @@ final class TJBench { System.out.format("Warmup time = %.1f seconds\n\n", warmup); } else usage(); - } else if (argv[i].equalsIgnoreCase("-yuv")) { - System.out.println("Testing YUV planar encoding/decoding\n"); + } 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") && i < argv.length - 1) { @@ -911,8 +1045,10 @@ final class TJBench { try { temp = Integer.parseInt(argv[++i]); } catch (NumberFormatException e) {} - if (temp >= 1) - yuvPad = temp; + if (temp >= 1 && (temp & (temp - 1)) == 0) + yuvAlign = temp; + else + usage(); } else if (argv[i].equalsIgnoreCase("-subsamp") && i < argv.length - 1) { i++; @@ -928,32 +1064,77 @@ final class TJBench { subsamp = TJ.SAMP_420; else if (argv[i].equals("411")) subsamp = TJ.SAMP_411; + else if (argv[i].equals("441")) + subsamp = TJ.SAMP_441; + else + usage(); } else if (argv[i].equalsIgnoreCase("-componly")) compOnly = true; 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 && 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"); - System.out.println("work when scaled decompression is enabled."); + System.out.println("work when scaled decompression is enabled.\n"); doTile = false; + xformOpt &= (~TJTransform.OPT_CROP); + } + + if (isCropped(cr)) { + if (!decompOnly) + throw new Exception("ERROR: Partial image decompression can only be enabled for JPEG input images"); + if (doTile) { + System.out.println("Disabling tiled compression/decompression tests, because those tests do not"); + System.out.println("work when partial image decompression is enabled.\n"); + doTile = false; + xformOpt &= (~TJTransform.OPT_CROP); + } + if (doYUV) + throw new Exception("ERROR: -crop and -yuv are incompatible"); } if (!decompOnly) { - int[] width = new int[1], height = new int[1]; + int[] width = new int[1], height = new int[1], + pixelFormat = new int[1]; + + tjc = new TJCompressor(); + tjc.set(TJ.PARAM_STOPONWARNING, stopOnWarning ? 1 : 0); + tjc.set(TJ.PARAM_BOTTOMUP, bottomUp ? 1 : 0); - srcBuf = loadImage(argv[0], width, height, pf); - w = width[0]; h = height[0]; + pixelFormat[0] = pf; + srcBuf = tjc.loadImage(precision, argv[0], width, 1, height, + pixelFormat); + w = width[0]; h = height[0]; pf = pixelFormat[0]; int index = -1; if ((index = argv[0].lastIndexOf('.')) >= 0) argv[0] = argv[0].substring(0, index); @@ -961,7 +1142,7 @@ final class TJBench { if (quiet == 1 && !decompOnly) { System.out.println("All performance values in Mpixels/sec\n"); - System.out.format("Bitmap JPEG JPEG %s %s ", + System.out.format("Pixel JPEG JPEG %s %s ", (doTile ? "Tile " : "Image"), (doTile ? "Tile " : "Image")); if (doYUV) @@ -970,7 +1151,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 "); @@ -986,25 +1168,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]); - System.out.println(""); - System.gc(); - for (int i = maxQual; i >= minQual; i--) - fullTest(srcBuf, w, h, TJ.SAMP_420, i, argv[0]); + fullTest(tjc, srcBuf, w, h, TJ.SAMP_420, i, argv[0]); System.out.println(""); System.gc(); for (int i = maxQual; i >= minQual; i--) - fullTest(srcBuf, w, h, TJ.SAMP_422, i, argv[0]); + fullTest(tjc, srcBuf, w, h, TJ.SAMP_422, i, argv[0]); System.out.println(""); System.gc(); for (int i = maxQual; i >= minQual; i--) - fullTest(srcBuf, w, h, TJ.SAMP_444, i, argv[0]); + fullTest(tjc, srcBuf, w, h, TJ.SAMP_444, i, argv[0]); System.out.println(""); } diff --git a/java/TJExample.java b/java/TJExample.java index 7859886..6a2148c 100644 --- a/java/TJExample.java +++ b/java/TJExample.java @@ -1,6 +1,6 @@ /* - * Copyright (C)2011-2012, 2014-2015, 2017-2018 D. R. Commander. - * All Rights Reserved. + * Copyright (C)2011-2012, 2014-2015, 2017-2018, 2022-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: @@ -52,7 +52,7 @@ class TJExample implements TJCustomFilter { static final String[] SUBSAMP_NAME = { - "4:4:4", "4:2:2", "4:2:0", "Grayscale", "4:4:0", "4:1:1" + "4:4:4", "4:2:2", "4:2:0", "Grayscale", "4:4:0", "4:1:1", "4:4:1" }; static final String[] COLORSPACE_NAME = { @@ -136,14 +136,9 @@ class TJExample implements TJCustomFilter { System.out.println("-display = Display output image (Output filename need not be specified in this"); System.out.println(" case.)\n"); - System.out.println("-fastupsample = Use the fastest chrominance upsampling algorithm available in"); - System.out.println(" the underlying codec.\n"); + System.out.println("-fastupsample = Use the fastest chrominance upsampling algorithm available\n"); - System.out.println("-fastdct = Use the fastest DCT/IDCT algorithms available in the underlying"); - System.out.println(" codec.\n"); - - System.out.println("-accuratedct = Use the most accurate DCT/IDCT algorithms available in the"); - System.out.println(" underlying codec.\n"); + System.out.println("-fastdct = Use the fastest DCT/IDCT algorithm available\n"); System.exit(1); } @@ -153,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; @@ -247,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(); } @@ -294,16 +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 inSubsamp = tjd.get(TJ.PARAM_SUBSAMP); + int inColorspace = tjd.get(TJ.PARAM_COLORSPACE); + + if (tjd.get(TJ.PARAM_LOSSLESS) == 1) + scalingFactor = TJ.UNSCALED; System.out.println((doTransform ? "Transformed" : "Input") + " Image (jpg): " + width + " x " + height + @@ -325,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. */ @@ -371,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 91ad5fd..6083928 100644 --- a/java/TJUnitTest.java +++ b/java/TJUnitTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C)2011-2018 D. R. Commander. All Rights Reserved. + * Copyright (C)2011-2018, 2022-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: @@ -48,18 +48,22 @@ final class TJUnitTest { static void usage() { System.out.println("\nUSAGE: java " + CLASS_NAME + " [options]\n"); System.out.println("Options:"); - System.out.println("-yuv = test YUV encoding/decoding support"); - System.out.println("-noyuvpad = do not pad each line of each Y, U, and V plane to the nearest"); - System.out.println(" 4-byte boundary"); - System.out.println("-bi = test BufferedImage support\n"); + 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 (8-bit data precision only)\n"); System.exit(1); } static final String[] SUBNAME_LONG = { - "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1" + "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1", "4:4:1" }; static final String[] SUBNAME = { - "444", "422", "420", "GRAY", "440", "411" + "444", "422", "420", "GRAY", "440", "411", "441" }; static final String[] PIXFORMATSTR = { @@ -67,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 = { @@ -92,7 +96,11 @@ final class TJUnitTest { }; private static boolean doYUV = false; - private static int pad = 4; + 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; @@ -142,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); @@ -152,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; @@ -222,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; @@ -243,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(); @@ -256,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 - 1 || v > cv + 1) { + if (v < cv - tolerance || v > cv + tolerance) { throw new Exception("Comp. " + vname + " at " + row + "," + col + " should be " + cv + ", not " + v); } @@ -278,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 > 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 < 254) { + 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); @@ -312,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"); } } } @@ -343,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"); @@ -364,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) { @@ -394,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); } } @@ -420,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; @@ -433,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; @@ -443,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"); @@ -463,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) { @@ -498,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 || @@ -511,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); } } @@ -532,7 +559,7 @@ final class TJUnitTest { int hsf = TJ.getMCUWidth(subsamp) / 8, vsf = TJ.getMCUHeight(subsamp) / 8; int pw = pad(w, hsf), ph = pad(h, vsf); int cw = pw / hsf, ch = ph / vsf; - int ypitch = pad(pw, pad), uvpitch = pad(cw, pad); + int ypitch = pad(pw, yuvAlign), uvpitch = pad(cw, yuvAlign); int retval = 1; int correctsize = ypitch * ph + (subsamp == TJ.SAMP_GRAY ? 0 : uvpitch * ch * 2); @@ -549,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 { @@ -572,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); @@ -627,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) { @@ -650,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(pad, 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"); @@ -681,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); @@ -697,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); @@ -717,24 +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, pad, scaledHeight, - flags); + YUVImage yuvImage = tjd.decompressToYUV(yuvAlign); if (checkBufYUV(yuvImage.getBuf(), yuvImage.getSize(), scaledWidth, scaledHeight, subsamp, sf) == 1) System.out.print("Passed.\n"); @@ -752,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"); @@ -777,20 +820,26 @@ 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; + + if (lossless) { + decompTest(tjd, jpegBuf, jpegSize, w, h, pf, baseName, subsamp, + TJ.UNSCALED); + return; + } + TJScalingFactor[] sf = TJ.getScalingFactors(); for (i = 0; i < sf.length; i++) { int num = sf[i].getNum(); int denom = sf[i].getDenom(); if (subsamp == TJ.SAMP_444 || subsamp == TJ.SAMP_GRAY || - (subsamp == TJ.SAMP_411 && num == 1 && + ((subsamp == TJ.SAMP_411 || subsamp == TJ.SAMP_441) && num == 1 && (denom == 2 || denom == 1)) || - (subsamp != TJ.SAMP_411 && num == 1 && + (subsamp != TJ.SAMP_411 && subsamp != TJ.SAMP_441 && 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]); } } @@ -801,28 +850,38 @@ final class TJUnitTest { int size; byte[] dstBuf; + if (lossless && subsamp != TJ.SAMP_GRAY) + subsamp = TJ.SAMP_444; + dstBuf = new byte[TJ.bufSize(w, h, subsamp)]; try { 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 || + subsamp == TJ.SAMP_441) + 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 (subsamp == TJ.SAMP_422 || subsamp == TJ.SAMP_420 || - subsamp == TJ.SAMP_440 || subsamp == TJ.SAMP_411) - flags |= TJ.FLAG_FASTUPSAMPLE; - if (i == 1) - flags |= TJ.FLAG_BOTTOMUP; - size = compTest(tjc, dstBuf, w, h, pf, baseName, subsamp, 100, - 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"); } @@ -837,8 +896,57 @@ final class TJUnitTest { if (tjd != null) tjd.close(); } + static void overflowTest() throws Exception { + /* Ensure that the various buffer size methods don't overflow */ + int size = 0; + boolean exception = false; + + try { + exception = false; + size = TJ.bufSize(18919, 18919, TJ.SAMP_444); + } catch (Exception e) { exception = true; } + if (!exception || size != 0) + throw new Exception("TJ.bufSize() overflow"); + try { + exception = false; + size = TJ.bufSizeYUV(26755, 1, 26755, TJ.SAMP_444); + } catch (Exception e) { exception = true; } + if (!exception || size != 0) + throw new Exception("TJ.bufSizeYUV() overflow"); + try { + exception = false; + size = TJ.bufSizeYUV(26754, 3, 26754, TJ.SAMP_444); + } catch (Exception e) { exception = true; } + if (!exception || size != 0) + throw new Exception("TJ.bufSizeYUV() overflow"); + try { + exception = false; + size = TJ.bufSizeYUV(26754, -1, 26754, TJ.SAMP_444); + } catch (Exception e) { exception = true; } + if (!exception || size != 0) + throw new Exception("TJ.bufSizeYUV() overflow"); + try { + exception = false; + size = TJ.planeSizeYUV(0, 46341, 0, 46341, TJ.SAMP_444); + } catch (Exception e) { exception = true; } + if (!exception || size != 0) + throw new Exception("TJ.planeSizeYUV() overflow"); + try { + exception = false; + size = TJ.planeWidth(0, Integer.MAX_VALUE, TJ.SAMP_420); + } catch (Exception e) { exception = true; } + if (!exception || size != 0) + throw new Exception("TJ.planeWidth() overflow"); + try { + exception = false; + size = TJ.planeHeight(0, Integer.MAX_VALUE, TJ.SAMP_420); + } catch (Exception e) { exception = true; } + if (!exception || size != 0) + throw new Exception("TJ.planeHeight() overflow"); + } + static void bufSizeTest() throws Exception { - int w, h, i, subsamp; + int w, h, i, subsamp, numSamp = TJ.NUMSAMP; byte[] srcBuf, dstBuf = null; YUVImage dstImage = null; TJCompressor tjc = null; @@ -846,8 +954,17 @@ final class TJUnitTest { try { 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 < TJ.NUMSAMP; subsamp++) { + 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++) { @@ -855,23 +972,21 @@ final class TJUnitTest { System.out.format("%04d x %04d\b\b\b\b\b\b\b\b\b\b\b", w, h); srcBuf = new byte[w * h * 4]; if (doYUV) - dstImage = new YUVImage(w, pad, h, subsamp); + dstImage = new YUVImage(w, yuvAlign, h, subsamp); else dstBuf = new byte[TJ.bufSize(w, h, subsamp)]; for (i = 0; i < w * h * 4; i++) { srcBuf[i] = (byte)(r.nextInt(2) * 255); } tjc.setSourceImage(srcBuf, 0, 0, w, 0, h, TJ.PF_BGRX); - tjc.setSubsamp(subsamp); - tjc.setJPEGQuality(100); if (doYUV) - tjc.encodeYUV(dstImage, 0); + tjc.encodeYUV(dstImage); else - tjc.compress(dstBuf, 0); + tjc.compress(dstBuf); srcBuf = new byte[h * w * 4]; if (doYUV) - dstImage = new YUVImage(h, pad, w, subsamp); + dstImage = new YUVImage(h, yuvAlign, w, subsamp); else dstBuf = new byte[TJ.bufSize(h, w, subsamp)]; for (i = 0; i < h * w * 4; i++) { @@ -879,9 +994,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, 0); + tjc.compress(dstBuf); } dstImage = null; dstBuf = null; @@ -903,42 +1018,79 @@ final class TJUnitTest { if (argv[i].equalsIgnoreCase("-yuv")) doYUV = true; else if (argv[i].equalsIgnoreCase("-noyuvpad")) - pad = 1; + yuvAlign = 1; + else if (argv[i].equalsIgnoreCase("-lossless")) + lossless = true; 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; - doTest(35, 39, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_444, - testName); - doTest(39, 41, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_444, - testName); - doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_422, - testName); - doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_422, - testName); - doTest(39, 41, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_420, + FORMATS_4SAMPLE[4] = -1; + overflowTest(); + doTest(35, 39, bi ? FORMATS_3BYTEBI : FORMATS_3SAMPLE, TJ.SAMP_444, testName); - doTest(41, 35, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_420, + doTest(39, 41, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_444, testName); - doTest(35, 39, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_440, - testName); - doTest(39, 41, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_440, - testName); - doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_411, - testName); - doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_411, + doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3SAMPLE, TJ.SAMP_422, testName); + if (!lossless) { + doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_422, + testName); + doTest(39, 41, bi ? FORMATS_3BYTEBI : FORMATS_3SAMPLE, TJ.SAMP_420, + testName); + doTest(41, 35, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_420, + testName); + doTest(35, 39, bi ? FORMATS_3BYTEBI : FORMATS_3SAMPLE, TJ.SAMP_440, + testName); + doTest(39, 41, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_440, + testName); + doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3SAMPLE, TJ.SAMP_411, + testName); + doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_411, + testName); + doTest(39, 41, bi ? FORMATS_3BYTEBI : FORMATS_3SAMPLE, TJ.SAMP_441, + testName); + doTest(41, 35, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_441, + testName); + } doTest(39, 41, bi ? FORMATS_GRAYBI : FORMATS_GRAY, TJ.SAMP_GRAY, testName); - doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_GRAY, - testName); - FORMATS_4BYTE[4] = -1; - doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_GRAY, - testName); + if (!lossless) { + doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3SAMPLE, TJ.SAMP_GRAY, + testName); + FORMATS_4SAMPLE[4] = -1; + doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_GRAY, + testName); + } if (!bi) bufSizeTest(); if (doYUV && !bi) { @@ -948,6 +1100,7 @@ final class TJUnitTest { doTest(48, 48, FORMATS_RGB, TJ.SAMP_420, "javatest_yuv0"); doTest(48, 48, FORMATS_RGB, TJ.SAMP_440, "javatest_yuv0"); doTest(48, 48, FORMATS_RGB, TJ.SAMP_411, "javatest_yuv0"); + doTest(48, 48, FORMATS_RGB, TJ.SAMP_441, "javatest_yuv0"); doTest(48, 48, FORMATS_RGB, TJ.SAMP_GRAY, "javatest_yuv0"); doTest(48, 48, FORMATS_GRAY, TJ.SAMP_GRAY, "javatest_yuv0"); } diff --git a/java/doc/allclasses-frame.html b/java/doc/allclasses-frame.html deleted file mode 100644 index fecac06..0000000 --- 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 0000000..f3179c5 --- /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 57% rename from java/doc/allclasses-noframe.html rename to java/doc/allclasses.html index 1f7fd3c..c87c92f 100644 --- a/java/doc/allclasses-noframe.html +++ b/java/doc/allclasses.html @@ -1,17 +1,30 @@ - + + All Classes + + + + + + + + + -

    All Classes

    +
    +

    All Classes

    +
    diff --git a/java/doc/allpackages-index.html b/java/doc/allpackages-index.html new file mode 100644 index 0000000..8ad354d --- /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 07ba052..1fa1fce 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 31d4e64..3f4f34a 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'); +
    + +
    +
    -
    +
    - + - - - -
      -
    • - - - - - - - + @@ -205,10 +250,15 @@ + + 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 6645d95..6d9362c 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 5def53e..4b15305 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 YUV planar - image with the given width, height, and level of chrominance subsampling.
      -
      -
      bufSizeYUV(int, int, int) - Static method in class org.libjpegturbo.turbojpeg.TJ
      -
      -
      Deprecated. - -
      +
      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 uncompressed source image associated with this compressor - instance and output a JPEG image to the given destination buffer.
      +
      Compress the packed-pixel or planar YUV source image associated with this + compressor instance and return a buffer containing a JPEG image.
      -
      compress(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      +
      compress(byte[]) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      -
      Compress the uncompressed source image associated with this compressor - instance and return a buffer containing a JPEG image.
      +
      Compress the packed-pixel or planar YUV source image associated with this + compressor instance and output a JPEG image to the given destination + buffer.
      -
      compress(BufferedImage, byte[], int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      +
      compress(byte[], int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      -
      -
      compress(BufferedImage, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      +
      compress(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      -
      -
      CS_CMYK - Static variable in class org.libjpegturbo.turbojpeg.TJ
      +
      CS_CMYK - Static variable in class org.libjpegturbo.turbojpeg.TJ
      CMYK colorspace.
      -
      CS_GRAY - Static variable in class org.libjpegturbo.turbojpeg.TJ
      +
      CS_GRAY - Static variable in class org.libjpegturbo.turbojpeg.TJ
      Grayscale colorspace.
      -
      CS_RGB - Static variable in class org.libjpegturbo.turbojpeg.TJ
      +
      CS_RGB - Static variable in class org.libjpegturbo.turbojpeg.TJ
      RGB colorspace.
      -
      CS_YCbCr - Static variable in class org.libjpegturbo.turbojpeg.TJ
      +
      CS_YCbCr - Static variable in class org.libjpegturbo.turbojpeg.TJ
      YCbCr colorspace.
      -
      CS_YCCK - Static variable in class org.libjpegturbo.turbojpeg.TJ
      +
      CS_YCCK - Static variable in class org.libjpegturbo.turbojpeg.TJ
      YCCK colorspace.
      -
      customFilter(ShortBuffer, Rectangle, Rectangle, int, int, TJTransform) - Method in interface org.libjpegturbo.turbojpeg.TJCustomFilter
      +
      customFilter(ShortBuffer, Rectangle, Rectangle, int, int, TJTransform) - Method in interface org.libjpegturbo.turbojpeg.TJCustomFilter
      A callback function that can be used to modify the DCT coefficients after they are losslessly transformed but before they are transcoded to a new JPEG image.
      - +

      D

      -
      decompress(byte[], int, int, int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      +
      decompress(byte[], int, int, int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      +
      + +
      +
      decompress(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
      -
      Decompress the JPEG source image or decode the YUV source image associated - with this decompressor instance and output a grayscale, RGB, or CMYK image - to the given destination buffer.
      +
      Deprecated. + +
      -
      decompress(byte[], int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      +
      decompress(BufferedImage, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      -
      -
      decompress(int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      +
      decompress12(int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      -
      Decompress the JPEG source image associated with this decompressor - instance and return a buffer containing the decompressed image.
      +
      Decompress the 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.
      -
      decompress(int[], int, int, int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      +
      decompress12(short[], int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      -
      Decompress the JPEG source image or decode the YUV source image associated - with this decompressor instance and output a grayscale, RGB, or CMYK image - to the given destination buffer.
      +
      Decompress the 12-bit-per-sample JPEG source image associated with this + decompressor instance and output a 12-bit-per-sample packed-pixel + grayscale, RGB, or CMYK image to the given destination buffer.
      -
      decompress(BufferedImage, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      +
      decompress16(int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      -
      Decompress the JPEG source image or decode the YUV source image associated - with this decompressor instance and output a decompressed/decoded image to - the given BufferedImage instance.
      +
      Decompress the 16-bit-per-sample JPEG source image associated with this + decompressor instance and return a buffer containing a 16-bit-per-sample + packed-pixel decompressed image.
      -
      decompress(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 YUV source image associated - with this decompressor instance and return a BufferedImage - instance containing the decompressed/decoded image.
      +
      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.
      -
      decompressToYUV(YUVImage, 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 associated with this decompressor - instance into a YUV planar 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(byte[], int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      +
      decompress8(int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      -
      Deprecated. - +
      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.
      +
      +
      decompress8(int[], int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      +
      +
      Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + source image associated with this decompressor instance and output an + 8-bit-per-sample packed-pixel grayscale, RGB, or CMYK image to the given + destination buffer.
      +
      +
      decompress8(int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      +
      +
      Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + source image associated with this decompressor instance and return a + buffer containing an 8-bit-per-sample packed-pixel decompressed image.
      +
      +
      decompress8(BufferedImage) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      +
      +
      Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + source image associated with this decompressor instance and output an + 8-bit-per-sample packed-pixel 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(int, int, 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.
      +
      -
      decompressToYUV(int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      +
      decompressToYUV(YUVImage) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      -
      Decompress the JPEG source image associated with this decompressor - instance into a unified YUV planar image buffer and return a - YUVImage instance containing the decompressed image.
      +
      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(int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      +
      decompressToYUV(YUVImage, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      -
      - +

      E

      -
      encodeYUV(YUVImage, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      -
      -
      Encode the uncompressed source image associated with this compressor - instance into a YUV planar image and store it in the given - YUVImage instance.
      -
      -
      encodeYUV(byte[], int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      +
      encodeYUV(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      -
      Deprecated. - -
      +
      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 uncompressed source image associated with this compressor - instance into a unified YUV planar image buffer and return a - YUVImage instance containing the encoded image.
      +
      Encode the 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 uncompressed 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. + +
      -
      encodeYUV(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      +
      encodeYUV(int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      -
      Deprecated. - +
      Deprecated. +
      -
      encodeYUV(BufferedImage, byte[], int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      +
      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(BufferedImage, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      +
      encodeYUV(YUVImage, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      -
      -
      equals(TJScalingFactor) - Method in class org.libjpegturbo.turbojpeg.TJScalingFactor
      +
      equals(TJScalingFactor) - Method in class org.libjpegturbo.turbojpeg.TJScalingFactor
      Returns true or false, depending on whether this instance and other have the same numerator and denominator.
      -
      ERR_FATAL - Static variable in class org.libjpegturbo.turbojpeg.TJ
      +
      ERR_FATAL - Static variable in class org.libjpegturbo.turbojpeg.TJ
      The error was fatal and non-recoverable.
      -
      ERR_WARNING - Static variable in class org.libjpegturbo.turbojpeg.TJ
      +
      ERR_WARNING - Static variable in class org.libjpegturbo.turbojpeg.TJ
      -
      The error was non-fatal and recoverable, but the image may still be - corrupt.
      +
      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
      -
      -
      Use the most accurate DCT/IDCT algorithm available in the underlying - codec.
      -
      -
      FLAG_BOTTOMUP - Static variable in class org.libjpegturbo.turbojpeg.TJ
      -
      -
      The uncompressed source/destination image is stored in bottom-up (Windows, - OpenGL) order, not top-down (X11) order.
      -
      -
      FLAG_FASTDCT - Static variable in class org.libjpegturbo.turbojpeg.TJ
      -
      -
      Use the fastest DCT/IDCT algorithm available in the underlying codec.
      -
      -
      FLAG_FASTUPSAMPLE - Static variable in class org.libjpegturbo.turbojpeg.TJ
      +
      FLAG_ACCURATEDCT - Static variable in class org.libjpegturbo.turbojpeg.TJ
      -
      When decompressing an image that was compressed using chrominance - subsampling, use the fastest chrominance upsampling algorithm available in - the underlying codec.
      -
      -
      FLAG_FORCEMMX - Static variable in class org.libjpegturbo.turbojpeg.TJ
      -
      -
      Deprecated.
      +
      Deprecated. +
      Use TJ.PARAM_FASTDCT instead.
      +
      -
      FLAG_FORCESSE - Static variable in class org.libjpegturbo.turbojpeg.TJ
      +
      FLAG_BOTTOMUP - Static variable in class org.libjpegturbo.turbojpeg.TJ
      -
      Deprecated.
      +
      Deprecated. +
      Use TJ.PARAM_BOTTOMUP instead.
      +
      -
      FLAG_FORCESSE2 - Static variable in class org.libjpegturbo.turbojpeg.TJ
      +
      FLAG_FASTDCT - Static variable in class org.libjpegturbo.turbojpeg.TJ
      -
      Deprecated.
      +
      Deprecated. +
      Use TJ.PARAM_FASTDCT instead.
      +
      -
      FLAG_FORCESSE3 - Static variable in class org.libjpegturbo.turbojpeg.TJ
      +
      FLAG_FASTUPSAMPLE - Static variable in class org.libjpegturbo.turbojpeg.TJ
      -
      Deprecated.
      +
      Deprecated. + +
      -
      FLAG_LIMITSCANS - Static variable in class org.libjpegturbo.turbojpeg.TJ
      +
      FLAG_LIMITSCANS - Static variable in class org.libjpegturbo.turbojpeg.TJ
      -
      Limit the number of progressive JPEG scans that the decompression and - transform operations will process.
      +
      Deprecated. +
      Use TJ.PARAM_SCANLIMIT instead.
      +
      -
      FLAG_PROGRESSIVE - Static variable in class org.libjpegturbo.turbojpeg.TJ
      +
      FLAG_PROGRESSIVE - Static variable in class org.libjpegturbo.turbojpeg.TJ
      -
      Use progressive entropy coding in JPEG images generated by compression and - transform operations.
      +
      Deprecated. + +
      -
      FLAG_STOPONWARNING - Static variable in class org.libjpegturbo.turbojpeg.TJ
      +
      FLAG_STOPONWARNING - Static variable in class org.libjpegturbo.turbojpeg.TJ
      -
      Immediately discontinue the current compression/decompression/transform - operation if the underlying codec throws a warning (non-fatal error).
      +
      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 image buffer (if this image is stored in a unified - buffer rather than separate image planes.)
      +
      Returns the YUV buffer (if this image is stored in a unified buffer rather + than separate image planes.)
      -
      getColorspace() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      +
      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.
      -
      getGreenOffset(int) - Static method in class org.libjpegturbo.turbojpeg.TJ
      +
      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 image buffer associated with this decompressor instance.
      +
      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 line padding used in the YUV image buffer (if this image is +
      Returns the row alignment (in bytes) of the YUV buffer (if this image is stored in a unified buffer rather than separate image planes.)
      -
      getPixelSize(int) - Static method in class org.libjpegturbo.turbojpeg.TJ
      +
      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 in - this implementation of TurboJPEG supports.
      +
      Returns a list of fractional scaling factors that the JPEG decompressor + supports.
      -
      getSize() - Method in class org.libjpegturbo.turbojpeg.YUVImage
      +
      getSize() - Method in class org.libjpegturbo.turbojpeg.YUVImage
      -
      Returns the size (in bytes) of the YUV image buffer (if this image is - stored in a unified buffer rather than separate image planes.)
      +
      Returns the size (in bytes) of the YUV buffer (if this image is stored in + a unified buffer rather than separate image planes.)
      -
      getStrides() - Method in class org.libjpegturbo.turbojpeg.YUVImage
      +
      getStrides() - Method in class org.libjpegturbo.turbojpeg.YUVImage
      -
      Returns the number of bytes per line of each plane in the YUV image.
      +
      Returns the number of bytes per row of each plane in the YUV image.
      -
      getSubsamp() - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      +
      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 - generated by the most recent transform operation.
      + (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
      -
       
      -
      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_COPYNONE - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
      +
      OPT_ARITHMETIC - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
      -
      This option will prevent TJTransformer.transform() from copying any extra markers (including EXIF - and ICC profile data) from the source image to the output image.
      +
      This option will enable arithmetic entropy coding in the JPEG image + generated by this particular transform.
      -
      OPT_CROP - 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 + and ICC profile data) from the source image to the destination image.
      +
      +
      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 input image and produce - a grayscale output image.
      +
      This option will discard the color data in the source image and produce a + grayscale destination image.
      -
      OPT_NOOUTPUT - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
      +
      OPT_NOOUTPUT - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
      -
      This option will prevent TJTransformer.transform() from outputting a JPEG image for this +
      This option will prevent TJTransformer.transform() from outputting a JPEG image for this particular transform.
      -
      OPT_PERFECT - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
      +
      OPT_OPTIMIZE - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
      +
      +
      This option will enable optimized baseline entropy coding in the JPEG + image generated by this particular transform.
      +
      +
      OPT_PERFECT - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
      -
      This option will cause TJTransformer.transform() to throw an exception if the transform is not +
      This option will cause TJTransformer.transform() to throw an exception if the transform is not perfect.
      -
      OPT_PROGRESSIVE - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
      +
      OPT_PROGRESSIVE - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
      -
      This option will enable progressive entropy coding in the output image +
      This option will enable progressive entropy coding in the JPEG image generated by this particular transform.
      -
      OPT_TRIM - Static variable in class org.libjpegturbo.turbojpeg.TJTransform
      +
      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_*)
      +
      Transform options (bitwise OR of one or more of + 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_441 - Static variable in class org.libjpegturbo.turbojpeg.TJ
      +
      +
      4:4:1 chrominance subsampling.
      +
      +
      SAMP_444 - Static variable in class org.libjpegturbo.turbojpeg.TJ
      4:4:4 chrominance subsampling (no chrominance subsampling).
      -
      SAMP_GRAY - Static variable in class org.libjpegturbo.turbojpeg.TJ
      +
      SAMP_GRAY - Static variable in class org.libjpegturbo.turbojpeg.TJ
      Grayscale.
      -
      setBuf(byte[][], int[], int, int[], int, int) - Method in class org.libjpegturbo.turbojpeg.YUVImage
      +
      SAMP_UNKNOWN - Static variable in class org.libjpegturbo.turbojpeg.TJ
      -
      Assign a set of image planes to this YUVImage instance.
      +
      Unknown subsampling.
      -
      setBuf(byte[], int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.YUVImage
      +
      set(int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      -
      Assign a unified image buffer to this YUVImage instance.
      +
      Set the value of a compression parameter.
      -
      setJPEGImage(byte[], int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      +
      set(int, int) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      -
      Deprecated. - -
      +
      Set the value of a decompression parameter.
      -
      setJPEGQuality(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      +
      setBuf(byte[][], int[], int, int[], int, int) - Method in class org.libjpegturbo.turbojpeg.YUVImage
      -
      Set the JPEG image quality level for subsequent compress operations.
      +
      Assign a set of image planes to this YUVImage instance.
      -
      setSourceImage(byte[], int, int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      +
      setBuf(byte[], int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.YUVImage
      -
      Associate an uncompressed RGB, grayscale, or CMYK source image with this - compressor instance.
      +
      Assign a unified buffer to this YUVImage instance.
      -
      setSourceImage(byte[], int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      +
      setCroppingRegion(Rectangle) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      - +
      Set the cropping region for partially decompressing a lossy JPEG image + into a packed-pixel image.
      -
      setSourceImage(BufferedImage, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      +
      setJPEGQuality(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      -
      Associate an uncompressed RGB or grayscale source image with this - compressor instance.
      +
      Deprecated. +
      Use + set(TJ.PARAM_QUALITY, ...) instead.
      +
      -
      setSourceImage(YUVImage) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      +
      setScalingFactor(TJScalingFactor) - Method in class org.libjpegturbo.turbojpeg.TJDecompressor
      -
      Associate an uncompressed YUV planar source image with this compressor - instance.
      +
      Set the scaling factor for subsequent lossy decompression operations.
      -
      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 YUV planar source image with this decompressor +
      Associate the specified planar YUV source image with this decompressor instance.
      -
      setSubsamp(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      +
      setSourceImage12(short[], int, int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      +
      +
      Associate a 12-bit-per-sample packed-pixel RGB, grayscale, or CMYK source + image with this compressor instance.
      +
      +
      setSourceImage16(short[], int, int, int, int, int, int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
      -
      Set the level of chrominance subsampling for subsequent compress/encode - operations.
      +
      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 uncompressed - 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(byte[], int, int, int, int) - Constructor for class org.libjpegturbo.turbojpeg.TJCompressor
      +
      TJCompressor(BufferedImage, int, int, int, int) - Constructor for class org.libjpegturbo.turbojpeg.TJCompressor
      - -
      -
      TJCompressor(BufferedImage, int, int, int, int) - Constructor for class org.libjpegturbo.turbojpeg.TJCompressor
      -
      -
      Create a TurboJPEG compressor instance and associate the uncompressed - 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 stored in jpegImage with the newly created instance.
      + image or "abbreviated table specification" (AKA "tables-only") datastream + stored in jpegImage with the newly created instance.
      -
      TJDecompressor(byte[], int) - Constructor for class org.libjpegturbo.turbojpeg.TJDecompressor
      +
      TJDecompressor(byte[], int) - Constructor for class org.libjpegturbo.turbojpeg.TJDecompressor
      Create a TurboJPEG decompressor instance and associate the JPEG source - image of length imageSize bytes stored in - jpegImage with the newly created instance.
      + image or "abbreviated table specification" (AKA "tables-only") datastream + of length imageSize bytes stored in jpegImage + with the newly created instance.
      -
      TJDecompressor(YUVImage) - Constructor for class org.libjpegturbo.turbojpeg.TJDecompressor
      +
      TJDecompressor(YUVImage) - Constructor for class org.libjpegturbo.turbojpeg.TJDecompressor
      -
      Create a TurboJPEG decompressor instance and associate the YUV planar - 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 - image stored in jpegImage with the newly created instance.
      + source image stored in jpegImage with the newly created + instance.
      -
      TJTransformer(byte[], int) - Constructor for class org.libjpegturbo.turbojpeg.TJTransformer
      +
      TJTransformer(byte[], int) - Constructor for class org.libjpegturbo.turbojpeg.TJTransformer
      Create a TurboJPEG lossless transformer instance and associate the JPEG - image of length imageSize bytes stored in + source image of length imageSize bytes stored in jpegImage with the newly created instance.
      -
      transform(byte[][], TJTransform[], int) - Method in class org.libjpegturbo.turbojpeg.TJTransformer
      +
      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(byte[][], TJTransform[], int) - Method in class org.libjpegturbo.turbojpeg.TJTransformer
      +
      + +
      +
      transform(TJTransform[]) - Method in class org.libjpegturbo.turbojpeg.TJTransformer
      -
      Losslessly transform the JPEG image associated with this transformer - instance into one or more JPEG images stored in the given destination - buffers.
      +
      Losslessly transform the JPEG source image associated with this + transformer instance 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
      +
      transform(TJTransform[], int) - Method in class org.libjpegturbo.turbojpeg.TJTransformer
      -
      Losslessly transform the JPEG image associated with this transformer - instance and return an array of TJDecompressor instances, each of - which has a transformed JPEG image associated with it.
      +
      - + -

      Y

      +

      U

      -
      yuvHeight - Variable in class org.libjpegturbo.turbojpeg.YUVImage
      -
       
      -
      yuvImage - Variable in class org.libjpegturbo.turbojpeg.TJDecompressor
      -
       
      -
      YUVImage - Class in org.libjpegturbo.turbojpeg
      +
      UNCROPPED - Static variable in class org.libjpegturbo.turbojpeg.TJ
      -
      This class encapsulates a YUV planar image and the metadata - associated with it.
      +
      A java.awt.Rectangle instance that specifies no cropping
      -
      YUVImage(int, int[], int, int) - Constructor for class org.libjpegturbo.turbojpeg.YUVImage
      +
      UNSCALED - Static variable in class org.libjpegturbo.turbojpeg.TJ
      -
      Create a new YUVImage instance backed by separate image - planes, and allocate memory for the image planes.
      +
      A TJScalingFactor instance that specifies a scaling factor of 1/1 + (no scaling)
      -
      YUVImage(int, int, int, int) - Constructor for class org.libjpegturbo.turbojpeg.YUVImage
      +
      + + + +

      Y

      +
      +
      YUVImage - Class in org.libjpegturbo.turbojpeg
      -
      Create a new YUVImage instance backed by a unified image - buffer, and allocate memory for the image buffer.
      +
      This class encapsulates a planar YUV image and the metadata + associated with it.
      -
      YUVImage(byte[][], int[], int, int[], int, int) - Constructor for class org.libjpegturbo.turbojpeg.YUVImage
      +
      YUVImage(byte[][], int[], int, int[], int, int) - Constructor for class org.libjpegturbo.turbojpeg.YUVImage
      Create a new YUVImage instance from a set of existing image planes.
      -
      YUVImage(byte[], int, int, int, int) - Constructor for class org.libjpegturbo.turbojpeg.YUVImage
      +
      YUVImage(byte[], int, int, int, int) - Constructor for class org.libjpegturbo.turbojpeg.YUVImage
      -
      Create a new YUVImage instance from an existing unified image +
      Create a new YUVImage instance from an existing unified buffer.
      -
      yuvOffsets - Variable in class org.libjpegturbo.turbojpeg.YUVImage
      -
       
      -
      yuvPad - Variable in class org.libjpegturbo.turbojpeg.YUVImage
      -
       
      -
      yuvPlanes - Variable in class org.libjpegturbo.turbojpeg.YUVImage
      -
       
      -
      yuvStrides - Variable in class org.libjpegturbo.turbojpeg.YUVImage
      -
       
      -
      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 4e21075..cae2fdd 100644 --- a/java/doc/index.html +++ b/java/doc/index.html @@ -1,71 +1,23 @@ - + +Generated Documentation (Untitled) - + + + + + - - - - +<body> +<main role="main"> <noscript> -<div>JavaScript is disabled on your browser.</div> +<p>JavaScript is disabled on your browser.</p> </noscript> -<h2>Frame Alert</h2> -<p>This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client. Link to <a href="org/libjpegturbo/turbojpeg/package-summary.html">Non-frame version</a>.</p> - - +

      org/libjpegturbo/turbojpeg/package-summary.html

      + + diff --git a/java/doc/jquery-ui.overrides.css b/java/doc/jquery-ui.overrides.css new file mode 100644 index 0000000..facf852 --- /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 0000000..5093733 --- /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, "
      Deprecated Constructors 
      Constructor and Description
      org.libjpegturbo.turbojpeg.TJCompressor(byte[], int, int, int, int) - +org.libjpegturbo.turbojpeg.TJTransformer.transform​(byte[][], TJTransform[], int) +
      ", "
      " ], + 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 0000000..93d8bc8 --- /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 0000000..775895e --- /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,394 +140,608 @@ 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, - int height, - int subsamp) -
            Deprecated.  - -
            -
            static intbufSizeYUV(int width, - int pad, +bufSizeYUV​(int width, + int align, int height, - int subsamp) -
            Returns the size of the buffer (in bytes) required to hold a YUV planar - image with the given width, height, and level of chrominance subsampling.
            + 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() -
            Returns a list of fractional scaling factors that the JPEG decompressor in - this implementation of TurboJPEG supports.
            +
            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

              @@ -501,6 +749,7 @@ extends java.lang.Object
          +
      @@ -508,12 +757,13 @@ extends java.lang.Object
      • +
          -
        • +
        • Field Detail

          - + - +
            @@ -534,10 +787,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
            +
          - +
            @@ -546,10 +802,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
            +
          - +
            @@ -558,10 +817,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
            +
          - +
            @@ -569,10 +831,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
            +
          - +
            @@ -582,10 +847,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
            +
          - +
            @@ -600,10 +868,56 @@ 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_441

      +
      public static final int SAMP_441
      +
      4:4:1 chrominance subsampling. The JPEG or YUV image will contain one + chrominance component for every 1x4 block of pixels in the source image. + JPEG images compressed with 4:4:1 subsampling will be almost exactly the + same size as those compressed with 4:2:0 subsampling, and in the + aggregate, both subsampling methods produce approximately the same + perceptual quality. However, 4:4:1 is better able to reproduce sharp + vertical features. Note that 4:4:1 subsampling is not fully accelerated + in libjpeg-turbo.
      +
      +
      See Also:
      +
      Constant Field Values
      +
      +
    • +
    + + + +
      +
    • +

      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
      +
      +
    • +
    + - +
      @@ -622,12 +939,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
    +
    - +
      @@ -635,12 +955,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
      +
    - +
      @@ -648,13 +971,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
      +
    - +
      @@ -662,13 +988,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
      +
    - +
      @@ -676,13 +1005,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
      +
    - +
      @@ -690,77 +1022,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
      +
    - +
      @@ -778,12 +1129,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 - CMYK pixels into a YCCK JPEG image (see CS_YCCK) and - decompressing YCCK JPEG images into CMYK pixels. -
      See Also:
      Constant Field Values
      + packed-pixel CMYK images into YCCK JPEG images (see CS_YCCK) and + decompressing YCCK JPEG images into packed-pixel CMYK images. +
      +
      See Also:
      +
      Constant Field Values
      +
    - + - +
      @@ -804,12 +1161,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 any of the extended RGB pixel formats or grayscale, but - they cannot be decompressed to 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
      +
    - +
      @@ -826,14 +1187,18 @@ extends java.lang.Object transformation allowed the same signal to drive both black & white and color televisions, but JPEG images use YCbCr primarily because it allows the color data to be optionally subsampled for the purposes of reducing - bandwidth or disk space. YCbCr is the most common JPEG colorspace, and - YCbCr JPEG images can be compressed from and decompressed to any of the - extended RGB pixel formats or grayscale, or they can be decompressed to - YUV planar images. -
      See Also:
      Constant Field Values
      + network or disk usage. YCbCr is the most common JPEG colorspace, and + YCbCr JPEG images can be compressed from and decompressed to packed-pixel + images with any of the extended RGB or grayscale pixel formats. YCbCr + JPEG images can also be compressed from and decompressed to planar YUV + images. +
      +
      See Also:
      +
      Constant Field Values
      +
    - +
      @@ -842,13 +1207,17 @@ extends java.lang.Object
      public static final int CS_GRAY
      Grayscale colorspace. The JPEG image retains only the luminance data (Y component), and any color data from the source image is discarded. - Grayscale JPEG images can be compressed from and decompressed to any of - the extended RGB pixel formats or grayscale, or they can be decompressed - to YUV planar images.
      -
      See Also:
      Constant Field Values
      + Grayscale JPEG images can be compressed from and decompressed to + packed-pixel images with any of the extended RGB or grayscale pixel + formats, or they can be compressed from and decompressed to planar YUV + images. +
      +
      See Also:
      +
      Constant Field Values
      +
    - +
      @@ -858,11 +1227,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 CMYK pixels.
      -
      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
      +
    - +
      @@ -875,169 +1248,718 @@ extends java.lang.Object reversibly transformed into YCCK, and as with YCbCr, the chrominance components in the YCCK pixels can be subsampled without incurring major perceptual loss. YCCK JPEG images can only be compressed from and - decompressed to CMYK pixels. -
      See Also:
      Constant Field Values
      + decompressed to packed-pixel images with the CMYK pixel format. +
      +
      See Also:
      +
      Constant Field Values
      +
    - +
    • -

      FLAG_BOTTOMUP

      -
      public static final int FLAG_BOTTOMUP
      -
      The uncompressed source/destination image is stored in bottom-up (Windows, - OpenGL) order, not top-down (X11) order.
      -
      See Also:
      Constant Field Values
      +

      PARAM_STOPONWARNING

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

      Value +

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

      FLAG_FORCEMMX

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

      PARAM_BOTTOMUP

      +
      public static final int PARAM_BOTTOMUP
      +
      Row order in packed-pixel source/destination images + +

      Value +

        +
      • 0 [default] top-down (X11) order +
      • 1 bottom-up (Windows, OpenGL) order +
      +
      +
      See Also:
      +
      Constant Field Values
      +
    - +
    • -

      FLAG_FORCESSE

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

      PARAM_QUALITY

      +
      public static final int PARAM_QUALITY
      +
      Perceptual quality of lossy JPEG images [compression only] + +

      Value +

        +
      • 1-100 (1 = worst quality but + best compression, 100 = best quality but worst compression) + [no default; must be explicitly specified] +
      +
      +
      See Also:
      +
      Constant Field Values
      +
    - +
    • -

      FLAG_FORCESSE2

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

      PARAM_SUBSAMP

      +
      public static final int PARAM_SUBSAMP
      +
      Chrominance subsampling level + +

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

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

      Value +

        +
      • One of TJ.SAMP_* [no default; must be + explicitly specified for lossy compression, encoding, and decoding] +
      +
      +
      See Also:
      +
      Constant Field Values
      +
    - +
    • -

      FLAG_FORCESSE3

      +

      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 unless + PARAM_ARITHMETIC is set.

      +
      +
      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. Can be combined with + PARAM_ARITHMETIC. Implies PARAM_OPTIMIZE unless + PARAM_ARITHMETIC is also set.

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

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

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

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

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

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

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

      -
      See Also:
      Constant Field Values
      +
      @Deprecated
      +public static final int FLAG_STOPONWARNING
      +
      Deprecated. +
      Use PARAM_STOPONWARNING instead.
      +
      +
      +
      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.
      -
      See Also:
      Constant Field Values
      +
      @Deprecated
      +public static final int FLAG_PROGRESSIVE
      +
      Deprecated. +
      Use PARAM_PROGRESSIVE instead.
      +
      +
      +
      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
      +
      @Deprecated
      +public static final int FLAG_LIMITSCANS
      +
      Deprecated. +
      Use PARAM_SCANLIMIT instead.
      +
      +
      +
      See Also:
      +
      Constant Field Values
      +
    - + - +
    • ERR_WARNING

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

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

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

        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 pad,
        -             int height,
        -             int subsamp)
        -
        Returns the size of the buffer (in bytes) required to hold a YUV planar - image with the given width, height, and level of chrominance subsampling.
        -
        Parameters:
        width - the width (in pixels) of the YUV image
        pad - the width of each line in each plane of the image is padded to - the nearest multiple of this number of bytes (must be a power of 2.)
        height - the height (in pixels) of the YUV image
        subsamp - the level of chrominance subsampling used in the YUV - image (one of TJ.SAMP_*)
        -
        Returns:
        the size of the buffer (in bytes) required to hold a YUV planar - image with the given width, height, and level of chrominance subsampling.
        -
      • -
      - - - -
        -
      • -

        bufSizeYUV

        -
        @Deprecated
        -public static int bufSizeYUV(int width,
        -                        int height,
        -                        int subsamp)
        -
        Deprecated. Use bufSizeYUV(int, int, int, int) instead.
        +
        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 + 2.) Setting this parameter to n specifies that each row in each plane of + the YUV image will be padded to the nearest multiple of n bytes + (1 = unpadded.)
        +
        height - the height (in pixels) of the YUV image
        +
        subsamp - the level of chrominance subsampling used in the YUV + image (one of TJ.SAMP_*)
        +
        Returns:
        +
        the size of the buffer (in bytes) required to hold a unified + planar YUV image with the given width, height, and level of chrominance + subsampling.
        +
      - +
      • 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 line in the image plane.
        height - height (in pixels) of the YUV image. NOTE: this is the - height of the whole image, not the plane height.
        subsamp - the level of chrominance subsampling used in the YUV - image (one of TJ.SAMP_*)
        -
        Returns:
        the size of the buffer (in bytes) required to hold a YUV planar - image with the given parameters.
        +
        +
        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()
        -
        Returns a list of fractional scaling factors that the JPEG decompressor in - this implementation of TurboJPEG supports.
        -
        Returns:
        a list of fractional scaling factors that the JPEG decompressor in - this implementation of TurboJPEG supports.
        +
        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.
        +
    +
    +
    + diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html b/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html index a53f879..a6e6dfc 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,254 +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, +TJCompressor​(byte[] srcImage, int x, int y, int width, - int height) -
        Create a TurboJPEG compressor instance and associate the uncompressed - source image stored in srcImage with the newly created - instance.
        - -
        TJCompressor(byte[] srcImage, - int width, int pitch, int height, - int pixelFormat) - + int pixelFormat) + +
        Create a TurboJPEG compressor instance and associate the 8-bit-per-sample + packed-pixel source image stored in srcImage with the newly + created instance.
        TJCompressor(byte[] srcImage, +
        TJCompressor​(java.awt.image.BufferedImage srcImage, int x, int y, int width, - int pitch, - int height, - int pixelFormat) -
        Create a TurboJPEG compressor instance and associate the uncompressed - source image stored in srcImage with the newly created - instance.
        + int height)
        +
        Create a TurboJPEG compressor instance and associate the 8-bit-per-sample + packed-pixel source image stored in srcImage with the newly + created instance.
      +
      +
        -
      • +
      • Method Summary

        - - +
        Methods 
        + - + + - + - + - - - - - + - + - + - - - - - + - + - + - + - + - - - + + + - - - + + + + + + + + - - - + + + - - - + + + - + - + - + - + + - + - + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + +
        All Methods Instance Methods Concrete Methods Deprecated Methods 
        Modifier and TypeMethod and DescriptionMethodDescription
        voidclose() +close()
        Free the native structures associated with this compressor instance.
        voidcompress(java.awt.image.BufferedImage srcImage, - byte[] dstBuf, - int flags) - -
        byte[]compress(java.awt.image.BufferedImage srcImage, - int flags) - +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 the uncompressed source image associated with this compressor - instance and output a JPEG image to the given destination buffer.
        -
        byte[]compress(int flags) -
        Compress the uncompressed source image associated with this compressor - instance and return a buffer containing a JPEG image.
        +
        compress​(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.
        voidencodeYUV(java.awt.image.BufferedImage srcImage, - byte[] dstBuf, - int flags) -compress​(byte[] dstBuf, + int flags) +
        Deprecated. +
        Use set() and compress(byte[]) instead.
        byte[]encodeYUV(java.awt.image.BufferedImage srcImage, - int flags) -compress​(int flags) +
        Deprecated. +
        Use set() and compress() instead.
        voidencodeYUV(byte[] dstBuf, - int flags) -
        Deprecated.  - -
        +
        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.
        byte[]encodeYUV(int flags) -
        Deprecated.  -
        Use encodeYUV(int, int) instead.
        +
        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[] strides, - int flags) -
        Encode the uncompressed 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) +
        Deprecated. +
        Use set() and encodeYUV(int) instead.
        +
        YUVImageencodeYUV(int pad, - int flags) -
        Encode the uncompressed source image associated with this compressor - instance into a unified YUV planar image buffer and return a - YUVImage instance containing the encoded image.
        +
        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) -
        Encode the uncompressed source image associated with this compressor - instance into a YUV planar image and store it in the given - YUVImage instance.
        +
        encodeYUV​(YUVImage dstImage, + int flags) +
        Deprecated. +
        Use set() and encodeYUV(YUVImage) + instead.
        +
        protected voidfinalize() finalize() 
        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 an uncompressed 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) - + int pixelFormat) +
        Associate an 8-bit-per-sample packed-pixel RGB, grayscale, or CMYK source + image with this compressor instance.
        voidsetSourceImage(byte[] srcImage, +setSourceImage​(java.awt.image.BufferedImage srcImage, int x, int y, int width, - int pitch, - int height, - int pixelFormat) -
        Associate an uncompressed RGB, grayscale, or CMYK source image with this - compressor instance.
        + int height)
        +
        Associate an 8-bit-per-pixel packed-pixel RGB or grayscale source image + with this compressor instance.
        voidsetSourceImage(YUVImage srcImage) -
        Associate an uncompressed YUV planar source image with this compressor +
        setSourceImage​(YUVImage srcImage) +
        Associate an 8-bit-per-sample planar YUV source image with this compressor instance.
        voidsetSubsamp(int newSubsamp) -
        Set the level of chrominance subsampling for subsequent compress/encode - operations.
        +
        setSourceImage12​(short[] srcImage, + int x, + int y, + int width, + int pitch, + int height, + int pixelFormat) +
        Associate a 12-bit-per-sample packed-pixel RGB, grayscale, or CMYK source + image with this compressor instance.
        +
        voidsetSourceImage16​(short[] srcImage, + int x, + int y, + int width, + int pitch, + int height, + int pixelFormat) +
        Associate a 16-bit-per-sample packed-pixel RGB, grayscale, or CMYK source + image with this compressor instance.
        +
        voidsetSubsamp​(int subsamp) +
        Deprecated. +
        Use + set(TJ.PARAM_SUBSAMP, ...) instead.
        +
          -
        • +
        • Methods inherited from class java.lang.Object

          @@ -366,6 +429,7 @@ implements java.io.Closeable
      +
  • @@ -373,505 +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 an uncompressed RGB, grayscale, or CMYK source image with this - compressor instance.
          -
          Parameters:
          srcImage - image buffer containing RGB, grayscale, or CMYK pixels to - be compressed or encoded. This buffer is not modified.
          x - x offset (in pixels) of the region in the source image from which - the JPEG or YUV image should be compressed/encoded
          y - y offset (in pixels) of the region in the source image from which - the JPEG or YUV image should be compressed/encoded
          width - width (in pixels) of the region in the source image from - which the JPEG or YUV image should be compressed/encoded
          pitch - bytes per line of the source image. Normally, this should be - width * TJ.pixelSize(pixelFormat) if the source image is - unpadded, but you can use this parameter to, for instance, specify that - the scanlines in the source image are padded to a 4-byte boundary or to - compress/encode a JPEG or YUV image from a region of a larger source - image. You can also be clever and use this parameter to skip lines, etc. - Setting this parameter to 0 is the equivalent of setting it to - width * TJ.pixelSize(pixelFormat).
          height - height (in pixels) of the region in the source image from - which the JPEG or YUV image should be compressed/encoded
          pixelFormat - pixel format of the source image (one of - TJ.PF_*)
          -
          Throws:
          -
          TJException
          -
        • -
        - +
        public void setSourceImage​(byte[] srcImage,
        +                           int x,
        +                           int y,
        +                           int width,
        +                           int pitch,
        +                           int height,
        +                           int pixelFormat)
        +                    throws TJException
        +
        Associate an 8-bit-per-sample packed-pixel RGB, grayscale, or CMYK source + image with this compressor instance.
        +
        +
        Parameters:
        +
        srcImage - buffer containing a packed-pixel RGB, grayscale, or CMYK + source image to be compressed or encoded. This buffer is not modified.
        +
        x - x offset (in pixels) of the region in the source image from which + the JPEG or YUV image should be compressed/encoded
        +
        y - y offset (in pixels) of the region in the source image from which + the JPEG or YUV image should be compressed/encoded
        +
        width - width (in pixels) of the region in the source image from + which the JPEG or YUV image should be compressed/encoded
        +
        pitch - bytes per row in the source image. Normally this should be + width * TJ.getPixelSize(pixelFormat), + if the source image is unpadded. (Setting this parameter to 0 is the + equivalent of setting it to width * + TJ.getPixelSize(pixelFormat).) However, + you can also use this parameter to specify the row alignment/padding of + the source image, to skip rows, or to compress/encode a JPEG or YUV image + from a specific region of a larger source image.
        +
        height - height (in pixels) of the region in the source image from + which the JPEG or YUV image should be compressed/encoded
        +
        pixelFormat - pixel format of the source image (one of + TJ.PF_*)
        +
        Throws:
        +
        TJException
        +
        +
      • +
      +
      • -

        setSourceImage

        -
        @Deprecated
        -public void setSourceImage(byte[] srcImage,
        +

        setSourceImage12

        +
        public void setSourceImage12​(short[] srcImage,
        +                             int x,
        +                             int y,
                                      int width,
                                      int pitch,
                                      int height,
                                      int pixelFormat)
        -                    throws TJException
        - -
        Throws:
        -
        TJException
        + 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 an uncompressed RGB or grayscale source image with this - compressor instance.
        -
        Parameters:
        srcImage - a BufferedImage instance containing RGB or - grayscale pixels to be compressed or encoded. This image is not modified.
        x - x offset (in pixels) of the region in the source image from which - 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 +
        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 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 an uncompressed YUV planar source image with this compressor - instance.
        -
        Parameters:
        srcImage - YUV planar image to be compressed. This image is not - modified.
        -
        Throws:
        -
        TJException
        +
        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
        +
      - +
      • -

        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 YUV - planar source. In that case, the level of chrominance subsampling in - the JPEG image is determined by the source. Furthermore, this method has - no effect when encoding to a pre-allocated YUVImage instance. In - that case, the level of chrominance subsampling is determined by the - destination.

        -
        Parameters:
        newSubsamp - the level of chrominance subsampling to use in - subsequent compress/encode oeprations (one of - TJ.SAMP_*)
        -
      • -
      - +

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

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

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

      compress

      -
      public void compress(byte[] dstBuf,
      -            int flags)
      -              throws TJException
      -
      Compress the uncompressed source image associated with this compressor - instance and output a JPEG image to the given destination buffer.
      -
      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
      +

      setSubsamp

      +
      @Deprecated
      +public void setSubsamp​(int subsamp)
      +
      Deprecated. +
      Use + set(TJ.PARAM_SUBSAMP, ...) instead.
      +
      +
    • +
    + + + +
      +
    • +

      setJPEGQuality

      +
      @Deprecated
      +public void setJPEGQuality​(int quality)
      +
      Deprecated. +
      Use + set(TJ.PARAM_QUALITY, ...) instead.
      +
    - +
    • compress

      -
      public byte[] compress(int flags)
      -                throws TJException
      -
      Compress the uncompressed source image associated with this compressor - instance and return a buffer containing a JPEG image.
      -
      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
      +
      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() 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

      -
      @Deprecated
      -public byte[] compress(java.awt.image.BufferedImage srcImage,
      -                         int flags)
      -                throws TJException
      - -
      Throws:
      -
      TJException
      +
      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.
      +
      +
      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 uncompressed source image associated with this compressor - instance into a YUV planar image and store it in the given - YUVImage instance. This method uses the accelerated color - conversion routines in TurboJPEG's underlying codec but does not execute - any of the other steps in the JPEG compression process. Encoding - CMYK source images to YUV is not supported.
      -
      Parameters:
      dstImage - YUVImage instance that will receive the YUV planar - image
      flags - the bitwise OR of one or more of - TJ.FLAG_*
      -
      Throws:
      -
      TJException
      +

      compress

      +
      @Deprecated
      +public byte[] compress​(int flags)
      +                throws TJException
      +
      Deprecated. +
      Use set() and compress() instead.
      +
      +
      +
      Throws:
      +
      TJException
      +
    - +
    • encodeYUV

      -
      @Deprecated
      -public void encodeYUV(byte[] dstBuf,
      -                        int flags)
      -               throws TJException
      -
      Deprecated. Use encodeYUV(YUVImage, int) instead.
      -
      Throws:
      -
      TJException
      +
      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. 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 pad,
      -                 int flags)
      -                   throws TJException
      -
      Encode the uncompressed source image associated with this compressor - instance into a unified YUV planar image buffer and return a - YUVImage instance containing the encoded image. This method - uses the accelerated color conversion routines in TurboJPEG's underlying - codec but does not execute any of the other steps in the JPEG compression - process. Encoding CMYK source images to YUV is not supported.
      -
      Parameters:
      pad - the width of each line in each plane of the YUV image will be - padded to the nearest multiple of this number of bytes (must be a power of - 2.)
      flags - the bitwise OR of one or more of - TJ.FLAG_*
      -
      Returns:
      a YUV planar image.
      -
      Throws:
      -
      TJException
      -
    • -
    - +
    @Deprecated
    +public void encodeYUV​(YUVImage dstImage,
    +                      int flags)
    +               throws TJException
    +
    Deprecated. +
    Use set() and encodeYUV(YUVImage) + instead.
    +
    +
    +
    Throws:
    +
    TJException
    +
    + + +
    • encodeYUV

      -
      public YUVImage encodeYUV(int[] strides,
      -                 int flags)
      -                   throws TJException
      -
      Encode the uncompressed source image associated with this compressor - instance into separate Y, U (Cb), and V (Cr) image planes and return a - YUVImage instance containing the encoded image planes. This - method uses the accelerated color conversion routines in TurboJPEG's - underlying codec but does not execute any of the other steps in the JPEG - compression process. Encoding CMYK source images to YUV is not supported.
      -
      Parameters:
      strides - an array of integers, each specifying the number of bytes - per line in the corresponding plane of the output image. Setting the - stride for any plane to 0 is the same as setting it to the component width - of the plane. If strides is null, then the strides for all - planes will be set to their respective component widths. You can adjust - the strides in order to add an arbitrary amount of line padding to each - plane.
      flags - the bitwise OR of one or more of - TJ.FLAG_*
      -
      Returns:
      a YUV planar image.
      -
      Throws:
      -
      TJException
      -
    • -
    - +
    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:
    +
    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

      -
      @Deprecated
      -public void encodeYUV(java.awt.image.BufferedImage srcImage,
      -                        byte[] dstBuf,
      -                        int flags)
      -               throws TJException
      - -
      Throws:
      -
      TJException
      +
      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 412dcd4..536f400 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 - 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 + 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 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 6666e4e..06de168 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,325 +148,444 @@ 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 stored in jpegImage with the newly created instance.
        + image or "abbreviated table specification" (AKA "tables-only") datastream + stored in jpegImage with the newly created instance.
        TJDecompressor(byte[] jpegImage, - int imageSize) +TJDecompressor​(byte[] jpegImage, + int imageSize)
        Create a TurboJPEG decompressor instance and associate the JPEG source - image of length imageSize bytes stored in - jpegImage with the newly created instance.
        + image or "abbreviated table specification" (AKA "tables-only") datastream + of length imageSize bytes stored in jpegImage + with the newly created instance.
        TJDecompressor(YUVImage yuvImage) -
        Create a TurboJPEG decompressor instance and associate the YUV planar - 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 YUV source image associated - with this decompressor instance and output a decompressed/decoded image to - the given BufferedImage instance.
        -
        voiddecompress(byte[] dstBuf, - int desiredWidth, - int pitch, - int desiredHeight, - int pixelFormat, - int flags) - -
        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 YUV source image associated - with this decompressor instance and output a 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 YUV source image associated - with this decompressor instance and output a 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 YUV source image associated - with this decompressor instance and return a BufferedImage - instance containing the 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 associated with this decompressor - instance and return a buffer containing the decompressed image.
        + int flags)
        +
        Deprecated. + +
        voiddecompressToYUV(byte[] dstBuf, - int flags) -
        Deprecated.  - +
        decompress​(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[]decompressToYUV(int flags) -
        Deprecated.  - -
        +
        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.
        YUVImagedecompressToYUV(int desiredWidth, +
        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, - int pad, +
        YUVImagedecompressToYUV​(int desiredWidth, + int align, int desiredHeight, - int flags) -
        Decompress the JPEG source image associated with this decompressor - instance into a unified YUV planar image buffer and return a - YUVImage instance containing the decompressed image.
        + int flags)
        +
        Deprecated. + +
        voiddecompressToYUV(YUVImage dstImage, - int flags) -
        Decompress the JPEG source image associated with this decompressor - instance into a YUV planar image and store it in the given - YUVImage instance.
        +
        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.
        intgetColorspace() +
        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() -
        Returns the JPEG image buffer associated with this decompressor instance.
        +
        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.
        voidsetJPEGImage(byte[] jpegImage, - int imageSize) -
        Deprecated.  - -
        +
        set​(int param, + int value) +
        Set the value of a decompression parameter.
        voidsetSourceImage(byte[] jpegImage, - int imageSize) +setCroppingRegion​(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) -
        Associate the specified YUV planar source image with this decompressor +
        setSourceImage​(YUVImage srcImage) +
        Associate the specified planar YUV source image with this decompressor instance.
          -
        • +
        • Methods inherited from class java.lang.Object

          @@ -440,777 +593,1067 @@ implements java.io.Closeable
      +
    • - + +
        -
      • +
      • -

        Field Detail

        - +

        Constructor Detail

        +
        • -

          handle

          -
          protected long handle
          +

          TJDecompressor

          +
          public TJDecompressor()
          +               throws TJException
          +
          Create a TurboJPEG decompresssor instance.
          +
          +
          Throws:
          +
          TJException
          +
        - +
        • -

          jpegBuf

          -
          protected byte[] jpegBuf
          +

          TJDecompressor

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

          jpegBufSize

          -
          protected int jpegBufSize
          +

          TJDecompressor

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

            yuvImage

            -
            protected YUVImage yuvImage
            +

            TJDecompressor

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

        Method Detail

        +
        • -

          jpegWidth

          -
          protected int jpegWidth
          +

          setSourceImage

          +
          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 + jpegImage contains a JPEG image, then this image will be used + as the source image for subsequent decompression operations. Passing a + tables-only datastream to this method primes the decompressor with + quantization and Huffman tables that can be used when decompressing + subsequent "abbreviated image" datastreams. This is useful, for instance, + when decompressing video streams in which all frames share the same + 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
          +
        - +
        • -

          jpegHeight

          -
          protected int jpegHeight
          +

          setSourceImage

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

          jpegSubsamp

          -
          protected int jpegSubsamp
          +

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

            jpegColorspace

            -
            protected int jpegColorspace
            -
          • -
          +

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

          Constructor Detail

          - +
          • -

            TJDecompressor

            -
            public TJDecompressor()
            -               throws TJException
            -
            Create a TurboJPEG decompresssor instance.
            -
            Throws:
            -
            TJException
            +

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

            TJDecompressor

            -
            public TJDecompressor(byte[] jpegImage)
            -               throws TJException
            -
            Create a TurboJPEG decompressor instance and associate the JPEG source - image stored in jpegImage with the newly created instance.
            -
            Parameters:
            jpegImage - JPEG image buffer (size of the JPEG image is assumed to - be the length of the array.) This buffer is not modified.
            -
            Throws:
            -
            TJException
            +

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

            TJDecompressor

            -
            public TJDecompressor(byte[] jpegImage,
            -              int imageSize)
            -               throws TJException
            -
            Create a TurboJPEG decompressor instance and associate the JPEG source - image of length imageSize bytes stored in - jpegImage with the newly created instance.
            -
            Parameters:
            jpegImage - JPEG image buffer. This buffer is not modified.
            imageSize - size of the JPEG image (in bytes)
            -
            Throws:
            -
            TJException
            +

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

              TJDecompressor

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

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

            Method Detail

            - +
            • -

              setSourceImage

              -
              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 - jpegImage contains a JPEG image, then this image will be used - as the source image for subsequent decompress operations. Passing a - tables-only datastream to this method primes the decompressor with - quantization and Huffman tables that can be used when decompressing - subsequent "abbreviated image" datastreams. This is useful, for instance, - when decompressing video streams in which all frames share the same - quantization and Huffman tables.
              -
              Parameters:
              jpegImage - buffer containing a JPEG image or an "abbreviated table - specification" (AKA "tables-only") datastream. This buffer is not - modified.
              imageSize - size of the JPEG image (in bytes)
              -
              Throws:
              -
              TJException
              +

              getSubsamp

              +
              @Deprecated
              +public int getSubsamp()
              +
              Deprecated. +
              Use get(TJ.PARAM_SUBSAMP) + instead.
              +
            - + - +
            • -

              setSourceImage

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

              getJPEGBuf

              +
              public byte[] getJPEGBuf()
              +
              Returns the JPEG buffer associated with this decompressor instance.
              +
              +
              Returns:
              +
              the JPEG buffer associated with this decompressor instance.
              +
            - +
            • -

              getWidth

              -
              public int getWidth()
              -
              Returns the width of the source image (JPEG or YUV) associated with this +

              getJPEGSize

              +
              public int getJPEGSize()
              +
              Returns the size of the JPEG image (in bytes) associated with this decompressor instance.
              -
              Returns:
              the width of the source image (JPEG or YUV) associated with this - decompressor instance.
              +
              +
              Returns:
              +
              the size of the JPEG image (in bytes) associated with this + decompressor instance.
              +
            - +
            • -

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

              getScaledWidth

              +
              @Deprecated
              +public int getScaledWidth​(int desiredWidth,
              +                          int desiredHeight)
              +
              Deprecated. + +
            - +
            • -

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

              getScaledHeight

              +
              @Deprecated
              +public int getScaledHeight​(int desiredWidth,
              +                           int desiredHeight)
              +
              Deprecated. + +
            - +
            • -

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

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

              getJPEGBuf

              -
              public byte[] getJPEGBuf()
              -
              Returns the JPEG image buffer associated with this decompressor instance.
              -
              Returns:
              the JPEG image buffer associated with this decompressor instance.
              +

              decompress

              +
              @Deprecated
              +public void decompress​(byte[] dstBuf,
              +                       int x,
              +                       int y,
              +                       int desiredWidth,
              +                       int pitch,
              +                       int desiredHeight,
              +                       int pixelFormat,
              +                       int flags)
              +                throws TJException
              + +
              +
              Throws:
              +
              TJException
              +
            - +
            • -

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

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

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

              decompress

              +
              @Deprecated
              +public byte[] decompress​(int desiredWidth,
              +                         int pitch,
              +                         int desiredHeight,
              +                         int pixelFormat,
              +                         int flags)
              +                  throws TJException
              +
              Deprecated. + +
              +
              +
              Throws:
              +
              TJException
              +
            - +
            • -

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

              decompress12

              +
              public void decompress12​(short[] dstBuf,
              +                         int x,
              +                         int y,
              +                         int pitch,
              +                         int pixelFormat)
              +                  throws TJException
              +
              Decompress the 12-bit-per-sample JPEG source image associated with this + decompressor instance and output a 12-bit-per-sample packed-pixel + grayscale, RGB, or CMYK image to the given destination buffer. +

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

              +
              +
              Parameters:
              +
              dstBuf - buffer that will receive the packed-pixel + decompressed image. This buffer should normally be + pitch * destinationHeight samples in size. However, the + buffer may also be larger, in which case the x, + y, and pitch parameters can be used to specify + the region into which the source image should be decompressed. NOTE: If + the source image is a lossy JPEG image, then + destinationHeight is either the scaled JPEG height (see + setScalingFactor(), + TJScalingFactor.getScaled(), and + getHeight()) or the height of the cropping region (see + setCroppingRegion().) If the source image is a + lossless JPEG image, then destinationHeight is the height of + the source image.
              +
              x - x offset (in pixels) of the region in the destination image into + which the source image should be decompressed
              +
              y - y offset (in pixels) of the region in the destination image into + which the source image should be decompressed
              +
              pitch - samples per row in the destination image. Normally this + should be set to destinationWidth * + TJ.getPixelSize(pixelFormat), if the + destination image will be unpadded. (Setting this parameter to 0 is the + equivalent of setting it to destinationWidth * + TJ.getPixelSize(pixelFormat).) However, + you can also use this parameter to specify the row alignment/padding of + the destination image, to skip rows, or to decompress into a specific + region of a larger image. NOTE: if the source image is a lossy JPEG + image, then destinationWidth is either the scaled JPEG width + (see setScalingFactor(), + TJScalingFactor.getScaled(), and + getWidth()) or the width of the cropping region (see + setCroppingRegion().) If the source image is a + YUV image or a lossless JPEG image, then destinationWidth is + the width of the source image.
              +
              pixelFormat - pixel format of the decompressed image (one of + TJ.PF_*)
              +
              Throws:
              +
              TJException
              +
            - +
            • -

              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 YUV source image associated - with this decompressor instance and output a grayscale, RGB, or CMYK image - to the given destination buffer. -

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

              -
              Parameters:
              dstBuf - buffer that will receive the decompressed/decoded image. - If the source image is a JPEG image, then this buffer should normally be - pitch * scaledHeight bytes in size, where - scaledHeight can be determined by calling - scalingFactor.getScaled(jpegHeight) - with one of the scaling factors returned from TJ.getScalingFactors() or by calling getScaledHeight(int, int). If the - source image is a YUV image, then this buffer should normally be - pitch * height bytes in size, where height is - the height of the YUV image. However, the buffer may also be larger than - the dimensions of the source image, in which case the x, - y, and pitch parameters can be used to specify - the region into which the source image should be decompressed/decoded.
              x - x offset (in pixels) of the region in the destination image into - 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 line of the destination image. Normally, this - should be set to scaledWidth * TJ.pixelSize(pixelFormat) if - the destination image is unpadded, but you can use this to, for instance, - pad each line of the destination image to a 4-byte boundary or to - decompress/decode the source image into a region of a larger image. NOTE: - if the source image is a JPEG image, then scaledWidth can be - determined by calling - scalingFactor.getScaled(jpegWidth) - or by calling getScaledWidth(int, int). If the source image is a - YUV image, then scaledWidth is the width of the YUV image. - Setting this parameter to 0 is the equivalent of setting it to - scaledWidth * TJ.pixelSize(pixelFormat).
              desiredHeight - If the source image is a JPEG image, then this - specifies the desired height (in pixels) of the decompressed image (or - 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
              +

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

              decompress

              -
              @Deprecated
              -public void decompress(byte[] dstBuf,
              -                         int desiredWidth,
              +

              decompress16

              +
              public void decompress16​(short[] dstBuf,
              +                         int x,
              +                         int y,
                                        int pitch,
              -                         int desiredHeight,
              -                         int pixelFormat,
              -                         int flags)
              -                throws TJException
              - -
              Throws:
              -
              TJException
              + int pixelFormat) + throws TJException
              +
              Decompress the 16-bit-per-sample lossless JPEG source image associated + with this decompressor instance and output a 16-bit-per-sample + packed-pixel grayscale, RGB, or CMYK image to the given destination + buffer. +

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

              +
              +
              Parameters:
              +
              dstBuf - buffer that will receive the packed-pixel + decompressed image. This buffer should normally be + pitch * jpegHeight samples in size. However, the buffer may + also be larger, in which case the x, + y, and pitch parameters can be used to specify + the region into which the source image should be decompressed.
              +
              x - x offset (in pixels) of the region in the destination image into + which the source image should be decompressed
              +
              y - y offset (in pixels) of the region in the destination image into + which the source image should be decompressed
              +
              pitch - samples per row in the destination image. Normally this + should be set to jpegWidth * + TJ.getPixelSize(pixelFormat), if the + destination image will be unpadded. (Setting this parameter to 0 is the + equivalent of setting it to jpegWidth * + TJ.getPixelSize(pixelFormat).) However, + you can also use this parameter to specify the row alignment/padding of + the destination image, to skip rows, or to decompress into a specific + region of a larger image.
              +
              pixelFormat - pixel format of the decompressed image (one of + TJ.PF_*)
              +
              Throws:
              +
              TJException
              +
            - + - +
            • decompressToYUV

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

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

              -
              Parameters:
              dstImage - YUVImage instance that will receive the YUV planar - image. The level of subsampling specified in this YUVImage - instance must match that of the JPEG image, and the width and height - specified in the YUVImage instance must match one of the - scaled image sizes that TurboJPEG is capable of generating from the JPEG - source image.
              flags - the bitwise OR of one or more of - TJ.FLAG_*
              -
              Throws:
              -
              TJException
              + 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 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 an RGB or - grayscale 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 line in the corresponding plane of the output image. Setting the - stride for any plane to 0 is the same as setting it to the scaled - component width of the plane. If strides is NULL, then the - strides for all planes will be set to their respective scaled component - widths. You can adjust the strides in order to add an arbitrary amount of - line padding to each plane.
              desiredHeight - desired height (in pixels) of the YUV image. If the - 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 YUV planar image.
              -
              Throws:
              -
              TJException
              +
              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 desiredWidth,
              -                       int pad,
              -                       int desiredHeight,
              -                       int flags)
              -                         throws TJException
              -
              Decompress the JPEG source image associated with this decompressor - instance into a unified YUV planar image buffer and return a - YUVImage instance containing the decompressed image. This - method performs JPEG decompression but leaves out the color conversion - step, so a planar YUV image is generated instead of an RGB or grayscale - image. This method cannot be used to decompress JPEG source images with - the CMYK or YCCK colorspace.
              -
              Parameters:
              desiredWidth - desired width (in pixels) of the YUV image. If the - desired image dimensions are different than the dimensions of the JPEG - image being decompressed, then TurboJPEG will use scaling in the JPEG - decompressor to generate the largest possible image that will fit within - the desired dimensions. Setting this to 0 is the same as setting it to - the width of the JPEG image (in other words, the width will not be - considered when determining the scaled image size.)
              pad - the width of each line in each plane of the YUV image will be - padded to the nearest multiple of this number of bytes (must be a power of - 2.)
              desiredHeight - desired height (in pixels) of the YUV image. If the - 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 YUV planar image.
              -
              Throws:
              -
              TJException
              +
              @Deprecated
              +public YUVImage decompressToYUV​(int desiredWidth,
              +                                int[] strides,
              +                                int desiredHeight,
              +                                int flags)
              +                         throws TJException
              +
              Deprecated. + +
              +
              +
              Throws:
              +
              TJException
              +
            - +
            • decompressToYUV

              -
              @Deprecated
              -public byte[] decompressToYUV(int flags)
              -                       throws TJException
              -
              Deprecated. Use decompressToYUV(int, int, int, int) instead.
              -
              Throws:
              -
              TJException
              +
              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.)
              +
              Returns:
              +
              a YUVImage instance containing the unified planar YUV + decompressed image
              +
              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 YUV source image associated - with this decompressor instance and output a grayscale, RGB, or CMYK image - to the given destination buffer. +

              decompressToYUV

              +
              @Deprecated
              +public YUVImage decompressToYUV​(int desiredWidth,
              +                                int align,
              +                                int desiredHeight,
              +                                int flags)
              +                         throws TJException
              +
              Deprecated. + +
              +
              +
              Throws:
              +
              TJException
              +
              +
            • +
            + + + +
              +
            • +

              decompress8

              +
              public void decompress8​(int[] dstBuf,
              +                        int x,
              +                        int y,
              +                        int stride,
              +                        int pixelFormat)
              +                 throws TJException
              +
              Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + source image associated with this decompressor instance and output an + 8-bit-per-sample packed-pixel grayscale, RGB, or CMYK image to the given + destination buffer.

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

              -
              Parameters:
              dstBuf - buffer that will receive the decompressed/decoded image. - If the source image is a JPEG image, then this buffer should normally be - stride * scaledHeight pixels in size, where - scaledHeight can be determined by calling - scalingFactor.getScaled(jpegHeight) - with one of the scaling factors returned from TJ.getScalingFactors() or by calling getScaledHeight(int, int). If the - source image is a YUV image, then this buffer should normally be - stride * height pixels in size, where height is - the height of the YUV image. However, the buffer may also be larger than - the dimensions of the JPEG image, in which case the x, - y, and stride parameters can be used to specify - the region into which the source image should be decompressed.
              x - x offset (in pixels) of the region in the destination image into - 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 line of 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
              + 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(java.awt.image.BufferedImage dstImage,
      -              int flags)
      -                throws TJException
      -
      Decompress the JPEG source image or decode the YUV source image associated - with this decompressor instance and output a decompressed/decoded image to - the given BufferedImage instance. +
      @Deprecated
      +public void decompress​(int[] dstBuf,
      +                       int x,
      +                       int y,
      +                       int desiredWidth,
      +                       int stride,
      +                       int desiredHeight,
      +                       int pixelFormat,
      +                       int flags)
      +                throws TJException
      + +
      +
      Throws:
      +
      TJException
      +
      +
    • +
    + + + +
      +
    • +

      decompress8

      +
      public void decompress8​(java.awt.image.BufferedImage dstImage)
      +                 throws TJException
      +
      Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + source image associated with this decompressor instance and output an + 8-bit-per-sample packed-pixel decompressed/decoded image to the given + BufferedImage instance.

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

      -
      Parameters:
      dstImage - a BufferedImage instance that will receive - the decompressed/decoded image. If the source image is a JPEG image, then - the width and height of the BufferedImage instance must match - one of the scaled image sizes that TurboJPEG is capable of generating from - the JPEG image. If the source image is a YUV image, then the width and - height of the BufferedImage instance must match the width and - height of the YUV image.
      flags - the bitwise OR of one or more of - 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:
    +
    dstImage - a BufferedImage instance that will receive + the packed-pixel decompressed/decoded image. If the source image is a + lossy JPEG image, then the width and height of the + BufferedImage instance must match the scaled JPEG width and + height (see setScalingFactor(), + TJScalingFactor.getScaled(), + getWidth(), and getHeight()) or the width and height of the + cropping region (see setCroppingRegion().) If + the source image is a YUV image or a lossless JPEG image, then the width + and height of the BufferedImage instance must match the width + and height of the source image.
    +
    Throws:
    +
    TJException
    +
    - + + + + +
      +
    • +

      decompress8

      +
      public java.awt.image.BufferedImage decompress8​(int bufferedImageType)
      +                                         throws TJException
      +
      Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + source image associated with this decompressor instance and return a + BufferedImage instance containing the 8-bit-per-sample + packed-pixel decompressed/decoded image.
      +
      +
      Parameters:
      +
      bufferedImageType - the image type of the BufferedImage instance that will be created (for instance, - BufferedImage.TYPE_INT_RGB)
      flags - the bitwise OR of one or more of - TJ.FLAG_*
      -
      Returns:
      a BufferedImage instance containing the - decompressed/decoded image.
      -
      Throws:
      -
      TJException
      + BufferedImage.TYPE_INT_RGB) +
      Returns:
      +
      a BufferedImage instance containing the + 8-bit-per-sample packed-pixel decompressed/decoded image.
      +
      Throws:
      +
      TJException
      +
    - + + + + +
    • close

      -
      public void close()
      -           throws TJException
      +
      public void close()
      +           throws TJException
      Free the native structures associated with this decompressor instance.
      -
      Specified by:
      -
      close in interface java.io.Closeable
      -
      Specified by:
      +
      Specified by:
      close in interface java.lang.AutoCloseable
      -
      Throws:
      -
      TJException
      +
      Specified by:
      +
      close in interface java.io.Closeable
      +
      Throws:
      +
      TJException
      +
    - +
    • finalize

      -
      protected void finalize()
      +
      protected void finalize()
                        throws java.lang.Throwable
      -
      Overrides:
      +
      Overrides:
      finalize in class java.lang.Object
      -
      Throws:
      -
      java.lang.Throwable
      +
      Throws:
      +
      java.lang.Throwable
      +
    + +
    + diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJException.html b/java/doc/org/libjpegturbo/turbojpeg/TJException.html index 66d73e7..5c2905d 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 4006bac..caa5c58 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 5f22691..0c2f5cc 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,149 +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_COPYNONE -
        This option will prevent TJTransformer.transform() from copying any extra markers (including EXIF - and ICC profile data) from the source image to the output image.
        +
        OPT_ARITHMETIC +
        This option will enable arithmetic entropy coding in the JPEG image + generated by this particular transform.
        static intOPT_CROP -
        This option will enable lossless cropping.
        +
        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_GRAY -
        This option will discard the color data in the input image and produce - a grayscale output image.
        +
        OPT_CROP +
        This option will enable lossless cropping.
        static intOPT_NOOUTPUT -
        This option will prevent TJTransformer.transform() from outputting a JPEG image for this +
        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 particular transform.
        static intOPT_OPTIMIZE +
        This option will enable optimized baseline entropy coding in the JPEG + image generated by this particular transform.
        +
        static intOPT_PERFECT -
        This option will cause TJTransformer.transform() to throw an exception if the transform is not +
        OPT_PERFECT +
        This option will cause TJTransformer.transform() to throw an exception if the transform is not perfect.
        static intOPT_PROGRESSIVE -
        This option will enable progressive entropy coding in the output image +
        OPT_PROGRESSIVE +
        This option will enable progressive entropy coding in the JPEG image generated by this particular transform.
        static intOPT_TRIM +OPT_TRIM
        This option will discard any partial MCU blocks that cannot be transformed.
        intoptions -
        Transform options (bitwise OR of one or more of OPT_*)
        +
        options +
        Transform options (bitwise OR of one or more of + OPT_*)
          -
        • +
        • Fields inherited from class java.awt.Rectangle

          height, width, x, y
          -
        • +
        • Fields inherited from class java.awt.geom.Rectangle2D

          @@ -290,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

          @@ -371,6 +450,7 @@ extends java.awt.Rectangle
      +
    @@ -378,12 +458,13 @@ extends java.awt.Rectangle
    • +
        -
      • +
      • Field Detail

        - + - +
          @@ -402,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
          +
        - +
          @@ -526,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
          +
        - + - +
        • OPT_GRAY

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

          public static final int OPT_PROGRESSIVE
          -
          This option will enable progressive entropy coding in the output image +
          This option will enable progressive entropy coding in the JPEG image generated by this particular transform. Progressive entropy coding will generally improve compression relative to baseline entropy coding (the - default), but it will reduce compression and decompression performance - considerably.
          -
          See Also:
          Constant Field Values
          + default), but it will reduce decompression performance considerably. + Can be combined with OPT_ARITHMETIC. Implies + OPT_OPTIMIZE unless OPT_ARITHMETIC is also specified.
          +
          +
          See Also:
          +
          Constant Field Values
          +
        - + - + + + +
          +
        • +

          OPT_ARITHMETIC

          +
          public static final int OPT_ARITHMETIC
          +
          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 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_*)
        - +
        • options

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

        - +
          @@ -641,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 a30fe30..83836ea 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
  • @@ -224,149 +277,219 @@ 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 - image stored in jpegImage with the newly created instance.
        -
        Parameters:
        jpegImage - JPEG image buffer (size of the JPEG image is assumed to - be the length of the array.) This buffer is not modified.
        -
        Throws:
        -
        TJException
        + source image stored in jpegImage with the newly created + instance. +
        +
        Parameters:
        +
        jpegImage - buffer containing the JPEG source image to transform. + (The size of the JPEG image is assumed to be the length of the array.) + This buffer is not modified.
        +
        Throws:
        +
        TJException
        +
      - +
      • 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 - image of length imageSize bytes stored in + source image of length imageSize bytes stored in jpegImage with the newly created instance.
        -
        Parameters:
        jpegImage - JPEG image buffer. This buffer is not modified.
        imageSize - size of the JPEG image (in bytes)
        -
        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
        +
    +
    +
      -
    • +
    • Method Detail

      - +
      • transform

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

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

        -
        public int[] getTransformedSizes()
        +
        public int[] getTransformedSizes()
        Returns an array containing the sizes of the transformed JPEG images - generated by the most recent transform operation.
        -
        Returns:
        an array containing the sizes of the transformed JPEG images - generated by the most recent transform operation.
        + (in bytes) generated by the most recent transform operation. +
        +
        Returns:
        +
        an array containing the sizes of the transformed JPEG images + (in bytes) generated by the most recent transform operation.
        +
    +
  • +
    + diff --git a/java/doc/org/libjpegturbo/turbojpeg/YUVImage.html b/java/doc/org/libjpegturbo/turbojpeg/YUVImage.html index d4485ed..2654c21 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,10 +130,9 @@

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

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

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

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

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

        - +
        - + + - + - + - + - +
        Constructors 
        Constructor and DescriptionConstructorDescription
        YUVImage(byte[][] planes, +YUVImage​(byte[][] planes, int[] offsets, int width, int[] strides, int height, - int subsamp) + int subsamp)
        Create a new YUVImage instance from a set of existing image planes.
        YUVImage(byte[] yuvImage, +YUVImage​(byte[] yuvImage, int width, - int pad, + int align, int height, - int subsamp) -
        Create a new YUVImage instance from an existing unified image + 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, - int pad, +YUVImage​(int width, + int align, int height, - int subsamp) -
        Create a new YUVImage instance backed by a unified image - buffer, and allocate memory for the image buffer.
        + 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() -
        Returns the YUV image buffer (if this image is stored in a unified - buffer rather than separate image planes.)
        +
        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() -
        Returns the line padding used in the YUV image buffer (if this image is +
        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() -
        Returns the size (in bytes) of the YUV image buffer (if this image is - stored in a unified buffer rather than separate image planes.)
        +
        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() -
        Returns the number of bytes per line of each plane in the YUV image.
        +
        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 pad, + int align, int height, - int subsamp) -
        Assign a unified image buffer to this YUVImage instance.
        + int subsamp)
        +
        Assign a unified buffer to this YUVImage instance.
          -
        • +
        • Methods inherited from class java.lang.Object

          @@ -337,380 +346,383 @@ extends java.lang.Object
      +
    • - -
        -
      • - - -

        Field Detail

        - - - -
          -
        • -

          handle

          -
          protected long handle
          -
        • -
        - - - -
          -
        • -

          yuvPlanes

          -
          protected byte[][] yuvPlanes
          -
        • -
        - - - -
          -
        • -

          yuvOffsets

          -
          protected int[] yuvOffsets
          -
        • -
        - - - -
          -
        • -

          yuvStrides

          -
          protected int[] yuvStrides
          -
        • -
        - - - -
          -
        • -

          yuvPad

          -
          protected int yuvPad
          -
        • -
        - - - -
          -
        • -

          yuvWidth

          -
          protected int yuvWidth
          -
        • -
        - - - -
          -
        • -

          yuvHeight

          -
          protected int yuvHeight
          -
        • -
        - - - -
          -
        • -

          yuvSubsamp

          -
          protected int yuvSubsamp
          -
        • -
        -
      • -
      +
      +
      • 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 + TJ.planeSizeYUV(i, width, strides[i], height, subsamp) + bytes in size.
        +
        offsets - If this YUVImage instance represents a subregion of a larger image, then offsets[i] specifies the offset (in bytes) of the subregion within plane i of the larger image. Setting this to null is the same as setting the offsets for - all planes to 0.
        width - width (in pixels) of the new YUV image (or subregion)
        strides - an array of integers, each specifying the number of bytes - per line in the corresponding plane of the YUV image. Setting the stride + 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 line padding + can adjust the strides in order to add an arbitrary amount of row padding to each plane or to specify that this YUVImage instance is a subregion of a larger image (in which case, strides[i] should - be set to the plane width of plane i in the larger image.)
        height - height (in pixels) of the new YUV image (or subregion)
        subsamp - the level of chrominance subsampling used in the YUV - 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 pad,
        -        int height,
        -        int subsamp)
        -
        Create a new YUVImage instance from an existing unified image +
        public YUVImage​(byte[] yuvImage,
        +                int width,
        +                int align,
        +                int height,
        +                int subsamp)
        +
        Create a new YUVImage instance from an existing unified buffer.
        -
        Parameters:
        yuvImage - image buffer that contains or will contain YUV planar - image data. Use TJ.bufSizeYUV(int, int, int, int) to determine the minimum size for - this buffer. The Y, U (Cb), and V (Cr) image planes are stored - sequentially in the buffer (see above for a description - of the image format.)
        width - width (in pixels) of the YUV image
        pad - the line padding used in the YUV image buffer. For - instance, if each line in each plane of the buffer is padded to the - nearest multiple of 4 bytes, then pad should be set to 4.
        height - height (in pixels) of the YUV image
        subsamp - the level of chrominance subsampling used in the YUV - image (one of TJ.SAMP_*)
        -
      • -
      -
    • -
    +
    +
    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_*)
    +
    + + + + + +
      -
    • +
    • 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 + TJ.planeSizeYUV(i, width, strides[i], height, subsamp) + bytes in size.
        +
        offsets - If this YUVImage instance represents a subregion of a larger image, then offsets[i] specifies the offset (in bytes) of the subregion within plane i of the larger image. Setting this to null is the same as setting the offsets for - all planes to 0.
        width - width (in pixels) of the YUV image (or subregion)
        strides - an array of integers, each specifying the number of bytes - per line in the corresponding plane of the YUV image. Setting the stride + 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 line padding - to each plane or to specify that this YUVImage image is a + can adjust the strides in order to add an arbitrary amount of row padding + to each plane or to specify that this YUVImage instance is a subregion of a larger image (in which case, strides[i] should - be set to the plane width of plane i in the larger image.)
        height - height (in pixels) of the YUV image (or subregion)
        subsamp - the level of chrominance subsampling used in the YUV - image (one of TJ.SAMP_*)
        + 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 pad,
        -          int height,
        -          int subsamp)
        -
        Assign a unified image buffer to this YUVImage instance.
        -
        Parameters:
        yuvImage - image buffer that contains or will contain YUV planar - image data. Use TJ.bufSizeYUV(int, int, int, int) to determine the minimum size for - this buffer. The Y, U (Cb), and V (Cr) image planes are stored - sequentially in the buffer (see above for a description - of the image format.)
        width - width (in pixels) of the YUV image
        pad - the line padding used in the YUV image buffer. For - instance, if each line in each plane of the buffer is padded to the - nearest multiple of 4 bytes, then pad should be set to 4.
        height - height (in pixels) of the YUV image
        subsamp - the level of chrominance subsampling used in the YUV - image (one of TJ.SAMP_*)
        -
      • -
      - +
      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() 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_*)
      +
      +
    • +
    +
    • 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()
      -
      Returns the line padding used in the YUV image buffer (if this image is +
      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 line padding used in the YUV image buffer
      +
      +
      Returns:
      +
      the row alignment of the YUV buffer
      +
    - +
    • getStrides

      -
      public int[] getStrides()
      -
      Returns the number of bytes per line of each plane in the YUV image.
      -
      Returns:
      the number of bytes per line of each plane in the YUV image
      +
      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
      +
    - +
    • 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()
      -
      Returns the YUV image buffer (if this image is stored in a unified - buffer rather than separate image planes.)
      -
      Returns:
      the YUV image buffer
      +
      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
      +
    - +
    • getSize

      -
      public int getSize()
      -
      Returns the size (in bytes) of the YUV image buffer (if this image is - stored in a unified buffer rather than separate image planes.)
      -
      Returns:
      the size (in bytes) of the YUV image buffer
      +
      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
      +
    + +
    + 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 08a8bf8..0000000 --- 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 dedcce5..0a027c3 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,45 +122,45 @@ - + - + - + - + - + - + - + @@ -139,7 +168,7 @@
      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 YUV planar image and the metadata +
      This class encapsulates a planar YUV image and the metadata associated with it.
    • - +
      @@ -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 5f0f8c3..ca8d36a 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 b659995..d2d6d53 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 0000000..7f0700b --- /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..b55d555a2ae1dce3fae814b099096f41ddf938ca GIT binary patch literal 237 zcmWIWW@Zs#;Nak3a9oxd#()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 b346356..7dc93c4 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 0000000..8492271 --- /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 45bbc86..33cde53 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

      • +

        Package org.libjpegturbo.turbojpeg

          -
        • +
        • Class org.libjpegturbo.turbojpeg.TJException extends java.io.IOException implements Serializable

          @@ -79,9 +109,7 @@
          1L
            -
          • - - +
          • Serialized Fields

            • @@ -92,7 +120,7 @@
          • -
          • +
          • Class org.libjpegturbo.turbojpeg.TJTransform extends java.awt.Rectangle implements Serializable

            @@ -101,38 +129,43 @@
            -127367705761430371L
              -
            • - - +
            • Serialized Fields

              • +

                cf

                +
                TJCustomFilter cf
                +
                Custom filter instance
                +
              • +
              • op

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

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

                cf

                -
                TJCustomFilter cf
                -
                Custom filter instance
                +
                Transform options (bitwise OR of one or more of + OPT_*)
          +
      +
      + diff --git a/java/doc/stylesheet.css b/java/doc/stylesheet.css index 0aeaa97..de945ed 100644 --- a/java/doc/stylesheet.css +++ b/java/doc/stylesheet.css @@ -1,73 +1,109 @@ -/* Javadoc style sheet */ +/* + * Javadoc style sheet + */ + +@import url('resources/fonts/dejavu.css'); + /* -Overall document style -*/ + * Styles for individual HTML elements. + * + * These are styles that are specific to individual HTML elements. Changing them affects the style of a particular + * HTML element throughout the page. + */ + body { background-color:#ffffff; color:#353833; - font-family:Arial, Helvetica, sans-serif; - font-size:76%; + font-family:'DejaVu Sans', Arial, Helvetica, sans-serif; + font-size:14px; margin:0; + padding:0; + height:100%; + width:100%; +} +iframe { + margin:0; + padding:0; + height:100%; + width:100%; + overflow-y:scroll; + border:none; } a:link, a:visited { text-decoration:none; - color:#4c6b87; + color:#4A6782; } -a:hover, a:focus { +a[href]:hover, a[href]:focus { text-decoration:none; color:#bb7a2a; } -a:active { - text-decoration:none; - color:#4c6b87; -} a[name] { color:#353833; } -a[name]:hover { - text-decoration:none; - color:#353833; +a[name]:before, a[name]:target, a[id]:before, a[id]:target { + content:""; + display:inline-block; + position:relative; + padding-top:129px; + margin-top:-129px; } pre { - font-size:1.3em; + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; } h1 { - font-size:1.8em; + font-size:20px; } h2 { - font-size:1.5em; + font-size:18px; } h3 { - font-size:1.4em; + font-size:16px; + font-style:italic; } h4 { - font-size:1.3em; + font-size:13px; } h5 { - font-size:1.2em; + font-size:12px; } h6 { - font-size:1.1em; + font-size:11px; } ul { list-style-type:disc; } code, tt { - font-size:1.2em; + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + padding-top:4px; + margin-top:8px; + line-height:1.4em; } dt code { - font-size:1.2em; + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + padding-top:4px; } table tr td dt code { - font-size:1.2em; + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; vertical-align:top; + padding-top:4px; } sup { - font-size:.6em; + font-size:8px; } + /* -Document title and Copyright styles -*/ + * Styles for HTML generated by javadoc. + * + * These are style classes that are used by the standard doclet to generate HTML documentation. + */ + +/* + * Styles for document title and copyright. + */ .clear { clear:both; height:0px; @@ -76,9 +112,9 @@ Document title and Copyright styles .aboutLanguage { float:right; padding:0px 21px; - font-size:.8em; + font-size:11px; z-index:200; - margin-top:-7px; + margin-top:-9px; } .legalCopy { margin-left:.5em; @@ -92,29 +128,33 @@ Document title and Copyright styles } .tab { background-color:#0066FF; - background-image:url(resources/titlebar.gif); - background-position:left top; - background-repeat:no-repeat; color:#ffffff; padding:8px; width:5em; font-weight:bold; } /* -Navigation bar styles -*/ + * Styles for navigation bar. + */ .bar { - background-image:url(resources/background.gif); - background-repeat:repeat-x; + background-color:#4D7A97; color:#FFFFFF; padding:.8em .5em .4em .8em; height:auto;/*height:1.8em;*/ - font-size:1em; + font-size:11px; margin:0; } +.navPadding { + padding-top: 107px; +} +.fixedNav { + position:fixed; + width:100%; + z-index:999; + background-color:#ffffff; +} .topNav { - background-image:url(resources/background.gif); - background-repeat:repeat-x; + background-color:#4D7A97; color:#FFFFFF; float:left; padding:0; @@ -123,11 +163,11 @@ Navigation bar styles height:2.8em; padding-top:10px; overflow:hidden; + font-size:12px; } .bottomNav { margin-top:10px; - background-image:url(resources/background.gif); - background-repeat:repeat-x; + background-color:#4D7A97; color:#FFFFFF; float:left; padding:0; @@ -136,18 +176,20 @@ Navigation bar styles height:2.8em; padding-top:10px; overflow:hidden; + font-size:12px; } .subNav { background-color:#dee3e9; - border-bottom:1px solid #9eadc0; float:left; width:100%; overflow:hidden; + font-size:12px; } .subNav div { clear:left; float:left; padding:0 0 5px 6px; + text-transform:uppercase; } ul.navList, ul.subNavList { float:left; @@ -157,42 +199,74 @@ ul.navList, ul.subNavList { ul.navList li{ list-style:none; float:left; - padding:3px 6px; + padding: 5px 6px; + text-transform:uppercase; +} +ul.navListSearch { + float:right; + margin:0 0 0 0; + padding:0; +} +ul.navListSearch li { + list-style:none; + float:right; + padding: 5px 6px; + text-transform:uppercase; } -ul.subNavList li{ +ul.navListSearch li label { + position:relative; + right:-16px; +} +ul.subNavList li { list-style:none; float:left; - font-size:90%; } .topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited { color:#FFFFFF; text-decoration:none; + text-transform:uppercase; } .topNav a:hover, .bottomNav a:hover { text-decoration:none; color:#bb7a2a; + text-transform:uppercase; } .navBarCell1Rev { - background-image:url(resources/tab.gif); - background-color:#a88834; - color:#FFFFFF; + background-color:#F8981D; + color:#253441; margin: auto 5px; - border:1px solid #c9aa44; +} +.skipNav { + position:absolute; + top:auto; + left:-9999px; + overflow:hidden; } /* -Page header and footer styles -*/ + * Styles for page header and footer. + */ .header, .footer { clear:both; margin:0 20px; padding:5px 0 0 0; } -.indexHeader { - margin:10px; +.indexNav { position:relative; + font-size:12px; + background-color:#dee3e9; } -.indexHeader h1 { - font-size:1.3em; +.indexNav ul { + margin-top:0; + padding:5px; +} +.indexNav ul li { + display:inline; + list-style-type:none; + padding-right:10px; + text-transform:uppercase; +} +.indexNav h1 { + font-size:13px; } .title { color:#2c4557; @@ -202,7 +276,7 @@ Page header and footer styles margin:5px 0 0 0; } .header ul { - margin:0 0 25px 0; + margin:0 0 15px 0; padding:0; } .footer ul { @@ -210,24 +284,22 @@ Page header and footer styles } .header ul li, .footer ul li { list-style:none; - font-size:1.2em; + font-size:13px; } /* -Heading styles -*/ + * Styles for headings. + */ div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 { background-color:#dee3e9; - border-top:1px solid #9eadc0; - border-bottom:1px solid #9eadc0; + border:1px solid #d0d9e0; margin:0 0 6px -8px; - padding:2px 5px; + padding:7px 5px; } ul.blockList ul.blockList ul.blockList li.blockList h3 { background-color:#dee3e9; - border-top:1px solid #9eadc0; - border-bottom:1px solid #9eadc0; + border:1px solid #d0d9e0; margin:0 0 6px -8px; - padding:2px 5px; + padding:7px 5px; } ul.blockList ul.blockList li.blockList h3 { padding:0; @@ -237,9 +309,10 @@ ul.blockList li.blockList h2 { padding:0px 0 20px 0; } /* -Page layout container styles -*/ -.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer { + * Styles for page layout containers. + */ +.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer, +.allClassesContainer, .allPackagesContainer { clear:both; padding:10px 20px; position:relative; @@ -247,10 +320,10 @@ Page layout container styles .indexContainer { margin:10px; position:relative; - font-size:1.0em; + font-size:12px; } .indexContainer h2 { - font-size:1.1em; + font-size:13px; padding:0 0 3px 0; } .indexContainer ul { @@ -259,15 +332,18 @@ Page layout container styles } .indexContainer ul li { list-style:none; + padding-top:2px; } .contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt { - font-size:1.1em; + font-size:12px; font-weight:bold; margin:10px 0 0 0; color:#4E4E4E; } .contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd { - margin:10px 0 10px 20px; + margin:5px 0 10px 0px; + font-size:14px; + font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; } .serializedFormContainer dl.nameValue dt { margin-left:1px; @@ -281,8 +357,11 @@ Page layout container styles display:inline; } /* -List styles -*/ + * Styles for lists. + */ +li.circle { + list-style:circle; +} ul.horizontal li { display:inline; font-size:0.9em; @@ -306,25 +385,24 @@ ul.blockList, ul.blockListLast { } ul.blockList li.blockList, ul.blockListLast li.blockList { list-style:none; - margin-bottom:25px; + margin-bottom:15px; + line-height:1.4; } ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList { padding:0px 20px 5px 10px; - border:1px solid #9eadc0; - background-color:#f9f9f9; + border:1px solid #ededed; + background-color:#f8f8f8; } ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList { padding:0 0 5px 8px; background-color:#ffffff; - border:1px solid #9eadc0; - border-top:none; + border:none; } ul.blockList ul.blockList ul.blockList ul.blockList li.blockList { margin-left:0; padding-left:0; padding-bottom:15px; border:none; - border-bottom:1px solid #9eadc0; } ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast { list-style:none; @@ -336,113 +414,207 @@ table tr td dl, table tr td dl dt, table tr td dl dd { margin-bottom:1px; } /* -Table styles -*/ -.contentContainer table, .classUseContainer table, .constantValuesContainer table { - border-bottom:1px solid #9eadc0; - width:100%; -} -.contentContainer ul li table, .classUseContainer ul li table, .constantValuesContainer ul li table { + * Styles for tables. + */ +.overviewSummary, .memberSummary, .typeSummary, .useSummary, .constantsSummary, .deprecatedSummary, +.requiresSummary, .packagesSummary, .providesSummary, .usesSummary { width:100%; + border-spacing:0; + border-left:1px solid #EEE; + border-right:1px solid #EEE; + border-bottom:1px solid #EEE; } -.contentContainer .description table, .contentContainer .details table { - border-bottom:none; -} -.contentContainer ul li table th.colOne, .contentContainer ul li table th.colFirst, .contentContainer ul li table th.colLast, .classUseContainer ul li table th, .constantValuesContainer ul li table th, .contentContainer ul li table td.colOne, .contentContainer ul li table td.colFirst, .contentContainer ul li table td.colLast, .classUseContainer ul li table td, .constantValuesContainer ul li table td{ - vertical-align:top; - padding-right:20px; -} -.contentContainer ul li table th.colLast, .classUseContainer ul li table th.colLast,.constantValuesContainer ul li table th.colLast, -.contentContainer ul li table td.colLast, .classUseContainer ul li table td.colLast,.constantValuesContainer ul li table td.colLast, -.contentContainer ul li table th.colOne, .classUseContainer ul li table th.colOne, -.contentContainer ul li table td.colOne, .classUseContainer ul li table td.colOne { - padding-right:3px; +.overviewSummary, .memberSummary, .requiresSummary, .packagesSummary, .providesSummary, .usesSummary { + padding:0px; } -.overviewSummary caption, .packageSummary caption, .contentContainer ul.blockList li.blockList caption, .summary caption, .classUseContainer caption, .constantValuesContainer caption { +.overviewSummary caption, .memberSummary caption, .typeSummary caption, +.useSummary caption, .constantsSummary caption, .deprecatedSummary caption, +.requiresSummary caption, .packagesSummary caption, .providesSummary caption, .usesSummary caption { position:relative; text-align:left; background-repeat:no-repeat; - color:#FFFFFF; + color:#253441; font-weight:bold; clear:none; overflow:hidden; padding:0px; + padding-top:10px; + padding-left:1px; margin:0px; -} -caption a:link, caption a:hover, caption a:active, caption a:visited { + white-space:pre; +} +.constantsSummary caption a:link, .constantsSummary caption a:visited, +.useSummary caption a:link, .useSummary caption a:visited { + color:#1f389c; +} +.overviewSummary caption a:link, .memberSummary caption a:link, .typeSummary caption a:link, +.deprecatedSummary caption a:link, +.requiresSummary caption a:link, .packagesSummary caption a:link, .providesSummary caption a:link, +.usesSummary caption a:link, +.overviewSummary caption a:hover, .memberSummary caption a:hover, .typeSummary caption a:hover, +.useSummary caption a:hover, .constantsSummary caption a:hover, .deprecatedSummary caption a:hover, +.requiresSummary caption a:hover, .packagesSummary caption a:hover, .providesSummary caption a:hover, +.usesSummary caption a:hover, +.overviewSummary caption a:active, .memberSummary caption a:active, .typeSummary caption a:active, +.useSummary caption a:active, .constantsSummary caption a:active, .deprecatedSummary caption a:active, +.requiresSummary caption a:active, .packagesSummary caption a:active, .providesSummary caption a:active, +.usesSummary caption a:active, +.overviewSummary caption a:visited, .memberSummary caption a:visited, .typeSummary caption a:visited, +.deprecatedSummary caption a:visited, +.requiresSummary caption a:visited, .packagesSummary caption a:visited, .providesSummary caption a:visited, +.usesSummary caption a:visited { color:#FFFFFF; } -.overviewSummary caption span, .packageSummary caption span, .contentContainer ul.blockList li.blockList caption span, .summary caption span, .classUseContainer caption span, .constantValuesContainer caption span { +.overviewSummary caption span, .memberSummary caption span, .typeSummary caption span, +.useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span, +.requiresSummary caption span, .packagesSummary caption span, .providesSummary caption span, +.usesSummary caption span { white-space:nowrap; - padding-top:8px; - padding-left:8px; - display:block; + padding-top:5px; + padding-left:12px; + padding-right:12px; + padding-bottom:7px; + display:inline-block; float:left; - background-image:url(resources/titlebar.gif); - height:18px; + background-color:#F8981D; + border: none; + height:16px; } -.overviewSummary .tabEnd, .packageSummary .tabEnd, .contentContainer ul.blockList li.blockList .tabEnd, .summary .tabEnd, .classUseContainer .tabEnd, .constantValuesContainer .tabEnd { - width:10px; - background-image:url(resources/titlebar_end.gif); - background-repeat:no-repeat; - background-position:top right; - position:relative; +.memberSummary caption span.activeTableTab span, .packagesSummary caption span.activeTableTab span, +.overviewSummary caption span.activeTableTab span, .typeSummary caption span.activeTableTab span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + margin-right:3px; + display:inline-block; float:left; + background-color:#F8981D; + height:16px; } -ul.blockList ul.blockList li.blockList table { - margin:0 0 12px 0px; - width:100%; +.memberSummary caption span.tableTab span, .packagesSummary caption span.tableTab span, +.overviewSummary caption span.tableTab span, .typeSummary caption span.tableTab span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + margin-right:3px; + display:inline-block; + float:left; + background-color:#4D7A97; + height:16px; +} +.memberSummary caption span.tableTab, .memberSummary caption span.activeTableTab, +.packagesSummary caption span.tableTab, .packagesSummary caption span.activeTableTab, +.overviewSummary caption span.tableTab, .overviewSummary caption span.activeTableTab, +.typeSummary caption span.tableTab, .typeSummary caption span.activeTableTab { + padding-top:0px; + padding-left:0px; + padding-right:0px; + background-image:none; + float:none; + display:inline; } -.tableSubHeadingColor { - background-color: #EEEEFF; +.overviewSummary .tabEnd, .memberSummary .tabEnd, .typeSummary .tabEnd, +.useSummary .tabEnd, .constantsSummary .tabEnd, .deprecatedSummary .tabEnd, +.requiresSummary .tabEnd, .packagesSummary .tabEnd, .providesSummary .tabEnd, .usesSummary .tabEnd { + display:none; + width:5px; + position:relative; + float:left; + background-color:#F8981D; +} +.memberSummary .activeTableTab .tabEnd, .packagesSummary .activeTableTab .tabEnd, +.overviewSummary .activeTableTab .tabEnd, .typeSummary .activeTableTab .tabEnd { + display:none; + width:5px; + margin-right:3px; + position:relative; + float:left; + background-color:#F8981D; } -.altColor { - background-color:#eeeeef; +.memberSummary .tableTab .tabEnd, .packagesSummary .tableTab .tabEnd, +.overviewSummary .tableTab .tabEnd, .typeSummary .tableTab .tabEnd { + display:none; + width:5px; + margin-right:3px; + position:relative; + background-color:#4D7A97; + float:left; } -.rowColor { - background-color:#ffffff; +.rowColor th, .altColor th { + font-weight:normal; } -.overviewSummary td, .packageSummary td, .contentContainer ul.blockList li.blockList td, .summary td, .classUseContainer td, .constantValuesContainer td { +.overviewSummary td, .memberSummary td, .typeSummary td, +.useSummary td, .constantsSummary td, .deprecatedSummary td, +.requiresSummary td, .packagesSummary td, .providesSummary td, .usesSummary td { text-align:left; - padding:3px 3px 3px 7px; + padding:0px 0px 12px 10px; +} +th.colFirst, th.colSecond, th.colLast, th.colConstructorName, th.colDeprecatedItemName, .useSummary th, +.constantsSummary th, .packagesSummary th, td.colFirst, td.colSecond, td.colLast, .useSummary td, +.constantsSummary td { + vertical-align:top; + padding-right:0px; + padding-top:8px; + padding-bottom:3px; } -th.colFirst, th.colLast, th.colOne, .constantValuesContainer th { +th.colFirst, th.colSecond, th.colLast, th.colConstructorName, th.colDeprecatedItemName, .constantsSummary th, +.packagesSummary th { background:#dee3e9; - border-top:1px solid #9eadc0; - border-bottom:1px solid #9eadc0; text-align:left; - padding:3px 3px 3px 7px; -} -td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover { - font-weight:bold; + padding:8px 3px 3px 7px; } td.colFirst, th.colFirst { - border-left:1px solid #9eadc0; - white-space:nowrap; -} -td.colLast, th.colLast { - border-right:1px solid #9eadc0; + font-size:13px; +} +td.colSecond, th.colSecond, td.colLast, th.colConstructorName, th.colDeprecatedItemName, th.colLast { + font-size:13px; +} +.constantsSummary th, .packagesSummary th { + font-size:13px; +} +.providesSummary th.colFirst, .providesSummary th.colLast, .providesSummary td.colFirst, +.providesSummary td.colLast { + white-space:normal; + font-size:13px; +} +.overviewSummary td.colFirst, .overviewSummary th.colFirst, +.requiresSummary td.colFirst, .requiresSummary th.colFirst, +.packagesSummary td.colFirst, .packagesSummary td.colSecond, .packagesSummary th.colFirst, .packagesSummary th, +.usesSummary td.colFirst, .usesSummary th.colFirst, +.providesSummary td.colFirst, .providesSummary th.colFirst, +.memberSummary td.colFirst, .memberSummary th.colFirst, +.memberSummary td.colSecond, .memberSummary th.colSecond, .memberSummary th.colConstructorName, +.typeSummary td.colFirst, .typeSummary th.colFirst { + vertical-align:top; } -td.colOne, th.colOne { - border-right:1px solid #9eadc0; - border-left:1px solid #9eadc0; +.packagesSummary th.colLast, .packagesSummary td.colLast { + white-space:normal; +} +td.colFirst a:link, td.colFirst a:visited, +td.colSecond a:link, td.colSecond a:visited, +th.colFirst a:link, th.colFirst a:visited, +th.colSecond a:link, th.colSecond a:visited, +th.colConstructorName a:link, th.colConstructorName a:visited, +th.colDeprecatedItemName a:link, th.colDeprecatedItemName a:visited, +.constantValuesContainer td a:link, .constantValuesContainer td a:visited, +.allClassesContainer td a:link, .allClassesContainer td a:visited, +.allPackagesContainer td a:link, .allPackagesContainer td a:visited { + font-weight:bold; } -table.overviewSummary { - padding:0px; - margin-left:0px; +.tableSubHeadingColor { + background-color:#EEEEFF; } -table.overviewSummary td.colFirst, table.overviewSummary th.colFirst, -table.overviewSummary td.colOne, table.overviewSummary th.colOne { - width:25%; - vertical-align:middle; +.altColor, .altColor th { + background-color:#FFFFFF; } -table.packageSummary td.colFirst, table.overviewSummary th.colFirst { - width:25%; - vertical-align:middle; +.rowColor, .rowColor th { + background-color:#EEEEEF; } /* -Content styles -*/ + * Styles for contents. + */ .description pre { margin-top:0; } @@ -453,9 +625,22 @@ Content styles .docSummary { padding:0; } +ul.blockList ul.blockList ul.blockList li.blockList h3 { + font-style:normal; +} +div.block { + font-size:14px; + font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; +} +td.colLast div { + padding-top:0px; +} +td.colLast a { + padding-bottom:3px; +} /* -Formatting effect styles -*/ + * Styles for formatting effect. + */ .sourceLineNo { color:green; padding:0 30px 0 0; @@ -463,12 +648,263 @@ Formatting effect styles h1.hidden { visibility:hidden; overflow:hidden; - font-size:.9em; + font-size:10px; } .block { display:block; - margin:3px 0 0 0; + margin:3px 10px 2px 0px; + color:#474747; +} +.deprecatedLabel, .descfrmTypeLabel, .implementationLabel, .memberNameLabel, .memberNameLink, +.moduleLabelInPackage, .moduleLabelInType, .overrideSpecifyLabel, .packageLabelInType, +.packageHierarchyLabel, .paramLabel, .returnLabel, .seeLabel, .simpleTagLabel, +.throwsLabel, .typeNameLabel, .typeNameLink, .searchTagLink { + font-weight:bold; +} +.deprecationComment, .emphasizedPhrase, .interfaceName { + font-style:italic; +} +.deprecationBlock { + font-size:14px; + font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; + border-style:solid; + border-width:thin; + border-radius:10px; + padding:10px; + margin-bottom:10px; + margin-right:10px; + display:inline-block; +} +div.block div.deprecationComment, div.block div.block span.emphasizedPhrase, +div.block div.block span.interfaceName { + font-style:normal; +} +div.contentContainer ul.blockList li.blockList h2 { + padding-bottom:0px; +} +/* + * Styles for IFRAME. + */ +.mainContainer { + margin:0 auto; + padding:0; + height:100%; + width:100%; + position:fixed; + top:0; + left:0; +} +.leftContainer { + height:100%; + position:fixed; + width:320px; +} +.leftTop { + position:relative; + float:left; + width:315px; + top:0; + left:0; + height:30%; + border-right:6px solid #ccc; + border-bottom:6px solid #ccc; +} +.leftBottom { + position:relative; + float:left; + width:315px; + bottom:0; + left:0; + height:70%; + border-right:6px solid #ccc; + border-top:1px solid #000; +} +.rightContainer { + position:absolute; + left:320px; + top:0; + bottom:0; + height:100%; + right:0; + border-left:1px solid #000; +} +.rightIframe { + margin:0; + padding:0; + height:100%; + right:30px; + width:100%; + overflow:visible; + margin-bottom:30px; +} +/* + * Styles specific to HTML5 elements. + */ +main, nav, header, footer, section { + display:block; +} +/* + * Styles for javadoc search. + */ +.ui-autocomplete-category { + font-weight:bold; + font-size:15px; + padding:7px 0 7px 3px; + background-color:#4D7A97; + color:#FFFFFF; +} +.resultItem { + font-size:13px; } -.strong { +.ui-autocomplete { + max-height:85%; + max-width:65%; + overflow-y:scroll; + overflow-x:scroll; + white-space:nowrap; + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); +} +ul.ui-autocomplete { + position:fixed; + z-index:999999; + background-color: #FFFFFF; +} +ul.ui-autocomplete li { + float:left; + clear:both; + width:100%; +} +.resultHighlight { font-weight:bold; } +.ui-autocomplete .result-item { + font-size: inherit; +} +#search { + background-image:url('resources/glass.png'); + background-size:13px; + background-repeat:no-repeat; + background-position:2px 3px; + padding-left:20px; + position:relative; + right:-18px; +} +#reset { + background-color: rgb(255,255,255); + background-image:url('resources/x.png'); + background-position:center; + background-repeat:no-repeat; + background-size:12px; + border:0 none; + width:16px; + height:17px; + position:relative; + left:-4px; + top:-4px; + font-size:0px; +} +.watermark { + color:#545454; +} +.searchTagDescResult { + font-style:italic; + font-size:11px; +} +.searchTagHolderResult { + font-style:italic; + font-size:12px; +} +.searchTagResult:before, .searchTagResult:target { + color:red; +} +.moduleGraph span { + display:none; + position:absolute; +} +.moduleGraph:hover span { + display:block; + margin: -100px 0 0 100px; + z-index: 1; +} +.methodSignature { + white-space:normal; +} + +/* + * Styles for user-provided tables. + * + * borderless: + * No borders, vertical margins, styled caption. + * This style is provided for use with existing doc comments. + * In general, borderless tables should not be used for layout purposes. + * + * plain: + * Plain borders around table and cells, vertical margins, styled caption. + * Best for small tables or for complex tables for tables with cells that span + * rows and columns, when the "striped" style does not work well. + * + * striped: + * Borders around the table and vertical borders between cells, striped rows, + * vertical margins, styled caption. + * Best for tables that have a header row, and a body containing a series of simple rows. + */ + +table.borderless, +table.plain, +table.striped { + margin-top: 10px; + margin-bottom: 10px; +} +table.borderless > caption, +table.plain > caption, +table.striped > caption { + font-weight: bold; + font-size: smaller; +} +table.borderless th, table.borderless td, +table.plain th, table.plain td, +table.striped th, table.striped td { + padding: 2px 5px; +} +table.borderless, +table.borderless > thead > tr > th, table.borderless > tbody > tr > th, table.borderless > tr > th, +table.borderless > thead > tr > td, table.borderless > tbody > tr > td, table.borderless > tr > td { + border: none; +} +table.borderless > thead > tr, table.borderless > tbody > tr, table.borderless > tr { + background-color: transparent; +} +table.plain { + border-collapse: collapse; + border: 1px solid black; +} +table.plain > thead > tr, table.plain > tbody tr, table.plain > tr { + background-color: transparent; +} +table.plain > thead > tr > th, table.plain > tbody > tr > th, table.plain > tr > th, +table.plain > thead > tr > td, table.plain > tbody > tr > td, table.plain > tr > td { + border: 1px solid black; +} +table.striped { + border-collapse: collapse; + border: 1px solid black; +} +table.striped > thead { + background-color: #E3E3E3; +} +table.striped > thead > tr > th, table.striped > thead > tr > td { + border: 1px solid black; +} +table.striped > tbody > tr:nth-child(even) { + background-color: #EEE +} +table.striped > tbody > tr:nth-child(odd) { + background-color: #FFF +} +table.striped > tbody > tr > th, table.striped > tbody > tr > td { + border-left: 1px solid black; + border-right: 1px solid black; +} +table.striped > tbody > tr > th { + font-weight: normal; +} diff --git a/java/doc/type-search-index.js b/java/doc/type-search-index.js new file mode 100644 index 0000000..d4bc564 --- /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..3f8aad13e27dce52e0e38ec1e38cbb6d1767969a GIT binary patch literal 311 zcmWIWW@Zs#;Nak3a9oxd#()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 * subsampling. * * @param subsamp the level of chrominance subsampling (one of - * SAMP_*) + * {@link #SAMP_444 SAMP_*}) * * @return the MCU block width for the given level of chrominance * subsampling. @@ -96,7 +120,7 @@ public final class TJ { } private static final int[] MCU_WIDTH = { - 8, 16, 16, 8, 8, 32 + 8, 16, 16, 8, 8, 32, 8 }; @@ -105,7 +129,7 @@ public final class TJ { * subsampling. * * @param subsamp the level of chrominance subsampling (one of - * SAMP_*) + * {@link #SAMP_444 SAMP_*}) * * @return the MCU block height for the given level of chrominance * subsampling. @@ -116,7 +140,7 @@ public final class TJ { } private static final int[] MCU_HEIGHT = { - 8, 8, 16, 8, 16, 8 + 8, 8, 16, 8, 16, 8, 32 }; @@ -126,71 +150,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; /** @@ -205,18 +230,18 @@ public final class TJ { * vice versa, but the mapping is typically not 1:1 or reversible, nor can it * be defined with a simple formula. Thus, such a conversion is out of scope * for a codec library. However, the TurboJPEG API allows for compressing - * CMYK pixels into a YCCK JPEG image (see {@link #CS_YCCK}) and - * decompressing YCCK JPEG images into CMYK pixels. + * packed-pixel CMYK images into YCCK JPEG images (see {@link #CS_YCCK}) and + * decompressing YCCK JPEG images into packed-pixel CMYK images. */ public static final int PF_CMYK = 11; /** - * 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 PF_*) + * @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,13 +254,13 @@ 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 PF_*) + * @param pixelFormat the pixel format (one of {@link #PF_RGB PF_*}) * * @return the red offset for the given pixel format, or -1 if the pixel * format does not have a red component. @@ -251,13 +276,13 @@ 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 PF_*) + * @param pixelFormat the pixel format (one of {@link #PF_RGB PF_*}) * * @return the green offset for the given pixel format, or -1 if the pixel * format does not have a green component. @@ -273,13 +298,13 @@ 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 PF_*) + * @param pixelFormat the pixel format (one of {@link #PF_RGB PF_*}) * * @return the blue offset for the given pixel format, or -1 if the pixel * format does not have a blue component. @@ -295,13 +320,13 @@ 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 PF_*) + * @param pixelFormat the pixel format (one of {@link #PF_RGB PF_*}) * * @return the alpha offset for the given pixel format, or -1 if the pixel * format does not have a alpha component. @@ -324,8 +349,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 any of the extended RGB pixel formats or grayscale, but - * they cannot be decompressed to 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; /** @@ -339,26 +365,29 @@ public final class TJ { * transformation allowed the same signal to drive both black & white and * color televisions, but JPEG images use YCbCr primarily because it allows * the color data to be optionally subsampled for the purposes of reducing - * bandwidth or disk space. YCbCr is the most common JPEG colorspace, and - * YCbCr JPEG images can be compressed from and decompressed to any of the - * extended RGB pixel formats or grayscale, or they can be decompressed to - * YUV planar images. + * network or disk usage. YCbCr is the most common JPEG colorspace, and + * YCbCr JPEG images can be compressed from and decompressed to packed-pixel + * images with any of the extended RGB or grayscale pixel formats. YCbCr + * JPEG images can also be compressed from and decompressed to planar YUV + * images. */ @SuppressWarnings("checkstyle:ConstantName") public static final int CS_YCbCr = 1; /** * Grayscale colorspace. The JPEG image retains only the luminance data (Y * component), and any color data from the source image is discarded. - * Grayscale JPEG images can be compressed from and decompressed to any of - * the extended RGB pixel formats or grayscale, or they can be decompressed - * to YUV planar images. + * Grayscale JPEG images can be compressed from and decompressed to + * packed-pixel images with any of the extended RGB or grayscale pixel + * formats, or they can be compressed from and decompressed to planar YUV + * images. */ public static final int CS_GRAY = 2; /** * CMYK colorspace. When compressing the JPEG image, the C, M, Y, and K * components in the source image are reordered into image planes, but no * colorspace conversion or subsampling is performed. CMYK JPEG images can - * only be decompressed to CMYK pixels. + * only be compressed from and decompressed to packed-pixel images with the + * CMYK pixel format. */ public static final int CS_CMYK = 3; /** @@ -368,85 +397,419 @@ public final class TJ { * reversibly transformed into YCCK, and as with YCbCr, the chrominance * components in the YCCK pixels can be subsampled without incurring major * perceptual loss. YCCK JPEG images can only be compressed from and - * decompressed to CMYK pixels. + * decompressed to packed-pixel images with the CMYK pixel format. */ public static final int CS_YCCK = 4; /** - * The uncompressed source/destination image is stored in bottom-up (Windows, - * OpenGL) order, not top-down (X11) order. + * Error handling behavior + * + *

      Value + *

        + *
      • 0 [default] Allow the current + * compression/decompression/transform operation to complete unless a fatal + * error is encountered. + *
      • 1 Immediately discontinue the current + * compression/decompression/transform operation if a warning (non-fatal + * error) occurs. + *
      */ - public static final int FLAG_BOTTOMUP = 2; + 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} unless + * {@link #PARAM_ARITHMETIC} is set. + */ + 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. Can be combined with + * {@link #PARAM_ARITHMETIC}. Implies {@link #PARAM_OPTIMIZE} unless + * {@link #PARAM_ARITHMETIC} is also set. + */ + public static final int PARAM_PROGRESSIVE = 12; + /** + * 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}. + */ + 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 transformation + *
      + * + * @see #PARAM_LOSSLESSPSV + * @see #PARAM_LOSSLESSPT + */ + public static final int PARAM_LOSSLESS = 15; + /** + * Lossless JPEG predictor selection value (PSV) + * + *

      Value + *

        + *
      • 1-7 [default for compression: + * 1] + *
      + * + * @see #PARAM_LOSSLESS + */ + public static final int PARAM_LOSSLESSPSV = 16; + /** + * Lossless JPEG point transform (Pt) + * + *

      Value + *

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

      A point transform value of 0 is necessary in order to + * generate a fully lossless JPEG image. (A non-zero point transform value + * right-shifts the input samples by the specified number of bits, which is + * effectively a form of lossy color quantization.) + * + * @see #PARAM_LOSSLESS + * @see #PARAM_PRECISION + */ + public static final int PARAM_LOSSLESSPT = 17; + /** + * JPEG restart marker interval in MCU blocks (lossy) or samples (lossless) + * [compression only] + * + *

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

      Value + *

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

      Setting this parameter to a non-zero value sets + * {@link #PARAM_RESTARTROWS} to 0. + */ + public static final int PARAM_RESTARTBLOCKS = 18; + /** + * JPEG restart marker interval in MCU rows (lossy) or sample rows (lossless) + * [compression only] + * + *

      See {@link #PARAM_RESTARTBLOCKS} for a description of restart markers. + * + *

      Value + *

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

      Setting this parameter to a non-zero value sets + * {@link #PARAM_RESTARTBLOCKS} to 0. + */ + public static final int PARAM_RESTARTROWS = 19; + /** + * JPEG horizontal pixel density + * + *

      Value + *

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

      This value is stored in or read from the JPEG header. It does not + * affect the contents of the JPEG image. + * + * @see #PARAM_DENSITYUNITS + */ + public static final int 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; - @SuppressWarnings("checkstyle:JavadocVariable") - @Deprecated - public static final int FLAG_FORCEMMX = 8; - @SuppressWarnings("checkstyle:JavadocVariable") - @Deprecated - public static final int FLAG_FORCESSE = 16; - @SuppressWarnings("checkstyle:JavadocVariable") - @Deprecated - public static final int FLAG_FORCESSE2 = 32; - @SuppressWarnings("checkstyle:JavadocVariable") - @Deprecated - public static final int FLAG_FORCESSE3 = 128; /** - * When decompressing an image that was compressed using chrominance - * subsampling, use the fastest chrominance upsampling algorithm available in - * the underlying codec. The default is to use smooth upsampling, which - * creates a smooth transition between neighboring chrominance components in - * order to reduce upsampling artifacts in the decompressed image. + * @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; /** - * Use the fastest DCT/IDCT algorithm available in the underlying codec. The - * default if this flag is not specified is implementation-specific. For - * example, the implementation of TurboJPEG for libjpeg[-turbo] uses the fast - * algorithm by default when compressing, because this has been shown to have - * only a very slight effect on accuracy, but it uses the accurate algorithm - * when decompressing, because this has been shown to have a larger effect. + * @deprecated Use {@link #PARAM_FASTDCT} instead. */ + @Deprecated public static final int FLAG_FASTDCT = 2048; /** - * Use the most accurate DCT/IDCT algorithm available in the underlying - * codec. The default if this flag is not specified is - * implementation-specific. For example, the implementation of TurboJPEG for - * libjpeg[-turbo] uses the fast algorithm by default when compressing, - * because this has been shown to have only a very slight effect on accuracy, - * but it uses the accurate algorithm when decompressing, because this has - * been shown to have a larger effect. + * @deprecated Use {@link #PARAM_FASTDCT} instead. */ + @Deprecated public static final int FLAG_ACCURATEDCT = 4096; /** - * Immediately discontinue the current compression/decompression/transform - * operation if the underlying codec throws a warning (non-fatal error). The - * default behavior is to allow the operation to complete unless a fatal - * error is encountered. - *

      - * NOTE: due to the design of the TurboJPEG Java API, only certain methods - * (specifically, {@link TJDecompressor TJDecompressor.decompress*()} methods - * with a void return type) will complete and leave the output image in a - * fully recoverable state after a non-fatal error occurs. + * @deprecated Use {@link #PARAM_STOPONWARNING} instead. */ + @Deprecated public static final int FLAG_STOPONWARNING = 8192; /** - * 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. + * @deprecated Use {@link #PARAM_PROGRESSIVE} instead. */ + @Deprecated public static final int FLAG_PROGRESSIVE = 16384; /** - * 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. + * @deprecated Use {@link #PARAM_SCANLIMIT} instead. */ + @Deprecated public static final int FLAG_LIMITSCANS = 32768; @@ -455,13 +818,13 @@ public final class TJ { */ public static final int NUMERR = 2; /** - * The error was non-fatal and recoverable, but the image may still be - * corrupt. + * The error was non-fatal and recoverable, but the destination image may + * still be corrupt. *

      * NOTE: due to the design of the TurboJPEG Java API, only certain methods * (specifically, {@link TJDecompressor TJDecompressor.decompress*()} methods - * with a void return type) will complete and leave the output image in a - * fully recoverable state after a non-fatal error occurs. + * with a void return type) will complete and leave the destination image in + * a fully recoverable state after a non-fatal error occurs. */ public static final int ERR_WARNING = 0; /** @@ -479,7 +842,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 TJ 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. @@ -487,33 +854,30 @@ public final class TJ { public static native int bufSize(int width, int height, int jpegSubsamp); /** - * Returns the size of the buffer (in bytes) required to hold a YUV planar - * image with the given width, height, and level of chrominance subsampling. + * Returns the size of the buffer (in bytes) required to hold a unified + * planar YUV image with the given width, height, and level of chrominance + * subsampling. * * @param width the width (in pixels) of the YUV image * - * @param pad the width of each line in each plane of the image is padded to - * the nearest multiple of this number of bytes (must be a power of 2.) + * @param align row alignment (in bytes) of the YUV image (must be a power of + * 2.) Setting this parameter to n specifies that each row in each plane of + * the YUV image will be padded to the nearest multiple of n bytes + * (1 = unpadded.) * * @param height the height (in pixels) of the YUV image * * @param subsamp the level of chrominance subsampling used in the YUV - * image (one of {@link TJ TJ.SAMP_*}) + * image (one of {@link #SAMP_444 TJ.SAMP_*}) * - * @return the size of the buffer (in bytes) required to hold a YUV planar - * image with the given width, height, and level of chrominance subsampling. + * @return the size of the buffer (in bytes) required to hold a unified + * planar YUV image with the given width, height, and level of chrominance + * subsampling. */ - public static native int bufSizeYUV(int width, int pad, int height, + public static native int bufSizeYUV(int width, int align, int height, int subsamp); /** - * @deprecated Use {@link #bufSizeYUV(int, int, int, int)} instead. - */ - @SuppressWarnings("checkstyle:JavadocMethod") - @Deprecated - public static native int bufSizeYUV(int width, int height, int subsamp); - - /** * Returns the size of the buffer (in bytes) required to hold a YUV image * plane with the given parameters. * @@ -523,23 +887,23 @@ public final class TJ { * @param width width (in pixels) of the YUV image. NOTE: this is the width * of the whole image, not the plane width. * - * @param stride bytes per line in the image plane. + * @param stride bytes per row in the image plane. * * @param height height (in pixels) of the YUV image. NOTE: this is the * height of the whole image, not the plane height. * * @param subsamp the level of chrominance subsampling used in the YUV - * image (one of {@link TJ TJ.SAMP_*}) + * image (one of {@link #SAMP_444 TJ.SAMP_*}) * - * @return the size of the buffer (in bytes) required to hold a YUV planar - * image with the given parameters. + * @return the size of the buffer (in bytes) required to hold a YUV image + * plane with the given parameters. */ public static native int planeSizeYUV(int componentID, int width, int stride, int height, int subsamp); /** * 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) @@ -547,7 +911,7 @@ public final class TJ { * @param width width (in pixels) of the YUV image * * @param subsamp the level of chrominance subsampling used in the YUV image - * (one of {@link TJ TJ.SAMP_*}) + * (one of {@link #SAMP_444 TJ.SAMP_*}) * * @return the plane width of a YUV image plane with the given parameters. */ @@ -555,7 +919,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) @@ -563,7 +927,7 @@ public final class TJ { * @param height height (in pixels) of the YUV image * * @param subsamp the level of chrominance subsampling used in the YUV image - * (one of {@link TJ TJ.SAMP_*}) + * (one of {@link #SAMP_444 TJ.SAMP_*}) * * @return the plane height of a YUV image plane with the given parameters. */ @@ -571,14 +935,25 @@ public final class TJ { int subsamp); /** - * Returns a list of fractional scaling factors that the JPEG decompressor in - * this implementation of TurboJPEG supports. + * Returns a list of fractional scaling factors that the JPEG decompressor + * supports. * - * @return a list of fractional scaling factors that the JPEG decompressor in - * this implementation of TurboJPEG supports. + * @return a list of fractional scaling factors that the JPEG decompressor + * supports. */ public static native TJScalingFactor[] getScalingFactors(); + /** + * 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 6d4830f..fed47de 100644 --- a/java/org/libjpegturbo/turbojpeg/TJCompressor.java +++ b/java/org/libjpegturbo/turbojpeg/TJCompressor.java @@ -1,5 +1,6 @@ /* - * Copyright (C)2011-2015, 2018, 2020 D. R. Commander. All Rights Reserved. + * Copyright (C)2011-2015, 2018, 2020, 2022-2023 D. R. Commander. + * All Rights Reserved. * Copyright (C)2015 Viktor Szathmáry. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,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. */ @@ -49,9 +47,9 @@ public class TJCompressor implements Closeable { } /** - * Create a TurboJPEG compressor instance and associate the uncompressed - * 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 * @@ -74,20 +72,9 @@ public class TJCompressor implements Closeable { } /** - * @deprecated Use - * {@link #TJCompressor(byte[], int, int, int, int, int, int)} instead. - */ - @SuppressWarnings("checkstyle:JavadocMethod") - @Deprecated - public TJCompressor(byte[] srcImage, int width, int pitch, int height, - int pixelFormat) throws TJException { - setSourceImage(srcImage, width, pitch, height, pixelFormat); - } - - /** - * Create a TurboJPEG compressor instance and associate the uncompressed - * 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 @@ -110,11 +97,11 @@ public class TJCompressor implements Closeable { } /** - * Associate an uncompressed 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 image buffer containing RGB, grayscale, or CMYK pixels to - * be compressed or encoded. This buffer is not modified. + * @param srcImage buffer containing a packed-pixel RGB, grayscale, or CMYK + * source image to be compressed or encoded. This buffer is not modified. * * @param x x offset (in pixels) of the region in the source image from which * the JPEG or YUV image should be compressed/encoded @@ -125,14 +112,14 @@ public class TJCompressor implements Closeable { * @param width width (in pixels) of the region in the source image from * which the JPEG or YUV image should be compressed/encoded * - * @param pitch bytes per line of the source image. Normally, this should be - * width * TJ.pixelSize(pixelFormat) if the source image is - * unpadded, but you can use this parameter to, for instance, specify that - * the scanlines in the source image are padded to a 4-byte boundary or to - * compress/encode a JPEG or YUV image from a region of a larger source - * image. You can also be clever and use this parameter to skip lines, etc. - * Setting this parameter to 0 is the equivalent of setting it to - * width * TJ.pixelSize(pixelFormat). + * @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. (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/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 @@ -147,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); @@ -157,28 +144,131 @@ public class TJCompressor implements Closeable { srcPixelFormat = pixelFormat; srcX = x; srcY = y; + srcBuf12 = null; + srcBuf16 = null; srcBufInt = null; srcYUVImage = null; } /** - * @deprecated Use - * {@link #setSourceImage(byte[], int, int, int, int, int, int)} instead. + * 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_*}) */ - @SuppressWarnings("checkstyle:JavadocMethod") - @Deprecated - public void setSourceImage(byte[] srcImage, int width, int pitch, - int height, int pixelFormat) throws TJException { - setSourceImage(srcImage, 0, 0, width, pitch, height, pixelFormat); - srcX = srcY = -1; + 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 uncompressed RGB or grayscale source image with this - * compressor instance. + * Associate an 8-bit-per-pixel packed-pixel RGB or grayscale source image + * with this compressor instance. * - * @param srcImage a BufferedImage instance containing RGB or - * grayscale pixels to be compressed or encoded. This image is not modified. + * @param srcImage a BufferedImage instance containing a + * packed-pixel RGB or grayscale source image to be compressed or encoded. + * This image is not modified. * * @param x x offset (in pixels) of the region in the source image from which * the JPEG or YUV image should be compressed/encoded @@ -244,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(); @@ -253,314 +343,283 @@ 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 an uncompressed YUV planar 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 YUV planar image to be compressed. This image is not - * modified. + * @param srcImage planar YUV source image to be compressed. This image is + * not modified. */ public void setSourceImage(YUVImage srcImage) throws TJException { if (handle == 0) init(); 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 YUV - * planar source. In that case, the level of chrominance subsampling in - * the JPEG image is determined by the source. Furthermore, this method has - * no effect when encoding to a pre-allocated {@link YUVImage} instance. In - * that case, the level of chrominance subsampling is determined by the - * destination. - * - * @param newSubsamp the level of chrominance subsampling to use in - * subsequent compress/encode oeprations (one of - * {@link TJ#SAMP_444 TJ.SAMP_*}) + * Set the value of a compression parameter. + * + * @param param one of {@link TJ#PARAM_STOPONWARNING TJ.PARAM_*} + * + * @param value value of the compression parameter (refer to + * {@link TJ#PARAM_STOPONWARNING parameter documentation}) + */ + public native void set(int param, int value); + + /** + * Get the value of a compression parameter. + * + * @param param one of {@link TJ#PARAM_STOPONWARNING TJ.PARAM_*} + * + * @return the value of the specified compression parameter, or -1 if the + * value is unknown. + */ + public native int get(int param); + + /** + * @deprecated Use + * {@link #set set}({@link TJ#PARAM_SUBSAMP}, ...) instead. */ - public void setSubsamp(int newSubsamp) { - if (newSubsamp < 0 || newSubsamp >= TJ.NUMSAMP) + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated + public void setSubsamp(int subsamp) { + if (subsamp < 0 || subsamp >= TJ.NUMSAMP) throw new IllegalArgumentException("Invalid argument in setSubsamp()"); - subsamp = newSubsamp; + set(TJ.PARAM_SUBSAMP, subsamp); } /** - * Set the JPEG image quality level for subsequent compress operations. - * - * @param quality the new JPEG image quality level (1 to 100, 1 = worst, - * 100 = best) + * @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); } /** - * Compress the uncompressed source image associated with this compressor - * instance and output a JPEG image to the given destination buffer. + * Compress the packed-pixel or planar YUV source image associated with this + * compressor instance and output a JPEG image to the given destination + * buffer. * * @param dstBuf buffer that will receive the JPEG image. Use - * {@link TJ#bufSize} to determine the maximum size for this buffer based on - * 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) { - if (srcX >= 0 && srcY >= 0) - compressedSize = compress(srcBuf, srcX, srcY, srcWidth, srcPitch, - srcHeight, srcPixelFormat, dstBuf, subsamp, - jpegQuality, flags); - else - compressedSize = compress(srcBuf, srcWidth, srcPitch, srcHeight, - srcPixelFormat, dstBuf, subsamp, jpegQuality, - flags); - } else if (srcBufInt != null) { - if (srcX >= 0 && srcY >= 0) - compressedSize = compress(srcBufInt, srcX, srcY, srcWidth, srcStride, - srcHeight, srcPixelFormat, dstBuf, subsamp, - jpegQuality, flags); - else - compressedSize = compress(srcBufInt, srcWidth, srcStride, 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 = compress8(srcBufInt, srcX, srcY, srcWidth, srcStride, + srcHeight, srcPixelFormat, dstBuf); + else + throw new IllegalStateException("No source image is associated with this instance"); } /** - * Compress the uncompressed 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_*} + * @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. * * @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; } /** - * @deprecated Use - * {@link #setSourceImage(BufferedImage, int, int, int, int)} and - * {@link #compress(byte[], int)} instead. - */ - @SuppressWarnings("checkstyle:JavadocMethod") - @Deprecated - public void compress(BufferedImage srcImage, byte[] dstBuf, int flags) - throws TJException { - setSourceImage(srcImage, 0, 0, 0, 0); - compress(dstBuf, flags); - } - - /** - * @deprecated Use - * {@link #setSourceImage(BufferedImage, int, int, int, int)} and - * {@link #compress(int)} instead. + * @deprecated Use {@link #set set()} and {@link #compress()} instead. */ @SuppressWarnings("checkstyle:JavadocMethod") @Deprecated - public byte[] compress(BufferedImage srcImage, int flags) - throws TJException { - setSourceImage(srcImage, 0, 0, 0, 0); - return compress(flags); + public byte[] compress(int flags) throws TJException { + processFlags(flags); + return compress(); } /** - * Encode the uncompressed source image associated with this compressor - * instance into a YUV planar image and store it in the given - * YUVImage instance. This method uses the accelerated color - * conversion routines in TurboJPEG's underlying codec but does not execute - * any of the other steps in the JPEG compression process. Encoding - * CMYK source images to YUV is not supported. - * - * @param dstImage {@link YUVImage} instance that will receive the YUV planar + * Encode the 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; } /** - * @deprecated Use {@link #encodeYUV(YUVImage, int)} instead. + * @deprecated Use {@link #set set()} and {@link #encodeYUV(YUVImage)} + * instead. */ @SuppressWarnings("checkstyle:JavadocMethod") @Deprecated - public void encodeYUV(byte[] dstBuf, int flags) throws TJException { - if (dstBuf == null) + public void encodeYUV(YUVImage dstImage, int flags) throws TJException { + if (flags < 0) throw new IllegalArgumentException("Invalid argument in encodeYUV()"); - checkSourceImage(); - checkSubsampling(); - YUVImage dstYUVImage = new YUVImage(dstBuf, srcWidth, 4, srcHeight, - subsamp); - encodeYUV(dstYUVImage, flags); - } - /** - * Encode the uncompressed source image associated with this compressor - * instance into a unified YUV planar image buffer and return a - * YUVImage instance containing the encoded image. This method - * uses the accelerated color conversion routines in TurboJPEG's underlying - * codec but does not execute any of the other steps in the JPEG compression - * process. Encoding CMYK source images to YUV is not supported. - * - * @param pad the width of each line in each plane of the YUV image will be - * padded to the nearest multiple of this number of bytes (must be a power of - * 2.) - * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} - * - * @return a YUV planar image. - */ - public YUVImage encodeYUV(int pad, int flags) throws TJException { - checkSourceImage(); - checkSubsampling(); - if (pad < 1 || ((pad & (pad - 1)) != 0)) - throw new IllegalStateException("Invalid argument in encodeYUV()"); - YUVImage dstYUVImage = new YUVImage(srcWidth, pad, srcHeight, subsamp); - encodeYUV(dstYUVImage, flags); - return dstYUVImage; + processFlags(flags); + encodeYUV(dstImage); } /** - * Encode the uncompressed source image associated with this compressor - * instance into separate Y, U (Cb), and V (Cr) image planes and return a - * YUVImage instance containing the encoded image planes. This - * method uses the accelerated color conversion routines in TurboJPEG's - * underlying codec but does not execute any of the other steps in the JPEG - * compression process. Encoding CMYK source images to YUV is not supported. - * - * @param strides an array of integers, each specifying the number of bytes - * per line in the corresponding plane of the output image. Setting the - * stride for any plane to 0 is the same as setting it to the component width - * of the plane. If strides is null, then the strides for all - * planes will be set to their respective component widths. You can adjust - * the strides in order to add an arbitrary amount of line padding to each - * plane. - * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} - * - * @return a YUV planar image. + * 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.) + * + * @return a {@link YUVImage} instance containing the unified planar YUV + * encoded image */ - public YUVImage encodeYUV(int[] strides, 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(); - YUVImage dstYUVImage = new YUVImage(srcWidth, strides, srcHeight, subsamp); - encodeYUV(dstYUVImage, flags); + if (align < 1 || ((align & (align - 1)) != 0)) + throw new IllegalStateException("Invalid argument in encodeYUV()"); + YUVImage dstYUVImage = new YUVImage(srcWidth, align, srcHeight, + get(TJ.PARAM_SUBSAMP)); + encodeYUV(dstYUVImage); return dstYUVImage; } /** - * @deprecated Use {@link #encodeYUV(int, int)} instead. + * @deprecated Use {@link #set set()} and {@link #encodeYUV(int)} instead. */ @SuppressWarnings("checkstyle:JavadocMethod") @Deprecated - public byte[] encodeYUV(int flags) throws TJException { - checkSourceImage(); - checkSubsampling(); - YUVImage dstYUVImage = new YUVImage(srcWidth, 4, srcHeight, subsamp); - encodeYUV(dstYUVImage, flags); - return dstYUVImage.getBuf(); + public YUVImage encodeYUV(int align, int flags) throws TJException { + processFlags(flags); + return encodeYUV(align); } /** - * @deprecated Use - * {@link #setSourceImage(BufferedImage, int, int, int, int)} and - * {@link #encodeYUV(byte[], int)} instead. + * 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 + * stride for any plane to 0 is the same as setting it to the plane width + * (see {@link YUVImage}.) If strides is null, then the strides + * for all planes will be set to their respective plane widths. You can + * adjust the strides in order to add an arbitrary amount of row padding to + * each plane. + * + * @return a {@link YUVImage} instance containing the encoded image planes */ - @SuppressWarnings("checkstyle:JavadocMethod") - @Deprecated - public void encodeYUV(BufferedImage srcImage, byte[] dstBuf, int flags) - throws TJException { - setSourceImage(srcImage, 0, 0, 0, 0); - encodeYUV(dstBuf, flags); + 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, + get(TJ.PARAM_SUBSAMP)); + encodeYUV(dstYUVImage); + return dstYUVImage; } /** - * @deprecated Use - * {@link #setSourceImage(BufferedImage, int, int, int, int)} and - * {@link #encodeYUV(int, int)} instead. + * @deprecated Use {@link #set set()} and {@link #encodeYUV(int[])} instead. */ @SuppressWarnings("checkstyle:JavadocMethod") @Deprecated - public byte[] encodeYUV(BufferedImage srcImage, int flags) - throws TJException { - setSourceImage(srcImage, 0, 0, 0, 0); - return encodeYUV(flags); + public YUVImage encodeYUV(int[] strides, int flags) throws TJException { + processFlags(flags); + return encodeYUV(strides); } /** @@ -594,79 +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") - @Deprecated - private native int compress(byte[] srcBuf, int width, int pitch, - int height, int pixelFormat, byte[] jpegBuf, int jpegSubsamp, int jpegQual, - int flags) throws TJException; - - @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") - @Deprecated - private native int compress(int[] srcBuf, 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 compress(int[] srcBuf, int x, int y, int width, - int stride, int height, int pixelFormat, byte[] jpegBuf, int jpegSubsamp, - int jpegQual, int flags) throws TJException; + private native int compress16(short[] srcBuf, int x, int y, int width, + int pitch, int height, int pixelFormat, byte[] jpegBuf) throws TJException; @SuppressWarnings("checkstyle:HiddenField") - private native int compressFromYUV(byte[][] srcPlanes, int[] srcOffsets, - int width, int[] srcStrides, int height, int subsamp, byte[] jpegBuf, - int jpegQual, int flags) + private native int compress8(int[] srcBuf, int x, int y, int width, + int stride, int height, int pixelFormat, byte[] jpegBuf) throws TJException; @SuppressWarnings("checkstyle:HiddenField") - @Deprecated - private native void encodeYUV(byte[] srcBuf, int width, int pitch, - int height, int pixelFormat, byte[] dstBuf, int subsamp, int flags) + private native int compressFromYUV8(byte[][] srcPlanes, int[] srcOffsets, + int width, int[] srcStrides, int height, byte[] jpegBuf) throws TJException; @SuppressWarnings("checkstyle:HiddenField") - private native void encodeYUV(byte[] srcBuf, int x, int y, int width, + private native void encodeYUV8(byte[] srcBuf, int x, int y, int width, int pitch, int height, int pixelFormat, byte[][] dstPlanes, - int[] dstOffsets, int[] dstStrides, int subsamp, int flags) - throws TJException; - - @SuppressWarnings("checkstyle:HiddenField") - @Deprecated - private native void encodeYUV(int[] srcBuf, int width, int stride, - int height, int pixelFormat, byte[] dstBuf, 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; @@ -676,9 +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 int yuvPad = 4; private ByteOrder byteOrder = null; } diff --git a/java/org/libjpegturbo/turbojpeg/TJCustomFilter.java b/java/org/libjpegturbo/turbojpeg/TJCustomFilter.java index 9a34587..78e6e4e 100644 --- a/java/org/libjpegturbo/turbojpeg/TJCustomFilter.java +++ b/java/org/libjpegturbo/turbojpeg/TJCustomFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (C)2011, 2013 D. R. Commander. All Rights Reserved. + * Copyright (C)2011, 2013, 2023 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -58,13 +58,13 @@ public interface TJCustomFilter { * component plane to which coeffBuffer belongs * * @param componentID ID number of the component plane to which - * coeffBuffer belongs (Y, Cb, and Cr have, respectively, ID's + * coeffBuffer belongs. (Y, Cb, and Cr have, respectively, ID's * of 0, 1, and 2 in typical JPEG images.) * * @param transformID ID number of the transformed image to which * 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 aba390b..d8c0545 100644 --- a/java/org/libjpegturbo/turbojpeg/TJDecompressor.java +++ b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java @@ -1,5 +1,6 @@ /* - * Copyright (C)2011-2015, 2018, 2022 D. R. Commander. All Rights Reserved. + * Copyright (C)2011-2015, 2018, 2022-2023 D. R. Commander. + * All Rights Reserved. * Copyright (C)2015 Viktor Szathmáry. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,6 +30,7 @@ package org.libjpegturbo.turbojpeg; +import java.awt.Rectangle; import java.awt.image.*; import java.nio.*; import java.io.*; @@ -50,10 +52,13 @@ public class TJDecompressor implements Closeable { /** * Create a TurboJPEG decompressor instance and associate the JPEG source - * image stored in jpegImage with the newly created instance. + * image or "abbreviated table specification" (AKA "tables-only") datastream + * stored in jpegImage with the newly created instance. Refer + * to {@link #setSourceImage(byte[], int)} for more details. * - * @param jpegImage JPEG image buffer (size of the JPEG image is assumed to - * be the length of the array.) This buffer is not modified. + * @param jpegImage buffer containing a JPEG source image or tables-only + * datastream. (The size of the JPEG image or datastream is assumed to be + * the length of the array.) This buffer is not modified. */ public TJDecompressor(byte[] jpegImage) throws TJException { init(); @@ -62,12 +67,16 @@ public class TJDecompressor implements Closeable { /** * Create a TurboJPEG decompressor instance and associate the JPEG source - * image of length imageSize bytes stored in - * jpegImage with the newly created instance. + * image or "abbreviated table specification" (AKA "tables-only") datastream + * of length imageSize bytes stored in jpegImage + * with the newly created instance. Refer to + * {@link #setSourceImage(byte[], int)} for more details. * - * @param jpegImage JPEG image buffer. This buffer is not modified. + * @param jpegImage buffer containing a JPEG source image or tables-only + * datastream. This buffer is not modified. * - * @param imageSize size of the JPEG image (in bytes) + * @param imageSize size of the JPEG source image or tables-only datastream + * (in bytes) */ public TJDecompressor(byte[] jpegImage, int imageSize) throws TJException { init(); @@ -75,11 +84,12 @@ public class TJDecompressor implements Closeable { } /** - * Create a TurboJPEG decompressor instance and associate the YUV planar - * 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 YUV planar + * @param yuvImage {@link YUVImage} instance containing a planar YUV source * image to be decoded. This image is not modified. */ @SuppressWarnings("checkstyle:HiddenField") @@ -93,18 +103,20 @@ public class TJDecompressor implements Closeable { * "tables-only") datastream of length imageSize bytes stored in * jpegImage with this decompressor instance. If * jpegImage contains a JPEG image, then this image will be used - * as the source image for subsequent decompress operations. Passing a + * as the source image for subsequent decompression operations. Passing a * tables-only datastream to this method primes the decompressor with * quantization and Huffman tables that can be used when decompressing * subsequent "abbreviated image" datastreams. This is useful, for instance, * 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 image or an "abbreviated table - * specification" (AKA "tables-only") datastream. This buffer is not - * modified. + * @param jpegImage buffer containing a JPEG source image or tables-only + * datastream. This buffer is not modified. * - * @param imageSize size of the JPEG image (in bytes) + * @param imageSize size of the JPEG source image or tables-only datastream + * (in bytes) */ public void setSourceImage(byte[] jpegImage, int imageSize) throws TJException { @@ -117,27 +129,20 @@ public class TJDecompressor implements Closeable { } /** - * @deprecated Use {@link #setSourceImage(byte[], int)} instead. - */ - @SuppressWarnings("checkstyle:JavadocMethod") - @Deprecated - public void setJPEGImage(byte[] jpegImage, int imageSize) - throws TJException { - setSourceImage(jpegImage, imageSize); - } - - /** - * Associate the specified YUV planar source image with this decompressor - * instance. Subsequent decompress operations will decode this image into an - * RGB or grayscale destination image. + * Associate the specified planar YUV source image with this decompressor + * instance. Subsequent decompression operations will decode this image into + * 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 YUV planar image to - * be decoded. This image is not modified. + * @param srcImage {@link YUVImage} instance containing a planar YUV source + * image to be decoded. This image is not modified. */ public void setSourceImage(YUVImage srcImage) { if (srcImage == null) throw new IllegalArgumentException("Invalid argument in setSourceImage()"); yuvImage = srcImage; + set(TJ.PARAM_SUBSAMP, srcImage.getSubsamp()); jpegBuf = null; jpegBufSize = 0; } @@ -153,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; @@ -168,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. + * + * @param param one of {@link TJ#PARAM_STOPONWARNING TJ.PARAM_*} + * + * @param value value of the decompression parameter (refer to + * {@link TJ#PARAM_STOPONWARNING parameter documentation}) + */ + public native void set(int param, int value); + + /** + * Get the value of a decompression parameter. * - * @return the level of chrominance subsampling used in the source image - * (JPEG or YUV) associated with this decompressor instance. + * @param param one of {@link TJ#PARAM_STOPONWARNING TJ.PARAM_*} + * + * @return the value of the specified decompression parameter, or -1 if the + * value is unknown. + */ + public native int get(int param); + + /** + * Set the scaling factor for subsequent lossy decompression operations. + * + * @param scalingFactor {@link TJScalingFactor} instance that specifies a + * fractional scaling factor that the decompressor supports (see + * {@link TJ#getScalingFactors}), or {@link TJ#UNSCALED} for no scaling. + * Decompression scaling is a function of the IDCT algorithm, so scaling + * factors are generally limited to multiples of 1/8. If the entire JPEG + * image will be decompressed, then the width and height of the scaled + * destination image can be determined by calling + * scalingFactor.{@link TJScalingFactor#getScaled getScaled()} + * with the JPEG image width and height (see {@link #getWidth} and + * {@link #getHeight}.) When decompressing into a planar YUV image, an + * intermediate buffer copy will be performed if the width or height of the + * scaled destination image is not an even multiple of the MCU block size + * (see {@link TJ#getMCUWidth TJ.getMCUWidth()} and {@link TJ#getMCUHeight + * TJ.getMCUHeight()}.) Note that decompression scaling is not available + * (and the specified scaling factor is ignored) when decompressing lossless + * JPEG images (see {@link TJ#PARAM_LOSSLESS}), since the IDCT algorithm is + * not used with those images. Note also that {@link TJ#PARAM_FASTDCT} is + * ignored when decompression scaling is enabled. + */ + @SuppressWarnings("checkstyle:HiddenField") + public void setScalingFactor(TJScalingFactor scalingFactor) { + if (scalingFactor == null) + throw new IllegalArgumentException("Invalid argument in setScalingFactor()"); + + TJScalingFactor[] sf = TJ.getScalingFactors(); + int i; + for (i = 0; i < sf.length; i++) { + if (scalingFactor.getNum() == sf[i].getNum() && + scalingFactor.getDenom() == sf[i].getDenom()) + break; + } + if (i >= sf.length) + throw new IllegalArgumentException("Unsupported scaling factor"); + + this.scalingFactor = scalingFactor; + } + + /** + * Set the cropping region for partially decompressing a lossy JPEG image + * into a packed-pixel image. + * + * @param croppingRegion java.awt.Rectangle instance that + * specifies a subregion of the JPEG image to decompress, or + * {@link TJ#UNCROPPED} for no cropping. The left boundary of the cropping + * region must be evenly divisible by the scaled MCU block width, which can + * be determined by calling {@link TJScalingFactor#getScaled + * TJScalingFactor.getScaled()} with the specified scaling factor (see + * {@link #setScalingFactor setScalingFactor()}) and the MCU block width + * (see {@link TJ#getMCUWidth TJ.getMCUWidth()}) for the level of chrominance + * subsampling in the JPEG image (see {@link TJ#PARAM_SUBSAMP}.) The + * cropping region should be specified relative to the scaled image + * dimensions. Unless croppingRegion is {@link TJ#UNCROPPED}, + * the JPEG header must be read (see {@link #setSourceImage(byte[], int)} + * prior to calling this method. + */ + @SuppressWarnings("checkstyle:HiddenField") + public void setCroppingRegion(Rectangle croppingRegion) throws TJException { + this.croppingRegion = croppingRegion; + setCroppingRegion(); + } + + /** + * @deprecated Use {@link #get get}({@link TJ#PARAM_SUBSAMP}) + * instead. */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated public int getSubsamp() { - if (yuvImage != null) - return yuvImage.getSubsamp(); - if (jpegSubsamp < 0) + int subsamp = get(TJ.PARAM_SUBSAMP); + if (subsamp == TJ.SAMP_UNKNOWN) throw new IllegalStateException(NO_ASSOC_ERROR); - if (jpegSubsamp >= TJ.NUMSAMP) + if (subsamp >= TJ.NUMSAMP) throw new IllegalStateException("JPEG header information is invalid"); - return jpegSubsamp; + return subsamp; } /** - * Returns the colorspace used in the source image (JPEG or YUV) associated - * with this decompressor instance. See {@link TJ#CS_RGB TJ.CS_*}. If the - * source image is YUV, then this always returns {@link TJ#CS_YCbCr}. - * - * @return the colorspace used in the source image (JPEG or YUV) associated - * with this decompressor instance. + * @deprecated Use {@link #get get}({@link TJ#PARAM_COLORSPACE}) + * instead. */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated public int getColorspace() { if (yuvImage != null) return TJ.CS_YCbCr; + int jpegColorspace = get(TJ.PARAM_COLORSPACE); if (jpegColorspace < 0) throw new IllegalStateException(NO_ASSOC_ERROR); if (jpegColorspace >= TJ.NUMCS) @@ -210,9 +305,9 @@ public class TJDecompressor implements Closeable { } /** - * Returns the JPEG image buffer associated with this decompressor instance. + * Returns the JPEG buffer associated with this decompressor instance. * - * @return the JPEG image buffer associated with this decompressor instance. + * @return the JPEG buffer associated with this decompressor instance. */ public byte[] getJPEGBuf() { if (jpegBuf == null) @@ -234,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 YUV source image associated - * with this decompressor instance and output a 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 output image is fully recoverable if this method throws a - * non-fatal {@link TJException} (unless - * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.) - * - * @param dstBuf buffer that will receive the decompressed/decoded image. - * If the source image is a JPEG image, then this buffer should normally be - * pitch * scaledHeight bytes in size, where - * scaledHeight can be determined by calling - * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegHeight) - * with one of the scaling factors returned from {@link - * TJ#getScalingFactors} or by calling {@link #getScaledHeight}. If the - * source image is a YUV image, then this buffer should normally be - * pitch * height bytes in size, where height is - * the height of the YUV image. However, the buffer may also be larger than - * the dimensions of the source image, in which case the x, - * y, and pitch parameters can be used to specify - * the region into which the source image should be decompressed/decoded. + * 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/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 {@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 @@ -344,320 +405,456 @@ 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 line of the destination image. Normally, this - * should be set to scaledWidth * TJ.pixelSize(pixelFormat) if - * the destination image is unpadded, but you can use this to, for instance, - * pad each line of the destination image to a 4-byte boundary or to - * decompress/decode the source image into a region of a larger image. NOTE: - * if the source image is a JPEG image, then scaledWidth can be - * determined by calling - * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegWidth) - * or by calling {@link #getScaledWidth}. If the source image is a - * YUV image, then scaledWidth is the width of the YUV image. - * Setting this parameter to 0 is the equivalent of setting it to - * scaledWidth * TJ.pixelSize(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. + * @param pitch bytes 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/decode into a + * specific region of a larger image. NOTE: if the source image is a lossy + * JPEG image, then destinationWidth is either the scaled JPEG + * width (see {@link #setScalingFactor setScalingFactor()}, + * {@link TJScalingFactor#getScaled TJScalingFactor.getScaled()}, and + * {@link #getWidth}) or the width of the cropping region (see + * {@link #setCroppingRegion setCroppingRegion()}.) If the source image is a + * YUV image or a lossless JPEG image, then destinationWidth is + * the width of the source image. * * @param pixelFormat pixel format of the decompressed/decoded image (one of * {@link TJ#PF_RGB TJ.PF_*}) - * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} */ + public void decompress8(byte[] dstBuf, int x, int y, int pitch, + int pixelFormat) throws TJException { + if (jpegBuf == null && yuvImage == null) + throw new IllegalStateException("No source image is associated with this instance"); + if (dstBuf == null || x < 0 || y < 0 || pitch < 0 || pixelFormat < 0 || + pixelFormat >= TJ.NUMPF) + throw new IllegalArgumentException("Invalid argument in decompress8()"); + if (yuvImage != null) { + checkSubsampling(); + decodeYUV8(yuvImage.getPlanes(), yuvImage.getOffsets(), + yuvImage.getStrides(), dstBuf, x, y, yuvImage.getWidth(), + pitch, yuvImage.getHeight(), pixelFormat); + } else + decompress8(jpegBuf, jpegBufSize, dstBuf, x, y, pitch, pixelFormat); + } + + /** + * @deprecated Use {@link #set set()}, + * {@link #setScalingFactor setScalingFactor()}, and + * {@link #decompress8(byte[], int, int, int, int)} instead. + */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated public void decompress(byte[] dstBuf, int x, int y, int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags) throws TJException { - if (jpegBuf == null && yuvImage == null) - throw new IllegalStateException(NO_ASSOC_ERROR); - if (dstBuf == null || x < 0 || y < 0 || pitch < 0 || - (yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) || - pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0) + if ((yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) || + flags < 0) throw new IllegalArgumentException("Invalid argument in decompress()"); - if (yuvImage != null) - decodeYUV(yuvImage.getPlanes(), yuvImage.getOffsets(), - yuvImage.getStrides(), yuvImage.getSubsamp(), dstBuf, x, y, - yuvImage.getWidth(), pitch, yuvImage.getHeight(), pixelFormat, - flags); - else { - if (x > 0 || y > 0) - decompress(jpegBuf, jpegBufSize, dstBuf, x, y, desiredWidth, pitch, - desiredHeight, pixelFormat, flags); - else - decompress(jpegBuf, jpegBufSize, dstBuf, desiredWidth, pitch, - desiredHeight, pixelFormat, flags); + + if (yuvImage == null) { + TJScalingFactor sf = getScalingFactor(desiredWidth, desiredHeight); + setScalingFactor(sf); } + processFlags(flags); + decompress8(dstBuf, x, y, pitch, pixelFormat); } /** - * @deprecated Use - * {@link #decompress(byte[], int, int, int, int, int, int, int)} instead. + * 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 #decompress8(byte[], 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 byte[] decompress8(int pitch, int pixelFormat) throws TJException { + if (pitch < 0 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF) + throw new IllegalArgumentException("Invalid argument in decompress8()"); + int pixelSize = TJ.getPixelSize(pixelFormat); + int scaledWidth = scalingFactor.getScaled(getJPEGWidth()); + int scaledHeight = scalingFactor.getScaled(getJPEGHeight()); + if (pitch == 0) + pitch = scaledWidth * pixelSize; + byte[] buf = new byte[pitch * scaledHeight]; + decompress8(buf, 0, 0, pitch, pixelFormat); + return buf; + } + + /** + * @deprecated Use {@link #set set()}, + * {@link #setScalingFactor setScalingFactor()}, and + * {@link #decompress8(int, int)} instead. */ @SuppressWarnings("checkstyle:JavadocMethod") @Deprecated - public void decompress(byte[] dstBuf, int desiredWidth, int pitch, - int desiredHeight, int pixelFormat, int flags) - throws TJException { - decompress(dstBuf, 0, 0, desiredWidth, pitch, desiredHeight, pixelFormat, - flags); + 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 JPEG source image associated with this decompressor - * instance and return a buffer containing the decompressed image. + * 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 desiredWidth see - * {@link #decompress(byte[], int, int, int, int, int, int, int)} - * for description + * @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 #decompress(byte[], int, int, int, int, int, int, int)} - * for description + * {@link #decompress12(short[], int, int, int, int)} for description * - * @param desiredHeight see - * {@link #decompress(byte[], int, int, int, 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 flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} + * @param pitch see + * {@link #decompress16(short[], int, int, int, int)} for description * - * @return a buffer containing the decompressed image. + * @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 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 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 = 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, desiredWidth, pitch, desiredHeight, pixelFormat, flags); + short[] buf = new short[pitch * scaledHeight]; + decompress16(buf, 0, 0, pitch, pixelFormat); return buf; } /** - * Decompress the JPEG source image associated with this decompressor - * instance into a YUV planar image and store it in the given - * YUVImage instance. This method performs JPEG decompression - * but leaves out the color conversion step, so a planar YUV image is - * generated instead of an RGB or grayscale image. This method cannot be + * 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 YUV planar output image is fully recoverable if this method + * NOTE: The planar YUV destination image is fully recoverable if this method * throws a non-fatal {@link TJException} (unless - * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.) + * {@link TJ#PARAM_STOPONWARNING} is set.) * - * @param dstImage {@link YUVImage} instance that will receive the YUV planar - * image. The level of subsampling specified in this YUVImage - * instance must match that of the JPEG image, and the width and height - * specified in the YUVImage instance must match one of the - * scaled image sizes that TurboJPEG is capable of generating from the JPEG - * source image. - * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} + * @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 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 TurboJPEG 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()); } /** - * @deprecated Use {@link #decompressToYUV(YUVImage, int)} instead. + * @deprecated Use {@link #set set()}, {@link #setScalingFactor + * setScalingFactor()}, and {@link #decompressToYUV(YUVImage)} instead. */ @SuppressWarnings("checkstyle:JavadocMethod") @Deprecated - public void decompressToYUV(byte[] dstBuf, int flags) throws TJException { - YUVImage dstYUVImage = new YUVImage(dstBuf, jpegWidth, 4, jpegHeight, - jpegSubsamp); - decompressToYUV(dstYUVImage, flags); + 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 JPEG source image associated with this decompressor - * instance into a set of Y, U (Cb), and V (Cr) image planes and return a - * YUVImage instance containing the decompressed image planes. - * This method performs JPEG decompression but leaves out the color - * conversion step, so a planar YUV image is generated instead of an RGB or - * grayscale image. This method cannot be used to decompress JPEG source - * images with the CMYK or YCCK colorspace. - * - * @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 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 line in the corresponding plane of the output image. Setting the - * stride for any plane to 0 is the same as setting it to the scaled - * component width of the plane. If strides is NULL, then the - * strides for all planes will be set to their respective scaled component - * widths. You can adjust the strides in order to add an arbitrary amount of - * line padding to each plane. - * - * @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 YUV planar image. + * per row in the corresponding plane of the YUV image. Setting the stride + * for any plane to 0 is the same as setting it to the scaled plane width + * (see {@link YUVImage}.) If strides is null, then the strides + * for all planes will be set to their respective scaled plane widths. You + * can adjust the strides in order to add an arbitrary amount of row padding + * to each plane. + * + * @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 YUV planar image buffer and return a - * YUVImage instance containing the decompressed image. This - * method performs JPEG decompression but leaves out the color conversion - * step, so a planar YUV image is generated instead of an RGB or grayscale - * image. This method cannot be used to decompress JPEG source images with - * the CMYK or YCCK colorspace. - * - * @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.) - * - * @param pad the width of each line in each plane of the YUV image will be - * padded to the nearest multiple of this number of bytes (must be a power of - * 2.) - * - * @param 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.) + * 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 flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} + * @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.) * - * @return a YUV planar image. + * @return a {@link YUVImage} instance containing the unified planar YUV + * decompressed image */ - public YUVImage decompressToYUV(int desiredWidth, int pad, int desiredHeight, - int flags) throws TJException { - 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"); + 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"); - int scaledWidth = getScaledWidth(desiredWidth, desiredHeight); - int scaledHeight = getScaledHeight(desiredWidth, desiredHeight); - YUVImage dstYUVImage = new YUVImage(scaledWidth, pad, scaledHeight, - jpegSubsamp); - decompressToYUV(dstYUVImage, flags); + YUVImage dstYUVImage = new YUVImage(scalingFactor.getScaled(jpegWidth), + align, + scalingFactor.getScaled(jpegHeight), + get(TJ.PARAM_SUBSAMP)); + decompressToYUV(dstYUVImage); return dstYUVImage; } /** - * @deprecated Use {@link #decompressToYUV(int, int, int, int)} instead. + * @deprecated Use {@link #set set()}, {@link #setScalingFactor + * setScalingFactor()}, and {@link #decompressToYUV(int)} instead. */ @SuppressWarnings("checkstyle:JavadocMethod") @Deprecated - public byte[] decompressToYUV(int flags) throws TJException { - YUVImage dstYUVImage = new YUVImage(jpegWidth, 4, jpegHeight, jpegSubsamp); - decompressToYUV(dstYUVImage, flags); - return dstYUVImage.getBuf(); + public YUVImage decompressToYUV(int desiredWidth, int align, + int desiredHeight, int flags) + throws TJException { + if (flags < 0) + throw new IllegalArgumentException("Invalid argument in decompressToYUV()"); + + TJScalingFactor sf = getScalingFactor(desiredWidth, desiredHeight); + setScalingFactor(sf); + processFlags(flags); + return decompressToYUV(align); } /** - * Decompress the JPEG source image or decode the YUV source image associated - * with this decompressor instance and output a grayscale, RGB, or CMYK image - * to the given destination buffer. + * Decompress the 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 output image is fully recoverable if this method throws a - * non-fatal {@link TJException} (unless - * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.) - * - * @param dstBuf buffer that will receive the decompressed/decoded image. - * If the source image is a JPEG image, then this buffer should normally be - * stride * scaledHeight pixels in size, where - * scaledHeight can be determined by calling - * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegHeight) - * with one of the scaling factors returned from {@link - * TJ#getScalingFactors} or by calling {@link #getScaledHeight}. If the - * source image is a YUV image, then this buffer should normally be - * stride * height pixels in size, where height is - * the height of the YUV image. However, the buffer may also be larger than - * the dimensions of the JPEG image, in which case the x, - * y, and stride parameters can be used to specify - * the region into which the source image should be decompressed. + * 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/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 @@ -665,101 +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 line of the destination image. Normally, this - * should be set to scaledWidth, but you can use this to, for - * instance, decompress the JPEG image into a region of a larger image. - * NOTE: if the source image is a JPEG image, then scaledWidth - * can be determined by calling - * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegWidth) - * or by calling {@link #getScaledWidth}. If the source image is a - * YUV image, then scaledWidth is the width of the YUV image. - * Setting this parameter to 0 is the equivalent of setting it to - * scaledWidth. - * - * @param desiredHeight If the source image is a JPEG image, then this - * specifies the desired height (in pixels) of the decompressed image (or - * image region.) If the desired destination image dimensions are different - * than the source image dimensions, then TurboJPEG will use scaling in the - * JPEG decompressor to generate the largest possible image that will fit - * within the desired dimensions. Setting this to 0 is the same as setting - * it to the height of the JPEG image (in other words, the height will not be - * considered when determining the scaled image size.) This parameter is - * ignored if the source image is a YUV image. + * @param 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 {@link #setScalingFactor + * setScalingFactor()}, {@link TJScalingFactor#getScaled + * TJScalingFactor.getScaled()}, and {@link #getWidth}) or the width of the + * cropping region (see {@link #setCroppingRegion setCroppingRegion()}.) If + * the source image is a YUV image or a lossless JPEG image, then + * destinationWidth is the width of the source image. * - * @param pixelFormat pixel format of the decompressed image (one of + * @param pixelFormat pixel format of the decompressed/decoded image (one of * {@link TJ#PF_RGB TJ.PF_*}) - * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} */ + public void 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 || + pixelFormat < 0 || pixelFormat >= TJ.NUMPF) + throw new IllegalArgumentException("Invalid argument in decompress8()"); + if (yuvImage != null) { + checkSubsampling(); + decodeYUV8(yuvImage.getPlanes(), yuvImage.getOffsets(), + yuvImage.getStrides(), dstBuf, x, y, yuvImage.getWidth(), + stride, yuvImage.getHeight(), pixelFormat); + } else + decompress8(jpegBuf, jpegBufSize, dstBuf, x, y, stride, pixelFormat); + } + + /** + * @deprecated Use {@link #set set()}, {@link #setScalingFactor + * setScalingFactor()}, and {@link #decompress8(int[], int, int, int, int)} + * instead. + */ + @SuppressWarnings("checkstyle:JavadocMethod") + @Deprecated public void decompress(int[] dstBuf, int x, int y, int desiredWidth, int stride, int desiredHeight, int pixelFormat, int flags) throws TJException { - if (jpegBuf == null && yuvImage == null) - throw new IllegalStateException(NO_ASSOC_ERROR); - if (dstBuf == null || x < 0 || y < 0 || stride < 0 || - (yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) || - pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0) + if ((yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) || + flags < 0) throw new IllegalArgumentException("Invalid argument in decompress()"); - if (yuvImage != null) - decodeYUV(yuvImage.getPlanes(), yuvImage.getOffsets(), - yuvImage.getStrides(), yuvImage.getSubsamp(), dstBuf, x, y, - yuvImage.getWidth(), stride, yuvImage.getHeight(), pixelFormat, - flags); - else - decompress(jpegBuf, jpegBufSize, dstBuf, x, y, desiredWidth, stride, - desiredHeight, pixelFormat, flags); + + if (yuvImage == null) { + TJScalingFactor sf = getScalingFactor(desiredWidth, desiredHeight); + setScalingFactor(sf); + } + processFlags(flags); + decompress8(dstBuf, x, y, stride, pixelFormat); } /** - * Decompress the JPEG source image or decode the YUV source image associated - * with this decompressor instance and output a decompressed/decoded image to - * the given BufferedImage instance. + * Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV + * source image associated with this decompressor instance and output an + * 8-bit-per-sample packed-pixel decompressed/decoded image to the given + * BufferedImage instance. *

      - * NOTE: The output image is fully recoverable if this method throws a - * non-fatal {@link TJException} (unless - * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.) + * NOTE: The destination image is fully recoverable if this method throws a + * non-fatal {@link TJException} (unless {@link TJ#PARAM_STOPONWARNING} + * is set.) * * @param dstImage a BufferedImage instance that will receive - * the decompressed/decoded image. If the source image is a JPEG image, then - * the width and height of the BufferedImage instance must match - * one of the scaled image sizes that TurboJPEG is capable of generating from - * the JPEG image. If the source image is a YUV image, then the width and - * height of the BufferedImage instance must match the width and - * height of the YUV image. - * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} + * the packed-pixel decompressed/decoded image. If the source image is a + * 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 TurboJPEG 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) @@ -801,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 = @@ -821,46 +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 YUV source image associated - * with this decompressor instance and return a BufferedImage - * instance containing the 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 - * decompressed/decoded image. + * 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); } /** @@ -883,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; @@ -890,51 +1123,58 @@ public class TJDecompressor implements Closeable { private native void decompressHeader(byte[] srcBuf, int size) throws TJException; - @Deprecated - private native void decompress(byte[] srcBuf, int size, byte[] dstBuf, - 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, byte[] dstBuf, int x, - int y, int desiredWidth, int pitch, 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; - @Deprecated - private native void decompress(byte[] srcBuf, int size, int[] dstBuf, - int desiredWidth, int stride, int desiredHeight, int pixelFormat, - 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 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 decompress16(byte[] srcBuf, int size, short[] dstBuf, + int x, int y, int pitch, int pixelFormat) throws TJException; - @Deprecated - private native void decompressToYUV(byte[] srcBuf, int size, byte[] dstBuf, - 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 decompressToYUV(byte[] srcBuf, int size, - byte[][] dstPlanes, int[] dstOffsets, int desiredWidth, int[] dstStrides, - int desiredheight, int flags) 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 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; + 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; - 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; + /** + * @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; + 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 41c4b45..7c32cce 100644 --- a/java/org/libjpegturbo/turbojpeg/TJTransform.java +++ b/java/org/libjpegturbo/turbojpeg/TJTransform.java @@ -1,5 +1,6 @@ /* - * Copyright (C)2011, 2013, 2018 D. R. Commander. All Rights Reserved. + * Copyright (C)2011, 2013, 2018, 2022-2023 D. R. Commander. + * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -95,29 +96,30 @@ 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; + public static final int OPT_PERFECT = (1 << 0); /** * This option will discard any partial MCU blocks that cannot be * transformed. */ - public static final int OPT_TRIM = 2; + public static final int OPT_TRIM = (1 << 1); /** * This option will enable lossless cropping. */ - public static final int OPT_CROP = 4; + public static final int OPT_CROP = (1 << 2); /** - * This option will discard the color data in the input image and produce - * a grayscale output image. + * This option will discard the color data in the source image and produce a + * grayscale destination image. */ - public static final int OPT_GRAY = 8; + public static final int OPT_GRAY = (1 << 3); /** * This option will prevent {@link TJTransformer#transform * TJTransformer.transform()} from outputting a JPEG image for this @@ -125,21 +127,36 @@ public class TJTransform extends Rectangle { * filter to capture the transformed DCT coefficients without transcoding * them. */ - public static final int OPT_NOOUTPUT = 16; + public static final int OPT_NOOUTPUT = (1 << 4); /** - * This option will enable progressive entropy coding in the output image + * This option will enable progressive entropy coding in the JPEG image * generated by this particular transform. Progressive entropy coding will * generally improve compression relative to baseline entropy coding (the - * default), but it will reduce compression and decompression performance - * considerably. + * default), but it will reduce decompression performance considerably. + * Can be combined with {@link #OPT_ARITHMETIC}. Implies + * {@link #OPT_OPTIMIZE} unless {@link #OPT_ARITHMETIC} is also specified. */ - public static final int OPT_PROGRESSIVE = 32; + public static final int OPT_PROGRESSIVE = (1 << 5); /** * This option will prevent {@link TJTransformer#transform * TJTransformer.transform()} from copying any extra markers (including EXIF - * and ICC profile data) from the source image to the output image. + * and ICC profile data) from the source image to the destination image. */ - public static final int OPT_COPYNONE = 64; + public static final int OPT_COPYNONE = (1 << 6); + /** + * This option will enable arithmetic entropy coding in the 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 {@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); /** @@ -152,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 - @@ -165,13 +184,13 @@ public class TJTransform extends Rectangle { * equivalent of setting it to (height of the source JPEG image - * y). * - * @param op one of the transform operations (OP_*) + * @param op one of the transform operations ({@link #OP_NONE OP_*}) * * @param options the bitwise OR of one or more of the transform options - * (OPT_*) + * ({@link #OPT_PERFECT OPT_*}) * - * @param cf an instance of an object that implements the {@link - * TJCustomFilter} interface, or null if no custom filter is needed + * @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, @@ -185,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 (OP_*) + * @param op one of the transform operations ({@link #OP_NONE OP_*}) * * @param options the bitwise OR of one or more of the transform options - * (OPT_*) + * ({@link #OPT_PERFECT OPT_*}) * - * @param cf an instance of an object that implements the {@link - * TJCustomFilter} interface, or null if no custom filter is needed + * @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, @@ -208,13 +227,14 @@ public class TJTransform extends Rectangle { } /** - * Transform operation (one of OP_*) + * Transform operation (one of {@link #OP_NONE OP_*}) */ @SuppressWarnings("checkstyle:VisibilityModifier") public int op = 0; /** - * Transform options (bitwise OR of one or more of OPT_*) + * Transform options (bitwise OR of one or more of + * {@link #OPT_PERFECT OPT_*}) */ @SuppressWarnings("checkstyle:VisibilityModifier") public int options = 0; diff --git a/java/org/libjpegturbo/turbojpeg/TJTransformer.java b/java/org/libjpegturbo/turbojpeg/TJTransformer.java index d7a56f3..4d46b5a 100644 --- a/java/org/libjpegturbo/turbojpeg/TJTransformer.java +++ b/java/org/libjpegturbo/turbojpeg/TJTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright (C)2011, 2013-2015 D. R. Commander. All Rights Reserved. + * Copyright (C)2011, 2013-2015, 2023 D. R. Commander. All Rights Reserved. * Copyright (C)2015 Viktor Szathmáry. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,10 +43,12 @@ public class TJTransformer extends TJDecompressor { /** * Create a TurboJPEG lossless transformer instance and associate the JPEG - * image stored in jpegImage with the newly created instance. + * source image stored in jpegImage with the newly created + * instance. * - * @param jpegImage JPEG image buffer (size of the JPEG image is assumed to - * be the length of the array.) This buffer is not modified. + * @param jpegImage buffer containing the JPEG source image to transform. + * (The size of the JPEG image is assumed to be the length of the array.) + * This buffer is not modified. */ public TJTransformer(byte[] jpegImage) throws TJException { init(); @@ -55,12 +57,13 @@ public class TJTransformer extends TJDecompressor { /** * Create a TurboJPEG lossless transformer instance and associate the JPEG - * image of length imageSize bytes stored in + * source image of length imageSize bytes stored in * jpegImage with the newly created instance. * - * @param jpegImage JPEG image buffer. This buffer is not modified. + * @param jpegImage buffer containing the JPEG source image to transform. + * This buffer is not modified. * - * @param imageSize size of the JPEG image (in bytes) + * @param imageSize size of the JPEG source image (in bytes) */ public TJTransformer(byte[] jpegImage, int imageSize) throws TJException { init(); @@ -68,81 +71,99 @@ public class TJTransformer extends TJDecompressor { } /** - * Losslessly transform the JPEG image associated with this transformer - * instance into one or more JPEG images stored in the given destination - * buffers. Lossless transforms work by moving the raw coefficients from one - * JPEG image structure to another without altering the values of the - * coefficients. While this is typically faster than decompressing the - * image, transforming it, and re-compressing it, lossless transforms are not - * free. Each lossless transform requires reading and performing Huffman - * decoding on all of the coefficients in the source image, regardless of the - * size of the destination image. Thus, this method provides a means of - * generating multiple transformed images from the same source or of applying - * multiple transformations simultaneously, in order to eliminate the need to - * read the source coefficients multiple times. + * Losslessly transform the JPEG source image associated with this + * transformer instance into one or more JPEG images stored in the given + * destination buffers. Lossless transforms work by moving the raw + * coefficients from one JPEG image structure to another without altering the + * values of the coefficients. While this is typically faster than + * decompressing the image, transforming it, and re-compressing it, lossless + * transforms are not free. Each lossless transform requires reading and + * performing Huffman decoding on all of the coefficients in the source + * image, regardless of the size of the destination image. Thus, this method + * provides a means of generating multiple transformed images from the same + * source or of applying multiple transformations simultaneously, in order to + * eliminate the need to read the source coefficients multiple times. * - * @param dstBufs an array of image buffers. dstbufs[i] will - * receive a JPEG image that has been transformed using the parameters in - * transforms[i]. Use {@link TJ#bufSize} to determine the - * maximum size for each buffer based on the transformed or cropped width and - * height and the level of subsampling used in the source image. + * @param dstBufs an array of JPEG destination buffers. + * dstbufs[i] will receive a JPEG image that has been + * transformed using the parameters in transforms[i]. Use + * {@link TJ#bufSize TJ.bufSize()} to determine the maximum size for each + * buffer based on the transformed or cropped width and height and the level + * of subsampling used in the source image. * * @param transforms an array of {@link TJTransform} instances, each of * which specifies the transform parameters and/or cropping region for the - * corresponding transformed output image - * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} + * corresponding transformed JPEG image + */ + 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); } /** - * Losslessly transform the JPEG image associated with this transformer - * instance and return an array of {@link TJDecompressor} instances, each of - * which has a transformed JPEG image associated with it. + * Losslessly transform the JPEG source image associated with this + * transformer instance and return an array of {@link TJDecompressor} + * instances, each of which has a transformed JPEG image associated with it. * * @param transforms an array of {@link TJTransform} instances, each of * which specifies the transform parameters and/or cropping region for the - * corresponding transformed output image - * - * @param flags the bitwise OR of one or more of - * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} + * corresponding transformed JPEG image * * @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 - * generated by the most recent transform operation. + * (in bytes) generated by the most recent transform operation. * * @return an array containing the sizes of the transformed JPEG images - * generated by the most recent transform operation. + * (in bytes) generated by the most recent transform operation. */ public int[] getTransformedSizes() { if (transformedSizes == null) @@ -153,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 4da9843..c2a43fd 100644 --- a/java/org/libjpegturbo/turbojpeg/YUVImage.java +++ b/java/org/libjpegturbo/turbojpeg/YUVImage.java @@ -1,5 +1,5 @@ /* - * Copyright (C)2014, 2017 D. R. Commander. All Rights Reserved. + * Copyright (C)2014, 2017, 2023 D. R. Commander. All Rights Reserved. * Copyright (C)2015 Viktor Szathmáry. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,7 +30,7 @@ package org.libjpegturbo.turbojpeg; /** - * This class encapsulates a YUV planar image and the metadata + * This class encapsulates a planar YUV image and the metadata * associated with it. The TurboJPEG API allows both the JPEG compression and * decompression pipelines to be split into stages: YUV encode, compress from * YUV, decompress to YUV, and YUV decode. A YUVImage instance @@ -38,30 +38,32 @@ package org.libjpegturbo.turbojpeg; * operations and as the source image for compress-from-YUV and YUV decode * operations. *

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

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

      * For example, if the source image is 35 x 35 pixels and 4:2:2 subsampling is * used, then the luminance plane would be 36 x 35 bytes, and each of the - * chrominance planes would be 18 x 35 bytes. If you specify a line padding of - * 4 bytes on top of this, then the luminance plane would be 36 x 35 bytes, and - * each of the chrominance planes would be 20 x 35 bytes. + * chrominance planes would be 18 x 35 bytes. If you specify a row alignment + * of 4 bytes on top of this, then the luminance plane would be 36 x 35 bytes, + * and each of the chrominance planes would be 20 x 35 bytes. */ public class YUVImage { @@ -75,7 +77,7 @@ public class YUVImage { * @param width width (in pixels) of the YUV image * * @param strides an array of integers, each specifying the number of bytes - * per line in the corresponding plane of the YUV image. Setting the stride + * per row in the corresponding plane of the YUV image. Setting the stride * for any plane to 0 is the same as setting it to the plane width (see * {@link YUVImage above}.) If strides is null, then the * strides for all planes will be set to their respective plane widths. When @@ -92,22 +94,24 @@ public class YUVImage { } /** - * Create a new YUVImage instance backed by a unified image - * buffer, and allocate memory for the image buffer. + * Create a new YUVImage instance backed by a unified buffer, + * and allocate memory for the buffer. * * @param width width (in pixels) of the YUV image * - * @param pad Each line of each plane in the YUV image buffer will be padded - * to this number of bytes (must be a power of 2.) + * @param align row alignment (in bytes) of the YUV image (must be a power of + * 2.) Setting this parameter to n specifies that each row in each plane of + * the YUV image will be padded to the nearest multiple of n bytes + * (1 = unpadded.) * * @param height height (in pixels) of the YUV image * * @param subsamp the level of chrominance subsampling to be used in the YUV * image (one of {@link TJ#SAMP_444 TJ.SAMP_*}) */ - public YUVImage(int width, int pad, int height, int subsamp) { - setBuf(new byte[TJ.bufSizeYUV(width, pad, height, subsamp)], width, pad, - height, subsamp); + public YUVImage(int width, int align, int height, int subsamp) { + setBuf(new byte[TJ.bufSizeYUV(width, align, height, subsamp)], width, + align, height, subsamp); } /** @@ -130,11 +134,11 @@ public class YUVImage { * @param width width (in pixels) of the new YUV image (or subregion) * * @param strides an array of integers, each specifying the number of bytes - * per line in the corresponding plane of the YUV image. Setting the stride + * per row in the corresponding plane of the YUV image. Setting the stride * for any plane to 0 is the same as setting it to the plane width (see * {@link YUVImage above}.) If strides is null, then the * strides for all planes will be set to their respective plane widths. You - * can adjust the strides in order to add an arbitrary amount of line padding + * can adjust the strides in order to add an arbitrary amount of row padding * to each plane or to specify that this YUVImage instance is a * subregion of a larger image (in which case, strides[i] should * be set to the plane width of plane i in the larger image.) @@ -150,29 +154,30 @@ public class YUVImage { } /** - * Create a new YUVImage instance from an existing unified image + * Create a new YUVImage instance from an existing unified * buffer. * - * @param yuvImage image buffer that contains or will contain YUV planar - * image data. Use {@link TJ#bufSizeYUV} to determine the minimum size for - * this buffer. The Y, U (Cb), and V (Cr) image planes are stored - * sequentially in the buffer (see {@link YUVImage above} for a description + * @param yuvImage buffer that contains or will receive a unified planar YUV + * 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 * - * @param pad the line padding used in the YUV image buffer. For - * instance, if each line in each plane of the buffer is padded to the - * nearest multiple of 4 bytes, then pad should be set to 4. + * @param align row alignment (in bytes) of the YUV image (must be a power of + * 2.) Setting this parameter to n specifies that each row in each plane of + * the YUV image will be padded to the nearest multiple of n bytes + * (1 = unpadded.) * * @param height height (in pixels) of the YUV image * * @param subsamp the level of chrominance subsampling used in the YUV * image (one of {@link TJ#SAMP_444 TJ.SAMP_*}) */ - public YUVImage(byte[] yuvImage, int width, int pad, int height, + public YUVImage(byte[] yuvImage, int width, int align, int height, int subsamp) { - setBuf(yuvImage, width, pad, height, subsamp); + setBuf(yuvImage, width, align, height, subsamp); } /** @@ -194,12 +199,12 @@ public class YUVImage { * @param width width (in pixels) of the YUV image (or subregion) * * @param strides an array of integers, each specifying the number of bytes - * per line in the corresponding plane of the YUV image. Setting the stride + * per row in the corresponding plane of the YUV image. Setting the stride * for any plane to 0 is the same as setting it to the plane width (see * {@link YUVImage above}.) If strides is null, then the * strides for all planes will be set to their respective plane widths. You - * can adjust the strides in order to add an arbitrary amount of line padding - * to each plane or to specify that this YUVImage image is a + * can adjust the strides in order to add an arbitrary amount of row padding + * to each plane or to specify that this YUVImage instance is a * subregion of a larger image (in which case, strides[i] should * be set to the plane width of plane i in the larger image.) * @@ -263,32 +268,34 @@ public class YUVImage { } /** - * Assign a unified image buffer to this YUVImage instance. + * Assign a unified buffer to this YUVImage instance. * - * @param yuvImage image buffer that contains or will contain YUV planar - * image data. Use {@link TJ#bufSizeYUV} to determine the minimum size for - * this buffer. The Y, U (Cb), and V (Cr) image planes are stored - * sequentially in the buffer (see {@link YUVImage above} for a description + * @param yuvImage buffer that contains or will receive a unified planar YUV + * 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 * - * @param pad the line padding used in the YUV image buffer. For - * instance, if each line in each plane of the buffer is padded to the - * nearest multiple of 4 bytes, then pad should be set to 4. + * @param align row alignment (in bytes) of the YUV image (must be a power of + * 2.) Setting this parameter to n specifies that each row in each plane of + * the YUV image will be padded to the nearest multiple of n bytes + * (1 = unpadded.) * * @param height height (in pixels) of the YUV image * * @param subsamp the level of chrominance subsampling used in the YUV * image (one of {@link TJ#SAMP_444 TJ.SAMP_*}) */ - public void setBuf(byte[] yuvImage, int width, int pad, int height, + public void setBuf(byte[] yuvImage, int width, int align, int height, int subsamp) { - if (yuvImage == null || width < 1 || pad < 1 || ((pad & (pad - 1)) != 0) || - height < 1 || subsamp < 0 || subsamp >= TJ.NUMSAMP) + if (yuvImage == null || width < 1 || align < 1 || + ((align & (align - 1)) != 0) || height < 1 || subsamp < 0 || + subsamp >= TJ.NUMSAMP) throw new IllegalArgumentException("Invalid argument in YUVImage::setBuf()"); - if (yuvImage.length < TJ.bufSizeYUV(width, pad, height, subsamp)) - throw new IllegalArgumentException("YUV image buffer is not large enough"); + if (yuvImage.length < TJ.bufSizeYUV(width, align, height, subsamp)) + throw new IllegalArgumentException("YUV buffer is not large enough"); int nc = (subsamp == TJ.SAMP_GRAY ? 1 : 3); byte[][] planes = new byte[nc][]; @@ -296,9 +303,9 @@ public class YUVImage { int[] offsets = new int[nc]; planes[0] = yuvImage; - strides[0] = pad(TJ.planeWidth(0, width, subsamp), pad); + strides[0] = pad(TJ.planeWidth(0, width, subsamp), align); if (subsamp != TJ.SAMP_GRAY) { - strides[1] = strides[2] = pad(TJ.planeWidth(1, width, subsamp), pad); + strides[1] = strides[2] = pad(TJ.planeWidth(1, width, subsamp), align); planes[1] = planes[2] = yuvImage; offsets[1] = offsets[0] + strides[0] * TJ.planeHeight(0, height, subsamp); @@ -306,7 +313,7 @@ public class YUVImage { strides[1] * TJ.planeHeight(1, height, subsamp); } - yuvPad = pad; + yuvAlign = align; setBuf(planes, offsets, width, strides, height, subsamp); } @@ -333,23 +340,23 @@ public class YUVImage { } /** - * Returns the line padding used in the YUV image buffer (if this image is + * Returns the row alignment (in bytes) of the YUV buffer (if this image is * stored in a unified buffer rather than separate image planes.) * - * @return the line padding used in the YUV image buffer + * @return the row alignment of the YUV buffer */ public int getPad() { if (yuvPlanes == null) throw new IllegalStateException(NO_ASSOC_ERROR); - if (yuvPad < 1 || ((yuvPad & (yuvPad - 1)) != 0)) + if (yuvAlign < 1 || ((yuvAlign & (yuvAlign - 1)) != 0)) throw new IllegalStateException("Image is not stored in a unified buffer"); - return yuvPad; + return yuvAlign; } /** - * Returns the number of bytes per line of each plane in the YUV image. + * Returns the number of bytes per row of each plane in the YUV image. * - * @return the number of bytes per line of each plane in the YUV image + * @return the number of bytes per row of each plane in the YUV image */ public int[] getStrides() { if (yuvStrides == null) @@ -395,10 +402,10 @@ public class YUVImage { } /** - * Returns the YUV image buffer (if this image is stored in a unified - * buffer rather than separate image planes.) + * Returns the YUV buffer (if this image is stored in a unified buffer rather + * than separate image planes.) * - * @return the YUV image buffer + * @return the YUV buffer */ public byte[] getBuf() { if (yuvPlanes == null || yuvSubsamp < 0 || yuvSubsamp >= TJ.NUMSAMP) @@ -412,34 +419,34 @@ public class YUVImage { } /** - * Returns the size (in bytes) of the YUV image buffer (if this image is - * stored in a unified buffer rather than separate image planes.) + * Returns the size (in bytes) of the YUV buffer (if this image is stored in + * a unified buffer rather than separate image planes.) * - * @return the size (in bytes) of the YUV image buffer + * @return the size (in bytes) of the YUV buffer */ public int getSize() { if (yuvPlanes == null || yuvSubsamp < 0 || yuvSubsamp >= TJ.NUMSAMP) throw new IllegalStateException(NO_ASSOC_ERROR); int nc = (yuvSubsamp == TJ.SAMP_GRAY ? 1 : 3); - if (yuvPad < 1) + if (yuvAlign < 1) throw new IllegalStateException("Image is not stored in a unified buffer"); for (int i = 1; i < nc; i++) { if (yuvPlanes[i] != yuvPlanes[0]) throw new IllegalStateException("Image is not stored in a unified buffer"); } - return TJ.bufSizeYUV(yuvWidth, yuvPad, yuvHeight, yuvSubsamp); + return TJ.bufSizeYUV(yuvWidth, yuvAlign, yuvHeight, yuvSubsamp); } private static int pad(int v, int p) { return (v + p - 1) & (~(p - 1)); } - protected long handle = 0; - protected byte[][] yuvPlanes = null; - protected int[] yuvOffsets = null; - protected int[] yuvStrides = null; - protected int yuvPad = 0; - 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 8ec4ecd..e27c3b5 100644 --- a/java/org_libjpegturbo_turbojpeg_TJ.h +++ b/java/org_libjpegturbo_turbojpeg_TJ.h @@ -8,7 +8,7 @@ extern "C" { #endif #undef org_libjpegturbo_turbojpeg_TJ_NUMSAMP -#define org_libjpegturbo_turbojpeg_TJ_NUMSAMP 6L +#define org_libjpegturbo_turbojpeg_TJ_NUMSAMP 7L #undef org_libjpegturbo_turbojpeg_TJ_SAMP_444 #define org_libjpegturbo_turbojpeg_TJ_SAMP_444 0L #undef org_libjpegturbo_turbojpeg_TJ_SAMP_422 @@ -21,6 +21,10 @@ extern "C" { #define org_libjpegturbo_turbojpeg_TJ_SAMP_440 4L #undef org_libjpegturbo_turbojpeg_TJ_SAMP_411 #define org_libjpegturbo_turbojpeg_TJ_SAMP_411 5L +#undef org_libjpegturbo_turbojpeg_TJ_SAMP_441 +#define org_libjpegturbo_turbojpeg_TJ_SAMP_441 6L +#undef org_libjpegturbo_turbojpeg_TJ_SAMP_UNKNOWN +#define org_libjpegturbo_turbojpeg_TJ_SAMP_UNKNOWN -1L #undef org_libjpegturbo_turbojpeg_TJ_NUMPF #define org_libjpegturbo_turbojpeg_TJ_NUMPF 12L #undef org_libjpegturbo_turbojpeg_TJ_PF_RGB @@ -59,16 +63,52 @@ 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_FORCEMMX -#define org_libjpegturbo_turbojpeg_TJ_FLAG_FORCEMMX 8L -#undef org_libjpegturbo_turbojpeg_TJ_FLAG_FORCESSE -#define org_libjpegturbo_turbojpeg_TJ_FLAG_FORCESSE 16L -#undef org_libjpegturbo_turbojpeg_TJ_FLAG_FORCESSE2 -#define org_libjpegturbo_turbojpeg_TJ_FLAG_FORCESSE2 32L -#undef org_libjpegturbo_turbojpeg_TJ_FLAG_FORCESSE3 -#define org_libjpegturbo_turbojpeg_TJ_FLAG_FORCESSE3 128L #undef org_libjpegturbo_turbojpeg_TJ_FLAG_FASTUPSAMPLE #define org_libjpegturbo_turbojpeg_TJ_FLAG_FASTUPSAMPLE 256L #undef org_libjpegturbo_turbojpeg_TJ_FLAG_FASTDCT @@ -105,14 +145,6 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSizeYUV__IIII /* * Class: org_libjpegturbo_turbojpeg_TJ - * Method: bufSizeYUV - * Signature: (III)I - */ -JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSizeYUV__III - (JNIEnv *, jclass, jint, jint, jint); - -/* - * Class: org_libjpegturbo_turbojpeg_TJ * Method: planeSizeYUV * Signature: (IIIII)I */ diff --git a/java/org_libjpegturbo_turbojpeg_TJCompressor.h b/java/org_libjpegturbo_turbojpeg_TJCompressor.h index e76bd0e..24bc521 100644 --- a/java/org_libjpegturbo_turbojpeg_TJCompressor.h +++ b/java/org_libjpegturbo_turbojpeg_TJCompressor.h @@ -9,6 +9,22 @@ 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 * Signature: ()V */ @@ -25,75 +41,67 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_destroy /* * Class: org_libjpegturbo_turbojpeg_TJCompressor - * Method: compress - * Signature: ([BIIII[BIII)I - */ -JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3BIIII_3BIII - (JNIEnv *, jobject, jbyteArray, jint, jint, jint, jint, jbyteArray, jint, jint, jint); - -/* - * 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: ([IIIII[BIII)I + * Method: compress12 + * Signature: ([SIIIIII[B)I */ -JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3IIIII_3BIII - (JNIEnv *, jobject, jintArray, 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: compress - * Signature: ([IIIIIII[BIII)I + * Method: compress16 + * 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_compress16 + (JNIEnv *, jobject, jshortArray, jint, jint, jint, jint, jint, jint, jbyteArray); /* * Class: org_libjpegturbo_turbojpeg_TJCompressor - * Method: compressFromYUV - * Signature: ([[B[II[III[BII)I + * Method: compress8 + * Signature: ([IIIIIII[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_compress8___3IIIIIII_3B + (JNIEnv *, jobject, jintArray, jint, jint, jint, jint, jint, jint, jbyteArray); /* * Class: org_libjpegturbo_turbojpeg_TJCompressor - * Method: encodeYUV - * Signature: ([BIIII[BII)V + * Method: compressFromYUV8 + * Signature: ([[B[II[II[B)I */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3BIIII_3BII - (JNIEnv *, jobject, jbyteArray, jint, jint, jint, jint, jbyteArray, 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: encodeYUV - * Signature: ([BIIIIII[[B[I[III)V + * Method: encodeYUV8 + * Signature: ([BIIIIII[[B[I[I)V */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3BIIIIII_3_3B_3I_3III - (JNIEnv *, jobject, jbyteArray, jint, jint, jint, jint, jint, jint, jobjectArray, jintArray, jintArray, jint, jint); +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV8___3BIIIIII_3_3B_3I_3I + (JNIEnv *, jobject, jbyteArray, jint, jint, jint, jint, jint, jint, jobjectArray, jintArray, jintArray); /* * Class: org_libjpegturbo_turbojpeg_TJCompressor - * Method: encodeYUV - * Signature: ([IIIII[BII)V + * Method: encodeYUV8 + * Signature: ([IIIIIII[[B[I[I)V */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3IIIII_3BII - (JNIEnv *, jobject, jintArray, jint, jint, jint, jint, jbyteArray, jint, jint); +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV8___3IIIIIII_3_3B_3I_3I + (JNIEnv *, jobject, jintArray, jint, jint, jint, jint, jint, jint, jobjectArray, jintArray, jintArray); /* * Class: org_libjpegturbo_turbojpeg_TJCompressor - * Method: encodeYUV - * Signature: ([IIIIIII[[B[I[III)V + * Method: loadImage + * Signature: (ILjava/lang/String;[II[I[I)Ljava/lang/Object; */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3IIIIIII_3_3B_3I_3III - (JNIEnv *, jobject, jintArray, jint, jint, jint, jint, jint, jint, jobjectArray, jintArray, jintArray, jint, jint); +JNIEXPORT jobject JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_loadImage + (JNIEnv *, jobject, jint, jstring, jintArray, jint, jintArray, jintArray); #ifdef __cplusplus } diff --git a/java/org_libjpegturbo_turbojpeg_TJDecompressor.h b/java/org_libjpegturbo_turbojpeg_TJDecompressor.h index 2d58e73..621ad5f 100644 --- a/java/org_libjpegturbo_turbojpeg_TJDecompressor.h +++ b/java/org_libjpegturbo_turbojpeg_TJDecompressor.h @@ -9,6 +9,22 @@ 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 * Signature: ()V */ @@ -33,67 +49,75 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress /* * Class: org_libjpegturbo_turbojpeg_TJDecompressor - * Method: decompress - * Signature: ([BI[BIIIII)V + * Method: setCroppingRegion + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_setCroppingRegion + (JNIEnv *, jobject); + +/* + * Class: org_libjpegturbo_turbojpeg_TJDecompressor + * Method: decompress8 + * Signature: ([BI[BIIII)V */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress___3BI_3BIIIII - (JNIEnv *, jobject, jbyteArray, jint, jbyteArray, 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: decompress - * Signature: ([BI[BIIIIIII)V + * Method: decompress12 + * Signature: ([BI[SIIII)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_decompress12 + (JNIEnv *, jobject, jbyteArray, jint, jshortArray, jint, jint, jint, jint); /* * Class: org_libjpegturbo_turbojpeg_TJDecompressor - * Method: decompress - * Signature: ([BI[IIIIII)V + * Method: decompress16 + * Signature: ([BI[SIIII)V */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress___3BI_3IIIIII - (JNIEnv *, jobject, jbyteArray, jint, jintArray, 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: decompress - * Signature: ([BI[IIIIIIII)V + * Method: decompress8 + * Signature: ([BI[IIIII)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_3IIIII + (JNIEnv *, jobject, jbyteArray, jint, jintArray, jint, jint, jint, jint); /* * Class: org_libjpegturbo_turbojpeg_TJDecompressor - * Method: decompressToYUV - * Signature: ([BI[BI)V + * Method: decompressToYUV8 + * Signature: ([BI[[B[I[I)V */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressToYUV___3BI_3BI - (JNIEnv *, jobject, jbyteArray, jint, jbyteArray, jint); +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressToYUV8 + (JNIEnv *, jobject, jbyteArray, jint, jobjectArray, jintArray, jintArray); /* * Class: org_libjpegturbo_turbojpeg_TJDecompressor - * Method: decompressToYUV - * Signature: ([BI[[B[II[III)V + * Method: decodeYUV8 + * Signature: ([[B[I[I[BIIIIII)V */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressToYUV___3BI_3_3B_3II_3III - (JNIEnv *, jobject, jbyteArray, jint, jobjectArray, jintArray, jint, jintArray, jint, jint); +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV8___3_3B_3I_3I_3BIIIIII + (JNIEnv *, jobject, jobjectArray, jintArray, jintArray, jbyteArray, jint, jint, jint, jint, jint, jint); /* * Class: org_libjpegturbo_turbojpeg_TJDecompressor - * Method: decodeYUV - * Signature: ([[B[I[II[BIIIIIII)V + * Method: decodeYUV8 + * Signature: ([[B[I[I[IIIIIII)V */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV___3_3B_3I_3II_3BIIIIIII - (JNIEnv *, jobject, jobjectArray, jintArray, jintArray, jint, jbyteArray, jint, jint, jint, jint, jint, jint, jint); +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV8___3_3B_3I_3I_3IIIIIII + (JNIEnv *, jobject, jobjectArray, jintArray, jintArray, jintArray, jint, jint, jint, jint, jint, jint); /* * Class: org_libjpegturbo_turbojpeg_TJDecompressor - * Method: decodeYUV - * Signature: ([[B[I[II[IIIIIIII)V + * Method: saveImage + * Signature: (ILjava/lang/String;Ljava/lang/Object;IIII)V */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV___3_3B_3I_3II_3IIIIIIII - (JNIEnv *, jobject, jobjectArray, jintArray, jintArray, jint, jintArray, jint, jint, jint, jint, jint, jint, jint); +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_saveImage + (JNIEnv *, jobject, jint, jstring, jobject, jint, jint, jint, jint); #ifdef __cplusplus } diff --git a/java/org_libjpegturbo_turbojpeg_TJTransformer.h b/java/org_libjpegturbo_turbojpeg_TJTransformer.h index a9dad4d..5d86033 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 84e7ecc..cbb3d13 100644 --- a/jcapimin.c +++ b/jcapimin.c @@ -23,6 +23,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" +#include "jcmaster.h" /* @@ -90,8 +91,18 @@ jpeg_CreateCompress(j_compress_ptr cinfo, int version, size_t structsize) cinfo->input_gamma = 1.0; /* in case application forgets */ + cinfo->data_precision = BITS_IN_JSAMPLE; + /* OK, I'm ready */ cinfo->global_state = CSTATE_START; + + /* The master struct is used to store extension parameters, so we allocate it + * here. + */ + cinfo->master = (struct jpeg_comp_master *) + (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_PERMANENT, + sizeof(my_comp_master)); + memset(cinfo->master, 0, sizeof(my_comp_master)); } @@ -183,8 +194,20 @@ jpeg_finish_compress(j_compress_ptr cinfo) /* We bypass the main controller and invoke coef controller directly; * all work is being done from the coefficient buffer. */ - if (!(*cinfo->coef->compress_data) (cinfo, (JSAMPIMAGE)NULL)) - ERREXIT(cinfo, JERR_CANT_SUSPEND); + if (cinfo->data_precision == 16) { +#ifdef C_LOSSLESS_SUPPORTED + if (!(*cinfo->coef->compress_data_16) (cinfo, (J16SAMPIMAGE)NULL)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); +#else + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); +#endif + } else if (cinfo->data_precision == 12) { + if (!(*cinfo->coef->compress_data_12) (cinfo, (J12SAMPIMAGE)NULL)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + } else { + if (!(*cinfo->coef->compress_data) (cinfo, (JSAMPIMAGE)NULL)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + } } (*cinfo->master->finish_pass) (cinfo); } diff --git a/jcapistd.c b/jcapistd.c index aa2aad9..2053028 100644 --- a/jcapistd.c +++ b/jcapistd.c @@ -1,8 +1,10 @@ /* * jcapistd.c * + * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -18,8 +20,11 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" +#include "jsamplecomp.h" +#if BITS_IN_JSAMPLE == 8 + /* * Compression initialization. * Before calling this, all parameters and a data destination must be set up. @@ -51,13 +56,15 @@ jpeg_start_compress(j_compress_ptr cinfo, boolean write_all_tables) jinit_compress_master(cinfo); /* Set up for the first pass */ (*cinfo->master->prepare_for_pass) (cinfo); - /* Ready for application to drive first pass through jpeg_write_scanlines - * or jpeg_write_raw_data. + /* Ready for application to drive first pass through _jpeg_write_scanlines + * or _jpeg_write_raw_data. */ cinfo->next_scanline = 0; cinfo->global_state = (cinfo->raw_data_in ? CSTATE_RAW_OK : CSTATE_SCANNING); } +#endif + /* * Write some scanlines of data to the JPEG compressor. @@ -67,7 +74,7 @@ jpeg_start_compress(j_compress_ptr cinfo, boolean write_all_tables) * the data destination module has requested suspension of the compressor, * or if more than image_height scanlines are passed in. * - * Note: we warn about excess calls to jpeg_write_scanlines() since + * Note: we warn about excess calls to _jpeg_write_scanlines() since * this likely signals an application programmer error. However, * excess scanlines passed in the last valid call are *silently* ignored, * so that the application need not adjust num_lines for end-of-image @@ -75,11 +82,15 @@ jpeg_start_compress(j_compress_ptr cinfo, boolean write_all_tables) */ GLOBAL(JDIMENSION) -jpeg_write_scanlines(j_compress_ptr cinfo, JSAMPARRAY scanlines, - JDIMENSION num_lines) +_jpeg_write_scanlines(j_compress_ptr cinfo, _JSAMPARRAY scanlines, + JDIMENSION num_lines) { +#if BITS_IN_JSAMPLE != 16 || defined(C_LOSSLESS_SUPPORTED) JDIMENSION row_ctr, rows_left; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + if (cinfo->global_state != CSTATE_SCANNING) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); if (cinfo->next_scanline >= cinfo->image_height) @@ -93,9 +104,9 @@ jpeg_write_scanlines(j_compress_ptr cinfo, JSAMPARRAY scanlines, } /* Give master control module another chance if this is first call to - * jpeg_write_scanlines. This lets output of the frame/scan headers be + * _jpeg_write_scanlines. This lets output of the frame/scan headers be * delayed so that application can write COM, etc, markers between - * jpeg_start_compress and jpeg_write_scanlines. + * jpeg_start_compress and _jpeg_write_scanlines. */ if (cinfo->master->call_pass_startup) (*cinfo->master->pass_startup) (cinfo); @@ -106,23 +117,35 @@ jpeg_write_scanlines(j_compress_ptr cinfo, JSAMPARRAY scanlines, num_lines = rows_left; row_ctr = 0; - (*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, num_lines); + (*cinfo->main->_process_data) (cinfo, scanlines, &row_ctr, num_lines); cinfo->next_scanline += row_ctr; return row_ctr; +#else + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + return 0; +#endif } +#if BITS_IN_JSAMPLE != 16 + /* * Alternate entry point to write raw data. * Processes exactly one iMCU row per call, unless suspended. */ GLOBAL(JDIMENSION) -jpeg_write_raw_data(j_compress_ptr cinfo, JSAMPIMAGE data, - JDIMENSION num_lines) +_jpeg_write_raw_data(j_compress_ptr cinfo, _JSAMPIMAGE data, + JDIMENSION num_lines) { JDIMENSION lines_per_iMCU_row; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); + if (cinfo->global_state != CSTATE_RAW_OK) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); if (cinfo->next_scanline >= cinfo->image_height) { @@ -138,9 +161,9 @@ jpeg_write_raw_data(j_compress_ptr cinfo, JSAMPIMAGE data, } /* Give master control module another chance if this is first call to - * jpeg_write_raw_data. This lets output of the frame/scan headers be + * _jpeg_write_raw_data. This lets output of the frame/scan headers be * delayed so that application can write COM, etc, markers between - * jpeg_start_compress and jpeg_write_raw_data. + * jpeg_start_compress and _jpeg_write_raw_data. */ if (cinfo->master->call_pass_startup) (*cinfo->master->pass_startup) (cinfo); @@ -151,7 +174,7 @@ jpeg_write_raw_data(j_compress_ptr cinfo, JSAMPIMAGE data, ERREXIT(cinfo, JERR_BUFFER_SIZE); /* Directly compress the row. */ - if (!(*cinfo->coef->compress_data) (cinfo, data)) { + if (!(*cinfo->coef->_compress_data) (cinfo, data)) { /* If compressor did not consume the whole row, suspend processing. */ return 0; } @@ -160,3 +183,5 @@ jpeg_write_raw_data(j_compress_ptr cinfo, JSAMPIMAGE data, cinfo->next_scanline += lines_per_iMCU_row; return lines_per_iMCU_row; } + +#endif /* BITS_IN_JSAMPLE != 16 */ diff --git a/jccoefct.c b/jccoefct.c index 068232a..2a5dde2 100644 --- a/jccoefct.c +++ b/jccoefct.c @@ -3,19 +3,20 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1997, Thomas G. Lane. - * It was modified by The libjpeg-turbo Project to include only code and - * information relevant to libjpeg-turbo. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * * This file contains the coefficient buffer controller for compression. - * This controller is the top level of the JPEG compressor proper. + * This controller is the top level of the lossy JPEG compressor proper. * The coefficient buffer lies between forward-DCT and entropy encoding steps. */ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" +#include "jsamplecomp.h" /* We use a full-image coefficient buffer when doing Huffman optimization, @@ -58,11 +59,12 @@ typedef my_coef_controller *my_coef_ptr; /* Forward declarations */ -METHODDEF(boolean) compress_data(j_compress_ptr cinfo, JSAMPIMAGE input_buf); +METHODDEF(boolean) compress_data(j_compress_ptr cinfo, _JSAMPIMAGE input_buf); #ifdef FULL_COEF_BUFFER_SUPPORTED METHODDEF(boolean) compress_first_pass(j_compress_ptr cinfo, - JSAMPIMAGE input_buf); -METHODDEF(boolean) compress_output(j_compress_ptr cinfo, JSAMPIMAGE input_buf); + _JSAMPIMAGE input_buf); +METHODDEF(boolean) compress_output(j_compress_ptr cinfo, + _JSAMPIMAGE input_buf); #endif @@ -106,18 +108,18 @@ start_pass_coef(j_compress_ptr cinfo, J_BUF_MODE pass_mode) case JBUF_PASS_THRU: if (coef->whole_image[0] != NULL) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - coef->pub.compress_data = compress_data; + coef->pub._compress_data = compress_data; break; #ifdef FULL_COEF_BUFFER_SUPPORTED case JBUF_SAVE_AND_PASS: if (coef->whole_image[0] == NULL) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - coef->pub.compress_data = compress_first_pass; + coef->pub._compress_data = compress_first_pass; break; case JBUF_CRANK_DEST: if (coef->whole_image[0] == NULL) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - coef->pub.compress_data = compress_output; + coef->pub._compress_data = compress_output; break; #endif default: @@ -138,7 +140,7 @@ start_pass_coef(j_compress_ptr cinfo, J_BUF_MODE pass_mode) */ METHODDEF(boolean) -compress_data(j_compress_ptr cinfo, JSAMPIMAGE input_buf) +compress_data(j_compress_ptr cinfo, _JSAMPIMAGE input_buf) { my_coef_ptr coef = (my_coef_ptr)cinfo->coef; JDIMENSION MCU_col_num; /* index of current MCU within row */ @@ -172,10 +174,10 @@ compress_data(j_compress_ptr cinfo, JSAMPIMAGE input_buf) for (yindex = 0; yindex < compptr->MCU_height; yindex++) { if (coef->iMCU_row_num < last_iMCU_row || yoffset + yindex < compptr->last_row_height) { - (*cinfo->fdct->forward_DCT) (cinfo, compptr, - input_buf[compptr->component_index], - coef->MCU_buffer[blkn], - ypos, xpos, (JDIMENSION)blockcnt); + (*cinfo->fdct->_forward_DCT) (cinfo, compptr, + input_buf[compptr->component_index], + coef->MCU_buffer[blkn], + ypos, xpos, (JDIMENSION)blockcnt); if (blockcnt < compptr->MCU_width) { /* Create some dummy blocks at the right edge of the image. */ jzero_far((void *)coef->MCU_buffer[blkn + blockcnt], @@ -242,7 +244,7 @@ compress_data(j_compress_ptr cinfo, JSAMPIMAGE input_buf) */ METHODDEF(boolean) -compress_first_pass(j_compress_ptr cinfo, JSAMPIMAGE input_buf) +compress_first_pass(j_compress_ptr cinfo, _JSAMPIMAGE input_buf) { my_coef_ptr coef = (my_coef_ptr)cinfo->coef; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; @@ -279,10 +281,10 @@ compress_first_pass(j_compress_ptr cinfo, JSAMPIMAGE input_buf) */ for (block_row = 0; block_row < block_rows; block_row++) { thisblockrow = buffer[block_row]; - (*cinfo->fdct->forward_DCT) (cinfo, compptr, - input_buf[ci], thisblockrow, - (JDIMENSION)(block_row * DCTSIZE), - (JDIMENSION)0, blocks_across); + (*cinfo->fdct->_forward_DCT) (cinfo, compptr, + input_buf[ci], thisblockrow, + (JDIMENSION)(block_row * DCTSIZE), + (JDIMENSION)0, blocks_across); if (ndummy > 0) { /* Create dummy blocks at the right edge of the image. */ thisblockrow += blocks_across; /* => first dummy block */ @@ -338,7 +340,7 @@ compress_first_pass(j_compress_ptr cinfo, JSAMPIMAGE input_buf) */ METHODDEF(boolean) -compress_output(j_compress_ptr cinfo, JSAMPIMAGE input_buf) +compress_output(j_compress_ptr cinfo, _JSAMPIMAGE input_buf) { my_coef_ptr coef = (my_coef_ptr)cinfo->coef; JDIMENSION MCU_col_num; /* index of current MCU within row */ @@ -402,10 +404,13 @@ compress_output(j_compress_ptr cinfo, JSAMPIMAGE input_buf) */ GLOBAL(void) -jinit_c_coef_controller(j_compress_ptr cinfo, boolean need_full_buffer) +_jinit_c_coef_controller(j_compress_ptr cinfo, boolean need_full_buffer) { my_coef_ptr coef; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + coef = (my_coef_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, sizeof(my_coef_controller)); diff --git a/jccolext.c b/jccolext.c index 303b322..8eba36c 100644 --- a/jccolext.c +++ b/jccolext.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2009-2012, 2015, D. R. Commander. + * Copyright (C) 2009-2012, 2015, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -29,15 +29,16 @@ INLINE LOCAL(void) -rgb_ycc_convert_internal(j_compress_ptr cinfo, JSAMPARRAY input_buf, - JSAMPIMAGE output_buf, JDIMENSION output_row, +rgb_ycc_convert_internal(j_compress_ptr cinfo, _JSAMPARRAY input_buf, + _JSAMPIMAGE output_buf, JDIMENSION output_row, int num_rows) { +#if BITS_IN_JSAMPLE != 16 my_cconvert_ptr cconvert = (my_cconvert_ptr)cinfo->cconvert; register int r, g, b; register JLONG *ctab = cconvert->rgb_ycc_tab; - register JSAMPROW inptr; - register JSAMPROW outptr0, outptr1, outptr2; + register _JSAMPROW inptr; + register _JSAMPROW outptr0, outptr1, outptr2; register JDIMENSION col; JDIMENSION num_cols = cinfo->image_width; @@ -48,26 +49,29 @@ rgb_ycc_convert_internal(j_compress_ptr cinfo, JSAMPARRAY input_buf, outptr2 = output_buf[2][output_row]; output_row++; for (col = 0; col < num_cols; col++) { - r = inptr[RGB_RED]; - g = inptr[RGB_GREEN]; - b = inptr[RGB_BLUE]; + r = RANGE_LIMIT(inptr[RGB_RED]); + g = RANGE_LIMIT(inptr[RGB_GREEN]); + b = RANGE_LIMIT(inptr[RGB_BLUE]); inptr += RGB_PIXELSIZE; - /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations + /* If the inputs are 0.._MAXJSAMPLE, the outputs of these equations * must be too; we do not need an explicit range-limiting operation. * Hence the value being shifted is never negative, and we don't * need the general RIGHT_SHIFT macro. */ /* Y */ - outptr0[col] = (JSAMPLE)((ctab[r + R_Y_OFF] + ctab[g + G_Y_OFF] + - ctab[b + B_Y_OFF]) >> SCALEBITS); + outptr0[col] = (_JSAMPLE)((ctab[r + R_Y_OFF] + ctab[g + G_Y_OFF] + + ctab[b + B_Y_OFF]) >> SCALEBITS); /* Cb */ - outptr1[col] = (JSAMPLE)((ctab[r + R_CB_OFF] + ctab[g + G_CB_OFF] + - ctab[b + B_CB_OFF]) >> SCALEBITS); + outptr1[col] = (_JSAMPLE)((ctab[r + R_CB_OFF] + ctab[g + G_CB_OFF] + + ctab[b + B_CB_OFF]) >> SCALEBITS); /* Cr */ - outptr2[col] = (JSAMPLE)((ctab[r + R_CR_OFF] + ctab[g + G_CR_OFF] + - ctab[b + B_CR_OFF]) >> SCALEBITS); + outptr2[col] = (_JSAMPLE)((ctab[r + R_CR_OFF] + ctab[g + G_CR_OFF] + + ctab[b + B_CR_OFF]) >> SCALEBITS); } } +#else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); +#endif } @@ -83,15 +87,16 @@ rgb_ycc_convert_internal(j_compress_ptr cinfo, JSAMPARRAY input_buf, INLINE LOCAL(void) -rgb_gray_convert_internal(j_compress_ptr cinfo, JSAMPARRAY input_buf, - JSAMPIMAGE output_buf, JDIMENSION output_row, +rgb_gray_convert_internal(j_compress_ptr cinfo, _JSAMPARRAY input_buf, + _JSAMPIMAGE output_buf, JDIMENSION output_row, int num_rows) { +#if BITS_IN_JSAMPLE != 16 my_cconvert_ptr cconvert = (my_cconvert_ptr)cinfo->cconvert; register int r, g, b; register JLONG *ctab = cconvert->rgb_ycc_tab; - register JSAMPROW inptr; - register JSAMPROW outptr; + register _JSAMPROW inptr; + register _JSAMPROW outptr; register JDIMENSION col; JDIMENSION num_cols = cinfo->image_width; @@ -100,15 +105,18 @@ rgb_gray_convert_internal(j_compress_ptr cinfo, JSAMPARRAY input_buf, outptr = output_buf[0][output_row]; output_row++; for (col = 0; col < num_cols; col++) { - r = inptr[RGB_RED]; - g = inptr[RGB_GREEN]; - b = inptr[RGB_BLUE]; + r = RANGE_LIMIT(inptr[RGB_RED]); + g = RANGE_LIMIT(inptr[RGB_GREEN]); + b = RANGE_LIMIT(inptr[RGB_BLUE]); inptr += RGB_PIXELSIZE; /* Y */ - outptr[col] = (JSAMPLE)((ctab[r + R_Y_OFF] + ctab[g + G_Y_OFF] + - ctab[b + B_Y_OFF]) >> SCALEBITS); + outptr[col] = (_JSAMPLE)((ctab[r + R_Y_OFF] + ctab[g + G_Y_OFF] + + ctab[b + B_Y_OFF]) >> SCALEBITS); } } +#else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); +#endif } @@ -119,12 +127,12 @@ rgb_gray_convert_internal(j_compress_ptr cinfo, JSAMPARRAY input_buf, INLINE LOCAL(void) -rgb_rgb_convert_internal(j_compress_ptr cinfo, JSAMPARRAY input_buf, - JSAMPIMAGE output_buf, JDIMENSION output_row, +rgb_rgb_convert_internal(j_compress_ptr cinfo, _JSAMPARRAY input_buf, + _JSAMPIMAGE output_buf, JDIMENSION output_row, int num_rows) { - register JSAMPROW inptr; - register JSAMPROW outptr0, outptr1, outptr2; + register _JSAMPROW inptr; + register _JSAMPROW outptr0, outptr1, outptr2; register JDIMENSION col; JDIMENSION num_cols = cinfo->image_width; diff --git a/jccolor.c b/jccolor.c index bdc563c..cd3a6a7 100644 --- a/jccolor.c +++ b/jccolor.c @@ -5,7 +5,7 @@ * Copyright (C) 1991-1996, Thomas G. Lane. * libjpeg-turbo Modifications: * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2009-2012, 2015, D. R. Commander. + * Copyright (C) 2009-2012, 2015, 2022, D. R. Commander. * Copyright (C) 2014, MIPS Technologies, Inc., California. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -17,16 +17,20 @@ #include "jinclude.h" #include "jpeglib.h" #include "jsimd.h" -#include "jconfigint.h" +#include "jsamplecomp.h" +#if BITS_IN_JSAMPLE != 16 || defined(C_LOSSLESS_SUPPORTED) + /* Private subobject */ typedef struct { struct jpeg_color_converter pub; /* public fields */ +#if BITS_IN_JSAMPLE != 16 /* Private state for RGB->YCC conversion */ JLONG *rgb_ycc_tab; /* => table for RGB to YCbCr conversion */ +#endif } my_color_converter; typedef my_color_converter *my_cconvert_ptr; @@ -36,14 +40,14 @@ typedef my_color_converter *my_cconvert_ptr; /* * YCbCr is defined per CCIR 601-1, except that Cb and Cr are - * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. + * normalized to the range 0.._MAXJSAMPLE rather than -0.5 .. 0.5. * The conversion equations to be implemented are therefore * Y = 0.29900 * R + 0.58700 * G + 0.11400 * B - * Cb = -0.16874 * R - 0.33126 * G + 0.50000 * B + CENTERJSAMPLE - * Cr = 0.50000 * R - 0.41869 * G - 0.08131 * B + CENTERJSAMPLE + * Cb = -0.16874 * R - 0.33126 * G + 0.50000 * B + _CENTERJSAMPLE + * Cr = 0.50000 * R - 0.41869 * G - 0.08131 * B + _CENTERJSAMPLE * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) - * Note: older versions of the IJG code used a zero offset of MAXJSAMPLE/2, - * rather than CENTERJSAMPLE, for Cb and Cr. This gave equal positive and + * Note: older versions of the IJG code used a zero offset of _MAXJSAMPLE/2, + * rather than _CENTERJSAMPLE, for Cb and Cr. This gave equal positive and * negative swings for Cb/Cr, but meant that grayscale values (Cb=Cr=0) * were not represented exactly. Now we sacrifice exact representation of * maximum red and maximum blue in order to get exact grayscales. @@ -54,16 +58,16 @@ typedef my_color_converter *my_cconvert_ptr; * * For even more speed, we avoid doing any multiplications in the inner loop * by precalculating the constants times R,G,B for all possible values. - * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); + * For 8-bit samples this is very reasonable (only 256 entries per table); * for 12-bit samples it is still acceptable. It's not very reasonable for * 16-bit samples, but if you want lossless storage you shouldn't be changing * colorspace anyway. - * The CENTERJSAMPLE offsets and the rounding fudge-factor of 0.5 are included + * The _CENTERJSAMPLE offsets and the rounding fudge-factor of 0.5 are included * in the tables to save adding them separately in the inner loop. */ #define SCALEBITS 16 /* speediest right-shift on some machines */ -#define CBCR_OFFSET ((JLONG)CENTERJSAMPLE << SCALEBITS) +#define CBCR_OFFSET ((JLONG)_CENTERJSAMPLE << SCALEBITS) #define ONE_HALF ((JLONG)1 << (SCALEBITS - 1)) #define FIX(x) ((JLONG)((x) * (1L << SCALEBITS) + 0.5)) @@ -74,15 +78,27 @@ typedef my_color_converter *my_cconvert_ptr; */ #define R_Y_OFF 0 /* offset to R => Y section */ -#define G_Y_OFF (1 * (MAXJSAMPLE + 1)) /* offset to G => Y section */ -#define B_Y_OFF (2 * (MAXJSAMPLE + 1)) /* etc. */ -#define R_CB_OFF (3 * (MAXJSAMPLE + 1)) -#define G_CB_OFF (4 * (MAXJSAMPLE + 1)) -#define B_CB_OFF (5 * (MAXJSAMPLE + 1)) +#define G_Y_OFF (1 * (_MAXJSAMPLE + 1)) /* offset to G => Y section */ +#define B_Y_OFF (2 * (_MAXJSAMPLE + 1)) /* etc. */ +#define R_CB_OFF (3 * (_MAXJSAMPLE + 1)) +#define G_CB_OFF (4 * (_MAXJSAMPLE + 1)) +#define B_CB_OFF (5 * (_MAXJSAMPLE + 1)) #define R_CR_OFF B_CB_OFF /* B=>Cb, R=>Cr are the same */ -#define G_CR_OFF (6 * (MAXJSAMPLE + 1)) -#define B_CR_OFF (7 * (MAXJSAMPLE + 1)) -#define TABLE_SIZE (8 * (MAXJSAMPLE + 1)) +#define G_CR_OFF (6 * (_MAXJSAMPLE + 1)) +#define B_CR_OFF (7 * (_MAXJSAMPLE + 1)) +#define TABLE_SIZE (8 * (_MAXJSAMPLE + 1)) + +/* 12-bit samples use a 16-bit data type, so it is possible to pass + * out-of-range sample values (< 0 or > 4095) to jpeg_write_scanlines(). + * Thus, we mask the incoming 12-bit samples to guard against overrunning + * or underrunning the conversion tables. + */ + +#if BITS_IN_JSAMPLE == 12 +#define RANGE_LIMIT(value) ((value) & 0xFFF) +#else +#define RANGE_LIMIT(value) (value) +#endif /* Include inline routines for colorspace extensions */ @@ -197,6 +213,7 @@ typedef my_color_converter *my_cconvert_ptr; METHODDEF(void) rgb_ycc_start(j_compress_ptr cinfo) { +#if BITS_IN_JSAMPLE != 16 my_cconvert_ptr cconvert = (my_cconvert_ptr)cinfo->cconvert; JLONG *rgb_ycc_tab; JLONG i; @@ -206,15 +223,15 @@ rgb_ycc_start(j_compress_ptr cinfo) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, (TABLE_SIZE * sizeof(JLONG))); - for (i = 0; i <= MAXJSAMPLE; i++) { + for (i = 0; i <= _MAXJSAMPLE; i++) { rgb_ycc_tab[i + R_Y_OFF] = FIX(0.29900) * i; rgb_ycc_tab[i + G_Y_OFF] = FIX(0.58700) * i; rgb_ycc_tab[i + B_Y_OFF] = FIX(0.11400) * i + ONE_HALF; rgb_ycc_tab[i + R_CB_OFF] = (-FIX(0.16874)) * i; rgb_ycc_tab[i + G_CB_OFF] = (-FIX(0.33126)) * i; /* We use a rounding fudge-factor of 0.5-epsilon for Cb and Cr. - * This ensures that the maximum output will round to MAXJSAMPLE - * not MAXJSAMPLE+1, and thus that we don't have to range-limit. + * This ensures that the maximum output will round to _MAXJSAMPLE + * not _MAXJSAMPLE+1, and thus that we don't have to range-limit. */ rgb_ycc_tab[i + B_CB_OFF] = FIX(0.50000) * i + CBCR_OFFSET + ONE_HALF - 1; /* B=>Cb and R=>Cr tables are the same @@ -223,6 +240,9 @@ rgb_ycc_start(j_compress_ptr cinfo) rgb_ycc_tab[i + G_CR_OFF] = (-FIX(0.41869)) * i; rgb_ycc_tab[i + B_CR_OFF] = (-FIX(0.08131)) * i; } +#else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); +#endif } @@ -231,8 +251,8 @@ rgb_ycc_start(j_compress_ptr cinfo) */ METHODDEF(void) -rgb_ycc_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, - JSAMPIMAGE output_buf, JDIMENSION output_row, int num_rows) +rgb_ycc_convert(j_compress_ptr cinfo, _JSAMPARRAY input_buf, + _JSAMPIMAGE output_buf, JDIMENSION output_row, int num_rows) { switch (cinfo->in_color_space) { case JCS_EXT_RGB: @@ -279,8 +299,8 @@ rgb_ycc_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, */ METHODDEF(void) -rgb_gray_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, - JSAMPIMAGE output_buf, JDIMENSION output_row, int num_rows) +rgb_gray_convert(j_compress_ptr cinfo, _JSAMPARRAY input_buf, + _JSAMPIMAGE output_buf, JDIMENSION output_row, int num_rows) { switch (cinfo->in_color_space) { case JCS_EXT_RGB: @@ -324,8 +344,8 @@ rgb_gray_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, */ METHODDEF(void) -rgb_rgb_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, - JSAMPIMAGE output_buf, JDIMENSION output_row, int num_rows) +rgb_rgb_convert(j_compress_ptr cinfo, _JSAMPARRAY input_buf, + _JSAMPIMAGE output_buf, JDIMENSION output_row, int num_rows) { switch (cinfo->in_color_space) { case JCS_EXT_RGB: @@ -373,14 +393,15 @@ rgb_rgb_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, */ METHODDEF(void) -cmyk_ycck_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, - JSAMPIMAGE output_buf, JDIMENSION output_row, int num_rows) +cmyk_ycck_convert(j_compress_ptr cinfo, _JSAMPARRAY input_buf, + _JSAMPIMAGE output_buf, JDIMENSION output_row, int num_rows) { +#if BITS_IN_JSAMPLE != 16 my_cconvert_ptr cconvert = (my_cconvert_ptr)cinfo->cconvert; register int r, g, b; register JLONG *ctab = cconvert->rgb_ycc_tab; - register JSAMPROW inptr; - register JSAMPROW outptr0, outptr1, outptr2, outptr3; + register _JSAMPROW inptr; + register _JSAMPROW outptr0, outptr1, outptr2, outptr3; register JDIMENSION col; JDIMENSION num_cols = cinfo->image_width; @@ -392,28 +413,31 @@ cmyk_ycck_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, outptr3 = output_buf[3][output_row]; output_row++; for (col = 0; col < num_cols; col++) { - r = MAXJSAMPLE - inptr[0]; - g = MAXJSAMPLE - inptr[1]; - b = MAXJSAMPLE - inptr[2]; + r = _MAXJSAMPLE - RANGE_LIMIT(inptr[0]); + g = _MAXJSAMPLE - RANGE_LIMIT(inptr[1]); + b = _MAXJSAMPLE - RANGE_LIMIT(inptr[2]); /* K passes through as-is */ outptr3[col] = inptr[3]; inptr += 4; - /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations + /* If the inputs are 0.._MAXJSAMPLE, the outputs of these equations * must be too; we do not need an explicit range-limiting operation. * Hence the value being shifted is never negative, and we don't * need the general RIGHT_SHIFT macro. */ /* Y */ - outptr0[col] = (JSAMPLE)((ctab[r + R_Y_OFF] + ctab[g + G_Y_OFF] + - ctab[b + B_Y_OFF]) >> SCALEBITS); + outptr0[col] = (_JSAMPLE)((ctab[r + R_Y_OFF] + ctab[g + G_Y_OFF] + + ctab[b + B_Y_OFF]) >> SCALEBITS); /* Cb */ - outptr1[col] = (JSAMPLE)((ctab[r + R_CB_OFF] + ctab[g + G_CB_OFF] + - ctab[b + B_CB_OFF]) >> SCALEBITS); + outptr1[col] = (_JSAMPLE)((ctab[r + R_CB_OFF] + ctab[g + G_CB_OFF] + + ctab[b + B_CB_OFF]) >> SCALEBITS); /* Cr */ - outptr2[col] = (JSAMPLE)((ctab[r + R_CR_OFF] + ctab[g + G_CR_OFF] + - ctab[b + B_CR_OFF]) >> SCALEBITS); + outptr2[col] = (_JSAMPLE)((ctab[r + R_CR_OFF] + ctab[g + G_CR_OFF] + + ctab[b + B_CR_OFF]) >> SCALEBITS); } } +#else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); +#endif } @@ -424,11 +448,11 @@ cmyk_ycck_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, */ METHODDEF(void) -grayscale_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, - JSAMPIMAGE output_buf, JDIMENSION output_row, int num_rows) +grayscale_convert(j_compress_ptr cinfo, _JSAMPARRAY input_buf, + _JSAMPIMAGE output_buf, JDIMENSION output_row, int num_rows) { - register JSAMPROW inptr; - register JSAMPROW outptr; + register _JSAMPROW inptr; + register _JSAMPROW outptr; register JDIMENSION col; JDIMENSION num_cols = cinfo->image_width; int instride = cinfo->input_components; @@ -452,11 +476,11 @@ grayscale_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, */ METHODDEF(void) -null_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, JSAMPIMAGE output_buf, - JDIMENSION output_row, int num_rows) +null_convert(j_compress_ptr cinfo, _JSAMPARRAY input_buf, + _JSAMPIMAGE output_buf, JDIMENSION output_row, int num_rows) { - register JSAMPROW inptr; - register JSAMPROW outptr, outptr0, outptr1, outptr2, outptr3; + register _JSAMPROW inptr; + register _JSAMPROW outptr, outptr0, outptr1, outptr2, outptr3; register JDIMENSION col; register int ci; int nc = cinfo->num_components; @@ -524,10 +548,13 @@ null_method(j_compress_ptr cinfo) */ GLOBAL(void) -jinit_color_converter(j_compress_ptr cinfo) +_jinit_color_converter(j_compress_ptr cinfo) { my_cconvert_ptr cconvert; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + cconvert = (my_cconvert_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, sizeof(my_color_converter)); @@ -574,123 +601,116 @@ jinit_color_converter(j_compress_ptr cinfo) break; } - /* Check num_components, set conversion method based on requested space */ + /* Check num_components, set conversion method based on requested space. + * NOTE: We do not allow any lossy color conversion algorithms in lossless + * mode. + */ switch (cinfo->jpeg_color_space) { case JCS_GRAYSCALE: + if (cinfo->master->lossless && + cinfo->in_color_space != cinfo->jpeg_color_space) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); if (cinfo->num_components != 1) ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); if (cinfo->in_color_space == JCS_GRAYSCALE) - cconvert->pub.color_convert = grayscale_convert; - else if (cinfo->in_color_space == JCS_RGB || - cinfo->in_color_space == JCS_EXT_RGB || - cinfo->in_color_space == JCS_EXT_RGBX || - cinfo->in_color_space == JCS_EXT_BGR || - cinfo->in_color_space == JCS_EXT_BGRX || - cinfo->in_color_space == JCS_EXT_XBGR || - cinfo->in_color_space == JCS_EXT_XRGB || - cinfo->in_color_space == JCS_EXT_RGBA || - cinfo->in_color_space == JCS_EXT_BGRA || - cinfo->in_color_space == JCS_EXT_ABGR || - cinfo->in_color_space == JCS_EXT_ARGB) { + cconvert->pub._color_convert = grayscale_convert; + else if (IsExtRGB(cinfo->in_color_space)) { +#ifdef WITH_SIMD if (jsimd_can_rgb_gray()) - cconvert->pub.color_convert = jsimd_rgb_gray_convert; - else { + cconvert->pub._color_convert = jsimd_rgb_gray_convert; + else +#endif + { cconvert->pub.start_pass = rgb_ycc_start; - cconvert->pub.color_convert = rgb_gray_convert; + cconvert->pub._color_convert = rgb_gray_convert; } } else if (cinfo->in_color_space == JCS_YCbCr) - cconvert->pub.color_convert = grayscale_convert; + cconvert->pub._color_convert = grayscale_convert; else ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); break; case JCS_RGB: + if (cinfo->master->lossless && !IsExtRGB(cinfo->in_color_space)) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); if (cinfo->num_components != 3) ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); if (rgb_red[cinfo->in_color_space] == 0 && rgb_green[cinfo->in_color_space] == 1 && rgb_blue[cinfo->in_color_space] == 2 && rgb_pixelsize[cinfo->in_color_space] == 3) { -#if defined(__mips__) +#if defined(WITH_SIMD) && defined(__mips__) if (jsimd_c_can_null_convert()) - cconvert->pub.color_convert = jsimd_c_null_convert; + cconvert->pub._color_convert = jsimd_c_null_convert; else #endif - cconvert->pub.color_convert = null_convert; - } else if (cinfo->in_color_space == JCS_RGB || - cinfo->in_color_space == JCS_EXT_RGB || - cinfo->in_color_space == JCS_EXT_RGBX || - cinfo->in_color_space == JCS_EXT_BGR || - cinfo->in_color_space == JCS_EXT_BGRX || - cinfo->in_color_space == JCS_EXT_XBGR || - cinfo->in_color_space == JCS_EXT_XRGB || - cinfo->in_color_space == JCS_EXT_RGBA || - cinfo->in_color_space == JCS_EXT_BGRA || - cinfo->in_color_space == JCS_EXT_ABGR || - cinfo->in_color_space == JCS_EXT_ARGB) - cconvert->pub.color_convert = rgb_rgb_convert; + cconvert->pub._color_convert = null_convert; + } else if (IsExtRGB(cinfo->in_color_space)) + cconvert->pub._color_convert = rgb_rgb_convert; else ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); break; case JCS_YCbCr: + if (cinfo->master->lossless && + cinfo->in_color_space != cinfo->jpeg_color_space) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); if (cinfo->num_components != 3) ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); - if (cinfo->in_color_space == JCS_RGB || - cinfo->in_color_space == JCS_EXT_RGB || - cinfo->in_color_space == JCS_EXT_RGBX || - cinfo->in_color_space == JCS_EXT_BGR || - cinfo->in_color_space == JCS_EXT_BGRX || - cinfo->in_color_space == JCS_EXT_XBGR || - cinfo->in_color_space == JCS_EXT_XRGB || - cinfo->in_color_space == JCS_EXT_RGBA || - cinfo->in_color_space == JCS_EXT_BGRA || - cinfo->in_color_space == JCS_EXT_ABGR || - cinfo->in_color_space == JCS_EXT_ARGB) { + if (IsExtRGB(cinfo->in_color_space)) { +#ifdef WITH_SIMD if (jsimd_can_rgb_ycc()) - cconvert->pub.color_convert = jsimd_rgb_ycc_convert; - else { + cconvert->pub._color_convert = jsimd_rgb_ycc_convert; + else +#endif + { cconvert->pub.start_pass = rgb_ycc_start; - cconvert->pub.color_convert = rgb_ycc_convert; + cconvert->pub._color_convert = rgb_ycc_convert; } } else if (cinfo->in_color_space == JCS_YCbCr) { -#if defined(__mips__) +#if defined(WITH_SIMD) && defined(__mips__) if (jsimd_c_can_null_convert()) - cconvert->pub.color_convert = jsimd_c_null_convert; + cconvert->pub._color_convert = jsimd_c_null_convert; else #endif - cconvert->pub.color_convert = null_convert; + cconvert->pub._color_convert = null_convert; } else ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); break; case JCS_CMYK: + if (cinfo->master->lossless && + cinfo->in_color_space != cinfo->jpeg_color_space) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); if (cinfo->num_components != 4) ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); if (cinfo->in_color_space == JCS_CMYK) { -#if defined(__mips__) +#if defined(WITH_SIMD) && defined(__mips__) if (jsimd_c_can_null_convert()) - cconvert->pub.color_convert = jsimd_c_null_convert; + cconvert->pub._color_convert = jsimd_c_null_convert; else #endif - cconvert->pub.color_convert = null_convert; + cconvert->pub._color_convert = null_convert; } else ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); break; case JCS_YCCK: + if (cinfo->master->lossless && + cinfo->in_color_space != cinfo->jpeg_color_space) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); if (cinfo->num_components != 4) ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); if (cinfo->in_color_space == JCS_CMYK) { cconvert->pub.start_pass = rgb_ycc_start; - cconvert->pub.color_convert = cmyk_ycck_convert; + cconvert->pub._color_convert = cmyk_ycck_convert; } else if (cinfo->in_color_space == JCS_YCCK) { -#if defined(__mips__) +#if defined(WITH_SIMD) && defined(__mips__) if (jsimd_c_can_null_convert()) - cconvert->pub.color_convert = jsimd_c_null_convert; + cconvert->pub._color_convert = jsimd_c_null_convert; else #endif - cconvert->pub.color_convert = null_convert; + cconvert->pub._color_convert = null_convert; } else ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); break; @@ -699,12 +719,14 @@ jinit_color_converter(j_compress_ptr cinfo) if (cinfo->jpeg_color_space != cinfo->in_color_space || cinfo->num_components != cinfo->input_components) ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); -#if defined(__mips__) +#if defined(WITH_SIMD) && defined(__mips__) if (jsimd_c_can_null_convert()) - cconvert->pub.color_convert = jsimd_c_null_convert; + cconvert->pub._color_convert = jsimd_c_null_convert; else #endif - cconvert->pub.color_convert = null_convert; + cconvert->pub._color_convert = null_convert; break; } } + +#endif /* BITS_IN_JSAMPLE != 16 || defined(C_LOSSLESS_SUPPORTED) */ diff --git a/jcdctmgr.c b/jcdctmgr.c index 7dae17a..7191ee7 100644 --- a/jcdctmgr.c +++ b/jcdctmgr.c @@ -6,7 +6,7 @@ * libjpeg-turbo Modifications: * Copyright (C) 1999-2006, MIYASAKA Masaru. * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2011, 2014-2015, D. R. Commander. + * Copyright (C) 2011, 2014-2015, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -28,10 +28,10 @@ typedef void (*forward_DCT_method_ptr) (DCTELEM *data); typedef void (*float_DCT_method_ptr) (FAST_FLOAT *data); -typedef void (*convsamp_method_ptr) (JSAMPARRAY sample_data, +typedef void (*convsamp_method_ptr) (_JSAMPARRAY sample_data, JDIMENSION start_col, DCTELEM *workspace); -typedef void (*float_convsamp_method_ptr) (JSAMPARRAY sample_data, +typedef void (*float_convsamp_method_ptr) (_JSAMPARRAY sample_data, JDIMENSION start_col, FAST_FLOAT *workspace); @@ -265,10 +265,14 @@ start_pass_fdctmgr(j_compress_ptr cinfo) dtbl = fdct->divisors[qtblno]; for (i = 0; i < DCTSIZE2; i++) { #if BITS_IN_JSAMPLE == 8 +#ifdef WITH_SIMD if (!compute_reciprocal(qtbl->quantval[i] << 3, &dtbl[i]) && fdct->quantize == jsimd_quantize) fdct->quantize = quantize; #else + compute_reciprocal(qtbl->quantval[i] << 3, &dtbl[i]); +#endif +#else dtbl[i] = ((DCTELEM)qtbl->quantval[i]) << 3; #endif } @@ -305,6 +309,7 @@ start_pass_fdctmgr(j_compress_ptr cinfo) dtbl = fdct->divisors[qtblno]; for (i = 0; i < DCTSIZE2; i++) { #if BITS_IN_JSAMPLE == 8 +#ifdef WITH_SIMD if (!compute_reciprocal( DESCALE(MULTIPLY16V16((JLONG)qtbl->quantval[i], (JLONG)aanscales[i]), @@ -312,6 +317,12 @@ start_pass_fdctmgr(j_compress_ptr cinfo) fdct->quantize == jsimd_quantize) fdct->quantize = quantize; #else + compute_reciprocal( + DESCALE(MULTIPLY16V16((JLONG)qtbl->quantval[i], + (JLONG)aanscales[i]), + CONST_BITS-3), &dtbl[i]); +#endif +#else dtbl[i] = (DCTELEM) DESCALE(MULTIPLY16V16((JLONG)qtbl->quantval[i], (JLONG)aanscales[i]), @@ -370,10 +381,10 @@ start_pass_fdctmgr(j_compress_ptr cinfo) */ METHODDEF(void) -convsamp(JSAMPARRAY sample_data, JDIMENSION start_col, DCTELEM *workspace) +convsamp(_JSAMPARRAY sample_data, JDIMENSION start_col, DCTELEM *workspace) { register DCTELEM *workspaceptr; - register JSAMPROW elemptr; + register _JSAMPROW elemptr; register int elemr; workspaceptr = workspace; @@ -381,19 +392,19 @@ convsamp(JSAMPARRAY sample_data, JDIMENSION start_col, DCTELEM *workspace) elemptr = sample_data[elemr] + start_col; #if DCTSIZE == 8 /* unroll the inner loop */ - *workspaceptr++ = (*elemptr++) - CENTERJSAMPLE; - *workspaceptr++ = (*elemptr++) - CENTERJSAMPLE; - *workspaceptr++ = (*elemptr++) - CENTERJSAMPLE; - *workspaceptr++ = (*elemptr++) - CENTERJSAMPLE; - *workspaceptr++ = (*elemptr++) - CENTERJSAMPLE; - *workspaceptr++ = (*elemptr++) - CENTERJSAMPLE; - *workspaceptr++ = (*elemptr++) - CENTERJSAMPLE; - *workspaceptr++ = (*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = (*elemptr++) - _CENTERJSAMPLE; + *workspaceptr++ = (*elemptr++) - _CENTERJSAMPLE; + *workspaceptr++ = (*elemptr++) - _CENTERJSAMPLE; + *workspaceptr++ = (*elemptr++) - _CENTERJSAMPLE; + *workspaceptr++ = (*elemptr++) - _CENTERJSAMPLE; + *workspaceptr++ = (*elemptr++) - _CENTERJSAMPLE; + *workspaceptr++ = (*elemptr++) - _CENTERJSAMPLE; + *workspaceptr++ = (*elemptr++) - _CENTERJSAMPLE; #else { register int elemc; for (elemc = DCTSIZE; elemc > 0; elemc--) - *workspaceptr++ = (*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = (*elemptr++) - _CENTERJSAMPLE; } #endif } @@ -488,7 +499,7 @@ quantize(JCOEFPTR coef_block, DCTELEM *divisors, DCTELEM *workspace) METHODDEF(void) forward_DCT(j_compress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + _JSAMPARRAY sample_data, JBLOCKROW coef_blocks, JDIMENSION start_row, JDIMENSION start_col, JDIMENSION num_blocks) /* This version is used for integer DCT implementations. */ { @@ -522,30 +533,30 @@ forward_DCT(j_compress_ptr cinfo, jpeg_component_info *compptr, #ifdef DCT_FLOAT_SUPPORTED METHODDEF(void) -convsamp_float(JSAMPARRAY sample_data, JDIMENSION start_col, +convsamp_float(_JSAMPARRAY sample_data, JDIMENSION start_col, FAST_FLOAT *workspace) { register FAST_FLOAT *workspaceptr; - register JSAMPROW elemptr; + register _JSAMPROW elemptr; register int elemr; workspaceptr = workspace; for (elemr = 0; elemr < DCTSIZE; elemr++) { elemptr = sample_data[elemr] + start_col; #if DCTSIZE == 8 /* unroll the inner loop */ - *workspaceptr++ = (FAST_FLOAT)((*elemptr++) - CENTERJSAMPLE); - *workspaceptr++ = (FAST_FLOAT)((*elemptr++) - CENTERJSAMPLE); - *workspaceptr++ = (FAST_FLOAT)((*elemptr++) - CENTERJSAMPLE); - *workspaceptr++ = (FAST_FLOAT)((*elemptr++) - CENTERJSAMPLE); - *workspaceptr++ = (FAST_FLOAT)((*elemptr++) - CENTERJSAMPLE); - *workspaceptr++ = (FAST_FLOAT)((*elemptr++) - CENTERJSAMPLE); - *workspaceptr++ = (FAST_FLOAT)((*elemptr++) - CENTERJSAMPLE); - *workspaceptr++ = (FAST_FLOAT)((*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)((*elemptr++) - _CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)((*elemptr++) - _CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)((*elemptr++) - _CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)((*elemptr++) - _CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)((*elemptr++) - _CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)((*elemptr++) - _CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)((*elemptr++) - _CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)((*elemptr++) - _CENTERJSAMPLE); #else { register int elemc; for (elemc = DCTSIZE; elemc > 0; elemc--) - *workspaceptr++ = (FAST_FLOAT)((*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)((*elemptr++) - _CENTERJSAMPLE); } #endif } @@ -577,7 +588,7 @@ quantize_float(JCOEFPTR coef_block, FAST_FLOAT *divisors, METHODDEF(void) forward_DCT_float(j_compress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + _JSAMPARRAY sample_data, JBLOCKROW coef_blocks, JDIMENSION start_row, JDIMENSION start_col, JDIMENSION num_blocks) /* This version is used for floating-point DCT implementations. */ @@ -617,11 +628,14 @@ forward_DCT_float(j_compress_ptr cinfo, jpeg_component_info *compptr, */ GLOBAL(void) -jinit_forward_dct(j_compress_ptr cinfo) +_jinit_forward_dct(j_compress_ptr cinfo) { my_fdct_ptr fdct; int i; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + fdct = (my_fdct_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, sizeof(my_fdct_controller)); @@ -632,28 +646,34 @@ jinit_forward_dct(j_compress_ptr cinfo) switch (cinfo->dct_method) { #ifdef DCT_ISLOW_SUPPORTED case JDCT_ISLOW: - fdct->pub.forward_DCT = forward_DCT; + fdct->pub._forward_DCT = forward_DCT; +#ifdef WITH_SIMD if (jsimd_can_fdct_islow()) fdct->dct = jsimd_fdct_islow; else - fdct->dct = jpeg_fdct_islow; +#endif + fdct->dct = _jpeg_fdct_islow; break; #endif #ifdef DCT_IFAST_SUPPORTED case JDCT_IFAST: - fdct->pub.forward_DCT = forward_DCT; + fdct->pub._forward_DCT = forward_DCT; +#ifdef WITH_SIMD if (jsimd_can_fdct_ifast()) fdct->dct = jsimd_fdct_ifast; else - fdct->dct = jpeg_fdct_ifast; +#endif + fdct->dct = _jpeg_fdct_ifast; break; #endif #ifdef DCT_FLOAT_SUPPORTED case JDCT_FLOAT: - fdct->pub.forward_DCT = forward_DCT_float; + fdct->pub._forward_DCT = forward_DCT_float; +#ifdef WITH_SIMD if (jsimd_can_fdct_float()) fdct->float_dct = jsimd_fdct_float; else +#endif fdct->float_dct = jpeg_fdct_float; break; #endif @@ -671,25 +691,33 @@ jinit_forward_dct(j_compress_ptr cinfo) case JDCT_IFAST: #endif #if defined(DCT_ISLOW_SUPPORTED) || defined(DCT_IFAST_SUPPORTED) +#ifdef WITH_SIMD if (jsimd_can_convsamp()) fdct->convsamp = jsimd_convsamp; else +#endif fdct->convsamp = convsamp; +#ifdef WITH_SIMD if (jsimd_can_quantize()) fdct->quantize = jsimd_quantize; else +#endif fdct->quantize = quantize; break; #endif #ifdef DCT_FLOAT_SUPPORTED case JDCT_FLOAT: +#ifdef WITH_SIMD if (jsimd_can_convsamp_float()) fdct->float_convsamp = jsimd_convsamp_float; else +#endif fdct->float_convsamp = convsamp_float; +#ifdef WITH_SIMD if (jsimd_can_quantize_float()) fdct->float_quantize = jsimd_quantize_float; else +#endif fdct->float_quantize = quantize_float; break; #endif diff --git a/jcdiffct.c b/jcdiffct.c new file mode 100644 index 0000000..0bae068 --- /dev/null +++ b/jcdiffct.c @@ -0,0 +1,411 @@ +/* + * jcdiffct.c + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1994-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file contains the difference buffer controller for compression. + * This controller is the top level of the lossless JPEG compressor proper. + * The difference buffer lies between the prediction/differencing and entropy + * encoding steps. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jlossls.h" /* Private declarations for lossless codec */ + + +#ifdef C_LOSSLESS_SUPPORTED + +/* We use a full-image sample buffer when doing Huffman optimization, + * and also for writing multiple-scan JPEG files. In all cases, the + * full-image buffer is filled during the first pass, and the scaling, + * prediction and differencing steps are run during subsequent passes. + */ +#ifdef ENTROPY_OPT_SUPPORTED +#define FULL_SAMP_BUFFER_SUPPORTED +#else +#ifdef C_MULTISCAN_FILES_SUPPORTED +#define FULL_SAMP_BUFFER_SUPPORTED +#endif +#endif + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_c_coef_controller pub; /* public fields */ + + JDIMENSION iMCU_row_num; /* iMCU row # within image */ + JDIMENSION mcu_ctr; /* counts MCUs processed in current row */ + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + _JSAMPROW cur_row[MAX_COMPONENTS]; /* row of point-transformed samples */ + _JSAMPROW prev_row[MAX_COMPONENTS]; /* previous row of Pt'd samples */ + JDIFFARRAY diff_buf[MAX_COMPONENTS]; /* iMCU row of differences */ + + /* In multi-pass modes, we need a virtual sample array for each component. */ + jvirt_sarray_ptr whole_image[MAX_COMPONENTS]; +} my_diff_controller; + +typedef my_diff_controller *my_diff_ptr; + + +/* Forward declarations */ +METHODDEF(boolean) compress_data(j_compress_ptr cinfo, _JSAMPIMAGE input_buf); +#ifdef FULL_SAMP_BUFFER_SUPPORTED +METHODDEF(boolean) compress_first_pass(j_compress_ptr cinfo, + _JSAMPIMAGE input_buf); +METHODDEF(boolean) compress_output(j_compress_ptr cinfo, + _JSAMPIMAGE input_buf); +#endif + + +LOCAL(void) +start_iMCU_row(j_compress_ptr cinfo) +/* Reset within-iMCU-row counters for a new row */ +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + * But at the bottom of the image, process only what's left. + */ + if (cinfo->comps_in_scan > 1) { + diff->MCU_rows_per_iMCU_row = 1; + } else { + if (diff->iMCU_row_num < (cinfo->total_iMCU_rows-1)) + diff->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + else + diff->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + } + + diff->mcu_ctr = 0; + diff->MCU_vert_offset = 0; +} + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_diff(j_compress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + + /* Because it is hitching a ride on the jpeg_forward_dct struct, + * start_pass_lossless() will be called at the start of the initial pass. + * This ensures that it will be called at the start of the Huffman + * optimization and output passes as well. + */ + if (pass_mode == JBUF_CRANK_DEST) + (*cinfo->fdct->start_pass) (cinfo); + + diff->iMCU_row_num = 0; + start_iMCU_row(cinfo); + + switch (pass_mode) { + case JBUF_PASS_THRU: + if (diff->whole_image[0] != NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + diff->pub._compress_data = compress_data; + break; +#ifdef FULL_SAMP_BUFFER_SUPPORTED + case JBUF_SAVE_AND_PASS: + if (diff->whole_image[0] == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + diff->pub._compress_data = compress_first_pass; + break; + case JBUF_CRANK_DEST: + if (diff->whole_image[0] == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + diff->pub._compress_data = compress_output; + break; +#endif + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } +} + + +#define SWAP_ROWS(rowa, rowb) { \ + _JSAMPROW temp = rowa; \ + rowa = rowb; rowb = temp; \ +} + +/* + * Process some data in the single-pass case. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor rows for each component in the image. + * Returns TRUE if the iMCU row is completed, FALSE if suspended. + * + * NB: input_buf contains a plane for each component in image, + * which we index according to the component's SOF position. + */ + +METHODDEF(boolean) +compress_data(j_compress_ptr cinfo, _JSAMPIMAGE input_buf) +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + lossless_comp_ptr losslessc = (lossless_comp_ptr)cinfo->fdct; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + JDIMENSION MCU_count; /* number of MCUs encoded */ + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + int ci, compi, yoffset, samp_row, samp_rows, samps_across; + jpeg_component_info *compptr; + + /* Loop to write as much as one whole iMCU row */ + for (yoffset = diff->MCU_vert_offset; yoffset < diff->MCU_rows_per_iMCU_row; + yoffset++) { + + MCU_col_num = diff->mcu_ctr; + + /* Scale and predict each scanline of the MCU row separately. + * + * Note: We only do this if we are at the start of an MCU row, ie, + * we don't want to reprocess a row suspended by the output. + */ + if (MCU_col_num == 0) { + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + compi = compptr->component_index; + if (diff->iMCU_row_num < last_iMCU_row) + samp_rows = compptr->v_samp_factor; + else { + /* NB: can't use last_row_height here, since may not be set! */ + samp_rows = + (int)(compptr->height_in_blocks % compptr->v_samp_factor); + if (samp_rows == 0) samp_rows = compptr->v_samp_factor; + else { + /* Fill dummy difference rows at the bottom edge with zeros, which + * will encode to the smallest amount of data. + */ + for (samp_row = samp_rows; samp_row < compptr->v_samp_factor; + samp_row++) + memset(diff->diff_buf[compi][samp_row], 0, + jround_up((long)compptr->width_in_blocks, + (long)compptr->h_samp_factor) * sizeof(JDIFF)); + } + } + samps_across = compptr->width_in_blocks; + + for (samp_row = 0; samp_row < samp_rows; samp_row++) { + (*losslessc->scaler_scale) (cinfo, + input_buf[compi][samp_row], + diff->cur_row[compi], + samps_across); + (*losslessc->predict_difference[compi]) + (cinfo, compi, diff->cur_row[compi], diff->prev_row[compi], + diff->diff_buf[compi][samp_row], samps_across); + SWAP_ROWS(diff->cur_row[compi], diff->prev_row[compi]); + } + } + } + /* Try to write the MCU row (or remaining portion of suspended MCU row). */ + MCU_count = + (*cinfo->entropy->encode_mcus) (cinfo, + diff->diff_buf, yoffset, MCU_col_num, + cinfo->MCUs_per_row - MCU_col_num); + if (MCU_count != cinfo->MCUs_per_row - MCU_col_num) { + /* Suspension forced; update state counters and exit */ + diff->MCU_vert_offset = yoffset; + diff->mcu_ctr += MCU_col_num; + return FALSE; + } + /* Completed an MCU row, but perhaps not an iMCU row */ + diff->mcu_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + diff->iMCU_row_num++; + start_iMCU_row(cinfo); + return TRUE; +} + + +#ifdef FULL_SAMP_BUFFER_SUPPORTED + +/* + * Process some data in the first pass of a multi-pass case. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor rows for each component in the image. + * This amount of data is read from the source buffer and saved into the + * virtual arrays. + * + * We must also emit the data to the compressor. This is conveniently + * done by calling compress_output() after we've loaded the current strip + * of the virtual arrays. + * + * NB: input_buf contains a plane for each component in image. All components + * are loaded into the virtual arrays in this pass. However, it may be that + * only a subset of the components are emitted to the compressor during + * this first pass; be careful about looking at the scan-dependent variables + * (MCU dimensions, etc). + */ + +METHODDEF(boolean) +compress_first_pass(j_compress_ptr cinfo, _JSAMPIMAGE input_buf) +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + JDIMENSION samps_across; + int ci, samp_row, samp_rows; + _JSAMPARRAY buffer; + jpeg_component_info *compptr; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Align the virtual buffer for this component. */ + buffer = (_JSAMPARRAY)(*cinfo->mem->access_virt_sarray) + ((j_common_ptr)cinfo, diff->whole_image[ci], + diff->iMCU_row_num * compptr->v_samp_factor, + (JDIMENSION)compptr->v_samp_factor, TRUE); + + /* Count non-dummy sample rows in this iMCU row. */ + if (diff->iMCU_row_num < last_iMCU_row) + samp_rows = compptr->v_samp_factor; + else { + /* NB: can't use last_row_height here, since may not be set! */ + samp_rows = (int)(compptr->height_in_blocks % compptr->v_samp_factor); + if (samp_rows == 0) samp_rows = compptr->v_samp_factor; + } + samps_across = compptr->width_in_blocks; + + /* Perform point transform scaling and prediction/differencing for all + * non-dummy rows in this iMCU row. Each call on these functions + * processes a complete row of samples. + */ + for (samp_row = 0; samp_row < samp_rows; samp_row++) { + memcpy(buffer[samp_row], input_buf[ci][samp_row], + samps_across * sizeof(_JSAMPLE)); + } + } + /* NB: compress_output will increment iMCU_row_num if successful. + * A suspension return will result in redoing all the work above next time. + */ + + /* Emit data to the compressor, sharing code with subsequent passes */ + return compress_output(cinfo, input_buf); +} + + +/* + * Process some data in subsequent passes of a multi-pass case. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor rows for each component in the scan. + * The data is obtained from the virtual arrays and fed to the compressor. + * Returns TRUE if the iMCU row is completed, FALSE if suspended. + * + * NB: input_buf is ignored; it is likely to be a NULL pointer. + */ + +METHODDEF(boolean) +compress_output(j_compress_ptr cinfo, _JSAMPIMAGE input_buf) +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + int ci, compi; + _JSAMPARRAY buffer[MAX_COMPS_IN_SCAN]; + jpeg_component_info *compptr; + + /* Align the virtual buffers for the components used in this scan. + * NB: during first pass, this is safe only because the buffers will + * already be aligned properly, so jmemmgr.c won't need to do any I/O. + */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + compi = compptr->component_index; + buffer[compi] = (_JSAMPARRAY)(*cinfo->mem->access_virt_sarray) + ((j_common_ptr)cinfo, diff->whole_image[compi], + diff->iMCU_row_num * compptr->v_samp_factor, + (JDIMENSION)compptr->v_samp_factor, FALSE); + } + + return compress_data(cinfo, buffer); +} + +#endif /* FULL_SAMP_BUFFER_SUPPORTED */ + + +/* + * Initialize difference buffer controller. + */ + +GLOBAL(void) +_jinit_c_diff_controller(j_compress_ptr cinfo, boolean need_full_buffer) +{ + my_diff_ptr diff; + int ci, row; + jpeg_component_info *compptr; + + diff = (my_diff_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, + sizeof(my_diff_controller)); + cinfo->coef = (struct jpeg_c_coef_controller *)diff; + diff->pub.start_pass = start_pass_diff; + + /* Create the prediction row buffers. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + diff->cur_row[ci] = *(_JSAMPARRAY)(*cinfo->mem->alloc_sarray) + ((j_common_ptr)cinfo, JPOOL_IMAGE, + (JDIMENSION)jround_up((long)compptr->width_in_blocks, + (long)compptr->h_samp_factor), + (JDIMENSION)1); + diff->prev_row[ci] = *(_JSAMPARRAY)(*cinfo->mem->alloc_sarray) + ((j_common_ptr)cinfo, JPOOL_IMAGE, + (JDIMENSION)jround_up((long)compptr->width_in_blocks, + (long)compptr->h_samp_factor), + (JDIMENSION)1); + } + + /* Create the difference buffer. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + diff->diff_buf[ci] = + ALLOC_DARRAY(JPOOL_IMAGE, + (JDIMENSION)jround_up((long)compptr->width_in_blocks, + (long)compptr->h_samp_factor), + (JDIMENSION)compptr->v_samp_factor); + /* Prefill difference rows with zeros. We do this because only actual + * data is placed in the buffers during prediction/differencing, leaving + * any dummy differences at the right edge as zeros, which will encode + * to the smallest amount of data. + */ + for (row = 0; row < compptr->v_samp_factor; row++) + memset(diff->diff_buf[ci][row], 0, + jround_up((long)compptr->width_in_blocks, + (long)compptr->h_samp_factor) * sizeof(JDIFF)); + } + + /* Create the sample buffer. */ + if (need_full_buffer) { +#ifdef FULL_SAMP_BUFFER_SUPPORTED + /* Allocate a full-image virtual array for each component, */ + /* padded to a multiple of samp_factor differences in each direction. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + diff->whole_image[ci] = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr)cinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION)jround_up((long)compptr->width_in_blocks, + (long)compptr->h_samp_factor), + (JDIMENSION)jround_up((long)compptr->height_in_blocks, + (long)compptr->v_samp_factor), + (JDIMENSION)compptr->v_samp_factor); + } +#else + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); +#endif + } else + diff->whole_image[0] = NULL; /* flag for no virtual arrays */ +} + +#endif /* C_LOSSLESS_SUPPORTED */ diff --git a/jchuff.c b/jchuff.c index f4dfa1c..3fede05 100644 --- a/jchuff.c +++ b/jchuff.c @@ -3,11 +3,14 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: - * Copyright (C) 2009-2011, 2014-2016, 2018-2022, D. R. Commander. + * Copyright (C) 2009-2011, 2014-2016, 2018-2023, D. R. Commander. * Copyright (C) 2015, Matthieu Darbois. * Copyright (C) 2018, Matthias Räncker. * Copyright (C) 2020, Arm Limited. + * Copyright (C) 2022, Felix Hanau. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -26,8 +29,11 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" +#ifdef WITH_SIMD #include "jsimd.h" -#include "jconfigint.h" +#else +#include "jchuff.h" /* Declarations shared with jc*huff.c */ +#endif #include /* @@ -102,7 +108,9 @@ typedef bit_buf_type simd_bit_buf_type; typedef struct { union { bit_buf_type c; +#ifdef WITH_SIMD simd_bit_buf_type simd; +#endif } put_buffer; /* current bit accumulation buffer */ int free_bits; /* # of bits available in it */ /* (Neon GAS: # of bits now in it) */ @@ -127,7 +135,9 @@ typedef struct { long *ac_count_ptrs[NUM_HUFF_TBLS]; #endif +#ifdef WITH_SIMD int simd; +#endif } huff_entropy_encoder; typedef huff_entropy_encoder *huff_entropy_ptr; @@ -141,7 +151,9 @@ typedef struct { size_t free_in_buffer; /* # of byte spaces remaining in buffer */ savable_state cur; /* Current bit buffer & DC state */ j_compress_ptr cinfo; /* dump_buffer needs access to this */ +#ifdef WITH_SIMD int simd; +#endif } working_state; @@ -180,7 +192,9 @@ start_pass_huff(j_compress_ptr cinfo, boolean gather_statistics) entropy->pub.finish_pass = finish_pass_huff; } +#ifdef WITH_SIMD entropy->simd = jsimd_can_huff_encode_one_block(); +#endif for (ci = 0; ci < cinfo->comps_in_scan; ci++) { compptr = cinfo->cur_comp_info[ci]; @@ -220,6 +234,7 @@ start_pass_huff(j_compress_ptr cinfo, boolean gather_statistics) } /* Initialize bit buffer to empty */ +#ifdef WITH_SIMD if (entropy->simd) { entropy->saved.put_buffer.simd = 0; #if defined(__aarch64__) && !defined(NEON_INTRINSICS) @@ -227,7 +242,9 @@ start_pass_huff(j_compress_ptr cinfo, boolean gather_statistics) #else entropy->saved.free_bits = SIMD_BIT_BUF_SIZE; #endif - } else { + } else +#endif + { entropy->saved.put_buffer.c = 0; entropy->saved.free_bits = BIT_BUF_SIZE; } @@ -242,7 +259,7 @@ start_pass_huff(j_compress_ptr cinfo, boolean gather_statistics) * Compute the derived values for a Huffman table. * This routine also performs some validation checks on the table. * - * Note this is also used by jcphuff.c. + * Note this is also used by jcphuff.c and jclhuff.c. */ GLOBAL(void) @@ -318,12 +335,12 @@ jpeg_make_c_derived_tbl(j_compress_ptr cinfo, boolean isDC, int tblno, memset(dtbl->ehufco, 0, sizeof(dtbl->ehufco)); memset(dtbl->ehufsi, 0, sizeof(dtbl->ehufsi)); - /* This is also a convenient place to check for out-of-range - * and duplicated VAL entries. We allow 0..255 for AC symbols - * but only 0..15 for DC. (We could constrain them further - * based on data depth and mode, but this seems enough.) + /* This is also a convenient place to check for out-of-range and duplicated + * VAL entries. We allow 0..255 for AC symbols but only 0..15 for DC in + * lossy mode and 0..16 for DC in lossless mode. (We could constrain them + * further based on data depth and mode, but this seems enough.) */ - maxsymbol = isDC ? 15 : 255; + maxsymbol = isDC ? (cinfo->master->lossless ? 16 : 15) : 255; for (p = 0; p < lastp; p++) { i = htbl->huffval[p]; @@ -500,6 +517,7 @@ flush_bits(working_state *state) simd_bit_buf_type put_buffer; int put_bits; int localbuf = 0; +#ifdef WITH_SIMD if (state->simd) { #if defined(__aarch64__) && !defined(NEON_INTRINSICS) put_bits = state->cur.free_bits; @@ -507,7 +525,9 @@ flush_bits(working_state *state) put_bits = SIMD_BIT_BUF_SIZE - state->cur.free_bits; #endif put_buffer = state->cur.put_buffer.simd; - } else { + } else +#endif + { put_bits = BIT_BUF_SIZE - state->cur.free_bits; put_buffer = state->cur.put_buffer.c; } @@ -525,6 +545,7 @@ flush_bits(working_state *state) EMIT_BYTE(temp) } +#ifdef WITH_SIMD if (state->simd) { /* and reset bit buffer to empty */ state->cur.put_buffer.simd = 0; #if defined(__aarch64__) && !defined(NEON_INTRINSICS) @@ -532,7 +553,9 @@ flush_bits(working_state *state) #else state->cur.free_bits = SIMD_BIT_BUF_SIZE; #endif - } else { + } else +#endif + { state->cur.put_buffer.c = 0; state->cur.free_bits = BIT_BUF_SIZE; } @@ -542,6 +565,8 @@ flush_bits(working_state *state) } +#ifdef WITH_SIMD + /* Encode a single block's worth of coefficients */ LOCAL(boolean) @@ -561,6 +586,8 @@ encode_one_block_simd(working_state *state, JCOEFPTR block, int last_dc_val, return TRUE; } +#endif + LOCAL(boolean) encode_one_block(working_state *state, JCOEFPTR block, int last_dc_val, c_derived_tbl *dctbl, c_derived_tbl *actbl) @@ -569,6 +596,7 @@ encode_one_block(working_state *state, JCOEFPTR block, int last_dc_val, bit_buf_type put_buffer; JOCTET _buffer[BUFSIZE], *buffer; int localbuf = 0; + int max_coef_bits = state->cinfo->data_precision + 2; free_bits = state->cur.free_bits; put_buffer = state->cur.put_buffer.c; @@ -589,6 +617,11 @@ encode_one_block(working_state *state, JCOEFPTR block, int last_dc_val, /* Find the number of bits needed for the magnitude of the coefficient */ nbits = JPEG_NBITS(nbits); + /* Check for out-of-range coefficient values. + * Since we're encoding a difference, the range limit is twice as much. + */ + if (nbits > max_coef_bits + 1) + ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); /* Emit the Huffman-coded symbol for the number of bits. * Emit that number of bits of the value, if positive, @@ -614,6 +647,9 @@ encode_one_block(working_state *state, JCOEFPTR block, int last_dc_val, temp += nbits; \ nbits ^= temp; \ nbits = JPEG_NBITS_NONZERO(nbits); \ + /* Check for out-of-range coefficient values */ \ + if (nbits > max_coef_bits) \ + ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); \ /* if run length > 15, must emit special run-length-16 codes (0xF0) */ \ while (r >= 16 * 16) { \ r -= 16 * 16; \ @@ -695,7 +731,9 @@ encode_mcu_huff(j_compress_ptr cinfo, JBLOCKROW *MCU_data) state.free_in_buffer = cinfo->dest->free_in_buffer; state.cur = entropy->saved; state.cinfo = cinfo; +#ifdef WITH_SIMD state.simd = entropy->simd; +#endif /* Emit restart marker if needed */ if (cinfo->restart_interval) { @@ -705,6 +743,7 @@ encode_mcu_huff(j_compress_ptr cinfo, JBLOCKROW *MCU_data) } /* Encode the MCU data blocks */ +#ifdef WITH_SIMD if (entropy->simd) { for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { ci = cinfo->MCU_membership[blkn]; @@ -717,7 +756,9 @@ encode_mcu_huff(j_compress_ptr cinfo, JBLOCKROW *MCU_data) /* Update last_dc_val */ state.cur.last_dc_val[ci] = MCU_data[blkn][0][0]; } - } else { + } else +#endif + { for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { ci = cinfo->MCU_membership[blkn]; compptr = cinfo->cur_comp_info[ci]; @@ -765,7 +806,9 @@ finish_pass_huff(j_compress_ptr cinfo) state.free_in_buffer = cinfo->dest->free_in_buffer; state.cur = entropy->saved; state.cinfo = cinfo; +#ifdef WITH_SIMD state.simd = entropy->simd; +#endif /* Flush out the last data */ if (!flush_bits(&state)) @@ -801,6 +844,7 @@ htest_one_block(j_compress_ptr cinfo, JCOEFPTR block, int last_dc_val, register int temp; register int nbits; register int k, r; + int max_coef_bits = cinfo->data_precision + 2; /* Encode the DC coefficient difference per section F.1.2.1 */ @@ -817,7 +861,7 @@ htest_one_block(j_compress_ptr cinfo, JCOEFPTR block, int last_dc_val, /* Check for out-of-range coefficient values. * Since we're encoding a difference, the range limit is twice as much. */ - if (nbits > MAX_COEF_BITS + 1) + if (nbits > max_coef_bits + 1) ERREXIT(cinfo, JERR_BAD_DCT_COEF); /* Count the Huffman symbol for the number of bits */ @@ -846,7 +890,7 @@ htest_one_block(j_compress_ptr cinfo, JCOEFPTR block, int last_dc_val, while ((temp >>= 1)) nbits++; /* Check for out-of-range coefficient values */ - if (nbits > MAX_COEF_BITS) + if (nbits > max_coef_bits) ERREXIT(cinfo, JERR_BAD_DCT_COEF); /* Count Huffman symbol for run length / number of bits */ @@ -901,7 +945,7 @@ encode_mcu_gather(j_compress_ptr cinfo, JBLOCKROW *MCU_data) /* * Generate the best Huffman code table for the given counts, fill htbl. - * Note this is also used by jcphuff.c. + * Note this is also used by jcphuff.c and jclhuff.c. * * The JPEG standard requires that no symbol be assigned a codeword of all * one bits (so that padding bits added at the end of a compressed segment @@ -933,11 +977,15 @@ jpeg_gen_optimal_table(j_compress_ptr cinfo, JHUFF_TBL *htbl, long freq[]) { #define MAX_CLEN 32 /* assumed maximum initial code length */ UINT8 bits[MAX_CLEN + 1]; /* bits[k] = # of symbols with code length k */ + int bit_pos[MAX_CLEN + 1]; /* # of symbols with smaller code length */ int codesize[257]; /* codesize[k] = code length of symbol k */ + int nz_index[257]; /* index of nonzero symbol in the original freq + array */ int others[257]; /* next symbol in current branch of tree */ int c1, c2; int p, i, j; - long v; + int num_nz_symbols; + long v, v2; /* This algorithm is explained in section K.2 of the JPEG standard */ @@ -952,28 +1000,41 @@ jpeg_gen_optimal_table(j_compress_ptr cinfo, JHUFF_TBL *htbl, long freq[]) * will be placed last in the largest codeword category. */ + /* Group nonzero frequencies together so we can more easily find the + * smallest. + */ + num_nz_symbols = 0; + for (i = 0; i < 257; i++) { + if (freq[i]) { + nz_index[num_nz_symbols] = i; + freq[num_nz_symbols] = freq[i]; + num_nz_symbols++; + } + } + /* Huffman's basic algorithm to assign optimal code lengths to symbols */ for (;;) { - /* Find the smallest nonzero frequency, set c1 = its symbol */ - /* In case of ties, take the larger symbol number */ + /* Find the two smallest nonzero frequencies; set c1, c2 = their symbols */ + /* In case of ties, take the larger symbol number. Since we have grouped + * the nonzero symbols together, checking for zero symbols is not + * necessary. + */ c1 = -1; - v = 1000000000L; - for (i = 0; i <= 256; i++) { - if (freq[i] && freq[i] <= v) { - v = freq[i]; - c1 = i; - } - } - - /* Find the next smallest nonzero frequency, set c2 = its symbol */ - /* In case of ties, take the larger symbol number */ c2 = -1; v = 1000000000L; - for (i = 0; i <= 256; i++) { - if (freq[i] && freq[i] <= v && i != c1) { - v = freq[i]; - c2 = i; + v2 = 1000000000L; + for (i = 0; i < num_nz_symbols; i++) { + if (freq[i] <= v2) { + if (freq[i] <= v) { + c2 = c1; + v2 = v; + v = freq[i]; + c1 = i; + } else { + v2 = freq[i]; + c2 = i; + } } } @@ -983,7 +1044,10 @@ jpeg_gen_optimal_table(j_compress_ptr cinfo, JHUFF_TBL *htbl, long freq[]) /* Else merge the two counts/trees */ freq[c1] += freq[c2]; - freq[c2] = 0; + /* Set the frequency to a very high value instead of zero, so we don't have + * to check for zero values. + */ + freq[c2] = 1000000001L; /* Increment the codesize of everything in c1's tree branch */ codesize[c1]++; @@ -1003,15 +1067,24 @@ jpeg_gen_optimal_table(j_compress_ptr cinfo, JHUFF_TBL *htbl, long freq[]) } /* Now count the number of symbols of each code length */ - for (i = 0; i <= 256; i++) { - if (codesize[i]) { - /* The JPEG standard seems to think that this can't happen, */ - /* but I'm paranoid... */ - if (codesize[i] > MAX_CLEN) - ERREXIT(cinfo, JERR_HUFF_CLEN_OVERFLOW); - - bits[codesize[i]]++; - } + for (i = 0; i < num_nz_symbols; i++) { + /* The JPEG standard seems to think that this can't happen, */ + /* but I'm paranoid... */ + if (codesize[i] > MAX_CLEN) + ERREXIT(cinfo, JERR_HUFF_CLEN_OVERFLOW); + + bits[codesize[i]]++; + } + + /* Count the number of symbols with a length smaller than i bits, so we can + * construct the symbol table more efficiently. Note that this includes the + * pseudo-symbol 256, but since it is the last symbol, it will not affect the + * table. + */ + p = 0; + for (i = 1; i <= MAX_CLEN; i++) { + bit_pos[i] = p; + p += bits[i]; } /* JPEG doesn't allow symbols with code lengths over 16 bits, so if the pure @@ -1051,14 +1124,9 @@ jpeg_gen_optimal_table(j_compress_ptr cinfo, JHUFF_TBL *htbl, long freq[]) * changes made above, but Rec. ITU-T T.81 | ISO/IEC 10918-1 seems to think * this works. */ - p = 0; - for (i = 1; i <= MAX_CLEN; i++) { - for (j = 0; j <= 255; j++) { - if (codesize[j] == i) { - htbl->huffval[p] = (UINT8)j; - p++; - } - } + for (i = 0; i < num_nz_symbols - 1; i++) { + htbl->huffval[bit_pos[codesize[i]]] = (UINT8)nz_index[i]; + bit_pos[codesize[i]]++; } /* Set sent_table FALSE so updated table will be written to JPEG file. */ diff --git a/jchuff.h b/jchuff.h index 314a232..21f17b8 100644 --- a/jchuff.h +++ b/jchuff.h @@ -3,8 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. - * It was modified by The libjpeg-turbo Project to include only code relevant - * to libjpeg-turbo. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -19,11 +19,13 @@ * Hence the magnitude should always fit in 10 or 14 bits respectively. */ -#if BITS_IN_JSAMPLE == 8 -#define MAX_COEF_BITS 10 -#else -#define MAX_COEF_BITS 14 -#endif +/* The progressive Huffman encoder uses an unsigned 16-bit data type to store + * absolute values of coefficients, because it is possible to inject a + * coefficient value of -32768 into the encoder by attempting to transform a + * malformed 12-bit JPEG image, and the absolute value of -32768 would overflow + * a signed 16-bit integer. + */ +typedef unsigned short UJCOEF; /* Derived data constructed for each Huffman table */ diff --git a/jcinit.c b/jcinit.c index 157353a..fe8a13a 100644 --- a/jcinit.c +++ b/jcinit.c @@ -3,8 +3,10 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: - * Copyright (C) 2020, D. R. Commander. + * Copyright (C) 2020, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -21,7 +23,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jpegcomp.h" +#include "jpegapicomp.h" /* @@ -38,34 +40,101 @@ jinit_compress_master(j_compress_ptr cinfo) /* Preprocessing */ if (!cinfo->raw_data_in) { - jinit_color_converter(cinfo); - jinit_downsampler(cinfo); - jinit_c_prep_controller(cinfo, FALSE /* never need full buffer here */); + if (cinfo->data_precision == 16) { +#ifdef C_LOSSLESS_SUPPORTED + j16init_color_converter(cinfo); + j16init_downsampler(cinfo); + j16init_c_prep_controller(cinfo, + FALSE /* never need full buffer here */); +#else + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); +#endif + } else if (cinfo->data_precision == 12) { + j12init_color_converter(cinfo); + j12init_downsampler(cinfo); + j12init_c_prep_controller(cinfo, + FALSE /* never need full buffer here */); + } else { + jinit_color_converter(cinfo); + jinit_downsampler(cinfo); + jinit_c_prep_controller(cinfo, FALSE /* never need full buffer here */); + } } - /* Forward DCT */ - jinit_forward_dct(cinfo); - /* Entropy encoding: either Huffman or arithmetic coding. */ - if (cinfo->arith_code) { -#ifdef C_ARITH_CODING_SUPPORTED - jinit_arith_encoder(cinfo); + + if (cinfo->master->lossless) { +#ifdef C_LOSSLESS_SUPPORTED + /* Prediction, sample differencing, and point transform */ + if (cinfo->data_precision == 16) + j16init_lossless_compressor(cinfo); + else if (cinfo->data_precision == 12) + j12init_lossless_compressor(cinfo); + else + jinit_lossless_compressor(cinfo); + /* Entropy encoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + jinit_lhuff_encoder(cinfo); + } + + /* Need a full-image difference buffer in any multi-pass mode. */ + if (cinfo->data_precision == 16) + j16init_c_diff_controller(cinfo, (boolean)(cinfo->num_scans > 1 || + cinfo->optimize_coding)); + else if (cinfo->data_precision == 12) + j12init_c_diff_controller(cinfo, (boolean)(cinfo->num_scans > 1 || + cinfo->optimize_coding)); + else + jinit_c_diff_controller(cinfo, (boolean)(cinfo->num_scans > 1 || + cinfo->optimize_coding)); #else - ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + ERREXIT(cinfo, JERR_NOT_COMPILED); #endif } else { - if (cinfo->progressive_mode) { + if (cinfo->data_precision == 16) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + /* Forward DCT */ + if (cinfo->data_precision == 12) + j12init_forward_dct(cinfo); + else + jinit_forward_dct(cinfo); + /* Entropy encoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { +#ifdef C_ARITH_CODING_SUPPORTED + jinit_arith_encoder(cinfo); +#else + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); +#endif + } else { + if (cinfo->progressive_mode) { #ifdef C_PROGRESSIVE_SUPPORTED - jinit_phuff_encoder(cinfo); + jinit_phuff_encoder(cinfo); #else - ERREXIT(cinfo, JERR_NOT_COMPILED); + ERREXIT(cinfo, JERR_NOT_COMPILED); #endif - } else - jinit_huff_encoder(cinfo); + } else + jinit_huff_encoder(cinfo); + } + + /* Need a full-image coefficient buffer in any multi-pass mode. */ + if (cinfo->data_precision == 12) + j12init_c_coef_controller(cinfo, (boolean)(cinfo->num_scans > 1 || + cinfo->optimize_coding)); + else + jinit_c_coef_controller(cinfo, (boolean)(cinfo->num_scans > 1 || + cinfo->optimize_coding)); } - /* Need a full-image coefficient buffer in any multi-pass mode. */ - jinit_c_coef_controller(cinfo, (boolean)(cinfo->num_scans > 1 || - cinfo->optimize_coding)); - jinit_c_main_controller(cinfo, FALSE /* never need full buffer here */); + if (cinfo->data_precision == 16) +#ifdef C_LOSSLESS_SUPPORTED + j16init_c_main_controller(cinfo, FALSE /* never need full buffer here */); +#else + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); +#endif + else if (cinfo->data_precision == 12) + j12init_c_main_controller(cinfo, FALSE /* never need full buffer here */); + else + jinit_c_main_controller(cinfo, FALSE /* never need full buffer here */); jinit_marker_writer(cinfo); diff --git a/jclhuff.c b/jclhuff.c new file mode 100644 index 0000000..ae41545 --- /dev/null +++ b/jclhuff.c @@ -0,0 +1,587 @@ +/* + * jclhuff.c + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1991-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file contains Huffman entropy encoding routines for lossless JPEG. + * + * Much of the complexity here has to do with supporting output suspension. + * If the data destination module demands suspension, we want to be able to + * back up to the start of the current MCU. To do this, we copy state + * variables into local working storage, and update them back to the + * permanent JPEG objects only upon successful completion of an MCU. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jlossls.h" /* Private declarations for lossless codec */ +#include "jchuff.h" /* Declarations shared with jc*huff.c */ + + +#ifdef C_LOSSLESS_SUPPORTED + +/* The legal range of a spatial difference is + * -32767 .. +32768. + * Hence the magnitude should always fit in 16 bits. + */ + +#define MAX_DIFF_BITS 16 + + +/* Expanded entropy encoder object for Huffman encoding in lossless mode. + * + * The savable_state subrecord contains fields that change within an MCU, + * but must not be updated permanently until we complete the MCU. + */ + +typedef struct { + size_t put_buffer; /* current bit-accumulation buffer */ + int put_bits; /* # of bits now in it */ +} savable_state; + + +typedef struct { + int ci, yoffset, MCU_width; +} lhe_input_ptr_info; + + +typedef struct { + struct jpeg_entropy_encoder pub; /* public fields */ + + savable_state saved; /* Bit buffer at start of MCU */ + + /* These fields are NOT loaded into local working state. */ + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + int next_restart_num; /* next restart number to write (0-7) */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + c_derived_tbl *derived_tbls[NUM_HUFF_TBLS]; + + /* Pointers to derived tables to be used for each data unit within an MCU */ + c_derived_tbl *cur_tbls[C_MAX_BLOCKS_IN_MCU]; + +#ifdef ENTROPY_OPT_SUPPORTED /* Statistics tables for optimization */ + long *count_ptrs[NUM_HUFF_TBLS]; + + /* Pointers to stats tables to be used for each data unit within an MCU */ + long *cur_counts[C_MAX_BLOCKS_IN_MCU]; +#endif + + /* Pointers to the proper input difference row for each group of data units + * within an MCU. For each component, there are Vi groups of Hi data units. + */ + JDIFFROW input_ptr[C_MAX_BLOCKS_IN_MCU]; + + /* Number of input pointers in use for the current MCU. This is the sum + * of all Vi in the MCU. + */ + int num_input_ptrs; + + /* Information used for positioning the input pointers within the input + * difference rows. + */ + lhe_input_ptr_info input_ptr_info[C_MAX_BLOCKS_IN_MCU]; + + /* Index of the proper input pointer for each data unit within an MCU */ + int input_ptr_index[C_MAX_BLOCKS_IN_MCU]; + +} lhuff_entropy_encoder; + +typedef lhuff_entropy_encoder *lhuff_entropy_ptr; + +/* Working state while writing an MCU. + * This struct contains all the fields that are needed by subroutines. + */ + +typedef struct { + JOCTET *next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + savable_state cur; /* Current bit buffer & DC state */ + j_compress_ptr cinfo; /* dump_buffer needs access to this */ +} working_state; + + +/* Forward declarations */ +METHODDEF(JDIMENSION) encode_mcus_huff(j_compress_ptr cinfo, + JDIFFIMAGE diff_buf, + JDIMENSION MCU_row_num, + JDIMENSION MCU_col_num, + JDIMENSION nMCU); +METHODDEF(void) finish_pass_huff(j_compress_ptr cinfo); +#ifdef ENTROPY_OPT_SUPPORTED +METHODDEF(JDIMENSION) encode_mcus_gather(j_compress_ptr cinfo, + JDIFFIMAGE diff_buf, + JDIMENSION MCU_row_num, + JDIMENSION MCU_col_num, + JDIMENSION nMCU); +METHODDEF(void) finish_pass_gather(j_compress_ptr cinfo); +#endif + + +/* + * Initialize for a Huffman-compressed scan. + * If gather_statistics is TRUE, we do not output anything during the scan, + * just count the Huffman symbols used and generate Huffman code tables. + */ + +METHODDEF(void) +start_pass_lhuff(j_compress_ptr cinfo, boolean gather_statistics) +{ + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr)cinfo->entropy; + int ci, dctbl, sampn, ptrn, yoffset, xoffset; + jpeg_component_info *compptr; + + if (gather_statistics) { +#ifdef ENTROPY_OPT_SUPPORTED + entropy->pub.encode_mcus = encode_mcus_gather; + entropy->pub.finish_pass = finish_pass_gather; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + entropy->pub.encode_mcus = encode_mcus_huff; + entropy->pub.finish_pass = finish_pass_huff; + } + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + if (gather_statistics) { +#ifdef ENTROPY_OPT_SUPPORTED + /* Check for invalid table indexes */ + /* (make_c_derived_tbl does this in the other path) */ + if (dctbl < 0 || dctbl >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, dctbl); + /* Allocate and zero the statistics tables */ + /* Note that jpeg_gen_optimal_table expects 257 entries in each table! */ + if (entropy->count_ptrs[dctbl] == NULL) + entropy->count_ptrs[dctbl] = (long *) + (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, + 257 * sizeof(long)); + memset(entropy->count_ptrs[dctbl], 0, 257 * sizeof(long)); +#endif + } else { + /* Compute derived values for Huffman tables */ + /* We may do this more than once for a table, but it's not expensive */ + jpeg_make_c_derived_tbl(cinfo, TRUE, dctbl, + &entropy->derived_tbls[dctbl]); + } + } + + /* Precalculate encoding info for each sample in an MCU of this scan */ + for (sampn = 0, ptrn = 0; sampn < cinfo->blocks_in_MCU;) { + compptr = cinfo->cur_comp_info[cinfo->MCU_membership[sampn]]; + ci = compptr->component_index; + for (yoffset = 0; yoffset < compptr->MCU_height; yoffset++, ptrn++) { + /* Precalculate the setup info for each input pointer */ + entropy->input_ptr_info[ptrn].ci = ci; + entropy->input_ptr_info[ptrn].yoffset = yoffset; + entropy->input_ptr_info[ptrn].MCU_width = compptr->MCU_width; + for (xoffset = 0; xoffset < compptr->MCU_width; xoffset++, sampn++) { + /* Precalculate the input pointer index for each sample */ + entropy->input_ptr_index[sampn] = ptrn; + /* Precalculate which tables to use for each sample */ + entropy->cur_tbls[sampn] = entropy->derived_tbls[compptr->dc_tbl_no]; + entropy->cur_counts[sampn] = entropy->count_ptrs[compptr->dc_tbl_no]; + } + } + } + entropy->num_input_ptrs = ptrn; + + /* Initialize bit buffer to empty */ + entropy->saved.put_buffer = 0; + entropy->saved.put_bits = 0; + + /* Initialize restart stuff */ + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num = 0; +} + + +/* Outputting bytes to the file */ + +/* Emit a byte, taking 'action' if must suspend. */ +#define emit_byte(state, val, action) { \ + *(state)->next_output_byte++ = (JOCTET)(val); \ + if (--(state)->free_in_buffer == 0) \ + if (!dump_buffer(state)) \ + { action; } \ +} + + +LOCAL(boolean) +dump_buffer(working_state *state) +/* Empty the output buffer; return TRUE if successful, FALSE if must suspend */ +{ + struct jpeg_destination_mgr *dest = state->cinfo->dest; + + if (!(*dest->empty_output_buffer) (state->cinfo)) + return FALSE; + /* After a successful buffer dump, must reset buffer pointers */ + state->next_output_byte = dest->next_output_byte; + state->free_in_buffer = dest->free_in_buffer; + return TRUE; +} + + +/* Outputting bits to the file */ + +/* Only the right 24 bits of put_buffer are used; the valid bits are + * left-justified in this part. At most 16 bits can be passed to emit_bits + * in one call, and we never retain more than 7 bits in put_buffer + * between calls, so 24 bits are sufficient. + */ + +INLINE +LOCAL(boolean) +emit_bits(working_state *state, unsigned int code, int size) +/* Emit some bits; return TRUE if successful, FALSE if must suspend */ +{ + /* This routine is heavily used, so it's worth coding tightly. */ + register size_t put_buffer = (size_t)code; + register int put_bits = state->cur.put_bits; + + /* if size is 0, caller used an invalid Huffman table entry */ + if (size == 0) + ERREXIT(state->cinfo, JERR_HUFF_MISSING_CODE); + + put_buffer &= (((size_t)1) << size) - 1; /* mask off any extra bits in code */ + + put_bits += size; /* new number of bits in buffer */ + + put_buffer <<= 24 - put_bits; /* align incoming bits */ + + put_buffer |= state->cur.put_buffer; /* and merge with old buffer contents */ + + while (put_bits >= 8) { + int c = (int)((put_buffer >> 16) & 0xFF); + + emit_byte(state, c, return FALSE); + if (c == 0xFF) { /* need to stuff a zero byte? */ + emit_byte(state, 0, return FALSE); + } + put_buffer <<= 8; + put_bits -= 8; + } + + state->cur.put_buffer = put_buffer; /* update state variables */ + state->cur.put_bits = put_bits; + + return TRUE; +} + + +LOCAL(boolean) +flush_bits(working_state *state) +{ + if (!emit_bits(state, 0x7F, 7)) /* fill any partial byte with ones */ + return FALSE; + state->cur.put_buffer = 0; /* and reset bit-buffer to empty */ + state->cur.put_bits = 0; + return TRUE; +} + + +/* + * Emit a restart marker & resynchronize predictions. + */ + +LOCAL(boolean) +emit_restart(working_state *state, int restart_num) +{ + if (!flush_bits(state)) + return FALSE; + + emit_byte(state, 0xFF, return FALSE); + emit_byte(state, JPEG_RST0 + restart_num, return FALSE); + + /* The restart counter is not updated until we successfully write the MCU. */ + + return TRUE; +} + + +/* + * Encode and output nMCU MCUs' worth of Huffman-compressed differences. + */ + +METHODDEF(JDIMENSION) +encode_mcus_huff(j_compress_ptr cinfo, JDIFFIMAGE diff_buf, + JDIMENSION MCU_row_num, JDIMENSION MCU_col_num, + JDIMENSION nMCU) +{ + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr)cinfo->entropy; + working_state state; + int sampn, ci, yoffset, MCU_width, ptrn; + JDIMENSION mcu_num; + + /* Load up working state */ + state.next_output_byte = cinfo->dest->next_output_byte; + state.free_in_buffer = cinfo->dest->free_in_buffer; + state.cur = entropy->saved; + state.cinfo = cinfo; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (!emit_restart(&state, entropy->next_restart_num)) + return 0; + } + + /* Set input pointer locations based on MCU_col_num */ + for (ptrn = 0; ptrn < entropy->num_input_ptrs; ptrn++) { + ci = entropy->input_ptr_info[ptrn].ci; + yoffset = entropy->input_ptr_info[ptrn].yoffset; + MCU_width = entropy->input_ptr_info[ptrn].MCU_width; + entropy->input_ptr[ptrn] = + diff_buf[ci][MCU_row_num + yoffset] + (MCU_col_num * MCU_width); + } + + for (mcu_num = 0; mcu_num < nMCU; mcu_num++) { + + /* Inner loop handles the samples in the MCU */ + for (sampn = 0; sampn < cinfo->blocks_in_MCU; sampn++) { + register int temp, temp2; + register int nbits; + c_derived_tbl *dctbl = entropy->cur_tbls[sampn]; + + /* Encode the difference per section H.1.2.2 */ + + /* Input the sample difference */ + temp = *entropy->input_ptr[entropy->input_ptr_index[sampn]]++; + + if (temp & 0x8000) { /* instead of temp < 0 */ + temp = (-temp) & 0x7FFF; /* absolute value, mod 2^16 */ + if (temp == 0) /* special case: magnitude = 32768 */ + temp2 = temp = 0x8000; + temp2 = ~temp; /* one's complement of magnitude */ + } else { + temp &= 0x7FFF; /* abs value mod 2^16 */ + temp2 = temp; /* magnitude */ + } + + /* Find the number of bits needed for the magnitude of the difference */ + nbits = 0; + while (temp) { + nbits++; + temp >>= 1; + } + /* Check for out-of-range difference values. + */ + if (nbits > MAX_DIFF_BITS) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Emit the Huffman-coded symbol for the number of bits */ + if (!emit_bits(&state, dctbl->ehufco[nbits], dctbl->ehufsi[nbits])) + return mcu_num; + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + if (nbits && /* emit_bits rejects calls with size 0 */ + nbits != 16) /* special case: no bits should be emitted */ + if (!emit_bits(&state, (unsigned int)temp2, nbits)) + return mcu_num; + } + + /* Completed MCU, so update state */ + cinfo->dest->next_output_byte = state.next_output_byte; + cinfo->dest->free_in_buffer = state.free_in_buffer; + entropy->saved = state.cur; + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + } + + return nMCU; +} + + +/* + * Finish up at the end of a Huffman-compressed scan. + */ + +METHODDEF(void) +finish_pass_huff(j_compress_ptr cinfo) +{ + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr)cinfo->entropy; + working_state state; + + /* Load up working state ... flush_bits needs it */ + state.next_output_byte = cinfo->dest->next_output_byte; + state.free_in_buffer = cinfo->dest->free_in_buffer; + state.cur = entropy->saved; + state.cinfo = cinfo; + + /* Flush out the last data */ + if (!flush_bits(&state)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + + /* Update state */ + cinfo->dest->next_output_byte = state.next_output_byte; + cinfo->dest->free_in_buffer = state.free_in_buffer; + entropy->saved = state.cur; +} + + +/* + * Huffman coding optimization. + * + * We first scan the supplied data and count the number of uses of each symbol + * that is to be Huffman-coded. (This process MUST agree with the code above.) + * Then we build a Huffman coding tree for the observed counts. + * Symbols which are not needed at all for the particular image are not + * assigned any code, which saves space in the DHT marker as well as in + * the compressed data. + */ + +#ifdef ENTROPY_OPT_SUPPORTED + +/* + * Trial-encode nMCU MCUs' worth of Huffman-compressed differences. + * No data is actually output, so no suspension return is possible. + */ + +METHODDEF(JDIMENSION) +encode_mcus_gather(j_compress_ptr cinfo, JDIFFIMAGE diff_buf, + JDIMENSION MCU_row_num, JDIMENSION MCU_col_num, + JDIMENSION nMCU) +{ + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr)cinfo->entropy; + int sampn, ci, yoffset, MCU_width, ptrn; + JDIMENSION mcu_num; + + /* Take care of restart intervals if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + /* Update restart state */ + entropy->restarts_to_go = cinfo->restart_interval; + } + entropy->restarts_to_go--; + } + + /* Set input pointer locations based on MCU_col_num */ + for (ptrn = 0; ptrn < entropy->num_input_ptrs; ptrn++) { + ci = entropy->input_ptr_info[ptrn].ci; + yoffset = entropy->input_ptr_info[ptrn].yoffset; + MCU_width = entropy->input_ptr_info[ptrn].MCU_width; + entropy->input_ptr[ptrn] = + diff_buf[ci][MCU_row_num + yoffset] + (MCU_col_num * MCU_width); + } + + for (mcu_num = 0; mcu_num < nMCU; mcu_num++) { + + /* Inner loop handles the samples in the MCU */ + for (sampn = 0; sampn < cinfo->blocks_in_MCU; sampn++) { + register int temp; + register int nbits; + long *counts = entropy->cur_counts[sampn]; + + /* Encode the difference per section H.1.2.2 */ + + /* Input the sample difference */ + temp = *entropy->input_ptr[entropy->input_ptr_index[sampn]]++; + + if (temp & 0x8000) { /* instead of temp < 0 */ + temp = (-temp) & 0x7FFF; /* absolute value, mod 2^16 */ + if (temp == 0) /* special case: magnitude = 32768 */ + temp = 0x8000; + } else + temp &= 0x7FFF; /* abs value mod 2^16 */ + + /* Find the number of bits needed for the magnitude of the difference */ + nbits = 0; + while (temp) { + nbits++; + temp >>= 1; + } + /* Check for out-of-range difference values. + */ + if (nbits > MAX_DIFF_BITS) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count the Huffman symbol for the number of bits */ + counts[nbits]++; + } + } + + return nMCU; +} + + +/* + * Finish up a statistics-gathering pass and create the new Huffman tables. + */ + +METHODDEF(void) +finish_pass_gather(j_compress_ptr cinfo) +{ + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr)cinfo->entropy; + int ci, dctbl; + jpeg_component_info *compptr; + JHUFF_TBL **htblptr; + boolean did_dc[NUM_HUFF_TBLS]; + + /* It's important not to apply jpeg_gen_optimal_table more than once + * per table, because it clobbers the input frequency counts! + */ + memset(did_dc, 0, sizeof(did_dc)); + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + if (!did_dc[dctbl]) { + htblptr = &cinfo->dc_huff_tbl_ptrs[dctbl]; + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr)cinfo); + jpeg_gen_optimal_table(cinfo, *htblptr, entropy->count_ptrs[dctbl]); + did_dc[dctbl] = TRUE; + } + } +} + + +#endif /* ENTROPY_OPT_SUPPORTED */ + + +/* + * Module initialization routine for Huffman entropy encoding. + */ + +GLOBAL(void) +jinit_lhuff_encoder(j_compress_ptr cinfo) +{ + lhuff_entropy_ptr entropy; + int i; + + entropy = (lhuff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, + sizeof(lhuff_entropy_encoder)); + cinfo->entropy = (struct jpeg_entropy_encoder *)entropy; + entropy->pub.start_pass = start_pass_lhuff; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->derived_tbls[i] = NULL; +#ifdef ENTROPY_OPT_SUPPORTED + entropy->count_ptrs[i] = NULL; +#endif + } +} + +#endif /* C_LOSSLESS_SUPPORTED */ diff --git a/jclossls.c b/jclossls.c new file mode 100644 index 0000000..e9ba92a --- /dev/null +++ b/jclossls.c @@ -0,0 +1,319 @@ +/* + * jclossls.c + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1998, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file contains prediction, sample differencing, and point transform + * routines for the lossless JPEG compressor. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jlossls.h" + +#ifdef C_LOSSLESS_SUPPORTED + + +/************************** Sample differencing **************************/ + +/* + * In order to avoid a performance penalty for checking which predictor is + * being used and which row is being processed for each call of the + * undifferencer, and to promote optimization, we have separate differencing + * functions for each predictor selection value. + * + * We are able to avoid duplicating source code by implementing the predictors + * and differencers as macros. Each of the differencing functions is simply a + * wrapper around a DIFFERENCE macro with the appropriate PREDICTOR macro + * passed as an argument. + */ + +/* Forward declarations */ +LOCAL(void) reset_predictor(j_compress_ptr cinfo, int ci); + + +/* Predictor for the first column of the first row: 2^(P-Pt-1) */ +#define INITIAL_PREDICTORx (1 << (cinfo->data_precision - cinfo->Al - 1)) + +/* Predictor for the first column of the remaining rows: Rb */ +#define INITIAL_PREDICTOR2 prev_row[0] + + +/* + * 1-Dimensional differencer routine. + * + * This macro implements the 1-D horizontal predictor (1). INITIAL_PREDICTOR + * is used as the special case predictor for the first column, which must be + * either INITIAL_PREDICTOR2 or INITIAL_PREDICTORx. The remaining samples + * use PREDICTOR1. + */ + +#define DIFFERENCE_1D(INITIAL_PREDICTOR) \ + lossless_comp_ptr losslessc = (lossless_comp_ptr)cinfo->fdct; \ + boolean restart = FALSE; \ + int samp, Ra; \ + \ + samp = *input_buf++; \ + *diff_buf++ = samp - INITIAL_PREDICTOR; \ + \ + while (--width) { \ + Ra = samp; \ + samp = *input_buf++; \ + *diff_buf++ = samp - PREDICTOR1; \ + } \ + \ + /* Account for restart interval (no-op if not using restarts) */ \ + if (cinfo->restart_interval) { \ + if (--(losslessc->restart_rows_to_go[ci]) == 0) { \ + reset_predictor(cinfo, ci); \ + restart = TRUE; \ + } \ + } + + +/* + * 2-Dimensional differencer routine. + * + * This macro implements the 2-D horizontal predictors (#2-7). PREDICTOR2 is + * used as the special case predictor for the first column. The remaining + * samples use PREDICTOR, which is a function of Ra, Rb, and Rc. + * + * Because prev_row and output_buf may point to the same storage area (in an + * interleaved image with Vi=1, for example), we must take care to buffer Rb/Rc + * before writing the current reconstructed sample value into output_buf. + */ + +#define DIFFERENCE_2D(PREDICTOR) \ + lossless_comp_ptr losslessc = (lossless_comp_ptr)cinfo->fdct; \ + int samp, Ra, Rb, Rc; \ + \ + Rb = *prev_row++; \ + samp = *input_buf++; \ + *diff_buf++ = samp - PREDICTOR2; \ + \ + while (--width) { \ + Rc = Rb; \ + Rb = *prev_row++; \ + Ra = samp; \ + samp = *input_buf++; \ + *diff_buf++ = samp - PREDICTOR; \ + } \ + \ + /* Account for restart interval (no-op if not using restarts) */ \ + if (cinfo->restart_interval) { \ + if (--losslessc->restart_rows_to_go[ci] == 0) \ + reset_predictor(cinfo, ci); \ + } + + +/* + * Differencers for the second and subsequent rows in a scan or restart + * interval. The first sample in the row is differenced using the vertical + * predictor (2). The rest of the samples are differenced using the predictor + * specified in the scan header. + */ + +METHODDEF(void) +jpeg_difference1(j_compress_ptr cinfo, int ci, + _JSAMPROW input_buf, _JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_1D(INITIAL_PREDICTOR2); + (void)(restart); +} + +METHODDEF(void) +jpeg_difference2(j_compress_ptr cinfo, int ci, + _JSAMPROW input_buf, _JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR2); + (void)(Ra); + (void)(Rc); +} + +METHODDEF(void) +jpeg_difference3(j_compress_ptr cinfo, int ci, + _JSAMPROW input_buf, _JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR3); + (void)(Ra); +} + +METHODDEF(void) +jpeg_difference4(j_compress_ptr cinfo, int ci, + _JSAMPROW input_buf, _JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR4); +} + +METHODDEF(void) +jpeg_difference5(j_compress_ptr cinfo, int ci, + _JSAMPROW input_buf, _JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR5); +} + +METHODDEF(void) +jpeg_difference6(j_compress_ptr cinfo, int ci, + _JSAMPROW input_buf, _JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR6); +} + +METHODDEF(void) +jpeg_difference7(j_compress_ptr cinfo, int ci, + _JSAMPROW input_buf, _JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR7); + (void)(Rc); +} + + +/* + * Differencer for the first row in a scan or restart interval. The first + * sample in the row is differenced using the special predictor constant + * x = 2 ^ (P-Pt-1). The rest of the samples are differenced using the + * 1-D horizontal predictor (1). + */ + +METHODDEF(void) +jpeg_difference_first_row(j_compress_ptr cinfo, int ci, + _JSAMPROW input_buf, _JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_1D(INITIAL_PREDICTORx); + + /* + * Now that we have differenced the first row, we want to use the + * differencer that corresponds to the predictor specified in the + * scan header. + * + * Note that we don't do this if we have just reset the predictor + * for a new restart interval. + */ + if (!restart) { + switch (cinfo->Ss) { + case 1: + losslessc->predict_difference[ci] = jpeg_difference1; + break; + case 2: + losslessc->predict_difference[ci] = jpeg_difference2; + break; + case 3: + losslessc->predict_difference[ci] = jpeg_difference3; + break; + case 4: + losslessc->predict_difference[ci] = jpeg_difference4; + break; + case 5: + losslessc->predict_difference[ci] = jpeg_difference5; + break; + case 6: + losslessc->predict_difference[ci] = jpeg_difference6; + break; + case 7: + losslessc->predict_difference[ci] = jpeg_difference7; + break; + } + } +} + +/* + * Reset predictor at the start of a pass or restart interval. + */ + +LOCAL(void) +reset_predictor(j_compress_ptr cinfo, int ci) +{ + lossless_comp_ptr losslessc = (lossless_comp_ptr)cinfo->fdct; + + /* Initialize restart counter */ + losslessc->restart_rows_to_go[ci] = + cinfo->restart_interval / cinfo->MCUs_per_row; + + /* Set difference function to first row function */ + losslessc->predict_difference[ci] = jpeg_difference_first_row; +} + + +/********************** Sample downscaling by 2^Pt ***********************/ + +METHODDEF(void) +simple_downscale(j_compress_ptr cinfo, + _JSAMPROW input_buf, _JSAMPROW output_buf, JDIMENSION width) +{ + do { + *output_buf++ = (_JSAMPLE)RIGHT_SHIFT(*input_buf++, cinfo->Al); + } while (--width); +} + + +METHODDEF(void) +noscale(j_compress_ptr cinfo, + _JSAMPROW input_buf, _JSAMPROW output_buf, JDIMENSION width) +{ + memcpy(output_buf, input_buf, width * sizeof(_JSAMPLE)); +} + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_lossless(j_compress_ptr cinfo) +{ + lossless_comp_ptr losslessc = (lossless_comp_ptr)cinfo->fdct; + int ci; + + /* Set scaler function based on Pt */ + if (cinfo->Al) + losslessc->scaler_scale = simple_downscale; + else + losslessc->scaler_scale = noscale; + + /* Check that the restart interval is an integer multiple of the number + * of MCUs in an MCU row. + */ + if (cinfo->restart_interval % cinfo->MCUs_per_row != 0) + ERREXIT2(cinfo, JERR_BAD_RESTART, + cinfo->restart_interval, cinfo->MCUs_per_row); + + /* Set predictors for start of pass */ + for (ci = 0; ci < cinfo->num_components; ci++) + reset_predictor(cinfo, ci); +} + + +/* + * Initialize the lossless compressor. + */ + +GLOBAL(void) +_jinit_lossless_compressor(j_compress_ptr cinfo) +{ + lossless_comp_ptr losslessc; + + /* Create subobject in permanent pool */ + losslessc = (lossless_comp_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_PERMANENT, + sizeof(jpeg_lossless_compressor)); + cinfo->fdct = (struct jpeg_forward_dct *)losslessc; + losslessc->pub.start_pass = start_pass_lossless; +} + +#endif /* C_LOSSLESS_SUPPORTED */ diff --git a/jcmainct.c b/jcmainct.c index 3f23028..fe8fc0b 100644 --- a/jcmainct.c +++ b/jcmainct.c @@ -3,8 +3,10 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. - * It was modified by The libjpeg-turbo Project to include only code relevant - * to libjpeg-turbo. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -16,8 +18,11 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" +#include "jsamplecomp.h" +#if BITS_IN_JSAMPLE != 16 || defined(C_LOSSLESS_SUPPORTED) + /* Private buffer controller object */ typedef struct { @@ -32,7 +37,7 @@ typedef struct { * (we allocate one for each component). In the full-image case, this * points to the currently accessible strips of the virtual arrays. */ - JSAMPARRAY buffer[MAX_COMPONENTS]; + _JSAMPARRAY buffer[MAX_COMPONENTS]; } my_main_controller; typedef my_main_controller *my_main_ptr; @@ -40,7 +45,7 @@ typedef my_main_controller *my_main_ptr; /* Forward declarations */ METHODDEF(void) process_data_simple_main(j_compress_ptr cinfo, - JSAMPARRAY input_buf, + _JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail); @@ -65,7 +70,7 @@ start_pass_main(j_compress_ptr cinfo, J_BUF_MODE pass_mode) main_ptr->rowgroup_ctr = 0; main_ptr->suspended = FALSE; main_ptr->pass_mode = pass_mode; /* save mode for use by process_data */ - main_ptr->pub.process_data = process_data_simple_main; + main_ptr->pub._process_data = process_data_simple_main; } @@ -76,28 +81,28 @@ start_pass_main(j_compress_ptr cinfo, J_BUF_MODE pass_mode) */ METHODDEF(void) -process_data_simple_main(j_compress_ptr cinfo, JSAMPARRAY input_buf, +process_data_simple_main(j_compress_ptr cinfo, _JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail) { my_main_ptr main_ptr = (my_main_ptr)cinfo->main; + JDIMENSION data_unit = cinfo->master->lossless ? 1 : DCTSIZE; while (main_ptr->cur_iMCU_row < cinfo->total_iMCU_rows) { /* Read input data if we haven't filled the main buffer yet */ - if (main_ptr->rowgroup_ctr < DCTSIZE) - (*cinfo->prep->pre_process_data) (cinfo, input_buf, in_row_ctr, - in_rows_avail, main_ptr->buffer, - &main_ptr->rowgroup_ctr, - (JDIMENSION)DCTSIZE); + if (main_ptr->rowgroup_ctr < data_unit) + (*cinfo->prep->_pre_process_data) (cinfo, input_buf, in_row_ctr, + in_rows_avail, main_ptr->buffer, + &main_ptr->rowgroup_ctr, data_unit); /* If we don't have a full iMCU row buffered, return to application for * more data. Note that preprocessor will always pad to fill the iMCU row * at the bottom of the image. */ - if (main_ptr->rowgroup_ctr != DCTSIZE) + if (main_ptr->rowgroup_ctr != data_unit) return; /* Send the completed row to the compressor */ - if (!(*cinfo->coef->compress_data) (cinfo, main_ptr->buffer)) { + if (!(*cinfo->coef->_compress_data) (cinfo, main_ptr->buffer)) { /* If compressor did not consume the whole row, then we must need to * suspend processing and return to the application. In this situation * we pretend we didn't yet consume the last input row; otherwise, if @@ -128,11 +133,15 @@ process_data_simple_main(j_compress_ptr cinfo, JSAMPARRAY input_buf, */ GLOBAL(void) -jinit_c_main_controller(j_compress_ptr cinfo, boolean need_full_buffer) +_jinit_c_main_controller(j_compress_ptr cinfo, boolean need_full_buffer) { my_main_ptr main_ptr; int ci; jpeg_component_info *compptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); main_ptr = (my_main_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, @@ -153,10 +162,12 @@ jinit_c_main_controller(j_compress_ptr cinfo, boolean need_full_buffer) /* Allocate a strip buffer for each component */ for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - main_ptr->buffer[ci] = (*cinfo->mem->alloc_sarray) + main_ptr->buffer[ci] = (_JSAMPARRAY)(*cinfo->mem->alloc_sarray) ((j_common_ptr)cinfo, JPOOL_IMAGE, - compptr->width_in_blocks * DCTSIZE, - (JDIMENSION)(compptr->v_samp_factor * DCTSIZE)); + compptr->width_in_blocks * data_unit, + (JDIMENSION)(compptr->v_samp_factor * data_unit)); } } } + +#endif /* BITS_IN_JSAMPLE != 16 || defined(C_LOSSLESS_SUPPORTED) */ diff --git a/jcmarker.c b/jcmarker.c index 801fbab..a064d4d 100644 --- a/jcmarker.c +++ b/jcmarker.c @@ -4,8 +4,10 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1998, Thomas G. Lane. * Modified 2003-2010 by Guido Vollbeding. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: - * Copyright (C) 2010, D. R. Commander. + * Copyright (C) 2010, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -15,7 +17,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jpegcomp.h" +#include "jpegapicomp.h" typedef enum { /* JPEG marker codes */ @@ -497,25 +499,26 @@ write_file_header(j_compress_ptr cinfo) METHODDEF(void) write_frame_header(j_compress_ptr cinfo) { - int ci, prec; + int ci, prec = 0; boolean is_baseline; jpeg_component_info *compptr; - /* Emit DQT for each quantization table. - * Note that emit_dqt() suppresses any duplicate tables. - */ - prec = 0; - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - prec += emit_dqt(cinfo, compptr->quant_tbl_no); + if (!cinfo->master->lossless) { + /* Emit DQT for each quantization table. + * Note that emit_dqt() suppresses any duplicate tables. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + prec += emit_dqt(cinfo, compptr->quant_tbl_no); + } + /* now prec is nonzero iff there are any 16-bit quant tables. */ } - /* now prec is nonzero iff there are any 16-bit quant tables. */ /* Check for a non-baseline specification. * Note we assume that Huffman table numbers won't be changed later. */ if (cinfo->arith_code || cinfo->progressive_mode || - cinfo->data_precision != 8) { + cinfo->master->lossless || cinfo->data_precision != 8) { is_baseline = FALSE; } else { is_baseline = TRUE; @@ -540,6 +543,8 @@ write_frame_header(j_compress_ptr cinfo) } else { if (cinfo->progressive_mode) emit_sof(cinfo, M_SOF2); /* SOF code for progressive Huffman */ + else if (cinfo->master->lossless) + emit_sof(cinfo, M_SOF3); /* SOF code for lossless Huffman */ else if (is_baseline) emit_sof(cinfo, M_SOF0); /* SOF code for baseline implementation */ else @@ -574,10 +579,11 @@ write_scan_header(j_compress_ptr cinfo) for (i = 0; i < cinfo->comps_in_scan; i++) { compptr = cinfo->cur_comp_info[i]; /* DC needs no table for refinement scan */ - if (cinfo->Ss == 0 && cinfo->Ah == 0) + if ((cinfo->Ss == 0 && cinfo->Ah == 0) || cinfo->master->lossless) emit_dht(cinfo, compptr->dc_tbl_no, FALSE); - /* AC needs no table when not present */ - if (cinfo->Se) + /* AC needs no table when not present, and lossless mode uses only DC + tables. */ + if (cinfo->Se && !cinfo->master->lossless) emit_dht(cinfo, compptr->ac_tbl_no, TRUE); } } diff --git a/jcmaster.c b/jcmaster.c index c2b2600..7e1408f 100644 --- a/jcmaster.c +++ b/jcmaster.c @@ -4,8 +4,10 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 2003-2010 by Guido Vollbeding. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: - * Copyright (C) 2010, 2016, 2018, D. R. Commander. + * Copyright (C) 2010, 2016, 2018, 2022-2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -18,40 +20,8 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jpegcomp.h" -#include "jconfigint.h" - - -/* Private state */ - -typedef enum { - main_pass, /* input data, also do first output step */ - huff_opt_pass, /* Huffman code optimization pass */ - output_pass /* data output pass */ -} c_pass_type; - -typedef struct { - struct jpeg_comp_master pub; /* public fields */ - - c_pass_type pass_type; /* the type of the current pass */ - - int pass_number; /* # of passes completed */ - int total_passes; /* total # of passes needed */ - - int scan_number; /* current index in scan_info[] */ - - /* - * This is here so we can add libjpeg-turbo version/build information to the - * global string table without introducing a new global symbol. Adding this - * information to the global string table allows one to examine a binary - * object and determine which version of libjpeg-turbo it was built from or - * linked against. - */ - const char *jpeg_version; - -} my_comp_master; - -typedef my_comp_master *my_master_ptr; +#include "jpegapicomp.h" +#include "jcmaster.h" /* @@ -69,11 +39,13 @@ GLOBAL(void) jpeg_calc_jpeg_dimensions(j_compress_ptr cinfo) /* Do computations that are needed before master selection phase */ { + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + /* Hardwire it to "no scaling" */ cinfo->jpeg_width = cinfo->image_width; cinfo->jpeg_height = cinfo->image_height; - cinfo->min_DCT_h_scaled_size = DCTSIZE; - cinfo->min_DCT_v_scaled_size = DCTSIZE; + cinfo->min_DCT_h_scaled_size = data_unit; + cinfo->min_DCT_v_scaled_size = data_unit; } #endif @@ -86,6 +58,7 @@ initial_setup(j_compress_ptr cinfo, boolean transcode_only) jpeg_component_info *compptr; long samplesperrow; JDIMENSION jd_samplesperrow; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; #if JPEG_LIB_VERSION >= 70 #if JPEG_LIB_VERSION >= 80 @@ -110,8 +83,12 @@ initial_setup(j_compress_ptr cinfo, boolean transcode_only) if ((long)jd_samplesperrow != samplesperrow) ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); - /* For now, precision must match compiled-in value... */ - if (cinfo->data_precision != BITS_IN_JSAMPLE) +#ifdef C_LOSSLESS_SUPPORTED + if (cinfo->data_precision != 8 && cinfo->data_precision != 12 && + cinfo->data_precision != 16) +#else + if (cinfo->data_precision != 8 && cinfo->data_precision != 12) +#endif ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); /* Check that number of components won't exceed internal array sizes */ @@ -142,17 +119,17 @@ initial_setup(j_compress_ptr cinfo, boolean transcode_only) compptr->component_index = ci; /* For compression, we never do DCT scaling. */ #if JPEG_LIB_VERSION >= 70 - compptr->DCT_h_scaled_size = compptr->DCT_v_scaled_size = DCTSIZE; + compptr->DCT_h_scaled_size = compptr->DCT_v_scaled_size = data_unit; #else - compptr->DCT_scaled_size = DCTSIZE; + compptr->DCT_scaled_size = data_unit; #endif - /* Size in DCT blocks */ + /* Size in data units */ compptr->width_in_blocks = (JDIMENSION) jdiv_round_up((long)cinfo->_jpeg_width * (long)compptr->h_samp_factor, - (long)(cinfo->max_h_samp_factor * DCTSIZE)); + (long)(cinfo->max_h_samp_factor * data_unit)); compptr->height_in_blocks = (JDIMENSION) jdiv_round_up((long)cinfo->_jpeg_height * (long)compptr->v_samp_factor, - (long)(cinfo->max_v_samp_factor * DCTSIZE)); + (long)(cinfo->max_v_samp_factor * data_unit)); /* Size in samples */ compptr->downsampled_width = (JDIMENSION) jdiv_round_up((long)cinfo->_jpeg_width * (long)compptr->h_samp_factor, @@ -165,15 +142,19 @@ initial_setup(j_compress_ptr cinfo, boolean transcode_only) } /* Compute number of fully interleaved MCU rows (number of times that - * main controller will call coefficient controller). + * main controller will call coefficient or difference controller). */ cinfo->total_iMCU_rows = (JDIMENSION) jdiv_round_up((long)cinfo->_jpeg_height, - (long)(cinfo->max_v_samp_factor * DCTSIZE)); + (long)(cinfo->max_v_samp_factor * data_unit)); } -#ifdef C_MULTISCAN_FILES_SUPPORTED +#if defined(C_MULTISCAN_FILES_SUPPORTED) || defined(C_LOSSLESS_SUPPORTED) +#define NEED_SCAN_SCRIPT +#endif + +#ifdef NEED_SCAN_SCRIPT LOCAL(void) validate_script(j_compress_ptr cinfo) @@ -194,13 +175,29 @@ validate_script(j_compress_ptr cinfo) if (cinfo->num_scans <= 0) ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, 0); +#ifndef C_MULTISCAN_FILES_SUPPORTED + if (cinfo->num_scans > 1) + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + + scanptr = cinfo->scan_info; + if (scanptr->Ss != 0 && scanptr->Se == 0) { +#ifdef C_LOSSLESS_SUPPORTED + cinfo->master->lossless = TRUE; + cinfo->progressive_mode = FALSE; + for (ci = 0; ci < cinfo->num_components; ci++) + component_sent[ci] = FALSE; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } /* For sequential JPEG, all scans must have Ss=0, Se=DCTSIZE2-1; * for progressive JPEG, no scan can have this. */ - scanptr = cinfo->scan_info; - if (scanptr->Ss != 0 || scanptr->Se != DCTSIZE2 - 1) { + else if (scanptr->Ss != 0 || scanptr->Se != DCTSIZE2 - 1) { #ifdef C_PROGRESSIVE_SUPPORTED cinfo->progressive_mode = TRUE; + cinfo->master->lossless = FALSE; last_bitpos_ptr = &last_bitpos[0][0]; for (ci = 0; ci < cinfo->num_components; ci++) for (coefi = 0; coefi < DCTSIZE2; coefi++) @@ -209,7 +206,7 @@ validate_script(j_compress_ptr cinfo) ERREXIT(cinfo, JERR_NOT_COMPILED); #endif } else { - cinfo->progressive_mode = FALSE; + cinfo->progressive_mode = cinfo->master->lossless = FALSE; for (ci = 0; ci < cinfo->num_components; ci++) component_sent[ci] = FALSE; } @@ -241,13 +238,10 @@ validate_script(j_compress_ptr cinfo) * out-of-range reconstructed DC values during the first DC scan, * which might cause problems for some decoders. */ -#if BITS_IN_JSAMPLE == 8 -#define MAX_AH_AL 10 -#else -#define MAX_AH_AL 13 -#endif + int max_Ah_Al = cinfo->data_precision == 12 ? 13 : 10; + if (Ss < 0 || Ss >= DCTSIZE2 || Se < Ss || Se >= DCTSIZE2 || - Ah < 0 || Ah > MAX_AH_AL || Al < 0 || Al > MAX_AH_AL) + Ah < 0 || Ah > max_Ah_Al || Al < 0 || Al > max_Ah_Al) ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); if (Ss == 0) { if (Se != 0) /* DC and AC together not OK */ @@ -275,9 +269,25 @@ validate_script(j_compress_ptr cinfo) } #endif } else { - /* For sequential JPEG, all progression parameters must be these: */ - if (Ss != 0 || Se != DCTSIZE2 - 1 || Ah != 0 || Al != 0) - ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); +#ifdef C_LOSSLESS_SUPPORTED + if (cinfo->master->lossless) { + /* The JPEG spec simply gives the range 0..15 for Al (Pt), but that + * seems wrong: the upper bound ought to depend on data precision. + * Perhaps they really meant 0..N-1 for N-bit precision, which is what + * we allow here. Values greater than or equal to the data precision + * will result in a blank image. + */ + if (Ss < 1 || Ss > 7 || /* predictor selection value */ + Se != 0 || Ah != 0 || + Al < 0 || Al >= cinfo->data_precision) /* point transform */ + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } else +#endif + { + /* For sequential JPEG, all progression parameters must be these: */ + if (Ss != 0 || Se != DCTSIZE2 - 1 || Ah != 0 || Al != 0) + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } /* Make sure components are not sent twice */ for (ci = 0; ci < ncomps; ci++) { thisi = scanptr->component_index[ci]; @@ -309,7 +319,7 @@ validate_script(j_compress_ptr cinfo) } } -#endif /* C_MULTISCAN_FILES_SUPPORTED */ +#endif /* NEED_SCAN_SCRIPT */ LOCAL(void) @@ -318,7 +328,7 @@ select_scan_parameters(j_compress_ptr cinfo) { int ci; -#ifdef C_MULTISCAN_FILES_SUPPORTED +#ifdef NEED_SCAN_SCRIPT if (cinfo->scan_info != NULL) { /* Prepare for current scan --- the script is already validated */ my_master_ptr master = (my_master_ptr)cinfo->master; @@ -344,10 +354,12 @@ select_scan_parameters(j_compress_ptr cinfo) for (ci = 0; ci < cinfo->num_components; ci++) { cinfo->cur_comp_info[ci] = &cinfo->comp_info[ci]; } - cinfo->Ss = 0; - cinfo->Se = DCTSIZE2 - 1; - cinfo->Ah = 0; - cinfo->Al = 0; + if (!cinfo->master->lossless) { + cinfo->Ss = 0; + cinfo->Se = DCTSIZE2 - 1; + cinfo->Ah = 0; + cinfo->Al = 0; + } } } @@ -359,6 +371,7 @@ per_scan_setup(j_compress_ptr cinfo) { int ci, mcublks, tmp; jpeg_component_info *compptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; if (cinfo->comps_in_scan == 1) { @@ -373,7 +386,7 @@ per_scan_setup(j_compress_ptr cinfo) compptr->MCU_width = 1; compptr->MCU_height = 1; compptr->MCU_blocks = 1; - compptr->MCU_sample_width = DCTSIZE; + compptr->MCU_sample_width = data_unit; compptr->last_col_width = 1; /* For noninterleaved scans, it is convenient to define last_row_height * as the number of block rows present in the last iMCU row. @@ -396,10 +409,10 @@ per_scan_setup(j_compress_ptr cinfo) /* Overall image size in MCUs */ cinfo->MCUs_per_row = (JDIMENSION) jdiv_round_up((long)cinfo->_jpeg_width, - (long)(cinfo->max_h_samp_factor * DCTSIZE)); + (long)(cinfo->max_h_samp_factor * data_unit)); cinfo->MCU_rows_in_scan = (JDIMENSION) jdiv_round_up((long)cinfo->_jpeg_height, - (long)(cinfo->max_v_samp_factor * DCTSIZE)); + (long)(cinfo->max_v_samp_factor * data_unit)); cinfo->blocks_in_MCU = 0; @@ -409,7 +422,7 @@ per_scan_setup(j_compress_ptr cinfo) compptr->MCU_width = compptr->h_samp_factor; compptr->MCU_height = compptr->v_samp_factor; compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height; - compptr->MCU_sample_width = compptr->MCU_width * DCTSIZE; + compptr->MCU_sample_width = compptr->MCU_width * data_unit; /* Figure number of non-dummy blocks in last MCU column & row */ tmp = (int)(compptr->width_in_blocks % compptr->MCU_width); if (tmp == 0) tmp = compptr->MCU_width; @@ -481,7 +494,8 @@ prepare_for_pass(j_compress_ptr cinfo) /* Do Huffman optimization for a scan after the first one. */ select_scan_parameters(cinfo); per_scan_setup(cinfo); - if (cinfo->Ss != 0 || cinfo->Ah == 0 || cinfo->arith_code) { + if (cinfo->Ss != 0 || cinfo->Ah == 0 || cinfo->arith_code || + cinfo->master->lossless) { (*cinfo->entropy->start_pass) (cinfo, TRUE); (*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST); master->pub.call_pass_startup = FALSE; @@ -590,22 +604,15 @@ finish_pass_master(j_compress_ptr cinfo) GLOBAL(void) jinit_c_master_control(j_compress_ptr cinfo, boolean transcode_only) { - my_master_ptr master; + my_master_ptr master = (my_master_ptr)cinfo->master; - master = (my_master_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, - sizeof(my_comp_master)); - cinfo->master = (struct jpeg_comp_master *)master; master->pub.prepare_for_pass = prepare_for_pass; master->pub.pass_startup = pass_startup; master->pub.finish_pass = finish_pass_master; master->pub.is_last_pass = FALSE; - /* Validate parameters, determine derived values */ - initial_setup(cinfo, transcode_only); - if (cinfo->scan_info != NULL) { -#ifdef C_MULTISCAN_FILES_SUPPORTED +#ifdef NEED_SCAN_SCRIPT validate_script(cinfo); #else ERREXIT(cinfo, JERR_NOT_COMPILED); @@ -615,8 +622,33 @@ jinit_c_master_control(j_compress_ptr cinfo, boolean transcode_only) cinfo->num_scans = 1; } - if (cinfo->progressive_mode && !cinfo->arith_code) /* TEMPORARY HACK ??? */ - cinfo->optimize_coding = TRUE; /* assume default tables no good for progressive mode */ + /* Disable smoothing and subsampling in lossless mode, since those are lossy + * algorithms. Set the JPEG colorspace to the input colorspace. Disable raw + * (downsampled) data input, because it isn't particularly useful without + * subsampling and has not been tested in lossless mode. + */ + if (cinfo->master->lossless) { + int ci; + jpeg_component_info *compptr; + + cinfo->raw_data_in = FALSE; + cinfo->smoothing_factor = 0; + jpeg_default_colorspace(cinfo); + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) + compptr->h_samp_factor = compptr->v_samp_factor = 1; + } + + /* Validate parameters, determine derived values */ + initial_setup(cinfo, transcode_only); + + if (cinfo->master->lossless || /* TEMPORARY HACK ??? */ + (cinfo->progressive_mode && !cinfo->arith_code)) + cinfo->optimize_coding = TRUE; /* assume default tables no good for + progressive mode or lossless mode */ + if (cinfo->data_precision == 12 && !cinfo->arith_code) + cinfo->optimize_coding = TRUE; /* assume default tables no good for 12-bit + data precision */ /* Initialize my private state */ if (transcode_only) { diff --git a/jcmaster.h b/jcmaster.h new file mode 100644 index 0000000..3b13289 --- /dev/null +++ b/jcmaster.h @@ -0,0 +1,43 @@ +/* + * jcmaster.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1991-1995, Thomas G. Lane. + * libjpeg-turbo Modifications: + * Copyright (C) 2016, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file contains master control structure for the JPEG compressor. + */ + +/* Private state */ + +typedef enum { + main_pass, /* input data, also do first output step */ + huff_opt_pass, /* Huffman code optimization pass */ + output_pass /* data output pass */ +} c_pass_type; + +typedef struct { + struct jpeg_comp_master pub; /* public fields */ + + c_pass_type pass_type; /* the type of the current pass */ + + int pass_number; /* # of passes completed */ + int total_passes; /* total # of passes needed */ + + int scan_number; /* current index in scan_info[] */ + + /* + * This is here so we can add libjpeg-turbo version/build information to the + * global string table without introducing a new global symbol. Adding this + * information to the global string table allows one to examine a binary + * object and determine which version of libjpeg-turbo it was built from or + * linked against. + */ + const char *jpeg_version; + +} my_comp_master; + +typedef my_comp_master *my_master_ptr; diff --git a/jconfig.h.in b/jconfig.h.in index e018012..6cb8296 100644 --- a/jconfig.h.in +++ b/jconfig.h.in @@ -9,29 +9,52 @@ /* libjpeg-turbo version in integer form */ #define LIBJPEG_TURBO_VERSION_NUMBER @LIBJPEG_TURBO_VERSION_NUMBER@ -/* Support arithmetic encoding */ +/* Support arithmetic encoding when using 8-bit samples */ #cmakedefine C_ARITH_CODING_SUPPORTED 1 -/* Support arithmetic decoding */ +/* Support arithmetic decoding when using 8-bit samples */ #cmakedefine D_ARITH_CODING_SUPPORTED 1 /* Support in-memory source/destination managers */ -#cmakedefine MEM_SRCDST_SUPPORTED 1 +#define MEM_SRCDST_SUPPORTED 1 -/* Use accelerated SIMD routines. */ +/* Use accelerated SIMD routines when using 8-bit samples */ #cmakedefine WITH_SIMD 1 -/* - * Define BITS_IN_JSAMPLE as either - * 8 for 8-bit sample values (the usual setting) - * 12 for 12-bit sample values - * Only 8 and 12 are legal data precisions for lossy JPEG according to the - * JPEG standard, and the IJG code does not support anything else! - * We do not support run-time selection of data precision, sorry. +/* This version of libjpeg-turbo supports run-time selection of data precision, + * so BITS_IN_JSAMPLE is no longer used to specify the data precision at build + * time. However, some downstream software expects the macro to be defined. + * Since 12-bit data precision is an opt-in feature that requires explicitly + * calling 12-bit-specific libjpeg API functions and using 12-bit-specific data + * types, the unmodified portion of the libjpeg API still behaves as if it were + * built for 8-bit precision, and JSAMPLE is still literally an 8-bit data + * type. Thus, it is correct to define BITS_IN_JSAMPLE to 8 here. */ +#ifndef BITS_IN_JSAMPLE +#define BITS_IN_JSAMPLE 8 +#endif -#define BITS_IN_JSAMPLE @BITS_IN_JSAMPLE@ /* use 8 or 12 */ +#ifdef _WIN32 + +#undef RIGHT_SHIFT_IS_UNSIGNED + +/* Define "boolean" as unsigned char, not int, per Windows custom */ +#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */ +typedef unsigned char boolean; +#endif +#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */ + +/* Define "INT32" as int, not long, per Windows custom */ +#if !(defined(_BASETSD_H_) || defined(_BASETSD_H)) /* don't conflict if basetsd.h already read */ +typedef short INT16; +typedef signed int INT32; +#endif +#define XMD_H /* prevent jmorecfg.h from redefining it */ + +#else /* Define if your (broken) compiler shifts signed values as if they were unsigned. */ #cmakedefine RIGHT_SHIFT_IS_UNSIGNED 1 + +#endif diff --git a/jconfigint.h.in b/jconfigint.h.in index d087d7b..e7e66e7 100644 --- a/jconfigint.h.in +++ b/jconfigint.h.in @@ -42,3 +42,32 @@ #else #define FALLTHROUGH #endif + +/* + * Define BITS_IN_JSAMPLE as either + * 8 for 8-bit sample values (the usual setting) + * 12 for 12-bit sample values + * Only 8 and 12 are legal data precisions for lossy JPEG according to the + * JPEG standard, and the IJG code does not support anything else! + */ + +#ifndef BITS_IN_JSAMPLE +#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */ +#endif + +#undef C_ARITH_CODING_SUPPORTED +#undef D_ARITH_CODING_SUPPORTED +#undef WITH_SIMD + +#if BITS_IN_JSAMPLE == 8 + +/* Support arithmetic encoding */ +#cmakedefine C_ARITH_CODING_SUPPORTED 1 + +/* Support arithmetic decoding */ +#cmakedefine D_ARITH_CODING_SUPPORTED 1 + +/* Use accelerated SIMD routines. */ +#cmakedefine WITH_SIMD 1 + +#endif diff --git a/jcparam.c b/jcparam.c index 5bc7174..d1dee4d 100644 --- a/jcparam.c +++ b/jcparam.c @@ -4,8 +4,10 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1998, Thomas G. Lane. * Modified 2003-2008 by Guido Vollbeding. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: - * Copyright (C) 2009-2011, 2018, D. R. Commander. + * Copyright (C) 2009-2011, 2018, 2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -202,7 +204,6 @@ jpeg_set_defaults(j_compress_ptr cinfo) cinfo->scale_num = 1; /* 1:1 scaling */ cinfo->scale_denom = 1; #endif - cinfo->data_precision = BITS_IN_JSAMPLE; /* Set up two quantization tables using default quality of 75 */ jpeg_set_quality(cinfo, 75, TRUE); /* Set up two Huffman tables */ @@ -232,7 +233,7 @@ jpeg_set_defaults(j_compress_ptr cinfo) * tables will be computed. This test can be removed if default tables * are supplied that are valid for the desired precision. */ - if (cinfo->data_precision > 8) + if (cinfo->data_precision == 12 && !cinfo->arith_code) cinfo->optimize_coding = TRUE; /* By default, use the simpler non-cosited sampling alignment */ @@ -296,7 +297,10 @@ jpeg_default_colorspace(j_compress_ptr cinfo) case JCS_EXT_BGRA: case JCS_EXT_ABGR: case JCS_EXT_ARGB: - jpeg_set_colorspace(cinfo, JCS_YCbCr); + if (cinfo->master->lossless) + jpeg_set_colorspace(cinfo, JCS_RGB); + else + jpeg_set_colorspace(cinfo, JCS_YCbCr); break; case JCS_YCbCr: jpeg_set_colorspace(cinfo, JCS_YCbCr); @@ -475,6 +479,11 @@ jpeg_simple_progression(j_compress_ptr cinfo) if (cinfo->global_state != CSTATE_START) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->master->lossless) { + cinfo->master->lossless = FALSE; + jpeg_default_colorspace(cinfo); + } + /* Figure space needed for script. Calculation must match code below! */ if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) { /* Custom script for YCbCr color images. */ @@ -539,3 +548,38 @@ jpeg_simple_progression(j_compress_ptr cinfo) } #endif /* C_PROGRESSIVE_SUPPORTED */ + + +#ifdef C_LOSSLESS_SUPPORTED + +/* + * Enable lossless mode. + */ + +GLOBAL(void) +jpeg_enable_lossless(j_compress_ptr cinfo, int predictor_selection_value, + int point_transform) +{ + /* Safety check to ensure start_compress not called yet. */ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + cinfo->master->lossless = TRUE; + cinfo->Ss = predictor_selection_value; + cinfo->Se = 0; + cinfo->Ah = 0; + cinfo->Al = point_transform; + + /* The JPEG spec simply gives the range 0..15 for Al (Pt), but that seems + * wrong: the upper bound ought to depend on data precision. Perhaps they + * really meant 0..N-1 for N-bit precision, which is what we allow here. + * Values greater than or equal to the data precision will result in a blank + * image. + */ + if (cinfo->Ss < 1 || cinfo->Ss > 7 || + cinfo->Al < 0 || cinfo->Al >= cinfo->data_precision) + ERREXIT4(cinfo, JERR_BAD_PROGRESSION, + cinfo->Ss, cinfo->Se, cinfo->Ah, cinfo->Al); +} + +#endif /* C_LOSSLESS_SUPPORTED */ diff --git a/jcphuff.c b/jcphuff.c index 872e570..56e63bd 100644 --- a/jcphuff.c +++ b/jcphuff.c @@ -3,9 +3,11 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1995-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2011, 2015, 2018, 2021-2022, D. R. Commander. - * Copyright (C) 2016, 2018, Matthieu Darbois. + * Copyright (C) 2016, 2018, 2022, Matthieu Darbois. * Copyright (C) 2020, Arm Limited. * Copyright (C) 2021, Alex Richardson. * For conditions of distribution and use, see the accompanying README.ijg @@ -21,8 +23,11 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" +#ifdef WITH_SIMD #include "jsimd.h" -#include "jconfigint.h" +#else +#include "jchuff.h" /* Declarations shared with jc*huff.c */ +#endif #include #ifdef HAVE_INTRIN_H @@ -83,11 +88,11 @@ typedef struct { /* Pointer to routine to prepare data for encode_mcu_AC_first() */ void (*AC_first_prepare) (const JCOEF *block, const int *jpeg_natural_order_start, int Sl, - int Al, JCOEF *values, size_t *zerobits); + int Al, UJCOEF *values, size_t *zerobits); /* Pointer to routine to prepare data for encode_mcu_AC_refine() */ int (*AC_refine_prepare) (const JCOEF *block, const int *jpeg_natural_order_start, int Sl, - int Al, JCOEF *absvalues, size_t *bits); + int Al, UJCOEF *absvalues, size_t *bits); /* Mode flag: TRUE for optimization, FALSE for actual data output */ boolean gather_statistics; @@ -157,14 +162,14 @@ METHODDEF(boolean) encode_mcu_DC_first(j_compress_ptr cinfo, JBLOCKROW *MCU_data); METHODDEF(void) encode_mcu_AC_first_prepare (const JCOEF *block, const int *jpeg_natural_order_start, int Sl, int Al, - JCOEF *values, size_t *zerobits); + UJCOEF *values, size_t *zerobits); METHODDEF(boolean) encode_mcu_AC_first(j_compress_ptr cinfo, JBLOCKROW *MCU_data); METHODDEF(boolean) encode_mcu_DC_refine(j_compress_ptr cinfo, JBLOCKROW *MCU_data); METHODDEF(int) encode_mcu_AC_refine_prepare (const JCOEF *block, const int *jpeg_natural_order_start, int Sl, int Al, - JCOEF *absvalues, size_t *bits); + UJCOEF *absvalues, size_t *bits); METHODDEF(boolean) encode_mcu_AC_refine(j_compress_ptr cinfo, JBLOCKROW *MCU_data); METHODDEF(void) finish_pass_phuff(j_compress_ptr cinfo); @@ -224,18 +229,22 @@ start_pass_phuff(j_compress_ptr cinfo, boolean gather_statistics) entropy->pub.encode_mcu = encode_mcu_DC_first; else entropy->pub.encode_mcu = encode_mcu_AC_first; +#ifdef WITH_SIMD if (jsimd_can_encode_mcu_AC_first_prepare()) entropy->AC_first_prepare = jsimd_encode_mcu_AC_first_prepare; else +#endif entropy->AC_first_prepare = encode_mcu_AC_first_prepare; } else { if (is_DC_band) entropy->pub.encode_mcu = encode_mcu_DC_refine; else { entropy->pub.encode_mcu = encode_mcu_AC_refine; +#ifdef WITH_SIMD if (jsimd_can_encode_mcu_AC_refine_prepare()) entropy->AC_refine_prepare = jsimd_encode_mcu_AC_refine_prepare; else +#endif entropy->AC_refine_prepare = encode_mcu_AC_refine_prepare; /* AC refinement needs a correction bit buffer */ if (entropy->bit_buffer == NULL) @@ -490,6 +499,7 @@ encode_mcu_DC_first(j_compress_ptr cinfo, JBLOCKROW *MCU_data) JBLOCKROW block; jpeg_component_info *compptr; ISHIFT_TEMPS + int max_coef_bits = cinfo->data_precision + 2; entropy->next_output_byte = cinfo->dest->next_output_byte; entropy->free_in_buffer = cinfo->dest->free_in_buffer; @@ -532,7 +542,7 @@ encode_mcu_DC_first(j_compress_ptr cinfo, JBLOCKROW *MCU_data) /* Check for out-of-range coefficient values. * Since we're encoding a difference, the range limit is twice as much. */ - if (nbits > MAX_COEF_BITS + 1) + if (nbits > max_coef_bits + 1) ERREXIT(cinfo, JERR_BAD_DCT_COEF); /* Count/emit the Huffman-coded symbol for the number of bits */ @@ -584,8 +594,8 @@ encode_mcu_DC_first(j_compress_ptr cinfo, JBLOCKROW *MCU_data) continue; \ /* For a negative coef, want temp2 = bitwise complement of abs(coef) */ \ temp2 ^= temp; \ - values[k] = (JCOEF)temp; \ - values[k + DCTSIZE2] = (JCOEF)temp2; \ + values[k] = (UJCOEF)temp; \ + values[k + DCTSIZE2] = (UJCOEF)temp2; \ zerobits |= ((size_t)1U) << k; \ } \ } @@ -593,7 +603,7 @@ encode_mcu_DC_first(j_compress_ptr cinfo, JBLOCKROW *MCU_data) METHODDEF(void) encode_mcu_AC_first_prepare(const JCOEF *block, const int *jpeg_natural_order_start, int Sl, - int Al, JCOEF *values, size_t *bits) + int Al, UJCOEF *values, size_t *bits) { register int k, temp, temp2; size_t zerobits = 0U; @@ -643,7 +653,7 @@ label \ /* Find the number of bits needed for the magnitude of the coefficient */ \ nbits = JPEG_NBITS_NONZERO(temp); /* there must be at least one 1 bit */ \ /* Check for out-of-range coefficient values */ \ - if (nbits > MAX_COEF_BITS) \ + if (nbits > max_coef_bits) \ ERREXIT(cinfo, JERR_BAD_DCT_COEF); \ \ /* Count/emit Huffman symbol for run length / number of bits */ \ @@ -666,11 +676,12 @@ encode_mcu_AC_first(j_compress_ptr cinfo, JBLOCKROW *MCU_data) register int nbits, r; int Sl = cinfo->Se - cinfo->Ss + 1; int Al = cinfo->Al; - JCOEF values_unaligned[2 * DCTSIZE2 + 15]; - JCOEF *values; - const JCOEF *cvalue; + UJCOEF values_unaligned[2 * DCTSIZE2 + 15]; + UJCOEF *values; + const UJCOEF *cvalue; size_t zerobits; size_t bits[8 / SIZEOF_SIZE_T]; + int max_coef_bits = cinfo->data_precision + 2; entropy->next_output_byte = cinfo->dest->next_output_byte; entropy->free_in_buffer = cinfo->dest->free_in_buffer; @@ -681,7 +692,7 @@ encode_mcu_AC_first(j_compress_ptr cinfo, JBLOCKROW *MCU_data) emit_restart(entropy, entropy->next_restart_num); #ifdef WITH_SIMD - cvalue = values = (JCOEF *)PAD((JUINTPTR)values_unaligned, 16); + cvalue = values = (UJCOEF *)PAD((JUINTPTR)values_unaligned, 16); #else /* Not using SIMD, so alignment is not needed */ cvalue = values = values_unaligned; @@ -815,7 +826,7 @@ encode_mcu_DC_refine(j_compress_ptr cinfo, JBLOCKROW *MCU_data) zerobits |= ((size_t)1U) << k; \ signbits |= ((size_t)(temp2 + 1)) << k; \ } \ - absvalues[k] = (JCOEF)temp; /* save abs value for main pass */ \ + absvalues[k] = (UJCOEF)temp; /* save abs value for main pass */ \ if (temp == 1) \ EOB = k + koffset; /* EOB = index of last newly-nonzero coef */ \ } \ @@ -824,7 +835,7 @@ encode_mcu_DC_refine(j_compress_ptr cinfo, JBLOCKROW *MCU_data) METHODDEF(int) encode_mcu_AC_refine_prepare(const JCOEF *block, const int *jpeg_natural_order_start, int Sl, - int Al, JCOEF *absvalues, size_t *bits) + int Al, UJCOEF *absvalues, size_t *bits) { register int k, temp, temp2; int EOB = 0; @@ -931,9 +942,9 @@ encode_mcu_AC_refine(j_compress_ptr cinfo, JBLOCKROW *MCU_data) unsigned int BR; int Sl = cinfo->Se - cinfo->Ss + 1; int Al = cinfo->Al; - JCOEF absvalues_unaligned[DCTSIZE2 + 15]; - JCOEF *absvalues; - const JCOEF *cabsvalue, *EOBPTR; + UJCOEF absvalues_unaligned[DCTSIZE2 + 15]; + UJCOEF *absvalues; + const UJCOEF *cabsvalue, *EOBPTR; size_t zerobits, signbits; size_t bits[16 / SIZEOF_SIZE_T]; @@ -946,7 +957,7 @@ encode_mcu_AC_refine(j_compress_ptr cinfo, JBLOCKROW *MCU_data) emit_restart(entropy, entropy->next_restart_num); #ifdef WITH_SIMD - cabsvalue = absvalues = (JCOEF *)PAD((JUINTPTR)absvalues_unaligned, 16); + cabsvalue = absvalues = (UJCOEF *)PAD((JUINTPTR)absvalues_unaligned, 16); #else /* Not using SIMD, so alignment is not needed */ cabsvalue = absvalues = absvalues_unaligned; diff --git a/jcprepct.c b/jcprepct.c index f27cc34..ac2311c 100644 --- a/jcprepct.c +++ b/jcprepct.c @@ -1,8 +1,10 @@ /* * jcprepct.c * - * This file is part of the Independent JPEG Group's software: + * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg @@ -20,8 +22,11 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" +#include "jsamplecomp.h" +#if BITS_IN_JSAMPLE != 16 || defined(C_LOSSLESS_SUPPORTED) + /* At present, jcsample.c can request context rows only for smoothing. * In the future, we might also need context rows for CCIR601 sampling * or other more-complex downsampling procedures. The code to support @@ -59,7 +64,7 @@ typedef struct { /* Downsampling input buffer. This buffer holds color-converted data * until we have enough to do a downsample step. */ - JSAMPARRAY color_buf[MAX_COMPONENTS]; + _JSAMPARRAY color_buf[MAX_COMPONENTS]; JDIMENSION rows_to_go; /* counts rows remaining in source image */ int next_buf_row; /* index of next row to store in color_buf */ @@ -106,14 +111,14 @@ start_pass_prep(j_compress_ptr cinfo, J_BUF_MODE pass_mode) */ LOCAL(void) -expand_bottom_edge(JSAMPARRAY image_data, JDIMENSION num_cols, int input_rows, +expand_bottom_edge(_JSAMPARRAY image_data, JDIMENSION num_cols, int input_rows, int output_rows) { register int row; for (row = input_rows; row < output_rows; row++) { - jcopy_sample_rows(image_data, input_rows - 1, image_data, row, 1, - num_cols); + _jcopy_sample_rows(image_data, input_rows - 1, image_data, row, 1, + num_cols); } } @@ -128,15 +133,16 @@ expand_bottom_edge(JSAMPARRAY image_data, JDIMENSION num_cols, int input_rows, */ METHODDEF(void) -pre_process_data(j_compress_ptr cinfo, JSAMPARRAY input_buf, +pre_process_data(j_compress_ptr cinfo, _JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail, - JSAMPIMAGE output_buf, JDIMENSION *out_row_group_ctr, + _JSAMPIMAGE output_buf, JDIMENSION *out_row_group_ctr, JDIMENSION out_row_groups_avail) { my_prep_ptr prep = (my_prep_ptr)cinfo->prep; int numrows, ci; JDIMENSION inrows; jpeg_component_info *compptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; while (*in_row_ctr < in_rows_avail && *out_row_group_ctr < out_row_groups_avail) { @@ -144,10 +150,10 @@ pre_process_data(j_compress_ptr cinfo, JSAMPARRAY input_buf, inrows = in_rows_avail - *in_row_ctr; numrows = cinfo->max_v_samp_factor - prep->next_buf_row; numrows = (int)MIN((JDIMENSION)numrows, inrows); - (*cinfo->cconvert->color_convert) (cinfo, input_buf + *in_row_ctr, - prep->color_buf, - (JDIMENSION)prep->next_buf_row, - numrows); + (*cinfo->cconvert->_color_convert) (cinfo, input_buf + *in_row_ctr, + prep->color_buf, + (JDIMENSION)prep->next_buf_row, + numrows); *in_row_ctr += numrows; prep->next_buf_row += numrows; prep->rows_to_go -= numrows; @@ -162,9 +168,9 @@ pre_process_data(j_compress_ptr cinfo, JSAMPARRAY input_buf, } /* If we've filled the conversion buffer, empty it. */ if (prep->next_buf_row == cinfo->max_v_samp_factor) { - (*cinfo->downsample->downsample) (cinfo, - prep->color_buf, (JDIMENSION)0, - output_buf, *out_row_group_ctr); + (*cinfo->downsample->_downsample) (cinfo, + prep->color_buf, (JDIMENSION)0, + output_buf, *out_row_group_ctr); prep->next_buf_row = 0; (*out_row_group_ctr)++; } @@ -174,7 +180,8 @@ pre_process_data(j_compress_ptr cinfo, JSAMPARRAY input_buf, if (prep->rows_to_go == 0 && *out_row_group_ctr < out_row_groups_avail) { for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - expand_bottom_edge(output_buf[ci], compptr->width_in_blocks * DCTSIZE, + expand_bottom_edge(output_buf[ci], + compptr->width_in_blocks * data_unit, (int)(*out_row_group_ctr * compptr->v_samp_factor), (int)(out_row_groups_avail * compptr->v_samp_factor)); } @@ -192,9 +199,9 @@ pre_process_data(j_compress_ptr cinfo, JSAMPARRAY input_buf, */ METHODDEF(void) -pre_process_context(j_compress_ptr cinfo, JSAMPARRAY input_buf, +pre_process_context(j_compress_ptr cinfo, _JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail, - JSAMPIMAGE output_buf, JDIMENSION *out_row_group_ctr, + _JSAMPIMAGE output_buf, JDIMENSION *out_row_group_ctr, JDIMENSION out_row_groups_avail) { my_prep_ptr prep = (my_prep_ptr)cinfo->prep; @@ -208,17 +215,17 @@ pre_process_context(j_compress_ptr cinfo, JSAMPARRAY input_buf, inrows = in_rows_avail - *in_row_ctr; numrows = prep->next_buf_stop - prep->next_buf_row; numrows = (int)MIN((JDIMENSION)numrows, inrows); - (*cinfo->cconvert->color_convert) (cinfo, input_buf + *in_row_ctr, - prep->color_buf, - (JDIMENSION)prep->next_buf_row, - numrows); + (*cinfo->cconvert->_color_convert) (cinfo, input_buf + *in_row_ctr, + prep->color_buf, + (JDIMENSION)prep->next_buf_row, + numrows); /* Pad at top of image, if first time through */ if (prep->rows_to_go == cinfo->image_height) { for (ci = 0; ci < cinfo->num_components; ci++) { int row; for (row = 1; row <= cinfo->max_v_samp_factor; row++) { - jcopy_sample_rows(prep->color_buf[ci], 0, prep->color_buf[ci], - -row, 1, cinfo->image_width); + _jcopy_sample_rows(prep->color_buf[ci], 0, prep->color_buf[ci], + -row, 1, cinfo->image_width); } } } @@ -240,9 +247,9 @@ pre_process_context(j_compress_ptr cinfo, JSAMPARRAY input_buf, } /* If we've gotten enough data, downsample a row group. */ if (prep->next_buf_row == prep->next_buf_stop) { - (*cinfo->downsample->downsample) (cinfo, prep->color_buf, - (JDIMENSION)prep->this_row_group, - output_buf, *out_row_group_ctr); + (*cinfo->downsample->_downsample) (cinfo, prep->color_buf, + (JDIMENSION)prep->this_row_group, + output_buf, *out_row_group_ctr); (*out_row_group_ctr)++; /* Advance pointers with wraparound as necessary. */ prep->this_row_group += cinfo->max_v_samp_factor; @@ -267,15 +274,16 @@ create_context_buffer(j_compress_ptr cinfo) int rgroup_height = cinfo->max_v_samp_factor; int ci, i; jpeg_component_info *compptr; - JSAMPARRAY true_buffer, fake_buffer; + _JSAMPARRAY true_buffer, fake_buffer; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; /* Grab enough space for fake row pointers for all the components; * we need five row groups' worth of pointers for each component. */ - fake_buffer = (JSAMPARRAY) + fake_buffer = (_JSAMPARRAY) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, (cinfo->num_components * 5 * rgroup_height) * - sizeof(JSAMPROW)); + sizeof(_JSAMPROW)); for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { @@ -283,14 +291,14 @@ create_context_buffer(j_compress_ptr cinfo) * We make the buffer wide enough to allow the downsampler to edge-expand * horizontally within the buffer, if it so chooses. */ - true_buffer = (*cinfo->mem->alloc_sarray) + true_buffer = (_JSAMPARRAY)(*cinfo->mem->alloc_sarray) ((j_common_ptr)cinfo, JPOOL_IMAGE, - (JDIMENSION)(((long)compptr->width_in_blocks * DCTSIZE * + (JDIMENSION)(((long)compptr->width_in_blocks * data_unit * cinfo->max_h_samp_factor) / compptr->h_samp_factor), (JDIMENSION)(3 * rgroup_height)); /* Copy true buffer row pointers into the middle of the fake row array */ memcpy(fake_buffer + rgroup_height, true_buffer, - 3 * rgroup_height * sizeof(JSAMPROW)); + 3 * rgroup_height * sizeof(_JSAMPROW)); /* Fill in the above and below wraparound pointers */ for (i = 0; i < rgroup_height; i++) { fake_buffer[i] = true_buffer[2 * rgroup_height + i]; @@ -309,11 +317,15 @@ create_context_buffer(j_compress_ptr cinfo) */ GLOBAL(void) -jinit_c_prep_controller(j_compress_ptr cinfo, boolean need_full_buffer) +_jinit_c_prep_controller(j_compress_ptr cinfo, boolean need_full_buffer) { my_prep_ptr prep; int ci; jpeg_component_info *compptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); if (need_full_buffer) /* safety check */ ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); @@ -331,21 +343,23 @@ jinit_c_prep_controller(j_compress_ptr cinfo, boolean need_full_buffer) if (cinfo->downsample->need_context_rows) { /* Set up to provide context rows */ #ifdef CONTEXT_ROWS_SUPPORTED - prep->pub.pre_process_data = pre_process_context; + prep->pub._pre_process_data = pre_process_context; create_context_buffer(cinfo); #else ERREXIT(cinfo, JERR_NOT_COMPILED); #endif } else { /* No context, just make it tall enough for one row group */ - prep->pub.pre_process_data = pre_process_data; + prep->pub._pre_process_data = pre_process_data; for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - prep->color_buf[ci] = (*cinfo->mem->alloc_sarray) + prep->color_buf[ci] = (_JSAMPARRAY)(*cinfo->mem->alloc_sarray) ((j_common_ptr)cinfo, JPOOL_IMAGE, - (JDIMENSION)(((long)compptr->width_in_blocks * DCTSIZE * + (JDIMENSION)(((long)compptr->width_in_blocks * data_unit * cinfo->max_h_samp_factor) / compptr->h_samp_factor), (JDIMENSION)cinfo->max_v_samp_factor); } } } + +#endif /* BITS_IN_JSAMPLE != 16 || defined(C_LOSSLESS_SUPPORTED) */ diff --git a/jcsample.c b/jcsample.c index e8515eb..30e6e54 100644 --- a/jcsample.c +++ b/jcsample.c @@ -3,10 +3,12 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1996, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright 2009 Pierre Ossman for Cendio AB * Copyright (C) 2014, MIPS Technologies, Inc., California. - * Copyright (C) 2015, 2019, D. R. Commander. + * Copyright (C) 2015, 2019, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -54,13 +56,16 @@ #include "jinclude.h" #include "jpeglib.h" #include "jsimd.h" +#include "jsamplecomp.h" +#if BITS_IN_JSAMPLE != 16 || defined(C_LOSSLESS_SUPPORTED) + /* Pointer to routine to downsample a single component */ typedef void (*downsample1_ptr) (j_compress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, - JSAMPARRAY output_data); + _JSAMPARRAY input_data, + _JSAMPARRAY output_data); /* Private subobject */ @@ -91,11 +96,11 @@ start_pass_downsample(j_compress_ptr cinfo) */ LOCAL(void) -expand_right_edge(JSAMPARRAY image_data, int num_rows, JDIMENSION input_cols, +expand_right_edge(_JSAMPARRAY image_data, int num_rows, JDIMENSION input_cols, JDIMENSION output_cols) { - register JSAMPROW ptr; - register JSAMPLE pixval; + register _JSAMPROW ptr; + register _JSAMPLE pixval; register int count; int row; int numcols = (int)(output_cols - input_cols); @@ -118,14 +123,14 @@ expand_right_edge(JSAMPARRAY image_data, int num_rows, JDIMENSION input_cols, */ METHODDEF(void) -sep_downsample(j_compress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION in_row_index, JSAMPIMAGE output_buf, +sep_downsample(j_compress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION in_row_index, _JSAMPIMAGE output_buf, JDIMENSION out_row_group_index) { my_downsample_ptr downsample = (my_downsample_ptr)cinfo->downsample; int ci; jpeg_component_info *compptr; - JSAMPARRAY in_ptr, out_ptr; + _JSAMPARRAY in_ptr, out_ptr; for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { @@ -145,12 +150,13 @@ sep_downsample(j_compress_ptr cinfo, JSAMPIMAGE input_buf, METHODDEF(void) int_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY output_data) + _JSAMPARRAY input_data, _JSAMPARRAY output_data) { int inrow, outrow, h_expand, v_expand, numpix, numpix2, h, v; JDIMENSION outcol, outcol_h; /* outcol_h == outcol*h_expand */ - JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; - JSAMPROW inptr, outptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + JDIMENSION output_cols = compptr->width_in_blocks * data_unit; + _JSAMPROW inptr, outptr; JLONG outvalue; h_expand = cinfo->max_h_samp_factor / compptr->h_samp_factor; @@ -177,7 +183,7 @@ int_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, outvalue += (JLONG)(*inptr++); } } - *outptr++ = (JSAMPLE)((outvalue + numpix2) / numpix); + *outptr++ = (_JSAMPLE)((outvalue + numpix2) / numpix); } inrow += v_expand; } @@ -192,14 +198,16 @@ int_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, METHODDEF(void) fullsize_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY output_data) + _JSAMPARRAY input_data, _JSAMPARRAY output_data) { + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + /* Copy the data */ - jcopy_sample_rows(input_data, 0, output_data, 0, cinfo->max_v_samp_factor, - cinfo->image_width); + _jcopy_sample_rows(input_data, 0, output_data, 0, cinfo->max_v_samp_factor, + cinfo->image_width); /* Edge-expand */ expand_right_edge(output_data, cinfo->max_v_samp_factor, cinfo->image_width, - compptr->width_in_blocks * DCTSIZE); + compptr->width_in_blocks * data_unit); } @@ -217,12 +225,13 @@ fullsize_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, METHODDEF(void) h2v1_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY output_data) + _JSAMPARRAY input_data, _JSAMPARRAY output_data) { int outrow; JDIMENSION outcol; - JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; - register JSAMPROW inptr, outptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + JDIMENSION output_cols = compptr->width_in_blocks * data_unit; + register _JSAMPROW inptr, outptr; register int bias; /* Expand input data enough to let all the output samples be generated @@ -237,7 +246,7 @@ h2v1_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, inptr = input_data[outrow]; bias = 0; /* bias = 0,1,0,1,... for successive samples */ for (outcol = 0; outcol < output_cols; outcol++) { - *outptr++ = (JSAMPLE)((inptr[0] + inptr[1] + bias) >> 1); + *outptr++ = (_JSAMPLE)((inptr[0] + inptr[1] + bias) >> 1); bias ^= 1; /* 0=>1, 1=>0 */ inptr += 2; } @@ -253,12 +262,13 @@ h2v1_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, METHODDEF(void) h2v2_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY output_data) + _JSAMPARRAY input_data, _JSAMPARRAY output_data) { int inrow, outrow; JDIMENSION outcol; - JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; - register JSAMPROW inptr0, inptr1, outptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + JDIMENSION output_cols = compptr->width_in_blocks * data_unit; + register _JSAMPROW inptr0, inptr1, outptr; register int bias; /* Expand input data enough to let all the output samples be generated @@ -275,8 +285,8 @@ h2v2_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, inptr1 = input_data[inrow + 1]; bias = 1; /* bias = 1,2,1,2,... for successive samples */ for (outcol = 0; outcol < output_cols; outcol++) { - *outptr++ = - (JSAMPLE)((inptr0[0] + inptr0[1] + inptr1[0] + inptr1[1] + bias) >> 2); + *outptr++ = (_JSAMPLE) + ((inptr0[0] + inptr0[1] + inptr1[0] + inptr1[1] + bias) >> 2); bias ^= 3; /* 1=>2, 2=>1 */ inptr0 += 2; inptr1 += 2; } @@ -295,12 +305,13 @@ h2v2_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, METHODDEF(void) h2v2_smooth_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY output_data) + _JSAMPARRAY input_data, _JSAMPARRAY output_data) { int inrow, outrow; JDIMENSION colctr; - JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; - register JSAMPROW inptr0, inptr1, above_ptr, below_ptr, outptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + JDIMENSION output_cols = compptr->width_in_blocks * data_unit; + register _JSAMPROW inptr0, inptr1, above_ptr, below_ptr, outptr; JLONG membersum, neighsum, memberscale, neighscale; /* Expand input data enough to let all the output samples be generated @@ -341,7 +352,7 @@ h2v2_smooth_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, neighsum += neighsum; neighsum += above_ptr[0] + above_ptr[2] + below_ptr[0] + below_ptr[2]; membersum = membersum * memberscale + neighsum * neighscale; - *outptr++ = (JSAMPLE)((membersum + 32768) >> 16); + *outptr++ = (_JSAMPLE)((membersum + 32768) >> 16); inptr0 += 2; inptr1 += 2; above_ptr += 2; below_ptr += 2; for (colctr = output_cols - 2; colctr > 0; colctr--) { @@ -357,7 +368,7 @@ h2v2_smooth_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, /* form final output scaled up by 2^16 */ membersum = membersum * memberscale + neighsum * neighscale; /* round, descale and output it */ - *outptr++ = (JSAMPLE)((membersum + 32768) >> 16); + *outptr++ = (_JSAMPLE)((membersum + 32768) >> 16); inptr0 += 2; inptr1 += 2; above_ptr += 2; below_ptr += 2; } @@ -368,7 +379,7 @@ h2v2_smooth_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, neighsum += neighsum; neighsum += above_ptr[-1] + above_ptr[1] + below_ptr[-1] + below_ptr[1]; membersum = membersum * memberscale + neighsum * neighscale; - *outptr = (JSAMPLE)((membersum + 32768) >> 16); + *outptr = (_JSAMPLE)((membersum + 32768) >> 16); inrow += 2; } @@ -383,12 +394,13 @@ h2v2_smooth_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, METHODDEF(void) fullsize_smooth_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY output_data) + _JSAMPARRAY input_data, _JSAMPARRAY output_data) { int outrow; JDIMENSION colctr; - JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; - register JSAMPROW inptr, above_ptr, below_ptr, outptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + JDIMENSION output_cols = compptr->width_in_blocks * data_unit; + register _JSAMPROW inptr, above_ptr, below_ptr, outptr; JLONG membersum, neighsum, memberscale, neighscale; int colsum, lastcolsum, nextcolsum; @@ -420,7 +432,7 @@ fullsize_smooth_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, nextcolsum = above_ptr[0] + below_ptr[0] + inptr[0]; neighsum = colsum + (colsum - membersum) + nextcolsum; membersum = membersum * memberscale + neighsum * neighscale; - *outptr++ = (JSAMPLE)((membersum + 32768) >> 16); + *outptr++ = (_JSAMPLE)((membersum + 32768) >> 16); lastcolsum = colsum; colsum = nextcolsum; for (colctr = output_cols - 2; colctr > 0; colctr--) { @@ -429,7 +441,7 @@ fullsize_smooth_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, nextcolsum = above_ptr[0] + below_ptr[0] + inptr[0]; neighsum = lastcolsum + (colsum - membersum) + nextcolsum; membersum = membersum * memberscale + neighsum * neighscale; - *outptr++ = (JSAMPLE)((membersum + 32768) >> 16); + *outptr++ = (_JSAMPLE)((membersum + 32768) >> 16); lastcolsum = colsum; colsum = nextcolsum; } @@ -437,7 +449,7 @@ fullsize_smooth_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, membersum = *inptr; neighsum = lastcolsum + (colsum - membersum) + colsum; membersum = membersum * memberscale + neighsum * neighscale; - *outptr = (JSAMPLE)((membersum + 32768) >> 16); + *outptr = (_JSAMPLE)((membersum + 32768) >> 16); } } @@ -451,19 +463,22 @@ fullsize_smooth_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, */ GLOBAL(void) -jinit_downsampler(j_compress_ptr cinfo) +_jinit_downsampler(j_compress_ptr cinfo) { my_downsample_ptr downsample; int ci; jpeg_component_info *compptr; boolean smoothok = TRUE; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + downsample = (my_downsample_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, sizeof(my_downsampler)); cinfo->downsample = (struct jpeg_downsampler *)downsample; downsample->pub.start_pass = start_pass_downsample; - downsample->pub.downsample = sep_downsample; + downsample->pub._downsample = sep_downsample; downsample->pub.need_context_rows = FALSE; if (cinfo->CCIR601_sampling) @@ -484,15 +499,17 @@ jinit_downsampler(j_compress_ptr cinfo) } else if (compptr->h_samp_factor * 2 == cinfo->max_h_samp_factor && compptr->v_samp_factor == cinfo->max_v_samp_factor) { smoothok = FALSE; +#ifdef WITH_SIMD if (jsimd_can_h2v1_downsample()) downsample->methods[ci] = jsimd_h2v1_downsample; else +#endif downsample->methods[ci] = h2v1_downsample; } else if (compptr->h_samp_factor * 2 == cinfo->max_h_samp_factor && compptr->v_samp_factor * 2 == cinfo->max_v_samp_factor) { #ifdef INPUT_SMOOTHING_SUPPORTED if (cinfo->smoothing_factor) { -#if defined(__mips__) +#if defined(WITH_SIMD) && defined(__mips__) if (jsimd_can_h2v2_smooth_downsample()) downsample->methods[ci] = jsimd_h2v2_smooth_downsample; else @@ -502,9 +519,11 @@ jinit_downsampler(j_compress_ptr cinfo) } else #endif { +#ifdef WITH_SIMD if (jsimd_can_h2v2_downsample()) downsample->methods[ci] = jsimd_h2v2_downsample; else +#endif downsample->methods[ci] = h2v2_downsample; } } else if ((cinfo->max_h_samp_factor % compptr->h_samp_factor) == 0 && @@ -520,3 +539,5 @@ jinit_downsampler(j_compress_ptr cinfo) TRACEMS(cinfo, 0, JTRC_SMOOTH_NOTIMPL); #endif } + +#endif /* BITS_IN_JSAMPLE != 16 || defined(C_LOSSLESS_SUPPORTED) */ diff --git a/jctrans.c b/jctrans.c index e121028..ae52e39 100644 --- a/jctrans.c +++ b/jctrans.c @@ -17,7 +17,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jpegcomp.h" +#include "jpegapicomp.h" /* Forward declarations */ @@ -42,6 +42,9 @@ LOCAL(void) transencode_coef_controller(j_compress_ptr cinfo, GLOBAL(void) jpeg_write_coefficients(j_compress_ptr cinfo, jvirt_barray_ptr *coef_arrays) { + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); + if (cinfo->global_state != CSTATE_START) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); /* Mark all tables to be written */ @@ -72,6 +75,9 @@ jpeg_copy_critical_parameters(j_decompress_ptr srcinfo, j_compress_ptr dstinfo) JQUANT_TBL *c_quant, *slot_quant; int tblno, ci, coefi; + if (srcinfo->master->lossless) + ERREXIT(dstinfo, JERR_NOTIMPL); + /* Safety check to ensure start_compress not called yet. */ if (dstinfo->global_state != CSTATE_START) ERREXIT1(dstinfo, JERR_BAD_STATE, dstinfo->global_state); @@ -364,6 +370,13 @@ compress_output(j_compress_ptr cinfo, JSAMPIMAGE input_buf) } +METHODDEF(boolean) +compress_output_12(j_compress_ptr cinfo, J12SAMPIMAGE input_buf) +{ + return compress_output(cinfo, (JSAMPIMAGE)input_buf); +} + + /* * Initialize coefficient buffer controller. * @@ -386,6 +399,7 @@ transencode_coef_controller(j_compress_ptr cinfo, cinfo->coef = (struct jpeg_c_coef_controller *)coef; coef->pub.start_pass = start_pass_coef; coef->pub.compress_data = compress_output; + coef->pub.compress_data_12 = compress_output_12; /* Save pointer to virtual arrays */ coef->whole_image = coef_arrays; diff --git a/jdapimin.c b/jdapimin.c index f50c27e..51ca552 100644 --- a/jdapimin.c +++ b/jdapimin.c @@ -3,6 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1998, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2016, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg @@ -23,7 +25,6 @@ #include "jinclude.h" #include "jpeglib.h" #include "jdmaster.h" -#include "jconfigint.h" /* @@ -83,6 +84,8 @@ jpeg_CreateDecompress(j_decompress_ptr cinfo, int version, size_t structsize) /* And initialize the overall input controller. */ jinit_input_controller(cinfo); + cinfo->data_precision = BITS_IN_JSAMPLE; + /* OK, I'm ready */ cinfo->global_state = DSTATE_START; @@ -163,7 +166,10 @@ default_decompress_parms(j_decompress_ptr cinfo) cinfo->jpeg_color_space = JCS_RGB; /* ASCII 'R', 'G', 'B' */ else { TRACEMS3(cinfo, 1, JTRC_UNKNOWN_IDS, cid0, cid1, cid2); - cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ + if (cinfo->master->lossless) + cinfo->jpeg_color_space = JCS_RGB; /* assume it's RGB */ + else + cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ } } /* Always guess RGB is proper output colorspace. */ diff --git a/jdapistd.c b/jdapistd.c index 02cd0cb..1f44927 100644 --- a/jdapistd.c +++ b/jdapistd.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2010, 2015-2020, 2022, D. R. Commander. + * Copyright (C) 2010, 2015-2020, 2022-2023, D. R. Commander. * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -19,13 +19,20 @@ */ #include "jinclude.h" +#if BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED) #include "jdmainct.h" #include "jdcoefct.h" +#else +#define JPEG_INTERNALS +#include "jpeglib.h" +#endif #include "jdmaster.h" #include "jdmerge.h" #include "jdsample.h" #include "jmemsys.h" +#if BITS_IN_JSAMPLE == 8 + /* Forward declarations */ LOCAL(boolean) output_pass_setup(j_decompress_ptr cinfo); @@ -121,8 +128,20 @@ output_pass_setup(j_decompress_ptr cinfo) } /* Process some data */ last_scanline = cinfo->output_scanline; - (*cinfo->main->process_data) (cinfo, (JSAMPARRAY)NULL, - &cinfo->output_scanline, (JDIMENSION)0); +#ifdef D_LOSSLESS_SUPPORTED + if (cinfo->data_precision == 16) + (*cinfo->main->process_data_16) (cinfo, (J16SAMPARRAY)NULL, + &cinfo->output_scanline, + (JDIMENSION)0); + else +#endif + if (cinfo->data_precision == 12) + (*cinfo->main->process_data_12) (cinfo, (J12SAMPARRAY)NULL, + &cinfo->output_scanline, + (JDIMENSION)0); + else + (*cinfo->main->process_data) (cinfo, (JSAMPARRAY)NULL, + &cinfo->output_scanline, (JDIMENSION)0); if (cinfo->output_scanline == last_scanline) return FALSE; /* No progress made, must suspend */ } @@ -135,25 +154,29 @@ output_pass_setup(j_decompress_ptr cinfo) #endif /* QUANT_2PASS_SUPPORTED */ } /* Ready for application to drive output pass through - * jpeg_read_scanlines or jpeg_read_raw_data. + * _jpeg_read_scanlines or _jpeg_read_raw_data. */ cinfo->global_state = cinfo->raw_data_out ? DSTATE_RAW_OK : DSTATE_SCANNING; return TRUE; } +#endif /* BITS_IN_JSAMPLE == 8 */ + + +#if BITS_IN_JSAMPLE != 16 /* * Enable partial scanline decompression * * Must be called after jpeg_start_decompress() and before any calls to - * jpeg_read_scanlines() or jpeg_skip_scanlines(). + * _jpeg_read_scanlines() or _jpeg_skip_scanlines(). * * Refer to libjpeg.txt for more information. */ GLOBAL(void) -jpeg_crop_scanline(j_decompress_ptr cinfo, JDIMENSION *xoffset, - JDIMENSION *width) +_jpeg_crop_scanline(j_decompress_ptr cinfo, JDIMENSION *xoffset, + JDIMENSION *width) { int ci, align, orig_downsampled_width; JDIMENSION input_xoffset; @@ -163,6 +186,12 @@ jpeg_crop_scanline(j_decompress_ptr cinfo, JDIMENSION *xoffset, my_master_ptr master = (my_master_ptr)cinfo->master; #endif + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); + if ((cinfo->global_state != DSTATE_SCANNING && cinfo->global_state != DSTATE_BUFIMAGE) || cinfo->output_scanline != 0) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); @@ -236,9 +265,11 @@ jpeg_crop_scanline(j_decompress_ptr cinfo, JDIMENSION *xoffset, /* Set downsampled_width to the new output width. */ orig_downsampled_width = compptr->downsampled_width; compptr->downsampled_width = - (JDIMENSION)jdiv_round_up((long)(cinfo->output_width * - compptr->h_samp_factor), - (long)cinfo->max_h_samp_factor); + (JDIMENSION)jdiv_round_up((long)cinfo->output_width * + (long)(compptr->h_samp_factor * + compptr->_DCT_scaled_size), + (long)(cinfo->max_h_samp_factor * + cinfo->_min_DCT_scaled_size)); if (compptr->downsampled_width < 2 && orig_downsampled_width >= 2) reinit_upsampler = TRUE; @@ -254,11 +285,13 @@ jpeg_crop_scanline(j_decompress_ptr cinfo, JDIMENSION *xoffset, if (reinit_upsampler) { cinfo->master->jinit_upsampler_no_alloc = TRUE; - jinit_upsampler(cinfo); + _jinit_upsampler(cinfo); cinfo->master->jinit_upsampler_no_alloc = FALSE; } } +#endif /* BITS_IN_JSAMPLE != 16 */ + /* * Read some scanlines of data from the JPEG decompressor. @@ -268,17 +301,21 @@ jpeg_crop_scanline(j_decompress_ptr cinfo, JDIMENSION *xoffset, * including bottom of image, data source suspension, and operating * modes that emit multiple scanlines at a time. * - * Note: we warn about excess calls to jpeg_read_scanlines() since + * Note: we warn about excess calls to _jpeg_read_scanlines() since * this likely signals an application programmer error. However, * an oversize buffer (max_lines > scanlines remaining) is not an error. */ GLOBAL(JDIMENSION) -jpeg_read_scanlines(j_decompress_ptr cinfo, JSAMPARRAY scanlines, - JDIMENSION max_lines) +_jpeg_read_scanlines(j_decompress_ptr cinfo, _JSAMPARRAY scanlines, + JDIMENSION max_lines) { +#if BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED) JDIMENSION row_ctr; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + if (cinfo->global_state != DSTATE_SCANNING) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); if (cinfo->output_scanline >= cinfo->output_height) { @@ -295,30 +332,36 @@ jpeg_read_scanlines(j_decompress_ptr cinfo, JSAMPARRAY scanlines, /* Process some data */ row_ctr = 0; - (*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, max_lines); + (*cinfo->main->_process_data) (cinfo, scanlines, &row_ctr, max_lines); cinfo->output_scanline += row_ctr; return row_ctr; +#else + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + return 0; +#endif } -/* Dummy color convert function used by jpeg_skip_scanlines() */ +#if BITS_IN_JSAMPLE != 16 + +/* Dummy color convert function used by _jpeg_skip_scanlines() */ LOCAL(void) -noop_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows) +noop_convert(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { } -/* Dummy quantize function used by jpeg_skip_scanlines() */ +/* Dummy quantize function used by _jpeg_skip_scanlines() */ LOCAL(void) -noop_quantize(j_decompress_ptr cinfo, JSAMPARRAY input_buf, - JSAMPARRAY output_buf, int num_rows) +noop_quantize(j_decompress_ptr cinfo, _JSAMPARRAY input_buf, + _JSAMPARRAY output_buf, int num_rows) { } /* - * In some cases, it is best to call jpeg_read_scanlines() and discard the + * In some cases, it is best to call _jpeg_read_scanlines() and discard the * output, rather than skipping the scanlines, because this allows us to * maintain the internal state of the context-based upsampler. In these cases, * we set up and tear down a dummy color converter in order to avoid valgrind @@ -332,27 +375,27 @@ read_and_discard_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines) #ifdef UPSAMPLE_MERGING_SUPPORTED my_master_ptr master = (my_master_ptr)cinfo->master; #endif - JSAMPLE dummy_sample[1] = { 0 }; - JSAMPROW dummy_row = dummy_sample; - JSAMPARRAY scanlines = NULL; - void (*color_convert) (j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, + _JSAMPLE dummy_sample[1] = { 0 }; + _JSAMPROW dummy_row = dummy_sample; + _JSAMPARRAY scanlines = NULL; + void (*color_convert) (j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) = NULL; - void (*color_quantize) (j_decompress_ptr cinfo, JSAMPARRAY input_buf, - JSAMPARRAY output_buf, int num_rows) = NULL; + void (*color_quantize) (j_decompress_ptr cinfo, _JSAMPARRAY input_buf, + _JSAMPARRAY output_buf, int num_rows) = NULL; - if (cinfo->cconvert && cinfo->cconvert->color_convert) { - color_convert = cinfo->cconvert->color_convert; - cinfo->cconvert->color_convert = noop_convert; + if (cinfo->cconvert && cinfo->cconvert->_color_convert) { + color_convert = cinfo->cconvert->_color_convert; + cinfo->cconvert->_color_convert = noop_convert; /* This just prevents UBSan from complaining about adding 0 to a NULL * pointer. The pointer isn't actually used. */ scanlines = &dummy_row; } - if (cinfo->cquantize && cinfo->cquantize->color_quantize) { - color_quantize = cinfo->cquantize->color_quantize; - cinfo->cquantize->color_quantize = noop_quantize; + if (cinfo->cquantize && cinfo->cquantize->_color_quantize) { + color_quantize = cinfo->cquantize->_color_quantize; + cinfo->cquantize->_color_quantize = noop_quantize; } #ifdef UPSAMPLE_MERGING_SUPPORTED @@ -363,19 +406,19 @@ read_and_discard_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines) #endif for (n = 0; n < num_lines; n++) - jpeg_read_scanlines(cinfo, scanlines, 1); + _jpeg_read_scanlines(cinfo, scanlines, 1); if (color_convert) - cinfo->cconvert->color_convert = color_convert; + cinfo->cconvert->_color_convert = color_convert; if (color_quantize) - cinfo->cquantize->color_quantize = color_quantize; + cinfo->cquantize->_color_quantize = color_quantize; } /* - * Called by jpeg_skip_scanlines(). This partially skips a decompress block by - * incrementing the rowgroup counter. + * Called by _jpeg_skip_scanlines(). This partially skips a decompress block + * by incrementing the rowgroup counter. */ LOCAL(void) @@ -414,7 +457,7 @@ increment_simple_rowgroup_ctr(j_decompress_ptr cinfo, JDIMENSION rows) */ GLOBAL(JDIMENSION) -jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines) +_jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines) { my_main_ptr main_ptr = (my_main_ptr)cinfo->main; my_coef_ptr coef = (my_coef_ptr)cinfo->coef; @@ -425,6 +468,12 @@ jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines) JDIMENSION lines_per_iMCU_row, lines_left_in_iMCU_row, lines_after_iMCU_row; JDIMENSION lines_to_skip, lines_to_read; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); + /* Two-pass color quantization is not supported. */ if (cinfo->quantize_colors && cinfo->two_pass_quantize) ERREXIT(cinfo, JERR_NOTIMPL); @@ -597,11 +646,17 @@ jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines) */ GLOBAL(JDIMENSION) -jpeg_read_raw_data(j_decompress_ptr cinfo, JSAMPIMAGE data, - JDIMENSION max_lines) +_jpeg_read_raw_data(j_decompress_ptr cinfo, _JSAMPIMAGE data, + JDIMENSION max_lines) { JDIMENSION lines_per_iMCU_row; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); + if (cinfo->global_state != DSTATE_RAW_OK) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); if (cinfo->output_scanline >= cinfo->output_height) { @@ -622,7 +677,7 @@ jpeg_read_raw_data(j_decompress_ptr cinfo, JSAMPIMAGE data, ERREXIT(cinfo, JERR_BUFFER_SIZE); /* Decompress directly into user's buffer. */ - if (!(*cinfo->coef->decompress_data) (cinfo, data)) + if (!(*cinfo->coef->_decompress_data) (cinfo, data)) return 0; /* suspension forced, can do nothing more */ /* OK, we processed one iMCU row. */ @@ -630,6 +685,10 @@ jpeg_read_raw_data(j_decompress_ptr cinfo, JSAMPIMAGE data, return lines_per_iMCU_row; } +#endif /* BITS_IN_JSAMPLE != 16 */ + + +#if BITS_IN_JSAMPLE == 8 /* Additional entry points for buffered-image mode. */ @@ -687,3 +746,5 @@ jpeg_finish_output(j_decompress_ptr cinfo) } #endif /* D_MULTISCAN_FILES_SUPPORTED */ + +#endif /* BITS_IN_JSAMPLE == 8 */ diff --git a/jdatadst-tj.c b/jdatadst-tj.c index e10d981..cce263a 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/jdatadst.c b/jdatadst.c index 6b4fed2..529f93b 100644 --- a/jdatadst.c +++ b/jdatadst.c @@ -38,7 +38,6 @@ typedef my_destination_mgr *my_dest_ptr; #define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */ -#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) /* Expanded data destination object for memory output */ typedef struct { @@ -52,7 +51,6 @@ typedef struct { } my_mem_destination_mgr; typedef my_mem_destination_mgr *my_mem_dest_ptr; -#endif /* @@ -74,13 +72,11 @@ init_destination(j_compress_ptr cinfo) dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; } -#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) METHODDEF(void) init_mem_destination(j_compress_ptr cinfo) { /* no work necessary here */ } -#endif /* @@ -121,7 +117,6 @@ empty_output_buffer(j_compress_ptr cinfo) return TRUE; } -#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) METHODDEF(boolean) empty_mem_output_buffer(j_compress_ptr cinfo) { @@ -150,7 +145,6 @@ empty_mem_output_buffer(j_compress_ptr cinfo) return TRUE; } -#endif /* @@ -179,7 +173,6 @@ term_destination(j_compress_ptr cinfo) ERREXIT(cinfo, JERR_FILE_WRITE); } -#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) METHODDEF(void) term_mem_destination(j_compress_ptr cinfo) { @@ -188,7 +181,6 @@ term_mem_destination(j_compress_ptr cinfo) *dest->outbuffer = dest->buffer; *dest->outsize = (unsigned long)(dest->bufsize - dest->pub.free_in_buffer); } -#endif /* @@ -227,7 +219,6 @@ jpeg_stdio_dest(j_compress_ptr cinfo, FILE *outfile) } -#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) /* * Prepare for output to a memory buffer. * The caller may supply an own initial buffer with appropriate size. @@ -284,4 +275,3 @@ jpeg_mem_dest(j_compress_ptr cinfo, unsigned char **outbuffer, dest->pub.next_output_byte = dest->buffer = *outbuffer; dest->pub.free_in_buffer = dest->bufsize = *outsize; } -#endif diff --git a/jdatasrc-tj.c b/jdatasrc-tj.c index 69fb5ea..a5970b5 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/jdatasrc.c b/jdatasrc.c index e36a30d..dc135f4 100644 --- a/jdatasrc.c +++ b/jdatasrc.c @@ -56,13 +56,11 @@ init_source(j_decompress_ptr cinfo) src->start_of_file = TRUE; } -#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) METHODDEF(void) init_mem_source(j_decompress_ptr cinfo) { /* no work necessary here */ } -#endif /* @@ -123,7 +121,6 @@ fill_input_buffer(j_decompress_ptr cinfo) return TRUE; } -#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) METHODDEF(boolean) fill_mem_input_buffer(j_decompress_ptr cinfo) { @@ -144,7 +141,6 @@ fill_mem_input_buffer(j_decompress_ptr cinfo) return TRUE; } -#endif /* @@ -253,7 +249,6 @@ jpeg_stdio_src(j_decompress_ptr cinfo, FILE *infile) } -#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) /* * Prepare for input from a supplied memory buffer. * The buffer must contain the whole JPEG data. @@ -292,4 +287,3 @@ jpeg_mem_src(j_decompress_ptr cinfo, const unsigned char *inbuffer, src->bytes_in_buffer = (size_t)insize; src->next_input_byte = (const JOCTET *)inbuffer; } -#endif diff --git a/jdcoefct.c b/jdcoefct.c index 88e10c0..40ce272 100644 --- a/jdcoefct.c +++ b/jdcoefct.c @@ -5,13 +5,13 @@ * Copyright (C) 1994-1997, Thomas G. Lane. * libjpeg-turbo Modifications: * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2010, 2015-2016, 2019-2020, 2022, D. R. Commander. + * Copyright (C) 2010, 2015-2016, 2019-2020, 2022-2023, D. R. Commander. * Copyright (C) 2015, 2020, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. * * This file contains the coefficient buffer controller for decompression. - * This controller is the top level of the JPEG decompressor proper. + * This controller is the top level of the lossy JPEG decompressor proper. * The coefficient buffer lies between entropy decoding and inverse-DCT steps. * * In buffered-image mode, this controller is the interface between @@ -21,19 +21,20 @@ #include "jinclude.h" #include "jdcoefct.h" -#include "jpegcomp.h" +#include "jpegapicomp.h" +#include "jsamplecomp.h" /* Forward declarations */ METHODDEF(int) decompress_onepass(j_decompress_ptr cinfo, - JSAMPIMAGE output_buf); + _JSAMPIMAGE output_buf); #ifdef D_MULTISCAN_FILES_SUPPORTED -METHODDEF(int) decompress_data(j_decompress_ptr cinfo, JSAMPIMAGE output_buf); +METHODDEF(int) decompress_data(j_decompress_ptr cinfo, _JSAMPIMAGE output_buf); #endif #ifdef BLOCK_SMOOTHING_SUPPORTED LOCAL(boolean) smoothing_ok(j_decompress_ptr cinfo); METHODDEF(int) decompress_smooth_data(j_decompress_ptr cinfo, - JSAMPIMAGE output_buf); + _JSAMPIMAGE output_buf); #endif @@ -62,9 +63,9 @@ start_output_pass(j_decompress_ptr cinfo) /* If multipass, check to see whether to use block smoothing on this pass */ if (coef->pub.coef_arrays != NULL) { if (cinfo->do_block_smoothing && smoothing_ok(cinfo)) - coef->pub.decompress_data = decompress_smooth_data; + coef->pub._decompress_data = decompress_smooth_data; else - coef->pub.decompress_data = decompress_data; + coef->pub._decompress_data = decompress_data; } #endif cinfo->output_iMCU_row = 0; @@ -82,17 +83,17 @@ start_output_pass(j_decompress_ptr cinfo) */ METHODDEF(int) -decompress_onepass(j_decompress_ptr cinfo, JSAMPIMAGE output_buf) +decompress_onepass(j_decompress_ptr cinfo, _JSAMPIMAGE output_buf) { my_coef_ptr coef = (my_coef_ptr)cinfo->coef; JDIMENSION MCU_col_num; /* index of current MCU within row */ JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; int blkn, ci, xindex, yindex, yoffset, useful_width; - JSAMPARRAY output_ptr; + _JSAMPARRAY output_ptr; JDIMENSION start_col, output_col; jpeg_component_info *compptr; - inverse_DCT_method_ptr inverse_DCT; + _inverse_DCT_method_ptr inverse_DCT; /* Loop to process as much as one whole iMCU row */ for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; @@ -129,7 +130,7 @@ decompress_onepass(j_decompress_ptr cinfo, JSAMPIMAGE output_buf) blkn += compptr->MCU_blocks; continue; } - inverse_DCT = cinfo->idct->inverse_DCT[compptr->component_index]; + inverse_DCT = cinfo->idct->_inverse_DCT[compptr->component_index]; useful_width = (MCU_col_num < last_MCU_col) ? compptr->MCU_width : compptr->last_col_width; output_ptr = output_buf[compptr->component_index] + @@ -262,7 +263,7 @@ consume_data(j_decompress_ptr cinfo) */ METHODDEF(int) -decompress_data(j_decompress_ptr cinfo, JSAMPIMAGE output_buf) +decompress_data(j_decompress_ptr cinfo, _JSAMPIMAGE output_buf) { my_coef_ptr coef = (my_coef_ptr)cinfo->coef; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; @@ -270,10 +271,10 @@ decompress_data(j_decompress_ptr cinfo, JSAMPIMAGE output_buf) int ci, block_row, block_rows; JBLOCKARRAY buffer; JBLOCKROW buffer_ptr; - JSAMPARRAY output_ptr; + _JSAMPARRAY output_ptr; JDIMENSION output_col; jpeg_component_info *compptr; - inverse_DCT_method_ptr inverse_DCT; + _inverse_DCT_method_ptr inverse_DCT; /* Force some input to be done if we are getting ahead of the input. */ while (cinfo->input_scan_number < cinfo->output_scan_number || @@ -302,7 +303,7 @@ decompress_data(j_decompress_ptr cinfo, JSAMPIMAGE output_buf) block_rows = (int)(compptr->height_in_blocks % compptr->v_samp_factor); if (block_rows == 0) block_rows = compptr->v_samp_factor; } - inverse_DCT = cinfo->idct->inverse_DCT[ci]; + inverse_DCT = cinfo->idct->_inverse_DCT[ci]; output_ptr = output_buf[ci]; /* Loop over all DCT blocks to be processed. */ for (block_row = 0; block_row < block_rows; block_row++) { @@ -425,19 +426,20 @@ smoothing_ok(j_decompress_ptr cinfo) */ METHODDEF(int) -decompress_smooth_data(j_decompress_ptr cinfo, JSAMPIMAGE output_buf) +decompress_smooth_data(j_decompress_ptr cinfo, _JSAMPIMAGE output_buf) { my_coef_ptr coef = (my_coef_ptr)cinfo->coef; JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; JDIMENSION block_num, last_block_column; - int ci, block_row, block_rows, access_rows; + int ci, block_row, block_rows, access_rows, image_block_row, + image_block_rows; JBLOCKARRAY buffer; JBLOCKROW buffer_ptr, prev_prev_block_row, prev_block_row; JBLOCKROW next_block_row, next_next_block_row; - JSAMPARRAY output_ptr; + _JSAMPARRAY output_ptr; JDIMENSION output_col; jpeg_component_info *compptr; - inverse_DCT_method_ptr inverse_DCT; + _inverse_DCT_method_ptr inverse_DCT; boolean change_dc; JCOEF *workspace; int *coef_bits; @@ -496,6 +498,7 @@ decompress_smooth_data(j_decompress_ptr cinfo, JSAMPIMAGE output_buf) (JDIMENSION)access_rows, FALSE); buffer += 2 * compptr->v_samp_factor; /* point to current iMCU row */ } else if (cinfo->output_iMCU_row > 0) { + access_rows += compptr->v_samp_factor; /* prior iMCU row too */ buffer = (*cinfo->mem->access_virt_barray) ((j_common_ptr)cinfo, coef->whole_image[ci], (cinfo->output_iMCU_row - 1) * compptr->v_samp_factor, @@ -535,32 +538,33 @@ decompress_smooth_data(j_decompress_ptr cinfo, JSAMPIMAGE output_buf) Q21 = quanttbl->quantval[Q21_POS]; Q30 = quanttbl->quantval[Q30_POS]; } - inverse_DCT = cinfo->idct->inverse_DCT[ci]; + inverse_DCT = cinfo->idct->_inverse_DCT[ci]; output_ptr = output_buf[ci]; /* Loop over all DCT blocks to be processed. */ + image_block_rows = block_rows * cinfo->total_iMCU_rows; for (block_row = 0; block_row < block_rows; block_row++) { + image_block_row = cinfo->output_iMCU_row * block_rows + block_row; buffer_ptr = buffer[block_row] + cinfo->master->first_MCU_col[ci]; - if (block_row > 0 || cinfo->output_iMCU_row > 0) + if (image_block_row > 0) prev_block_row = buffer[block_row - 1] + cinfo->master->first_MCU_col[ci]; else prev_block_row = buffer_ptr; - if (block_row > 1 || cinfo->output_iMCU_row > 1) + if (image_block_row > 1) prev_prev_block_row = buffer[block_row - 2] + cinfo->master->first_MCU_col[ci]; else prev_prev_block_row = prev_block_row; - if (block_row < block_rows - 1 || cinfo->output_iMCU_row < last_iMCU_row) + if (image_block_row < image_block_rows - 1) next_block_row = buffer[block_row + 1] + cinfo->master->first_MCU_col[ci]; else next_block_row = buffer_ptr; - if (block_row < block_rows - 2 || - cinfo->output_iMCU_row + 1 < last_iMCU_row) + if (image_block_row < image_block_rows - 2) next_next_block_row = buffer[block_row + 2] + cinfo->master->first_MCU_col[ci]; else @@ -583,11 +587,11 @@ decompress_smooth_data(j_decompress_ptr cinfo, JSAMPIMAGE output_buf) /* Update DC values */ if (block_num == cinfo->master->first_MCU_col[ci] && block_num < last_block_column) { - DC04 = (int)prev_prev_block_row[1][0]; - DC09 = (int)prev_block_row[1][0]; - DC14 = (int)buffer_ptr[1][0]; - DC19 = (int)next_block_row[1][0]; - DC24 = (int)next_next_block_row[1][0]; + DC04 = DC05 = (int)prev_prev_block_row[1][0]; + DC09 = DC10 = (int)prev_block_row[1][0]; + DC14 = DC15 = (int)buffer_ptr[1][0]; + DC19 = DC20 = (int)next_block_row[1][0]; + DC24 = DC25 = (int)next_next_block_row[1][0]; } if (block_num + 1 < last_block_column) { DC05 = (int)prev_prev_block_row[2][0]; @@ -810,10 +814,13 @@ decompress_smooth_data(j_decompress_ptr cinfo, JSAMPIMAGE output_buf) */ GLOBAL(void) -jinit_d_coef_controller(j_decompress_ptr cinfo, boolean need_full_buffer) +_jinit_d_coef_controller(j_decompress_ptr cinfo, boolean need_full_buffer) { my_coef_ptr coef; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + coef = (my_coef_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, sizeof(my_coef_controller)); @@ -850,7 +857,7 @@ jinit_d_coef_controller(j_decompress_ptr cinfo, boolean need_full_buffer) (JDIMENSION)access_rows); } coef->pub.consume_data = consume_data; - coef->pub.decompress_data = decompress_data; + coef->pub._decompress_data = decompress_data; coef->pub.coef_arrays = coef->whole_image; /* link to virtual arrays */ #else ERREXIT(cinfo, JERR_NOT_COMPILED); @@ -867,7 +874,7 @@ jinit_d_coef_controller(j_decompress_ptr cinfo, boolean need_full_buffer) coef->MCU_buffer[i] = buffer + i; } coef->pub.consume_data = dummy_consume_data; - coef->pub.decompress_data = decompress_onepass; + coef->pub._decompress_data = decompress_onepass; coef->pub.coef_arrays = NULL; /* flag for no virtual arrays */ } diff --git a/jdcoefct.h b/jdcoefct.h index 9a0e780..bbe9e97 100644 --- a/jdcoefct.h +++ b/jdcoefct.h @@ -6,6 +6,7 @@ * libjpeg-turbo Modifications: * Copyright 2009 Pierre Ossman for Cendio AB * Copyright (C) 2020, Google, Inc. + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. */ @@ -14,6 +15,8 @@ #include "jpeglib.h" +#if BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED) + /* Block smoothing is only applicable for progressive JPEG, so: */ #ifndef D_PROGRESSIVE_SUPPORTED #undef BLOCK_SMOOTHING_SUPPORTED @@ -81,3 +84,5 @@ start_iMCU_row(j_decompress_ptr cinfo) coef->MCU_ctr = 0; coef->MCU_vert_offset = 0; } + +#endif /* BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED) */ diff --git a/jdcol565.c b/jdcol565.c index 53c7bd9..2172d98 100644 --- a/jdcol565.c +++ b/jdcol565.c @@ -5,7 +5,7 @@ * Copyright (C) 1991-1997, Thomas G. Lane. * Modifications: * Copyright (C) 2013, Linaro Limited. - * Copyright (C) 2014-2015, D. R. Commander. + * Copyright (C) 2014-2015, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -17,18 +17,19 @@ INLINE LOCAL(void) -ycc_rgb565_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, +ycc_rgb565_convert_internal(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { +#if BITS_IN_JSAMPLE != 16 my_cconvert_ptr cconvert = (my_cconvert_ptr)cinfo->cconvert; register int y, cb, cr; - register JSAMPROW outptr; - register JSAMPROW inptr0, inptr1, inptr2; + register _JSAMPROW outptr; + register _JSAMPROW inptr0, inptr1, inptr2; register JDIMENSION col; JDIMENSION num_cols = cinfo->output_width; /* copy these pointers into registers if possible */ - register JSAMPLE *range_limit = cinfo->sample_range_limit; + register _JSAMPLE *range_limit = (_JSAMPLE *)cinfo->sample_range_limit; register int *Crrtab = cconvert->Cr_r_tab; register int *Cbbtab = cconvert->Cb_b_tab; register JLONG *Crgtab = cconvert->Cr_g_tab; @@ -91,23 +92,27 @@ ycc_rgb565_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, *(INT16 *)outptr = (INT16)rgb; } } +#else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); +#endif } INLINE LOCAL(void) -ycc_rgb565D_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, +ycc_rgb565D_convert_internal(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { +#if BITS_IN_JSAMPLE != 16 my_cconvert_ptr cconvert = (my_cconvert_ptr)cinfo->cconvert; register int y, cb, cr; - register JSAMPROW outptr; - register JSAMPROW inptr0, inptr1, inptr2; + register _JSAMPROW outptr; + register _JSAMPROW inptr0, inptr1, inptr2; register JDIMENSION col; JDIMENSION num_cols = cinfo->output_width; /* copy these pointers into registers if possible */ - register JSAMPLE *range_limit = cinfo->sample_range_limit; + register _JSAMPLE *range_limit = (_JSAMPLE *)cinfo->sample_range_limit; register int *Crrtab = cconvert->Cr_r_tab; register int *Cbbtab = cconvert->Cb_b_tab; register JLONG *Crgtab = cconvert->Cr_g_tab; @@ -177,17 +182,20 @@ ycc_rgb565D_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, *(INT16 *)outptr = (INT16)rgb; } } +#else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); +#endif } INLINE LOCAL(void) -rgb_rgb565_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, +rgb_rgb565_convert_internal(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { - register JSAMPROW outptr; - register JSAMPROW inptr0, inptr1, inptr2; + register _JSAMPROW outptr; + register _JSAMPROW inptr0, inptr1, inptr2; register JDIMENSION col; JDIMENSION num_cols = cinfo->output_width; SHIFT_TEMPS @@ -237,14 +245,14 @@ rgb_rgb565_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, INLINE LOCAL(void) -rgb_rgb565D_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, +rgb_rgb565D_convert_internal(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { - register JSAMPROW outptr; - register JSAMPROW inptr0, inptr1, inptr2; + register _JSAMPROW outptr; + register _JSAMPROW inptr0, inptr1, inptr2; register JDIMENSION col; - register JSAMPLE *range_limit = cinfo->sample_range_limit; + register _JSAMPLE *range_limit = (_JSAMPLE *)cinfo->sample_range_limit; JDIMENSION num_cols = cinfo->output_width; JLONG d0 = dither_matrix[cinfo->output_scanline & DITHER_MASK]; SHIFT_TEMPS @@ -296,11 +304,11 @@ rgb_rgb565D_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, INLINE LOCAL(void) -gray_rgb565_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, +gray_rgb565_convert_internal(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { - register JSAMPROW inptr, outptr; + register _JSAMPROW inptr, outptr; register JDIMENSION col; JDIMENSION num_cols = cinfo->output_width; @@ -336,13 +344,13 @@ gray_rgb565_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, INLINE LOCAL(void) -gray_rgb565D_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, +gray_rgb565D_convert_internal(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { - register JSAMPROW inptr, outptr; + register _JSAMPROW inptr, outptr; register JDIMENSION col; - register JSAMPLE *range_limit = cinfo->sample_range_limit; + register _JSAMPLE *range_limit = (_JSAMPLE *)cinfo->sample_range_limit; JDIMENSION num_cols = cinfo->output_width; JLONG d0 = dither_matrix[cinfo->output_scanline & DITHER_MASK]; diff --git a/jdcolext.c b/jdcolext.c index 863c7a2..f22e29d 100644 --- a/jdcolext.c +++ b/jdcolext.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2009, 2011, 2015, D. R. Commander. + * Copyright (C) 2009, 2011, 2015, 2022-2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -28,18 +28,19 @@ INLINE LOCAL(void) -ycc_rgb_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, +ycc_rgb_convert_internal(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { +#if BITS_IN_JSAMPLE != 16 my_cconvert_ptr cconvert = (my_cconvert_ptr)cinfo->cconvert; register int y, cb, cr; - register JSAMPROW outptr; - register JSAMPROW inptr0, inptr1, inptr2; + register _JSAMPROW outptr; + register _JSAMPROW inptr0, inptr1, inptr2; register JDIMENSION col; JDIMENSION num_cols = cinfo->output_width; /* copy these pointers into registers if possible */ - register JSAMPLE *range_limit = cinfo->sample_range_limit; + register _JSAMPLE *range_limit = (_JSAMPLE *)cinfo->sample_range_limit; register int *Crrtab = cconvert->Cr_r_tab; register int *Cbbtab = cconvert->Cb_b_tab; register JLONG *Crgtab = cconvert->Cr_g_tab; @@ -62,14 +63,17 @@ ycc_rgb_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, ((int)RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS))]; outptr[RGB_BLUE] = range_limit[y + Cbbtab[cb]]; - /* Set unused byte to 0xFF so it can be interpreted as an opaque */ - /* alpha channel value */ + /* Set unused byte to _MAXJSAMPLE so it can be interpreted as an */ + /* opaque alpha channel value */ #ifdef RGB_ALPHA - outptr[RGB_ALPHA] = 0xFF; + outptr[RGB_ALPHA] = _MAXJSAMPLE; #endif outptr += RGB_PIXELSIZE; } } +#else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); +#endif } @@ -81,11 +85,11 @@ ycc_rgb_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, INLINE LOCAL(void) -gray_rgb_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, +gray_rgb_convert_internal(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { - register JSAMPROW inptr, outptr; + register _JSAMPROW inptr, outptr; register JDIMENSION col; JDIMENSION num_cols = cinfo->output_width; @@ -94,10 +98,10 @@ gray_rgb_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, outptr = *output_buf++; for (col = 0; col < num_cols; col++) { outptr[RGB_RED] = outptr[RGB_GREEN] = outptr[RGB_BLUE] = inptr[col]; - /* Set unused byte to 0xFF so it can be interpreted as an opaque */ - /* alpha channel value */ + /* Set unused byte to _MAXJSAMPLE so it can be interpreted as an */ + /* opaque alpha channel value */ #ifdef RGB_ALPHA - outptr[RGB_ALPHA] = 0xFF; + outptr[RGB_ALPHA] = _MAXJSAMPLE; #endif outptr += RGB_PIXELSIZE; } @@ -111,12 +115,12 @@ gray_rgb_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, INLINE LOCAL(void) -rgb_rgb_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, +rgb_rgb_convert_internal(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { - register JSAMPROW inptr0, inptr1, inptr2; - register JSAMPROW outptr; + register _JSAMPROW inptr0, inptr1, inptr2; + register _JSAMPROW outptr; register JDIMENSION col; JDIMENSION num_cols = cinfo->output_width; @@ -130,10 +134,10 @@ rgb_rgb_convert_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, outptr[RGB_RED] = inptr0[col]; outptr[RGB_GREEN] = inptr1[col]; outptr[RGB_BLUE] = inptr2[col]; - /* Set unused byte to 0xFF so it can be interpreted as an opaque */ - /* alpha channel value */ + /* Set unused byte to _MAXJSAMPLE so it can be interpreted as an */ + /* opaque alpha channel value */ #ifdef RGB_ALPHA - outptr[RGB_ALPHA] = 0xFF; + outptr[RGB_ALPHA] = _MAXJSAMPLE; #endif outptr += RGB_PIXELSIZE; } diff --git a/jdcolor.c b/jdcolor.c index 8da2b4e..e5c7b58 100644 --- a/jdcolor.c +++ b/jdcolor.c @@ -6,7 +6,7 @@ * Modified 2011 by Guido Vollbeding. * libjpeg-turbo Modifications: * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2009, 2011-2012, 2014-2015, D. R. Commander. + * Copyright (C) 2009, 2011-2012, 2014-2015, 2022, D. R. Commander. * Copyright (C) 2013, Linaro Limited. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -18,14 +18,17 @@ #include "jinclude.h" #include "jpeglib.h" #include "jsimd.h" -#include "jconfigint.h" +#include "jsamplecomp.h" +#if BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED) + /* Private subobject */ typedef struct { struct jpeg_color_deconverter pub; /* public fields */ +#if BITS_IN_JSAMPLE != 16 /* Private state for YCC->RGB conversion */ int *Cr_r_tab; /* => table for Cr to R conversion */ int *Cb_b_tab; /* => table for Cb to B conversion */ @@ -34,6 +37,7 @@ typedef struct { /* Private state for RGB->Y conversion */ JLONG *rgb_y_tab; /* => table for RGB to Y conversion */ +#endif } my_color_deconverter; typedef my_color_deconverter *my_cconvert_ptr; @@ -44,7 +48,7 @@ typedef my_color_deconverter *my_cconvert_ptr; /* * YCbCr is defined per CCIR 601-1, except that Cb and Cr are - * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. + * normalized to the range 0.._MAXJSAMPLE rather than -0.5 .. 0.5. * The conversion equations to be implemented are therefore * * R = Y + 1.40200 * Cr @@ -53,7 +57,7 @@ typedef my_color_deconverter *my_cconvert_ptr; * * Y = 0.29900 * R + 0.58700 * G + 0.11400 * B * - * where Cb and Cr represent the incoming values less CENTERJSAMPLE. + * where Cb and Cr represent the incoming values less _CENTERJSAMPLE. * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) * * To avoid floating-point arithmetic, we represent the fractional constants @@ -64,7 +68,7 @@ typedef my_color_deconverter *my_cconvert_ptr; * * For even more speed, we avoid doing any multiplications in the inner loop * by precalculating the constants times Cb and Cr for all possible values. - * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); + * For 8-bit samples this is very reasonable (only 256 entries per table); * for 12-bit samples it is still acceptable. It's not very reasonable for * 16-bit samples, but if you want lossless storage you shouldn't be changing * colorspace anyway. @@ -85,9 +89,9 @@ typedef my_color_deconverter *my_cconvert_ptr; */ #define R_Y_OFF 0 /* offset to R => Y section */ -#define G_Y_OFF (1 * (MAXJSAMPLE + 1)) /* offset to G => Y section */ -#define B_Y_OFF (2 * (MAXJSAMPLE + 1)) /* etc. */ -#define TABLE_SIZE (3 * (MAXJSAMPLE + 1)) +#define G_Y_OFF (1 * (_MAXJSAMPLE + 1)) /* offset to G => Y section */ +#define B_Y_OFF (2 * (_MAXJSAMPLE + 1)) /* etc. */ +#define TABLE_SIZE (3 * (_MAXJSAMPLE + 1)) /* Include inline routines for colorspace extensions */ @@ -210,6 +214,7 @@ typedef my_color_deconverter *my_cconvert_ptr; LOCAL(void) build_ycc_rgb_table(j_decompress_ptr cinfo) { +#if BITS_IN_JSAMPLE != 16 my_cconvert_ptr cconvert = (my_cconvert_ptr)cinfo->cconvert; int i; JLONG x; @@ -217,20 +222,20 @@ build_ycc_rgb_table(j_decompress_ptr cinfo) cconvert->Cr_r_tab = (int *) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, - (MAXJSAMPLE + 1) * sizeof(int)); + (_MAXJSAMPLE + 1) * sizeof(int)); cconvert->Cb_b_tab = (int *) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, - (MAXJSAMPLE + 1) * sizeof(int)); + (_MAXJSAMPLE + 1) * sizeof(int)); cconvert->Cr_g_tab = (JLONG *) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, - (MAXJSAMPLE + 1) * sizeof(JLONG)); + (_MAXJSAMPLE + 1) * sizeof(JLONG)); cconvert->Cb_g_tab = (JLONG *) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, - (MAXJSAMPLE + 1) * sizeof(JLONG)); + (_MAXJSAMPLE + 1) * sizeof(JLONG)); - for (i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) { - /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ - /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ + for (i = 0, x = -_CENTERJSAMPLE; i <= _MAXJSAMPLE; i++, x++) { + /* i is the actual input pixel value, in the range 0.._MAXJSAMPLE */ + /* The Cb or Cr value we are thinking of is x = i - _CENTERJSAMPLE */ /* Cr=>R value is nearest int to 1.40200 * x */ cconvert->Cr_r_tab[i] = (int) RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); @@ -243,6 +248,9 @@ build_ycc_rgb_table(j_decompress_ptr cinfo) /* We also add in ONE_HALF so that need not do it in inner loop */ cconvert->Cb_g_tab[i] = (-FIX(0.34414)) * x + ONE_HALF; } +#else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); +#endif } @@ -251,8 +259,8 @@ build_ycc_rgb_table(j_decompress_ptr cinfo) */ METHODDEF(void) -ycc_rgb_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows) +ycc_rgb_convert(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { switch (cinfo->out_color_space) { case JCS_EXT_RGB: @@ -301,6 +309,7 @@ ycc_rgb_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, LOCAL(void) build_rgb_y_table(j_decompress_ptr cinfo) { +#if BITS_IN_JSAMPLE != 16 my_cconvert_ptr cconvert = (my_cconvert_ptr)cinfo->cconvert; JLONG *rgb_y_tab; JLONG i; @@ -310,11 +319,14 @@ build_rgb_y_table(j_decompress_ptr cinfo) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, (TABLE_SIZE * sizeof(JLONG))); - for (i = 0; i <= MAXJSAMPLE; i++) { + for (i = 0; i <= _MAXJSAMPLE; i++) { rgb_y_tab[i + R_Y_OFF] = FIX(0.29900) * i; rgb_y_tab[i + G_Y_OFF] = FIX(0.58700) * i; rgb_y_tab[i + B_Y_OFF] = FIX(0.11400) * i + ONE_HALF; } +#else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); +#endif } @@ -323,14 +335,15 @@ build_rgb_y_table(j_decompress_ptr cinfo) */ METHODDEF(void) -rgb_gray_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows) +rgb_gray_convert(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { +#if BITS_IN_JSAMPLE != 16 my_cconvert_ptr cconvert = (my_cconvert_ptr)cinfo->cconvert; register int r, g, b; register JLONG *ctab = cconvert->rgb_y_tab; - register JSAMPROW outptr; - register JSAMPROW inptr0, inptr1, inptr2; + register _JSAMPROW outptr; + register _JSAMPROW inptr0, inptr1, inptr2; register JDIMENSION col; JDIMENSION num_cols = cinfo->output_width; @@ -345,10 +358,13 @@ rgb_gray_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, g = inptr1[col]; b = inptr2[col]; /* Y */ - outptr[col] = (JSAMPLE)((ctab[r + R_Y_OFF] + ctab[g + G_Y_OFF] + - ctab[b + B_Y_OFF]) >> SCALEBITS); + outptr[col] = (_JSAMPLE)((ctab[r + R_Y_OFF] + ctab[g + G_Y_OFF] + + ctab[b + B_Y_OFF]) >> SCALEBITS); } } +#else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); +#endif } @@ -358,10 +374,10 @@ rgb_gray_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, */ METHODDEF(void) -null_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows) +null_convert(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { - register JSAMPROW inptr, inptr0, inptr1, inptr2, inptr3, outptr; + register _JSAMPROW inptr, inptr0, inptr1, inptr2, inptr3, outptr; register JDIMENSION col; register int num_components = cinfo->num_components; JDIMENSION num_cols = cinfo->output_width; @@ -419,11 +435,11 @@ null_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, */ METHODDEF(void) -grayscale_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows) +grayscale_convert(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { - jcopy_sample_rows(input_buf[0], (int)input_row, output_buf, 0, num_rows, - cinfo->output_width); + _jcopy_sample_rows(input_buf[0], (int)input_row, output_buf, 0, num_rows, + cinfo->output_width); } @@ -432,8 +448,8 @@ grayscale_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, */ METHODDEF(void) -gray_rgb_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows) +gray_rgb_convert(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { switch (cinfo->out_color_space) { case JCS_EXT_RGB: @@ -477,8 +493,8 @@ gray_rgb_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, */ METHODDEF(void) -rgb_rgb_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows) +rgb_rgb_convert(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { switch (cinfo->out_color_space) { case JCS_EXT_RGB: @@ -525,17 +541,18 @@ rgb_rgb_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, */ METHODDEF(void) -ycck_cmyk_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows) +ycck_cmyk_convert(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { +#if BITS_IN_JSAMPLE != 16 my_cconvert_ptr cconvert = (my_cconvert_ptr)cinfo->cconvert; register int y, cb, cr; - register JSAMPROW outptr; - register JSAMPROW inptr0, inptr1, inptr2, inptr3; + register _JSAMPROW outptr; + register _JSAMPROW inptr0, inptr1, inptr2, inptr3; register JDIMENSION col; JDIMENSION num_cols = cinfo->output_width; /* copy these pointers into registers if possible */ - register JSAMPLE *range_limit = cinfo->sample_range_limit; + register _JSAMPLE *range_limit = (_JSAMPLE *)cinfo->sample_range_limit; register int *Crrtab = cconvert->Cr_r_tab; register int *Cbbtab = cconvert->Cb_b_tab; register JLONG *Crgtab = cconvert->Cr_g_tab; @@ -554,16 +571,19 @@ ycck_cmyk_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, cb = inptr1[col]; cr = inptr2[col]; /* Range-limiting is essential due to noise introduced by DCT losses. */ - outptr[0] = range_limit[MAXJSAMPLE - (y + Crrtab[cr])]; /* red */ - outptr[1] = range_limit[MAXJSAMPLE - (y + /* green */ + outptr[0] = range_limit[_MAXJSAMPLE - (y + Crrtab[cr])]; /* red */ + outptr[1] = range_limit[_MAXJSAMPLE - (y + /* green */ ((int)RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS)))]; - outptr[2] = range_limit[MAXJSAMPLE - (y + Cbbtab[cb])]; /* blue */ + outptr[2] = range_limit[_MAXJSAMPLE - (y + Cbbtab[cb])]; /* blue */ /* K passes through unchanged */ outptr[3] = inptr3[col]; outptr += 4; } } +#else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); +#endif } @@ -653,8 +673,8 @@ static INLINE boolean is_big_endian(void) METHODDEF(void) -ycc_rgb565_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows) +ycc_rgb565_convert(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { if (is_big_endian()) ycc_rgb565_convert_be(cinfo, input_buf, input_row, output_buf, num_rows); @@ -664,8 +684,8 @@ ycc_rgb565_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, METHODDEF(void) -ycc_rgb565D_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows) +ycc_rgb565D_convert(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { if (is_big_endian()) ycc_rgb565D_convert_be(cinfo, input_buf, input_row, output_buf, num_rows); @@ -675,8 +695,8 @@ ycc_rgb565D_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, METHODDEF(void) -rgb_rgb565_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows) +rgb_rgb565_convert(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { if (is_big_endian()) rgb_rgb565_convert_be(cinfo, input_buf, input_row, output_buf, num_rows); @@ -686,8 +706,8 @@ rgb_rgb565_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, METHODDEF(void) -rgb_rgb565D_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows) +rgb_rgb565D_convert(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { if (is_big_endian()) rgb_rgb565D_convert_be(cinfo, input_buf, input_row, output_buf, num_rows); @@ -697,8 +717,8 @@ rgb_rgb565D_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, METHODDEF(void) -gray_rgb565_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows) +gray_rgb565_convert(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { if (is_big_endian()) gray_rgb565_convert_be(cinfo, input_buf, input_row, output_buf, num_rows); @@ -708,8 +728,8 @@ gray_rgb565_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, METHODDEF(void) -gray_rgb565D_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows) +gray_rgb565D_convert(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION input_row, _JSAMPARRAY output_buf, int num_rows) { if (is_big_endian()) gray_rgb565D_convert_be(cinfo, input_buf, input_row, output_buf, num_rows); @@ -734,11 +754,14 @@ start_pass_dcolor(j_decompress_ptr cinfo) */ GLOBAL(void) -jinit_color_deconverter(j_decompress_ptr cinfo) +_jinit_color_deconverter(j_decompress_ptr cinfo) { my_cconvert_ptr cconvert; int ci; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + cconvert = (my_cconvert_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, sizeof(my_color_deconverter)); @@ -773,19 +796,24 @@ jinit_color_deconverter(j_decompress_ptr cinfo) /* Set out_color_components and conversion method based on requested space. * Also clear the component_needed flags for any unused components, * so that earlier pipeline stages can avoid useless computation. + * NOTE: We do not allow any lossy color conversion algorithms in lossless + * mode. */ switch (cinfo->out_color_space) { case JCS_GRAYSCALE: + if (cinfo->master->lossless && + cinfo->jpeg_color_space != cinfo->out_color_space) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); cinfo->out_color_components = 1; if (cinfo->jpeg_color_space == JCS_GRAYSCALE || cinfo->jpeg_color_space == JCS_YCbCr) { - cconvert->pub.color_convert = grayscale_convert; + cconvert->pub._color_convert = grayscale_convert; /* For color->grayscale conversion, only the Y (0) component is needed */ for (ci = 1; ci < cinfo->num_components; ci++) cinfo->comp_info[ci].component_needed = FALSE; } else if (cinfo->jpeg_color_space == JCS_RGB) { - cconvert->pub.color_convert = rgb_gray_convert; + cconvert->pub._color_convert = rgb_gray_convert; build_rgb_y_table(cinfo); } else ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); @@ -802,65 +830,78 @@ jinit_color_deconverter(j_decompress_ptr cinfo) case JCS_EXT_BGRA: case JCS_EXT_ABGR: case JCS_EXT_ARGB: + if (cinfo->master->lossless && cinfo->jpeg_color_space != JCS_RGB) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); cinfo->out_color_components = rgb_pixelsize[cinfo->out_color_space]; if (cinfo->jpeg_color_space == JCS_YCbCr) { +#ifdef WITH_SIMD if (jsimd_can_ycc_rgb()) - cconvert->pub.color_convert = jsimd_ycc_rgb_convert; - else { - cconvert->pub.color_convert = ycc_rgb_convert; + cconvert->pub._color_convert = jsimd_ycc_rgb_convert; + else +#endif + { + cconvert->pub._color_convert = ycc_rgb_convert; build_ycc_rgb_table(cinfo); } } else if (cinfo->jpeg_color_space == JCS_GRAYSCALE) { - cconvert->pub.color_convert = gray_rgb_convert; + cconvert->pub._color_convert = gray_rgb_convert; } else if (cinfo->jpeg_color_space == JCS_RGB) { if (rgb_red[cinfo->out_color_space] == 0 && rgb_green[cinfo->out_color_space] == 1 && rgb_blue[cinfo->out_color_space] == 2 && rgb_pixelsize[cinfo->out_color_space] == 3) - cconvert->pub.color_convert = null_convert; + cconvert->pub._color_convert = null_convert; else - cconvert->pub.color_convert = rgb_rgb_convert; + cconvert->pub._color_convert = rgb_rgb_convert; } else ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); break; case JCS_RGB565: + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); cinfo->out_color_components = 3; if (cinfo->dither_mode == JDITHER_NONE) { if (cinfo->jpeg_color_space == JCS_YCbCr) { +#ifdef WITH_SIMD if (jsimd_can_ycc_rgb565()) - cconvert->pub.color_convert = jsimd_ycc_rgb565_convert; - else { - cconvert->pub.color_convert = ycc_rgb565_convert; + cconvert->pub._color_convert = jsimd_ycc_rgb565_convert; + else +#endif + { + cconvert->pub._color_convert = ycc_rgb565_convert; build_ycc_rgb_table(cinfo); } } else if (cinfo->jpeg_color_space == JCS_GRAYSCALE) { - cconvert->pub.color_convert = gray_rgb565_convert; + cconvert->pub._color_convert = gray_rgb565_convert; } else if (cinfo->jpeg_color_space == JCS_RGB) { - cconvert->pub.color_convert = rgb_rgb565_convert; + cconvert->pub._color_convert = rgb_rgb565_convert; } else ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); } else { /* only ordered dithering is supported */ if (cinfo->jpeg_color_space == JCS_YCbCr) { - cconvert->pub.color_convert = ycc_rgb565D_convert; + cconvert->pub._color_convert = ycc_rgb565D_convert; build_ycc_rgb_table(cinfo); } else if (cinfo->jpeg_color_space == JCS_GRAYSCALE) { - cconvert->pub.color_convert = gray_rgb565D_convert; + cconvert->pub._color_convert = gray_rgb565D_convert; } else if (cinfo->jpeg_color_space == JCS_RGB) { - cconvert->pub.color_convert = rgb_rgb565D_convert; + cconvert->pub._color_convert = rgb_rgb565D_convert; } else ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); } break; case JCS_CMYK: + if (cinfo->master->lossless && + cinfo->jpeg_color_space != cinfo->out_color_space) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); cinfo->out_color_components = 4; if (cinfo->jpeg_color_space == JCS_YCCK) { - cconvert->pub.color_convert = ycck_cmyk_convert; + cconvert->pub._color_convert = ycck_cmyk_convert; build_ycc_rgb_table(cinfo); } else if (cinfo->jpeg_color_space == JCS_CMYK) { - cconvert->pub.color_convert = null_convert; + cconvert->pub._color_convert = null_convert; } else ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); break; @@ -869,7 +910,7 @@ jinit_color_deconverter(j_decompress_ptr cinfo) /* Permit null conversion to same output space */ if (cinfo->out_color_space == cinfo->jpeg_color_space) { cinfo->out_color_components = cinfo->num_components; - cconvert->pub.color_convert = null_convert; + cconvert->pub._color_convert = null_convert; } else /* unsupported non-null conversion */ ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); break; @@ -880,3 +921,5 @@ jinit_color_deconverter(j_decompress_ptr cinfo) else cinfo->output_components = cinfo->out_color_components; } + +#endif /* BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED) */ diff --git a/jdct.h b/jdct.h index 66d1718..0411a79 100644 --- a/jdct.h +++ b/jdct.h @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2015, D. R. Commander. + * Copyright (C) 2015, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -15,13 +15,15 @@ * machine-dependent tuning (e.g., assembly coding). */ +#include "jsamplecomp.h" + /* * A forward DCT routine is given a pointer to a work area of type DCTELEM[]; * the DCT is to be performed in-place in that buffer. Type DCTELEM is int * for 8-bit samples, JLONG for 12-bit samples. (NOTE: Floating-point DCT * implementations use an array of type FAST_FLOAT, instead.) - * The DCT inputs are expected to be signed (range +-CENTERJSAMPLE). + * The DCT inputs are expected to be signed (range +-_CENTERJSAMPLE). * The DCT outputs are returned scaled up by a factor of 8; they therefore * have a range of +-8K for 8-bit data, +-128K for 12-bit data. This * convention improves accuracy in integer implementations and saves some @@ -76,78 +78,89 @@ typedef FAST_FLOAT FLOAT_MULT_TYPE; /* preferred floating type */ /* * Each IDCT routine is responsible for range-limiting its results and - * converting them to unsigned form (0..MAXJSAMPLE). The raw outputs could + * converting them to unsigned form (0.._MAXJSAMPLE). The raw outputs could * be quite far out of range if the input data is corrupt, so a bulletproof * range-limiting step is required. We use a mask-and-table-lookup method * to do the combined operations quickly. See the comments with * prepare_range_limit_table (in jdmaster.c) for more info. */ -#define IDCT_range_limit(cinfo) ((cinfo)->sample_range_limit + CENTERJSAMPLE) +#define IDCT_range_limit(cinfo) \ + ((_JSAMPLE *)((cinfo)->sample_range_limit) + _CENTERJSAMPLE) -#define RANGE_MASK (MAXJSAMPLE * 4 + 3) /* 2 bits wider than legal samples */ +#define RANGE_MASK (_MAXJSAMPLE * 4 + 3) /* 2 bits wider than legal samples */ /* Extern declarations for the forward and inverse DCT routines. */ -EXTERN(void) jpeg_fdct_islow(DCTELEM *data); -EXTERN(void) jpeg_fdct_ifast(DCTELEM *data); +EXTERN(void) _jpeg_fdct_islow(DCTELEM *data); +EXTERN(void) _jpeg_fdct_ifast(DCTELEM *data); EXTERN(void) jpeg_fdct_float(FAST_FLOAT *data); -EXTERN(void) jpeg_idct_islow(j_decompress_ptr cinfo, - jpeg_component_info *compptr, JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col); -EXTERN(void) jpeg_idct_ifast(j_decompress_ptr cinfo, - jpeg_component_info *compptr, JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col); -EXTERN(void) jpeg_idct_float(j_decompress_ptr cinfo, - jpeg_component_info *compptr, JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col); -EXTERN(void) jpeg_idct_7x7(j_decompress_ptr cinfo, - jpeg_component_info *compptr, JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col); -EXTERN(void) jpeg_idct_6x6(j_decompress_ptr cinfo, - jpeg_component_info *compptr, JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col); -EXTERN(void) jpeg_idct_5x5(j_decompress_ptr cinfo, - jpeg_component_info *compptr, JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col); -EXTERN(void) jpeg_idct_4x4(j_decompress_ptr cinfo, - jpeg_component_info *compptr, JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col); -EXTERN(void) jpeg_idct_3x3(j_decompress_ptr cinfo, - jpeg_component_info *compptr, JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col); -EXTERN(void) jpeg_idct_2x2(j_decompress_ptr cinfo, - jpeg_component_info *compptr, JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col); -EXTERN(void) jpeg_idct_1x1(j_decompress_ptr cinfo, - jpeg_component_info *compptr, JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col); -EXTERN(void) jpeg_idct_9x9(j_decompress_ptr cinfo, - jpeg_component_info *compptr, JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col); -EXTERN(void) jpeg_idct_10x10(j_decompress_ptr cinfo, - jpeg_component_info *compptr, JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col); -EXTERN(void) jpeg_idct_11x11(j_decompress_ptr cinfo, - jpeg_component_info *compptr, JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col); -EXTERN(void) jpeg_idct_12x12(j_decompress_ptr cinfo, - jpeg_component_info *compptr, JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col); -EXTERN(void) jpeg_idct_13x13(j_decompress_ptr cinfo, - jpeg_component_info *compptr, JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col); -EXTERN(void) jpeg_idct_14x14(j_decompress_ptr cinfo, - jpeg_component_info *compptr, JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col); -EXTERN(void) jpeg_idct_15x15(j_decompress_ptr cinfo, - jpeg_component_info *compptr, JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col); -EXTERN(void) jpeg_idct_16x16(j_decompress_ptr cinfo, - jpeg_component_info *compptr, JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col); +EXTERN(void) _jpeg_idct_islow(j_decompress_ptr cinfo, + jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col); +EXTERN(void) _jpeg_idct_ifast(j_decompress_ptr cinfo, + jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col); +EXTERN(void) _jpeg_idct_float(j_decompress_ptr cinfo, + jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col); +EXTERN(void) _jpeg_idct_7x7(j_decompress_ptr cinfo, + jpeg_component_info *compptr, JCOEFPTR coef_block, + _JSAMPARRAY output_buf, JDIMENSION output_col); +EXTERN(void) _jpeg_idct_6x6(j_decompress_ptr cinfo, + jpeg_component_info *compptr, JCOEFPTR coef_block, + _JSAMPARRAY output_buf, JDIMENSION output_col); +EXTERN(void) _jpeg_idct_5x5(j_decompress_ptr cinfo, + jpeg_component_info *compptr, JCOEFPTR coef_block, + _JSAMPARRAY output_buf, JDIMENSION output_col); +EXTERN(void) _jpeg_idct_4x4(j_decompress_ptr cinfo, + jpeg_component_info *compptr, JCOEFPTR coef_block, + _JSAMPARRAY output_buf, JDIMENSION output_col); +EXTERN(void) _jpeg_idct_3x3(j_decompress_ptr cinfo, + jpeg_component_info *compptr, JCOEFPTR coef_block, + _JSAMPARRAY output_buf, JDIMENSION output_col); +EXTERN(void) _jpeg_idct_2x2(j_decompress_ptr cinfo, + jpeg_component_info *compptr, JCOEFPTR coef_block, + _JSAMPARRAY output_buf, JDIMENSION output_col); +EXTERN(void) _jpeg_idct_1x1(j_decompress_ptr cinfo, + jpeg_component_info *compptr, JCOEFPTR coef_block, + _JSAMPARRAY output_buf, JDIMENSION output_col); +EXTERN(void) _jpeg_idct_9x9(j_decompress_ptr cinfo, + jpeg_component_info *compptr, JCOEFPTR coef_block, + _JSAMPARRAY output_buf, JDIMENSION output_col); +EXTERN(void) _jpeg_idct_10x10(j_decompress_ptr cinfo, + jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col); +EXTERN(void) _jpeg_idct_11x11(j_decompress_ptr cinfo, + jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col); +EXTERN(void) _jpeg_idct_12x12(j_decompress_ptr cinfo, + jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col); +EXTERN(void) _jpeg_idct_13x13(j_decompress_ptr cinfo, + jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col); +EXTERN(void) _jpeg_idct_14x14(j_decompress_ptr cinfo, + jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col); +EXTERN(void) _jpeg_idct_15x15(j_decompress_ptr cinfo, + jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col); +EXTERN(void) _jpeg_idct_16x16(j_decompress_ptr cinfo, + jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col); /* diff --git a/jddctmgr.c b/jddctmgr.c index e78d7be..0bd8c2b 100644 --- a/jddctmgr.c +++ b/jddctmgr.c @@ -26,7 +26,7 @@ #include "jpeglib.h" #include "jdct.h" /* Private declarations for DCT subsystem */ #include "jsimddct.h" -#include "jpegcomp.h" +#include "jpegapicomp.h" /* @@ -100,7 +100,7 @@ start_pass(j_decompress_ptr cinfo) int ci, i; jpeg_component_info *compptr; int method = 0; - inverse_DCT_method_ptr method_ptr = NULL; + _inverse_DCT_method_ptr method_ptr = NULL; JQUANT_TBL *qtbl; for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; @@ -109,42 +109,46 @@ start_pass(j_decompress_ptr cinfo) switch (compptr->_DCT_scaled_size) { #ifdef IDCT_SCALING_SUPPORTED case 1: - method_ptr = jpeg_idct_1x1; + method_ptr = _jpeg_idct_1x1; method = JDCT_ISLOW; /* jidctred uses islow-style table */ break; case 2: +#ifdef WITH_SIMD if (jsimd_can_idct_2x2()) method_ptr = jsimd_idct_2x2; else - method_ptr = jpeg_idct_2x2; +#endif + method_ptr = _jpeg_idct_2x2; method = JDCT_ISLOW; /* jidctred uses islow-style table */ break; case 3: - method_ptr = jpeg_idct_3x3; + method_ptr = _jpeg_idct_3x3; method = JDCT_ISLOW; /* jidctint uses islow-style table */ break; case 4: +#ifdef WITH_SIMD if (jsimd_can_idct_4x4()) method_ptr = jsimd_idct_4x4; else - method_ptr = jpeg_idct_4x4; +#endif + method_ptr = _jpeg_idct_4x4; method = JDCT_ISLOW; /* jidctred uses islow-style table */ break; case 5: - method_ptr = jpeg_idct_5x5; + method_ptr = _jpeg_idct_5x5; method = JDCT_ISLOW; /* jidctint uses islow-style table */ break; case 6: -#if defined(__mips__) +#if defined(WITH_SIMD) && defined(__mips__) if (jsimd_can_idct_6x6()) method_ptr = jsimd_idct_6x6; else #endif - method_ptr = jpeg_idct_6x6; + method_ptr = _jpeg_idct_6x6; method = JDCT_ISLOW; /* jidctint uses islow-style table */ break; case 7: - method_ptr = jpeg_idct_7x7; + method_ptr = _jpeg_idct_7x7; method = JDCT_ISLOW; /* jidctint uses islow-style table */ break; #endif @@ -152,28 +156,34 @@ start_pass(j_decompress_ptr cinfo) switch (cinfo->dct_method) { #ifdef DCT_ISLOW_SUPPORTED case JDCT_ISLOW: +#ifdef WITH_SIMD if (jsimd_can_idct_islow()) method_ptr = jsimd_idct_islow; else - method_ptr = jpeg_idct_islow; +#endif + method_ptr = _jpeg_idct_islow; method = JDCT_ISLOW; break; #endif #ifdef DCT_IFAST_SUPPORTED case JDCT_IFAST: +#ifdef WITH_SIMD if (jsimd_can_idct_ifast()) method_ptr = jsimd_idct_ifast; else - method_ptr = jpeg_idct_ifast; +#endif + method_ptr = _jpeg_idct_ifast; method = JDCT_IFAST; break; #endif #ifdef DCT_FLOAT_SUPPORTED case JDCT_FLOAT: +#ifdef WITH_SIMD if (jsimd_can_idct_float()) method_ptr = jsimd_idct_float; else - method_ptr = jpeg_idct_float; +#endif + method_ptr = _jpeg_idct_float; method = JDCT_FLOAT; break; #endif @@ -184,40 +194,40 @@ start_pass(j_decompress_ptr cinfo) break; #ifdef IDCT_SCALING_SUPPORTED case 9: - method_ptr = jpeg_idct_9x9; + method_ptr = _jpeg_idct_9x9; method = JDCT_ISLOW; /* jidctint uses islow-style table */ break; case 10: - method_ptr = jpeg_idct_10x10; + method_ptr = _jpeg_idct_10x10; method = JDCT_ISLOW; /* jidctint uses islow-style table */ break; case 11: - method_ptr = jpeg_idct_11x11; + method_ptr = _jpeg_idct_11x11; method = JDCT_ISLOW; /* jidctint uses islow-style table */ break; case 12: -#if defined(__mips__) +#if defined(WITH_SIMD) && defined(__mips__) if (jsimd_can_idct_12x12()) method_ptr = jsimd_idct_12x12; else #endif - method_ptr = jpeg_idct_12x12; + method_ptr = _jpeg_idct_12x12; method = JDCT_ISLOW; /* jidctint uses islow-style table */ break; case 13: - method_ptr = jpeg_idct_13x13; + method_ptr = _jpeg_idct_13x13; method = JDCT_ISLOW; /* jidctint uses islow-style table */ break; case 14: - method_ptr = jpeg_idct_14x14; + method_ptr = _jpeg_idct_14x14; method = JDCT_ISLOW; /* jidctint uses islow-style table */ break; case 15: - method_ptr = jpeg_idct_15x15; + method_ptr = _jpeg_idct_15x15; method = JDCT_ISLOW; /* jidctint uses islow-style table */ break; case 16: - method_ptr = jpeg_idct_16x16; + method_ptr = _jpeg_idct_16x16; method = JDCT_ISLOW; /* jidctint uses islow-style table */ break; #endif @@ -225,7 +235,7 @@ start_pass(j_decompress_ptr cinfo) ERREXIT1(cinfo, JERR_BAD_DCTSIZE, compptr->_DCT_scaled_size); break; } - idct->pub.inverse_DCT[ci] = method_ptr; + idct->pub._inverse_DCT[ci] = method_ptr; /* Create multiplier table from quant table. * However, we can skip this if the component is uninteresting * or if we already built the table. Also, if no quant table @@ -327,12 +337,15 @@ start_pass(j_decompress_ptr cinfo) */ GLOBAL(void) -jinit_inverse_dct(j_decompress_ptr cinfo) +_jinit_inverse_dct(j_decompress_ptr cinfo) { my_idct_ptr idct; int ci; jpeg_component_info *compptr; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + idct = (my_idct_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, sizeof(my_idct_controller)); diff --git a/jddiffct.c b/jddiffct.c new file mode 100644 index 0000000..f1d7f61 --- /dev/null +++ b/jddiffct.c @@ -0,0 +1,403 @@ +/* + * jddiffct.c + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1994-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file contains the [un]difference buffer controller for decompression. + * This controller is the top level of the lossless JPEG decompressor proper. + * The difference buffer lies between the entropy decoding and + * prediction/undifferencing steps. The undifference buffer lies between the + * prediction/undifferencing and scaling steps. + * + * In buffered-image mode, this controller is the interface between + * input-oriented processing and output-oriented processing. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jlossls.h" /* Private declarations for lossless codec */ + + +#ifdef D_LOSSLESS_SUPPORTED + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_coef_controller pub; /* public fields */ + + /* These variables keep track of the current location of the input side. */ + /* cinfo->input_iMCU_row is also used for this. */ + JDIMENSION MCU_ctr; /* counts MCUs processed in current row */ + unsigned int restart_rows_to_go; /* MCU rows left in this restart + interval */ + unsigned int MCU_vert_offset; /* counts MCU rows within iMCU row */ + unsigned int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + /* The output side's location is represented by cinfo->output_iMCU_row. */ + + JDIFFARRAY diff_buf[MAX_COMPONENTS]; /* iMCU row of differences */ + JDIFFARRAY undiff_buf[MAX_COMPONENTS]; /* iMCU row of undiff'd samples */ + +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* In multi-pass modes, we need a virtual sample array for each component. */ + jvirt_sarray_ptr whole_image[MAX_COMPONENTS]; +#endif +} my_diff_controller; + +typedef my_diff_controller *my_diff_ptr; + +/* Forward declarations */ +METHODDEF(int) decompress_data(j_decompress_ptr cinfo, _JSAMPIMAGE output_buf); +#ifdef D_MULTISCAN_FILES_SUPPORTED +METHODDEF(int) output_data(j_decompress_ptr cinfo, _JSAMPIMAGE output_buf); +#endif + + +LOCAL(void) +start_iMCU_row(j_decompress_ptr cinfo) +/* Reset within-iMCU-row counters for a new row (input side) */ +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + * But at the bottom of the image, process only what's left. + */ + if (cinfo->comps_in_scan > 1) { + diff->MCU_rows_per_iMCU_row = 1; + } else { + if (cinfo->input_iMCU_row < (cinfo->total_iMCU_rows-1)) + diff->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + else + diff->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + } + + diff->MCU_ctr = 0; + diff->MCU_vert_offset = 0; +} + + +/* + * Initialize for an input processing pass. + */ + +METHODDEF(void) +start_input_pass(j_decompress_ptr cinfo) +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + + /* Because it is hitching a ride on the jpeg_inverse_dct struct, + * start_pass_lossless() will be called at the start of the output pass. + * This ensures that it will be called at the start of the input pass as + * well. + */ + (*cinfo->idct->start_pass) (cinfo); + + /* Check that the restart interval is an integer multiple of the number + * of MCUs in an MCU row. + */ + if (cinfo->restart_interval % cinfo->MCUs_per_row != 0) + ERREXIT2(cinfo, JERR_BAD_RESTART, + cinfo->restart_interval, cinfo->MCUs_per_row); + + /* Initialize restart counter */ + diff->restart_rows_to_go = cinfo->restart_interval / cinfo->MCUs_per_row; + + cinfo->input_iMCU_row = 0; + start_iMCU_row(cinfo); +} + + +/* + * Check for a restart marker & resynchronize decoder, undifferencer. + * Returns FALSE if must suspend. + */ + +METHODDEF(boolean) +process_restart(j_decompress_ptr cinfo) +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + + if (!(*cinfo->entropy->process_restart) (cinfo)) + return FALSE; + + (*cinfo->idct->start_pass) (cinfo); + + /* Reset restart counter */ + diff->restart_rows_to_go = cinfo->restart_interval / cinfo->MCUs_per_row; + + return TRUE; +} + + +/* + * Initialize for an output processing pass. + */ + +METHODDEF(void) +start_output_pass(j_decompress_ptr cinfo) +{ + cinfo->output_iMCU_row = 0; +} + + +/* + * Decompress and return some data in the supplied buffer. + * Always attempts to emit one fully interleaved MCU row ("iMCU" row). + * Input and output must run in lockstep since we have only a one-MCU buffer. + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + * + * NB: output_buf contains a plane for each component in image, + * which we index according to the component's SOF position. + */ + +METHODDEF(int) +decompress_data(j_decompress_ptr cinfo, _JSAMPIMAGE output_buf) +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + lossless_decomp_ptr losslessd = (lossless_decomp_ptr)cinfo->idct; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + JDIMENSION MCU_count; /* number of MCUs decoded */ + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + int ci, compi, row, prev_row; + unsigned int yoffset; + jpeg_component_info *compptr; + + /* Loop to process as much as one whole iMCU row */ + for (yoffset = diff->MCU_vert_offset; yoffset < diff->MCU_rows_per_iMCU_row; + yoffset++) { + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (diff->restart_rows_to_go == 0) + if (!process_restart(cinfo)) + return JPEG_SUSPENDED; + } + + MCU_col_num = diff->MCU_ctr; + /* Try to fetch an MCU row (or remaining portion of suspended MCU row). */ + MCU_count = + (*cinfo->entropy->decode_mcus) (cinfo, + diff->diff_buf, yoffset, MCU_col_num, + cinfo->MCUs_per_row - MCU_col_num); + if (MCU_count != cinfo->MCUs_per_row - MCU_col_num) { + /* Suspension forced; update state counters and exit */ + diff->MCU_vert_offset = yoffset; + diff->MCU_ctr += MCU_count; + return JPEG_SUSPENDED; + } + + /* Account for restart interval (no-op if not using restarts) */ + if (cinfo->restart_interval) + diff->restart_rows_to_go--; + + /* Completed an MCU row, but perhaps not an iMCU row */ + diff->MCU_ctr = 0; + } + + /* + * Undifference and scale each scanline of the disassembled MCU row + * separately. We do not process dummy samples at the end of a scanline + * or dummy rows at the end of the image. + */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + compi = compptr->component_index; + for (row = 0, prev_row = compptr->v_samp_factor - 1; + row < (cinfo->input_iMCU_row == last_iMCU_row ? + compptr->last_row_height : compptr->v_samp_factor); + prev_row = row, row++) { + (*losslessd->predict_undifference[compi]) + (cinfo, compi, diff->diff_buf[compi][row], + diff->undiff_buf[compi][prev_row], diff->undiff_buf[compi][row], + compptr->width_in_blocks); + (*losslessd->scaler_scale) (cinfo, diff->undiff_buf[compi][row], + output_buf[compi][row], + compptr->width_in_blocks); + } + } + + /* Completed the iMCU row, advance counters for next one. + * + * NB: output_data will increment output_iMCU_row. + * This counter is not needed for the single-pass case + * or the input side of the multi-pass case. + */ + if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) { + start_iMCU_row(cinfo); + return JPEG_ROW_COMPLETED; + } + /* Completed the scan */ + (*cinfo->inputctl->finish_input_pass) (cinfo); + return JPEG_SCAN_COMPLETED; +} + + +/* + * Dummy consume-input routine for single-pass operation. + */ + +METHODDEF(int) +dummy_consume_data(j_decompress_ptr cinfo) +{ + return JPEG_SUSPENDED; /* Always indicate nothing was done */ +} + + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +/* + * Consume input data and store it in the full-image sample buffer. + * We read as much as one fully interleaved MCU row ("iMCU" row) per call, + * ie, v_samp_factor rows for each component in the scan. + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + */ + +METHODDEF(int) +consume_data(j_decompress_ptr cinfo) +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + int ci, compi; + _JSAMPARRAY buffer[MAX_COMPS_IN_SCAN]; + jpeg_component_info *compptr; + + /* Align the virtual buffers for the components used in this scan. */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + compi = compptr->component_index; + buffer[compi] = (_JSAMPARRAY)(*cinfo->mem->access_virt_sarray) + ((j_common_ptr)cinfo, diff->whole_image[compi], + cinfo->input_iMCU_row * compptr->v_samp_factor, + (JDIMENSION)compptr->v_samp_factor, TRUE); + } + + return decompress_data(cinfo, buffer); +} + + +/* + * Output some data from the full-image sample buffer in the multi-pass case. + * Always attempts to emit one fully interleaved MCU row ("iMCU" row). + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + * + * NB: output_buf contains a plane for each component in image. + */ + +METHODDEF(int) +output_data(j_decompress_ptr cinfo, _JSAMPIMAGE output_buf) +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + int ci, samp_rows, row; + _JSAMPARRAY buffer; + jpeg_component_info *compptr; + + /* Force some input to be done if we are getting ahead of the input. */ + while (cinfo->input_scan_number < cinfo->output_scan_number || + (cinfo->input_scan_number == cinfo->output_scan_number && + cinfo->input_iMCU_row <= cinfo->output_iMCU_row)) { + if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED) + return JPEG_SUSPENDED; + } + + /* OK, output from the virtual arrays. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Align the virtual buffer for this component. */ + buffer = (_JSAMPARRAY)(*cinfo->mem->access_virt_sarray) + ((j_common_ptr)cinfo, diff->whole_image[ci], + cinfo->output_iMCU_row * compptr->v_samp_factor, + (JDIMENSION)compptr->v_samp_factor, FALSE); + + if (cinfo->output_iMCU_row < last_iMCU_row) + samp_rows = compptr->v_samp_factor; + else { + /* NB: can't use last_row_height here; it is input-side-dependent! */ + samp_rows = (int)(compptr->height_in_blocks % compptr->v_samp_factor); + if (samp_rows == 0) samp_rows = compptr->v_samp_factor; + } + + for (row = 0; row < samp_rows; row++) { + memcpy(output_buf[ci][row], buffer[row], + compptr->width_in_blocks * sizeof(_JSAMPLE)); + } + } + + if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows) + return JPEG_ROW_COMPLETED; + return JPEG_SCAN_COMPLETED; +} + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + + +/* + * Initialize difference buffer controller. + */ + +GLOBAL(void) +_jinit_d_diff_controller(j_decompress_ptr cinfo, boolean need_full_buffer) +{ + my_diff_ptr diff; + int ci; + jpeg_component_info *compptr; + + diff = (my_diff_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, + sizeof(my_diff_controller)); + cinfo->coef = (struct jpeg_d_coef_controller *)diff; + diff->pub.start_input_pass = start_input_pass; + diff->pub.start_output_pass = start_output_pass; + + /* Create the [un]difference buffers. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + diff->diff_buf[ci] = + ALLOC_DARRAY(JPOOL_IMAGE, + (JDIMENSION)jround_up((long)compptr->width_in_blocks, + (long)compptr->h_samp_factor), + (JDIMENSION)compptr->v_samp_factor); + diff->undiff_buf[ci] = + ALLOC_DARRAY(JPOOL_IMAGE, + (JDIMENSION)jround_up((long)compptr->width_in_blocks, + (long)compptr->h_samp_factor), + (JDIMENSION)compptr->v_samp_factor); + } + + if (need_full_buffer) { +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* Allocate a full-image virtual array for each component. */ + int access_rows; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + access_rows = compptr->v_samp_factor; + diff->whole_image[ci] = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr)cinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION)jround_up((long)compptr->width_in_blocks, + (long)compptr->h_samp_factor), + (JDIMENSION)jround_up((long)compptr->height_in_blocks, + (long)compptr->v_samp_factor), + (JDIMENSION)access_rows); + } + diff->pub.consume_data = consume_data; + diff->pub._decompress_data = output_data; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + diff->pub.consume_data = dummy_consume_data; + diff->pub._decompress_data = decompress_data; + diff->whole_image[0] = NULL; /* flag for no virtual arrays */ + } +} + +#endif /* D_LOSSLESS_SUPPORTED */ diff --git a/jdhuff.c b/jdhuff.c index 679d221..cd8c084 100644 --- a/jdhuff.c +++ b/jdhuff.c @@ -3,8 +3,10 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: - * Copyright (C) 2009-2011, 2016, 2018-2019, D. R. Commander. + * Copyright (C) 2009-2011, 2016, 2018-2019, 2022, D. R. Commander. * Copyright (C) 2018, Matthias Räncker. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -24,8 +26,8 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jdhuff.h" /* Declarations shared with jdphuff.c */ -#include "jpegcomp.h" +#include "jdhuff.h" /* Declarations shared with jd*huff.c */ +#include "jpegapicomp.h" #include "jstdhuff.c" @@ -134,7 +136,7 @@ start_pass_huff_decoder(j_decompress_ptr cinfo) * Compute the derived values for a Huffman table. * This routine also performs some validation checks on the table. * - * Note this is also used by jdphuff.c. + * Note this is also used by jdphuff.c and jdlhuff.c. */ GLOBAL(void) @@ -245,14 +247,14 @@ jpeg_make_d_derived_tbl(j_decompress_ptr cinfo, boolean isDC, int tblno, /* Validate symbols as being reasonable. * For AC tables, we make no check, but accept all byte values 0..255. - * For DC tables, we require the symbols to be in range 0..15. - * (Tighter bounds could be applied depending on the data depth and mode, - * but this is sufficient to ensure safe decoding.) + * For DC tables, we require the symbols to be in range 0..15 in lossy mode + * and 0..16 in lossless mode. (Tighter bounds could be applied depending on + * the data depth and mode, but this is sufficient to ensure safe decoding.) */ if (isDC) { for (i = 0; i < numsymbols; i++) { int sym = htbl->huffval[i]; - if (sym < 0 || sym > 15) + if (sym < 0 || sym > (cinfo->master->lossless ? 16 : 15)) ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); } } @@ -260,7 +262,7 @@ jpeg_make_d_derived_tbl(j_decompress_ptr cinfo, boolean isDC, int tblno, /* - * Out-of-line code for bit fetching (shared with jdphuff.c). + * Out-of-line code for bit fetching (shared with jdphuff.c and jdlhuff.c). * See jdhuff.h for info about usage. * Note: current values of get_buffer and bits_left are passed as parameters, * but are returned in the corresponding fields of the state struct. diff --git a/jdhuff.h b/jdhuff.h index cfa0b7f..3eee002 100644 --- a/jdhuff.h +++ b/jdhuff.h @@ -3,6 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2010-2011, 2015-2016, 2021, D. R. Commander. * Copyright (C) 2018, Matthias Räncker. @@ -10,8 +12,9 @@ * file. * * This file contains declarations for Huffman entropy decoding routines - * that are shared between the sequential decoder (jdhuff.c) and the - * progressive decoder (jdphuff.c). No other modules need to see these. + * that are shared between the sequential decoder (jdhuff.c), the progressive + * decoder (jdphuff.c), and the lossless decoder (jdlhuff.c). No other modules + * need to see these. */ #include "jconfigint.h" diff --git a/jdinput.c b/jdinput.c index 1bc5aff..136bef5 100644 --- a/jdinput.c +++ b/jdinput.c @@ -3,6 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2010, 2016, 2018, 2022, D. R. Commander. * Copyright (C) 2015, Google, Inc. @@ -11,14 +13,15 @@ * * This file contains input control logic for the JPEG decompressor. * These routines are concerned with controlling the decompressor's input - * processing (marker reading and coefficient decoding). The actual input - * reading is done in jdmarker.c, jdhuff.c, and jdphuff.c. + * processing (marker reading and coefficient/difference decoding). + * The actual input reading is done in jdmarker.c, jdhuff.c, jdphuff.c, + * and jdlhuff.c. */ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jpegcomp.h" +#include "jpegapicomp.h" /* Private state */ @@ -46,6 +49,7 @@ initial_setup(j_decompress_ptr cinfo) { int ci; jpeg_component_info *compptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; /* Make sure image isn't bigger than I can handle */ if ((long)cinfo->image_height > (long)JPEG_MAX_DIMENSION || @@ -53,7 +57,12 @@ initial_setup(j_decompress_ptr cinfo) ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int)JPEG_MAX_DIMENSION); /* For now, precision must match compiled-in value... */ - if (cinfo->data_precision != BITS_IN_JSAMPLE) +#ifdef D_LOSSLESS_SUPPORTED + if (cinfo->data_precision != 8 && cinfo->data_precision != 12 && + cinfo->data_precision != 16) +#else + if (cinfo->data_precision != 8 && cinfo->data_precision != 12) +#endif ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); /* Check that number of components won't exceed internal array sizes */ @@ -78,36 +87,36 @@ initial_setup(j_decompress_ptr cinfo) } #if JPEG_LIB_VERSION >= 80 - cinfo->block_size = DCTSIZE; + cinfo->block_size = data_unit; cinfo->natural_order = jpeg_natural_order; cinfo->lim_Se = DCTSIZE2 - 1; #endif - /* We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE. - * In the full decompressor, this will be overridden by jdmaster.c; + /* We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE in lossy + * mode. In the full decompressor, this will be overridden by jdmaster.c; * but in the transcoder, jdmaster.c is not used, so we must do it here. */ #if JPEG_LIB_VERSION >= 70 - cinfo->min_DCT_h_scaled_size = cinfo->min_DCT_v_scaled_size = DCTSIZE; + cinfo->min_DCT_h_scaled_size = cinfo->min_DCT_v_scaled_size = data_unit; #else - cinfo->min_DCT_scaled_size = DCTSIZE; + cinfo->min_DCT_scaled_size = data_unit; #endif /* Compute dimensions of components */ for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { #if JPEG_LIB_VERSION >= 70 - compptr->DCT_h_scaled_size = compptr->DCT_v_scaled_size = DCTSIZE; + compptr->DCT_h_scaled_size = compptr->DCT_v_scaled_size = data_unit; #else - compptr->DCT_scaled_size = DCTSIZE; + compptr->DCT_scaled_size = data_unit; #endif - /* Size in DCT blocks */ + /* Size in data units */ compptr->width_in_blocks = (JDIMENSION) jdiv_round_up((long)cinfo->image_width * (long)compptr->h_samp_factor, - (long)(cinfo->max_h_samp_factor * DCTSIZE)); + (long)(cinfo->max_h_samp_factor * data_unit)); compptr->height_in_blocks = (JDIMENSION) jdiv_round_up((long)cinfo->image_height * (long)compptr->v_samp_factor, - (long)(cinfo->max_v_samp_factor * DCTSIZE)); + (long)(cinfo->max_v_samp_factor * data_unit)); /* Set the first and last MCU columns to decompress from multi-scan images. * By default, decompress all of the MCU columns. */ @@ -133,7 +142,7 @@ initial_setup(j_decompress_ptr cinfo) /* Compute number of fully interleaved MCU rows. */ cinfo->total_iMCU_rows = (JDIMENSION) jdiv_round_up((long)cinfo->image_height, - (long)(cinfo->max_v_samp_factor * DCTSIZE)); + (long)(cinfo->max_v_samp_factor * data_unit)); /* Decide whether file contains multiple scans */ if (cinfo->comps_in_scan < cinfo->num_components || cinfo->progressive_mode) @@ -150,6 +159,7 @@ per_scan_setup(j_decompress_ptr cinfo) { int ci, mcublks, tmp; jpeg_component_info *compptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; if (cinfo->comps_in_scan == 1) { @@ -160,14 +170,14 @@ per_scan_setup(j_decompress_ptr cinfo) cinfo->MCUs_per_row = compptr->width_in_blocks; cinfo->MCU_rows_in_scan = compptr->height_in_blocks; - /* For noninterleaved scan, always one block per MCU */ + /* For noninterleaved scan, always one data unit per MCU */ compptr->MCU_width = 1; compptr->MCU_height = 1; compptr->MCU_blocks = 1; compptr->MCU_sample_width = compptr->_DCT_scaled_size; compptr->last_col_width = 1; /* For noninterleaved scans, it is convenient to define last_row_height - * as the number of block rows present in the last iMCU row. + * as the number of data unit rows present in the last iMCU row. */ tmp = (int)(compptr->height_in_blocks % compptr->v_samp_factor); if (tmp == 0) tmp = compptr->v_samp_factor; @@ -187,22 +197,22 @@ per_scan_setup(j_decompress_ptr cinfo) /* Overall image size in MCUs */ cinfo->MCUs_per_row = (JDIMENSION) jdiv_round_up((long)cinfo->image_width, - (long)(cinfo->max_h_samp_factor * DCTSIZE)); + (long)(cinfo->max_h_samp_factor * data_unit)); cinfo->MCU_rows_in_scan = (JDIMENSION) jdiv_round_up((long)cinfo->image_height, - (long)(cinfo->max_v_samp_factor * DCTSIZE)); + (long)(cinfo->max_v_samp_factor * data_unit)); cinfo->blocks_in_MCU = 0; for (ci = 0; ci < cinfo->comps_in_scan; ci++) { compptr = cinfo->cur_comp_info[ci]; - /* Sampling factors give # of blocks of component in each MCU */ + /* Sampling factors give # of data units of component in each MCU */ compptr->MCU_width = compptr->h_samp_factor; compptr->MCU_height = compptr->v_samp_factor; compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height; compptr->MCU_sample_width = compptr->MCU_width * compptr->_DCT_scaled_size; - /* Figure number of non-dummy blocks in last MCU column & row */ + /* Figure number of non-dummy data units in last MCU column & row */ tmp = (int)(compptr->width_in_blocks % compptr->MCU_width); if (tmp == 0) tmp = compptr->MCU_width; compptr->last_col_width = tmp; @@ -281,7 +291,8 @@ METHODDEF(void) start_input_pass(j_decompress_ptr cinfo) { per_scan_setup(cinfo); - latch_quant_tables(cinfo); + if (!cinfo->master->lossless) + latch_quant_tables(cinfo); (*cinfo->entropy->start_pass) (cinfo); (*cinfo->coef->start_input_pass) (cinfo); cinfo->inputctl->consume_input = cinfo->coef->consume_data; @@ -290,8 +301,8 @@ start_input_pass(j_decompress_ptr cinfo) /* * Finish up after inputting a compressed-data scan. - * This is called by the coefficient controller after it's read all - * the expected data of the scan. + * This is called by the coefficient or difference controller after it's read + * all the expected data of the scan. */ METHODDEF(void) @@ -307,8 +318,8 @@ finish_input_pass(j_decompress_ptr cinfo) * Return value is JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. * * The consume_input method pointer points either here or to the - * coefficient controller's consume_data routine, depending on whether - * we are reading a compressed data segment or inter-segment markers. + * coefficient or difference controller's consume_data routine, depending on + * whether we are reading a compressed data segment or inter-segment markers. */ METHODDEF(int) diff --git a/jdlhuff.c b/jdlhuff.c new file mode 100644 index 0000000..9964830 --- /dev/null +++ b/jdlhuff.c @@ -0,0 +1,302 @@ +/* + * jdlhuff.c + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1991-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file contains Huffman entropy decoding routines for lossless JPEG. + * + * Much of the complexity here has to do with supporting input suspension. + * If the data source module demands suspension, we want to be able to back + * up to the start of the current MCU. To do this, we copy state variables + * into local working storage, and update them back to the permanent + * storage only upon successful completion of an MCU. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jlossls.h" /* Private declarations for lossless codec */ +#include "jdhuff.h" /* Declarations shared with jd*huff.c */ + + +#ifdef D_LOSSLESS_SUPPORTED + +typedef struct { + int ci, yoffset, MCU_width; +} lhd_output_ptr_info; + +/* + * Expanded entropy decoder object for Huffman decoding in lossless mode. + */ + +typedef struct { + struct jpeg_entropy_decoder pub; /* public fields */ + + /* These fields are loaded into local variables at start of each MCU. + * In case of suspension, we exit WITHOUT updating them. + */ + bitread_perm_state bitstate; /* Bit buffer at start of MCU */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + d_derived_tbl *derived_tbls[NUM_HUFF_TBLS]; + + /* Precalculated info set up by start_pass for use in decode_mcus: */ + + /* Pointers to derived tables to be used for each data unit within an MCU */ + d_derived_tbl *cur_tbls[D_MAX_BLOCKS_IN_MCU]; + + /* Pointers to the proper output difference row for each group of data units + * within an MCU. For each component, there are Vi groups of Hi data units. + */ + JDIFFROW output_ptr[D_MAX_BLOCKS_IN_MCU]; + + /* Number of output pointers in use for the current MCU. This is the sum + * of all Vi in the MCU. + */ + int num_output_ptrs; + + /* Information used for positioning the output pointers within the output + * difference rows. + */ + lhd_output_ptr_info output_ptr_info[D_MAX_BLOCKS_IN_MCU]; + + /* Index of the proper output pointer for each data unit within an MCU */ + int output_ptr_index[D_MAX_BLOCKS_IN_MCU]; + +} lhuff_entropy_decoder; + +typedef lhuff_entropy_decoder *lhuff_entropy_ptr; + + +/* + * Initialize for a Huffman-compressed scan. + */ + +METHODDEF(void) +start_pass_lhuff_decoder(j_decompress_ptr cinfo) +{ + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr)cinfo->entropy; + int ci, dctbl, sampn, ptrn, yoffset, xoffset; + jpeg_component_info *compptr; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + /* Make sure requested tables are present */ + if (dctbl < 0 || dctbl >= NUM_HUFF_TBLS || + cinfo->dc_huff_tbl_ptrs[dctbl] == NULL) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, dctbl); + /* Compute derived values for Huffman tables */ + /* We may do this more than once for a table, but it's not expensive */ + jpeg_make_d_derived_tbl(cinfo, TRUE, dctbl, + &entropy->derived_tbls[dctbl]); + } + + /* Precalculate decoding info for each sample in an MCU of this scan */ + for (sampn = 0, ptrn = 0; sampn < cinfo->blocks_in_MCU;) { + compptr = cinfo->cur_comp_info[cinfo->MCU_membership[sampn]]; + ci = compptr->component_index; + for (yoffset = 0; yoffset < compptr->MCU_height; yoffset++, ptrn++) { + /* Precalculate the setup info for each output pointer */ + entropy->output_ptr_info[ptrn].ci = ci; + entropy->output_ptr_info[ptrn].yoffset = yoffset; + entropy->output_ptr_info[ptrn].MCU_width = compptr->MCU_width; + for (xoffset = 0; xoffset < compptr->MCU_width; xoffset++, sampn++) { + /* Precalculate the output pointer index for each sample */ + entropy->output_ptr_index[sampn] = ptrn; + /* Precalculate which table to use for each sample */ + entropy->cur_tbls[sampn] = entropy->derived_tbls[compptr->dc_tbl_no]; + } + } + } + entropy->num_output_ptrs = ptrn; + + /* Initialize bitread state variables */ + entropy->bitstate.bits_left = 0; + entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ + entropy->pub.insufficient_data = FALSE; +} + + +/* + * Figure F.12: extend sign bit. + * On some machines, a shift and add will be faster than a table lookup. + */ + +#define AVOID_TABLES +#ifdef AVOID_TABLES + +#define NEG_1 ((unsigned int)-1) +#define HUFF_EXTEND(x, s) \ + ((x) + ((((x) - (1 << ((s) - 1))) >> 31) & (((NEG_1) << (s)) + 1))) + +#else + +#define HUFF_EXTEND(x, s) \ + ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)) + +static const int extend_test[16] = { /* entry n is 2**(n-1) */ + 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, + 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 +}; + +static const int extend_offset[16] = { /* entry n is (-1 << n) + 1 */ + 0, ((-1) << 1) + 1, ((-1) << 2) + 1, ((-1) << 3) + 1, ((-1) << 4) + 1, + ((-1) << 5) + 1, ((-1) << 6) + 1, ((-1) << 7) + 1, ((-1) << 8) + 1, + ((-1) << 9) + 1, ((-1) << 10) + 1, ((-1) << 11) + 1, ((-1) << 12) + 1, + ((-1) << 13) + 1, ((-1) << 14) + 1, ((-1) << 15) + 1 +}; + +#endif /* AVOID_TABLES */ + + +/* + * Check for a restart marker & resynchronize decoder. + * Returns FALSE if must suspend. + */ + +LOCAL(boolean) +process_restart(j_decompress_ptr cinfo) +{ + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr)cinfo->entropy; + + /* Throw away any unused bits remaining in bit buffer; */ + /* include any full bytes in next_marker's count of discarded bytes */ + cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8; + entropy->bitstate.bits_left = 0; + + /* Advance past the RSTn marker */ + if (!(*cinfo->marker->read_restart_marker) (cinfo)) + return FALSE; + + /* Reset out-of-data flag, unless read_restart_marker left us smack up + * against a marker. In that case we will end up treating the next data + * segment as empty, and we can avoid producing bogus output pixels by + * leaving the flag set. + */ + if (cinfo->unread_marker == 0) + entropy->pub.insufficient_data = FALSE; + + return TRUE; +} + + +/* + * Decode and return nMCU MCUs' worth of Huffman-compressed differences. + * Each MCU is also disassembled and placed accordingly in diff_buf. + * + * MCU_col_num specifies the column of the first MCU being requested within + * the MCU row. This tells us where to position the output row pointers in + * diff_buf. + * + * Returns the number of MCUs decoded. This may be less than nMCU MCUs if + * data source requested suspension. In that case no changes have been made + * to permanent state. (Exception: some output differences may already have + * been assigned. This is harmless for this module, since we'll just + * re-assign them on the next call.) + */ + +METHODDEF(JDIMENSION) +decode_mcus(j_decompress_ptr cinfo, JDIFFIMAGE diff_buf, + JDIMENSION MCU_row_num, JDIMENSION MCU_col_num, JDIMENSION nMCU) +{ + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr)cinfo->entropy; + int sampn, ci, yoffset, MCU_width, ptrn; + JDIMENSION mcu_num; + BITREAD_STATE_VARS; + + /* Set output pointer locations based on MCU_col_num */ + for (ptrn = 0; ptrn < entropy->num_output_ptrs; ptrn++) { + ci = entropy->output_ptr_info[ptrn].ci; + yoffset = entropy->output_ptr_info[ptrn].yoffset; + MCU_width = entropy->output_ptr_info[ptrn].MCU_width; + entropy->output_ptr[ptrn] = + diff_buf[ci][MCU_row_num + yoffset] + (MCU_col_num * MCU_width); + } + + /* + * If we've run out of data, zero out the buffers and return. + * By resetting the undifferencer, the output samples will be CENTERJSAMPLE. + * + * NB: We should find a way to do this without interacting with the + * undifferencer module directly. + */ + if (entropy->pub.insufficient_data) { + for (ptrn = 0; ptrn < entropy->num_output_ptrs; ptrn++) + jzero_far((void FAR *)entropy->output_ptr[ptrn], + nMCU * entropy->output_ptr_info[ptrn].MCU_width * + sizeof(JDIFF)); + + (*cinfo->idct->start_pass) (cinfo); + + } else { + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo, entropy->bitstate); + + /* Outer loop handles the number of MCUs requested */ + + for (mcu_num = 0; mcu_num < nMCU; mcu_num++) { + + /* Inner loop handles the samples in the MCU */ + for (sampn = 0; sampn < cinfo->blocks_in_MCU; sampn++) { + d_derived_tbl *dctbl = entropy->cur_tbls[sampn]; + register int s, r; + + /* Section H.2.2: decode the sample difference */ + HUFF_DECODE(s, br_state, dctbl, return mcu_num, label1); + if (s) { + if (s == 16) /* special case: always output 32768 */ + s = 32768; + else { /* normal case: fetch subsequent bits */ + CHECK_BIT_BUFFER(br_state, s, return mcu_num); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + } + } + + /* Output the sample difference */ + *entropy->output_ptr[entropy->output_ptr_index[sampn]]++ = (JDIFF)s; + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo, entropy->bitstate); + } + } + + return nMCU; +} + + +/* + * Module initialization routine for lossless mode Huffman entropy decoding. + */ + +GLOBAL(void) +jinit_lhuff_decoder(j_decompress_ptr cinfo) +{ + lhuff_entropy_ptr entropy; + int i; + + entropy = (lhuff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, + sizeof(lhuff_entropy_decoder)); + cinfo->entropy = (struct jpeg_entropy_decoder *)entropy; + entropy->pub.start_pass = start_pass_lhuff_decoder; + entropy->pub.decode_mcus = decode_mcus; + entropy->pub.process_restart = process_restart; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->derived_tbls[i] = NULL; + } +} + +#endif /* D_LOSSLESS_SUPPORTED */ diff --git a/jdlossls.c b/jdlossls.c new file mode 100644 index 0000000..4d15e6b --- /dev/null +++ b/jdlossls.c @@ -0,0 +1,289 @@ +/* + * jdlossls.c + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1998, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file contains prediction, sample undifferencing, point transform, and + * sample scaling routines for the lossless JPEG decompressor. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jlossls.h" + +#ifdef D_LOSSLESS_SUPPORTED + + +/**************** Sample undifferencing (reconstruction) *****************/ + +/* + * In order to avoid a performance penalty for checking which predictor is + * being used and which row is being processed for each call of the + * undifferencer, and to promote optimization, we have separate undifferencing + * functions for each predictor selection value. + * + * We are able to avoid duplicating source code by implementing the predictors + * and undifferencers as macros. Each of the undifferencing functions is + * simply a wrapper around an UNDIFFERENCE macro with the appropriate PREDICTOR + * macro passed as an argument. + */ + +/* Predictor for the first column of the first row: 2^(P-Pt-1) */ +#define INITIAL_PREDICTORx (1 << (cinfo->data_precision - cinfo->Al - 1)) + +/* Predictor for the first column of the remaining rows: Rb */ +#define INITIAL_PREDICTOR2 prev_row[0] + + +/* + * 1-Dimensional undifferencer routine. + * + * This macro implements the 1-D horizontal predictor (1). INITIAL_PREDICTOR + * is used as the special case predictor for the first column, which must be + * either INITIAL_PREDICTOR2 or INITIAL_PREDICTORx. The remaining samples + * use PREDICTOR1. + * + * The reconstructed sample is supposed to be calculated modulo 2^16, so we + * logically AND the result with 0xFFFF. + */ + +#define UNDIFFERENCE_1D(INITIAL_PREDICTOR) \ + int Ra; \ + \ + Ra = (*diff_buf++ + INITIAL_PREDICTOR) & 0xFFFF; \ + *undiff_buf++ = Ra; \ + \ + while (--width) { \ + Ra = (*diff_buf++ + PREDICTOR1) & 0xFFFF; \ + *undiff_buf++ = Ra; \ + } + + +/* + * 2-Dimensional undifferencer routine. + * + * This macro implements the 2-D horizontal predictors (#2-7). PREDICTOR2 is + * used as the special case predictor for the first column. The remaining + * samples use PREDICTOR, which is a function of Ra, Rb, and Rc. + * + * Because prev_row and output_buf may point to the same storage area (in an + * interleaved image with Vi=1, for example), we must take care to buffer Rb/Rc + * before writing the current reconstructed sample value into output_buf. + * + * The reconstructed sample is supposed to be calculated modulo 2^16, so we + * logically AND the result with 0xFFFF. + */ + +#define UNDIFFERENCE_2D(PREDICTOR) \ + int Ra, Rb, Rc; \ + \ + Rb = *prev_row++; \ + Ra = (*diff_buf++ + PREDICTOR2) & 0xFFFF; \ + *undiff_buf++ = Ra; \ + \ + while (--width) { \ + Rc = Rb; \ + Rb = *prev_row++; \ + Ra = (*diff_buf++ + PREDICTOR) & 0xFFFF; \ + *undiff_buf++ = Ra; \ + } + + +/* + * Undifferencers for the second and subsequent rows in a scan or restart + * interval. The first sample in the row is undifferenced using the vertical + * predictor (2). The rest of the samples are undifferenced using the + * predictor specified in the scan header. + */ + +METHODDEF(void) +jpeg_undifference1(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_1D(INITIAL_PREDICTOR2); +} + +METHODDEF(void) +jpeg_undifference2(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR2); + (void)(Rc); +} + +METHODDEF(void) +jpeg_undifference3(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR3); +} + +METHODDEF(void) +jpeg_undifference4(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR4); +} + +METHODDEF(void) +jpeg_undifference5(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR5); +} + +METHODDEF(void) +jpeg_undifference6(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR6); +} + +METHODDEF(void) +jpeg_undifference7(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR7); + (void)(Rc); +} + + +/* + * Undifferencer for the first row in a scan or restart interval. The first + * sample in the row is undifferenced using the special predictor constant + * x=2^(P-Pt-1). The rest of the samples are undifferenced using the + * 1-D horizontal predictor (1). + */ + +METHODDEF(void) +jpeg_undifference_first_row(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + lossless_decomp_ptr losslessd = (lossless_decomp_ptr)cinfo->idct; + + UNDIFFERENCE_1D(INITIAL_PREDICTORx); + + /* + * Now that we have undifferenced the first row, we want to use the + * undifferencer that corresponds to the predictor specified in the + * scan header. + */ + switch (cinfo->Ss) { + case 1: + losslessd->predict_undifference[comp_index] = jpeg_undifference1; + break; + case 2: + losslessd->predict_undifference[comp_index] = jpeg_undifference2; + break; + case 3: + losslessd->predict_undifference[comp_index] = jpeg_undifference3; + break; + case 4: + losslessd->predict_undifference[comp_index] = jpeg_undifference4; + break; + case 5: + losslessd->predict_undifference[comp_index] = jpeg_undifference5; + break; + case 6: + losslessd->predict_undifference[comp_index] = jpeg_undifference6; + break; + case 7: + losslessd->predict_undifference[comp_index] = jpeg_undifference7; + break; + } +} + + +/*********************** Sample upscaling by 2^Pt ************************/ + +METHODDEF(void) +simple_upscale(j_decompress_ptr cinfo, + JDIFFROW diff_buf, _JSAMPROW output_buf, JDIMENSION width) +{ + do { + *output_buf++ = (_JSAMPLE)(*diff_buf++ << cinfo->Al); + } while (--width); +} + +METHODDEF(void) +noscale(j_decompress_ptr cinfo, + JDIFFROW diff_buf, _JSAMPROW output_buf, JDIMENSION width) +{ + do { + *output_buf++ = (_JSAMPLE)(*diff_buf++); + } while (--width); +} + + +/* + * Initialize for an input processing pass. + */ + +METHODDEF(void) +start_pass_lossless(j_decompress_ptr cinfo) +{ + lossless_decomp_ptr losslessd = (lossless_decomp_ptr)cinfo->idct; + int ci; + + /* Check that the scan parameters Ss, Se, Ah, Al are OK for lossless JPEG. + * + * Ss is the predictor selection value (psv). Legal values for sequential + * lossless JPEG are: 1 <= psv <= 7. + * + * Se and Ah are not used and should be zero. + * + * Al specifies the point transform (Pt). + * Legal values are: 0 <= Pt <= (data precision - 1). + */ + if (cinfo->Ss < 1 || cinfo->Ss > 7 || + cinfo->Se != 0 || cinfo->Ah != 0 || + cinfo->Al < 0 || cinfo->Al >= cinfo->data_precision) + ERREXIT4(cinfo, JERR_BAD_PROGRESSION, + cinfo->Ss, cinfo->Se, cinfo->Ah, cinfo->Al); + + /* Set undifference functions to first row function */ + for (ci = 0; ci < cinfo->num_components; ci++) + losslessd->predict_undifference[ci] = jpeg_undifference_first_row; + + /* Set scaler function based on Pt */ + if (cinfo->Al) + losslessd->scaler_scale = simple_upscale; + else + losslessd->scaler_scale = noscale; +} + + +/* + * Initialize the lossless decompressor. + */ + +GLOBAL(void) +_jinit_lossless_decompressor(j_decompress_ptr cinfo) +{ + lossless_decomp_ptr losslessd; + + /* Create subobject in permanent pool */ + losslessd = (lossless_decomp_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_PERMANENT, + sizeof(jpeg_lossless_decompressor)); + cinfo->idct = (struct jpeg_inverse_dct *)losslessd; + losslessd->pub.start_pass = start_pass_lossless; +} + +#endif /* D_LOSSLESS_SUPPORTED */ diff --git a/jdmainct.c b/jdmainct.c index f466b25..c672b4b 100644 --- a/jdmainct.c +++ b/jdmainct.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2010, 2016, D. R. Commander. + * Copyright (C) 2010, 2016, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -18,15 +18,17 @@ #include "jinclude.h" #include "jdmainct.h" -#include "jconfigint.h" +#if BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED) + /* * In the current system design, the main buffer need never be a full-image - * buffer; any full-height buffers will be found inside the coefficient or - * postprocessing controllers. Nonetheless, the main controller is not - * trivial. Its responsibility is to provide context rows for upsampling/ - * rescaling, and doing this in an efficient fashion is a bit tricky. + * buffer; any full-height buffers will be found inside the coefficient, + * difference, or postprocessing controllers. Nonetheless, the main controller + * is not trivial. Its responsibility is to provide context rows for + * upsampling/rescaling, and doing this in an efficient fashion is a bit + * tricky. * * Postprocessor input data is counted in "row groups". A row group * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) @@ -38,20 +40,20 @@ * row group (times any additional scale factor that the upsampler is * applying). * - * The coefficient controller will deliver data to us one iMCU row at a time; - * each iMCU row contains v_samp_factor * DCT_scaled_size sample rows, or - * exactly min_DCT_scaled_size row groups. (This amount of data corresponds - * to one row of MCUs when the image is fully interleaved.) Note that the - * number of sample rows varies across components, but the number of row - * groups does not. Some garbage sample rows may be included in the last iMCU - * row at the bottom of the image. + * The coefficient or difference controller will deliver data to us one iMCU + * row at a time; each iMCU row contains v_samp_factor * DCT_scaled_size sample + * rows, or exactly min_DCT_scaled_size row groups. (This amount of data + * corresponds to one row of MCUs when the image is fully interleaved.) Note + * that the number of sample rows varies across components, but the number of + * row groups does not. Some garbage sample rows may be included in the last + * iMCU row at the bottom of the image. * * Depending on the vertical scaling algorithm used, the upsampler may need * access to the sample row(s) above and below its current input row group. * The upsampler is required to set need_context_rows TRUE at global selection * time if so. When need_context_rows is FALSE, this controller can simply - * obtain one iMCU row at a time from the coefficient controller and dole it - * out as row groups to the postprocessor. + * obtain one iMCU row at a time from the coefficient or difference controller + * and dole it out as row groups to the postprocessor. * * When need_context_rows is TRUE, this controller guarantees that the buffer * passed to postprocessing contains at least one row group's worth of samples @@ -114,16 +116,16 @@ /* Forward declarations */ METHODDEF(void) process_data_simple_main(j_decompress_ptr cinfo, - JSAMPARRAY output_buf, + _JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail); METHODDEF(void) process_data_context_main(j_decompress_ptr cinfo, - JSAMPARRAY output_buf, + _JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail); #ifdef QUANT_2PASS_SUPPORTED METHODDEF(void) process_data_crank_post(j_decompress_ptr cinfo, - JSAMPARRAY output_buf, + _JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail); #endif @@ -139,14 +141,15 @@ alloc_funny_pointers(j_decompress_ptr cinfo) int ci, rgroup; int M = cinfo->_min_DCT_scaled_size; jpeg_component_info *compptr; - JSAMPARRAY xbuf; + _JSAMPARRAY xbuf; /* Get top-level space for component array pointers. * We alloc both arrays with one call to save a few cycles. */ - main_ptr->xbuffer[0] = (JSAMPIMAGE) + main_ptr->xbuffer[0] = (_JSAMPIMAGE) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, - cinfo->num_components * 2 * sizeof(JSAMPARRAY)); + cinfo->num_components * 2 * + sizeof(_JSAMPARRAY)); main_ptr->xbuffer[1] = main_ptr->xbuffer[0] + cinfo->num_components; for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; @@ -156,9 +159,9 @@ alloc_funny_pointers(j_decompress_ptr cinfo) /* Get space for pointer lists --- M+4 row groups in each list. * We alloc both pointer lists with one call to save a few cycles. */ - xbuf = (JSAMPARRAY) + xbuf = (_JSAMPARRAY) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, - 2 * (rgroup * (M + 4)) * sizeof(JSAMPROW)); + 2 * (rgroup * (M + 4)) * sizeof(_JSAMPROW)); xbuf += rgroup; /* want one row group at negative offsets */ main_ptr->xbuffer[0][ci] = xbuf; xbuf += rgroup * (M + 4); @@ -180,7 +183,7 @@ make_funny_pointers(j_decompress_ptr cinfo) int ci, i, rgroup; int M = cinfo->_min_DCT_scaled_size; jpeg_component_info *compptr; - JSAMPARRAY buf, xbuf0, xbuf1; + _JSAMPARRAY buf, xbuf0, xbuf1; for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { @@ -220,7 +223,7 @@ set_bottom_pointers(j_decompress_ptr cinfo) my_main_ptr main_ptr = (my_main_ptr)cinfo->main; int ci, i, rgroup, iMCUheight, rows_left; jpeg_component_info *compptr; - JSAMPARRAY xbuf; + _JSAMPARRAY xbuf; for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { @@ -259,14 +262,14 @@ start_pass_main(j_decompress_ptr cinfo, J_BUF_MODE pass_mode) switch (pass_mode) { case JBUF_PASS_THRU: if (cinfo->upsample->need_context_rows) { - main_ptr->pub.process_data = process_data_context_main; + main_ptr->pub._process_data = process_data_context_main; make_funny_pointers(cinfo); /* Create the xbuffer[] lists */ main_ptr->whichptr = 0; /* Read first iMCU row into xbuffer[0] */ main_ptr->context_state = CTX_PREPARE_FOR_IMCU; main_ptr->iMCU_row_ctr = 0; } else { /* Simple case with no context needed */ - main_ptr->pub.process_data = process_data_simple_main; + main_ptr->pub._process_data = process_data_simple_main; } main_ptr->buffer_full = FALSE; /* Mark buffer empty */ main_ptr->rowgroup_ctr = 0; @@ -274,7 +277,7 @@ start_pass_main(j_decompress_ptr cinfo, J_BUF_MODE pass_mode) #ifdef QUANT_2PASS_SUPPORTED case JBUF_CRANK_DEST: /* For last pass of 2-pass quantization, just crank the postprocessor */ - main_ptr->pub.process_data = process_data_crank_post; + main_ptr->pub._process_data = process_data_crank_post; break; #endif default: @@ -290,7 +293,7 @@ start_pass_main(j_decompress_ptr cinfo, J_BUF_MODE pass_mode) */ METHODDEF(void) -process_data_simple_main(j_decompress_ptr cinfo, JSAMPARRAY output_buf, +process_data_simple_main(j_decompress_ptr cinfo, _JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail) { my_main_ptr main_ptr = (my_main_ptr)cinfo->main; @@ -298,7 +301,7 @@ process_data_simple_main(j_decompress_ptr cinfo, JSAMPARRAY output_buf, /* Read input data if we haven't filled the main buffer yet */ if (!main_ptr->buffer_full) { - if (!(*cinfo->coef->decompress_data) (cinfo, main_ptr->buffer)) + if (!(*cinfo->coef->_decompress_data) (cinfo, main_ptr->buffer)) return; /* suspension forced, can do nothing more */ main_ptr->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ } @@ -311,9 +314,9 @@ process_data_simple_main(j_decompress_ptr cinfo, JSAMPARRAY output_buf, */ /* Feed the postprocessor */ - (*cinfo->post->post_process_data) (cinfo, main_ptr->buffer, - &main_ptr->rowgroup_ctr, rowgroups_avail, - output_buf, out_row_ctr, out_rows_avail); + (*cinfo->post->_post_process_data) (cinfo, main_ptr->buffer, + &main_ptr->rowgroup_ctr, rowgroups_avail, + output_buf, out_row_ctr, out_rows_avail); /* Has postprocessor consumed all the data yet? If so, mark buffer empty */ if (main_ptr->rowgroup_ctr >= rowgroups_avail) { @@ -329,15 +332,15 @@ process_data_simple_main(j_decompress_ptr cinfo, JSAMPARRAY output_buf, */ METHODDEF(void) -process_data_context_main(j_decompress_ptr cinfo, JSAMPARRAY output_buf, +process_data_context_main(j_decompress_ptr cinfo, _JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail) { my_main_ptr main_ptr = (my_main_ptr)cinfo->main; /* Read input data if we haven't filled the main buffer yet */ if (!main_ptr->buffer_full) { - if (!(*cinfo->coef->decompress_data) (cinfo, - main_ptr->xbuffer[main_ptr->whichptr])) + if (!(*cinfo->coef->_decompress_data) (cinfo, + main_ptr->xbuffer[main_ptr->whichptr])) return; /* suspension forced, can do nothing more */ main_ptr->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ main_ptr->iMCU_row_ctr++; /* count rows received */ @@ -351,11 +354,11 @@ process_data_context_main(j_decompress_ptr cinfo, JSAMPARRAY output_buf, switch (main_ptr->context_state) { case CTX_POSTPONED_ROW: /* Call postprocessor using previously set pointers for postponed row */ - (*cinfo->post->post_process_data) (cinfo, - main_ptr->xbuffer[main_ptr->whichptr], - &main_ptr->rowgroup_ctr, - main_ptr->rowgroups_avail, output_buf, - out_row_ctr, out_rows_avail); + (*cinfo->post->_post_process_data) (cinfo, + main_ptr->xbuffer[main_ptr->whichptr], + &main_ptr->rowgroup_ctr, + main_ptr->rowgroups_avail, output_buf, + out_row_ctr, out_rows_avail); if (main_ptr->rowgroup_ctr < main_ptr->rowgroups_avail) return; /* Need to suspend */ main_ptr->context_state = CTX_PREPARE_FOR_IMCU; @@ -375,11 +378,11 @@ process_data_context_main(j_decompress_ptr cinfo, JSAMPARRAY output_buf, FALLTHROUGH /*FALLTHROUGH*/ case CTX_PROCESS_IMCU: /* Call postprocessor using previously set pointers */ - (*cinfo->post->post_process_data) (cinfo, - main_ptr->xbuffer[main_ptr->whichptr], - &main_ptr->rowgroup_ctr, - main_ptr->rowgroups_avail, output_buf, - out_row_ctr, out_rows_avail); + (*cinfo->post->_post_process_data) (cinfo, + main_ptr->xbuffer[main_ptr->whichptr], + &main_ptr->rowgroup_ctr, + main_ptr->rowgroups_avail, output_buf, + out_row_ctr, out_rows_avail); if (main_ptr->rowgroup_ctr < main_ptr->rowgroups_avail) return; /* Need to suspend */ /* After the first iMCU, change wraparound pointers to normal state */ @@ -406,12 +409,12 @@ process_data_context_main(j_decompress_ptr cinfo, JSAMPARRAY output_buf, #ifdef QUANT_2PASS_SUPPORTED METHODDEF(void) -process_data_crank_post(j_decompress_ptr cinfo, JSAMPARRAY output_buf, +process_data_crank_post(j_decompress_ptr cinfo, _JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail) { - (*cinfo->post->post_process_data) (cinfo, (JSAMPIMAGE)NULL, - (JDIMENSION *)NULL, (JDIMENSION)0, - output_buf, out_row_ctr, out_rows_avail); + (*cinfo->post->_post_process_data) (cinfo, (_JSAMPIMAGE)NULL, + (JDIMENSION *)NULL, (JDIMENSION)0, + output_buf, out_row_ctr, out_rows_avail); } #endif /* QUANT_2PASS_SUPPORTED */ @@ -422,12 +425,15 @@ process_data_crank_post(j_decompress_ptr cinfo, JSAMPARRAY output_buf, */ GLOBAL(void) -jinit_d_main_controller(j_decompress_ptr cinfo, boolean need_full_buffer) +_jinit_d_main_controller(j_decompress_ptr cinfo, boolean need_full_buffer) { my_main_ptr main_ptr; int ci, rgroup, ngroups; jpeg_component_info *compptr; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + main_ptr = (my_main_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, sizeof(my_main_controller)); @@ -453,9 +459,11 @@ jinit_d_main_controller(j_decompress_ptr cinfo, boolean need_full_buffer) ci++, compptr++) { rgroup = (compptr->v_samp_factor * compptr->_DCT_scaled_size) / cinfo->_min_DCT_scaled_size; /* height of a row group of component */ - main_ptr->buffer[ci] = (*cinfo->mem->alloc_sarray) + main_ptr->buffer[ci] = (_JSAMPARRAY)(*cinfo->mem->alloc_sarray) ((j_common_ptr)cinfo, JPOOL_IMAGE, compptr->width_in_blocks * compptr->_DCT_scaled_size, (JDIMENSION)(rgroup * ngroups)); } } + +#endif /* BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED) */ diff --git a/jdmainct.h b/jdmainct.h index 37b201c..914ad11 100644 --- a/jdmainct.h +++ b/jdmainct.h @@ -3,22 +3,27 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. */ #define JPEG_INTERNALS #include "jpeglib.h" -#include "jpegcomp.h" +#include "jpegapicomp.h" +#include "jsamplecomp.h" +#if BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED) + /* Private buffer controller object */ typedef struct { struct jpeg_d_main_controller pub; /* public fields */ /* Pointer to allocated workspace (M or M+2 row groups). */ - JSAMPARRAY buffer[MAX_COMPONENTS]; + _JSAMPARRAY buffer[MAX_COMPONENTS]; boolean buffer_full; /* Have we gotten an iMCU row from decoder? */ JDIMENSION rowgroup_ctr; /* counts row groups output to postprocessor */ @@ -26,7 +31,7 @@ typedef struct { /* Remaining fields are only used in the context case. */ /* These are the master pointers to the funny-order pointer lists. */ - JSAMPIMAGE xbuffer[2]; /* pointers to weird pointer lists */ + _JSAMPIMAGE xbuffer[2]; /* pointers to weird pointer lists */ int whichptr; /* indicates which pointer set is now in use */ int context_state; /* process_data state machine status */ @@ -53,7 +58,7 @@ set_wraparound_pointers(j_decompress_ptr cinfo) int ci, i, rgroup; int M = cinfo->_min_DCT_scaled_size; jpeg_component_info *compptr; - JSAMPARRAY xbuf0, xbuf1; + _JSAMPARRAY xbuf0, xbuf1; for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { @@ -69,3 +74,5 @@ set_wraparound_pointers(j_decompress_ptr cinfo) } } } + +#endif /* BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED) */ diff --git a/jdmarker.c b/jdmarker.c index f7eba61..acd28ce 100644 --- a/jdmarker.c +++ b/jdmarker.c @@ -3,6 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1998, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2012, 2015, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg @@ -237,7 +239,8 @@ get_soi(j_decompress_ptr cinfo) LOCAL(boolean) -get_sof(j_decompress_ptr cinfo, boolean is_prog, boolean is_arith) +get_sof(j_decompress_ptr cinfo, boolean is_prog, boolean is_lossless, + boolean is_arith) /* Process a SOFn marker */ { JLONG length; @@ -246,6 +249,7 @@ get_sof(j_decompress_ptr cinfo, boolean is_prog, boolean is_arith) INPUT_VARS(cinfo); cinfo->progressive_mode = is_prog; + cinfo->master->lossless = is_lossless; cinfo->arith_code = is_arith; INPUT_2BYTES(cinfo, length, return FALSE); @@ -990,32 +994,40 @@ read_markers(j_decompress_ptr cinfo) case M_SOF0: /* Baseline */ case M_SOF1: /* Extended sequential, Huffman */ - if (!get_sof(cinfo, FALSE, FALSE)) + if (!get_sof(cinfo, FALSE, FALSE, FALSE)) return JPEG_SUSPENDED; break; case M_SOF2: /* Progressive, Huffman */ - if (!get_sof(cinfo, TRUE, FALSE)) + if (!get_sof(cinfo, TRUE, FALSE, FALSE)) + return JPEG_SUSPENDED; + break; + + case M_SOF3: /* Lossless, Huffman */ + if (!get_sof(cinfo, FALSE, TRUE, FALSE)) return JPEG_SUSPENDED; break; case M_SOF9: /* Extended sequential, arithmetic */ - if (!get_sof(cinfo, FALSE, TRUE)) + if (!get_sof(cinfo, FALSE, FALSE, TRUE)) return JPEG_SUSPENDED; break; case M_SOF10: /* Progressive, arithmetic */ - if (!get_sof(cinfo, TRUE, TRUE)) + if (!get_sof(cinfo, TRUE, FALSE, TRUE)) + return JPEG_SUSPENDED; + break; + + case M_SOF11: /* Lossless, arithmetic */ + if (!get_sof(cinfo, FALSE, TRUE, TRUE)) return JPEG_SUSPENDED; break; /* Currently unsupported SOFn types */ - case M_SOF3: /* Lossless, Huffman */ case M_SOF5: /* Differential sequential, Huffman */ case M_SOF6: /* Differential progressive, Huffman */ case M_SOF7: /* Differential lossless, Huffman */ case M_JPG: /* Reserved for JPEG extensions */ - case M_SOF11: /* Lossless, arithmetic */ case M_SOF13: /* Differential sequential, arithmetic */ case M_SOF14: /* Differential progressive, arithmetic */ case M_SOF15: /* Differential lossless, arithmetic */ diff --git a/jdmaster.c b/jdmaster.c index a3690bf..80a4842 100644 --- a/jdmaster.c +++ b/jdmaster.c @@ -4,8 +4,10 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 2002-2009 by Guido Vollbeding. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: - * Copyright (C) 2009-2011, 2016, 2019, 2022, D. R. Commander. + * Copyright (C) 2009-2011, 2016, 2019, 2022-2023, D. R. Commander. * Copyright (C) 2013, Linaro Limited. * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg @@ -20,7 +22,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jpegcomp.h" +#include "jpegapicomp.h" #include "jdmaster.h" @@ -33,6 +35,9 @@ LOCAL(boolean) use_merged_upsample(j_decompress_ptr cinfo) { #ifdef UPSAMPLE_MERGING_SUPPORTED + /* Colorspace conversion is not supported with lossless JPEG images */ + if (cinfo->master->lossless) + return FALSE; /* Merging is the equivalent of plain box-filter upsampling */ if (cinfo->do_fancy_upsampling || cinfo->CCIR601_sampling) return FALSE; @@ -97,154 +102,154 @@ jpeg_core_output_dimensions(j_decompress_ptr cinfo) int ci; jpeg_component_info *compptr; - /* Compute actual output image dimensions and DCT scaling choices. */ - if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom) { - /* Provide 1/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 1; - cinfo->_min_DCT_v_scaled_size = 1; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 2) { - /* Provide 2/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 2L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 2L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 2; - cinfo->_min_DCT_v_scaled_size = 2; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 3) { - /* Provide 3/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 3L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 3L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 3; - cinfo->_min_DCT_v_scaled_size = 3; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 4) { - /* Provide 4/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 4L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 4L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 4; - cinfo->_min_DCT_v_scaled_size = 4; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 5) { - /* Provide 5/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 5L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 5L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 5; - cinfo->_min_DCT_v_scaled_size = 5; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 6) { - /* Provide 6/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 6L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 6L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 6; - cinfo->_min_DCT_v_scaled_size = 6; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 7) { - /* Provide 7/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 7L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 7L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 7; - cinfo->_min_DCT_v_scaled_size = 7; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 8) { - /* Provide 8/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 8L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 8L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 8; - cinfo->_min_DCT_v_scaled_size = 8; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 9) { - /* Provide 9/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 9L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 9L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 9; - cinfo->_min_DCT_v_scaled_size = 9; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 10) { - /* Provide 10/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 10L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 10L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 10; - cinfo->_min_DCT_v_scaled_size = 10; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 11) { - /* Provide 11/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 11L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 11L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 11; - cinfo->_min_DCT_v_scaled_size = 11; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 12) { - /* Provide 12/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 12L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 12L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 12; - cinfo->_min_DCT_v_scaled_size = 12; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 13) { - /* Provide 13/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 13L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 13L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 13; - cinfo->_min_DCT_v_scaled_size = 13; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 14) { - /* Provide 14/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 14L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 14L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 14; - cinfo->_min_DCT_v_scaled_size = 14; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 15) { - /* Provide 15/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 15L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 15L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 15; - cinfo->_min_DCT_v_scaled_size = 15; - } else { - /* Provide 16/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 16L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 16L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 16; - cinfo->_min_DCT_v_scaled_size = 16; - } + if (!cinfo->master->lossless) { + /* Compute actual output image dimensions and DCT scaling choices. */ + if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom) { + /* Provide 1/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 1; + cinfo->_min_DCT_v_scaled_size = 1; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 2) { + /* Provide 2/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 2L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 2L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 2; + cinfo->_min_DCT_v_scaled_size = 2; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 3) { + /* Provide 3/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 3L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 3L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 3; + cinfo->_min_DCT_v_scaled_size = 3; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 4) { + /* Provide 4/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 4L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 4L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 4; + cinfo->_min_DCT_v_scaled_size = 4; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 5) { + /* Provide 5/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 5L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 5L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 5; + cinfo->_min_DCT_v_scaled_size = 5; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 6) { + /* Provide 6/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 6L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 6L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 6; + cinfo->_min_DCT_v_scaled_size = 6; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 7) { + /* Provide 7/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 7L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 7L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 7; + cinfo->_min_DCT_v_scaled_size = 7; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 8) { + /* Provide 8/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 8L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 8L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 8; + cinfo->_min_DCT_v_scaled_size = 8; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 9) { + /* Provide 9/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 9L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 9L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 9; + cinfo->_min_DCT_v_scaled_size = 9; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 10) { + /* Provide 10/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 10L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 10L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 10; + cinfo->_min_DCT_v_scaled_size = 10; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 11) { + /* Provide 11/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 11L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 11L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 11; + cinfo->_min_DCT_v_scaled_size = 11; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 12) { + /* Provide 12/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 12L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 12L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 12; + cinfo->_min_DCT_v_scaled_size = 12; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 13) { + /* Provide 13/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 13L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 13L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 13; + cinfo->_min_DCT_v_scaled_size = 13; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 14) { + /* Provide 14/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 14L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 14L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 14; + cinfo->_min_DCT_v_scaled_size = 14; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 15) { + /* Provide 15/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 15L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 15L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 15; + cinfo->_min_DCT_v_scaled_size = 15; + } else { + /* Provide 16/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 16L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 16L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 16; + cinfo->_min_DCT_v_scaled_size = 16; + } - /* Recompute dimensions of components */ - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - compptr->_DCT_h_scaled_size = cinfo->_min_DCT_h_scaled_size; - compptr->_DCT_v_scaled_size = cinfo->_min_DCT_v_scaled_size; + /* Recompute dimensions of components */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + compptr->_DCT_h_scaled_size = cinfo->_min_DCT_h_scaled_size; + compptr->_DCT_v_scaled_size = cinfo->_min_DCT_v_scaled_size; + } + } else +#endif /* !IDCT_SCALING_SUPPORTED */ + { + /* Hardwire it to "no scaling" */ + cinfo->output_width = cinfo->image_width; + cinfo->output_height = cinfo->image_height; + /* jdinput.c has already initialized DCT_scaled_size, + * and has computed unscaled downsampled_width and downsampled_height. + */ } - -#else /* !IDCT_SCALING_SUPPORTED */ - - /* Hardwire it to "no scaling" */ - cinfo->output_width = cinfo->image_width; - cinfo->output_height = cinfo->image_height; - /* jdinput.c has already initialized DCT_scaled_size, - * and has computed unscaled downsampled_width and downsampled_height. - */ - -#endif /* IDCT_SCALING_SUPPORTED */ } @@ -273,54 +278,56 @@ jpeg_calc_output_dimensions(j_decompress_ptr cinfo) #ifdef IDCT_SCALING_SUPPORTED - /* In selecting the actual DCT scaling for each component, we try to - * scale up the chroma components via IDCT scaling rather than upsampling. - * This saves time if the upsampler gets to use 1:1 scaling. - * Note this code adapts subsampling ratios which are powers of 2. - */ - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - int ssize = cinfo->_min_DCT_scaled_size; - while (ssize < DCTSIZE && - ((cinfo->max_h_samp_factor * cinfo->_min_DCT_scaled_size) % - (compptr->h_samp_factor * ssize * 2) == 0) && - ((cinfo->max_v_samp_factor * cinfo->_min_DCT_scaled_size) % - (compptr->v_samp_factor * ssize * 2) == 0)) { - ssize = ssize * 2; - } + if (!cinfo->master->lossless) { + /* In selecting the actual DCT scaling for each component, we try to + * scale up the chroma components via IDCT scaling rather than upsampling. + * This saves time if the upsampler gets to use 1:1 scaling. + * Note this code adapts subsampling ratios which are powers of 2. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + int ssize = cinfo->_min_DCT_scaled_size; + while (ssize < DCTSIZE && + ((cinfo->max_h_samp_factor * cinfo->_min_DCT_scaled_size) % + (compptr->h_samp_factor * ssize * 2) == 0) && + ((cinfo->max_v_samp_factor * cinfo->_min_DCT_scaled_size) % + (compptr->v_samp_factor * ssize * 2) == 0)) { + ssize = ssize * 2; + } #if JPEG_LIB_VERSION >= 70 - compptr->DCT_h_scaled_size = compptr->DCT_v_scaled_size = ssize; + compptr->DCT_h_scaled_size = compptr->DCT_v_scaled_size = ssize; #else - compptr->DCT_scaled_size = ssize; + compptr->DCT_scaled_size = ssize; #endif - } - - /* Recompute downsampled dimensions of components; - * application needs to know these if using raw downsampled data. - */ - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - /* Size in samples, after IDCT scaling */ - compptr->downsampled_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * - (long)(compptr->h_samp_factor * compptr->_DCT_scaled_size), - (long)(cinfo->max_h_samp_factor * DCTSIZE)); - compptr->downsampled_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * - (long)(compptr->v_samp_factor * compptr->_DCT_scaled_size), - (long)(cinfo->max_v_samp_factor * DCTSIZE)); - } - -#else /* !IDCT_SCALING_SUPPORTED */ - - /* Hardwire it to "no scaling" */ - cinfo->output_width = cinfo->image_width; - cinfo->output_height = cinfo->image_height; - /* jdinput.c has already initialized DCT_scaled_size to DCTSIZE, - * and has computed unscaled downsampled_width and downsampled_height. - */ + } + /* Recompute downsampled dimensions of components; + * application needs to know these if using raw downsampled data. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Size in samples, after IDCT scaling */ + compptr->downsampled_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * + (long)(compptr->h_samp_factor * + compptr->_DCT_scaled_size), + (long)(cinfo->max_h_samp_factor * DCTSIZE)); + compptr->downsampled_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * + (long)(compptr->v_samp_factor * + compptr->_DCT_scaled_size), + (long)(cinfo->max_v_samp_factor * DCTSIZE)); + } + } else #endif /* IDCT_SCALING_SUPPORTED */ + { + /* Hardwire it to "no scaling" */ + cinfo->output_width = cinfo->image_width; + cinfo->output_height = cinfo->image_height; + /* jdinput.c has already initialized DCT_scaled_size to DCTSIZE, + * and has computed unscaled downsampled_width and downsampled_height. + */ + } /* Report number of components in selected colorspace. */ /* Probably this should be in the color conversion module... */ @@ -409,27 +416,83 @@ prepare_range_limit_table(j_decompress_ptr cinfo) /* Allocate and fill in the sample_range_limit table */ { JSAMPLE *table; + J12SAMPLE *table12; +#ifdef D_LOSSLESS_SUPPORTED + J16SAMPLE *table16; +#endif int i; - table = (JSAMPLE *) - (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, - (5 * (MAXJSAMPLE + 1) + CENTERJSAMPLE) * sizeof(JSAMPLE)); - table += (MAXJSAMPLE + 1); /* allow negative subscripts of simple table */ - cinfo->sample_range_limit = table; - /* First segment of "simple" table: limit[x] = 0 for x < 0 */ - memset(table - (MAXJSAMPLE + 1), 0, (MAXJSAMPLE + 1) * sizeof(JSAMPLE)); - /* Main part of "simple" table: limit[x] = x */ - for (i = 0; i <= MAXJSAMPLE; i++) - table[i] = (JSAMPLE)i; - table += CENTERJSAMPLE; /* Point to where post-IDCT table starts */ - /* End of simple table, rest of first half of post-IDCT table */ - for (i = CENTERJSAMPLE; i < 2 * (MAXJSAMPLE + 1); i++) - table[i] = MAXJSAMPLE; - /* Second half of post-IDCT table */ - memset(table + (2 * (MAXJSAMPLE + 1)), 0, - (2 * (MAXJSAMPLE + 1) - CENTERJSAMPLE) * sizeof(JSAMPLE)); - memcpy(table + (4 * (MAXJSAMPLE + 1) - CENTERJSAMPLE), - cinfo->sample_range_limit, CENTERJSAMPLE * sizeof(JSAMPLE)); + if (cinfo->data_precision == 16) { +#ifdef D_LOSSLESS_SUPPORTED + table16 = (J16SAMPLE *) + (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, + (5 * (MAXJ16SAMPLE + 1) + CENTERJ16SAMPLE) * + sizeof(J16SAMPLE)); + table16 += (MAXJ16SAMPLE + 1); /* allow negative subscripts of simple + table */ + cinfo->sample_range_limit = (JSAMPLE *)table16; + /* First segment of "simple" table: limit[x] = 0 for x < 0 */ + memset(table16 - (MAXJ16SAMPLE + 1), 0, + (MAXJ16SAMPLE + 1) * sizeof(J16SAMPLE)); + /* Main part of "simple" table: limit[x] = x */ + for (i = 0; i <= MAXJ16SAMPLE; i++) + table16[i] = (J16SAMPLE)i; + table16 += CENTERJ16SAMPLE; /* Point to where post-IDCT table starts */ + /* End of simple table, rest of first half of post-IDCT table */ + for (i = CENTERJ16SAMPLE; i < 2 * (MAXJ16SAMPLE + 1); i++) + table16[i] = MAXJ16SAMPLE; + /* Second half of post-IDCT table */ + memset(table16 + (2 * (MAXJ16SAMPLE + 1)), 0, + (2 * (MAXJ16SAMPLE + 1) - CENTERJ16SAMPLE) * sizeof(J16SAMPLE)); + memcpy(table16 + (4 * (MAXJ16SAMPLE + 1) - CENTERJ16SAMPLE), + cinfo->sample_range_limit, CENTERJ16SAMPLE * sizeof(J16SAMPLE)); +#else + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); +#endif + } else if (cinfo->data_precision == 12) { + table12 = (J12SAMPLE *) + (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, + (5 * (MAXJ12SAMPLE + 1) + CENTERJ12SAMPLE) * + sizeof(J12SAMPLE)); + table12 += (MAXJ12SAMPLE + 1); /* allow negative subscripts of simple + table */ + cinfo->sample_range_limit = (JSAMPLE *)table12; + /* First segment of "simple" table: limit[x] = 0 for x < 0 */ + memset(table12 - (MAXJ12SAMPLE + 1), 0, + (MAXJ12SAMPLE + 1) * sizeof(J12SAMPLE)); + /* Main part of "simple" table: limit[x] = x */ + for (i = 0; i <= MAXJ12SAMPLE; i++) + table12[i] = (J12SAMPLE)i; + table12 += CENTERJ12SAMPLE; /* Point to where post-IDCT table starts */ + /* End of simple table, rest of first half of post-IDCT table */ + for (i = CENTERJ12SAMPLE; i < 2 * (MAXJ12SAMPLE + 1); i++) + table12[i] = MAXJ12SAMPLE; + /* Second half of post-IDCT table */ + memset(table12 + (2 * (MAXJ12SAMPLE + 1)), 0, + (2 * (MAXJ12SAMPLE + 1) - CENTERJ12SAMPLE) * sizeof(J12SAMPLE)); + memcpy(table12 + (4 * (MAXJ12SAMPLE + 1) - CENTERJ12SAMPLE), + cinfo->sample_range_limit, CENTERJ12SAMPLE * sizeof(J12SAMPLE)); + } else { + table = (JSAMPLE *) + (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, + (5 * (MAXJSAMPLE + 1) + CENTERJSAMPLE) * sizeof(JSAMPLE)); + table += (MAXJSAMPLE + 1); /* allow negative subscripts of simple table */ + cinfo->sample_range_limit = table; + /* First segment of "simple" table: limit[x] = 0 for x < 0 */ + memset(table - (MAXJSAMPLE + 1), 0, (MAXJSAMPLE + 1) * sizeof(JSAMPLE)); + /* Main part of "simple" table: limit[x] = x */ + for (i = 0; i <= MAXJSAMPLE; i++) + table[i] = (JSAMPLE)i; + table += CENTERJSAMPLE; /* Point to where post-IDCT table starts */ + /* End of simple table, rest of first half of post-IDCT table */ + for (i = CENTERJSAMPLE; i < 2 * (MAXJSAMPLE + 1); i++) + table[i] = MAXJSAMPLE; + /* Second half of post-IDCT table */ + memset(table + (2 * (MAXJSAMPLE + 1)), 0, + (2 * (MAXJSAMPLE + 1) - CENTERJSAMPLE) * sizeof(JSAMPLE)); + memcpy(table + (4 * (MAXJSAMPLE + 1) - CENTERJSAMPLE), + cinfo->sample_range_limit, CENTERJSAMPLE * sizeof(JSAMPLE)); + } } @@ -452,6 +515,17 @@ master_selection(j_decompress_ptr cinfo) long samplesperrow; JDIMENSION jd_samplesperrow; + /* Disable IDCT scaling and raw (downsampled) data output in lossless mode. + * IDCT scaling is not useful in lossless mode, and it must be disabled in + * order to properly calculate the output dimensions. Raw data output isn't + * particularly useful without subsampling and has not been tested in + * lossless mode. + */ + if (cinfo->master->lossless) { + cinfo->raw_data_out = FALSE; + cinfo->scale_num = cinfo->scale_denom = 1; + } + /* Initialize dimensions and other stuff */ jpeg_calc_output_dimensions(cinfo); prepare_range_limit_table(cinfo); @@ -480,7 +554,8 @@ master_selection(j_decompress_ptr cinfo) if (cinfo->raw_data_out) ERREXIT(cinfo, JERR_NOTIMPL); /* 2-pass quantizer only works in 3-component color space. */ - if (cinfo->out_color_components != 3) { + if (cinfo->out_color_components != 3 || + cinfo->out_color_space == JCS_RGB565) { cinfo->enable_1pass_quant = TRUE; cinfo->enable_external_quant = FALSE; cinfo->enable_2pass_quant = FALSE; @@ -495,7 +570,12 @@ master_selection(j_decompress_ptr cinfo) if (cinfo->enable_1pass_quant) { #ifdef QUANT_1PASS_SUPPORTED - jinit_1pass_quantizer(cinfo); + if (cinfo->data_precision == 16) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + else if (cinfo->data_precision == 12) + j12init_1pass_quantizer(cinfo); + else + jinit_1pass_quantizer(cinfo); master->quantizer_1pass = cinfo->cquantize; #else ERREXIT(cinfo, JERR_NOT_COMPILED); @@ -505,7 +585,12 @@ master_selection(j_decompress_ptr cinfo) /* We use the 2-pass code to map to external colormaps. */ if (cinfo->enable_2pass_quant || cinfo->enable_external_quant) { #ifdef QUANT_2PASS_SUPPORTED - jinit_2pass_quantizer(cinfo); + if (cinfo->data_precision == 16) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + else if (cinfo->data_precision == 12) + j12init_2pass_quantizer(cinfo); + else + jinit_2pass_quantizer(cinfo); master->quantizer_2pass = cinfo->cquantize; #else ERREXIT(cinfo, JERR_NOT_COMPILED); @@ -520,42 +605,122 @@ master_selection(j_decompress_ptr cinfo) if (!cinfo->raw_data_out) { if (master->using_merged_upsample) { #ifdef UPSAMPLE_MERGING_SUPPORTED - jinit_merged_upsampler(cinfo); /* does color conversion too */ + if (cinfo->data_precision == 16) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + else if (cinfo->data_precision == 12) + j12init_merged_upsampler(cinfo); /* does color conversion too */ + else + jinit_merged_upsampler(cinfo); /* does color conversion too */ #else ERREXIT(cinfo, JERR_NOT_COMPILED); #endif } else { - jinit_color_deconverter(cinfo); - jinit_upsampler(cinfo); + if (cinfo->data_precision == 16) { +#ifdef D_LOSSLESS_SUPPORTED + j16init_color_deconverter(cinfo); + j16init_upsampler(cinfo); +#else + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); +#endif + } else if (cinfo->data_precision == 12) { + j12init_color_deconverter(cinfo); + j12init_upsampler(cinfo); + } else { + jinit_color_deconverter(cinfo); + jinit_upsampler(cinfo); + } } - jinit_d_post_controller(cinfo, cinfo->enable_2pass_quant); + if (cinfo->data_precision == 16) +#ifdef D_LOSSLESS_SUPPORTED + j16init_d_post_controller(cinfo, cinfo->enable_2pass_quant); +#else + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); +#endif + else if (cinfo->data_precision == 12) + j12init_d_post_controller(cinfo, cinfo->enable_2pass_quant); + else + jinit_d_post_controller(cinfo, cinfo->enable_2pass_quant); } - /* Inverse DCT */ - jinit_inverse_dct(cinfo); - /* Entropy decoding: either Huffman or arithmetic coding. */ - if (cinfo->arith_code) { -#ifdef D_ARITH_CODING_SUPPORTED - jinit_arith_decoder(cinfo); + + if (cinfo->master->lossless) { +#ifdef D_LOSSLESS_SUPPORTED + /* Prediction, sample undifferencing, point transform, and sample size + * scaling + */ + if (cinfo->data_precision == 16) + j16init_lossless_decompressor(cinfo); + else if (cinfo->data_precision == 12) + j12init_lossless_decompressor(cinfo); + else + jinit_lossless_decompressor(cinfo); + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + jinit_lhuff_decoder(cinfo); + } + + /* Initialize principal buffer controllers. */ + use_c_buffer = cinfo->inputctl->has_multiple_scans || + cinfo->buffered_image; + if (cinfo->data_precision == 16) + j16init_d_diff_controller(cinfo, use_c_buffer); + else if (cinfo->data_precision == 12) + j12init_d_diff_controller(cinfo, use_c_buffer); + else + jinit_d_diff_controller(cinfo, use_c_buffer); #else - ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + ERREXIT(cinfo, JERR_NOT_COMPILED); #endif } else { - if (cinfo->progressive_mode) { + if (cinfo->data_precision == 16) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + /* Inverse DCT */ + if (cinfo->data_precision == 12) + j12init_inverse_dct(cinfo); + else + jinit_inverse_dct(cinfo); + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { +#ifdef D_ARITH_CODING_SUPPORTED + jinit_arith_decoder(cinfo); +#else + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); +#endif + } else { + if (cinfo->progressive_mode) { #ifdef D_PROGRESSIVE_SUPPORTED - jinit_phuff_decoder(cinfo); + jinit_phuff_decoder(cinfo); #else - ERREXIT(cinfo, JERR_NOT_COMPILED); + ERREXIT(cinfo, JERR_NOT_COMPILED); #endif - } else - jinit_huff_decoder(cinfo); - } + } else + jinit_huff_decoder(cinfo); + } - /* Initialize principal buffer controllers. */ - use_c_buffer = cinfo->inputctl->has_multiple_scans || cinfo->buffered_image; - jinit_d_coef_controller(cinfo, use_c_buffer); + /* Initialize principal buffer controllers. */ + use_c_buffer = cinfo->inputctl->has_multiple_scans || + cinfo->buffered_image; + if (cinfo->data_precision == 12) + j12init_d_coef_controller(cinfo, use_c_buffer); + else + jinit_d_coef_controller(cinfo, use_c_buffer); + } - if (!cinfo->raw_data_out) - jinit_d_main_controller(cinfo, FALSE /* never need full buffer here */); + if (!cinfo->raw_data_out) { + if (cinfo->data_precision == 16) +#ifdef D_LOSSLESS_SUPPORTED + j16init_d_main_controller(cinfo, + FALSE /* never need full buffer here */); +#else + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); +#endif + else if (cinfo->data_precision == 12) + j12init_d_main_controller(cinfo, + FALSE /* never need full buffer here */); + else + jinit_d_main_controller(cinfo, FALSE /* never need full buffer here */); + } /* We can now tell the memory manager to allocate virtual arrays. */ (*cinfo->mem->realize_virt_arrays) ((j_common_ptr)cinfo); diff --git a/jdmerge.c b/jdmerge.c index 3a456d6..49f2006 100644 --- a/jdmerge.c +++ b/jdmerge.c @@ -5,7 +5,7 @@ * Copyright (C) 1994-1996, Thomas G. Lane. * libjpeg-turbo Modifications: * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2009, 2011, 2014-2015, 2020, D. R. Commander. + * Copyright (C) 2009, 2011, 2014-2015, 2020, 2022, D. R. Commander. * Copyright (C) 2013, Linaro Limited. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -42,7 +42,6 @@ #include "jpeglib.h" #include "jdmerge.h" #include "jsimd.h" -#include "jconfigint.h" #ifdef UPSAMPLE_MERGING_SUPPORTED @@ -168,20 +167,20 @@ build_ycc_rgb_table(j_decompress_ptr cinfo) upsample->Cr_r_tab = (int *) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, - (MAXJSAMPLE + 1) * sizeof(int)); + (_MAXJSAMPLE + 1) * sizeof(int)); upsample->Cb_b_tab = (int *) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, - (MAXJSAMPLE + 1) * sizeof(int)); + (_MAXJSAMPLE + 1) * sizeof(int)); upsample->Cr_g_tab = (JLONG *) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, - (MAXJSAMPLE + 1) * sizeof(JLONG)); + (_MAXJSAMPLE + 1) * sizeof(JLONG)); upsample->Cb_g_tab = (JLONG *) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, - (MAXJSAMPLE + 1) * sizeof(JLONG)); + (_MAXJSAMPLE + 1) * sizeof(JLONG)); - for (i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) { - /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ - /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ + for (i = 0, x = -_CENTERJSAMPLE; i <= _MAXJSAMPLE; i++, x++) { + /* i is the actual input pixel value, in the range 0.._MAXJSAMPLE */ + /* The Cb or Cr value we are thinking of is x = i - _CENTERJSAMPLE */ /* Cr=>R value is nearest int to 1.40200 * x */ upsample->Cr_r_tab[i] = (int) RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); @@ -220,14 +219,14 @@ start_pass_merged_upsample(j_decompress_ptr cinfo) */ METHODDEF(void) -merged_2v_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, +merged_2v_upsample(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, - JDIMENSION in_row_groups_avail, JSAMPARRAY output_buf, + JDIMENSION in_row_groups_avail, _JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail) /* 2:1 vertical sampling case: may need a spare row. */ { my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample; - JSAMPROW work_ptrs[2]; + _JSAMPROW work_ptrs[2]; JDIMENSION num_rows; /* number of rows returned to caller */ if (upsample->spare_full) { @@ -235,8 +234,8 @@ merged_2v_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION size = upsample->out_row_width; if (cinfo->out_color_space == JCS_RGB565) size = cinfo->output_width * 2; - jcopy_sample_rows(&upsample->spare_row, 0, output_buf + *out_row_ctr, 0, 1, - size); + _jcopy_sample_rows(&upsample->spare_row, 0, output_buf + *out_row_ctr, 0, + 1, size); num_rows = 1; upsample->spare_full = FALSE; } else { @@ -271,9 +270,9 @@ merged_2v_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, METHODDEF(void) -merged_1v_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, +merged_1v_upsample(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, - JDIMENSION in_row_groups_avail, JSAMPARRAY output_buf, + JDIMENSION in_row_groups_avail, _JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail) /* 1:1 vertical sampling case: much easier, never need a spare row. */ { @@ -303,8 +302,8 @@ merged_1v_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, */ METHODDEF(void) -h2v1_merged_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf) +h2v1_merged_upsample(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION in_row_group_ctr, _JSAMPARRAY output_buf) { switch (cinfo->out_color_space) { case JCS_EXT_RGB: @@ -348,8 +347,8 @@ h2v1_merged_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, */ METHODDEF(void) -h2v2_merged_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf) +h2v2_merged_upsample(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION in_row_group_ctr, _JSAMPARRAY output_buf) { switch (cinfo->out_color_space) { case JCS_EXT_RGB: @@ -475,8 +474,8 @@ static INLINE boolean is_big_endian(void) METHODDEF(void) -h2v1_merged_upsample_565(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf) +h2v1_merged_upsample_565(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION in_row_group_ctr, _JSAMPARRAY output_buf) { if (is_big_endian()) h2v1_merged_upsample_565_be(cinfo, input_buf, in_row_group_ctr, @@ -488,8 +487,8 @@ h2v1_merged_upsample_565(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, METHODDEF(void) -h2v1_merged_upsample_565D(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf) +h2v1_merged_upsample_565D(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION in_row_group_ctr, _JSAMPARRAY output_buf) { if (is_big_endian()) h2v1_merged_upsample_565D_be(cinfo, input_buf, in_row_group_ctr, @@ -501,8 +500,8 @@ h2v1_merged_upsample_565D(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, METHODDEF(void) -h2v2_merged_upsample_565(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf) +h2v2_merged_upsample_565(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION in_row_group_ctr, _JSAMPARRAY output_buf) { if (is_big_endian()) h2v2_merged_upsample_565_be(cinfo, input_buf, in_row_group_ctr, @@ -514,8 +513,8 @@ h2v2_merged_upsample_565(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, METHODDEF(void) -h2v2_merged_upsample_565D(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf) +h2v2_merged_upsample_565D(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION in_row_group_ctr, _JSAMPARRAY output_buf) { if (is_big_endian()) h2v2_merged_upsample_565D_be(cinfo, input_buf, in_row_group_ctr, @@ -535,10 +534,13 @@ h2v2_merged_upsample_565D(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, */ GLOBAL(void) -jinit_merged_upsampler(j_decompress_ptr cinfo) +_jinit_merged_upsampler(j_decompress_ptr cinfo) { my_merged_upsample_ptr upsample; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + upsample = (my_merged_upsample_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, sizeof(my_merged_upsampler)); @@ -549,10 +551,12 @@ jinit_merged_upsampler(j_decompress_ptr cinfo) upsample->out_row_width = cinfo->output_width * cinfo->out_color_components; if (cinfo->max_v_samp_factor == 2) { - upsample->pub.upsample = merged_2v_upsample; + upsample->pub._upsample = merged_2v_upsample; +#ifdef WITH_SIMD if (jsimd_can_h2v2_merged_upsample()) upsample->upmethod = jsimd_h2v2_merged_upsample; else +#endif upsample->upmethod = h2v2_merged_upsample; if (cinfo->out_color_space == JCS_RGB565) { if (cinfo->dither_mode != JDITHER_NONE) { @@ -562,14 +566,16 @@ jinit_merged_upsampler(j_decompress_ptr cinfo) } } /* Allocate a spare row buffer */ - upsample->spare_row = (JSAMPROW) + upsample->spare_row = (_JSAMPROW) (*cinfo->mem->alloc_large) ((j_common_ptr)cinfo, JPOOL_IMAGE, - (size_t)(upsample->out_row_width * sizeof(JSAMPLE))); + (size_t)(upsample->out_row_width * sizeof(_JSAMPLE))); } else { - upsample->pub.upsample = merged_1v_upsample; + upsample->pub._upsample = merged_1v_upsample; +#ifdef WITH_SIMD if (jsimd_can_h2v1_merged_upsample()) upsample->upmethod = jsimd_h2v1_merged_upsample; else +#endif upsample->upmethod = h2v1_merged_upsample; if (cinfo->out_color_space == JCS_RGB565) { if (cinfo->dither_mode != JDITHER_NONE) { diff --git a/jdmerge.h b/jdmerge.h index b583396..73cbd60 100644 --- a/jdmerge.h +++ b/jdmerge.h @@ -4,13 +4,14 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2020, D. R. Commander. + * Copyright (C) 2020, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. */ #define JPEG_INTERNALS #include "jpeglib.h" +#include "jsamplecomp.h" #ifdef UPSAMPLE_MERGING_SUPPORTED @@ -21,8 +22,8 @@ typedef struct { struct jpeg_upsampler pub; /* public fields */ /* Pointer to routine to do actual upsampling/conversion of one row group */ - void (*upmethod) (j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf); + void (*upmethod) (j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, + JDIMENSION in_row_group_ctr, _JSAMPARRAY output_buf); /* Private state for YCC->RGB conversion */ int *Cr_r_tab; /* => table for Cr to R conversion */ @@ -35,7 +36,7 @@ typedef struct { * application provides just a one-row buffer; we also use the spare * to discard the dummy last row if the image height is odd. */ - JSAMPROW spare_row; + _JSAMPROW spare_row; boolean spare_full; /* T if spare buffer is occupied */ JDIMENSION out_row_width; /* samples per output row */ diff --git a/jdmrg565.c b/jdmrg565.c index 980a4e2..0c719b9 100644 --- a/jdmrg565.c +++ b/jdmrg565.c @@ -5,7 +5,7 @@ * Copyright (C) 1994-1996, Thomas G. Lane. * libjpeg-turbo Modifications: * Copyright (C) 2013, Linaro Limited. - * Copyright (C) 2014-2015, 2018, 2020, D. R. Commander. + * Copyright (C) 2014-2015, 2018, 2020, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -15,18 +15,19 @@ INLINE LOCAL(void) -h2v1_merged_upsample_565_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, +h2v1_merged_upsample_565_internal(j_decompress_ptr cinfo, + _JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, - JSAMPARRAY output_buf) + _JSAMPARRAY output_buf) { my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample; register int y, cred, cgreen, cblue; int cb, cr; - register JSAMPROW outptr; - JSAMPROW inptr0, inptr1, inptr2; + register _JSAMPROW outptr; + _JSAMPROW inptr0, inptr1, inptr2; JDIMENSION col; /* copy these pointers into registers if possible */ - register JSAMPLE *range_limit = cinfo->sample_range_limit; + register _JSAMPLE *range_limit = (_JSAMPLE *)cinfo->sample_range_limit; int *Crrtab = upsample->Cr_r_tab; int *Cbbtab = upsample->Cb_b_tab; JLONG *Crgtab = upsample->Cr_g_tab; @@ -86,18 +87,18 @@ h2v1_merged_upsample_565_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, INLINE LOCAL(void) h2v1_merged_upsample_565D_internal(j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, + _JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, - JSAMPARRAY output_buf) + _JSAMPARRAY output_buf) { my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample; register int y, cred, cgreen, cblue; int cb, cr; - register JSAMPROW outptr; - JSAMPROW inptr0, inptr1, inptr2; + register _JSAMPROW outptr; + _JSAMPROW inptr0, inptr1, inptr2; JDIMENSION col; /* copy these pointers into registers if possible */ - register JSAMPLE *range_limit = cinfo->sample_range_limit; + register _JSAMPLE *range_limit = (_JSAMPLE *)cinfo->sample_range_limit; int *Crrtab = upsample->Cr_r_tab; int *Cbbtab = upsample->Cb_b_tab; JLONG *Crgtab = upsample->Cr_g_tab; @@ -159,18 +160,18 @@ h2v1_merged_upsample_565D_internal(j_decompress_ptr cinfo, INLINE LOCAL(void) -h2v2_merged_upsample_565_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, +h2v2_merged_upsample_565_internal(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, - JSAMPARRAY output_buf) + _JSAMPARRAY output_buf) { my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample; register int y, cred, cgreen, cblue; int cb, cr; - register JSAMPROW outptr0, outptr1; - JSAMPROW inptr00, inptr01, inptr1, inptr2; + register _JSAMPROW outptr0, outptr1; + _JSAMPROW inptr00, inptr01, inptr1, inptr2; JDIMENSION col; /* copy these pointers into registers if possible */ - register JSAMPLE *range_limit = cinfo->sample_range_limit; + register _JSAMPLE *range_limit = (_JSAMPLE *)cinfo->sample_range_limit; int *Crrtab = upsample->Cr_r_tab; int *Cbbtab = upsample->Cb_b_tab; JLONG *Crgtab = upsample->Cr_g_tab; @@ -255,18 +256,18 @@ h2v2_merged_upsample_565_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, INLINE LOCAL(void) h2v2_merged_upsample_565D_internal(j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, + _JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, - JSAMPARRAY output_buf) + _JSAMPARRAY output_buf) { my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample; register int y, cred, cgreen, cblue; int cb, cr; - register JSAMPROW outptr0, outptr1; - JSAMPROW inptr00, inptr01, inptr1, inptr2; + register _JSAMPROW outptr0, outptr1; + _JSAMPROW inptr00, inptr01, inptr1, inptr2; JDIMENSION col; /* copy these pointers into registers if possible */ - register JSAMPLE *range_limit = cinfo->sample_range_limit; + register _JSAMPLE *range_limit = (_JSAMPLE *)cinfo->sample_range_limit; int *Crrtab = upsample->Cr_r_tab; int *Cbbtab = upsample->Cb_b_tab; JLONG *Crgtab = upsample->Cr_g_tab; diff --git a/jdmrgext.c b/jdmrgext.c index 9bf4f1a..8139e0a 100644 --- a/jdmrgext.c +++ b/jdmrgext.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2011, 2015, 2020, D. R. Commander. + * Copyright (C) 2011, 2015, 2020, 2022-2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -21,18 +21,18 @@ INLINE LOCAL(void) -h2v1_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, +h2v1_merged_upsample_internal(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, - JSAMPARRAY output_buf) + _JSAMPARRAY output_buf) { my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample; register int y, cred, cgreen, cblue; int cb, cr; - register JSAMPROW outptr; - JSAMPROW inptr0, inptr1, inptr2; + register _JSAMPROW outptr; + _JSAMPROW inptr0, inptr1, inptr2; JDIMENSION col; /* copy these pointers into registers if possible */ - register JSAMPLE *range_limit = cinfo->sample_range_limit; + register _JSAMPLE *range_limit = (_JSAMPLE *)cinfo->sample_range_limit; int *Crrtab = upsample->Cr_r_tab; int *Cbbtab = upsample->Cb_b_tab; JLONG *Crgtab = upsample->Cr_g_tab; @@ -57,7 +57,7 @@ h2v1_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, outptr[RGB_GREEN] = range_limit[y + cgreen]; outptr[RGB_BLUE] = range_limit[y + cblue]; #ifdef RGB_ALPHA - outptr[RGB_ALPHA] = 0xFF; + outptr[RGB_ALPHA] = _MAXJSAMPLE; #endif outptr += RGB_PIXELSIZE; y = *inptr0++; @@ -65,7 +65,7 @@ h2v1_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, outptr[RGB_GREEN] = range_limit[y + cgreen]; outptr[RGB_BLUE] = range_limit[y + cblue]; #ifdef RGB_ALPHA - outptr[RGB_ALPHA] = 0xFF; + outptr[RGB_ALPHA] = _MAXJSAMPLE; #endif outptr += RGB_PIXELSIZE; } @@ -81,7 +81,7 @@ h2v1_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, outptr[RGB_GREEN] = range_limit[y + cgreen]; outptr[RGB_BLUE] = range_limit[y + cblue]; #ifdef RGB_ALPHA - outptr[RGB_ALPHA] = 0xFF; + outptr[RGB_ALPHA] = _MAXJSAMPLE; #endif } } @@ -93,18 +93,18 @@ h2v1_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, INLINE LOCAL(void) -h2v2_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, +h2v2_merged_upsample_internal(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, - JSAMPARRAY output_buf) + _JSAMPARRAY output_buf) { my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample; register int y, cred, cgreen, cblue; int cb, cr; - register JSAMPROW outptr0, outptr1; - JSAMPROW inptr00, inptr01, inptr1, inptr2; + register _JSAMPROW outptr0, outptr1; + _JSAMPROW inptr00, inptr01, inptr1, inptr2; JDIMENSION col; /* copy these pointers into registers if possible */ - register JSAMPLE *range_limit = cinfo->sample_range_limit; + register _JSAMPLE *range_limit = (_JSAMPLE *)cinfo->sample_range_limit; int *Crrtab = upsample->Cr_r_tab; int *Cbbtab = upsample->Cb_b_tab; JLONG *Crgtab = upsample->Cr_g_tab; @@ -131,7 +131,7 @@ h2v2_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, outptr0[RGB_GREEN] = range_limit[y + cgreen]; outptr0[RGB_BLUE] = range_limit[y + cblue]; #ifdef RGB_ALPHA - outptr0[RGB_ALPHA] = 0xFF; + outptr0[RGB_ALPHA] = _MAXJSAMPLE; #endif outptr0 += RGB_PIXELSIZE; y = *inptr00++; @@ -139,7 +139,7 @@ h2v2_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, outptr0[RGB_GREEN] = range_limit[y + cgreen]; outptr0[RGB_BLUE] = range_limit[y + cblue]; #ifdef RGB_ALPHA - outptr0[RGB_ALPHA] = 0xFF; + outptr0[RGB_ALPHA] = _MAXJSAMPLE; #endif outptr0 += RGB_PIXELSIZE; y = *inptr01++; @@ -147,7 +147,7 @@ h2v2_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, outptr1[RGB_GREEN] = range_limit[y + cgreen]; outptr1[RGB_BLUE] = range_limit[y + cblue]; #ifdef RGB_ALPHA - outptr1[RGB_ALPHA] = 0xFF; + outptr1[RGB_ALPHA] = _MAXJSAMPLE; #endif outptr1 += RGB_PIXELSIZE; y = *inptr01++; @@ -155,7 +155,7 @@ h2v2_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, outptr1[RGB_GREEN] = range_limit[y + cgreen]; outptr1[RGB_BLUE] = range_limit[y + cblue]; #ifdef RGB_ALPHA - outptr1[RGB_ALPHA] = 0xFF; + outptr1[RGB_ALPHA] = _MAXJSAMPLE; #endif outptr1 += RGB_PIXELSIZE; } @@ -171,14 +171,14 @@ h2v2_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, outptr0[RGB_GREEN] = range_limit[y + cgreen]; outptr0[RGB_BLUE] = range_limit[y + cblue]; #ifdef RGB_ALPHA - outptr0[RGB_ALPHA] = 0xFF; + outptr0[RGB_ALPHA] = _MAXJSAMPLE; #endif y = *inptr01; outptr1[RGB_RED] = range_limit[y + cred]; outptr1[RGB_GREEN] = range_limit[y + cgreen]; outptr1[RGB_BLUE] = range_limit[y + cblue]; #ifdef RGB_ALPHA - outptr1[RGB_ALPHA] = 0xFF; + outptr1[RGB_ALPHA] = _MAXJSAMPLE; #endif } } diff --git a/jdphuff.c b/jdphuff.c index 9680ebc..bf97333 100644 --- a/jdphuff.c +++ b/jdphuff.c @@ -3,6 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1995-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2015-2016, 2018-2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg @@ -23,7 +25,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jdhuff.h" /* Declarations shared with jdhuff.c */ +#include "jdhuff.h" /* Declarations shared with jd*huff.c */ #include diff --git a/jdpostct.c b/jdpostct.c index 6a2cf5c..d38495f 100644 --- a/jdpostct.c +++ b/jdpostct.c @@ -3,8 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. - * It was modified by The libjpeg-turbo Project to include only code relevant - * to libjpeg-turbo. + * libjpeg-turbo Modifications: + * Copyright (C) 2022-2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -22,8 +22,11 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" +#include "jsamplecomp.h" +#if BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED) + /* Private buffer controller object */ typedef struct { @@ -35,7 +38,7 @@ typedef struct { * for one-pass operation, a strip buffer is sufficient. */ jvirt_sarray_ptr whole_image; /* virtual array, or NULL if one-pass */ - JSAMPARRAY buffer; /* strip buffer, or current strip of virtual */ + _JSAMPARRAY buffer; /* strip buffer, or current strip of virtual */ JDIMENSION strip_height; /* buffer size in rows */ /* for two-pass mode only: */ JDIMENSION starting_row; /* row # of first row in current strip */ @@ -46,26 +49,28 @@ typedef my_post_controller *my_post_ptr; /* Forward declarations */ +#if BITS_IN_JSAMPLE != 16 METHODDEF(void) post_process_1pass(j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, + _JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, JDIMENSION in_row_groups_avail, - JSAMPARRAY output_buf, + _JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail); -#ifdef QUANT_2PASS_SUPPORTED +#endif +#if defined(QUANT_2PASS_SUPPORTED) && BITS_IN_JSAMPLE != 16 METHODDEF(void) post_process_prepass(j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, + _JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, JDIMENSION in_row_groups_avail, - JSAMPARRAY output_buf, + _JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail); METHODDEF(void) post_process_2pass(j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, + _JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, JDIMENSION in_row_groups_avail, - JSAMPARRAY output_buf, + _JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail); #endif @@ -82,39 +87,42 @@ start_pass_dpost(j_decompress_ptr cinfo, J_BUF_MODE pass_mode) switch (pass_mode) { case JBUF_PASS_THRU: +#if BITS_IN_JSAMPLE != 16 if (cinfo->quantize_colors) { /* Single-pass processing with color quantization. */ - post->pub.post_process_data = post_process_1pass; + post->pub._post_process_data = post_process_1pass; /* We could be doing buffered-image output before starting a 2-pass * color quantization; in that case, jinit_d_post_controller did not * allocate a strip buffer. Use the virtual-array buffer as workspace. */ if (post->buffer == NULL) { - post->buffer = (*cinfo->mem->access_virt_sarray) + post->buffer = (_JSAMPARRAY)(*cinfo->mem->access_virt_sarray) ((j_common_ptr)cinfo, post->whole_image, (JDIMENSION)0, post->strip_height, TRUE); } - } else { + } else +#endif + { /* For single-pass processing without color quantization, * I have no work to do; just call the upsampler directly. */ - post->pub.post_process_data = cinfo->upsample->upsample; + post->pub._post_process_data = cinfo->upsample->_upsample; } break; -#ifdef QUANT_2PASS_SUPPORTED +#if defined(QUANT_2PASS_SUPPORTED) && BITS_IN_JSAMPLE != 16 case JBUF_SAVE_AND_PASS: /* First pass of 2-pass quantization */ if (post->whole_image == NULL) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - post->pub.post_process_data = post_process_prepass; + post->pub._post_process_data = post_process_prepass; break; case JBUF_CRANK_DEST: /* Second pass of 2-pass quantization */ if (post->whole_image == NULL) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - post->pub.post_process_data = post_process_2pass; + post->pub._post_process_data = post_process_2pass; break; -#endif /* QUANT_2PASS_SUPPORTED */ +#endif /* defined(QUANT_2PASS_SUPPORTED) && BITS_IN_JSAMPLE != 16 */ default: ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); break; @@ -128,10 +136,12 @@ start_pass_dpost(j_decompress_ptr cinfo, J_BUF_MODE pass_mode) * This is used for color precision reduction as well as one-pass quantization. */ +#if BITS_IN_JSAMPLE != 16 + METHODDEF(void) -post_process_1pass(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, +post_process_1pass(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, - JDIMENSION in_row_groups_avail, JSAMPARRAY output_buf, + JDIMENSION in_row_groups_avail, _JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail) { my_post_ptr post = (my_post_ptr)cinfo->post; @@ -143,27 +153,29 @@ post_process_1pass(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, if (max_rows > post->strip_height) max_rows = post->strip_height; num_rows = 0; - (*cinfo->upsample->upsample) (cinfo, input_buf, in_row_group_ctr, - in_row_groups_avail, post->buffer, &num_rows, - max_rows); + (*cinfo->upsample->_upsample) (cinfo, input_buf, in_row_group_ctr, + in_row_groups_avail, post->buffer, &num_rows, + max_rows); /* Quantize and emit data. */ - (*cinfo->cquantize->color_quantize) (cinfo, post->buffer, - output_buf + *out_row_ctr, - (int)num_rows); + (*cinfo->cquantize->_color_quantize) (cinfo, post->buffer, + output_buf + *out_row_ctr, + (int)num_rows); *out_row_ctr += num_rows; } +#endif -#ifdef QUANT_2PASS_SUPPORTED + +#if defined(QUANT_2PASS_SUPPORTED) && BITS_IN_JSAMPLE != 16 /* * Process some data in the first pass of 2-pass quantization. */ METHODDEF(void) -post_process_prepass(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, +post_process_prepass(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, - JDIMENSION in_row_groups_avail, JSAMPARRAY output_buf, + JDIMENSION in_row_groups_avail, _JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail) { my_post_ptr post = (my_post_ptr)cinfo->post; @@ -171,23 +183,23 @@ post_process_prepass(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, /* Reposition virtual buffer if at start of strip. */ if (post->next_row == 0) { - post->buffer = (*cinfo->mem->access_virt_sarray) + post->buffer = (_JSAMPARRAY)(*cinfo->mem->access_virt_sarray) ((j_common_ptr)cinfo, post->whole_image, post->starting_row, post->strip_height, TRUE); } /* Upsample some data (up to a strip height's worth). */ old_next_row = post->next_row; - (*cinfo->upsample->upsample) (cinfo, input_buf, in_row_group_ctr, - in_row_groups_avail, post->buffer, - &post->next_row, post->strip_height); + (*cinfo->upsample->_upsample) (cinfo, input_buf, in_row_group_ctr, + in_row_groups_avail, post->buffer, + &post->next_row, post->strip_height); /* Allow quantizer to scan new data. No data is emitted, */ /* but we advance out_row_ctr so outer loop can tell when we're done. */ if (post->next_row > old_next_row) { num_rows = post->next_row - old_next_row; - (*cinfo->cquantize->color_quantize) (cinfo, post->buffer + old_next_row, - (JSAMPARRAY)NULL, (int)num_rows); + (*cinfo->cquantize->_color_quantize) (cinfo, post->buffer + old_next_row, + (_JSAMPARRAY)NULL, (int)num_rows); *out_row_ctr += num_rows; } @@ -204,9 +216,9 @@ post_process_prepass(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, */ METHODDEF(void) -post_process_2pass(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, +post_process_2pass(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, - JDIMENSION in_row_groups_avail, JSAMPARRAY output_buf, + JDIMENSION in_row_groups_avail, _JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail) { my_post_ptr post = (my_post_ptr)cinfo->post; @@ -214,7 +226,7 @@ post_process_2pass(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, /* Reposition virtual buffer if at start of strip. */ if (post->next_row == 0) { - post->buffer = (*cinfo->mem->access_virt_sarray) + post->buffer = (_JSAMPARRAY)(*cinfo->mem->access_virt_sarray) ((j_common_ptr)cinfo, post->whole_image, post->starting_row, post->strip_height, FALSE); } @@ -230,9 +242,9 @@ post_process_2pass(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, num_rows = max_rows; /* Quantize and emit data. */ - (*cinfo->cquantize->color_quantize) (cinfo, post->buffer + post->next_row, - output_buf + *out_row_ctr, - (int)num_rows); + (*cinfo->cquantize->_color_quantize) (cinfo, post->buffer + post->next_row, + output_buf + *out_row_ctr, + (int)num_rows); *out_row_ctr += num_rows; /* Advance if we filled the strip. */ @@ -243,7 +255,7 @@ post_process_2pass(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, } } -#endif /* QUANT_2PASS_SUPPORTED */ +#endif /* defined(QUANT_2PASS_SUPPORTED) && BITS_IN_JSAMPLE != 16 */ /* @@ -251,10 +263,13 @@ post_process_2pass(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, */ GLOBAL(void) -jinit_d_post_controller(j_decompress_ptr cinfo, boolean need_full_buffer) +_jinit_d_post_controller(j_decompress_ptr cinfo, boolean need_full_buffer) { my_post_ptr post; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + post = (my_post_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, sizeof(my_post_controller)); @@ -265,6 +280,7 @@ jinit_d_post_controller(j_decompress_ptr cinfo, boolean need_full_buffer) /* Create the quantization buffer, if needed */ if (cinfo->quantize_colors) { +#if BITS_IN_JSAMPLE != 16 /* The buffer strip height is max_v_samp_factor, which is typically * an efficient number of rows for upsampling to return. * (In the presence of output rescaling, we might want to be smarter?) @@ -285,10 +301,15 @@ jinit_d_post_controller(j_decompress_ptr cinfo, boolean need_full_buffer) #endif /* QUANT_2PASS_SUPPORTED */ } else { /* One-pass color quantization: just make a strip buffer. */ - post->buffer = (*cinfo->mem->alloc_sarray) + post->buffer = (_JSAMPARRAY)(*cinfo->mem->alloc_sarray) ((j_common_ptr)cinfo, JPOOL_IMAGE, cinfo->output_width * cinfo->out_color_components, post->strip_height); } +#else + ERREXIT(cinfo, JERR_NOTIMPL); +#endif } } + +#endif /* BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED) */ diff --git a/jdsample.c b/jdsample.c index eaad72a..cc8015c 100644 --- a/jdsample.c +++ b/jdsample.c @@ -5,7 +5,7 @@ * Copyright (C) 1991-1996, Thomas G. Lane. * libjpeg-turbo Modifications: * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2010, 2015-2016, D. R. Commander. + * Copyright (C) 2010, 2015-2016, 2022, D. R. Commander. * Copyright (C) 2014, MIPS Technologies, Inc., California. * Copyright (C) 2015, Google, Inc. * Copyright (C) 2019-2020, Arm Limited. @@ -28,10 +28,12 @@ #include "jinclude.h" #include "jdsample.h" #include "jsimd.h" -#include "jpegcomp.h" +#include "jpegapicomp.h" +#if BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED) + /* * Initialize for an upsampling pass. */ @@ -57,9 +59,9 @@ start_pass_upsample(j_decompress_ptr cinfo) */ METHODDEF(void) -sep_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, +sep_upsample(j_decompress_ptr cinfo, _JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, JDIMENSION in_row_groups_avail, - JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + _JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail) { my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample; @@ -95,9 +97,10 @@ sep_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, if (num_rows > out_rows_avail) num_rows = out_rows_avail; - (*cinfo->cconvert->color_convert) (cinfo, upsample->color_buf, - (JDIMENSION)upsample->next_row_out, - output_buf + *out_row_ctr, (int)num_rows); + (*cinfo->cconvert->_color_convert) (cinfo, upsample->color_buf, + (JDIMENSION)upsample->next_row_out, + output_buf + *out_row_ctr, + (int)num_rows); /* Adjust counts */ *out_row_ctr += num_rows; @@ -124,7 +127,7 @@ sep_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, METHODDEF(void) fullsize_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) + _JSAMPARRAY input_data, _JSAMPARRAY *output_data_ptr) { *output_data_ptr = input_data; } @@ -137,7 +140,7 @@ fullsize_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, METHODDEF(void) noop_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) + _JSAMPARRAY input_data, _JSAMPARRAY *output_data_ptr) { *output_data_ptr = NULL; /* safety check */ } @@ -156,14 +159,14 @@ noop_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, METHODDEF(void) int_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) + _JSAMPARRAY input_data, _JSAMPARRAY *output_data_ptr) { my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample; - JSAMPARRAY output_data = *output_data_ptr; - register JSAMPROW inptr, outptr; - register JSAMPLE invalue; + _JSAMPARRAY output_data = *output_data_ptr; + register _JSAMPROW inptr, outptr; + register _JSAMPLE invalue; register int h; - JSAMPROW outend; + _JSAMPROW outend; int h_expand, v_expand; int inrow, outrow; @@ -184,8 +187,8 @@ int_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, } /* Generate any additional output rows by duplicating the first one */ if (v_expand > 1) { - jcopy_sample_rows(output_data, outrow, output_data, outrow + 1, - v_expand - 1, cinfo->output_width); + _jcopy_sample_rows(output_data, outrow, output_data, outrow + 1, + v_expand - 1, cinfo->output_width); } inrow++; outrow += v_expand; @@ -200,12 +203,12 @@ int_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, METHODDEF(void) h2v1_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) + _JSAMPARRAY input_data, _JSAMPARRAY *output_data_ptr) { - JSAMPARRAY output_data = *output_data_ptr; - register JSAMPROW inptr, outptr; - register JSAMPLE invalue; - JSAMPROW outend; + _JSAMPARRAY output_data = *output_data_ptr; + register _JSAMPROW inptr, outptr; + register _JSAMPLE invalue; + _JSAMPROW outend; int inrow; for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) { @@ -228,12 +231,12 @@ h2v1_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, METHODDEF(void) h2v2_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) + _JSAMPARRAY input_data, _JSAMPARRAY *output_data_ptr) { - JSAMPARRAY output_data = *output_data_ptr; - register JSAMPROW inptr, outptr; - register JSAMPLE invalue; - JSAMPROW outend; + _JSAMPARRAY output_data = *output_data_ptr; + register _JSAMPROW inptr, outptr; + register _JSAMPLE invalue; + _JSAMPROW outend; int inrow, outrow; inrow = outrow = 0; @@ -246,8 +249,8 @@ h2v2_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, *outptr++ = invalue; *outptr++ = invalue; } - jcopy_sample_rows(output_data, outrow, output_data, outrow + 1, 1, - cinfo->output_width); + _jcopy_sample_rows(output_data, outrow, output_data, outrow + 1, 1, + cinfo->output_width); inrow++; outrow += 2; } @@ -271,10 +274,10 @@ h2v2_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, METHODDEF(void) h2v1_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) + _JSAMPARRAY input_data, _JSAMPARRAY *output_data_ptr) { - JSAMPARRAY output_data = *output_data_ptr; - register JSAMPROW inptr, outptr; + _JSAMPARRAY output_data = *output_data_ptr; + register _JSAMPROW inptr, outptr; register int invalue; register JDIMENSION colctr; int inrow; @@ -284,20 +287,20 @@ h2v1_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, outptr = output_data[inrow]; /* Special case for first column */ invalue = *inptr++; - *outptr++ = (JSAMPLE)invalue; - *outptr++ = (JSAMPLE)((invalue * 3 + inptr[0] + 2) >> 2); + *outptr++ = (_JSAMPLE)invalue; + *outptr++ = (_JSAMPLE)((invalue * 3 + inptr[0] + 2) >> 2); for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) { /* General case: 3/4 * nearer pixel + 1/4 * further pixel */ invalue = (*inptr++) * 3; - *outptr++ = (JSAMPLE)((invalue + inptr[-2] + 1) >> 2); - *outptr++ = (JSAMPLE)((invalue + inptr[0] + 2) >> 2); + *outptr++ = (_JSAMPLE)((invalue + inptr[-2] + 1) >> 2); + *outptr++ = (_JSAMPLE)((invalue + inptr[0] + 2) >> 2); } /* Special case for last column */ invalue = *inptr; - *outptr++ = (JSAMPLE)((invalue * 3 + inptr[-1] + 1) >> 2); - *outptr++ = (JSAMPLE)invalue; + *outptr++ = (_JSAMPLE)((invalue * 3 + inptr[-1] + 1) >> 2); + *outptr++ = (_JSAMPLE)invalue; } } @@ -311,10 +314,10 @@ h2v1_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, METHODDEF(void) h1v2_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) + _JSAMPARRAY input_data, _JSAMPARRAY *output_data_ptr) { - JSAMPARRAY output_data = *output_data_ptr; - JSAMPROW inptr0, inptr1, outptr; + _JSAMPARRAY output_data = *output_data_ptr; + _JSAMPROW inptr0, inptr1, outptr; #if BITS_IN_JSAMPLE == 8 int thiscolsum, bias; #else @@ -339,7 +342,7 @@ h1v2_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, for (colctr = 0; colctr < compptr->downsampled_width; colctr++) { thiscolsum = (*inptr0++) * 3 + (*inptr1++); - *outptr++ = (JSAMPLE)((thiscolsum + bias) >> 2); + *outptr++ = (_JSAMPLE)((thiscolsum + bias) >> 2); } } inrow++; @@ -357,10 +360,10 @@ h1v2_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, METHODDEF(void) h2v2_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) + _JSAMPARRAY input_data, _JSAMPARRAY *output_data_ptr) { - JSAMPARRAY output_data = *output_data_ptr; - register JSAMPROW inptr0, inptr1, outptr; + _JSAMPARRAY output_data = *output_data_ptr; + register _JSAMPROW inptr0, inptr1, outptr; #if BITS_IN_JSAMPLE == 8 register int thiscolsum, lastcolsum, nextcolsum; #else @@ -383,22 +386,22 @@ h2v2_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, /* Special case for first column */ thiscolsum = (*inptr0++) * 3 + (*inptr1++); nextcolsum = (*inptr0++) * 3 + (*inptr1++); - *outptr++ = (JSAMPLE)((thiscolsum * 4 + 8) >> 4); - *outptr++ = (JSAMPLE)((thiscolsum * 3 + nextcolsum + 7) >> 4); + *outptr++ = (_JSAMPLE)((thiscolsum * 4 + 8) >> 4); + *outptr++ = (_JSAMPLE)((thiscolsum * 3 + nextcolsum + 7) >> 4); lastcolsum = thiscolsum; thiscolsum = nextcolsum; for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) { /* General case: 3/4 * nearer pixel + 1/4 * further pixel in each */ /* dimension, thus 9/16, 3/16, 3/16, 1/16 overall */ nextcolsum = (*inptr0++) * 3 + (*inptr1++); - *outptr++ = (JSAMPLE)((thiscolsum * 3 + lastcolsum + 8) >> 4); - *outptr++ = (JSAMPLE)((thiscolsum * 3 + nextcolsum + 7) >> 4); + *outptr++ = (_JSAMPLE)((thiscolsum * 3 + lastcolsum + 8) >> 4); + *outptr++ = (_JSAMPLE)((thiscolsum * 3 + nextcolsum + 7) >> 4); lastcolsum = thiscolsum; thiscolsum = nextcolsum; } /* Special case for last column */ - *outptr++ = (JSAMPLE)((thiscolsum * 3 + lastcolsum + 8) >> 4); - *outptr++ = (JSAMPLE)((thiscolsum * 4 + 7) >> 4); + *outptr++ = (_JSAMPLE)((thiscolsum * 3 + lastcolsum + 8) >> 4); + *outptr++ = (_JSAMPLE)((thiscolsum * 4 + 7) >> 4); } inrow++; } @@ -410,7 +413,7 @@ h2v2_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, */ GLOBAL(void) -jinit_upsampler(j_decompress_ptr cinfo) +_jinit_upsampler(j_decompress_ptr cinfo) { my_upsample_ptr upsample; int ci; @@ -418,13 +421,16 @@ jinit_upsampler(j_decompress_ptr cinfo) boolean need_buffer, do_fancy; int h_in_group, v_in_group, h_out_group, v_out_group; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + if (!cinfo->master->jinit_upsampler_no_alloc) { upsample = (my_upsample_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, sizeof(my_upsampler)); cinfo->upsample = (struct jpeg_upsampler *)upsample; upsample->pub.start_pass = start_pass_upsample; - upsample->pub.upsample = sep_upsample; + upsample->pub._upsample = sep_upsample; upsample->pub.need_context_rows = FALSE; /* until we find out differently */ } else upsample = (my_upsample_ptr)cinfo->upsample; @@ -464,21 +470,25 @@ jinit_upsampler(j_decompress_ptr cinfo) } else if (h_in_group * 2 == h_out_group && v_in_group == v_out_group) { /* Special cases for 2h1v upsampling */ if (do_fancy && compptr->downsampled_width > 2) { +#ifdef WITH_SIMD if (jsimd_can_h2v1_fancy_upsample()) upsample->methods[ci] = jsimd_h2v1_fancy_upsample; else +#endif upsample->methods[ci] = h2v1_fancy_upsample; } else { +#ifdef WITH_SIMD if (jsimd_can_h2v1_upsample()) upsample->methods[ci] = jsimd_h2v1_upsample; else +#endif upsample->methods[ci] = h2v1_upsample; } } else if (h_in_group == h_out_group && v_in_group * 2 == v_out_group && do_fancy) { /* Non-fancy upsampling is handled by the generic method */ -#if defined(__arm__) || defined(__aarch64__) || \ - defined(_M_ARM) || defined(_M_ARM64) +#if defined(WITH_SIMD) && (defined(__arm__) || defined(__aarch64__) || \ + defined(_M_ARM) || defined(_M_ARM64)) if (jsimd_can_h1v2_fancy_upsample()) upsample->methods[ci] = jsimd_h1v2_fancy_upsample; else @@ -489,21 +499,25 @@ jinit_upsampler(j_decompress_ptr cinfo) v_in_group * 2 == v_out_group) { /* Special cases for 2h2v upsampling */ if (do_fancy && compptr->downsampled_width > 2) { +#ifdef WITH_SIMD if (jsimd_can_h2v2_fancy_upsample()) upsample->methods[ci] = jsimd_h2v2_fancy_upsample; else +#endif upsample->methods[ci] = h2v2_fancy_upsample; upsample->pub.need_context_rows = TRUE; } else { +#ifdef WITH_SIMD if (jsimd_can_h2v2_upsample()) upsample->methods[ci] = jsimd_h2v2_upsample; else +#endif upsample->methods[ci] = h2v2_upsample; } } else if ((h_out_group % h_in_group) == 0 && (v_out_group % v_in_group) == 0) { /* Generic integral-factors upsampling method */ -#if defined(__mips__) +#if defined(WITH_SIMD) && defined(__mips__) if (jsimd_can_int_upsample()) upsample->methods[ci] = jsimd_int_upsample; else @@ -514,7 +528,7 @@ jinit_upsampler(j_decompress_ptr cinfo) } else ERREXIT(cinfo, JERR_FRACT_SAMPLE_NOTIMPL); if (need_buffer && !cinfo->master->jinit_upsampler_no_alloc) { - upsample->color_buf[ci] = (*cinfo->mem->alloc_sarray) + upsample->color_buf[ci] = (_JSAMPARRAY)(*cinfo->mem->alloc_sarray) ((j_common_ptr)cinfo, JPOOL_IMAGE, (JDIMENSION)jround_up((long)cinfo->output_width, (long)cinfo->max_h_samp_factor), @@ -522,3 +536,5 @@ jinit_upsampler(j_decompress_ptr cinfo) } } } + +#endif /* BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED) */ diff --git a/jdsample.h b/jdsample.h index a6bf08a..a8a9298 100644 --- a/jdsample.h +++ b/jdsample.h @@ -3,19 +3,22 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1996, Thomas G. Lane. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. */ #define JPEG_INTERNALS #include "jpeglib.h" +#include "jsamplecomp.h" /* Pointer to routine to upsample a single component */ typedef void (*upsample1_ptr) (j_decompress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, - JSAMPARRAY *output_data_ptr); + _JSAMPARRAY input_data, + _JSAMPARRAY *output_data_ptr); /* Private subobject */ @@ -29,7 +32,7 @@ typedef struct { * ie do not need rescaling. The corresponding entry of color_buf[] is * simply set to point to the input data array, thereby avoiding copying. */ - JSAMPARRAY color_buf[MAX_COMPONENTS]; + _JSAMPARRAY color_buf[MAX_COMPONENTS]; /* Per-component upsampling method pointers */ upsample1_ptr methods[MAX_COMPONENTS]; diff --git a/jdtrans.c b/jdtrans.c index d7ec4b8..719813f 100644 --- a/jdtrans.c +++ b/jdtrans.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1995-1997, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2020, D. R. Commander. + * Copyright (C) 2020, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -16,7 +16,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jpegcomp.h" +#include "jpegapicomp.h" /* Forward declarations */ @@ -48,6 +48,9 @@ LOCAL(void) transdecode_master_selection(j_decompress_ptr cinfo); GLOBAL(jvirt_barray_ptr *) jpeg_read_coefficients(j_decompress_ptr cinfo) { + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); + if (cinfo->global_state == DSTATE_READY) { /* First call: initialize active modules */ transdecode_master_selection(cinfo); @@ -127,7 +130,10 @@ transdecode_master_selection(j_decompress_ptr cinfo) } /* Always get a full-image coefficient buffer. */ - jinit_d_coef_controller(cinfo, TRUE); + if (cinfo->data_precision == 12) + j12init_d_coef_controller(cinfo, TRUE); + else + jinit_d_coef_controller(cinfo, TRUE); /* We can now tell the memory manager to allocate virtual arrays. */ (*cinfo->mem->realize_virt_arrays) ((j_common_ptr)cinfo); diff --git a/jerror.h b/jerror.h index eb44a11..39362fd 100644 --- a/jerror.h +++ b/jerror.h @@ -4,6 +4,8 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1997, Thomas G. Lane. * Modified 1997-2009 by Guido Vollbeding. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2014, 2017, 2021-2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg @@ -53,7 +55,8 @@ JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") #if JPEG_LIB_VERSION >= 70 JMESSAGE(JERR_BAD_CROP_SPEC, "Invalid crop request") #endif -JMESSAGE(JERR_BAD_DCT_COEF, "DCT coefficient out of range") +JMESSAGE(JERR_BAD_DCT_COEF, + "DCT coefficient (lossy) or spatial difference (lossless) out of range") JMESSAGE(JERR_BAD_DCTSIZE, "IDCT output block size %d not supported") #if JPEG_LIB_VERSION >= 70 JMESSAGE(JERR_BAD_DROP_SAMPLING, @@ -69,9 +72,9 @@ JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan") JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d") JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d") JMESSAGE(JERR_BAD_PROGRESSION, - "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d") + "Invalid progressive/lossless parameters Ss=%d Se=%d Ah=%d Al=%d") JMESSAGE(JERR_BAD_PROG_SCRIPT, - "Invalid progressive parameters at scan script entry %d") + "Invalid progressive/lossless parameters at scan script entry %d") JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors") JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d") JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d") @@ -180,7 +183,7 @@ JMESSAGE(JTRC_THUMB_PALETTE, JMESSAGE(JTRC_THUMB_RGB, "JFIF extension marker: RGB thumbnail image, length %u") JMESSAGE(JTRC_UNKNOWN_IDS, - "Unrecognized component IDs %d %d %d, assuming YCbCr") + "Unrecognized component IDs %d %d %d, assuming YCbCr (lossy) or RGB (lossless)") JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") @@ -211,6 +214,8 @@ JMESSAGE(JWRN_BOGUS_ICC, "Corrupt JPEG data: bad ICC marker") JMESSAGE(JERR_BAD_DROP_SAMPLING, "Component index %d: mismatching sampling ratio %d:%d, %d:%d, %c") #endif +JMESSAGE(JERR_BAD_RESTART, + "Invalid restart interval %d; must be an integer multiple of the number of MCUs in an MCU row (%d)") #ifdef JMAKE_ENUM_LIST diff --git a/jfdctfst.c b/jfdctfst.c index 4c9ce0d..26070d1 100644 --- a/jfdctfst.c +++ b/jfdctfst.c @@ -114,7 +114,7 @@ */ GLOBAL(void) -jpeg_fdct_ifast(DCTELEM *data) +_jpeg_fdct_ifast(DCTELEM *data) { DCTELEM tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; DCTELEM tmp10, tmp11, tmp12, tmp13; diff --git a/jfdctint.c b/jfdctint.c index c95a3a7..974013f 100644 --- a/jfdctint.c +++ b/jfdctint.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2015, 2020, D. R. Commander. + * Copyright (C) 2015, 2020, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -140,7 +140,7 @@ */ GLOBAL(void) -jpeg_fdct_islow(DCTELEM *data) +_jpeg_fdct_islow(DCTELEM *data) { JLONG tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; JLONG tmp10, tmp11, tmp12, tmp13; diff --git a/jidctflt.c b/jidctflt.c index 5aee74e..ee3a31a 100644 --- a/jidctflt.c +++ b/jidctflt.c @@ -5,7 +5,7 @@ * Copyright (C) 1994-1998, Thomas G. Lane. * Modified 2010 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2014, D. R. Commander. + * Copyright (C) 2014, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -69,9 +69,9 @@ */ GLOBAL(void) -jpeg_idct_float(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) +_jpeg_idct_float(j_decompress_ptr cinfo, jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col) { FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; FAST_FLOAT tmp10, tmp11, tmp12, tmp13; @@ -79,8 +79,8 @@ jpeg_idct_float(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR inptr; FLOAT_MULT_TYPE *quantptr; FAST_FLOAT *wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = cinfo->sample_range_limit; + _JSAMPROW outptr; + _JSAMPLE *range_limit = (_JSAMPLE *)cinfo->sample_range_limit; int ctr; FAST_FLOAT workspace[DCTSIZE2]; /* buffers data between passes */ #define _0_125 ((FLOAT_MULT_TYPE)0.125) @@ -192,7 +192,7 @@ jpeg_idct_float(j_decompress_ptr cinfo, jpeg_component_info *compptr, /* Even part */ /* Apply signed->unsigned and prepare float->int conversion */ - z5 = wsptr[0] + ((FAST_FLOAT)CENTERJSAMPLE + (FAST_FLOAT)0.5); + z5 = wsptr[0] + ((FAST_FLOAT)_CENTERJSAMPLE + (FAST_FLOAT)0.5); tmp10 = z5 + wsptr[4]; tmp11 = z5 - wsptr[4]; diff --git a/jidctfst.c b/jidctfst.c index 89a20c9..68119b9 100644 --- a/jidctfst.c +++ b/jidctfst.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1998, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2015, D. R. Commander. + * Copyright (C) 2015, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -64,10 +64,10 @@ * The dequantized coefficients are not integers because the AA&N scaling * factors have been incorporated. We represent them scaled up by PASS1_BITS, * so that the first and second IDCT rounds have the same input scaling. - * For 8-bit JSAMPLEs, we choose IFAST_SCALE_BITS = PASS1_BITS so as to + * For 8-bit samples, we choose IFAST_SCALE_BITS = PASS1_BITS so as to * avoid a descaling shift; this compromises accuracy rather drastically * for small quantization table entries, but it saves a lot of shifts. - * For 12-bit JSAMPLEs, there's no hope of using 16x16 multiplies anyway, + * For 12-bit samples, there's no hope of using 16x16 multiplies anyway, * so we use a much larger scaling factor to preserve accuracy. * * A final compromise is to represent the multiplicative constants to only @@ -168,9 +168,9 @@ */ GLOBAL(void) -jpeg_idct_ifast(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) +_jpeg_idct_ifast(j_decompress_ptr cinfo, jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col) { DCTELEM tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; DCTELEM tmp10, tmp11, tmp12, tmp13; @@ -178,8 +178,8 @@ jpeg_idct_ifast(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR inptr; IFAST_MULT_TYPE *quantptr; int *wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); + _JSAMPROW outptr; + _JSAMPLE *range_limit = IDCT_range_limit(cinfo); int ctr; int workspace[DCTSIZE2]; /* buffers data between passes */ SHIFT_TEMPS /* for DESCALE */ @@ -296,7 +296,7 @@ jpeg_idct_ifast(j_decompress_ptr cinfo, jpeg_component_info *compptr, if (wsptr[1] == 0 && wsptr[2] == 0 && wsptr[3] == 0 && wsptr[4] == 0 && wsptr[5] == 0 && wsptr[6] == 0 && wsptr[7] == 0) { /* AC terms all zero */ - JSAMPLE dcval = + _JSAMPLE dcval = range_limit[IDESCALE(wsptr[0], PASS1_BITS + 3) & RANGE_MASK]; outptr[0] = dcval; diff --git a/jidctint.c b/jidctint.c index bb08748..c58592d 100644 --- a/jidctint.c +++ b/jidctint.c @@ -5,7 +5,7 @@ * Copyright (C) 1991-1998, Thomas G. Lane. * Modification developed 2002-2018 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2015, 2020, D. R. Commander. + * Copyright (C) 2015, 2020, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -170,9 +170,9 @@ */ GLOBAL(void) -jpeg_idct_islow(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) +_jpeg_idct_islow(j_decompress_ptr cinfo, jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col) { JLONG tmp0, tmp1, tmp2, tmp3; JLONG tmp10, tmp11, tmp12, tmp13; @@ -180,8 +180,8 @@ jpeg_idct_islow(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR inptr; ISLOW_MULT_TYPE *quantptr; int *wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); + _JSAMPROW outptr; + _JSAMPLE *range_limit = IDCT_range_limit(cinfo); int ctr; int workspace[DCTSIZE2]; /* buffers data between passes */ SHIFT_TEMPS @@ -314,8 +314,8 @@ jpeg_idct_islow(j_decompress_ptr cinfo, jpeg_component_info *compptr, if (wsptr[1] == 0 && wsptr[2] == 0 && wsptr[3] == 0 && wsptr[4] == 0 && wsptr[5] == 0 && wsptr[6] == 0 && wsptr[7] == 0) { /* AC terms all zero */ - JSAMPLE dcval = range_limit[(int)DESCALE((JLONG)wsptr[0], - PASS1_BITS + 3) & RANGE_MASK]; + _JSAMPLE dcval = range_limit[(int)DESCALE((JLONG)wsptr[0], + PASS1_BITS + 3) & RANGE_MASK]; outptr[0] = dcval; outptr[1] = dcval; @@ -424,17 +424,17 @@ jpeg_idct_islow(j_decompress_ptr cinfo, jpeg_component_info *compptr, */ GLOBAL(void) -jpeg_idct_7x7(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) +_jpeg_idct_7x7(j_decompress_ptr cinfo, jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col) { JLONG tmp0, tmp1, tmp2, tmp10, tmp11, tmp12, tmp13; JLONG z1, z2, z3; JCOEFPTR inptr; ISLOW_MULT_TYPE *quantptr; int *wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); + _JSAMPROW outptr; + _JSAMPLE *range_limit = IDCT_range_limit(cinfo); int ctr; int workspace[7 * 7]; /* buffers data between passes */ SHIFT_TEMPS @@ -573,17 +573,17 @@ jpeg_idct_7x7(j_decompress_ptr cinfo, jpeg_component_info *compptr, */ GLOBAL(void) -jpeg_idct_6x6(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) +_jpeg_idct_6x6(j_decompress_ptr cinfo, jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col) { JLONG tmp0, tmp1, tmp2, tmp10, tmp11, tmp12; JLONG z1, z2, z3; JCOEFPTR inptr; ISLOW_MULT_TYPE *quantptr; int *wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); + _JSAMPROW outptr; + _JSAMPLE *range_limit = IDCT_range_limit(cinfo); int ctr; int workspace[6 * 6]; /* buffers data between passes */ SHIFT_TEMPS @@ -694,17 +694,17 @@ jpeg_idct_6x6(j_decompress_ptr cinfo, jpeg_component_info *compptr, */ GLOBAL(void) -jpeg_idct_5x5(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) +_jpeg_idct_5x5(j_decompress_ptr cinfo, jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col) { JLONG tmp0, tmp1, tmp10, tmp11, tmp12; JLONG z1, z2, z3; JCOEFPTR inptr; ISLOW_MULT_TYPE *quantptr; int *wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); + _JSAMPROW outptr; + _JSAMPLE *range_limit = IDCT_range_limit(cinfo); int ctr; int workspace[5 * 5]; /* buffers data between passes */ SHIFT_TEMPS @@ -809,16 +809,16 @@ jpeg_idct_5x5(j_decompress_ptr cinfo, jpeg_component_info *compptr, */ GLOBAL(void) -jpeg_idct_3x3(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) +_jpeg_idct_3x3(j_decompress_ptr cinfo, jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col) { JLONG tmp0, tmp2, tmp10, tmp12; JCOEFPTR inptr; ISLOW_MULT_TYPE *quantptr; int *wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); + _JSAMPROW outptr; + _JSAMPLE *range_limit = IDCT_range_limit(cinfo); int ctr; int workspace[3 * 3]; /* buffers data between passes */ SHIFT_TEMPS @@ -899,17 +899,17 @@ jpeg_idct_3x3(j_decompress_ptr cinfo, jpeg_component_info *compptr, */ GLOBAL(void) -jpeg_idct_9x9(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) +_jpeg_idct_9x9(j_decompress_ptr cinfo, jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col) { JLONG tmp0, tmp1, tmp2, tmp3, tmp10, tmp11, tmp12, tmp13, tmp14; JLONG z1, z2, z3, z4; JCOEFPTR inptr; ISLOW_MULT_TYPE *quantptr; int *wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); + _JSAMPROW outptr; + _JSAMPLE *range_limit = IDCT_range_limit(cinfo); int ctr; int workspace[8 * 9]; /* buffers data between passes */ SHIFT_TEMPS @@ -1070,9 +1070,9 @@ jpeg_idct_9x9(j_decompress_ptr cinfo, jpeg_component_info *compptr, */ GLOBAL(void) -jpeg_idct_10x10(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) +_jpeg_idct_10x10(j_decompress_ptr cinfo, jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col) { JLONG tmp10, tmp11, tmp12, tmp13, tmp14; JLONG tmp20, tmp21, tmp22, tmp23, tmp24; @@ -1080,8 +1080,8 @@ jpeg_idct_10x10(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR inptr; ISLOW_MULT_TYPE *quantptr; int *wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); + _JSAMPROW outptr; + _JSAMPLE *range_limit = IDCT_range_limit(cinfo); int ctr; int workspace[8 * 10]; /* buffers data between passes */ SHIFT_TEMPS @@ -1265,9 +1265,9 @@ jpeg_idct_10x10(j_decompress_ptr cinfo, jpeg_component_info *compptr, */ GLOBAL(void) -jpeg_idct_11x11(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) +_jpeg_idct_11x11(j_decompress_ptr cinfo, jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col) { JLONG tmp10, tmp11, tmp12, tmp13, tmp14; JLONG tmp20, tmp21, tmp22, tmp23, tmp24, tmp25; @@ -1275,8 +1275,8 @@ jpeg_idct_11x11(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR inptr; ISLOW_MULT_TYPE *quantptr; int *wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); + _JSAMPROW outptr; + _JSAMPLE *range_limit = IDCT_range_limit(cinfo); int ctr; int workspace[8 * 11]; /* buffers data between passes */ SHIFT_TEMPS @@ -1459,9 +1459,9 @@ jpeg_idct_11x11(j_decompress_ptr cinfo, jpeg_component_info *compptr, */ GLOBAL(void) -jpeg_idct_12x12(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) +_jpeg_idct_12x12(j_decompress_ptr cinfo, jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col) { JLONG tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; JLONG tmp20, tmp21, tmp22, tmp23, tmp24, tmp25; @@ -1469,8 +1469,8 @@ jpeg_idct_12x12(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR inptr; ISLOW_MULT_TYPE *quantptr; int *wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); + _JSAMPROW outptr; + _JSAMPLE *range_limit = IDCT_range_limit(cinfo); int ctr; int workspace[8 * 12]; /* buffers data between passes */ SHIFT_TEMPS @@ -1675,9 +1675,9 @@ jpeg_idct_12x12(j_decompress_ptr cinfo, jpeg_component_info *compptr, */ GLOBAL(void) -jpeg_idct_13x13(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) +_jpeg_idct_13x13(j_decompress_ptr cinfo, jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col) { JLONG tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; JLONG tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26; @@ -1685,8 +1685,8 @@ jpeg_idct_13x13(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR inptr; ISLOW_MULT_TYPE *quantptr; int *wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); + _JSAMPROW outptr; + _JSAMPLE *range_limit = IDCT_range_limit(cinfo); int ctr; int workspace[8 * 13]; /* buffers data between passes */ SHIFT_TEMPS @@ -1903,9 +1903,9 @@ jpeg_idct_13x13(j_decompress_ptr cinfo, jpeg_component_info *compptr, */ GLOBAL(void) -jpeg_idct_14x14(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) +_jpeg_idct_14x14(j_decompress_ptr cinfo, jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col) { JLONG tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16; JLONG tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26; @@ -1913,8 +1913,8 @@ jpeg_idct_14x14(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR inptr; ISLOW_MULT_TYPE *quantptr; int *wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); + _JSAMPROW outptr; + _JSAMPLE *range_limit = IDCT_range_limit(cinfo); int ctr; int workspace[8 * 14]; /* buffers data between passes */ SHIFT_TEMPS @@ -2129,9 +2129,9 @@ jpeg_idct_14x14(j_decompress_ptr cinfo, jpeg_component_info *compptr, */ GLOBAL(void) -jpeg_idct_15x15(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) +_jpeg_idct_15x15(j_decompress_ptr cinfo, jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col) { JLONG tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16; JLONG tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27; @@ -2139,8 +2139,8 @@ jpeg_idct_15x15(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR inptr; ISLOW_MULT_TYPE *quantptr; int *wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); + _JSAMPROW outptr; + _JSAMPLE *range_limit = IDCT_range_limit(cinfo); int ctr; int workspace[8 * 15]; /* buffers data between passes */ SHIFT_TEMPS @@ -2371,9 +2371,9 @@ jpeg_idct_15x15(j_decompress_ptr cinfo, jpeg_component_info *compptr, */ GLOBAL(void) -jpeg_idct_16x16(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) +_jpeg_idct_16x16(j_decompress_ptr cinfo, jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col) { JLONG tmp0, tmp1, tmp2, tmp3, tmp10, tmp11, tmp12, tmp13; JLONG tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27; @@ -2381,8 +2381,8 @@ jpeg_idct_16x16(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR inptr; ISLOW_MULT_TYPE *quantptr; int *wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); + _JSAMPROW outptr; + _JSAMPLE *range_limit = IDCT_range_limit(cinfo); int ctr; int workspace[8 * 16]; /* buffers data between passes */ SHIFT_TEMPS diff --git a/jidctred.c b/jidctred.c index 1dd65a9..6521e3e 100644 --- a/jidctred.c +++ b/jidctred.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1998, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2015, D. R. Commander. + * Copyright (C) 2015, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -118,17 +118,17 @@ */ GLOBAL(void) -jpeg_idct_4x4(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) +_jpeg_idct_4x4(j_decompress_ptr cinfo, jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col) { JLONG tmp0, tmp2, tmp10, tmp12; JLONG z1, z2, z3, z4; JCOEFPTR inptr; ISLOW_MULT_TYPE *quantptr; int *wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); + _JSAMPROW outptr; + _JSAMPLE *range_limit = IDCT_range_limit(cinfo); int ctr; int workspace[DCTSIZE * 4]; /* buffers data between passes */ SHIFT_TEMPS @@ -210,8 +210,8 @@ jpeg_idct_4x4(j_decompress_ptr cinfo, jpeg_component_info *compptr, if (wsptr[1] == 0 && wsptr[2] == 0 && wsptr[3] == 0 && wsptr[5] == 0 && wsptr[6] == 0 && wsptr[7] == 0) { /* AC terms all zero */ - JSAMPLE dcval = range_limit[(int)DESCALE((JLONG)wsptr[0], - PASS1_BITS + 3) & RANGE_MASK]; + _JSAMPLE dcval = range_limit[(int)DESCALE((JLONG)wsptr[0], + PASS1_BITS + 3) & RANGE_MASK]; outptr[0] = dcval; outptr[1] = dcval; @@ -276,16 +276,16 @@ jpeg_idct_4x4(j_decompress_ptr cinfo, jpeg_component_info *compptr, */ GLOBAL(void) -jpeg_idct_2x2(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) +_jpeg_idct_2x2(j_decompress_ptr cinfo, jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col) { JLONG tmp0, tmp10, z1; JCOEFPTR inptr; ISLOW_MULT_TYPE *quantptr; int *wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); + _JSAMPROW outptr; + _JSAMPLE *range_limit = IDCT_range_limit(cinfo); int ctr; int workspace[DCTSIZE * 2]; /* buffers data between passes */ SHIFT_TEMPS @@ -345,8 +345,8 @@ jpeg_idct_2x2(j_decompress_ptr cinfo, jpeg_component_info *compptr, #ifndef NO_ZERO_ROW_TEST if (wsptr[1] == 0 && wsptr[3] == 0 && wsptr[5] == 0 && wsptr[7] == 0) { /* AC terms all zero */ - JSAMPLE dcval = range_limit[(int)DESCALE((JLONG)wsptr[0], - PASS1_BITS + 3) & RANGE_MASK]; + _JSAMPLE dcval = range_limit[(int)DESCALE((JLONG)wsptr[0], + PASS1_BITS + 3) & RANGE_MASK]; outptr[0] = dcval; outptr[1] = dcval; @@ -387,13 +387,13 @@ jpeg_idct_2x2(j_decompress_ptr cinfo, jpeg_component_info *compptr, */ GLOBAL(void) -jpeg_idct_1x1(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) +_jpeg_idct_1x1(j_decompress_ptr cinfo, jpeg_component_info *compptr, + JCOEFPTR coef_block, _JSAMPARRAY output_buf, + JDIMENSION output_col) { int dcval; ISLOW_MULT_TYPE *quantptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); + _JSAMPLE *range_limit = IDCT_range_limit(cinfo); SHIFT_TEMPS /* We hardly need an inverse DCT routine for this: just take the diff --git a/jinclude.h b/jinclude.h index e8d983a..56e7a4b 100644 --- a/jinclude.h +++ b/jinclude.h @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1994, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2022, D. R. Commander. + * Copyright (C) 2022-2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -123,6 +123,8 @@ static INLINE int GETENV_S(char *buffer, size_t buffer_size, const char *name) #else +#include + /* This provides a similar interface to the Microsoft _putenv_s() function, but * other than parameter validation, it has no advantages over setenv(). */ diff --git a/jlossls.h b/jlossls.h new file mode 100644 index 0000000..ce41704 --- /dev/null +++ b/jlossls.h @@ -0,0 +1,101 @@ +/* + * jlossls.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1998, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This include file contains common declarations for the lossless JPEG + * codec modules. + */ + +#ifndef JLOSSLS_H +#define JLOSSLS_H + +#if defined(C_LOSSLESS_SUPPORTED) || defined(D_LOSSLESS_SUPPORTED) + +#define JPEG_INTERNALS +#include "jpeglib.h" +#include "jsamplecomp.h" + + +#define ALLOC_DARRAY(pool_id, diffsperrow, numrows) \ + (JDIFFARRAY)(*cinfo->mem->alloc_sarray) \ + ((j_common_ptr)cinfo, pool_id, \ + (diffsperrow) * sizeof(JDIFF) / sizeof(_JSAMPLE), numrows) + + +/* + * Table H.1: Predictors for lossless coding. + */ + +#define PREDICTOR1 Ra +#define PREDICTOR2 Rb +#define PREDICTOR3 Rc +#define PREDICTOR4 (int)((JLONG)Ra + (JLONG)Rb - (JLONG)Rc) +#define PREDICTOR5 (int)((JLONG)Ra + RIGHT_SHIFT((JLONG)Rb - (JLONG)Rc, 1)) +#define PREDICTOR6 (int)((JLONG)Rb + RIGHT_SHIFT((JLONG)Ra - (JLONG)Rc, 1)) +#define PREDICTOR7 (int)RIGHT_SHIFT((JLONG)Ra + (JLONG)Rb, 1) + +#endif + + +#ifdef C_LOSSLESS_SUPPORTED + +typedef void (*predict_difference_method_ptr) (j_compress_ptr cinfo, int ci, + _JSAMPROW input_buf, + _JSAMPROW prev_row, + JDIFFROW diff_buf, + JDIMENSION width); + +/* Lossless compressor */ +typedef struct { + struct jpeg_forward_dct pub; /* public fields */ + + /* It is useful to allow each component to have a separate diff method. */ + predict_difference_method_ptr predict_difference[MAX_COMPONENTS]; + + /* MCU rows left in the restart interval for each component */ + unsigned int restart_rows_to_go[MAX_COMPONENTS]; + + /* Sample scaling */ + void (*scaler_scale) (j_compress_ptr cinfo, _JSAMPROW input_buf, + _JSAMPROW output_buf, JDIMENSION width); +} jpeg_lossless_compressor; + +typedef jpeg_lossless_compressor *lossless_comp_ptr; + +#endif /* C_LOSSLESS_SUPPORTED */ + + +#ifdef D_LOSSLESS_SUPPORTED + +typedef void (*predict_undifference_method_ptr) (j_decompress_ptr cinfo, + int comp_index, + JDIFFROW diff_buf, + JDIFFROW prev_row, + JDIFFROW undiff_buf, + JDIMENSION width); + +/* Lossless decompressor */ +typedef struct { + struct jpeg_inverse_dct pub; /* public fields */ + + /* It is useful to allow each component to have a separate undiff method. */ + predict_undifference_method_ptr predict_undifference[MAX_COMPONENTS]; + + /* Sample scaling */ + void (*scaler_scale) (j_decompress_ptr cinfo, JDIFFROW diff_buf, + _JSAMPROW output_buf, JDIMENSION width); +} jpeg_lossless_decompressor; + +typedef jpeg_lossless_decompressor *lossless_decomp_ptr; + +#endif /* D_LOSSLESS_SUPPORTED */ + +#endif /* JLOSSLS_H */ diff --git a/jmemmgr.c b/jmemmgr.c index a40446f..dca8f5c 100644 --- a/jmemmgr.c +++ b/jmemmgr.c @@ -155,7 +155,9 @@ typedef my_memory_mgr *my_mem_ptr; */ struct jvirt_sarray_control { - JSAMPARRAY mem_buffer; /* => the in-memory buffer */ + JSAMPARRAY mem_buffer; /* => the in-memory buffer (if + cinfo->data_precision is 12, then this is + actually a J12SAMPARRAY) */ JDIMENSION rows_in_array; /* total virtual array height */ JDIMENSION samplesperrow; /* width of array (and of memory buffer) */ JDIMENSION maxaccess; /* max rows accessed by access_virt_sarray */ @@ -351,9 +353,10 @@ alloc_small(j_common_ptr cinfo, int pool_id, size_t sizeofobject) * request is large enough that it may as well be passed directly to * jpeg_get_large; the pool management just links everything together * so that we can free it all on demand. - * Note: the major use of "large" objects is in JSAMPARRAY and JBLOCKARRAY - * structures. The routines that create these structures (see below) - * deliberately bunch rows together to ensure a large request size. + * Note: the major use of "large" objects is in + * JSAMPARRAY/J12SAMPARRAY/J16SAMPARRAY and JBLOCKARRAY structures. The + * routines that create these structures (see below) deliberately bunch rows + * together to ensure a large request size. */ METHODDEF(void *) @@ -437,9 +440,22 @@ alloc_sarray(j_common_ptr cinfo, int pool_id, JDIMENSION samplesperrow, JSAMPROW workspace; JDIMENSION rowsperchunk, currow, i; long ltemp; + J12SAMPARRAY result12; + J12SAMPROW workspace12; +#if defined(C_LOSSLESS_SUPPORTED) || defined(D_LOSSLESS_SUPPORTED) + J16SAMPARRAY result16; + J16SAMPROW workspace16; +#endif + int data_precision = cinfo->is_decompressor ? + ((j_decompress_ptr)cinfo)->data_precision : + ((j_compress_ptr)cinfo)->data_precision; + size_t sample_size = data_precision == 16 ? + sizeof(J16SAMPLE) : (data_precision == 12 ? + sizeof(J12SAMPLE) : + sizeof(JSAMPLE)); /* Make sure each row is properly aligned */ - if ((ALIGN_SIZE % sizeof(JSAMPLE)) != 0) + if ((ALIGN_SIZE % sample_size) != 0) out_of_memory(cinfo, 5); /* safety check */ if (samplesperrow > MAX_ALLOC_CHUNK) { @@ -448,11 +464,11 @@ alloc_sarray(j_common_ptr cinfo, int pool_id, JDIMENSION samplesperrow, out_of_memory(cinfo, 9); } samplesperrow = (JDIMENSION)round_up_pow2(samplesperrow, (2 * ALIGN_SIZE) / - sizeof(JSAMPLE)); + sample_size); /* Calculate max # of rows allowed in one allocation chunk */ ltemp = (MAX_ALLOC_CHUNK - sizeof(large_pool_hdr)) / - ((long)samplesperrow * sizeof(JSAMPLE)); + ((long)samplesperrow * (long)sample_size); if (ltemp <= 0) ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); if (ltemp < (long)numrows) @@ -461,24 +477,68 @@ alloc_sarray(j_common_ptr cinfo, int pool_id, JDIMENSION samplesperrow, rowsperchunk = numrows; mem->last_rowsperchunk = rowsperchunk; - /* Get space for row pointers (small object) */ - result = (JSAMPARRAY)alloc_small(cinfo, pool_id, - (size_t)(numrows * sizeof(JSAMPROW))); + if (data_precision == 16) { +#if defined(C_LOSSLESS_SUPPORTED) || defined(D_LOSSLESS_SUPPORTED) + /* Get space for row pointers (small object) */ + result16 = (J16SAMPARRAY)alloc_small(cinfo, pool_id, + (size_t)(numrows * + sizeof(J16SAMPROW))); + + /* Get the rows themselves (large objects) */ + currow = 0; + while (currow < numrows) { + rowsperchunk = MIN(rowsperchunk, numrows - currow); + workspace16 = (J16SAMPROW)alloc_large(cinfo, pool_id, + (size_t)((size_t)rowsperchunk * (size_t)samplesperrow * sample_size)); + for (i = rowsperchunk; i > 0; i--) { + result16[currow++] = workspace16; + workspace16 += samplesperrow; + } + } - /* Get the rows themselves (large objects) */ - currow = 0; - while (currow < numrows) { - rowsperchunk = MIN(rowsperchunk, numrows - currow); - workspace = (JSAMPROW)alloc_large(cinfo, pool_id, - (size_t)((size_t)rowsperchunk * (size_t)samplesperrow * - sizeof(JSAMPLE))); - for (i = rowsperchunk; i > 0; i--) { - result[currow++] = workspace; - workspace += samplesperrow; + return (JSAMPARRAY)result16; +#else + ERREXIT1(cinfo, JERR_BAD_PRECISION, data_precision); + return NULL; +#endif + } else if (data_precision == 12) { + /* Get space for row pointers (small object) */ + result12 = (J12SAMPARRAY)alloc_small(cinfo, pool_id, + (size_t)(numrows * + sizeof(J12SAMPROW))); + + /* Get the rows themselves (large objects) */ + currow = 0; + while (currow < numrows) { + rowsperchunk = MIN(rowsperchunk, numrows - currow); + workspace12 = (J12SAMPROW)alloc_large(cinfo, pool_id, + (size_t)((size_t)rowsperchunk * (size_t)samplesperrow * sample_size)); + for (i = rowsperchunk; i > 0; i--) { + result12[currow++] = workspace12; + workspace12 += samplesperrow; + } } - } - return result; + return (JSAMPARRAY)result12; + } else { + /* Get space for row pointers (small object) */ + result = (JSAMPARRAY)alloc_small(cinfo, pool_id, + (size_t)(numrows * sizeof(JSAMPROW))); + + /* Get the rows themselves (large objects) */ + currow = 0; + while (currow < numrows) { + rowsperchunk = MIN(rowsperchunk, numrows - currow); + workspace = (JSAMPROW)alloc_large(cinfo, pool_id, + (size_t)((size_t)rowsperchunk * (size_t)samplesperrow * sample_size)); + for (i = rowsperchunk; i > 0; i--) { + result[currow++] = workspace; + workspace += samplesperrow; + } + } + + return result; + } } @@ -640,6 +700,13 @@ realize_virt_arrays(j_common_ptr cinfo) size_t minheights, max_minheights; jvirt_sarray_ptr sptr; jvirt_barray_ptr bptr; + int data_precision = cinfo->is_decompressor ? + ((j_decompress_ptr)cinfo)->data_precision : + ((j_compress_ptr)cinfo)->data_precision; + size_t sample_size = data_precision == 16 ? + sizeof(J16SAMPLE) : (data_precision == 12 ? + sizeof(J12SAMPLE) : + sizeof(JSAMPLE)); /* Compute the minimum space needed (maxaccess rows in each buffer) * and the maximum space needed (full image height in each buffer). @@ -650,10 +717,10 @@ realize_virt_arrays(j_common_ptr cinfo) for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { if (sptr->mem_buffer == NULL) { /* if not realized yet */ size_t new_space = (long)sptr->rows_in_array * - (long)sptr->samplesperrow * sizeof(JSAMPLE); + (long)sptr->samplesperrow * sample_size; space_per_minheight += (long)sptr->maxaccess * - (long)sptr->samplesperrow * sizeof(JSAMPLE); + (long)sptr->samplesperrow * sample_size; if (SIZE_MAX - maximum_space < new_space) out_of_memory(cinfo, 10); maximum_space += new_space; @@ -708,7 +775,7 @@ realize_virt_arrays(j_common_ptr cinfo) jpeg_open_backing_store(cinfo, &sptr->b_s_info, (long)sptr->rows_in_array * (long)sptr->samplesperrow * - (long)sizeof(JSAMPLE)); + (long)sample_size); sptr->b_s_open = TRUE; } sptr->mem_buffer = alloc_sarray(cinfo, JPOOL_IMAGE, @@ -751,8 +818,15 @@ do_sarray_io(j_common_ptr cinfo, jvirt_sarray_ptr ptr, boolean writing) /* Do backing store read or write of a virtual sample array */ { long bytesperrow, file_offset, byte_count, rows, thisrow, i; - - bytesperrow = (long)ptr->samplesperrow * sizeof(JSAMPLE); + int data_precision = cinfo->is_decompressor ? + ((j_decompress_ptr)cinfo)->data_precision : + ((j_compress_ptr)cinfo)->data_precision; + size_t sample_size = data_precision == 16 ? + sizeof(J16SAMPLE) : (data_precision == 12 ? + sizeof(J12SAMPLE) : + sizeof(JSAMPLE)); + + bytesperrow = (long)ptr->samplesperrow * (long)sample_size; file_offset = ptr->cur_start_row * bytesperrow; /* Loop to read or write each allocation chunk in mem_buffer */ for (i = 0; i < (long)ptr->rows_in_mem; i += ptr->rowsperchunk) { @@ -766,14 +840,42 @@ do_sarray_io(j_common_ptr cinfo, jvirt_sarray_ptr ptr, boolean writing) if (rows <= 0) /* this chunk might be past end of file! */ break; byte_count = rows * bytesperrow; - if (writing) - (*ptr->b_s_info.write_backing_store) (cinfo, &ptr->b_s_info, - (void *)ptr->mem_buffer[i], - file_offset, byte_count); - else - (*ptr->b_s_info.read_backing_store) (cinfo, &ptr->b_s_info, - (void *)ptr->mem_buffer[i], - file_offset, byte_count); + if (data_precision == 16) { +#if defined(C_LOSSLESS_SUPPORTED) || defined(D_LOSSLESS_SUPPORTED) + J16SAMPARRAY mem_buffer16 = (J16SAMPARRAY)ptr->mem_buffer; + + if (writing) + (*ptr->b_s_info.write_backing_store) (cinfo, &ptr->b_s_info, + (void *)mem_buffer16[i], + file_offset, byte_count); + else + (*ptr->b_s_info.read_backing_store) (cinfo, &ptr->b_s_info, + (void *)mem_buffer16[i], + file_offset, byte_count); +#else + ERREXIT1(cinfo, JERR_BAD_PRECISION, data_precision); +#endif + } else if (data_precision == 12) { + J12SAMPARRAY mem_buffer12 = (J12SAMPARRAY)ptr->mem_buffer; + + if (writing) + (*ptr->b_s_info.write_backing_store) (cinfo, &ptr->b_s_info, + (void *)mem_buffer12[i], + file_offset, byte_count); + else + (*ptr->b_s_info.read_backing_store) (cinfo, &ptr->b_s_info, + (void *)mem_buffer12[i], + file_offset, byte_count); + } else { + if (writing) + (*ptr->b_s_info.write_backing_store) (cinfo, &ptr->b_s_info, + (void *)ptr->mem_buffer[i], + file_offset, byte_count); + else + (*ptr->b_s_info.read_backing_store) (cinfo, &ptr->b_s_info, + (void *)ptr->mem_buffer[i], + file_offset, byte_count); + } file_offset += byte_count; } } @@ -821,6 +923,13 @@ access_virt_sarray(j_common_ptr cinfo, jvirt_sarray_ptr ptr, { JDIMENSION end_row = start_row + num_rows; JDIMENSION undef_row; + int data_precision = cinfo->is_decompressor ? + ((j_decompress_ptr)cinfo)->data_precision : + ((j_compress_ptr)cinfo)->data_precision; + size_t sample_size = data_precision == 16 ? + sizeof(J16SAMPLE) : (data_precision == 12 ? + sizeof(J12SAMPLE) : + sizeof(JSAMPLE)); /* debugging check */ if (end_row > ptr->rows_in_array || num_rows > ptr->maxaccess || @@ -876,7 +985,7 @@ access_virt_sarray(j_common_ptr cinfo, jvirt_sarray_ptr ptr, if (writable) ptr->first_undef_row = end_row; if (ptr->pre_zero) { - size_t bytesperrow = (size_t)ptr->samplesperrow * sizeof(JSAMPLE); + size_t bytesperrow = (size_t)ptr->samplesperrow * sample_size; undef_row -= ptr->cur_start_row; /* make indexes relative to buffer */ end_row -= ptr->cur_start_row; while (undef_row < end_row) { diff --git a/jmorecfg.h b/jmorecfg.h index b33a991..89c7842 100644 --- a/jmorecfg.h +++ b/jmorecfg.h @@ -4,8 +4,10 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 1997-2009 by Guido Vollbeding. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: - * Copyright (C) 2009, 2011, 2014-2015, 2018, 2020, D. R. Commander. + * Copyright (C) 2009, 2011, 2014-2015, 2018, 2020, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -41,31 +43,29 @@ * arrays is very slow on your hardware, you might want to change these. */ -#if BITS_IN_JSAMPLE == 8 -/* JSAMPLE should be the smallest type that will hold the values 0..255. - */ +/* JSAMPLE should be the smallest type that will hold the values 0..255. */ typedef unsigned char JSAMPLE; #define GETJSAMPLE(value) ((int)(value)) -#define MAXJSAMPLE 255 -#define CENTERJSAMPLE 128 +#define MAXJSAMPLE 255 +#define CENTERJSAMPLE 128 -#endif /* BITS_IN_JSAMPLE == 8 */ +/* J12SAMPLE should be the smallest type that will hold the values 0..4095. */ -#if BITS_IN_JSAMPLE == 12 -/* JSAMPLE should be the smallest type that will hold the values 0..4095. - * On nearly all machines "short" will do nicely. - */ +typedef short J12SAMPLE; -typedef short JSAMPLE; -#define GETJSAMPLE(value) ((int)(value)) +#define MAXJ12SAMPLE 4095 +#define CENTERJ12SAMPLE 2048 + + +/* J16SAMPLE should be the smallest type that will hold the values 0..65535. */ -#define MAXJSAMPLE 4095 -#define CENTERJSAMPLE 2048 +typedef unsigned short J16SAMPLE; -#endif /* BITS_IN_JSAMPLE == 12 */ +#define MAXJ16SAMPLE 65535 +#define CENTERJ16SAMPLE 32768 /* Representation of a DCT frequency coefficient. @@ -242,14 +242,16 @@ typedef int boolean; #define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ #define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define C_LOSSLESS_SUPPORTED /* Lossless JPEG? */ #define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ /* Note: if you selected 12-bit data precision, it is dangerous to turn off * ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only good for 8-bit * precision, so jchuff.c normally uses entropy optimization to compute * usable tables for higher precision. If you don't want to do optimization, * you'll have to supply different default Huffman tables. - * The exact same statements apply for progressive JPEG: the default tables - * don't work for progressive mode. (This may get fixed, however.) + * The exact same statements apply for progressive and lossless JPEG: + * the default tables don't work for progressive mode or lossless mode. + * (This may get fixed, however.) */ #define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ @@ -257,6 +259,7 @@ typedef int boolean; #define D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ #define D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define D_LOSSLESS_SUPPORTED /* Lossless JPEG? */ #define SAVE_MARKERS_SUPPORTED /* jpeg_save_markers() needed? */ #define BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ #define IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? */ diff --git a/jpegcomp.h b/jpegapicomp.h similarity index 98% rename from jpegcomp.h rename to jpegapicomp.h index c4834ac..bb3912e 100644 --- a/jpegcomp.h +++ b/jpegapicomp.h @@ -1,5 +1,5 @@ /* - * jpegcomp.h + * jpegapicomp.h * * Copyright (C) 2010, 2020, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg diff --git a/jpegint.h b/jpegint.h index 6af9e2a..6541420 100644 --- a/jpegint.h +++ b/jpegint.h @@ -4,8 +4,10 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 1997-2009 by Guido Vollbeding. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: - * Copyright (C) 2015-2016, 2019, 2021, D. R. Commander. + * Copyright (C) 2015-2017, 2019, 2021-2022, D. R. Commander. * Copyright (C) 2015, Google, Inc. * Copyright (C) 2021, Alex Richardson. * For conditions of distribution and use, see the accompanying README.ijg @@ -17,6 +19,17 @@ */ +/* Representation of a spatial difference value. + * This should be a signed value of at least 16 bits; int is usually OK. + */ + +typedef int JDIFF; + +typedef JDIFF FAR *JDIFFROW; /* pointer to one row of difference values */ +typedef JDIFFROW *JDIFFARRAY; /* ptr to some rows (a 2-D diff array) */ +typedef JDIFFARRAY *JDIFFIMAGE; /* a 3-D diff array: top index is color */ + + /* Declarations for both compression & decompression */ typedef enum { /* Operating modes for buffer controllers */ @@ -61,6 +74,9 @@ typedef __UINTPTR_TYPE__ JUINTPTR; typedef size_t JUINTPTR; #endif +#define IsExtRGB(cs) \ + (cs == JCS_RGB || (cs >= JCS_EXT_RGB && cs <= JCS_EXT_ARGB)) + /* * Left shift macro that handles a negative operand without causing any * sanitizer warnings @@ -80,6 +96,7 @@ struct jpeg_comp_master { /* State variables made visible to other modules */ boolean call_pass_startup; /* True if pass_startup must be called */ boolean is_last_pass; /* True during last pass */ + boolean lossless; /* True if lossless mode is enabled */ }; /* Main buffer control (downsampled-data buffer) */ @@ -87,6 +104,12 @@ struct jpeg_c_main_controller { void (*start_pass) (j_compress_ptr cinfo, J_BUF_MODE pass_mode); void (*process_data) (j_compress_ptr cinfo, JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail); + void (*process_data_12) (j_compress_ptr cinfo, J12SAMPARRAY input_buf, + JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail); +#ifdef C_LOSSLESS_SUPPORTED + void (*process_data_16) (j_compress_ptr cinfo, J16SAMPARRAY input_buf, + JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail); +#endif }; /* Compression preprocessing (downsampling input buffer control) */ @@ -97,12 +120,32 @@ struct jpeg_c_prep_controller { JSAMPIMAGE output_buf, JDIMENSION *out_row_group_ctr, JDIMENSION out_row_groups_avail); + void (*pre_process_data_12) (j_compress_ptr cinfo, J12SAMPARRAY input_buf, + JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail, + J12SAMPIMAGE output_buf, + JDIMENSION *out_row_group_ctr, + JDIMENSION out_row_groups_avail); +#ifdef C_LOSSLESS_SUPPORTED + void (*pre_process_data_16) (j_compress_ptr cinfo, J16SAMPARRAY input_buf, + JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail, + J16SAMPIMAGE output_buf, + JDIMENSION *out_row_group_ctr, + JDIMENSION out_row_groups_avail); +#endif }; -/* Coefficient buffer control */ +/* Lossy mode: Coefficient buffer control + * Lossless mode: Difference buffer control + */ struct jpeg_c_coef_controller { void (*start_pass) (j_compress_ptr cinfo, J_BUF_MODE pass_mode); boolean (*compress_data) (j_compress_ptr cinfo, JSAMPIMAGE input_buf); + boolean (*compress_data_12) (j_compress_ptr cinfo, J12SAMPIMAGE input_buf); +#ifdef C_LOSSLESS_SUPPORTED + boolean (*compress_data_16) (j_compress_ptr cinfo, J16SAMPIMAGE input_buf); +#endif }; /* Colorspace conversion */ @@ -111,6 +154,14 @@ struct jpeg_color_converter { void (*color_convert) (j_compress_ptr cinfo, JSAMPARRAY input_buf, JSAMPIMAGE output_buf, JDIMENSION output_row, int num_rows); + void (*color_convert_12) (j_compress_ptr cinfo, J12SAMPARRAY input_buf, + J12SAMPIMAGE output_buf, JDIMENSION output_row, + int num_rows); +#ifdef C_LOSSLESS_SUPPORTED + void (*color_convert_16) (j_compress_ptr cinfo, J16SAMPARRAY input_buf, + J16SAMPIMAGE output_buf, JDIMENSION output_row, + int num_rows); +#endif }; /* Downsampling */ @@ -119,24 +170,47 @@ struct jpeg_downsampler { void (*downsample) (j_compress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION in_row_index, JSAMPIMAGE output_buf, JDIMENSION out_row_group_index); + void (*downsample_12) (j_compress_ptr cinfo, J12SAMPIMAGE input_buf, + JDIMENSION in_row_index, J12SAMPIMAGE output_buf, + JDIMENSION out_row_group_index); +#ifdef C_LOSSLESS_SUPPORTED + void (*downsample_16) (j_compress_ptr cinfo, J16SAMPIMAGE input_buf, + JDIMENSION in_row_index, J16SAMPIMAGE output_buf, + JDIMENSION out_row_group_index); +#endif boolean need_context_rows; /* TRUE if need rows above & below */ }; -/* Forward DCT (also controls coefficient quantization) */ +/* Lossy mode: Forward DCT (also controls coefficient quantization) + * Lossless mode: Prediction, sample differencing, and point transform + */ struct jpeg_forward_dct { void (*start_pass) (j_compress_ptr cinfo); + + /* Lossy mode */ /* perhaps this should be an array??? */ void (*forward_DCT) (j_compress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY sample_data, JBLOCKROW coef_blocks, JDIMENSION start_row, JDIMENSION start_col, JDIMENSION num_blocks); + void (*forward_DCT_12) (j_compress_ptr cinfo, jpeg_component_info *compptr, + J12SAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks); }; /* Entropy encoding */ struct jpeg_entropy_encoder { void (*start_pass) (j_compress_ptr cinfo, boolean gather_statistics); + + /* Lossy mode */ boolean (*encode_mcu) (j_compress_ptr cinfo, JBLOCKROW *MCU_data); + /* Lossless mode */ + JDIMENSION (*encode_mcus) (j_compress_ptr cinfo, JDIFFIMAGE diff_buf, + JDIMENSION MCU_row_num, JDIMENSION MCU_col_num, + JDIMENSION nMCU); + void (*finish_pass) (j_compress_ptr cinfo); }; @@ -164,6 +238,7 @@ struct jpeg_decomp_master { /* State variables made visible to other modules */ boolean is_dummy_pass; /* True during 1st pass for 2-pass quant */ + boolean lossless; /* True if decompressing a lossless image */ /* Partial decompression variables */ JDIMENSION first_iMCU_col; @@ -193,14 +268,36 @@ struct jpeg_d_main_controller { void (*start_pass) (j_decompress_ptr cinfo, J_BUF_MODE pass_mode); void (*process_data) (j_decompress_ptr cinfo, JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail); + void (*process_data_12) (j_decompress_ptr cinfo, J12SAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail); +#ifdef D_LOSSLESS_SUPPORTED + void (*process_data_16) (j_decompress_ptr cinfo, J16SAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail); +#endif }; -/* Coefficient buffer control */ +/* Lossy mode: Coefficient buffer control + * Lossless mode: Difference buffer control + */ struct jpeg_d_coef_controller { void (*start_input_pass) (j_decompress_ptr cinfo); int (*consume_data) (j_decompress_ptr cinfo); void (*start_output_pass) (j_decompress_ptr cinfo); int (*decompress_data) (j_decompress_ptr cinfo, JSAMPIMAGE output_buf); + int (*decompress_data_12) (j_decompress_ptr cinfo, J12SAMPIMAGE output_buf); +#ifdef D_LOSSLESS_SUPPORTED + int (*decompress_data_16) (j_decompress_ptr cinfo, J16SAMPIMAGE output_buf); +#endif + + /* These variables keep track of the current location of the input side. */ + /* cinfo->input_iMCU_row is also used for this. */ + JDIMENSION MCU_ctr; /* counts MCUs processed in current row */ + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + /* The output side's location is represented by cinfo->output_iMCU_row. */ + + /* Lossy mode */ /* Pointer to array of coefficient virtual arrays, or NULL if none */ jvirt_barray_ptr *coef_arrays; }; @@ -213,6 +310,20 @@ struct jpeg_d_post_controller { JDIMENSION in_row_groups_avail, JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail); + void (*post_process_data_12) (j_decompress_ptr cinfo, J12SAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + J12SAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail); +#ifdef D_LOSSLESS_SUPPORTED + void (*post_process_data_16) (j_decompress_ptr cinfo, J16SAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + J16SAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail); +#endif }; /* Marker reading & parsing */ @@ -238,24 +349,42 @@ struct jpeg_marker_reader { /* Entropy decoding */ struct jpeg_entropy_decoder { void (*start_pass) (j_decompress_ptr cinfo); + + /* Lossy mode */ boolean (*decode_mcu) (j_decompress_ptr cinfo, JBLOCKROW *MCU_data); + /* Lossless mode */ + JDIMENSION (*decode_mcus) (j_decompress_ptr cinfo, JDIFFIMAGE diff_buf, + JDIMENSION MCU_row_num, JDIMENSION MCU_col_num, + JDIMENSION nMCU); + boolean (*process_restart) (j_decompress_ptr cinfo); /* This is here to share code between baseline and progressive decoders; */ /* other modules probably should not use it */ boolean insufficient_data; /* set TRUE after emitting warning */ }; -/* Inverse DCT (also performs dequantization) */ +/* Lossy mode: Inverse DCT (also performs dequantization) + * Lossless mode: Prediction, sample undifferencing, point transform, and + * sample size scaling + */ typedef void (*inverse_DCT_method_ptr) (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); +typedef void (*inverse_DCT_12_method_ptr) (j_decompress_ptr cinfo, + jpeg_component_info *compptr, + JCOEFPTR coef_block, + J12SAMPARRAY output_buf, + JDIMENSION output_col); struct jpeg_inverse_dct { void (*start_pass) (j_decompress_ptr cinfo); + + /* Lossy mode */ /* It is useful to allow each component to have a separate IDCT method. */ inverse_DCT_method_ptr inverse_DCT[MAX_COMPONENTS]; + inverse_DCT_12_method_ptr inverse_DCT_12[MAX_COMPONENTS]; }; /* Upsampling (note that upsampler must also call color converter) */ @@ -265,6 +394,16 @@ struct jpeg_upsampler { JDIMENSION *in_row_group_ctr, JDIMENSION in_row_groups_avail, JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail); + void (*upsample_12) (j_decompress_ptr cinfo, J12SAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, J12SAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail); +#ifdef D_LOSSLESS_SUPPORTED + void (*upsample_16) (j_decompress_ptr cinfo, J16SAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, J16SAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail); +#endif boolean need_context_rows; /* TRUE if need rows above & below */ }; @@ -275,6 +414,14 @@ struct jpeg_color_deconverter { void (*color_convert) (j_decompress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows); + void (*color_convert_12) (j_decompress_ptr cinfo, J12SAMPIMAGE input_buf, + JDIMENSION input_row, J12SAMPARRAY output_buf, + int num_rows); +#ifdef D_LOSSLESS_SUPPORTED + void (*color_convert_16) (j_decompress_ptr cinfo, J16SAMPIMAGE input_buf, + JDIMENSION input_row, J16SAMPARRAY output_buf, + int num_rows); +#endif }; /* Color quantization or color precision reduction */ @@ -282,6 +429,8 @@ struct jpeg_color_quantizer { void (*start_pass) (j_decompress_ptr cinfo, boolean is_pre_scan); void (*color_quantize) (j_decompress_ptr cinfo, JSAMPARRAY input_buf, JSAMPARRAY output_buf, int num_rows); + void (*color_quantize_12) (j_decompress_ptr cinfo, J12SAMPARRAY input_buf, + J12SAMPARRAY output_buf, int num_rows); void (*finish_pass) (j_decompress_ptr cinfo); void (*new_color_map) (j_decompress_ptr cinfo); }; @@ -323,36 +472,95 @@ EXTERN(void) jinit_c_master_control(j_compress_ptr cinfo, boolean transcode_only); EXTERN(void) jinit_c_main_controller(j_compress_ptr cinfo, boolean need_full_buffer); +EXTERN(void) j12init_c_main_controller(j_compress_ptr cinfo, + boolean need_full_buffer); EXTERN(void) jinit_c_prep_controller(j_compress_ptr cinfo, boolean need_full_buffer); +EXTERN(void) j12init_c_prep_controller(j_compress_ptr cinfo, + boolean need_full_buffer); EXTERN(void) jinit_c_coef_controller(j_compress_ptr cinfo, boolean need_full_buffer); +EXTERN(void) j12init_c_coef_controller(j_compress_ptr cinfo, + boolean need_full_buffer); EXTERN(void) jinit_color_converter(j_compress_ptr cinfo); +EXTERN(void) j12init_color_converter(j_compress_ptr cinfo); EXTERN(void) jinit_downsampler(j_compress_ptr cinfo); +EXTERN(void) j12init_downsampler(j_compress_ptr cinfo); EXTERN(void) jinit_forward_dct(j_compress_ptr cinfo); +EXTERN(void) j12init_forward_dct(j_compress_ptr cinfo); EXTERN(void) jinit_huff_encoder(j_compress_ptr cinfo); EXTERN(void) jinit_phuff_encoder(j_compress_ptr cinfo); EXTERN(void) jinit_arith_encoder(j_compress_ptr cinfo); EXTERN(void) jinit_marker_writer(j_compress_ptr cinfo); +#ifdef C_LOSSLESS_SUPPORTED +EXTERN(void) j16init_c_main_controller(j_compress_ptr cinfo, + boolean need_full_buffer); +EXTERN(void) j16init_c_prep_controller(j_compress_ptr cinfo, + boolean need_full_buffer); +EXTERN(void) j16init_color_converter(j_compress_ptr cinfo); +EXTERN(void) j16init_downsampler(j_compress_ptr cinfo); +EXTERN(void) jinit_c_diff_controller(j_compress_ptr cinfo, + boolean need_full_buffer); +EXTERN(void) j12init_c_diff_controller(j_compress_ptr cinfo, + boolean need_full_buffer); +EXTERN(void) j16init_c_diff_controller(j_compress_ptr cinfo, + boolean need_full_buffer); +EXTERN(void) jinit_lhuff_encoder(j_compress_ptr cinfo); +EXTERN(void) jinit_lossless_compressor(j_compress_ptr cinfo); +EXTERN(void) j12init_lossless_compressor(j_compress_ptr cinfo); +EXTERN(void) j16init_lossless_compressor(j_compress_ptr cinfo); +#endif + /* Decompression module initialization routines */ EXTERN(void) jinit_master_decompress(j_decompress_ptr cinfo); EXTERN(void) jinit_d_main_controller(j_decompress_ptr cinfo, boolean need_full_buffer); +EXTERN(void) j12init_d_main_controller(j_decompress_ptr cinfo, + boolean need_full_buffer); EXTERN(void) jinit_d_coef_controller(j_decompress_ptr cinfo, boolean need_full_buffer); +EXTERN(void) j12init_d_coef_controller(j_decompress_ptr cinfo, + boolean need_full_buffer); EXTERN(void) jinit_d_post_controller(j_decompress_ptr cinfo, boolean need_full_buffer); +EXTERN(void) j12init_d_post_controller(j_decompress_ptr cinfo, + boolean need_full_buffer); EXTERN(void) jinit_input_controller(j_decompress_ptr cinfo); EXTERN(void) jinit_marker_reader(j_decompress_ptr cinfo); EXTERN(void) jinit_huff_decoder(j_decompress_ptr cinfo); EXTERN(void) jinit_phuff_decoder(j_decompress_ptr cinfo); EXTERN(void) jinit_arith_decoder(j_decompress_ptr cinfo); EXTERN(void) jinit_inverse_dct(j_decompress_ptr cinfo); +EXTERN(void) j12init_inverse_dct(j_decompress_ptr cinfo); EXTERN(void) jinit_upsampler(j_decompress_ptr cinfo); +EXTERN(void) j12init_upsampler(j_decompress_ptr cinfo); EXTERN(void) jinit_color_deconverter(j_decompress_ptr cinfo); +EXTERN(void) j12init_color_deconverter(j_decompress_ptr cinfo); EXTERN(void) jinit_1pass_quantizer(j_decompress_ptr cinfo); +EXTERN(void) j12init_1pass_quantizer(j_decompress_ptr cinfo); EXTERN(void) jinit_2pass_quantizer(j_decompress_ptr cinfo); +EXTERN(void) j12init_2pass_quantizer(j_decompress_ptr cinfo); EXTERN(void) jinit_merged_upsampler(j_decompress_ptr cinfo); +EXTERN(void) j12init_merged_upsampler(j_decompress_ptr cinfo); +#ifdef D_LOSSLESS_SUPPORTED +EXTERN(void) j16init_d_main_controller(j_decompress_ptr cinfo, + boolean need_full_buffer); +EXTERN(void) j16init_d_post_controller(j_decompress_ptr cinfo, + boolean need_full_buffer); +EXTERN(void) j16init_upsampler(j_decompress_ptr cinfo); +EXTERN(void) j16init_color_deconverter(j_decompress_ptr cinfo); +EXTERN(void) jinit_d_diff_controller(j_decompress_ptr cinfo, + boolean need_full_buffer); +EXTERN(void) j12init_d_diff_controller(j_decompress_ptr cinfo, + boolean need_full_buffer); +EXTERN(void) j16init_d_diff_controller(j_decompress_ptr cinfo, + boolean need_full_buffer); +EXTERN(void) jinit_lhuff_decoder(j_decompress_ptr cinfo); +EXTERN(void) jinit_lossless_decompressor(j_decompress_ptr cinfo); +EXTERN(void) j12init_lossless_decompressor(j_decompress_ptr cinfo); +EXTERN(void) j16init_lossless_decompressor(j_decompress_ptr cinfo); +#endif + /* Memory manager initialization */ EXTERN(void) jinit_memory_mgr(j_common_ptr cinfo); @@ -362,6 +570,14 @@ EXTERN(long) jround_up(long a, long b); EXTERN(void) jcopy_sample_rows(JSAMPARRAY input_array, int source_row, JSAMPARRAY output_array, int dest_row, int num_rows, JDIMENSION num_cols); +EXTERN(void) j12copy_sample_rows(J12SAMPARRAY input_array, int source_row, + J12SAMPARRAY output_array, int dest_row, + int num_rows, JDIMENSION num_cols); +#if defined(C_LOSSLESS_SUPPORTED) || defined(D_LOSSLESS_SUPPORTED) +EXTERN(void) j16copy_sample_rows(J16SAMPARRAY input_array, int source_row, + J16SAMPARRAY output_array, int dest_row, + int num_rows, JDIMENSION num_cols); +#endif EXTERN(void) jcopy_block_row(JBLOCKROW input_row, JBLOCKROW output_row, JDIMENSION num_blocks); EXTERN(void) jzero_far(void *target, size_t bytestozero); diff --git a/jpeglib.h b/jpeglib.h index d7664f0..a59e98c 100644 --- a/jpeglib.h +++ b/jpeglib.h @@ -4,8 +4,11 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1998, Thomas G. Lane. * Modified 2002-2009 by Guido Vollbeding. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: - * Copyright (C) 2009-2011, 2013-2014, 2016-2017, 2020, D. R. Commander. + * Copyright (C) 2009-2011, 2013-2014, 2016-2017, 2020, 2022-2023, + D. R. Commander. * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -43,6 +46,13 @@ extern "C" { * if you want to be compatible. */ +/* NOTE: In lossless mode, an MCU contains one or more samples rather than one + * or more 8x8 DCT blocks, so the term "data unit" is used to generically + * describe a sample in lossless mode or an 8x8 DCT block in lossy mode. To + * preserve backward API/ABI compatibility, the field and macro names retain + * the "block" terminology. + */ + #define DCTSIZE 8 /* The basic DCT block is 8x8 samples */ #define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ #define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ @@ -57,9 +67,9 @@ extern "C" { * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe * sometimes emits noncompliant files doesn't mean you should too. */ -#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on data units/MCU */ #ifndef D_MAX_BLOCKS_IN_MCU -#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on data units/MCU */ #endif @@ -70,6 +80,20 @@ typedef JSAMPLE *JSAMPROW; /* ptr to one image row of pixel samples. */ typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ +typedef J12SAMPLE *J12SAMPROW; /* ptr to one image row of 12-bit pixel + samples. */ +typedef J12SAMPROW *J12SAMPARRAY; /* ptr to some 12-bit sample rows (a 2-D + 12-bit sample array) */ +typedef J12SAMPARRAY *J12SAMPIMAGE; /* a 3-D 12-bit sample array: top index is + color */ + +typedef J16SAMPLE *J16SAMPROW; /* ptr to one image row of 16-bit pixel + samples. */ +typedef J16SAMPROW *J16SAMPARRAY; /* ptr to some 16-bit sample rows (a 2-D + 16-bit sample array) */ +typedef J16SAMPARRAY *J16SAMPIMAGE; /* a 3-D 16-bit sample array: top index is + color */ + typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ typedef JBLOCK *JBLOCKROW; /* pointer to one row of coefficient blocks */ typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ @@ -135,17 +159,20 @@ typedef struct { /* Remaining fields should be treated as private by applications. */ /* These values are computed during compression or decompression startup: */ - /* Component's size in DCT blocks. - * Any dummy blocks added to complete an MCU are not counted; therefore - * these values do not depend on whether a scan is interleaved or not. + /* Component's size in data units. + * In lossy mode, any dummy blocks added to complete an MCU are not counted; + * therefore these values do not depend on whether a scan is interleaved or + * not. In lossless mode, these are always equal to the image width and + * height. */ JDIMENSION width_in_blocks; JDIMENSION height_in_blocks; - /* Size of a DCT block in samples. Always DCTSIZE for compression. - * For decompression this is the size of the output from one DCT block, + /* Size of a data unit in samples. Always DCTSIZE for lossy compression. + * For lossy decompression this is the size of the output from one DCT block, * reflecting any scaling we choose to apply during the IDCT step. - * Values from 1 to 16 are supported. - * Note that different components may receive different IDCT scalings. + * Values from 1 to 16 are supported. Note that different components may + * receive different IDCT scalings. In lossless mode, this is always equal + * to 1. */ #if JPEG_LIB_VERSION >= 70 int DCT_h_scaled_size; @@ -156,8 +183,10 @@ typedef struct { /* The downsampled dimensions are the component's actual, unpadded number * of samples at the main buffer (preprocessing/compression interface), thus * downsampled_width = ceil(image_width * Hi/Hmax) - * and similarly for height. For decompression, IDCT scaling is included, so + * and similarly for height. For lossy decompression, IDCT scaling is + * included, so * downsampled_width = ceil(image_width * Hi/Hmax * DCT_[h_]scaled_size/DCTSIZE) + * In lossless mode, these are always equal to the image width and height. */ JDIMENSION downsampled_width; /* actual width in samples */ JDIMENSION downsampled_height; /* actual height in samples */ @@ -169,12 +198,12 @@ typedef struct { /* These values are computed before starting a scan of the component. */ /* The decompressor output side may not use these variables. */ - int MCU_width; /* number of blocks per MCU, horizontally */ - int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_width; /* number of data units per MCU, horizontally */ + int MCU_height; /* number of data units per MCU, vertically */ int MCU_blocks; /* MCU_width * MCU_height */ int MCU_sample_width; /* MCU width in samples, MCU_width*DCT_[h_]scaled_size */ - int last_col_width; /* # of non-dummy blocks across in last MCU */ - int last_row_height; /* # of non-dummy blocks down in last MCU */ + int last_col_width; /* # of non-dummy data units across in last MCU */ + int last_row_height; /* # of non-dummy data units down in last MCU */ /* Saved quantization table for component; NULL if none yet saved. * See jdinput.c comments about the need for this information. @@ -192,8 +221,12 @@ typedef struct { typedef struct { int comps_in_scan; /* number of components encoded in this scan */ int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ - int Ss, Se; /* progressive JPEG spectral selection parms */ - int Ah, Al; /* progressive JPEG successive approx. parms */ + int Ss, Se; /* progressive JPEG spectral selection parms + (Ss is the predictor selection value in + lossless mode) */ + int Ah, Al; /* progressive JPEG successive approx. parms + (Al is the point transform value in lossless + mode) */ } jpeg_scan_info; /* The decompressor can save APPn and COM markers in a list of these: */ @@ -238,7 +271,8 @@ typedef enum { JCS_EXT_BGRA, /* blue/green/red/alpha */ JCS_EXT_ABGR, /* alpha/blue/green/red */ JCS_EXT_ARGB, /* alpha/red/green/blue */ - JCS_RGB565 /* 5-bit red/6-bit green/5-bit blue */ + JCS_RGB565 /* 5-bit red/6-bit green/5-bit blue + [decompression only] */ } J_COLOR_SPACE; /* DCT/IDCT algorithm options. */ @@ -419,11 +453,13 @@ struct jpeg_compress_struct { int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ #endif - JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ - /* The coefficient controller receives data in units of MCU rows as defined - * for fully interleaved scans (whether the JPEG file is interleaved or not). - * There are v_samp_factor * DCTSIZE sample rows of each component in an - * "iMCU" (interleaved MCU) row. + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coefficient or + difference controller */ + /* The coefficient or difference controller receives data in units of MCU + * rows as defined for fully interleaved scans (whether the JPEG file is + * interleaved or not). In lossy mode, there are v_samp_factor * DCTSIZE + * sample rows of each component in an "iMCU" (interleaved MCU) row. In + * lossless mode, total_iMCU_rows is always equal to the image height. */ /* @@ -437,12 +473,13 @@ struct jpeg_compress_struct { JDIMENSION MCUs_per_row; /* # of MCUs across the image */ JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ - int blocks_in_MCU; /* # of DCT blocks per MCU */ + int blocks_in_MCU; /* # of data units per MCU */ int MCU_membership[C_MAX_BLOCKS_IN_MCU]; /* MCU_membership[i] is index in cur_comp_info of component owning */ - /* i'th block in an MCU */ + /* i'th data unit in an MCU */ - int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + int Ss, Se, Ah, Al; /* progressive/lossless JPEG parameters for + scan */ #if JPEG_LIB_VERSION >= 80 int block_size; /* the basic DCT block size: 1..16 */ @@ -537,7 +574,12 @@ struct jpeg_decompress_struct { * The map has out_color_components rows and actual_number_of_colors columns. */ int actual_number_of_colors; /* number of entries in use */ - JSAMPARRAY colormap; /* The color map as a 2-D pixel array */ + JSAMPARRAY colormap; /* The color map as a 2-D pixel array + If data_precision is 12 or 16, then this is + actually a J12SAMPARRAY or a J16SAMPARRAY, + so callers must type-cast it in order to + read/write 12-bit or 16-bit samples from/to + the array. */ /* State variables: these variables indicate the progress of decompression. * The application may examine these but must not modify them. @@ -647,15 +689,21 @@ struct jpeg_decompress_struct { #endif JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ - /* The coefficient controller's input and output progress is measured in - * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows - * in fully interleaved JPEG scans, but are used whether the scan is - * interleaved or not. We define an iMCU row as v_samp_factor DCT block - * rows of each component. Therefore, the IDCT output contains + /* The coefficient or difference controller's input and output progress is + * measured in units of "iMCU" (interleaved MCU) rows. These are the same as + * MCU rows in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. In lossy mode, we define an iMCU row as v_samp_factor + * DCT block rows of each component. Therefore, the IDCT output contains * v_samp_factor*DCT_[v_]scaled_size sample rows of a component per iMCU row. + * In lossless mode, total_iMCU_rows is always equal to the image height. */ - JSAMPLE *sample_range_limit; /* table for fast range-limiting */ + JSAMPLE *sample_range_limit; /* table for fast range-limiting + If data_precision is 12 or 16, then this is + actually a J12SAMPLE pointer or a J16SAMPLE + pointer, so callers must type-cast it in + order to read 12-bit or 16-bit samples from + the array. */ /* * These fields are valid during any one scan. @@ -669,12 +717,13 @@ struct jpeg_decompress_struct { JDIMENSION MCUs_per_row; /* # of MCUs across the image */ JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ - int blocks_in_MCU; /* # of DCT blocks per MCU */ + int blocks_in_MCU; /* # of data units per MCU */ int MCU_membership[D_MAX_BLOCKS_IN_MCU]; /* MCU_membership[i] is index in cur_comp_info of component owning */ - /* i'th block in an MCU */ + /* i'th data unit in an MCU */ - int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + int Ss, Se, Ah, Al; /* progressive/lossless JPEG parameters for + scan */ #if JPEG_LIB_VERSION >= 80 /* These fields are derived from Se of first SOS marker. @@ -835,6 +884,11 @@ struct jpeg_memory_mgr { void *(*alloc_small) (j_common_ptr cinfo, int pool_id, size_t sizeofobject); void *(*alloc_large) (j_common_ptr cinfo, int pool_id, size_t sizeofobject); + /* If cinfo->data_precision is 12 or 16, then this method and the + * access_virt_sarray method actually return a J12SAMPARRAY or a + * J16SAMPARRAY, so callers must type-cast the return value in order to + * read/write 12-bit or 16-bit samples from/to the array. + */ JSAMPARRAY (*alloc_sarray) (j_common_ptr cinfo, int pool_id, JDIMENSION samplesperrow, JDIMENSION numrows); JBLOCKARRAY (*alloc_barray) (j_common_ptr cinfo, int pool_id, @@ -916,13 +970,11 @@ EXTERN(void) jpeg_destroy_decompress(j_decompress_ptr cinfo); EXTERN(void) jpeg_stdio_dest(j_compress_ptr cinfo, FILE *outfile); EXTERN(void) jpeg_stdio_src(j_decompress_ptr cinfo, FILE *infile); -#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) /* Data source and destination managers: memory buffers. */ EXTERN(void) jpeg_mem_dest(j_compress_ptr cinfo, unsigned char **outbuffer, unsigned long *outsize); EXTERN(void) jpeg_mem_src(j_decompress_ptr cinfo, const unsigned char *inbuffer, unsigned long insize); -#endif /* Default parameter setup for compression */ EXTERN(void) jpeg_set_defaults(j_compress_ptr cinfo); @@ -942,6 +994,9 @@ EXTERN(void) jpeg_add_quant_table(j_compress_ptr cinfo, int which_tbl, const unsigned int *basic_table, int scale_factor, boolean force_baseline); EXTERN(int) jpeg_quality_scaling(int quality); +EXTERN(void) jpeg_enable_lossless(j_compress_ptr cinfo, + int predictor_selection_value, + int point_transform); EXTERN(void) jpeg_simple_progression(j_compress_ptr cinfo); EXTERN(void) jpeg_suppress_tables(j_compress_ptr cinfo, boolean suppress); EXTERN(JQUANT_TBL *) jpeg_alloc_quant_table(j_common_ptr cinfo); @@ -953,6 +1008,12 @@ EXTERN(void) jpeg_start_compress(j_compress_ptr cinfo, EXTERN(JDIMENSION) jpeg_write_scanlines(j_compress_ptr cinfo, JSAMPARRAY scanlines, JDIMENSION num_lines); +EXTERN(JDIMENSION) jpeg12_write_scanlines(j_compress_ptr cinfo, + J12SAMPARRAY scanlines, + JDIMENSION num_lines); +EXTERN(JDIMENSION) jpeg16_write_scanlines(j_compress_ptr cinfo, + J16SAMPARRAY scanlines, + JDIMENSION num_lines); EXTERN(void) jpeg_finish_compress(j_compress_ptr cinfo); #if JPEG_LIB_VERSION >= 70 @@ -963,6 +1024,9 @@ EXTERN(void) jpeg_calc_jpeg_dimensions(j_compress_ptr cinfo); /* Replaces jpeg_write_scanlines when writing raw downsampled data. */ EXTERN(JDIMENSION) jpeg_write_raw_data(j_compress_ptr cinfo, JSAMPIMAGE data, JDIMENSION num_lines); +EXTERN(JDIMENSION) jpeg12_write_raw_data(j_compress_ptr cinfo, + J12SAMPIMAGE data, + JDIMENSION num_lines); /* Write a special marker. See libjpeg.txt concerning safe usage. */ EXTERN(void) jpeg_write_marker(j_compress_ptr cinfo, int marker, @@ -998,15 +1062,28 @@ EXTERN(boolean) jpeg_start_decompress(j_decompress_ptr cinfo); EXTERN(JDIMENSION) jpeg_read_scanlines(j_decompress_ptr cinfo, JSAMPARRAY scanlines, JDIMENSION max_lines); +EXTERN(JDIMENSION) jpeg12_read_scanlines(j_decompress_ptr cinfo, + J12SAMPARRAY scanlines, + JDIMENSION max_lines); +EXTERN(JDIMENSION) jpeg16_read_scanlines(j_decompress_ptr cinfo, + J16SAMPARRAY scanlines, + JDIMENSION max_lines); EXTERN(JDIMENSION) jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines); +EXTERN(JDIMENSION) jpeg12_skip_scanlines(j_decompress_ptr cinfo, + JDIMENSION num_lines); EXTERN(void) jpeg_crop_scanline(j_decompress_ptr cinfo, JDIMENSION *xoffset, JDIMENSION *width); +EXTERN(void) jpeg12_crop_scanline(j_decompress_ptr cinfo, JDIMENSION *xoffset, + JDIMENSION *width); EXTERN(boolean) jpeg_finish_decompress(j_decompress_ptr cinfo); /* Replaces jpeg_read_scanlines when reading raw downsampled data. */ EXTERN(JDIMENSION) jpeg_read_raw_data(j_decompress_ptr cinfo, JSAMPIMAGE data, JDIMENSION max_lines); +EXTERN(JDIMENSION) jpeg12_read_raw_data(j_decompress_ptr cinfo, + J12SAMPIMAGE data, + JDIMENSION max_lines); /* Additional entry points for buffered-image mode. */ EXTERN(boolean) jpeg_has_multiple_scans(j_decompress_ptr cinfo); diff --git a/jquant1.c b/jquant1.c index 73b83e1..2e914b9 100644 --- a/jquant1.c +++ b/jquant1.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2009, 2015, D. R. Commander. + * Copyright (C) 2009, 2015, 2022-2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -16,8 +16,9 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" +#include "jsamplecomp.h" -#ifdef QUANT_1PASS_SUPPORTED +#if defined(QUANT_1PASS_SUPPORTED) && BITS_IN_JSAMPLE != 16 /* @@ -66,7 +67,7 @@ * worse, since the dither may be too much or too little at a given point. * * The normal calculation would be to form pixel value + dither, range-limit - * this to 0..MAXJSAMPLE, and then index into the colorindex table as usual. + * this to 0.._MAXJSAMPLE, and then index into the colorindex table as usual. * We can skip the separate range-limiting step by extending the colorindex * table in both directions. */ @@ -144,13 +145,13 @@ typedef struct { struct jpeg_color_quantizer pub; /* public fields */ /* Initially allocated colormap is saved here */ - JSAMPARRAY sv_colormap; /* The color map as a 2-D pixel array */ + _JSAMPARRAY sv_colormap; /* The color map as a 2-D pixel array */ int sv_actual; /* number of entries in use */ - JSAMPARRAY colorindex; /* Precomputed mapping for speed */ + _JSAMPARRAY colorindex; /* Precomputed mapping for speed */ /* colorindex[i][j] = index of color closest to pixel value j in component i, * premultiplied as described above. Since colormap indexes must fit into - * JSAMPLEs, the entries of this array will too. + * _JSAMPLEs, the entries of this array will too. */ boolean is_padded; /* is the colorindex padded for odither? */ @@ -248,24 +249,24 @@ select_ncolors(j_decompress_ptr cinfo, int Ncolors[]) LOCAL(int) output_value(j_decompress_ptr cinfo, int ci, int j, int maxj) /* Return j'th output value, where j will range from 0 to maxj */ -/* The output values must fall in 0..MAXJSAMPLE in increasing order */ +/* The output values must fall in 0.._MAXJSAMPLE in increasing order */ { - /* We always provide values 0 and MAXJSAMPLE for each component; + /* We always provide values 0 and _MAXJSAMPLE for each component; * any additional values are equally spaced between these limits. * (Forcing the upper and lower values to the limits ensures that * dithering can't produce a color outside the selected gamut.) */ - return (int)(((JLONG)j * MAXJSAMPLE + maxj / 2) / maxj); + return (int)(((JLONG)j * _MAXJSAMPLE + maxj / 2) / maxj); } LOCAL(int) largest_input_value(j_decompress_ptr cinfo, int ci, int j, int maxj) /* Return largest input value that should map to j'th output value */ -/* Must have largest(j=0) >= 0, and largest(j=maxj) >= MAXJSAMPLE */ +/* Must have largest(j=0) >= 0, and largest(j=maxj) >= _MAXJSAMPLE */ { /* Breakpoints are halfway between values returned by output_value */ - return (int)(((JLONG)(2 * j + 1) * MAXJSAMPLE + maxj) / (2 * maxj)); + return (int)(((JLONG)(2 * j + 1) * _MAXJSAMPLE + maxj) / (2 * maxj)); } @@ -277,7 +278,7 @@ LOCAL(void) create_colormap(j_decompress_ptr cinfo) { my_cquantize_ptr cquantize = (my_cquantize_ptr)cinfo->cquantize; - JSAMPARRAY colormap; /* Created colormap */ + _JSAMPARRAY colormap; /* Created colormap */ int total_colors; /* Number of distinct output colors */ int i, j, k, nci, blksize, blkdist, ptr, val; @@ -296,7 +297,7 @@ create_colormap(j_decompress_ptr cinfo) /* The colors are ordered in the map in standard row-major order, */ /* i.e. rightmost (highest-indexed) color changes most rapidly. */ - colormap = (*cinfo->mem->alloc_sarray) + colormap = (_JSAMPARRAY)(*cinfo->mem->alloc_sarray) ((j_common_ptr)cinfo, JPOOL_IMAGE, (JDIMENSION)total_colors, (JDIMENSION)cinfo->out_color_components); @@ -315,7 +316,7 @@ create_colormap(j_decompress_ptr cinfo) for (ptr = j * blksize; ptr < total_colors; ptr += blkdist) { /* fill in blksize entries beginning at ptr */ for (k = 0; k < blksize; k++) - colormap[i][ptr + k] = (JSAMPLE)val; + colormap[i][ptr + k] = (_JSAMPLE)val; } } blkdist = blksize; /* blksize of this color is blkdist of next */ @@ -337,25 +338,25 @@ LOCAL(void) create_colorindex(j_decompress_ptr cinfo) { my_cquantize_ptr cquantize = (my_cquantize_ptr)cinfo->cquantize; - JSAMPROW indexptr; + _JSAMPROW indexptr; int i, j, k, nci, blksize, val, pad; - /* For ordered dither, we pad the color index tables by MAXJSAMPLE in - * each direction (input index values can be -MAXJSAMPLE .. 2*MAXJSAMPLE). + /* For ordered dither, we pad the color index tables by _MAXJSAMPLE in + * each direction (input index values can be -_MAXJSAMPLE .. 2*_MAXJSAMPLE). * This is not necessary in the other dithering modes. However, we * flag whether it was done in case user changes dithering mode. */ if (cinfo->dither_mode == JDITHER_ORDERED) { - pad = MAXJSAMPLE * 2; + pad = _MAXJSAMPLE * 2; cquantize->is_padded = TRUE; } else { pad = 0; cquantize->is_padded = FALSE; } - cquantize->colorindex = (*cinfo->mem->alloc_sarray) + cquantize->colorindex = (_JSAMPARRAY)(*cinfo->mem->alloc_sarray) ((j_common_ptr)cinfo, JPOOL_IMAGE, - (JDIMENSION)(MAXJSAMPLE + 1 + pad), + (JDIMENSION)(_MAXJSAMPLE + 1 + pad), (JDIMENSION)cinfo->out_color_components); /* blksize is number of adjacent repeated entries for a component */ @@ -368,24 +369,24 @@ create_colorindex(j_decompress_ptr cinfo) /* adjust colorindex pointers to provide padding at negative indexes. */ if (pad) - cquantize->colorindex[i] += MAXJSAMPLE; + cquantize->colorindex[i] += _MAXJSAMPLE; /* in loop, val = index of current output value, */ /* and k = largest j that maps to current val */ indexptr = cquantize->colorindex[i]; val = 0; k = largest_input_value(cinfo, i, 0, nci - 1); - for (j = 0; j <= MAXJSAMPLE; j++) { + for (j = 0; j <= _MAXJSAMPLE; j++) { while (j > k) /* advance val if past boundary */ k = largest_input_value(cinfo, i, ++val, nci - 1); /* premultiply so that no multiplication needed in main processing */ - indexptr[j] = (JSAMPLE)(val * blksize); + indexptr[j] = (_JSAMPLE)(val * blksize); } /* Pad at both ends if necessary */ if (pad) - for (j = 1; j <= MAXJSAMPLE; j++) { + for (j = 1; j <= _MAXJSAMPLE; j++) { indexptr[-j] = indexptr[0]; - indexptr[MAXJSAMPLE + j] = indexptr[MAXJSAMPLE]; + indexptr[_MAXJSAMPLE + j] = indexptr[_MAXJSAMPLE]; } } } @@ -406,16 +407,16 @@ make_odither_array(j_decompress_ptr cinfo, int ncolors) odither = (ODITHER_MATRIX_PTR) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, sizeof(ODITHER_MATRIX)); - /* The inter-value distance for this color is MAXJSAMPLE/(ncolors-1). + /* The inter-value distance for this color is _MAXJSAMPLE/(ncolors-1). * Hence the dither value for the matrix cell with fill order f - * (f=0..N-1) should be (N-1-2*f)/(2*N) * MAXJSAMPLE/(ncolors-1). + * (f=0..N-1) should be (N-1-2*f)/(2*N) * _MAXJSAMPLE/(ncolors-1). * On 16-bit-int machine, be careful to avoid overflow. */ den = 2 * ODITHER_CELLS * ((JLONG)(ncolors - 1)); for (j = 0; j < ODITHER_SIZE; j++) { for (k = 0; k < ODITHER_SIZE; k++) { num = ((JLONG)(ODITHER_CELLS - 1 - - 2 * ((int)base_dither_matrix[j][k]))) * MAXJSAMPLE; + 2 * ((int)base_dither_matrix[j][k]))) * _MAXJSAMPLE; /* Ensure round towards zero despite C's lack of consistency * about rounding negative values in integer division... */ @@ -460,14 +461,14 @@ create_odither_tables(j_decompress_ptr cinfo) */ METHODDEF(void) -color_quantize(j_decompress_ptr cinfo, JSAMPARRAY input_buf, - JSAMPARRAY output_buf, int num_rows) +color_quantize(j_decompress_ptr cinfo, _JSAMPARRAY input_buf, + _JSAMPARRAY output_buf, int num_rows) /* General case, no dithering */ { my_cquantize_ptr cquantize = (my_cquantize_ptr)cinfo->cquantize; - JSAMPARRAY colorindex = cquantize->colorindex; + _JSAMPARRAY colorindex = cquantize->colorindex; register int pixcode, ci; - register JSAMPROW ptrin, ptrout; + register _JSAMPROW ptrin, ptrout; int row; JDIMENSION col; JDIMENSION width = cinfo->output_width; @@ -481,23 +482,23 @@ color_quantize(j_decompress_ptr cinfo, JSAMPARRAY input_buf, for (ci = 0; ci < nc; ci++) { pixcode += colorindex[ci][*ptrin++]; } - *ptrout++ = (JSAMPLE)pixcode; + *ptrout++ = (_JSAMPLE)pixcode; } } } METHODDEF(void) -color_quantize3(j_decompress_ptr cinfo, JSAMPARRAY input_buf, - JSAMPARRAY output_buf, int num_rows) +color_quantize3(j_decompress_ptr cinfo, _JSAMPARRAY input_buf, + _JSAMPARRAY output_buf, int num_rows) /* Fast path for out_color_components==3, no dithering */ { my_cquantize_ptr cquantize = (my_cquantize_ptr)cinfo->cquantize; register int pixcode; - register JSAMPROW ptrin, ptrout; - JSAMPROW colorindex0 = cquantize->colorindex[0]; - JSAMPROW colorindex1 = cquantize->colorindex[1]; - JSAMPROW colorindex2 = cquantize->colorindex[2]; + register _JSAMPROW ptrin, ptrout; + _JSAMPROW colorindex0 = cquantize->colorindex[0]; + _JSAMPROW colorindex1 = cquantize->colorindex[1]; + _JSAMPROW colorindex2 = cquantize->colorindex[2]; int row; JDIMENSION col; JDIMENSION width = cinfo->output_width; @@ -509,21 +510,21 @@ color_quantize3(j_decompress_ptr cinfo, JSAMPARRAY input_buf, pixcode = colorindex0[*ptrin++]; pixcode += colorindex1[*ptrin++]; pixcode += colorindex2[*ptrin++]; - *ptrout++ = (JSAMPLE)pixcode; + *ptrout++ = (_JSAMPLE)pixcode; } } } METHODDEF(void) -quantize_ord_dither(j_decompress_ptr cinfo, JSAMPARRAY input_buf, - JSAMPARRAY output_buf, int num_rows) +quantize_ord_dither(j_decompress_ptr cinfo, _JSAMPARRAY input_buf, + _JSAMPARRAY output_buf, int num_rows) /* General case, with ordered dithering */ { my_cquantize_ptr cquantize = (my_cquantize_ptr)cinfo->cquantize; - register JSAMPROW input_ptr; - register JSAMPROW output_ptr; - JSAMPROW colorindex_ci; + register _JSAMPROW input_ptr; + register _JSAMPROW output_ptr; + _JSAMPROW colorindex_ci; int *dither; /* points to active row of dither matrix */ int row_index, col_index; /* current indexes into dither matrix */ int nc = cinfo->out_color_components; @@ -534,7 +535,7 @@ quantize_ord_dither(j_decompress_ptr cinfo, JSAMPARRAY input_buf, for (row = 0; row < num_rows; row++) { /* Initialize output values to 0 so can process components separately */ - jzero_far((void *)output_buf[row], (size_t)(width * sizeof(JSAMPLE))); + jzero_far((void *)output_buf[row], (size_t)(width * sizeof(_JSAMPLE))); row_index = cquantize->row_index; for (ci = 0; ci < nc; ci++) { input_ptr = input_buf[row] + ci; @@ -544,11 +545,11 @@ quantize_ord_dither(j_decompress_ptr cinfo, JSAMPARRAY input_buf, col_index = 0; for (col = width; col > 0; col--) { - /* Form pixel value + dither, range-limit to 0..MAXJSAMPLE, + /* Form pixel value + dither, range-limit to 0.._MAXJSAMPLE, * select output value, accumulate into output code for this pixel. * Range-limiting need not be done explicitly, as we have extended * the colorindex table to produce the right answers for out-of-range - * inputs. The maximum dither is +- MAXJSAMPLE; this sets the + * inputs. The maximum dither is +- _MAXJSAMPLE; this sets the * required amount of padding. */ *output_ptr += @@ -566,17 +567,17 @@ quantize_ord_dither(j_decompress_ptr cinfo, JSAMPARRAY input_buf, METHODDEF(void) -quantize3_ord_dither(j_decompress_ptr cinfo, JSAMPARRAY input_buf, - JSAMPARRAY output_buf, int num_rows) +quantize3_ord_dither(j_decompress_ptr cinfo, _JSAMPARRAY input_buf, + _JSAMPARRAY output_buf, int num_rows) /* Fast path for out_color_components==3, with ordered dithering */ { my_cquantize_ptr cquantize = (my_cquantize_ptr)cinfo->cquantize; register int pixcode; - register JSAMPROW input_ptr; - register JSAMPROW output_ptr; - JSAMPROW colorindex0 = cquantize->colorindex[0]; - JSAMPROW colorindex1 = cquantize->colorindex[1]; - JSAMPROW colorindex2 = cquantize->colorindex[2]; + register _JSAMPROW input_ptr; + register _JSAMPROW output_ptr; + _JSAMPROW colorindex0 = cquantize->colorindex[0]; + _JSAMPROW colorindex1 = cquantize->colorindex[1]; + _JSAMPROW colorindex2 = cquantize->colorindex[2]; int *dither0; /* points to active row of dither matrix */ int *dither1; int *dither2; @@ -598,7 +599,7 @@ quantize3_ord_dither(j_decompress_ptr cinfo, JSAMPARRAY input_buf, pixcode = colorindex0[(*input_ptr++) + dither0[col_index]]; pixcode += colorindex1[(*input_ptr++) + dither1[col_index]]; pixcode += colorindex2[(*input_ptr++) + dither2[col_index]]; - *output_ptr++ = (JSAMPLE)pixcode; + *output_ptr++ = (_JSAMPLE)pixcode; col_index = (col_index + 1) & ODITHER_MASK; } row_index = (row_index + 1) & ODITHER_MASK; @@ -608,8 +609,8 @@ quantize3_ord_dither(j_decompress_ptr cinfo, JSAMPARRAY input_buf, METHODDEF(void) -quantize_fs_dither(j_decompress_ptr cinfo, JSAMPARRAY input_buf, - JSAMPARRAY output_buf, int num_rows) +quantize_fs_dither(j_decompress_ptr cinfo, _JSAMPARRAY input_buf, + _JSAMPARRAY output_buf, int num_rows) /* General case, with Floyd-Steinberg dithering */ { my_cquantize_ptr cquantize = (my_cquantize_ptr)cinfo->cquantize; @@ -619,10 +620,10 @@ quantize_fs_dither(j_decompress_ptr cinfo, JSAMPARRAY input_buf, LOCFSERROR bnexterr; /* error for below/next col */ LOCFSERROR delta; register FSERRPTR errorptr; /* => fserrors[] at column before current */ - register JSAMPROW input_ptr; - register JSAMPROW output_ptr; - JSAMPROW colorindex_ci; - JSAMPROW colormap_ci; + register _JSAMPROW input_ptr; + register _JSAMPROW output_ptr; + _JSAMPROW colorindex_ci; + _JSAMPROW colormap_ci; int pixcode; int nc = cinfo->out_color_components; int dir; /* 1 for left-to-right, -1 for right-to-left */ @@ -631,12 +632,12 @@ quantize_fs_dither(j_decompress_ptr cinfo, JSAMPARRAY input_buf, int row; JDIMENSION col; JDIMENSION width = cinfo->output_width; - JSAMPLE *range_limit = cinfo->sample_range_limit; + _JSAMPLE *range_limit = (_JSAMPLE *)cinfo->sample_range_limit; SHIFT_TEMPS for (row = 0; row < num_rows; row++) { /* Initialize output values to 0 so can process components separately */ - jzero_far((void *)output_buf[row], (size_t)(width * sizeof(JSAMPLE))); + jzero_far((void *)output_buf[row], (size_t)(width * sizeof(_JSAMPLE))); for (ci = 0; ci < nc; ci++) { input_ptr = input_buf[row] + ci; output_ptr = output_buf[row]; @@ -670,15 +671,15 @@ quantize_fs_dither(j_decompress_ptr cinfo, JSAMPARRAY input_buf, * Note: errorptr points to *previous* column's array entry. */ cur = RIGHT_SHIFT(cur + errorptr[dir] + 8, 4); - /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE. - * The maximum error is +- MAXJSAMPLE; this sets the required size + /* Form pixel value + error, and range-limit to 0.._MAXJSAMPLE. + * The maximum error is +- _MAXJSAMPLE; this sets the required size * of the range_limit array. */ cur += *input_ptr; cur = range_limit[cur]; /* Select output value, accumulate into output code for this pixel */ pixcode = colorindex_ci[cur]; - *output_ptr += (JSAMPLE)pixcode; + *output_ptr += (_JSAMPLE)pixcode; /* Compute actual representation error at this pixel */ /* Note: we can do this even though we don't have the final */ /* pixel code, because the colormap is orthogonal. */ @@ -745,22 +746,22 @@ start_pass_1_quant(j_decompress_ptr cinfo, boolean is_pre_scan) int i; /* Install my colormap. */ - cinfo->colormap = cquantize->sv_colormap; + cinfo->colormap = (JSAMPARRAY)cquantize->sv_colormap; cinfo->actual_number_of_colors = cquantize->sv_actual; /* Initialize for desired dithering mode. */ switch (cinfo->dither_mode) { case JDITHER_NONE: if (cinfo->out_color_components == 3) - cquantize->pub.color_quantize = color_quantize3; + cquantize->pub._color_quantize = color_quantize3; else - cquantize->pub.color_quantize = color_quantize; + cquantize->pub._color_quantize = color_quantize; break; case JDITHER_ORDERED: if (cinfo->out_color_components == 3) - cquantize->pub.color_quantize = quantize3_ord_dither; + cquantize->pub._color_quantize = quantize3_ord_dither; else - cquantize->pub.color_quantize = quantize_ord_dither; + cquantize->pub._color_quantize = quantize_ord_dither; cquantize->row_index = 0; /* initialize state for ordered dither */ /* If user changed to ordered dither from another mode, * we must recreate the color index table with padding. @@ -773,7 +774,7 @@ start_pass_1_quant(j_decompress_ptr cinfo, boolean is_pre_scan) create_odither_tables(cinfo); break; case JDITHER_FS: - cquantize->pub.color_quantize = quantize_fs_dither; + cquantize->pub._color_quantize = quantize_fs_dither; cquantize->on_odd_row = FALSE; /* initialize state for F-S dither */ /* Allocate Floyd-Steinberg workspace if didn't already. */ if (cquantize->fserrors[0] == NULL) @@ -818,10 +819,17 @@ new_color_map_1_quant(j_decompress_ptr cinfo) */ GLOBAL(void) -jinit_1pass_quantizer(j_decompress_ptr cinfo) +_jinit_1pass_quantizer(j_decompress_ptr cinfo) { my_cquantize_ptr cquantize; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + + /* Color quantization is not supported with lossless JPEG images */ + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); + cquantize = (my_cquantize_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, sizeof(my_cquantizer)); @@ -835,9 +843,9 @@ jinit_1pass_quantizer(j_decompress_ptr cinfo) /* Make sure my internal arrays won't overflow */ if (cinfo->out_color_components > MAX_Q_COMPS) ERREXIT1(cinfo, JERR_QUANT_COMPONENTS, MAX_Q_COMPS); - /* Make sure colormap indexes can be represented by JSAMPLEs */ - if (cinfo->desired_number_of_colors > (MAXJSAMPLE + 1)) - ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, MAXJSAMPLE + 1); + /* Make sure colormap indexes can be represented by _JSAMPLEs */ + if (cinfo->desired_number_of_colors > (_MAXJSAMPLE + 1)) + ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, _MAXJSAMPLE + 1); /* Create the colormap and color index table. */ create_colormap(cinfo); @@ -853,4 +861,4 @@ jinit_1pass_quantizer(j_decompress_ptr cinfo) alloc_fs_workspace(cinfo); } -#endif /* QUANT_1PASS_SUPPORTED */ +#endif /* defined(QUANT_1PASS_SUPPORTED) && BITS_IN_JSAMPLE != 16 */ diff --git a/jquant2.c b/jquant2.c index 44efb18..9ba51fa 100644 --- a/jquant2.c +++ b/jquant2.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2009, 2014-2015, 2020, D. R. Commander. + * Copyright (C) 2009, 2014-2015, 2020, 2022-2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -23,8 +23,9 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" +#include "jsamplecomp.h" -#ifdef QUANT_2PASS_SUPPORTED +#if defined(QUANT_2PASS_SUPPORTED) && BITS_IN_JSAMPLE != 16 /* @@ -106,7 +107,7 @@ static const int c_scales[3] = { R_SCALE, G_SCALE, B_SCALE }; * each 2-D array has 2^6*2^5 = 2048 or 2^6*2^6 = 4096 entries. */ -#define MAXNUMCOLORS (MAXJSAMPLE + 1) /* maximum size of colormap */ +#define MAXNUMCOLORS (_MAXJSAMPLE + 1) /* maximum size of colormap */ /* These will do the right thing for either R,G,B or B,G,R color order, * but you may not like the results for other color orders. @@ -173,7 +174,7 @@ typedef struct { struct jpeg_color_quantizer pub; /* public fields */ /* Space for the eventually created colormap is stashed here */ - JSAMPARRAY sv_colormap; /* colormap allocated at init time */ + _JSAMPARRAY sv_colormap; /* colormap allocated at init time */ int desired; /* desired # of colors = size of colormap */ /* Variables for accumulating image statistics */ @@ -200,11 +201,11 @@ typedef my_cquantizer *my_cquantize_ptr; */ METHODDEF(void) -prescan_quantize(j_decompress_ptr cinfo, JSAMPARRAY input_buf, - JSAMPARRAY output_buf, int num_rows) +prescan_quantize(j_decompress_ptr cinfo, _JSAMPARRAY input_buf, + _JSAMPARRAY output_buf, int num_rows) { my_cquantize_ptr cquantize = (my_cquantize_ptr)cinfo->cquantize; - register JSAMPROW ptr; + register _JSAMPROW ptr; register histptr histp; register hist3d histogram = cquantize->histogram; int row; @@ -377,7 +378,7 @@ have_c2max: * against making long narrow boxes, and it has the side benefit that * a box is splittable iff norm > 0. * Since the differences are expressed in histogram-cell units, - * we have to shift back to JSAMPLE units to get consistent distances; + * we have to shift back to _JSAMPLE units to get consistent distances; * after which, we scale according to the selected distance scale factors. */ dist0 = ((c0max - c0min) << C0_SHIFT) * C0_SCALE; @@ -508,9 +509,12 @@ compute_color(j_decompress_ptr cinfo, boxptr boxp, int icolor) } } - cinfo->colormap[0][icolor] = (JSAMPLE)((c0total + (total >> 1)) / total); - cinfo->colormap[1][icolor] = (JSAMPLE)((c1total + (total >> 1)) / total); - cinfo->colormap[2][icolor] = (JSAMPLE)((c2total + (total >> 1)) / total); + ((_JSAMPARRAY)cinfo->colormap)[0][icolor] = + (_JSAMPLE)((c0total + (total >> 1)) / total); + ((_JSAMPARRAY)cinfo->colormap)[1][icolor] = + (_JSAMPLE)((c1total + (total >> 1)) / total); + ((_JSAMPARRAY)cinfo->colormap)[2][icolor] = + (_JSAMPLE)((c2total + (total >> 1)) / total); } @@ -528,11 +532,11 @@ select_colors(j_decompress_ptr cinfo, int desired_colors) /* Initialize one box containing whole space */ numboxes = 1; boxlist[0].c0min = 0; - boxlist[0].c0max = MAXJSAMPLE >> C0_SHIFT; + boxlist[0].c0max = _MAXJSAMPLE >> C0_SHIFT; boxlist[0].c1min = 0; - boxlist[0].c1max = MAXJSAMPLE >> C1_SHIFT; + boxlist[0].c1max = _MAXJSAMPLE >> C1_SHIFT; boxlist[0].c2min = 0; - boxlist[0].c2max = MAXJSAMPLE >> C2_SHIFT; + boxlist[0].c2max = _MAXJSAMPLE >> C2_SHIFT; /* Shrink it to actually-used volume and set its statistics */ update_box(cinfo, &boxlist[0]); /* Perform median-cut to produce final box list */ @@ -623,7 +627,7 @@ select_colors(j_decompress_ptr cinfo, int desired_colors) LOCAL(int) find_nearby_colors(j_decompress_ptr cinfo, int minc0, int minc1, int minc2, - JSAMPLE colorlist[]) + _JSAMPLE colorlist[]) /* Locate the colormap entries close enough to an update box to be candidates * for the nearest entry to some cell(s) in the update box. The update box * is specified by the center coordinates of its first cell. The number of @@ -665,7 +669,7 @@ find_nearby_colors(j_decompress_ptr cinfo, int minc0, int minc1, int minc2, for (i = 0; i < numcolors; i++) { /* We compute the squared-c0-distance term, then add in the other two. */ - x = cinfo->colormap[0][i]; + x = ((_JSAMPARRAY)cinfo->colormap)[0][i]; if (x < minc0) { tdist = (x - minc0) * C0_SCALE; min_dist = tdist * tdist; @@ -688,7 +692,7 @@ find_nearby_colors(j_decompress_ptr cinfo, int minc0, int minc1, int minc2, } } - x = cinfo->colormap[1][i]; + x = ((_JSAMPARRAY)cinfo->colormap)[1][i]; if (x < minc1) { tdist = (x - minc1) * C1_SCALE; min_dist += tdist * tdist; @@ -710,7 +714,7 @@ find_nearby_colors(j_decompress_ptr cinfo, int minc0, int minc1, int minc2, } } - x = cinfo->colormap[2][i]; + x = ((_JSAMPARRAY)cinfo->colormap)[2][i]; if (x < minc2) { tdist = (x - minc2) * C2_SCALE; min_dist += tdist * tdist; @@ -744,7 +748,7 @@ find_nearby_colors(j_decompress_ptr cinfo, int minc0, int minc1, int minc2, ncolors = 0; for (i = 0; i < numcolors; i++) { if (mindist[i] <= minmaxdist) - colorlist[ncolors++] = (JSAMPLE)i; + colorlist[ncolors++] = (_JSAMPLE)i; } return ncolors; } @@ -752,7 +756,7 @@ find_nearby_colors(j_decompress_ptr cinfo, int minc0, int minc1, int minc2, LOCAL(void) find_best_colors(j_decompress_ptr cinfo, int minc0, int minc1, int minc2, - int numcolors, JSAMPLE colorlist[], JSAMPLE bestcolor[]) + int numcolors, _JSAMPLE colorlist[], _JSAMPLE bestcolor[]) /* Find the closest colormap entry for each cell in the update box, * given the list of candidate colors prepared by find_nearby_colors. * Return the indexes of the closest entries in the bestcolor[] array. @@ -763,7 +767,7 @@ find_best_colors(j_decompress_ptr cinfo, int minc0, int minc1, int minc2, int ic0, ic1, ic2; int i, icolor; register JLONG *bptr; /* pointer into bestdist[] array */ - JSAMPLE *cptr; /* pointer into bestcolor[] array */ + _JSAMPLE *cptr; /* pointer into bestcolor[] array */ JLONG dist0, dist1; /* initial distance values */ register JLONG dist2; /* current distance in inner loop */ JLONG xx0, xx1; /* distance increments */ @@ -790,11 +794,11 @@ find_best_colors(j_decompress_ptr cinfo, int minc0, int minc1, int minc2, for (i = 0; i < numcolors; i++) { icolor = colorlist[i]; /* Compute (square of) distance from minc0/c1/c2 to this color */ - inc0 = (minc0 - cinfo->colormap[0][icolor]) * C0_SCALE; + inc0 = (minc0 - ((_JSAMPARRAY)cinfo->colormap)[0][icolor]) * C0_SCALE; dist0 = inc0 * inc0; - inc1 = (minc1 - cinfo->colormap[1][icolor]) * C1_SCALE; + inc1 = (minc1 - ((_JSAMPARRAY)cinfo->colormap)[1][icolor]) * C1_SCALE; dist0 += inc1 * inc1; - inc2 = (minc2 - cinfo->colormap[2][icolor]) * C2_SCALE; + inc2 = (minc2 - ((_JSAMPARRAY)cinfo->colormap)[2][icolor]) * C2_SCALE; dist0 += inc2 * inc2; /* Form the initial difference increments */ inc0 = inc0 * (2 * STEP_C0) + STEP_C0 * STEP_C0; @@ -813,7 +817,7 @@ find_best_colors(j_decompress_ptr cinfo, int minc0, int minc1, int minc2, for (ic2 = BOX_C2_ELEMS - 1; ic2 >= 0; ic2--) { if (dist2 < *bptr) { *bptr = dist2; - *cptr = (JSAMPLE)icolor; + *cptr = (_JSAMPLE)icolor; } dist2 += xx2; xx2 += 2 * STEP_C2 * STEP_C2; @@ -840,13 +844,13 @@ fill_inverse_cmap(j_decompress_ptr cinfo, int c0, int c1, int c2) hist3d histogram = cquantize->histogram; int minc0, minc1, minc2; /* lower left corner of update box */ int ic0, ic1, ic2; - register JSAMPLE *cptr; /* pointer into bestcolor[] array */ + register _JSAMPLE *cptr; /* pointer into bestcolor[] array */ register histptr cachep; /* pointer into main cache array */ /* This array lists the candidate colormap indexes. */ - JSAMPLE colorlist[MAXNUMCOLORS]; + _JSAMPLE colorlist[MAXNUMCOLORS]; int numcolors; /* number of candidate colors */ /* This array holds the actually closest colormap index for each cell. */ - JSAMPLE bestcolor[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS]; + _JSAMPLE bestcolor[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS]; /* Convert cell coordinates to update box ID */ c0 >>= BOX_C0_LOG; @@ -891,13 +895,13 @@ fill_inverse_cmap(j_decompress_ptr cinfo, int c0, int c1, int c2) */ METHODDEF(void) -pass2_no_dither(j_decompress_ptr cinfo, JSAMPARRAY input_buf, - JSAMPARRAY output_buf, int num_rows) +pass2_no_dither(j_decompress_ptr cinfo, _JSAMPARRAY input_buf, + _JSAMPARRAY output_buf, int num_rows) /* This version performs no dithering */ { my_cquantize_ptr cquantize = (my_cquantize_ptr)cinfo->cquantize; hist3d histogram = cquantize->histogram; - register JSAMPROW inptr, outptr; + register _JSAMPROW inptr, outptr; register histptr cachep; register int c0, c1, c2; int row; @@ -918,15 +922,15 @@ pass2_no_dither(j_decompress_ptr cinfo, JSAMPARRAY input_buf, if (*cachep == 0) fill_inverse_cmap(cinfo, c0, c1, c2); /* Now emit the colormap index for this cell */ - *outptr++ = (JSAMPLE)(*cachep - 1); + *outptr++ = (_JSAMPLE)(*cachep - 1); } } } METHODDEF(void) -pass2_fs_dither(j_decompress_ptr cinfo, JSAMPARRAY input_buf, - JSAMPARRAY output_buf, int num_rows) +pass2_fs_dither(j_decompress_ptr cinfo, _JSAMPARRAY input_buf, + _JSAMPARRAY output_buf, int num_rows) /* This version performs Floyd-Steinberg dithering */ { my_cquantize_ptr cquantize = (my_cquantize_ptr)cinfo->cquantize; @@ -935,19 +939,19 @@ pass2_fs_dither(j_decompress_ptr cinfo, JSAMPARRAY input_buf, LOCFSERROR belowerr0, belowerr1, belowerr2; /* error for pixel below cur */ LOCFSERROR bpreverr0, bpreverr1, bpreverr2; /* error for below/prev col */ register FSERRPTR errorptr; /* => fserrors[] at column before current */ - JSAMPROW inptr; /* => current input pixel */ - JSAMPROW outptr; /* => current output pixel */ + _JSAMPROW inptr; /* => current input pixel */ + _JSAMPROW outptr; /* => current output pixel */ histptr cachep; int dir; /* +1 or -1 depending on direction */ int dir3; /* 3*dir, for advancing inptr & errorptr */ int row; JDIMENSION col; JDIMENSION width = cinfo->output_width; - JSAMPLE *range_limit = cinfo->sample_range_limit; + _JSAMPLE *range_limit = (_JSAMPLE *)cinfo->sample_range_limit; int *error_limit = cquantize->error_limiter; - JSAMPROW colormap0 = cinfo->colormap[0]; - JSAMPROW colormap1 = cinfo->colormap[1]; - JSAMPROW colormap2 = cinfo->colormap[2]; + _JSAMPROW colormap0 = ((_JSAMPARRAY)cinfo->colormap)[0]; + _JSAMPROW colormap1 = ((_JSAMPARRAY)cinfo->colormap)[1]; + _JSAMPROW colormap2 = ((_JSAMPARRAY)cinfo->colormap)[2]; SHIFT_TEMPS for (row = 0; row < num_rows; row++) { @@ -992,8 +996,8 @@ pass2_fs_dither(j_decompress_ptr cinfo, JSAMPARRAY input_buf, cur0 = error_limit[cur0]; cur1 = error_limit[cur1]; cur2 = error_limit[cur2]; - /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE. - * The maximum error is +- MAXJSAMPLE (or less with error limiting); + /* Form pixel value + error, and range-limit to 0.._MAXJSAMPLE. + * The maximum error is +- _MAXJSAMPLE (or less with error limiting); * this sets the required size of the range_limit array. */ cur0 += inptr[0]; @@ -1013,7 +1017,7 @@ pass2_fs_dither(j_decompress_ptr cinfo, JSAMPARRAY input_buf, /* Now emit the colormap index for this cell */ { register int pixcode = *cachep - 1; - *outptr = (JSAMPLE)pixcode; + *outptr = (_JSAMPLE)pixcode; /* Compute representation error for this pixel */ cur0 -= colormap0[pixcode]; cur1 -= colormap1[pixcode]; @@ -1064,7 +1068,7 @@ pass2_fs_dither(j_decompress_ptr cinfo, JSAMPARRAY input_buf, /* * Initialize the error-limiting transfer function (lookup table). * The raw F-S error computation can potentially compute error values of up to - * +- MAXJSAMPLE. But we want the maximum correction applied to a pixel to be + * +- _MAXJSAMPLE. But we want the maximum correction applied to a pixel to be * much less, otherwise obviously wrong pixels will be created. (Typical * effects include weird fringes at color-area boundaries, isolated bright * pixels in a dark area, etc.) The standard advice for avoiding this problem @@ -1073,7 +1077,7 @@ pass2_fs_dither(j_decompress_ptr cinfo, JSAMPARRAY input_buf, * error buildup. However, that only prevents the error from getting * completely out of hand; Aaron Giles reports that error limiting improves * the results even with corner colors allocated. - * A simple clamping of the error values to about +- MAXJSAMPLE/8 works pretty + * A simple clamping of the error values to about +- _MAXJSAMPLE/8 works pretty * well, but the smoother transfer function used below is even better. Thanks * to Aaron Giles for this idea. */ @@ -1087,22 +1091,22 @@ init_error_limit(j_decompress_ptr cinfo) int in, out; table = (int *)(*cinfo->mem->alloc_small) - ((j_common_ptr)cinfo, JPOOL_IMAGE, (MAXJSAMPLE * 2 + 1) * sizeof(int)); - table += MAXJSAMPLE; /* so can index -MAXJSAMPLE .. +MAXJSAMPLE */ + ((j_common_ptr)cinfo, JPOOL_IMAGE, (_MAXJSAMPLE * 2 + 1) * sizeof(int)); + table += _MAXJSAMPLE; /* so can index -_MAXJSAMPLE .. +_MAXJSAMPLE */ cquantize->error_limiter = table; -#define STEPSIZE ((MAXJSAMPLE + 1) / 16) - /* Map errors 1:1 up to +- MAXJSAMPLE/16 */ +#define STEPSIZE ((_MAXJSAMPLE + 1) / 16) + /* Map errors 1:1 up to +- _MAXJSAMPLE/16 */ out = 0; for (in = 0; in < STEPSIZE; in++, out++) { table[in] = out; table[-in] = -out; } - /* Map errors 1:2 up to +- 3*MAXJSAMPLE/16 */ + /* Map errors 1:2 up to +- 3*_MAXJSAMPLE/16 */ for (; in < STEPSIZE * 3; in++, out += (in & 1) ? 0 : 1) { table[in] = out; table[-in] = -out; } - /* Clamp the rest to final out value (which is (MAXJSAMPLE+1)/8) */ - for (; in <= MAXJSAMPLE; in++) { + /* Clamp the rest to final out value (which is (_MAXJSAMPLE+1)/8) */ + for (; in <= _MAXJSAMPLE; in++) { table[in] = out; table[-in] = -out; } #undef STEPSIZE @@ -1119,7 +1123,7 @@ finish_pass1(j_decompress_ptr cinfo) my_cquantize_ptr cquantize = (my_cquantize_ptr)cinfo->cquantize; /* Select the representative colors and fill in cinfo->colormap */ - cinfo->colormap = cquantize->sv_colormap; + cinfo->colormap = (JSAMPARRAY)cquantize->sv_colormap; select_colors(cinfo, cquantize->desired); /* Force next pass to zero the color index table */ cquantize->needs_zeroed = TRUE; @@ -1151,15 +1155,15 @@ start_pass_2_quant(j_decompress_ptr cinfo, boolean is_pre_scan) if (is_pre_scan) { /* Set up method pointers */ - cquantize->pub.color_quantize = prescan_quantize; + cquantize->pub._color_quantize = prescan_quantize; cquantize->pub.finish_pass = finish_pass1; cquantize->needs_zeroed = TRUE; /* Always zero histogram */ } else { /* Set up method pointers */ if (cinfo->dither_mode == JDITHER_FS) - cquantize->pub.color_quantize = pass2_fs_dither; + cquantize->pub._color_quantize = pass2_fs_dither; else - cquantize->pub.color_quantize = pass2_no_dither; + cquantize->pub._color_quantize = pass2_no_dither; cquantize->pub.finish_pass = finish_pass2; /* Make sure color count is acceptable */ @@ -1215,11 +1219,14 @@ new_color_map_2_quant(j_decompress_ptr cinfo) */ GLOBAL(void) -jinit_2pass_quantizer(j_decompress_ptr cinfo) +_jinit_2pass_quantizer(j_decompress_ptr cinfo) { my_cquantize_ptr cquantize; int i; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + cquantize = (my_cquantize_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, sizeof(my_cquantizer)); @@ -1230,7 +1237,8 @@ jinit_2pass_quantizer(j_decompress_ptr cinfo) cquantize->error_limiter = NULL; /* Make sure jdmaster didn't give me a case I can't handle */ - if (cinfo->out_color_components != 3) + if (cinfo->out_color_components != 3 || + cinfo->out_color_space == JCS_RGB565 || cinfo->master->lossless) ERREXIT(cinfo, JERR_NOTIMPL); /* Allocate the histogram/inverse colormap storage */ @@ -1253,10 +1261,10 @@ jinit_2pass_quantizer(j_decompress_ptr cinfo) /* Lower bound on # of colors ... somewhat arbitrary as long as > 0 */ if (desired < 8) ERREXIT1(cinfo, JERR_QUANT_FEW_COLORS, 8); - /* Make sure colormap indexes can be represented by JSAMPLEs */ + /* Make sure colormap indexes can be represented by _JSAMPLEs */ if (desired > MAXNUMCOLORS) ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, MAXNUMCOLORS); - cquantize->sv_colormap = (*cinfo->mem->alloc_sarray) + cquantize->sv_colormap = (_JSAMPARRAY)(*cinfo->mem->alloc_sarray) ((j_common_ptr)cinfo, JPOOL_IMAGE, (JDIMENSION)desired, (JDIMENSION)3); cquantize->desired = desired; } else @@ -1282,4 +1290,4 @@ jinit_2pass_quantizer(j_decompress_ptr cinfo) } } -#endif /* QUANT_2PASS_SUPPORTED */ +#endif /* defined(QUANT_2PASS_SUPPORTED) && BITS_IN_JSAMPLE != 16 */ diff --git a/jsamplecomp.h b/jsamplecomp.h new file mode 100644 index 0000000..f3f275e --- /dev/null +++ b/jsamplecomp.h @@ -0,0 +1,336 @@ +/* + * jsamplecomp.h + * + * Copyright (C) 2022, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + */ + +/* In source files that must be compiled for multiple data precisions, we + * prefix all precision-dependent data types, macros, methods, fields, and + * function names with an underscore. Including this file replaces those + * precision-independent tokens with their precision-dependent equivalents, + * based on the value of BITS_IN_JSAMPLE. + */ + +#ifndef JSAMPLECOMP_H +#define JSAMPLECOMP_H + +#if BITS_IN_JSAMPLE == 16 + +/* Sample data types and macros (jmorecfg.h) */ +#define _JSAMPLE J16SAMPLE + +#define _MAXJSAMPLE MAXJ16SAMPLE +#define _CENTERJSAMPLE CENTERJ16SAMPLE + +#define _JSAMPROW J16SAMPROW +#define _JSAMPARRAY J16SAMPARRAY +#define _JSAMPIMAGE J16SAMPIMAGE + +/* External functions (jpeglib.h) */ +#define _jpeg_write_scanlines jpeg16_write_scanlines +#define _jpeg_read_scanlines jpeg16_read_scanlines + +/* Internal methods (jpegint.h) */ + +#ifdef C_LOSSLESS_SUPPORTED +/* Use the 16-bit method in the jpeg_c_main_controller structure. */ +#define _process_data process_data_16 +/* Use the 16-bit method in the jpeg_c_prep_controller structure. */ +#define _pre_process_data pre_process_data_16 +/* Use the 16-bit method in the jpeg_c_coef_controller structure. */ +#define _compress_data compress_data_16 +/* Use the 16-bit method in the jpeg_color_converter structure. */ +#define _color_convert color_convert_16 +/* Use the 16-bit method in the jpeg_downsampler structure. */ +#define _downsample downsample_16 +#endif +#ifdef D_LOSSLESS_SUPPORTED +/* Use the 16-bit method in the jpeg_d_main_controller structure. */ +#define _process_data process_data_16 +/* Use the 16-bit method in the jpeg_d_coef_controller structure. */ +#define _decompress_data decompress_data_16 +/* Use the 16-bit method in the jpeg_d_post_controller structure. */ +#define _post_process_data post_process_data_16 +/* Use the 16-bit method in the jpeg_upsampler structure. */ +#define _upsample upsample_16 +/* Use the 16-bit method in the jpeg_color_converter structure. */ +#define _color_convert color_convert_16 +#endif + +/* Global internal functions (jpegint.h) */ +#ifdef C_LOSSLESS_SUPPORTED +#define _jinit_c_main_controller j16init_c_main_controller +#define _jinit_c_prep_controller j16init_c_prep_controller +#define _jinit_color_converter j16init_color_converter +#define _jinit_downsampler j16init_downsampler +#define _jinit_c_diff_controller j16init_c_diff_controller +#define _jinit_lossless_compressor j16init_lossless_compressor +#endif + +#ifdef D_LOSSLESS_SUPPORTED +#define _jinit_d_main_controller j16init_d_main_controller +#define _jinit_d_post_controller j16init_d_post_controller +#define _jinit_upsampler j16init_upsampler +#define _jinit_color_deconverter j16init_color_deconverter +#define _jinit_merged_upsampler j16init_merged_upsampler +#define _jinit_d_diff_controller j16init_d_diff_controller +#define _jinit_lossless_decompressor j16init_lossless_decompressor +#endif + +#if defined(C_LOSSLESS_SUPPORTED) || defined(D_LOSSLESS_SUPPORTED) +#define _jcopy_sample_rows j16copy_sample_rows +#endif + +/* Internal fields (cdjpeg.h) */ + +#if defined(C_LOSSLESS_SUPPORTED) || defined(D_LOSSLESS_SUPPORTED) +/* Use the 16-bit buffer in the cjpeg_source_struct and djpeg_dest_struct + structures. */ +#define _buffer buffer16 +#endif + +/* Image I/O functions (cdjpeg.h) */ +#ifdef C_LOSSLESS_SUPPORTED +#define _jinit_read_gif j16init_read_gif +#define _jinit_read_ppm j16init_read_ppm +#endif + +#ifdef D_LOSSLESS_SUPPORTED +#define _jinit_write_ppm j16init_write_ppm +#endif + +#elif BITS_IN_JSAMPLE == 12 + +/* Sample data types and macros (jmorecfg.h) */ +#define _JSAMPLE J12SAMPLE + +#define _MAXJSAMPLE MAXJ12SAMPLE +#define _CENTERJSAMPLE CENTERJ12SAMPLE + +#define _JSAMPROW J12SAMPROW +#define _JSAMPARRAY J12SAMPARRAY +#define _JSAMPIMAGE J12SAMPIMAGE + +/* External functions (jpeglib.h) */ +#define _jpeg_write_scanlines jpeg12_write_scanlines +#define _jpeg_write_raw_data jpeg12_write_raw_data +#define _jpeg_read_scanlines jpeg12_read_scanlines +#define _jpeg_skip_scanlines jpeg12_skip_scanlines +#define _jpeg_crop_scanline jpeg12_crop_scanline +#define _jpeg_read_raw_data jpeg12_read_raw_data + +/* Internal methods (jpegint.h) */ + +/* Use the 12-bit method in the jpeg_c_main_controller structure. */ +#define _process_data process_data_12 +/* Use the 12-bit method in the jpeg_c_prep_controller structure. */ +#define _pre_process_data pre_process_data_12 +/* Use the 12-bit method in the jpeg_c_coef_controller structure. */ +#define _compress_data compress_data_12 +/* Use the 12-bit method in the jpeg_color_converter structure. */ +#define _color_convert color_convert_12 +/* Use the 12-bit method in the jpeg_downsampler structure. */ +#define _downsample downsample_12 +/* Use the 12-bit method in the jpeg_forward_dct structure. */ +#define _forward_DCT forward_DCT_12 +/* Use the 12-bit method in the jpeg_d_main_controller structure. */ +#define _process_data process_data_12 +/* Use the 12-bit method in the jpeg_d_coef_controller structure. */ +#define _decompress_data decompress_data_12 +/* Use the 12-bit method in the jpeg_d_post_controller structure. */ +#define _post_process_data post_process_data_12 +/* Use the 12-bit method in the jpeg_inverse_dct structure. */ +#define _inverse_DCT_method_ptr inverse_DCT_12_method_ptr +#define _inverse_DCT inverse_DCT_12 +/* Use the 12-bit method in the jpeg_upsampler structure. */ +#define _upsample upsample_12 +/* Use the 12-bit method in the jpeg_color_converter structure. */ +#define _color_convert color_convert_12 +/* Use the 12-bit method in the jpeg_color_quantizer structure. */ +#define _color_quantize color_quantize_12 + +/* Global internal functions (jpegint.h) */ +#define _jinit_c_main_controller j12init_c_main_controller +#define _jinit_c_prep_controller j12init_c_prep_controller +#define _jinit_c_coef_controller j12init_c_coef_controller +#define _jinit_color_converter j12init_color_converter +#define _jinit_downsampler j12init_downsampler +#define _jinit_forward_dct j12init_forward_dct +#ifdef C_LOSSLESS_SUPPORTED +#define _jinit_c_diff_controller j12init_c_diff_controller +#define _jinit_lossless_compressor j12init_lossless_compressor +#endif + +#define _jinit_d_main_controller j12init_d_main_controller +#define _jinit_d_coef_controller j12init_d_coef_controller +#define _jinit_d_post_controller j12init_d_post_controller +#define _jinit_inverse_dct j12init_inverse_dct +#define _jinit_upsampler j12init_upsampler +#define _jinit_color_deconverter j12init_color_deconverter +#define _jinit_1pass_quantizer j12init_1pass_quantizer +#define _jinit_2pass_quantizer j12init_2pass_quantizer +#define _jinit_merged_upsampler j12init_merged_upsampler +#ifdef D_LOSSLESS_SUPPORTED +#define _jinit_d_diff_controller j12init_d_diff_controller +#define _jinit_lossless_decompressor j12init_lossless_decompressor +#endif + +#define _jcopy_sample_rows j12copy_sample_rows + +/* Global internal functions (jdct.h) */ +#define _jpeg_fdct_islow jpeg12_fdct_islow +#define _jpeg_fdct_ifast jpeg12_fdct_ifast + +#define _jpeg_idct_islow jpeg12_idct_islow +#define _jpeg_idct_ifast jpeg12_idct_ifast +#define _jpeg_idct_float jpeg12_idct_float +#define _jpeg_idct_7x7 jpeg12_idct_7x7 +#define _jpeg_idct_6x6 jpeg12_idct_6x6 +#define _jpeg_idct_5x5 jpeg12_idct_5x5 +#define _jpeg_idct_4x4 jpeg12_idct_4x4 +#define _jpeg_idct_3x3 jpeg12_idct_3x3 +#define _jpeg_idct_2x2 jpeg12_idct_2x2 +#define _jpeg_idct_1x1 jpeg12_idct_1x1 +#define _jpeg_idct_9x9 jpeg12_idct_9x9 +#define _jpeg_idct_10x10 jpeg12_idct_10x10 +#define _jpeg_idct_11x11 jpeg12_idct_11x11 +#define _jpeg_idct_12x12 jpeg12_idct_12x12 +#define _jpeg_idct_13x13 jpeg12_idct_13x13 +#define _jpeg_idct_14x14 jpeg12_idct_14x14 +#define _jpeg_idct_15x15 jpeg12_idct_15x15 +#define _jpeg_idct_16x16 jpeg12_idct_16x16 + +/* Internal fields (cdjpeg.h) */ + +/* Use the 12-bit buffer in the cjpeg_source_struct and djpeg_dest_struct + structures. */ +#define _buffer buffer12 + +/* Image I/O functions (cdjpeg.h) */ +#define _jinit_read_gif j12init_read_gif +#define _jinit_write_gif j12init_write_gif +#define _jinit_read_ppm j12init_read_ppm +#define _jinit_write_ppm j12init_write_ppm + +#define _read_color_map read_color_map_12 + +#else /* BITS_IN_JSAMPLE */ + +/* Sample data types and macros (jmorecfg.h) */ +#define _JSAMPLE JSAMPLE + +#define _MAXJSAMPLE MAXJSAMPLE +#define _CENTERJSAMPLE CENTERJSAMPLE + +#define _JSAMPROW JSAMPROW +#define _JSAMPARRAY JSAMPARRAY +#define _JSAMPIMAGE JSAMPIMAGE + +/* External functions (jpeglib.h) */ +#define _jpeg_write_scanlines jpeg_write_scanlines +#define _jpeg_write_raw_data jpeg_write_raw_data +#define _jpeg_read_scanlines jpeg_read_scanlines +#define _jpeg_skip_scanlines jpeg_skip_scanlines +#define _jpeg_crop_scanline jpeg_crop_scanline +#define _jpeg_read_raw_data jpeg_read_raw_data + +/* Internal methods (jpegint.h) */ + +/* Use the 8-bit method in the jpeg_c_main_controller structure. */ +#define _process_data process_data +/* Use the 8-bit method in the jpeg_c_prep_controller structure. */ +#define _pre_process_data pre_process_data +/* Use the 8-bit method in the jpeg_c_coef_controller structure. */ +#define _compress_data compress_data +/* Use the 8-bit method in the jpeg_color_converter structure. */ +#define _color_convert color_convert +/* Use the 8-bit method in the jpeg_downsampler structure. */ +#define _downsample downsample +/* Use the 8-bit method in the jpeg_forward_dct structure. */ +#define _forward_DCT forward_DCT +/* Use the 8-bit method in the jpeg_d_main_controller structure. */ +#define _process_data process_data +/* Use the 8-bit method in the jpeg_d_coef_controller structure. */ +#define _decompress_data decompress_data +/* Use the 8-bit method in the jpeg_d_post_controller structure. */ +#define _post_process_data post_process_data +/* Use the 8-bit method in the jpeg_inverse_dct structure. */ +#define _inverse_DCT_method_ptr inverse_DCT_method_ptr +#define _inverse_DCT inverse_DCT +/* Use the 8-bit method in the jpeg_upsampler structure. */ +#define _upsample upsample +/* Use the 8-bit method in the jpeg_color_converter structure. */ +#define _color_convert color_convert +/* Use the 8-bit method in the jpeg_color_quantizer structure. */ +#define _color_quantize color_quantize + +/* Global internal functions (jpegint.h) */ +#define _jinit_c_main_controller jinit_c_main_controller +#define _jinit_c_prep_controller jinit_c_prep_controller +#define _jinit_c_coef_controller jinit_c_coef_controller +#define _jinit_color_converter jinit_color_converter +#define _jinit_downsampler jinit_downsampler +#define _jinit_forward_dct jinit_forward_dct +#ifdef C_LOSSLESS_SUPPORTED +#define _jinit_c_diff_controller jinit_c_diff_controller +#define _jinit_lossless_compressor jinit_lossless_compressor +#endif + +#define _jinit_d_main_controller jinit_d_main_controller +#define _jinit_d_coef_controller jinit_d_coef_controller +#define _jinit_d_post_controller jinit_d_post_controller +#define _jinit_inverse_dct jinit_inverse_dct +#define _jinit_upsampler jinit_upsampler +#define _jinit_color_deconverter jinit_color_deconverter +#define _jinit_1pass_quantizer jinit_1pass_quantizer +#define _jinit_2pass_quantizer jinit_2pass_quantizer +#define _jinit_merged_upsampler jinit_merged_upsampler +#ifdef D_LOSSLESS_SUPPORTED +#define _jinit_d_diff_controller jinit_d_diff_controller +#define _jinit_lossless_decompressor jinit_lossless_decompressor +#endif + +#define _jcopy_sample_rows jcopy_sample_rows + +/* Global internal functions (jdct.h) */ +#define _jpeg_fdct_islow jpeg_fdct_islow +#define _jpeg_fdct_ifast jpeg_fdct_ifast + +#define _jpeg_idct_islow jpeg_idct_islow +#define _jpeg_idct_ifast jpeg_idct_ifast +#define _jpeg_idct_float jpeg_idct_float +#define _jpeg_idct_7x7 jpeg_idct_7x7 +#define _jpeg_idct_6x6 jpeg_idct_6x6 +#define _jpeg_idct_5x5 jpeg_idct_5x5 +#define _jpeg_idct_4x4 jpeg_idct_4x4 +#define _jpeg_idct_3x3 jpeg_idct_3x3 +#define _jpeg_idct_2x2 jpeg_idct_2x2 +#define _jpeg_idct_1x1 jpeg_idct_1x1 +#define _jpeg_idct_9x9 jpeg_idct_9x9 +#define _jpeg_idct_10x10 jpeg_idct_10x10 +#define _jpeg_idct_11x11 jpeg_idct_11x11 +#define _jpeg_idct_12x12 jpeg_idct_12x12 +#define _jpeg_idct_13x13 jpeg_idct_13x13 +#define _jpeg_idct_14x14 jpeg_idct_14x14 +#define _jpeg_idct_15x15 jpeg_idct_15x15 +#define _jpeg_idct_16x16 jpeg_idct_16x16 + +/* Internal fields (cdjpeg.h) */ + +/* Use the 8-bit buffer in the cjpeg_source_struct and djpeg_dest_struct + structures. */ +#define _buffer buffer + +/* Image I/O functions (cdjpeg.h) */ +#define _jinit_read_gif jinit_read_gif +#define _jinit_write_gif jinit_write_gif +#define _jinit_read_ppm jinit_read_ppm +#define _jinit_write_ppm jinit_write_ppm + +#define _read_color_map read_color_map + +#endif /* BITS_IN_JSAMPLE */ + +#endif /* JSAMPLECOMP_H */ diff --git a/jsimd.h b/jsimd.h index 6c20365..6ae021a 100644 --- a/jsimd.h +++ b/jsimd.h @@ -2,8 +2,8 @@ * jsimd.h * * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2011, 2014, D. R. Commander. - * Copyright (C) 2015-2016, 2018, Matthieu Darbois. + * Copyright (C) 2011, 2014, 2022, D. R. Commander. + * Copyright (C) 2015-2016, 2018, 2022, Matthieu Darbois. * Copyright (C) 2020, Arm Limited. * * Based on the x86 SIMD extension for IJG JPEG library, @@ -12,6 +12,8 @@ * */ +#ifdef WITH_SIMD + #include "jchuff.h" /* Declarations shared with jcphuff.c */ EXTERN(int) jsimd_can_rgb_ycc(void); @@ -114,10 +116,12 @@ EXTERN(int) jsimd_can_encode_mcu_AC_first_prepare(void); EXTERN(void) jsimd_encode_mcu_AC_first_prepare (const JCOEF *block, const int *jpeg_natural_order_start, int Sl, int Al, - JCOEF *values, size_t *zerobits); + UJCOEF *values, size_t *zerobits); EXTERN(int) jsimd_can_encode_mcu_AC_refine_prepare(void); EXTERN(int) jsimd_encode_mcu_AC_refine_prepare (const JCOEF *block, const int *jpeg_natural_order_start, int Sl, int Al, - JCOEF *absvalues, size_t *bits); + UJCOEF *absvalues, size_t *bits); + +#endif /* WITH_SIMD */ diff --git a/jsimd_none.c b/jsimd_none.c deleted file mode 100644 index 5b38a9f..0000000 --- a/jsimd_none.c +++ /dev/null @@ -1,431 +0,0 @@ -/* - * jsimd_none.c - * - * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2009-2011, 2014, D. R. Commander. - * Copyright (C) 2015-2016, 2018, Matthieu Darbois. - * Copyright (C) 2020, Arm Limited. - * - * Based on the x86 SIMD extension for IJG JPEG library, - * Copyright (C) 1999-2006, MIYASAKA Masaru. - * For conditions of distribution and use, see copyright notice in jsimdext.inc - * - * This file contains stubs for when there is no SIMD support available. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jsimd.h" -#include "jdct.h" -#include "jsimddct.h" - -GLOBAL(int) -jsimd_can_rgb_ycc(void) -{ - return 0; -} - -GLOBAL(int) -jsimd_can_rgb_gray(void) -{ - return 0; -} - -GLOBAL(int) -jsimd_can_ycc_rgb(void) -{ - return 0; -} - -GLOBAL(int) -jsimd_can_ycc_rgb565(void) -{ - return 0; -} - -GLOBAL(int) -jsimd_c_can_null_convert(void) -{ - return 0; -} - -GLOBAL(void) -jsimd_rgb_ycc_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, - JSAMPIMAGE output_buf, JDIMENSION output_row, - int num_rows) -{ -} - -GLOBAL(void) -jsimd_rgb_gray_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, - JSAMPIMAGE output_buf, JDIMENSION output_row, - int num_rows) -{ -} - -GLOBAL(void) -jsimd_ycc_rgb_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, - int num_rows) -{ -} - -GLOBAL(void) -jsimd_ycc_rgb565_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION input_row, JSAMPARRAY output_buf, - int num_rows) -{ -} - -GLOBAL(void) -jsimd_c_null_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, - JSAMPIMAGE output_buf, JDIMENSION output_row, - int num_rows) -{ -} - -GLOBAL(int) -jsimd_can_h2v2_downsample(void) -{ - return 0; -} - -GLOBAL(int) -jsimd_can_h2v1_downsample(void) -{ - return 0; -} - -GLOBAL(int) -jsimd_can_h2v2_smooth_downsample(void) -{ - return 0; -} - -GLOBAL(void) -jsimd_h2v2_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY output_data) -{ -} - -GLOBAL(void) -jsimd_h2v2_smooth_downsample(j_compress_ptr cinfo, - jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY output_data) -{ -} - -GLOBAL(void) -jsimd_h2v1_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY output_data) -{ -} - -GLOBAL(int) -jsimd_can_h2v2_upsample(void) -{ - return 0; -} - -GLOBAL(int) -jsimd_can_h2v1_upsample(void) -{ - return 0; -} - -GLOBAL(int) -jsimd_can_int_upsample(void) -{ - return 0; -} - -GLOBAL(void) -jsimd_int_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) -{ -} - -GLOBAL(void) -jsimd_h2v2_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) -{ -} - -GLOBAL(void) -jsimd_h2v1_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) -{ -} - -GLOBAL(int) -jsimd_can_h2v2_fancy_upsample(void) -{ - return 0; -} - -GLOBAL(int) -jsimd_can_h2v1_fancy_upsample(void) -{ - return 0; -} - -GLOBAL(int) -jsimd_can_h1v2_fancy_upsample(void) -{ - return 0; -} - -GLOBAL(void) -jsimd_h2v2_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) -{ -} - -GLOBAL(void) -jsimd_h2v1_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) -{ -} - -GLOBAL(void) -jsimd_h1v2_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) -{ -} - -GLOBAL(int) -jsimd_can_h2v2_merged_upsample(void) -{ - return 0; -} - -GLOBAL(int) -jsimd_can_h2v1_merged_upsample(void) -{ - return 0; -} - -GLOBAL(void) -jsimd_h2v2_merged_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf) -{ -} - -GLOBAL(void) -jsimd_h2v1_merged_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf) -{ -} - -GLOBAL(int) -jsimd_can_convsamp(void) -{ - return 0; -} - -GLOBAL(int) -jsimd_can_convsamp_float(void) -{ - return 0; -} - -GLOBAL(void) -jsimd_convsamp(JSAMPARRAY sample_data, JDIMENSION start_col, - DCTELEM *workspace) -{ -} - -GLOBAL(void) -jsimd_convsamp_float(JSAMPARRAY sample_data, JDIMENSION start_col, - FAST_FLOAT *workspace) -{ -} - -GLOBAL(int) -jsimd_can_fdct_islow(void) -{ - return 0; -} - -GLOBAL(int) -jsimd_can_fdct_ifast(void) -{ - return 0; -} - -GLOBAL(int) -jsimd_can_fdct_float(void) -{ - return 0; -} - -GLOBAL(void) -jsimd_fdct_islow(DCTELEM *data) -{ -} - -GLOBAL(void) -jsimd_fdct_ifast(DCTELEM *data) -{ -} - -GLOBAL(void) -jsimd_fdct_float(FAST_FLOAT *data) -{ -} - -GLOBAL(int) -jsimd_can_quantize(void) -{ - return 0; -} - -GLOBAL(int) -jsimd_can_quantize_float(void) -{ - return 0; -} - -GLOBAL(void) -jsimd_quantize(JCOEFPTR coef_block, DCTELEM *divisors, DCTELEM *workspace) -{ -} - -GLOBAL(void) -jsimd_quantize_float(JCOEFPTR coef_block, FAST_FLOAT *divisors, - FAST_FLOAT *workspace) -{ -} - -GLOBAL(int) -jsimd_can_idct_2x2(void) -{ - return 0; -} - -GLOBAL(int) -jsimd_can_idct_4x4(void) -{ - return 0; -} - -GLOBAL(int) -jsimd_can_idct_6x6(void) -{ - return 0; -} - -GLOBAL(int) -jsimd_can_idct_12x12(void) -{ - return 0; -} - -GLOBAL(void) -jsimd_idct_2x2(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) -{ -} - -GLOBAL(void) -jsimd_idct_4x4(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) -{ -} - -GLOBAL(void) -jsimd_idct_6x6(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) -{ -} - -GLOBAL(void) -jsimd_idct_12x12(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) -{ -} - -GLOBAL(int) -jsimd_can_idct_islow(void) -{ - return 0; -} - -GLOBAL(int) -jsimd_can_idct_ifast(void) -{ - return 0; -} - -GLOBAL(int) -jsimd_can_idct_float(void) -{ - return 0; -} - -GLOBAL(void) -jsimd_idct_islow(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) -{ -} - -GLOBAL(void) -jsimd_idct_ifast(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) -{ -} - -GLOBAL(void) -jsimd_idct_float(j_decompress_ptr cinfo, jpeg_component_info *compptr, - JCOEFPTR coef_block, JSAMPARRAY output_buf, - JDIMENSION output_col) -{ -} - -GLOBAL(int) -jsimd_can_huff_encode_one_block(void) -{ - return 0; -} - -GLOBAL(JOCTET *) -jsimd_huff_encode_one_block(void *state, JOCTET *buffer, JCOEFPTR block, - int last_dc_val, c_derived_tbl *dctbl, - c_derived_tbl *actbl) -{ - return NULL; -} - -GLOBAL(int) -jsimd_can_encode_mcu_AC_first_prepare(void) -{ - return 0; -} - -GLOBAL(void) -jsimd_encode_mcu_AC_first_prepare(const JCOEF *block, - const int *jpeg_natural_order_start, int Sl, - int Al, JCOEF *values, size_t *zerobits) -{ -} - -GLOBAL(int) -jsimd_can_encode_mcu_AC_refine_prepare(void) -{ - return 0; -} - -GLOBAL(int) -jsimd_encode_mcu_AC_refine_prepare(const JCOEF *block, - const int *jpeg_natural_order_start, int Sl, - int Al, JCOEF *absvalues, size_t *bits) -{ - return 0; -} diff --git a/jutils.c b/jutils.c index d862716..24caac1 100644 --- a/jutils.c +++ b/jutils.c @@ -17,8 +17,11 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" +#include "jsamplecomp.h" +#if BITS_IN_JSAMPLE == 8 + /* * jpeg_zigzag_order[i] is the zigzag-order position of the i'th element * of a DCT block read in natural order (left to right, top to bottom). @@ -89,19 +92,24 @@ jround_up(long a, long b) return a - (a % b); } +#endif /* BITS_IN_JSAMPLE == 8 */ + + +#if BITS_IN_JSAMPLE != 16 || \ + defined(C_LOSSLESS_SUPPORTED) || defined(D_LOSSLESS_SUPPORTED) GLOBAL(void) -jcopy_sample_rows(JSAMPARRAY input_array, int source_row, - JSAMPARRAY output_array, int dest_row, int num_rows, - JDIMENSION num_cols) +_jcopy_sample_rows(_JSAMPARRAY input_array, int source_row, + _JSAMPARRAY output_array, int dest_row, int num_rows, + JDIMENSION num_cols) /* Copy some rows of samples from one place to another. * num_rows rows are copied from input_array[source_row++] * to output_array[dest_row++]; these areas may overlap for duplication. * The source and destination arrays must be at least as wide as num_cols. */ { - register JSAMPROW inptr, outptr; - register size_t count = (size_t)(num_cols * sizeof(JSAMPLE)); + register _JSAMPROW inptr, outptr; + register size_t count = (size_t)(num_cols * sizeof(_JSAMPLE)); register int row; input_array += source_row; @@ -114,6 +122,11 @@ jcopy_sample_rows(JSAMPARRAY input_array, int source_row, } } +#endif /* BITS_IN_JSAMPLE != 16 || + defined(C_LOSSLESS_SUPPORTED) || defined(D_LOSSLESS_SUPPORTED) */ + + +#if BITS_IN_JSAMPLE == 8 GLOBAL(void) jcopy_block_row(JBLOCKROW input_row, JBLOCKROW output_row, @@ -131,3 +144,5 @@ jzero_far(void *target, size_t bytestozero) { memset(target, 0, bytestozero); } + +#endif /* BITS_IN_JSAMPLE == 8 */ diff --git a/jversion.h.in b/jversion.h.in index dca4f08..42a4e73 100644 --- a/jversion.h.in +++ b/jversion.h.in @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-2020, Thomas G. Lane, Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2010, 2012-2022, D. R. Commander. + * Copyright (C) 2010, 2012-2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -37,7 +37,7 @@ */ #define JCOPYRIGHT \ - "Copyright (C) 2009-2022 D. R. Commander\n" \ + "Copyright (C) 2009-2023 D. R. Commander\n" \ "Copyright (C) 2015, 2020 Google, Inc.\n" \ "Copyright (C) 2019-2020 Arm Limited\n" \ "Copyright (C) 2015-2016, 2018 Matthieu Darbois\n" \ @@ -48,6 +48,7 @@ "Copyright (C) 2009, 2012 Pierre Ossman for Cendio AB\n" \ "Copyright (C) 2009-2011 Nokia Corporation and/or its subsidiary(-ies)\n" \ "Copyright (C) 1999-2006 MIYASAKA Masaru\n" \ + "Copyright (C) 1999 Ken Murchison\n" \ "Copyright (C) 1991-2020 Thomas G. Lane, Guido Vollbeding" #define JCOPYRIGHT_SHORT \ diff --git a/libjpeg.txt b/libjpeg.txt index 309f9d3..2dae2d2 100644 --- a/libjpeg.txt +++ b/libjpeg.txt @@ -2,8 +2,10 @@ USING THE IJG JPEG LIBRARY This file was part of the Independent JPEG Group's software: Copyright (C) 1994-2013, Thomas G. Lane, Guido Vollbeding. +Lossless JPEG Modifications: +Copyright (C) 1999, Ken Murchison. libjpeg-turbo Modifications: -Copyright (C) 2010, 2014-2018, 2020, 2022, D. R. Commander. +Copyright (C) 2010, 2014-2018, 2020, 2022-2023, D. R. Commander. Copyright (C) 2015, Google, Inc. For conditions of distribution and use, see the accompanying README.ijg file. @@ -11,8 +13,8 @@ For conditions of distribution and use, see the accompanying README.ijg file. This file describes how to use the IJG JPEG library within an application program. Read it if you want to write a program that uses the library. -The file example.txt provides heavily commented skeleton code for calling the -JPEG library. Also see jpeglib.h (the include file to be used by application +The file example.c provides heavily commented code for calling the JPEG +library. Also see jpeglib.h (the include file to be used by application programs) for full details about data structures and function parameter lists. The library source code, of course, is the ultimate reference. @@ -29,6 +31,7 @@ TABLE OF CONTENTS Overview: Functions provided by the library + 12-bit and 16-bit Data Precision Outline of typical usage Basic library usage: Data formats @@ -96,12 +99,10 @@ the ISO JPEG standard; most baseline, extended-sequential, and progressive JPEG processes are supported. (Our subset includes all features now in common use.) Unsupported ISO options include: * Hierarchical storage - * Lossless JPEG * DNL marker * Nonintegral subsampling ratios -We support both 8- and 12-bit data precision, but this is a compile-time -choice rather than a run-time choice; hence it is difficult to use both -precisions in a single application. +We support 8-bit (lossy and lossless), 12-bit (lossy and lossless), and 16-bit +(lossless) data precision. By itself, the library handles only interchange JPEG datastreams --- in particular the widely used JFIF file format. The library can be used by @@ -110,6 +111,58 @@ are embedded in more complex file formats. (For example, this library is used by the free LIBTIFF library to support JPEG compression in TIFF.) +12-bit and 16-bit Data Precision +-------------------------------- + +The JPEG standard provides for baseline 8-bit and 12-bit DCT processes as well +as 8-bit, 12-bit, and 16-bit lossless (predictive) processes. This code +supports 12-bit-per-component lossy or lossless JPEG if you set +cinfo->data_precision to 12 and 16-bit-per-component lossless JPEG if you set +cinfo->data_precision to 16. Note that this causes the sample size to be +larger than a char, so it affects the surrounding application's image data. +The sample applications cjpeg and djpeg can support 12-bit mode only for PPM, +PGM, and GIF file formats and 16-bit mode only for PPM and PGM file formats. + +Note that, when 12-bit data precision is enabled, the library always compresses +in Huffman optimization mode, in order to generate valid Huffman tables. This +is necessary because our default Huffman tables only cover 8-bit data. If you +need to output 12-bit files in one pass, you'll have to supply suitable default +Huffman tables. You may also want to supply your own DCT quantization tables; +the existing quality-scaling code has been developed for 8-bit use, and +probably doesn't generate especially good tables for 12-bit. + +Functions that are specific to 12-bit data precision have a prefix of "jpeg12_" +instead of "jpeg_" and use the following data types and macros: + + * J12SAMPLE instead of JSAMPLE + * J12SAMPROW instead of JSAMPROW + * J12SAMPARRAY instead of JSAMPARRAY + * J12SAMPIMAGE instead of JSAMPIMAGE + * MAXJ12SAMPLE instead of MAXJSAMPLE + * CENTERJ12SAMPLE instead of CENTERJSAMPLE + +Functions that are specific to 16-bit data precision have a prefix of "jpeg16_" +instead of "jpeg_" and use the following data types and macros: + + * J16SAMPLE instead of JSAMPLE + * J16SAMPROW instead of JSAMPROW + * J16SAMPARRAY instead of JSAMPARRAY + * J16SAMPIMAGE instead of JSAMPIMAGE + * MAXJ16SAMPLE instead of MAXJSAMPLE + * CENTERJ16SAMPLE instead of CENTERJSAMPLE + +This allows 8-bit, 12-bit, and 16-bit data precision to be used in a single +application. (Refer to example.c). Arithmetic coding and SIMD acceleration +are not currently implemented for 12-bit data precision, nor are they +implemented for lossless mode with any data precision. + +Refer to the descriptions of the data_precision compression and decompression +parameters below for further information. + +This documentation uses "J*SAMPLE", "J*SAMPROW", "J*SAMPARRAY", and +"J*SAMPIMAGE" to generically refer to the 8-bit, 12-bit, or 16-bit data types. + + Outline of typical usage ------------------------ @@ -120,7 +173,10 @@ The rough outline of a JPEG compression operation is: Set parameters for compression, including image size & colorspace jpeg_start_compress(...); while (scan lines remain to be written) - jpeg_write_scanlines(...); + jpeg_write_scanlines(...); /* Use jpeg12_write_scanlines() for + 12-bit data precision and + jpeg16_write_scanlines() for + 16-bit data precision. */ jpeg_finish_compress(...); Release the JPEG compression object @@ -132,7 +188,7 @@ same parameter settings for a sequence of images. Re-use of a JPEG object also has important implications for processing abbreviated JPEG datastreams, as discussed later. -The image data to be compressed is supplied to jpeg_write_scanlines() from +The image data to be compressed is supplied to jpeg*_write_scanlines() from in-memory buffers. If the application is doing file-to-file compression, reading image data from the source file is the application's responsibility. The library emits compressed data by calling a "data destination manager", @@ -147,7 +203,10 @@ Similarly, the rough outline of a JPEG decompression operation is: Set parameters for decompression jpeg_start_decompress(...); while (scan lines remain to be read) - jpeg_read_scanlines(...); + jpeg_read_scanlines(...); /* Use jpeg12_read_scanlines() for + 12-bit data precision and + jpeg16_read_scanlines() for + 16-bit data precision. */ jpeg_finish_decompress(...); Release the JPEG decompression object @@ -160,7 +219,7 @@ output scaling ratio that will fit the image into the available screen size. The decompression library obtains compressed data by calling a data source manager, which typically will read the data from a file; but other behaviors can be obtained with a custom source manager. Decompressed data is delivered -into in-memory buffers passed to jpeg_read_scanlines(). +into in-memory buffers passed to jpeg*_read_scanlines(). It is possible to abort an incomplete compression or decompression operation by calling jpeg_abort(); or, if you do not need to retain the JPEG object, @@ -208,16 +267,16 @@ and the other references mentioned in the README.ijg file. Pixels are stored by scanlines, with each scanline running from left to right. The component values for each pixel are adjacent in the row; for example, R,G,B,R,G,B,R,G,B,... for 24-bit RGB color. Each scanline is an -array of data type JSAMPLE --- which is typically "unsigned char", unless -you've changed jmorecfg.h. (You can also change the RGB pixel layout, say -to B,G,R order, by modifying jmorecfg.h. But see the restrictions listed in -that file before doing so.) +array of data type JSAMPLE or J12SAMPLE --- which is typically "unsigned char" +or "short" (respectively), unless you've changed jmorecfg.h. (You can also +change the RGB pixel layout, say to B,G,R order, by modifying jmorecfg.h. But +see the restrictions listed in that file before doing so.) A 2-D array of pixels is formed by making a list of pointers to the starts of scanlines; so the scanlines need not be physically adjacent in memory. Even if you process just one scanline at a time, you must make a one-element -pointer array to conform to this structure. Pointers to JSAMPLE rows are of -type JSAMPROW, and the pointer to the pointer array is of type JSAMPARRAY. +pointer array to conform to this structure. Pointers to J*SAMPLE rows are of +type J*SAMPROW, and the pointer to the pointer array is of type J*SAMPARRAY. The library accepts or supplies one or more complete scanlines per call. It is not possible to process part of a row at a time. Scanlines are always @@ -226,24 +285,24 @@ have it all in memory, but usually it's simplest to process one scanline at a time. For best results, source data values should have the precision specified by -BITS_IN_JSAMPLE (normally 8 bits). For instance, if you choose to compress -data that's only 6 bits/channel, you should left-justify each value in a -byte before passing it to the compressor. If you need to compress data -that has more than 8 bits/channel, compile with BITS_IN_JSAMPLE = 12. -(See "Library compile-time options", later.) +cinfo->data_precision (normally 8 bits). For instance, if you choose to +compress data that's only 6 bits/channel, you should left-justify each value in +a byte before passing it to the compressor. If you need to compress data +that has more than 8 bits/channel, set cinfo->data_precision = 12 or 16. The data format returned by the decompressor is the same in all details, except that colormapped output is supported. (Again, a JPEG file is never colormapped. But you can ask the decompressor to perform on-the-fly color quantization to deliver colormapped output.) If you request colormapped -output then the returned data array contains a single JSAMPLE per pixel; +output then the returned data array contains a single J*SAMPLE per pixel; its value is an index into a color map. The color map is represented as -a 2-D JSAMPARRAY in which each row holds the values of one color component, +a 2-D J*SAMPARRAY in which each row holds the values of one color component, that is, colormap[i][j] is the value of the i'th color component for pixel value (map index) j. Note that since the colormap indexes are stored in -JSAMPLEs, the maximum number of colors is limited by the size of JSAMPLE -(ie, at most 256 colors for an 8-bit JPEG library). +J*SAMPLEs, the maximum number of colors is limited by the size of J*SAMPLE +(ie, at most 256 colors for 8-bit data precision, 4096 colors for 12-bit data +precision, and 65536 colors for 16-bit data precision). Compression details @@ -380,9 +439,12 @@ the compression cycle. 5. while (scan lines remain to be written) - jpeg_write_scanlines(...); + jpeg_write_scanlines(...); /* Use jpeg12_write_scanlines() for 12-bit + data precision and + jpeg16_write_scanlines() for 16-bit data + precision. */ -Now write all the required image data by calling jpeg_write_scanlines() +Now write all the required image data by calling jpeg*_write_scanlines() one or more times. You can pass one or more scanlines in each call, up to the total image height. In most applications it is convenient to pass just one or a few scanlines at a time. The expected format for the passed @@ -402,20 +464,24 @@ this variable as the loop counter, so that the loop test looks like "while (cinfo.next_scanline < cinfo.image_height)". Code for this step depends heavily on the way that you store the source data. -example.txt shows the following code for the case of a full-size 2-D source +example.c shows the following code for the case of a full-size 2-D source array containing 3-byte RGB pixels: - JSAMPROW row_pointer[1]; /* pointer to a single row */ - int row_stride; /* physical row width in buffer */ - - row_stride = image_width * 3; /* JSAMPLEs per row in image_buffer */ + JSAMPROW row_pointer[1]; /* pointer to a single row + Use J12SAMPROW for 12-bit data + precision and J16SAMPROW for 16-bit + data precision. */ while (cinfo.next_scanline < cinfo.image_height) { - row_pointer[0] = &image_buffer[cinfo.next_scanline * row_stride]; + row_pointer[0] = image_buffer[cinfo.next_scanline]; jpeg_write_scanlines(&cinfo, row_pointer, 1); + /* Use jpeg12_write_scanlines() for + 12-bit data precision and + jpeg16_write_scanlines() for 16-bit + data precision. */ } -jpeg_write_scanlines() returns the number of scanlines actually written. +jpeg*_write_scanlines() returns the number of scanlines actually written. This will normally be equal to the number passed in, so you can usually ignore the return value. It is different in just two cases: * If you try to write more scanlines than the declared image height, @@ -640,11 +706,11 @@ colormapped output has been requested. Useful fields include actual_number_of_colors number of entries in colormap output_components is 1 (a colormap index) when quantizing colors; otherwise it -equals out_color_components. It is the number of JSAMPLE values that will be +equals out_color_components. It is the number of J*SAMPLE values that will be emitted per pixel in the output arrays. Typically you will need to allocate data buffers to hold the incoming image. -You will need output_width * output_components JSAMPLEs per scanline in your +You will need output_width * output_components J*SAMPLEs per scanline in your output buffer, and a total of output_height scanlines will be returned. Note: if you are using the JPEG library's internal memory manager to allocate @@ -656,11 +722,14 @@ relevant parameters (scaling, output color space, and quantization flag). 6. while (scan lines remain to be read) - jpeg_read_scanlines(...); + jpeg_read_scanlines(...); /* Use jpeg12_read_scanlines() for 12-bit + data precision and + jpeg16_read_scanlines() for 16-bit data + precision. */ -Now you can read the decompressed image data by calling jpeg_read_scanlines() +Now you can read the decompressed image data by calling jpeg*_read_scanlines() one or more times. At each call, you pass in the maximum number of scanlines -to be read (ie, the height of your working buffer); jpeg_read_scanlines() +to be read (ie, the height of your working buffer); jpeg*_read_scanlines() will return up to that many lines. The return value is the number of lines actually read. The format of the returned data is discussed under "Data formats", above. Don't forget that grayscale and color JPEGs will return @@ -680,13 +749,13 @@ image_height field is the height of the original unscaled image.) The return value always equals the change in the value of output_scanline. If you don't use a suspending data source, it is safe to assume that -jpeg_read_scanlines() reads at least one scanline per call, until the +jpeg*_read_scanlines() reads at least one scanline per call, until the bottom of the image has been reached. If you use a buffer larger than one scanline, it is NOT safe to assume that -jpeg_read_scanlines() fills it. (The current implementation returns only a +jpeg*_read_scanlines() fills it. (The current implementation returns only a few scanlines per call, no matter how large a buffer you pass.) So you must -always provide a loop that calls jpeg_read_scanlines() repeatedly until the +always provide a loop that calls jpeg*_read_scanlines() repeatedly until the whole image has been read. @@ -744,33 +813,34 @@ partial image decompression: 1. Skipping rows when decompressing jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines); + /* Use jpeg12_skip_scanlines() for 12-bit data precision. */ This function provides application programmers with the ability to skip over multiple rows in the JPEG image. Suspending data sources are not supported by this function. Calling -jpeg_skip_scanlines() with a suspending data source will result in undefined +jpeg*_skip_scanlines() with a suspending data source will result in undefined behavior. Two-pass color quantization is also not supported by this function. -Calling jpeg_skip_scanlines() with two-pass color quantization enabled will +Calling jpeg*_skip_scanlines() with two-pass color quantization enabled will result in an error. -jpeg_skip_scanlines() will not allow skipping past the bottom of the image. If -the value of num_lines is large enough to skip past the bottom of the image, +jpeg*_skip_scanlines() will not allow skipping past the bottom of the image. +If the value of num_lines is large enough to skip past the bottom of the image, then the function will skip to the end of the image instead. -If the value of num_lines is valid, then jpeg_skip_scanlines() will always +If the value of num_lines is valid, then jpeg*_skip_scanlines() will always skip all of the input rows requested. There is no need to inspect the return value of the function in that case. -Best results will be achieved by calling jpeg_skip_scanlines() for large chunks -of rows. The function should be viewed as a way to quickly jump to a +Best results will be achieved by calling jpeg*_skip_scanlines() for large +chunks of rows. The function should be viewed as a way to quickly jump to a particular vertical offset in the JPEG image in order to decode a subset of the image. Used in this manner, it will provide significant performance improvements. -Calling jpeg_skip_scanlines() for small values of num_lines has several +Calling jpeg*_skip_scanlines() for small values of num_lines has several potential drawbacks: - 1) JPEG decompression occurs in blocks, so if jpeg_skip_scanlines() is + 1) JPEG decompression occurs in blocks, so if jpeg*_skip_scanlines() is called from the middle of a decompression block, then it is likely that much of the decompression work has already been done for the first couple of rows that need to be skipped. @@ -778,18 +848,19 @@ potential drawbacks: such that it is ready to read the next line. This may involve decompressing a block that must be partially skipped. These issues are especially tricky for cases in which upsampling requires -context rows. In the worst case, jpeg_skip_scanlines() will perform similarly -to jpeg_read_scanlines() (since it will actually call jpeg_read_scanlines().) +context rows. In the worst case, jpeg*_skip_scanlines() will perform similarly +to jpeg*_read_scanlines() (since it will actually call jpeg*_read_scanlines().) 2. Decompressing partial scanlines jpeg_crop_scanline (j_decompress_ptr cinfo, JDIMENSION *xoffset, JDIMENSION *width) + /* Use jpeg12_crop_scanline() for 12-bit data precision. */ This function provides application programmers with the ability to decompress only a portion of each row in the JPEG image. It must be called after -jpeg_start_decompress() and before any calls to jpeg_read_scanlines() or -jpeg_skip_scanlines(). +jpeg_start_decompress() and before any calls to jpeg*_read_scanlines() or +jpeg*_skip_scanlines(). If xoffset and width do not form a valid subset of the image row, then this function will generate an error. Note that if the output image is scaled, then @@ -799,12 +870,12 @@ xoffset and width are passed by reference because xoffset must fall on an iMCU boundary. If it doesn't, then it will be moved left to the nearest iMCU boundary, and width will be increased accordingly. If the calling program does not like the adjusted values of xoffset and width, then it can call -jpeg_crop_scanline() again with new values (for instance, if it wants to move +jpeg*_crop_scanline() again with new values (for instance, if it wants to move xoffset to the nearest iMCU boundary to the right instead of to the left.) After calling this function, cinfo->output_width will be set to the adjusted width. This value should be used when allocating an output buffer to pass to -jpeg_read_scanlines(). +jpeg*_read_scanlines(). The output image from a partial-width decompression will be identical to the corresponding image region from a full decode, with one exception: The "fancy" @@ -949,6 +1020,30 @@ jpeg_simple_progression (j_compress_ptr cinfo) unless you want to make a custom scan sequence. You must ensure that the JPEG color space is set correctly before calling this routine. +jpeg_enable_lossless (j_compress_ptr cinfo, int predictor_selection_value, + int point_transform) + Enables lossless mode with the specified predictor selection value + (1 - 7) and optional point transform (0 - {precision}-1, where + {precision} is the JPEG data precision). A point transform value of 0 + is necessary in order to create a fully lossless JPEG image. (A + non-zero point transform value right-shifts the input samples by the + specified number of bits, which is effectively a form of lossy color + quantization.) Note that the following features will be unavailable + when compressing or decompressing lossless JPEG images: + * Partial image decompression + * Quality/quantization table selection + * DCT/IDCT algorithm selection + * Smoothing + * Downsampling/upsampling + * Color space conversion (the JPEG image will use the same color + space as the input image) + * Color quantization + * IDCT scaling + * Raw (downsampled) data input/output + * Transcoding of DCT coefficients + Any parameters used to enable or configure those features will be + ignored. + Compression parameters (cinfo fields) include: @@ -956,6 +1051,15 @@ boolean arith_code If TRUE, use arithmetic coding. If FALSE, use Huffman coding. +int data_precision + To create a 12-bit-per-component JPEG file, set data_precision to 12 + prior to calling jpeg_start_compress() or using the memory manager, + then use jpeg12_write_scanlines() or jpeg12_write_raw_data() instead of + jpeg_write_scanlines() or jpeg_write_raw_data(). To create a + 16-bit-per-component lossless JPEG file, set data_precision to 16 prior + to calling jpeg_start_compress() or using the memory manager, then use + jpeg16_write_scanlines() instead of jpeg_write_scanlines(). + J_DCT_METHOD dct_method Selects the algorithm used for the DCT step. Choices are: JDCT_ISLOW: accurate integer method @@ -1010,15 +1114,15 @@ boolean optimize_coding unsigned int restart_interval int restart_in_rows To emit restart markers in the JPEG file, set one of these nonzero. - Set restart_interval to specify the exact interval in MCU blocks. - Set restart_in_rows to specify the interval in MCU rows. (If - restart_in_rows is not 0, then restart_interval is set after the - image width in MCUs is computed.) Defaults are zero (no restarts). - One restart marker per MCU row is often a good choice. - NOTE: the overhead of restart markers is higher in grayscale JPEG - files than in color files, and MUCH higher in progressive JPEGs. - If you use restarts, you may want to use larger intervals in those - cases. + Set restart_interval to specify the exact interval in MCU blocks + (samples in lossless mode). Set restart_in_rows to specify the + interval in MCU rows. (If restart_in_rows is not 0, then + restart_interval is set after the image width in MCUs is computed.) + Defaults are zero (no restarts). One restart marker per MCU row is + often a good choice. NOTE: the overhead of restart markers is higher + in grayscale JPEG files than in color files, and MUCH higher in + progressive JPEGs. If you use restarts, you may want to use larger + intervals in those cases. const jpeg_scan_info *scan_info int num_scans @@ -1176,6 +1280,14 @@ processing. The following fields in the JPEG object are set by jpeg_read_header() and may be useful to the application in choosing decompression parameters: +int data_precision Data precision (bits per component) + If data_precision is 12, then use jpeg12_read_scanlines(), + jpeg12_skip_scanlines(), jpeg12_crop_scanline(), and/or + jpeg12_read_raw_data() instead of jpeg_read_scanlines(), + jpeg_skip_scanlines(), jpeg_crop_scanline(), and/or + jpeg_read_raw_data(). If data_precision is 16, then use + jpeg16_read_scanlines() instead of jpeg_read_scanlines(). + JDIMENSION image_width Width and height of image JDIMENSION image_height int num_components Number of color components @@ -1259,6 +1371,9 @@ JSAMPARRAY colormap CAUTION: if the JPEG library creates its own colormap, the storage pointed to by this field is released by jpeg_finish_decompress(). Copy the colormap somewhere else first, if you want to save it. + CAUTION: if data_precision is 12 or 16, then this is actually a + J12SAMPARRAY or a J16SAMPARRAY, so it must be type-cast in order to + read/write 12-bit or 16-bit samples from/to the array. int actual_number_of_colors The number of colors in the color map. @@ -1338,10 +1453,10 @@ int rec_outbuf_height Recommended height of scanline buffer. When quantizing colors, output_components is 1, indicating a single color map index per pixel. Otherwise it equals out_color_components. The output arrays -are required to be output_width * output_components JSAMPLEs wide. +are required to be output_width * output_components J*SAMPLEs wide. rec_outbuf_height is the recommended minimum height (in scanlines) of the -buffer passed to jpeg_read_scanlines(). If the buffer is smaller, the +buffer passed to jpeg*_read_scanlines(). If the buffer is smaller, the library will still work, but time will be wasted due to unnecessary data copying. In high-quality modes, rec_outbuf_height is always 1, but some faster, lower-quality modes set it to larger values (typically 2 to 4). @@ -1445,7 +1560,7 @@ When the default error handler is used, any error detected inside the JPEG routines will cause a message to be printed on stderr, followed by exit(). You can supply your own error handling routines to override this behavior and to control the treatment of nonfatal warnings and trace/debug messages. -The file example.txt illustrates the most common case, which is to have the +The file example.c illustrates the most common case, which is to have the application regain control after an error rather than exiting. The JPEG library never writes any message directly; it always goes through @@ -1462,7 +1577,7 @@ You may, if you wish, simply replace the entire JPEG error handling module only replacing some of the routines depending on the behavior you need. This is accomplished by calling jpeg_std_error() as usual, but then overriding some of the method pointers in the jpeg_error_mgr struct, as illustrated by -example.txt. +example.c. All of the error handling routines will receive a pointer to the JPEG object (a j_common_ptr which points to either a jpeg_compress_struct or a @@ -1473,7 +1588,7 @@ additional data which is not known to the JPEG library or the standard error handler. The most convenient way to do this is to embed either the JPEG object or the jpeg_error_mgr struct in a larger structure that contains additional fields; then casting the passed pointer provides access to the -additional fields. Again, see example.txt for one way to do it. (Beginning +additional fields. Again, see example.c for one way to do it. (Beginning with IJG version 6b, there is also a void pointer "client_data" in each JPEG object, which the application can also use to find related data. The library does not touch client_data at all.) @@ -1725,10 +1840,10 @@ Compression suspension: For compression suspension, use an empty_output_buffer() routine that returns FALSE; typically it will not do anything else. This will cause the -compressor to return to the caller of jpeg_write_scanlines(), with the return +compressor to return to the caller of jpeg*_write_scanlines(), with the return value indicating that not all the supplied scanlines have been accepted. The application must make more room in the output buffer, adjust the output -buffer pointer/count appropriately, and then call jpeg_write_scanlines() +buffer pointer/count appropriately, and then call jpeg*_write_scanlines() again, pointing to the first unconsumed scanline. When forced to suspend, the compressor will backtrack to a convenient stopping @@ -1744,7 +1859,7 @@ for efficiency; you don't want the compressor to suspend often. (In fact, an overly small buffer could lead to infinite looping, if a single MCU required more data than would fit in the buffer.) We recommend a buffer of at least several Kbytes. You may want to insert explicit code to ensure that you don't -call jpeg_write_scanlines() unless there is a reasonable amount of space in +call jpeg*_write_scanlines() unless there is a reasonable amount of space in the output buffer; in other words, flush the buffer before trying to compress more data. @@ -1778,11 +1893,11 @@ This will cause the decompressor to return to its caller with an indication that suspension has occurred. This can happen at four places: * jpeg_read_header(): will return JPEG_SUSPENDED. * jpeg_start_decompress(): will return FALSE, rather than its usual TRUE. - * jpeg_read_scanlines(): will return the number of scanlines already + * jpeg*_read_scanlines(): will return the number of scanlines already completed (possibly 0). * jpeg_finish_decompress(): will return FALSE, rather than its usual TRUE. The surrounding application must recognize these cases, load more data into -the input buffer, and repeat the call. In the case of jpeg_read_scanlines(), +the input buffer, and repeat the call. In the case of jpeg*_read_scanlines(), increment the passed pointers past any scanlines successfully read. Just as with compression, the decompressor will typically backtrack to a @@ -1919,7 +2034,7 @@ progressive scan sequence design. (If you want to provide user control of scan sequences, you may wish to borrow the scan script reading code found in rdswitch.c, so that you can read scan script files just like cjpeg's.) When scan_info is not NULL, the compression library will store DCT'd data -into a buffer array as jpeg_write_scanlines() is called, and will emit all +into a buffer array as jpeg*_write_scanlines() is called, and will emit all the requested scans during jpeg_finish_compress(). This implies that multiple-scan output cannot be created with a suspending data destination manager, since jpeg_finish_compress() does not support suspension. We @@ -1967,7 +2082,10 @@ The basic control flow for buffered-image decoding is adjust output decompression parameters if required jpeg_start_output() /* start a new output pass */ for (all scanlines in image) { - jpeg_read_scanlines() + jpeg_read_scanlines() /* Use jpeg12_read_scanlines() for + 12-bit data precision and + jpeg16_read_scanlines() for 16-bit + data precision. */ display scanlines } jpeg_finish_output() /* terminate output pass */ @@ -1997,7 +2115,7 @@ input and output processing run in lockstep. After reading the final scan and reaching the end of the input file, the buffered image remains available; it can be read additional times by -repeating the jpeg_start_output()/jpeg_read_scanlines()/jpeg_finish_output() +repeating the jpeg_start_output()/jpeg*_read_scanlines()/jpeg_finish_output() sequence. For example, a useful technique is to use fast one-pass color quantization for display passes made while the image is arriving, followed by a final display pass using two-pass quantization for highest quality. This @@ -2161,7 +2279,7 @@ buffered-image mode must be prepared for suspension returns from these routines: * jpeg_start_output() performs input only if you request 2-pass quantization and the target scan isn't fully read yet. (This is discussed below.) -* jpeg_read_scanlines(), as always, returns the number of scanlines that it +* jpeg*_read_scanlines(), as always, returns the number of scanlines that it was able to produce before suspending. * jpeg_finish_output() will read any markers following the target scan, up to the end of the file or the SOS marker that begins another scan. @@ -2520,7 +2638,7 @@ Adobe markers and will set the JPEG colorspace properly when one is found. You can write special markers immediately following the datastream header by calling jpeg_write_marker() after jpeg_start_compress() and before the first -call to jpeg_write_scanlines(). When you do this, the markers appear after +call to jpeg*_write_scanlines(). When you do this, the markers appear after the SOI and the JFIF APP0 and Adobe APP14 markers (if written), but before all else. Specify the marker type parameter as "JPEG_COM" for COM or "JPEG_APP0 + n" for APPn. (Actually, jpeg_write_marker will let you write @@ -2662,9 +2780,9 @@ JPEG file while writing it, or to extract the profile data from a JPEG file while reading it. jpeg_write_icc_profile() must be called after calling jpeg_start_compress() and -before the first call to jpeg_write_scanlines() or jpeg_write_raw_data(). This -ordering ensures that the APP2 marker(s) will appear after the SOI and JFIF or -Adobe markers, but before all other data. +before the first call to jpeg*_write_scanlines() or jpeg*_write_raw_data(). +This ordering ensures that the APP2 marker(s) will appear after the SOI and +JFIF or Adobe markers, but before all other data. jpeg_read_icc_profile() returns TRUE if an ICC profile was found and FALSE otherwise. If an ICC profile was found, then the function will allocate a @@ -2700,8 +2818,8 @@ To compress raw data, you must supply the data in the colorspace to be used in the JPEG file (please read the earlier section on Special color spaces) and downsampled to the sampling factors specified in the JPEG parameters. You must supply the data in the format used internally by the JPEG library, -namely a JSAMPIMAGE array. This is an array of pointers to two-dimensional -arrays, each of type JSAMPARRAY. Each 2-D array holds the values for one +namely a J*SAMPIMAGE array. This is an array of pointers to two-dimensional +arrays, each of type J*SAMPARRAY. Each 2-D array holds the values for one color component. This structure is necessary since the components are of different sizes. If the image dimensions are not a multiple of the MCU size, you must also pad the data correctly (usually, this is done by replicating @@ -2713,8 +2831,9 @@ images, the standard image size is usually a multiple of the DCT block size, so that no padding need actually be done.) The procedure for compression of raw data is basically the same as normal -compression, except that you call jpeg_write_raw_data() in place of -jpeg_write_scanlines(). Before calling jpeg_start_compress(), you must do +compression, except that you call jpeg_write_raw_data() or +jpeg12_write_raw_data() in place of jpeg_write_scanlines() or +jpeg12_write_scanlines(). Before calling jpeg_start_compress(), you must do the following: * Set cinfo->raw_data_in to TRUE. (It is set FALSE by jpeg_set_defaults().) This notifies the library that you will be supplying raw data. @@ -2727,13 +2846,13 @@ the following: dimensions of the data you are supplying, it's wise to set them explicitly, rather than assuming the library's defaults are what you want. -To pass raw data to the library, call jpeg_write_raw_data() in place of -jpeg_write_scanlines(). The two routines work similarly except that -jpeg_write_raw_data takes a JSAMPIMAGE data array rather than JSAMPARRAY. -The scanlines count passed to and returned from jpeg_write_raw_data is +To pass raw data to the library, call jpeg*_write_raw_data() in place of +jpeg*_write_scanlines(). The routines work similarly except that +jpeg*_write_raw_data takes a J*SAMPIMAGE data array rather than J*SAMPARRAY. +The scanlines count passed to and returned from jpeg*_write_raw_data is measured in terms of the component with the largest v_samp_factor. -jpeg_write_raw_data() processes one MCU row per call, which is to say +jpeg*_write_raw_data() processes one MCU row per call, which is to say v_samp_factor*DCTSIZE sample rows of each component. The passed num_lines value must be at least max_v_samp_factor*DCTSIZE, and the return value will be exactly that amount (or possibly some multiple of that amount, in future @@ -2764,7 +2883,7 @@ downsampled_width = 51 and width_in_blocks = 7 for Cb and Cr (and the same for the height fields). You must pad the Y data to at least 13*8 = 104 columns and rows, the Cb/Cr data to at least 7*8 = 56 columns and rows. The MCU height is max_v_samp_factor = 2 DCT rows so you must pass at least 16 -scanlines on each call to jpeg_write_raw_data(), which is to say 16 actual +scanlines on each call to jpeg*_write_raw_data(), which is to say 16 actual sample rows of Y and 8 each of Cb and Cr. A total of 7 MCU rows are needed, so you must pass a total of 7*16 = 112 "scanlines". The last DCT block row of Y data is dummy, so it doesn't matter what you pass for it in the data @@ -2772,7 +2891,7 @@ arrays, but the scanlines count must total up to 112 so that all of the Cb and Cr data gets passed. Output suspension is supported with raw-data compression: if the data -destination module suspends, jpeg_write_raw_data() will return 0. +destination module suspends, jpeg*_write_raw_data() will return 0. In this case the same data rows must be passed again on the next call. @@ -2786,10 +2905,11 @@ The library will not convert to a different color space for you. To obtain raw data output, set cinfo->raw_data_out = TRUE before jpeg_start_decompress() (it is set FALSE by jpeg_read_header()). Be sure to verify that the color space and sampling factors are ones you can handle. -Then call jpeg_read_raw_data() in place of jpeg_read_scanlines(). The -decompression process is otherwise the same as usual. +Then call jpeg_read_raw_data() or jpeg12_read_raw_data() in place of +jpeg_read_scanlines() or jpeg12_read_scanlines(). The decompression process is +otherwise the same as usual. -jpeg_read_raw_data() returns one MCU row per call, and thus you must pass a +jpeg*_read_raw_data() returns one MCU row per call, and thus you must pass a buffer of at least max_v_samp_factor*DCTSIZE scanlines (scanline counting is the same as for raw-data compression). The buffer you pass must be large enough to hold the actual data plus padding to DCT-block boundaries. As with @@ -2799,7 +2919,7 @@ above example of computing buffer dimensions for raw-data compression is equally valid for decompression. Input suspension is supported with raw-data decompression: if the data source -module suspends, jpeg_read_raw_data() will return 0. You can also use +module suspends, jpeg*_read_raw_data() will return 0. You can also use buffered-image mode to read raw data in multiple passes. @@ -2814,7 +2934,7 @@ multi-strip or multi-tile TIFF/JPEG file into a single JPEG datastream, etc. To read the contents of a JPEG file as DCT coefficients, open the file and do jpeg_read_header() as usual. But instead of calling jpeg_start_decompress() -and jpeg_read_scanlines(), call jpeg_read_coefficients(). This will read the +and jpeg*_read_scanlines(), call jpeg_read_coefficients(). This will read the entire image into a set of virtual coefficient-block arrays, one array per component. The return value is a pointer to an array of virtual-array descriptors. Each virtual array can be accessed directly using the JPEG @@ -2855,7 +2975,7 @@ the DCT coefficients stored in virtual block arrays. You can either pass block arrays read from an input JPEG file by jpeg_read_coefficients(), or allocate virtual arrays from the JPEG compression object and fill them yourself. In either case, jpeg_write_coefficients() is substituted for -jpeg_start_compress() and jpeg_write_scanlines(). Thus the sequence is +jpeg_start_compress() and jpeg*_write_scanlines(). Thus the sequence is * Create compression object * Set all compression parameters as necessary * Request virtual arrays if needed @@ -2899,7 +3019,7 @@ Some applications may need to regain control from the JPEG library every so often. The typical use of this feature is to produce a percent-done bar or other progress display. (For a simple example, see cjpeg.c or djpeg.c.) Although you do get control back frequently during the data-transferring pass -(the jpeg_read_scanlines or jpeg_write_scanlines loop), any additional passes +(the jpeg*_read_scanlines or jpeg*_write_scanlines loop), any additional passes will occur inside jpeg_finish_compress or jpeg_start_decompress; those routines may take a long time to execute, and you don't get control back until they are done. @@ -2910,8 +3030,8 @@ so we don't recommend you use it for mouse tracking or anything like that. At present, a call will occur once per MCU row, scanline, or sample row group, whichever unit is convenient for the current processing mode; so the wider the image, the longer the time between calls. During the data -transferring pass, only one call occurs per call of jpeg_read_scanlines or -jpeg_write_scanlines, so don't pass a large number of scanlines at once if +transferring pass, only one call occurs per call of jpeg*_read_scanlines or +jpeg*_write_scanlines, so don't pass a large number of scanlines at once if you want fine resolution in the progress count. (If you really need to use the callback mechanism for time-critical tasks like mouse tracking, you could insert additional calls inside some of the library's inner loops.) @@ -3033,9 +3153,9 @@ This does not count any memory allocated by the application, such as a buffer to hold the final output image. The above figures are valid for 8-bit JPEG data precision and a machine with -32-bit ints. For 12-bit JPEG data, double the size of the strip buffers and -quantization pixel buffer. The "fixed-size" data will be somewhat smaller -with 16-bit ints, larger with 64-bit ints. Also, CMYK or other unusual +32-bit ints. For 12-bit and 16-bit JPEG data, double the size of the strip +buffers and quantization pixel buffer. The "fixed-size" data will be somewhat +smaller with 16-bit ints, larger with 64-bit ints. Also, CMYK or other unusual color spaces will require different amounts of space. The full-image coefficient and pixel buffers, if needed at all, do not @@ -3058,23 +3178,6 @@ Library compile-time options A number of compile-time options are available by modifying jmorecfg.h. -The JPEG standard provides for both the baseline 8-bit DCT process and -a 12-bit DCT process. The IJG code supports 12-bit lossy JPEG if you define -BITS_IN_JSAMPLE as 12 rather than 8. Note that this causes JSAMPLE to be -larger than a char, so it affects the surrounding application's image data. -The sample applications cjpeg and djpeg can support 12-bit mode only for PPM -and GIF file formats; you must disable the other file formats to compile a -12-bit cjpeg or djpeg. At present, a 12-bit library can handle *only* 12-bit -images, not both precisions. - -Note that a 12-bit library always compresses in Huffman optimization mode, -in order to generate valid Huffman tables. This is necessary because our -default Huffman tables only cover 8-bit data. If you need to output 12-bit -files in one pass, you'll have to supply suitable default Huffman tables. -You may also want to supply your own DCT quantization tables; the existing -quality-scaling code has been developed for 8-bit use, and probably doesn't -generate especially good tables for 12-bit. - The maximum number of components (color channels) in the image is determined by MAX_COMPONENTS. The JPEG standard allows up to 255 components, but we expect that few applications will need more than four or so. @@ -3084,7 +3187,7 @@ performance or reduce memory space by tweaking the various typedefs in jmorecfg.h. In particular, on some RISC CPUs, access to arrays of "short"s is quite slow; consider trading memory for speed by making JCOEF, INT16, and UINT16 be "int" or "unsigned int". UINT8 is also a candidate to become int. -You probably don't want to make JSAMPLE be int unless you have lots of memory +You probably don't want to make J*SAMPLE be int unless you have lots of memory to burn. You can reduce the size of the library by compiling out various optional diff --git a/rdbmp.c b/rdbmp.c index 433ebe2..88ee751 100644 --- a/rdbmp.c +++ b/rdbmp.c @@ -670,6 +670,9 @@ jinit_read_bmp(j_compress_ptr cinfo, boolean use_inversion_array) { bmp_source_ptr source; + if (cinfo->data_precision != 8) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + /* Create module interface object */ source = (bmp_source_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, diff --git a/rdcolmap.c b/rdcolmap.c index d2ed95c..836685e 100644 --- a/rdcolmap.c +++ b/rdcolmap.c @@ -1,8 +1,10 @@ /* * rdcolmap.c * + * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -18,13 +20,15 @@ * of each desired color. Such a file can be extracted from an * ordinary image PPM file with ppmtomap(1). * - * Rescaling a PPM that has a maxval unequal to MAXJSAMPLE is not + * Rescaling a PPM that has a maxval unequal to _MAXJSAMPLE is not * currently implemented. */ #include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ +#include "jsamplecomp.h" #ifdef QUANT_2PASS_SUPPORTED /* otherwise can't quantize to supplied map */ +#if BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED) /* Portions of this code are based on the PBMPLUS library, which is: ** @@ -46,9 +50,9 @@ LOCAL(void) add_map_entry(j_decompress_ptr cinfo, int R, int G, int B) { - JSAMPROW colormap0 = cinfo->colormap[0]; - JSAMPROW colormap1 = cinfo->colormap[1]; - JSAMPROW colormap2 = cinfo->colormap[2]; + _JSAMPROW colormap0 = ((_JSAMPARRAY)cinfo->colormap)[0]; + _JSAMPROW colormap1 = ((_JSAMPARRAY)cinfo->colormap)[1]; + _JSAMPROW colormap2 = ((_JSAMPARRAY)cinfo->colormap)[2]; int ncolors = cinfo->actual_number_of_colors; int index; @@ -60,13 +64,13 @@ add_map_entry(j_decompress_ptr cinfo, int R, int G, int B) } /* Check for map overflow. */ - if (ncolors >= (MAXJSAMPLE + 1)) - ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, (MAXJSAMPLE + 1)); + if (ncolors >= (_MAXJSAMPLE + 1)) + ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, (_MAXJSAMPLE + 1)); /* OK, add color to map. */ - colormap0[ncolors] = (JSAMPLE)R; - colormap1[ncolors] = (JSAMPLE)G; - colormap2[ncolors] = (JSAMPLE)B; + colormap0[ncolors] = (_JSAMPLE)R; + colormap1[ncolors] = (_JSAMPLE)G; + colormap2[ncolors] = (_JSAMPLE)B; cinfo->actual_number_of_colors++; } @@ -186,7 +190,7 @@ read_ppm_map(j_decompress_ptr cinfo, FILE *infile) ERREXIT(cinfo, JERR_BAD_CMAP_FILE); /* For now, we don't support rescaling from an unusual maxval. */ - if (maxval != (unsigned int)MAXJSAMPLE) + if (maxval != (unsigned int)_MAXJSAMPLE) ERREXIT(cinfo, JERR_BAD_CMAP_FILE); switch (c) { @@ -228,12 +232,15 @@ read_ppm_map(j_decompress_ptr cinfo, FILE *infile) */ GLOBAL(void) -read_color_map(j_decompress_ptr cinfo, FILE *infile) +_read_color_map(j_decompress_ptr cinfo, FILE *infile) { + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + /* Allocate space for a color map of maximum supported size. */ cinfo->colormap = (*cinfo->mem->alloc_sarray) ((j_common_ptr)cinfo, JPOOL_IMAGE, - (JDIMENSION)(MAXJSAMPLE + 1), (JDIMENSION)3); + (JDIMENSION)(_MAXJSAMPLE + 1), (JDIMENSION)3); cinfo->actual_number_of_colors = 0; /* initialize map to empty */ /* Read first byte to determine file format */ @@ -250,4 +257,5 @@ read_color_map(j_decompress_ptr cinfo, FILE *infile) } } +#endif /* BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED) */ #endif /* QUANT_2PASS_SUPPORTED */ diff --git a/rdgif.c b/rdgif.c index bdf7401..0cbd279 100644 --- a/rdgif.c +++ b/rdgif.c @@ -34,8 +34,10 @@ */ #include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ +#include "jsamplecomp.h" -#ifdef GIF_SUPPORTED +#if defined(GIF_SUPPORTED) && \ + (BITS_IN_JSAMPLE != 16 || defined(C_LOSSLESS_SUPPORTED)) /* Macros to deal with unsigned chars as efficiently as compiler allows */ @@ -89,7 +91,7 @@ typedef struct { j_compress_ptr cinfo; /* back link saves passing separate parm */ - JSAMPARRAY colormap; /* GIF colormap (converted to my format) */ + _JSAMPARRAY colormap; /* GIF colormap (converted to my format) */ /* State for GetCode and LZWReadByte */ U_CHAR code_buf[256 + 4]; /* current input data block */ @@ -342,7 +344,7 @@ LZWReadByte(gif_source_ptr sinfo) LOCAL(void) -ReadColorMap(gif_source_ptr sinfo, int cmaplen, JSAMPARRAY cmap) +ReadColorMap(gif_source_ptr sinfo, int cmaplen, _JSAMPARRAY cmap) /* Read a GIF colormap */ { int i, gray = 1; @@ -353,9 +355,9 @@ ReadColorMap(gif_source_ptr sinfo, int cmaplen, JSAMPARRAY cmap) #else #define UPSCALE(x) ((x) << (BITS_IN_JSAMPLE - 8)) #endif - cmap[CM_RED][i] = (JSAMPLE)UPSCALE(ReadByte(sinfo)); - cmap[CM_GREEN][i] = (JSAMPLE)UPSCALE(ReadByte(sinfo)); - cmap[CM_BLUE][i] = (JSAMPLE)UPSCALE(ReadByte(sinfo)); + cmap[CM_RED][i] = (_JSAMPLE)UPSCALE(ReadByte(sinfo)); + cmap[CM_GREEN][i] = (_JSAMPLE)UPSCALE(ReadByte(sinfo)); + cmap[CM_BLUE][i] = (_JSAMPLE)UPSCALE(ReadByte(sinfo)); if (cmap[CM_RED][i] != cmap[CM_GREEN][i] || cmap[CM_GREEN][i] != cmap[CM_BLUE][i]) gray = 0; @@ -427,7 +429,7 @@ start_input_gif(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) TRACEMS(cinfo, 1, JTRC_GIF_NONSQUARE); /* Allocate space to store the colormap */ - source->colormap = (*cinfo->mem->alloc_sarray) + source->colormap = (_JSAMPARRAY)(*cinfo->mem->alloc_sarray) ((j_common_ptr)cinfo, JPOOL_IMAGE, (JDIMENSION)MAXCOLORMAPSIZE, (JDIMENSION)NUMCOLORS); colormaplen = 0; /* indicate initialization */ @@ -530,7 +532,7 @@ start_input_gif(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) } /* Create compressor input buffer. */ - source->pub.buffer = (*cinfo->mem->alloc_sarray) + source->pub._buffer = (_JSAMPARRAY)(*cinfo->mem->alloc_sarray) ((j_common_ptr)cinfo, JPOOL_IMAGE, (JDIMENSION)width * cinfo->input_components, (JDIMENSION)1); source->pub.buffer_height = 1; @@ -539,7 +541,7 @@ start_input_gif(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) for (c = colormaplen; c < source->clear_code; c++) { source->colormap[CM_RED][c] = source->colormap[CM_GREEN][c] = - source->colormap[CM_BLUE][c] = CENTERJSAMPLE; + source->colormap[CM_BLUE][c] = _CENTERJSAMPLE; } /* Return info about the image. */ @@ -562,11 +564,11 @@ get_pixel_rows(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) { gif_source_ptr source = (gif_source_ptr)sinfo; register int c; - register JSAMPROW ptr; + register _JSAMPROW ptr; register JDIMENSION col; - register JSAMPARRAY colormap = source->colormap; + register _JSAMPARRAY colormap = source->colormap; - ptr = source->pub.buffer[0]; + ptr = source->pub._buffer[0]; if (cinfo->in_color_space == JCS_GRAYSCALE) { for (col = cinfo->image_width; col > 0; col--) { c = LZWReadByte(source); @@ -594,7 +596,7 @@ METHODDEF(JDIMENSION) load_interlaced_image(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) { gif_source_ptr source = (gif_source_ptr)sinfo; - register JSAMPROW sptr; + register _JSAMPROW sptr; register JDIMENSION col; JDIMENSION row; cd_progress_ptr progress = (cd_progress_ptr)cinfo->progress; @@ -606,11 +608,11 @@ load_interlaced_image(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) progress->pub.pass_limit = (long)cinfo->image_height; (*progress->pub.progress_monitor) ((j_common_ptr)cinfo); } - sptr = *(*cinfo->mem->access_virt_sarray) + sptr = *(_JSAMPARRAY)(*cinfo->mem->access_virt_sarray) ((j_common_ptr)cinfo, source->interlaced_image, row, (JDIMENSION)1, TRUE); for (col = cinfo->image_width; col > 0; col--) { - *sptr++ = (JSAMPLE)LZWReadByte(source); + *sptr++ = (_JSAMPLE)LZWReadByte(source); } } if (progress != NULL) @@ -639,9 +641,9 @@ get_interlaced_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) { gif_source_ptr source = (gif_source_ptr)sinfo; register int c; - register JSAMPROW sptr, ptr; + register _JSAMPROW sptr, ptr; register JDIMENSION col; - register JSAMPARRAY colormap = source->colormap; + register _JSAMPARRAY colormap = source->colormap; JDIMENSION irow; /* Figure out which row of interlaced image is needed, and access it. */ @@ -659,11 +661,11 @@ get_interlaced_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) default: /* fourth-pass row */ irow = (source->cur_row_number >> 1) + source->pass4_offset; } - sptr = *(*cinfo->mem->access_virt_sarray) + sptr = *(_JSAMPARRAY)(*cinfo->mem->access_virt_sarray) ((j_common_ptr)cinfo, source->interlaced_image, irow, (JDIMENSION)1, FALSE); /* Scan the row, expand colormap, and output */ - ptr = source->pub.buffer[0]; + ptr = source->pub._buffer[0]; if (cinfo->in_color_space == JCS_GRAYSCALE) { for (col = cinfo->image_width; col > 0; col--) { c = *sptr++; @@ -698,10 +700,13 @@ finish_input_gif(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) */ GLOBAL(cjpeg_source_ptr) -jinit_read_gif(j_compress_ptr cinfo) +_jinit_read_gif(j_compress_ptr cinfo) { gif_source_ptr source; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + /* Create module interface object */ source = (gif_source_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, @@ -717,4 +722,5 @@ jinit_read_gif(j_compress_ptr cinfo) return (cjpeg_source_ptr)source; } -#endif /* GIF_SUPPORTED */ +#endif /* defined(GIF_SUPPORTED) && + (BITS_IN_JSAMPLE != 16 || defined(C_LOSSLESS_SUPPORTED)) */ diff --git a/rdppm.c b/rdppm.c index 294749a..5705806 100644 --- a/rdppm.c +++ b/rdppm.c @@ -5,7 +5,7 @@ * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 2009 by Bill Allombert, Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2015-2017, 2020-2022, D. R. Commander. + * Copyright (C) 2015-2017, 2020-2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -25,7 +25,8 @@ #include "cmyk.h" #include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ -#ifdef PPM_SUPPORTED +#if defined(PPM_SUPPORTED) && \ + (BITS_IN_JSAMPLE != 16 || defined(C_LOSSLESS_SUPPORTED)) /* Portions of this code are based on the PBMPLUS library, which is: @@ -62,9 +63,9 @@ typedef struct { /* Usually these two pointers point to the same place: */ U_CHAR *iobuffer; /* fread's I/O buffer */ - JSAMPROW pixrow; /* compressor input buffer */ + _JSAMPROW pixrow; /* compressor input buffer */ size_t buffer_width; /* width of I/O buffer */ - JSAMPLE *rescale; /* => maxval-remapping array, or NULL */ + _JSAMPLE *rescale; /* => maxval-remapping array, or NULL */ unsigned int maxval; } ppm_source_struct; @@ -124,10 +125,10 @@ read_pbm_integer(j_compress_ptr cinfo, FILE *infile, unsigned int maxval) * Read one row of pixels. * * We provide several different versions depending on input file format. - * In all cases, input is scaled to the size of JSAMPLE. + * In all cases, input is scaled to the size of _JSAMPLE. * * A really fast path is provided for reading byte/sample raw files with - * maxval = MAXJSAMPLE, which is the normal case for 8-bit data. + * maxval = _MAXJSAMPLE, which is the normal case for 8-bit data. */ @@ -137,12 +138,12 @@ get_text_gray_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) { ppm_source_ptr source = (ppm_source_ptr)sinfo; FILE *infile = source->pub.input_file; - register JSAMPROW ptr; - register JSAMPLE *rescale = source->rescale; + register _JSAMPROW ptr; + register _JSAMPLE *rescale = source->rescale; JDIMENSION col; unsigned int maxval = source->maxval; - ptr = source->pub.buffer[0]; + ptr = source->pub._buffer[0]; for (col = cinfo->image_width; col > 0; col--) { *ptr++ = rescale[read_pbm_integer(cinfo, infile, maxval)]; } @@ -165,8 +166,8 @@ get_text_gray_rgb_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) { ppm_source_ptr source = (ppm_source_ptr)sinfo; FILE *infile = source->pub.input_file; - register JSAMPROW ptr; - register JSAMPLE *rescale = source->rescale; + register _JSAMPROW ptr; + register _JSAMPLE *rescale = source->rescale; JDIMENSION col; unsigned int maxval = source->maxval; register int rindex = rgb_red[cinfo->in_color_space]; @@ -175,17 +176,17 @@ get_text_gray_rgb_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) register int aindex = alpha_index[cinfo->in_color_space]; register int ps = rgb_pixelsize[cinfo->in_color_space]; - ptr = source->pub.buffer[0]; - if (maxval == MAXJSAMPLE) { + ptr = source->pub._buffer[0]; + if (maxval == _MAXJSAMPLE) { if (aindex >= 0) - GRAY_RGB_READ_LOOP((JSAMPLE)read_pbm_integer(cinfo, infile, maxval), - ptr[aindex] = 0xFF;) + GRAY_RGB_READ_LOOP((_JSAMPLE)read_pbm_integer(cinfo, infile, maxval), + ptr[aindex] = _MAXJSAMPLE;) else - GRAY_RGB_READ_LOOP((JSAMPLE)read_pbm_integer(cinfo, infile, maxval), {}) + GRAY_RGB_READ_LOOP((_JSAMPLE)read_pbm_integer(cinfo, infile, maxval), {}) } else { if (aindex >= 0) GRAY_RGB_READ_LOOP(rescale[read_pbm_integer(cinfo, infile, maxval)], - ptr[aindex] = 0xFF;) + ptr[aindex] = _MAXJSAMPLE;) else GRAY_RGB_READ_LOOP(rescale[read_pbm_integer(cinfo, infile, maxval)], {}) } @@ -200,21 +201,21 @@ get_text_gray_cmyk_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) { ppm_source_ptr source = (ppm_source_ptr)sinfo; FILE *infile = source->pub.input_file; - register JSAMPROW ptr; - register JSAMPLE *rescale = source->rescale; + register _JSAMPROW ptr; + register _JSAMPLE *rescale = source->rescale; JDIMENSION col; unsigned int maxval = source->maxval; - ptr = source->pub.buffer[0]; - if (maxval == MAXJSAMPLE) { + ptr = source->pub._buffer[0]; + if (maxval == _MAXJSAMPLE) { for (col = cinfo->image_width; col > 0; col--) { - JSAMPLE gray = (JSAMPLE)read_pbm_integer(cinfo, infile, maxval); + _JSAMPLE gray = (_JSAMPLE)read_pbm_integer(cinfo, infile, maxval); rgb_to_cmyk(gray, gray, gray, ptr, ptr + 1, ptr + 2, ptr + 3); ptr += 4; } } else { for (col = cinfo->image_width; col > 0; col--) { - JSAMPLE gray = rescale[read_pbm_integer(cinfo, infile, maxval)]; + _JSAMPLE gray = rescale[read_pbm_integer(cinfo, infile, maxval)]; rgb_to_cmyk(gray, gray, gray, ptr, ptr + 1, ptr + 2, ptr + 3); ptr += 4; } @@ -239,8 +240,8 @@ get_text_rgb_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) { ppm_source_ptr source = (ppm_source_ptr)sinfo; FILE *infile = source->pub.input_file; - register JSAMPROW ptr; - register JSAMPLE *rescale = source->rescale; + register _JSAMPROW ptr; + register _JSAMPLE *rescale = source->rescale; JDIMENSION col; unsigned int maxval = source->maxval; register int rindex = rgb_red[cinfo->in_color_space]; @@ -249,17 +250,17 @@ get_text_rgb_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) register int aindex = alpha_index[cinfo->in_color_space]; register int ps = rgb_pixelsize[cinfo->in_color_space]; - ptr = source->pub.buffer[0]; - if (maxval == MAXJSAMPLE) { + ptr = source->pub._buffer[0]; + if (maxval == _MAXJSAMPLE) { if (aindex >= 0) - RGB_READ_LOOP((JSAMPLE)read_pbm_integer(cinfo, infile, maxval), - ptr[aindex] = 0xFF;) + RGB_READ_LOOP((_JSAMPLE)read_pbm_integer(cinfo, infile, maxval), + ptr[aindex] = _MAXJSAMPLE;) else - RGB_READ_LOOP((JSAMPLE)read_pbm_integer(cinfo, infile, maxval), {}) + RGB_READ_LOOP((_JSAMPLE)read_pbm_integer(cinfo, infile, maxval), {}) } else { if (aindex >= 0) RGB_READ_LOOP(rescale[read_pbm_integer(cinfo, infile, maxval)], - ptr[aindex] = 0xFF;) + ptr[aindex] = _MAXJSAMPLE;) else RGB_READ_LOOP(rescale[read_pbm_integer(cinfo, infile, maxval)], {}) } @@ -274,25 +275,25 @@ get_text_rgb_cmyk_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) { ppm_source_ptr source = (ppm_source_ptr)sinfo; FILE *infile = source->pub.input_file; - register JSAMPROW ptr; - register JSAMPLE *rescale = source->rescale; + register _JSAMPROW ptr; + register _JSAMPLE *rescale = source->rescale; JDIMENSION col; unsigned int maxval = source->maxval; - ptr = source->pub.buffer[0]; - if (maxval == MAXJSAMPLE) { + ptr = source->pub._buffer[0]; + if (maxval == _MAXJSAMPLE) { for (col = cinfo->image_width; col > 0; col--) { - JSAMPLE r = (JSAMPLE)read_pbm_integer(cinfo, infile, maxval); - JSAMPLE g = (JSAMPLE)read_pbm_integer(cinfo, infile, maxval); - JSAMPLE b = (JSAMPLE)read_pbm_integer(cinfo, infile, maxval); + _JSAMPLE r = (_JSAMPLE)read_pbm_integer(cinfo, infile, maxval); + _JSAMPLE g = (_JSAMPLE)read_pbm_integer(cinfo, infile, maxval); + _JSAMPLE b = (_JSAMPLE)read_pbm_integer(cinfo, infile, maxval); rgb_to_cmyk(r, g, b, ptr, ptr + 1, ptr + 2, ptr + 3); ptr += 4; } } else { for (col = cinfo->image_width; col > 0; col--) { - JSAMPLE r = rescale[read_pbm_integer(cinfo, infile, maxval)]; - JSAMPLE g = rescale[read_pbm_integer(cinfo, infile, maxval)]; - JSAMPLE b = rescale[read_pbm_integer(cinfo, infile, maxval)]; + _JSAMPLE r = rescale[read_pbm_integer(cinfo, infile, maxval)]; + _JSAMPLE g = rescale[read_pbm_integer(cinfo, infile, maxval)]; + _JSAMPLE b = rescale[read_pbm_integer(cinfo, infile, maxval)]; rgb_to_cmyk(r, g, b, ptr, ptr + 1, ptr + 2, ptr + 3); ptr += 4; } @@ -306,14 +307,14 @@ get_scaled_gray_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) /* This version is for reading raw-byte-format PGM files with any maxval */ { ppm_source_ptr source = (ppm_source_ptr)sinfo; - register JSAMPROW ptr; + register _JSAMPROW ptr; register U_CHAR *bufferptr; - register JSAMPLE *rescale = source->rescale; + register _JSAMPLE *rescale = source->rescale; JDIMENSION col; if (!ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) ERREXIT(cinfo, JERR_INPUT_EOF); - ptr = source->pub.buffer[0]; + ptr = source->pub._buffer[0]; bufferptr = source->iobuffer; for (col = cinfo->image_width; col > 0; col--) { *ptr++ = rescale[UCH(*bufferptr++)]; @@ -328,9 +329,9 @@ get_gray_rgb_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) and converting to extended RGB */ { ppm_source_ptr source = (ppm_source_ptr)sinfo; - register JSAMPROW ptr; + register _JSAMPROW ptr; register U_CHAR *bufferptr; - register JSAMPLE *rescale = source->rescale; + register _JSAMPLE *rescale = source->rescale; JDIMENSION col; unsigned int maxval = source->maxval; register int rindex = rgb_red[cinfo->in_color_space]; @@ -341,16 +342,17 @@ get_gray_rgb_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) if (!ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) ERREXIT(cinfo, JERR_INPUT_EOF); - ptr = source->pub.buffer[0]; + ptr = source->pub._buffer[0]; bufferptr = source->iobuffer; - if (maxval == MAXJSAMPLE) { + if (maxval == _MAXJSAMPLE) { if (aindex >= 0) - GRAY_RGB_READ_LOOP(*bufferptr++, ptr[aindex] = 0xFF;) + GRAY_RGB_READ_LOOP(*bufferptr++, ptr[aindex] = _MAXJSAMPLE;) else GRAY_RGB_READ_LOOP(*bufferptr++, {}) } else { if (aindex >= 0) - GRAY_RGB_READ_LOOP(rescale[UCH(*bufferptr++)], ptr[aindex] = 0xFF;) + GRAY_RGB_READ_LOOP(rescale[UCH(*bufferptr++)], + ptr[aindex] = _MAXJSAMPLE;) else GRAY_RGB_READ_LOOP(rescale[UCH(*bufferptr++)], {}) } @@ -364,25 +366,25 @@ get_gray_cmyk_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) and converting to CMYK */ { ppm_source_ptr source = (ppm_source_ptr)sinfo; - register JSAMPROW ptr; + register _JSAMPROW ptr; register U_CHAR *bufferptr; - register JSAMPLE *rescale = source->rescale; + 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]; + ptr = source->pub._buffer[0]; bufferptr = source->iobuffer; - if (maxval == MAXJSAMPLE) { + if (maxval == _MAXJSAMPLE) { for (col = cinfo->image_width; col > 0; col--) { - JSAMPLE gray = *bufferptr++; + _JSAMPLE gray = *bufferptr++; rgb_to_cmyk(gray, gray, gray, ptr, ptr + 1, ptr + 2, ptr + 3); ptr += 4; } } else { for (col = cinfo->image_width; col > 0; col--) { - JSAMPLE gray = rescale[UCH(*bufferptr++)]; + _JSAMPLE gray = rescale[UCH(*bufferptr++)]; rgb_to_cmyk(gray, gray, gray, ptr, ptr + 1, ptr + 2, ptr + 3); ptr += 4; } @@ -396,9 +398,9 @@ get_rgb_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) /* This version is for reading raw-byte-format PPM files with any maxval */ { ppm_source_ptr source = (ppm_source_ptr)sinfo; - register JSAMPROW ptr; + register _JSAMPROW ptr; register U_CHAR *bufferptr; - register JSAMPLE *rescale = source->rescale; + register _JSAMPLE *rescale = source->rescale; JDIMENSION col; unsigned int maxval = source->maxval; register int rindex = rgb_red[cinfo->in_color_space]; @@ -409,16 +411,16 @@ get_rgb_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) if (!ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) ERREXIT(cinfo, JERR_INPUT_EOF); - ptr = source->pub.buffer[0]; + ptr = source->pub._buffer[0]; bufferptr = source->iobuffer; - if (maxval == MAXJSAMPLE) { + if (maxval == _MAXJSAMPLE) { if (aindex >= 0) - RGB_READ_LOOP(*bufferptr++, ptr[aindex] = 0xFF;) + RGB_READ_LOOP(*bufferptr++, ptr[aindex] = _MAXJSAMPLE;) else RGB_READ_LOOP(*bufferptr++, {}) } else { if (aindex >= 0) - RGB_READ_LOOP(rescale[UCH(*bufferptr++)], ptr[aindex] = 0xFF;) + RGB_READ_LOOP(rescale[UCH(*bufferptr++)], ptr[aindex] = _MAXJSAMPLE;) else RGB_READ_LOOP(rescale[UCH(*bufferptr++)], {}) } @@ -432,29 +434,29 @@ get_rgb_cmyk_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) converting to CMYK */ { ppm_source_ptr source = (ppm_source_ptr)sinfo; - register JSAMPROW ptr; + register _JSAMPROW ptr; register U_CHAR *bufferptr; - register JSAMPLE *rescale = source->rescale; + 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]; + ptr = source->pub._buffer[0]; bufferptr = source->iobuffer; - if (maxval == MAXJSAMPLE) { + if (maxval == _MAXJSAMPLE) { for (col = cinfo->image_width; col > 0; col--) { - JSAMPLE r = *bufferptr++; - JSAMPLE g = *bufferptr++; - JSAMPLE b = *bufferptr++; + _JSAMPLE r = *bufferptr++; + _JSAMPLE g = *bufferptr++; + _JSAMPLE b = *bufferptr++; rgb_to_cmyk(r, g, b, ptr, ptr + 1, ptr + 2, ptr + 3); ptr += 4; } } else { for (col = cinfo->image_width; col > 0; col--) { - JSAMPLE r = rescale[UCH(*bufferptr++)]; - JSAMPLE g = rescale[UCH(*bufferptr++)]; - JSAMPLE b = rescale[UCH(*bufferptr++)]; + _JSAMPLE r = rescale[UCH(*bufferptr++)]; + _JSAMPLE g = rescale[UCH(*bufferptr++)]; + _JSAMPLE b = rescale[UCH(*bufferptr++)]; rgb_to_cmyk(r, g, b, ptr, ptr + 1, ptr + 2, ptr + 3); ptr += 4; } @@ -465,8 +467,8 @@ get_rgb_cmyk_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) METHODDEF(JDIMENSION) get_raw_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) -/* This version is for reading raw-byte-format files with maxval = MAXJSAMPLE. - * In this case we just read right into the JSAMPLE buffer! +/* This version is for reading raw-byte-format files with maxval = _MAXJSAMPLE. + * In this case we just read right into the _JSAMPLE buffer! * Note that same code works for PPM and PGM files. */ { @@ -483,15 +485,15 @@ get_word_gray_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 _JSAMPROW ptr; register U_CHAR *bufferptr; - register JSAMPLE *rescale = source->rescale; + 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]; + ptr = source->pub._buffer[0]; bufferptr = source->iobuffer; for (col = cinfo->image_width; col > 0; col--) { register unsigned int temp; @@ -506,13 +508,77 @@ 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 */ { ppm_source_ptr source = (ppm_source_ptr)sinfo; - register JSAMPROW ptr; + register _JSAMPROW ptr; register U_CHAR *bufferptr; - register JSAMPLE *rescale = source->rescale; + register _JSAMPLE *rescale = source->rescale; JDIMENSION col; unsigned int maxval = source->maxval; register int rindex = rgb_red[cinfo->in_color_space]; @@ -523,7 +589,7 @@ get_word_rgb_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) if (!ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) ERREXIT(cinfo, JERR_INPUT_EOF); - ptr = source->pub.buffer[0]; + ptr = source->pub._buffer[0]; bufferptr = source->iobuffer; for (col = cinfo->image_width; col > 0; col--) { register unsigned int temp; @@ -543,13 +609,50 @@ get_word_rgb_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) ERREXIT(cinfo, JERR_PPM_OUTOFRANGE); ptr[bindex] = rescale[temp]; if (aindex >= 0) - ptr[aindex] = 0xFF; + ptr[aindex] = _MAXJSAMPLE; ptr += ps; } return 1; } +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. */ @@ -639,9 +742,13 @@ 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) && + } else if (maxval == _MAXJSAMPLE && sizeof(_JSAMPLE) == sizeof(U_CHAR) && cinfo->in_color_space == JCS_GRAYSCALE) { source->pub.get_pixel_rows = get_raw_row; use_raw_buffer = TRUE; @@ -665,9 +772,11 @@ 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) && + } else if (maxval == _MAXJSAMPLE && sizeof(_JSAMPLE) == sizeof(U_CHAR) && #if RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 3 (cinfo->in_color_space == JCS_EXT_RGB || cinfo->in_color_space == JCS_RGB)) { @@ -711,13 +820,13 @@ start_input_ppm(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) /* Create compressor input buffer. */ if (use_raw_buffer) { /* For unscaled raw-input case, we can just map it onto the I/O buffer. */ - /* Synthesize a JSAMPARRAY pointer structure */ - source->pixrow = (JSAMPROW)source->iobuffer; - source->pub.buffer = &source->pixrow; + /* Synthesize a _JSAMPARRAY pointer structure */ + source->pixrow = (_JSAMPROW)source->iobuffer; + source->pub._buffer = &source->pixrow; source->pub.buffer_height = 1; } else { /* Need to translate anyway, so make a separate sample buffer. */ - source->pub.buffer = (*cinfo->mem->alloc_sarray) + source->pub._buffer = (_JSAMPARRAY)(*cinfo->mem->alloc_sarray) ((j_common_ptr)cinfo, JPOOL_IMAGE, (JDIMENSION)w * cinfo->input_components, (JDIMENSION)1); source->pub.buffer_height = 1; @@ -728,16 +837,16 @@ start_input_ppm(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) long val, half_maxval; /* On 16-bit-int machines we have to be careful of maxval = 65535 */ - source->rescale = (JSAMPLE *) + source->rescale = (_JSAMPLE *) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, (size_t)(((long)MAX(maxval, 255) + 1L) * - sizeof(JSAMPLE))); + sizeof(_JSAMPLE))); memset(source->rescale, 0, (size_t)(((long)MAX(maxval, 255) + 1L) * - sizeof(JSAMPLE))); + sizeof(_JSAMPLE))); half_maxval = maxval / 2; for (val = 0; val <= (long)maxval; val++) { /* The multiplication here must be done in 32 bits to avoid overflow */ - source->rescale[val] = (JSAMPLE)((val * MAXJSAMPLE + half_maxval) / + source->rescale[val] = (_JSAMPLE)((val * _MAXJSAMPLE + half_maxval) / maxval); } } @@ -760,10 +869,13 @@ finish_input_ppm(j_compress_ptr cinfo, cjpeg_source_ptr sinfo) */ GLOBAL(cjpeg_source_ptr) -jinit_read_ppm(j_compress_ptr cinfo) +_jinit_read_ppm(j_compress_ptr cinfo) { ppm_source_ptr source; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + /* Create module interface object */ source = (ppm_source_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, @@ -778,4 +890,5 @@ jinit_read_ppm(j_compress_ptr cinfo) return (cjpeg_source_ptr)source; } -#endif /* PPM_SUPPORTED */ +#endif /* defined(PPM_SUPPORTED) && + (BITS_IN_JSAMPLE != 16 || defined(C_LOSSLESS_SUPPORTED)) */ diff --git a/rdtarga.c b/rdtarga.c index 3ed7eb3..dae0b58 100644 --- a/rdtarga.c +++ b/rdtarga.c @@ -490,6 +490,9 @@ jinit_read_targa(j_compress_ptr cinfo) { tga_source_ptr source; + if (cinfo->data_precision != 8) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + /* Create module interface object */ source = (tga_source_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, diff --git a/release/ReadMe.txt b/release/ReadMe.txt index 446ce46..a71d15c 100644 --- a/release/ReadMe.txt +++ b/release/ReadMe.txt @@ -2,4 +2,4 @@ libjpeg-turbo is a JPEG image codec that uses SIMD instructions to accelerate ba libjpeg-turbo implements both the traditional libjpeg API as well as the less powerful but more straightforward TurboJPEG API. libjpeg-turbo also features colorspace extensions that allow it to compress from/decompress to 32-bit and big-endian pixel buffers (RGBX, XBGR, etc.), as well as a full-featured Java interface. -libjpeg-turbo was originally based on libjpeg/SIMD, an MMX-accelerated derivative of libjpeg v6b developed by Miyasaka Masaru. The TigerVNC and VirtualGL projects made numerous enhancements to the codec in 2009, and in early 2010, libjpeg-turbo spun off into an independent project, with the goal of making high-speed JPEG compression/decompression technology available to a broader range of users and developers. +libjpeg-turbo was originally based on libjpeg/SIMD, an MMX-accelerated derivative of libjpeg v6b developed by Miyasaka Masaru. The TigerVNC and VirtualGL projects made numerous enhancements to the codec in 2009, and in early 2010, libjpeg-turbo spun off into an independent project, with the goal of making high-speed JPEG compression/decompression technology available to a broader range of users and developers. libjpeg-turbo is an ISO/IEC and ITU-T reference implementation of the JPEG standard. diff --git a/release/deb-control.in b/release/deb-control.in index 72bceec..2c2ae38 100644 --- a/release/deb-control.in +++ b/release/deb-control.in @@ -28,4 +28,5 @@ Description: A SIMD-accelerated JPEG codec that provides both the libjpeg and Tu VirtualGL projects made numerous enhancements to the codec in 2009, and in early 2010, libjpeg-turbo spun off into an independent project, with the goal of making high-speed JPEG compression/decompression technology available to a - broader range of users and developers. + broader range of users and developers. libjpeg-turbo is an ISO/IEC and ITU-T + reference implementation of the JPEG standard. diff --git a/release/installer.nsi.in b/release/installer.nsi.in index 65db63d..0fdd2ed 100644 --- a/release/installer.nsi.in +++ b/release/installer.nsi.in @@ -90,7 +90,7 @@ Section "@CMAKE_PROJECT_NAME@ SDK for @INST_PLATFORM@ (required)" File "@CMAKE_CURRENT_SOURCE_DIR@\README.ijg" File "@CMAKE_CURRENT_SOURCE_DIR@\README.md" File "@CMAKE_CURRENT_SOURCE_DIR@\LICENSE.md" - File "@CMAKE_CURRENT_SOURCE_DIR@\example.txt" + File "@CMAKE_CURRENT_SOURCE_DIR@\example.c" File "@CMAKE_CURRENT_SOURCE_DIR@\libjpeg.txt" File "@CMAKE_CURRENT_SOURCE_DIR@\structure.txt" File "@CMAKE_CURRENT_SOURCE_DIR@\usage.txt" @@ -168,7 +168,7 @@ Section "Uninstall" Delete $INSTDIR\doc\README.ijg Delete $INSTDIR\doc\README.md Delete $INSTDIR\doc\LICENSE.md - Delete $INSTDIR\doc\example.txt + Delete $INSTDIR\doc\example.c Delete $INSTDIR\doc\libjpeg.txt Delete $INSTDIR\doc\structure.txt Delete $INSTDIR\doc\usage.txt diff --git a/release/rpm.spec.in b/release/rpm.spec.in index 4207024..24872db 100644 --- a/release/rpm.spec.in +++ b/release/rpm.spec.in @@ -77,7 +77,8 @@ derivative of libjpeg v6b developed by Miyasaka Masaru. The TigerVNC and VirtualGL projects made numerous enhancements to the codec in 2009, and in early 2010, libjpeg-turbo spun off into an independent project, with the goal of making high-speed JPEG compression/decompression technology available to a -broader range of users and developers. +broader range of users and developers. libjpeg-turbo is an ISO/IEC and ITU-T +reference implementation of the JPEG standard. #-->%prep #-->%setup -q -n @CMAKE_PROJECT_NAME@-%{version} @@ -99,11 +100,10 @@ broader range of users and developers. #--> -DSO_MINOR_VERSION=@SO_MINOR_VERSION@ \ #--> -DJPEG_LIB_VERSION=@JPEG_LIB_VERSION@ \ #--> -DREQUIRE_SIMD=@REQUIRE_SIMD@ \ -#--> -DWITH_12BIT=@WITH_12BIT@ -DWITH_ARITH_DEC=@WITH_ARITH_DEC@ \ -#--> -DWITH_ARITH_ENC=@WITH_ARITH_ENC@ -DWITH_JAVA=@WITH_JAVA@ \ +#--> -DWITH_ARITH_DEC=@WITH_ARITH_DEC@ -DWITH_ARITH_ENC=@WITH_ARITH_ENC@ \ +#--> -DWITH_JAVA=@WITH_JAVA@ \ #--> -DWITH_JPEG7=@WITH_JPEG7@ -DWITH_JPEG8=@WITH_JPEG8@ \ -#--> -DWITH_MEM_SRCDST=@WITH_MEM_SRCDST@ -DWITH_SIMD=@WITH_SIMD@ \ -#--> -DWITH_TURBOJPEG=@WITH_TURBOJPEG@ . +#--> -DWITH_SIMD=@WITH_SIMD@ -DWITH_TURBOJPEG=@WITH_TURBOJPEG@ . #-->make DESTDIR=$RPM_BUILD_ROOT %install diff --git a/sharedlib/CMakeLists.txt b/sharedlib/CMakeLists.txt index aea0b9d..8e94256 100644 --- a/sharedlib/CMakeLists.txt +++ b/sharedlib/CMakeLists.txt @@ -29,19 +29,15 @@ if(WITH_SIMD AND (MSVC_IDE OR XCODE)) endif() if(WIN32) - if(WITH_MEM_SRCDST) - set(DEFFILE ../win/jpeg${SO_MAJOR_VERSION}-memsrcdst.def) - else() - set(DEFFILE ../win/jpeg${SO_MAJOR_VERSION}.def) - endif() + set(DEFFILE ../win/jpeg${SO_MAJOR_VERSION}.def) endif() if(MSVC) configure_file(${CMAKE_SOURCE_DIR}/win/jpeg.rc.in ${CMAKE_BINARY_DIR}/win/jpeg.rc) set(JPEG_SRCS ${JPEG_SRCS} ${CMAKE_BINARY_DIR}/win/jpeg.rc) endif() -add_library(jpeg SHARED ${JPEG_SRCS} ${DEFFILE} $ - ${SIMD_OBJS}) +add_library(jpeg SHARED ${JPEG_SRCS} ${DEFFILE} ${SIMD_TARGET_OBJECTS} + ${SIMD_OBJS} $ $) set_target_properties(jpeg PROPERTIES SOVERSION ${SO_MAJOR_VERSION} VERSION ${SO_MAJOR_VERSION}.${SO_AGE}.${SO_MINOR_VERSION}) @@ -60,7 +56,8 @@ if(MSVC) set_target_properties(jpeg PROPERTIES RUNTIME_OUTPUT_NAME jpeg${SO_MAJOR_VERSION}) # The jsimd_*.c file is built using /MT, so this prevents a linker warning. - set_target_properties(jpeg PROPERTIES LINK_FLAGS "/NODEFAULTLIB:LIBCMT /NODEFAULTLIB:LIBCMTD") + set_target_properties(jpeg PROPERTIES LINK_FLAGS + "/NODEFAULTLIB:LIBCMT /NODEFAULTLIB:LIBCMTD") elseif(MINGW) set_target_properties(jpeg PROPERTIES SUFFIX -${SO_MAJOR_VERSION}.dll) endif() @@ -68,28 +65,44 @@ endif() if(WIN32) set(USE_SETMODE "-DUSE_SETMODE") endif() -if(WITH_12BIT) - set(COMPILE_FLAGS "-DGIF_SUPPORTED -DPPM_SUPPORTED ${USE_SETMODE}") -else() - set(COMPILE_FLAGS "-DBMP_SUPPORTED -DGIF_SUPPORTED -DPPM_SUPPORTED -DTARGA_SUPPORTED ${USE_SETMODE}") - set(CJPEG_BMP_SOURCES ../rdbmp.c ../rdtarga.c) - set(DJPEG_BMP_SOURCES ../wrbmp.c ../wrtarga.c) -endif() +set(CDJPEG_COMPILE_FLAGS + "-DBMP_SUPPORTED -DGIF_SUPPORTED -DPPM_SUPPORTED -DTARGA_SUPPORTED ${USE_SETMODE}") -add_executable(cjpeg ../cjpeg.c ../cdjpeg.c ../rdgif.c ../rdppm.c - ../rdswitch.c ${CJPEG_BMP_SOURCES}) -set_property(TARGET cjpeg PROPERTY COMPILE_FLAGS ${COMPILE_FLAGS}) +# Compile a separate version of these source files with 12-bit and 16-bit data +# precision. +add_library(cjpeg12 OBJECT ../rdgif.c ../rdppm.c) +set_property(TARGET cjpeg12 PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=12 -DGIF_SUPPORTED -DPPM_SUPPORTED") +add_library(cjpeg16 OBJECT ../rdgif.c ../rdppm.c) +set_property(TARGET cjpeg16 PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=16 -DGIF_SUPPORTED -DPPM_SUPPORTED") +add_executable(cjpeg ../cjpeg.c ../cdjpeg.c ../rdbmp.c ../rdgif.c ../rdppm.c + ../rdswitch.c ../rdtarga.c $ + $) +set_property(TARGET cjpeg PROPERTY COMPILE_FLAGS ${CDJPEG_COMPILE_FLAGS}) target_link_libraries(cjpeg jpeg) +# Compile a separate version of these source files with 12-bit and 16-bit data +# precision. +add_library(djpeg12 OBJECT ../rdcolmap.c ../wrgif.c ../wrppm.c) +set_property(TARGET djpeg12 PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=12 -DGIF_SUPPORTED -DPPM_SUPPORTED") +add_library(djpeg16 OBJECT ../wrppm.c) +set_property(TARGET djpeg16 PROPERTY COMPILE_FLAGS + "-DBITS_IN_JSAMPLE=16 -DPPM_SUPPORTED") add_executable(djpeg ../djpeg.c ../cdjpeg.c ../rdcolmap.c ../rdswitch.c - ../wrgif.c ../wrppm.c ${DJPEG_BMP_SOURCES}) -set_property(TARGET djpeg PROPERTY COMPILE_FLAGS ${COMPILE_FLAGS}) + ../wrbmp.c ../wrgif.c ../wrppm.c ../wrtarga.c $ + $) +set_property(TARGET djpeg PROPERTY COMPILE_FLAGS ${CDJPEG_COMPILE_FLAGS}) target_link_libraries(djpeg jpeg) add_executable(jpegtran ../jpegtran.c ../cdjpeg.c ../rdswitch.c ../transupp.c) target_link_libraries(jpegtran jpeg) set_property(TARGET jpegtran PROPERTY COMPILE_FLAGS "${USE_SETMODE}") +add_executable(example ../example.c) +target_link_libraries(example jpeg) + add_executable(jcstest ../jcstest.c) target_link_libraries(jcstest jpeg) diff --git a/simd/arm/aarch32/jsimd.c b/simd/arm/aarch32/jsimd.c index 920f765..04d6452 100644 --- a/simd/arm/aarch32/jsimd.c +++ b/simd/arm/aarch32/jsimd.c @@ -4,7 +4,7 @@ * Copyright 2009 Pierre Ossman for Cendio AB * Copyright (C) 2011, Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2009-2011, 2013-2014, 2016, 2018, 2022, D. R. Commander. - * Copyright (C) 2015-2016, 2018, Matthieu Darbois. + * Copyright (C) 2015-2016, 2018, 2022, Matthieu Darbois. * Copyright (C) 2019, Google LLC. * Copyright (C) 2020, Arm Limited. * @@ -27,8 +27,8 @@ #include -static unsigned int simd_support = ~0; -static unsigned int simd_huffman = 1; +static THREAD_LOCAL unsigned int simd_support = ~0; +static THREAD_LOCAL unsigned int simd_huffman = 1; #if !defined(__ARM_NEON__) && (defined(__linux__) || defined(ANDROID) || defined(__ANDROID__)) @@ -96,8 +96,6 @@ parse_proc_cpuinfo(int bufsize) /* * Check what SIMD accelerations are supported. - * - * FIXME: This code is racy under a multi-threaded environment. */ LOCAL(void) init_simd(void) @@ -945,7 +943,7 @@ jsimd_can_encode_mcu_AC_first_prepare(void) GLOBAL(void) jsimd_encode_mcu_AC_first_prepare(const JCOEF *block, const int *jpeg_natural_order_start, int Sl, - int Al, JCOEF *values, size_t *zerobits) + int Al, UJCOEF *values, size_t *zerobits) { jsimd_encode_mcu_AC_first_prepare_neon(block, jpeg_natural_order_start, Sl, Al, values, zerobits); @@ -970,7 +968,7 @@ jsimd_can_encode_mcu_AC_refine_prepare(void) GLOBAL(int) jsimd_encode_mcu_AC_refine_prepare(const JCOEF *block, const int *jpeg_natural_order_start, int Sl, - int Al, JCOEF *absvalues, size_t *bits) + int Al, UJCOEF *absvalues, size_t *bits) { return jsimd_encode_mcu_AC_refine_prepare_neon(block, jpeg_natural_order_start, Sl, diff --git a/simd/arm/aarch64/jsimd.c b/simd/arm/aarch64/jsimd.c index 41c06d3..358e159 100644 --- a/simd/arm/aarch64/jsimd.c +++ b/simd/arm/aarch64/jsimd.c @@ -4,7 +4,7 @@ * Copyright 2009 Pierre Ossman for Cendio AB * Copyright (C) 2011, Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2009-2011, 2013-2014, 2016, 2018, 2020, 2022, D. R. Commander. - * Copyright (C) 2015-2016, 2018, Matthieu Darbois. + * Copyright (C) 2015-2016, 2018, 2022, Matthieu Darbois. * Copyright (C) 2020, Arm Limited. * * Based on the x86 SIMD extension for IJG JPEG library, @@ -23,7 +23,6 @@ #include "../../../jdct.h" #include "../../../jsimddct.h" #include "../../jsimd.h" -#include "jconfigint.h" #include @@ -31,10 +30,10 @@ #define JSIMD_FASTST3 2 #define JSIMD_FASTTBL 4 -static unsigned int simd_support = ~0; -static unsigned int simd_huffman = 1; -static unsigned int simd_features = JSIMD_FASTLD3 | JSIMD_FASTST3 | - JSIMD_FASTTBL; +static THREAD_LOCAL unsigned int simd_support = ~0; +static THREAD_LOCAL unsigned int simd_huffman = 1; +static THREAD_LOCAL unsigned int simd_features = JSIMD_FASTLD3 | + JSIMD_FASTST3 | JSIMD_FASTTBL; #if defined(__linux__) || defined(ANDROID) || defined(__ANDROID__) @@ -109,8 +108,6 @@ parse_proc_cpuinfo(int bufsize) /* * Check what SIMD accelerations are supported. - * - * FIXME: This code is racy under a multi-threaded environment. */ /* @@ -1021,7 +1018,7 @@ jsimd_can_encode_mcu_AC_first_prepare(void) GLOBAL(void) jsimd_encode_mcu_AC_first_prepare(const JCOEF *block, const int *jpeg_natural_order_start, int Sl, - int Al, JCOEF *values, size_t *zerobits) + int Al, UJCOEF *values, size_t *zerobits) { jsimd_encode_mcu_AC_first_prepare_neon(block, jpeg_natural_order_start, Sl, Al, values, zerobits); @@ -1048,7 +1045,7 @@ jsimd_can_encode_mcu_AC_refine_prepare(void) GLOBAL(int) jsimd_encode_mcu_AC_refine_prepare(const JCOEF *block, const int *jpeg_natural_order_start, int Sl, - int Al, JCOEF *absvalues, size_t *bits) + int Al, UJCOEF *absvalues, size_t *bits) { return jsimd_encode_mcu_AC_refine_prepare_neon(block, jpeg_natural_order_start, diff --git a/simd/arm/jcphuff-neon.c b/simd/arm/jcphuff-neon.c index b91c5db..51db3c5 100644 --- a/simd/arm/jcphuff-neon.c +++ b/simd/arm/jcphuff-neon.c @@ -2,6 +2,8 @@ * jcphuff-neon.c - prepare data for progressive Huffman encoding (Arm Neon) * * Copyright (C) 2020-2021, Arm Limited. All Rights Reserved. + * Copyright (C) 2022, Matthieu Darbois. All Rights Reserved. + * Copyright (C) 2022, D. R. Commander. All Rights Reserved. * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages @@ -21,7 +23,6 @@ */ #define JPEG_INTERNALS -#include "jconfigint.h" #include "../../jinclude.h" #include "../../jpeglib.h" #include "../../jsimd.h" @@ -41,10 +42,10 @@ void jsimd_encode_mcu_AC_first_prepare_neon (const JCOEF *block, const int *jpeg_natural_order_start, int Sl, int Al, - JCOEF *values, size_t *zerobits) + UJCOEF *values, size_t *zerobits) { - JCOEF *values_ptr = values; - JCOEF *diff_values_ptr = values + DCTSIZE2; + UJCOEF *values_ptr = values; + UJCOEF *diff_values_ptr = values + DCTSIZE2; /* Rows of coefficients to zero (since they haven't been processed) */ int i, rows_to_zero = 8; @@ -68,23 +69,23 @@ void jsimd_encode_mcu_AC_first_prepare_neon coefs2 = vld1q_lane_s16(block + jpeg_natural_order_start[15], coefs2, 7); /* Isolate sign of coefficients. */ - int16x8_t sign_coefs1 = vshrq_n_s16(coefs1, 15); - int16x8_t sign_coefs2 = vshrq_n_s16(coefs2, 15); + uint16x8_t sign_coefs1 = vreinterpretq_u16_s16(vshrq_n_s16(coefs1, 15)); + uint16x8_t sign_coefs2 = vreinterpretq_u16_s16(vshrq_n_s16(coefs2, 15)); /* Compute absolute value of coefficients and apply point transform Al. */ - int16x8_t abs_coefs1 = vabsq_s16(coefs1); - int16x8_t abs_coefs2 = vabsq_s16(coefs2); - coefs1 = vshlq_s16(abs_coefs1, vdupq_n_s16(-Al)); - coefs2 = vshlq_s16(abs_coefs2, vdupq_n_s16(-Al)); + uint16x8_t abs_coefs1 = vreinterpretq_u16_s16(vabsq_s16(coefs1)); + uint16x8_t abs_coefs2 = vreinterpretq_u16_s16(vabsq_s16(coefs2)); + abs_coefs1 = vshlq_u16(abs_coefs1, vdupq_n_s16(-Al)); + abs_coefs2 = vshlq_u16(abs_coefs2, vdupq_n_s16(-Al)); /* Compute diff values. */ - int16x8_t diff1 = veorq_s16(coefs1, sign_coefs1); - int16x8_t diff2 = veorq_s16(coefs2, sign_coefs2); + uint16x8_t diff1 = veorq_u16(abs_coefs1, sign_coefs1); + uint16x8_t diff2 = veorq_u16(abs_coefs2, sign_coefs2); /* Store transformed coefficients and diff values. */ - vst1q_s16(values_ptr, coefs1); - vst1q_s16(values_ptr + DCTSIZE, coefs2); - vst1q_s16(diff_values_ptr, diff1); - vst1q_s16(diff_values_ptr + DCTSIZE, diff2); + vst1q_u16(values_ptr, abs_coefs1); + vst1q_u16(values_ptr + DCTSIZE, abs_coefs2); + vst1q_u16(diff_values_ptr, diff1); + vst1q_u16(diff_values_ptr + DCTSIZE, diff2); values_ptr += 16; diff_values_ptr += 16; jpeg_natural_order_start += 16; @@ -130,23 +131,23 @@ void jsimd_encode_mcu_AC_first_prepare_neon } /* Isolate sign of coefficients. */ - int16x8_t sign_coefs1 = vshrq_n_s16(coefs1, 15); - int16x8_t sign_coefs2 = vshrq_n_s16(coefs2, 15); + uint16x8_t sign_coefs1 = vreinterpretq_u16_s16(vshrq_n_s16(coefs1, 15)); + uint16x8_t sign_coefs2 = vreinterpretq_u16_s16(vshrq_n_s16(coefs2, 15)); /* Compute absolute value of coefficients and apply point transform Al. */ - int16x8_t abs_coefs1 = vabsq_s16(coefs1); - int16x8_t abs_coefs2 = vabsq_s16(coefs2); - coefs1 = vshlq_s16(abs_coefs1, vdupq_n_s16(-Al)); - coefs2 = vshlq_s16(abs_coefs2, vdupq_n_s16(-Al)); + uint16x8_t abs_coefs1 = vreinterpretq_u16_s16(vabsq_s16(coefs1)); + uint16x8_t abs_coefs2 = vreinterpretq_u16_s16(vabsq_s16(coefs2)); + abs_coefs1 = vshlq_u16(abs_coefs1, vdupq_n_s16(-Al)); + abs_coefs2 = vshlq_u16(abs_coefs2, vdupq_n_s16(-Al)); /* Compute diff values. */ - int16x8_t diff1 = veorq_s16(coefs1, sign_coefs1); - int16x8_t diff2 = veorq_s16(coefs2, sign_coefs2); + uint16x8_t diff1 = veorq_u16(abs_coefs1, sign_coefs1); + uint16x8_t diff2 = veorq_u16(abs_coefs2, sign_coefs2); /* Store transformed coefficients and diff values. */ - vst1q_s16(values_ptr, coefs1); - vst1q_s16(values_ptr + DCTSIZE, coefs2); - vst1q_s16(diff_values_ptr, diff1); - vst1q_s16(diff_values_ptr + DCTSIZE, diff2); + vst1q_u16(values_ptr, abs_coefs1); + vst1q_u16(values_ptr + DCTSIZE, abs_coefs2); + vst1q_u16(diff_values_ptr, diff1); + vst1q_u16(diff_values_ptr + DCTSIZE, diff2); values_ptr += 16; diff_values_ptr += 16; rows_to_zero -= 2; @@ -184,17 +185,17 @@ void jsimd_encode_mcu_AC_first_prepare_neon } /* Isolate sign of coefficients. */ - int16x8_t sign_coefs = vshrq_n_s16(coefs, 15); + uint16x8_t sign_coefs = vreinterpretq_u16_s16(vshrq_n_s16(coefs, 15)); /* Compute absolute value of coefficients and apply point transform Al. */ - int16x8_t abs_coefs = vabsq_s16(coefs); - coefs = vshlq_s16(abs_coefs, vdupq_n_s16(-Al)); + uint16x8_t abs_coefs = vreinterpretq_u16_s16(vabsq_s16(coefs)); + abs_coefs = vshlq_u16(abs_coefs, vdupq_n_s16(-Al)); /* Compute diff values. */ - int16x8_t diff = veorq_s16(coefs, sign_coefs); + uint16x8_t diff = veorq_u16(abs_coefs, sign_coefs); /* Store transformed coefficients and diff values. */ - vst1q_s16(values_ptr, coefs); - vst1q_s16(diff_values_ptr, diff); + vst1q_u16(values_ptr, abs_coefs); + vst1q_u16(diff_values_ptr, diff); values_ptr += 8; diff_values_ptr += 8; rows_to_zero--; @@ -202,8 +203,8 @@ void jsimd_encode_mcu_AC_first_prepare_neon /* Zero remaining memory in the values and diff_values blocks. */ for (i = 0; i < rows_to_zero; i++) { - vst1q_s16(values_ptr, vdupq_n_s16(0)); - vst1q_s16(diff_values_ptr, vdupq_n_s16(0)); + vst1q_u16(values_ptr, vdupq_n_u16(0)); + vst1q_u16(diff_values_ptr, vdupq_n_u16(0)); values_ptr += 8; diff_values_ptr += 8; } @@ -211,23 +212,23 @@ void jsimd_encode_mcu_AC_first_prepare_neon /* Construct zerobits bitmap. A set bit means that the corresponding * coefficient != 0. */ - int16x8_t row0 = vld1q_s16(values + 0 * DCTSIZE); - int16x8_t row1 = vld1q_s16(values + 1 * DCTSIZE); - int16x8_t row2 = vld1q_s16(values + 2 * DCTSIZE); - int16x8_t row3 = vld1q_s16(values + 3 * DCTSIZE); - int16x8_t row4 = vld1q_s16(values + 4 * DCTSIZE); - int16x8_t row5 = vld1q_s16(values + 5 * DCTSIZE); - int16x8_t row6 = vld1q_s16(values + 6 * DCTSIZE); - int16x8_t row7 = vld1q_s16(values + 7 * DCTSIZE); - - uint8x8_t row0_eq0 = vmovn_u16(vceqq_s16(row0, vdupq_n_s16(0))); - uint8x8_t row1_eq0 = vmovn_u16(vceqq_s16(row1, vdupq_n_s16(0))); - uint8x8_t row2_eq0 = vmovn_u16(vceqq_s16(row2, vdupq_n_s16(0))); - uint8x8_t row3_eq0 = vmovn_u16(vceqq_s16(row3, vdupq_n_s16(0))); - uint8x8_t row4_eq0 = vmovn_u16(vceqq_s16(row4, vdupq_n_s16(0))); - uint8x8_t row5_eq0 = vmovn_u16(vceqq_s16(row5, vdupq_n_s16(0))); - uint8x8_t row6_eq0 = vmovn_u16(vceqq_s16(row6, vdupq_n_s16(0))); - uint8x8_t row7_eq0 = vmovn_u16(vceqq_s16(row7, vdupq_n_s16(0))); + uint16x8_t row0 = vld1q_u16(values + 0 * DCTSIZE); + uint16x8_t row1 = vld1q_u16(values + 1 * DCTSIZE); + uint16x8_t row2 = vld1q_u16(values + 2 * DCTSIZE); + uint16x8_t row3 = vld1q_u16(values + 3 * DCTSIZE); + uint16x8_t row4 = vld1q_u16(values + 4 * DCTSIZE); + uint16x8_t row5 = vld1q_u16(values + 5 * DCTSIZE); + uint16x8_t row6 = vld1q_u16(values + 6 * DCTSIZE); + uint16x8_t row7 = vld1q_u16(values + 7 * DCTSIZE); + + uint8x8_t row0_eq0 = vmovn_u16(vceqq_u16(row0, vdupq_n_u16(0))); + uint8x8_t row1_eq0 = vmovn_u16(vceqq_u16(row1, vdupq_n_u16(0))); + uint8x8_t row2_eq0 = vmovn_u16(vceqq_u16(row2, vdupq_n_u16(0))); + uint8x8_t row3_eq0 = vmovn_u16(vceqq_u16(row3, vdupq_n_u16(0))); + uint8x8_t row4_eq0 = vmovn_u16(vceqq_u16(row4, vdupq_n_u16(0))); + uint8x8_t row5_eq0 = vmovn_u16(vceqq_u16(row5, vdupq_n_u16(0))); + uint8x8_t row6_eq0 = vmovn_u16(vceqq_u16(row6, vdupq_n_u16(0))); + uint8x8_t row7_eq0 = vmovn_u16(vceqq_u16(row7, vdupq_n_u16(0))); /* { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 } */ const uint8x8_t bitmap_mask = @@ -274,7 +275,7 @@ void jsimd_encode_mcu_AC_first_prepare_neon int jsimd_encode_mcu_AC_refine_prepare_neon (const JCOEF *block, const int *jpeg_natural_order_start, int Sl, int Al, - JCOEF *absvalues, size_t *bits) + UJCOEF *absvalues, size_t *bits) { /* Temporary storage buffers for data used to compute the signbits bitmap and * the end-of-block (EOB) position @@ -282,7 +283,7 @@ int jsimd_encode_mcu_AC_refine_prepare_neon uint8_t coef_sign_bits[64]; uint8_t coef_eq1_bits[64]; - JCOEF *absvalues_ptr = absvalues; + UJCOEF *absvalues_ptr = absvalues; uint8_t *coef_sign_bits_ptr = coef_sign_bits; uint8_t *eq1_bits_ptr = coef_eq1_bits; @@ -316,18 +317,18 @@ int jsimd_encode_mcu_AC_refine_prepare_neon vst1_u8(coef_sign_bits_ptr + DCTSIZE, sign_coefs2); /* Compute absolute value of coefficients and apply point transform Al. */ - int16x8_t abs_coefs1 = vabsq_s16(coefs1); - int16x8_t abs_coefs2 = vabsq_s16(coefs2); - coefs1 = vshlq_s16(abs_coefs1, vdupq_n_s16(-Al)); - coefs2 = vshlq_s16(abs_coefs2, vdupq_n_s16(-Al)); - vst1q_s16(absvalues_ptr, coefs1); - vst1q_s16(absvalues_ptr + DCTSIZE, coefs2); + uint16x8_t abs_coefs1 = vreinterpretq_u16_s16(vabsq_s16(coefs1)); + uint16x8_t abs_coefs2 = vreinterpretq_u16_s16(vabsq_s16(coefs2)); + abs_coefs1 = vshlq_u16(abs_coefs1, vdupq_n_s16(-Al)); + abs_coefs2 = vshlq_u16(abs_coefs2, vdupq_n_s16(-Al)); + vst1q_u16(absvalues_ptr, abs_coefs1); + vst1q_u16(absvalues_ptr + DCTSIZE, abs_coefs2); /* Test whether transformed coefficient values == 1 (used to find EOB * position.) */ - uint8x8_t coefs_eq11 = vmovn_u16(vceqq_s16(coefs1, vdupq_n_s16(1))); - uint8x8_t coefs_eq12 = vmovn_u16(vceqq_s16(coefs2, vdupq_n_s16(1))); + uint8x8_t coefs_eq11 = vmovn_u16(vceqq_u16(abs_coefs1, vdupq_n_u16(1))); + uint8x8_t coefs_eq12 = vmovn_u16(vceqq_u16(abs_coefs2, vdupq_n_u16(1))); vst1_u8(eq1_bits_ptr, coefs_eq11); vst1_u8(eq1_bits_ptr + DCTSIZE, coefs_eq12); @@ -385,18 +386,18 @@ int jsimd_encode_mcu_AC_refine_prepare_neon vst1_u8(coef_sign_bits_ptr + DCTSIZE, sign_coefs2); /* Compute absolute value of coefficients and apply point transform Al. */ - int16x8_t abs_coefs1 = vabsq_s16(coefs1); - int16x8_t abs_coefs2 = vabsq_s16(coefs2); - coefs1 = vshlq_s16(abs_coefs1, vdupq_n_s16(-Al)); - coefs2 = vshlq_s16(abs_coefs2, vdupq_n_s16(-Al)); - vst1q_s16(absvalues_ptr, coefs1); - vst1q_s16(absvalues_ptr + DCTSIZE, coefs2); + uint16x8_t abs_coefs1 = vreinterpretq_u16_s16(vabsq_s16(coefs1)); + uint16x8_t abs_coefs2 = vreinterpretq_u16_s16(vabsq_s16(coefs2)); + abs_coefs1 = vshlq_u16(abs_coefs1, vdupq_n_s16(-Al)); + abs_coefs2 = vshlq_u16(abs_coefs2, vdupq_n_s16(-Al)); + vst1q_u16(absvalues_ptr, abs_coefs1); + vst1q_u16(absvalues_ptr + DCTSIZE, abs_coefs2); /* Test whether transformed coefficient values == 1 (used to find EOB * position.) */ - uint8x8_t coefs_eq11 = vmovn_u16(vceqq_s16(coefs1, vdupq_n_s16(1))); - uint8x8_t coefs_eq12 = vmovn_u16(vceqq_s16(coefs2, vdupq_n_s16(1))); + uint8x8_t coefs_eq11 = vmovn_u16(vceqq_u16(abs_coefs1, vdupq_n_u16(1))); + uint8x8_t coefs_eq12 = vmovn_u16(vceqq_u16(abs_coefs2, vdupq_n_u16(1))); vst1_u8(eq1_bits_ptr, coefs_eq11); vst1_u8(eq1_bits_ptr + DCTSIZE, coefs_eq12); @@ -444,14 +445,14 @@ int jsimd_encode_mcu_AC_refine_prepare_neon vst1_u8(coef_sign_bits_ptr, sign_coefs); /* Compute absolute value of coefficients and apply point transform Al. */ - int16x8_t abs_coefs = vabsq_s16(coefs); - coefs = vshlq_s16(abs_coefs, vdupq_n_s16(-Al)); - vst1q_s16(absvalues_ptr, coefs); + uint16x8_t abs_coefs = vreinterpretq_u16_s16(vabsq_s16(coefs)); + abs_coefs = vshlq_u16(abs_coefs, vdupq_n_s16(-Al)); + vst1q_u16(absvalues_ptr, abs_coefs); /* Test whether transformed coefficient values == 1 (used to find EOB * position.) */ - uint8x8_t coefs_eq1 = vmovn_u16(vceqq_s16(coefs, vdupq_n_s16(1))); + uint8x8_t coefs_eq1 = vmovn_u16(vceqq_u16(abs_coefs, vdupq_n_u16(1))); vst1_u8(eq1_bits_ptr, coefs_eq1); absvalues_ptr += 8; @@ -462,7 +463,7 @@ int jsimd_encode_mcu_AC_refine_prepare_neon /* Zero remaining memory in blocks. */ for (i = 0; i < rows_to_zero; i++) { - vst1q_s16(absvalues_ptr, vdupq_n_s16(0)); + vst1q_u16(absvalues_ptr, vdupq_n_u16(0)); vst1_u8(coef_sign_bits_ptr, vdup_n_u8(0)); vst1_u8(eq1_bits_ptr, vdup_n_u8(0)); absvalues_ptr += 8; @@ -471,23 +472,23 @@ int jsimd_encode_mcu_AC_refine_prepare_neon } /* Construct zerobits bitmap. */ - int16x8_t abs_row0 = vld1q_s16(absvalues + 0 * DCTSIZE); - int16x8_t abs_row1 = vld1q_s16(absvalues + 1 * DCTSIZE); - int16x8_t abs_row2 = vld1q_s16(absvalues + 2 * DCTSIZE); - int16x8_t abs_row3 = vld1q_s16(absvalues + 3 * DCTSIZE); - int16x8_t abs_row4 = vld1q_s16(absvalues + 4 * DCTSIZE); - int16x8_t abs_row5 = vld1q_s16(absvalues + 5 * DCTSIZE); - int16x8_t abs_row6 = vld1q_s16(absvalues + 6 * DCTSIZE); - int16x8_t abs_row7 = vld1q_s16(absvalues + 7 * DCTSIZE); - - uint8x8_t abs_row0_eq0 = vmovn_u16(vceqq_s16(abs_row0, vdupq_n_s16(0))); - uint8x8_t abs_row1_eq0 = vmovn_u16(vceqq_s16(abs_row1, vdupq_n_s16(0))); - uint8x8_t abs_row2_eq0 = vmovn_u16(vceqq_s16(abs_row2, vdupq_n_s16(0))); - uint8x8_t abs_row3_eq0 = vmovn_u16(vceqq_s16(abs_row3, vdupq_n_s16(0))); - uint8x8_t abs_row4_eq0 = vmovn_u16(vceqq_s16(abs_row4, vdupq_n_s16(0))); - uint8x8_t abs_row5_eq0 = vmovn_u16(vceqq_s16(abs_row5, vdupq_n_s16(0))); - uint8x8_t abs_row6_eq0 = vmovn_u16(vceqq_s16(abs_row6, vdupq_n_s16(0))); - uint8x8_t abs_row7_eq0 = vmovn_u16(vceqq_s16(abs_row7, vdupq_n_s16(0))); + uint16x8_t abs_row0 = vld1q_u16(absvalues + 0 * DCTSIZE); + uint16x8_t abs_row1 = vld1q_u16(absvalues + 1 * DCTSIZE); + uint16x8_t abs_row2 = vld1q_u16(absvalues + 2 * DCTSIZE); + uint16x8_t abs_row3 = vld1q_u16(absvalues + 3 * DCTSIZE); + uint16x8_t abs_row4 = vld1q_u16(absvalues + 4 * DCTSIZE); + uint16x8_t abs_row5 = vld1q_u16(absvalues + 5 * DCTSIZE); + uint16x8_t abs_row6 = vld1q_u16(absvalues + 6 * DCTSIZE); + uint16x8_t abs_row7 = vld1q_u16(absvalues + 7 * DCTSIZE); + + uint8x8_t abs_row0_eq0 = vmovn_u16(vceqq_u16(abs_row0, vdupq_n_u16(0))); + uint8x8_t abs_row1_eq0 = vmovn_u16(vceqq_u16(abs_row1, vdupq_n_u16(0))); + uint8x8_t abs_row2_eq0 = vmovn_u16(vceqq_u16(abs_row2, vdupq_n_u16(0))); + uint8x8_t abs_row3_eq0 = vmovn_u16(vceqq_u16(abs_row3, vdupq_n_u16(0))); + uint8x8_t abs_row4_eq0 = vmovn_u16(vceqq_u16(abs_row4, vdupq_n_u16(0))); + uint8x8_t abs_row5_eq0 = vmovn_u16(vceqq_u16(abs_row5, vdupq_n_u16(0))); + uint8x8_t abs_row6_eq0 = vmovn_u16(vceqq_u16(abs_row6, vdupq_n_u16(0))); + uint8x8_t abs_row7_eq0 = vmovn_u16(vceqq_u16(abs_row7, vdupq_n_u16(0))); /* { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 } */ const uint8x8_t bitmap_mask = diff --git a/simd/arm/jdcolor-neon.c b/simd/arm/jdcolor-neon.c index ea4668f..28dbc57 100644 --- a/simd/arm/jdcolor-neon.c +++ b/simd/arm/jdcolor-neon.c @@ -21,7 +21,6 @@ */ #define JPEG_INTERNALS -#include "jconfigint.h" #include "../../jinclude.h" #include "../../jpeglib.h" #include "../../jsimd.h" diff --git a/simd/arm/jdmerge-neon.c b/simd/arm/jdmerge-neon.c index e4f91fd..18fb9d8 100644 --- a/simd/arm/jdmerge-neon.c +++ b/simd/arm/jdmerge-neon.c @@ -21,7 +21,6 @@ */ #define JPEG_INTERNALS -#include "jconfigint.h" #include "../../jinclude.h" #include "../../jpeglib.h" #include "../../jsimd.h" diff --git a/simd/arm/jidctint-neon.c b/simd/arm/jidctint-neon.c index 043b652..d25112e 100644 --- a/simd/arm/jidctint-neon.c +++ b/simd/arm/jidctint-neon.c @@ -22,7 +22,6 @@ */ #define JPEG_INTERNALS -#include "jconfigint.h" #include "../../jinclude.h" #include "../../jpeglib.h" #include "../../jsimd.h" diff --git a/simd/i386/jsimd.c b/simd/i386/jsimd.c index 80bc821..b429b0a 100644 --- a/simd/i386/jsimd.c +++ b/simd/i386/jsimd.c @@ -2,8 +2,8 @@ * jsimd_i386.c * * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2009-2011, 2013-2014, 2016, 2018, 2022, D. R. Commander. - * Copyright (C) 2015-2016, 2018, Matthieu Darbois. + * Copyright (C) 2009-2011, 2013-2014, 2016, 2018, 2022-2023, D. R. Commander. + * Copyright (C) 2015-2016, 2018, 2022, Matthieu Darbois. * * Based on the x86 SIMD extension for IJG JPEG library, * Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -21,7 +21,6 @@ #include "../../jdct.h" #include "../../jsimddct.h" #include "../jsimd.h" -#include "jconfigint.h" /* * In the PIC cases, we have no guarantee that constants will keep @@ -32,13 +31,11 @@ #define IS_ALIGNED_SSE(ptr) (IS_ALIGNED(ptr, 4)) /* 16 byte alignment */ #define IS_ALIGNED_AVX(ptr) (IS_ALIGNED(ptr, 5)) /* 32 byte alignment */ -static unsigned int simd_support = (unsigned int)(~0); -static unsigned int simd_huffman = 1; +static THREAD_LOCAL unsigned int simd_support = (unsigned int)(~0); +static THREAD_LOCAL unsigned int simd_huffman = 1; /* * Check what SIMD accelerations are supported. - * - * FIXME: This code is racy under a multi-threaded environment. */ LOCAL(void) init_simd(void) @@ -161,6 +158,9 @@ jsimd_rgb_ycc_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, void (*sse2fct) (JDIMENSION, JSAMPARRAY, JSAMPIMAGE, JDIMENSION, int); void (*mmxfct) (JDIMENSION, JSAMPARRAY, JSAMPIMAGE, JDIMENSION, int); + if (simd_support == ~0U) + init_simd(); + switch (cinfo->in_color_space) { case JCS_EXT_RGB: avx2fct = jsimd_extrgb_ycc_convert_avx2; @@ -220,6 +220,9 @@ jsimd_rgb_gray_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, void (*sse2fct) (JDIMENSION, JSAMPARRAY, JSAMPIMAGE, JDIMENSION, int); void (*mmxfct) (JDIMENSION, JSAMPARRAY, JSAMPIMAGE, JDIMENSION, int); + if (simd_support == ~0U) + init_simd(); + switch (cinfo->in_color_space) { case JCS_EXT_RGB: avx2fct = jsimd_extrgb_gray_convert_avx2; @@ -279,6 +282,9 @@ jsimd_ycc_rgb_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, void (*sse2fct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY, int); void (*mmxfct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY, int); + if (simd_support == ~0U) + init_simd(); + switch (cinfo->out_color_space) { case JCS_EXT_RGB: avx2fct = jsimd_ycc_extrgb_convert_avx2; @@ -382,6 +388,9 @@ GLOBAL(void) jsimd_h2v2_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY output_data) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v2_downsample_avx2(cinfo->image_width, cinfo->max_v_samp_factor, compptr->v_samp_factor, @@ -402,6 +411,9 @@ GLOBAL(void) jsimd_h2v1_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY output_data) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v1_downsample_avx2(cinfo->image_width, cinfo->max_v_samp_factor, compptr->v_samp_factor, @@ -464,6 +476,9 @@ GLOBAL(void) jsimd_h2v2_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v2_upsample_avx2(cinfo->max_v_samp_factor, cinfo->output_width, input_data, output_data_ptr); @@ -479,6 +494,9 @@ GLOBAL(void) jsimd_h2v1_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v1_upsample_avx2(cinfo->max_v_samp_factor, cinfo->output_width, input_data, output_data_ptr); @@ -540,6 +558,9 @@ GLOBAL(void) jsimd_h2v2_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v2_fancy_upsample_avx2(cinfo->max_v_samp_factor, compptr->downsampled_width, input_data, @@ -558,6 +579,9 @@ GLOBAL(void) jsimd_h2v1_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v1_fancy_upsample_avx2(cinfo->max_v_samp_factor, compptr->downsampled_width, input_data, @@ -626,6 +650,9 @@ jsimd_h2v2_merged_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, void (*sse2fct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY); void (*mmxfct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY); + if (simd_support == ~0U) + init_simd(); + switch (cinfo->out_color_space) { case JCS_EXT_RGB: avx2fct = jsimd_h2v2_extrgb_merged_upsample_avx2; @@ -684,6 +711,9 @@ jsimd_h2v1_merged_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, void (*sse2fct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY); void (*mmxfct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY); + if (simd_support == ~0U) + init_simd(); + switch (cinfo->out_color_space) { case JCS_EXT_RGB: avx2fct = jsimd_h2v1_extrgb_merged_upsample_avx2; @@ -788,6 +818,9 @@ GLOBAL(void) jsimd_convsamp(JSAMPARRAY sample_data, JDIMENSION start_col, DCTELEM *workspace) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_convsamp_avx2(sample_data, start_col, workspace); else if (simd_support & JSIMD_SSE2) @@ -800,6 +833,9 @@ GLOBAL(void) jsimd_convsamp_float(JSAMPARRAY sample_data, JDIMENSION start_col, FAST_FLOAT *workspace) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_SSE2) jsimd_convsamp_float_sse2(sample_data, start_col, workspace); else if (simd_support & JSIMD_SSE) @@ -870,6 +906,9 @@ jsimd_can_fdct_float(void) GLOBAL(void) jsimd_fdct_islow(DCTELEM *data) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_fdct_islow_avx2(data); else if (simd_support & JSIMD_SSE2) @@ -881,6 +920,9 @@ jsimd_fdct_islow(DCTELEM *data) GLOBAL(void) jsimd_fdct_ifast(DCTELEM *data) { + if (simd_support == ~0U) + init_simd(); + if ((simd_support & JSIMD_SSE2) && IS_ALIGNED_SSE(jconst_fdct_islow_sse2)) jsimd_fdct_ifast_sse2(data); else @@ -890,6 +932,9 @@ jsimd_fdct_ifast(DCTELEM *data) GLOBAL(void) jsimd_fdct_float(FAST_FLOAT *data) { + if (simd_support == ~0U) + init_simd(); + if ((simd_support & JSIMD_SSE) && IS_ALIGNED_SSE(jconst_fdct_float_sse)) jsimd_fdct_float_sse(data); else if (simd_support & JSIMD_3DNOW) @@ -945,6 +990,9 @@ jsimd_can_quantize_float(void) GLOBAL(void) jsimd_quantize(JCOEFPTR coef_block, DCTELEM *divisors, DCTELEM *workspace) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_quantize_avx2(coef_block, divisors, workspace); else if (simd_support & JSIMD_SSE2) @@ -957,6 +1005,9 @@ GLOBAL(void) jsimd_quantize_float(JCOEFPTR coef_block, FAST_FLOAT *divisors, FAST_FLOAT *workspace) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_SSE2) jsimd_quantize_float_sse2(coef_block, divisors, workspace); else if (simd_support & JSIMD_SSE) @@ -1020,6 +1071,9 @@ jsimd_idct_2x2(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col) { + if (simd_support == ~0U) + init_simd(); + if ((simd_support & JSIMD_SSE2) && IS_ALIGNED_SSE(jconst_idct_red_sse2)) jsimd_idct_2x2_sse2(compptr->dct_table, coef_block, output_buf, output_col); @@ -1032,6 +1086,9 @@ jsimd_idct_4x4(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col) { + if (simd_support == ~0U) + init_simd(); + if ((simd_support & JSIMD_SSE2) && IS_ALIGNED_SSE(jconst_idct_red_sse2)) jsimd_idct_4x4_sse2(compptr->dct_table, coef_block, output_buf, output_col); @@ -1126,6 +1183,9 @@ jsimd_idct_islow(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_idct_islow_avx2(compptr->dct_table, coef_block, output_buf, output_col); @@ -1142,6 +1202,9 @@ jsimd_idct_ifast(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col) { + if (simd_support == ~0U) + init_simd(); + if ((simd_support & JSIMD_SSE2) && IS_ALIGNED_SSE(jconst_idct_ifast_sse2)) jsimd_idct_ifast_sse2(compptr->dct_table, coef_block, output_buf, output_col); @@ -1155,6 +1218,9 @@ jsimd_idct_float(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col) { + if (simd_support == ~0U) + init_simd(); + if ((simd_support & JSIMD_SSE2) && IS_ALIGNED_SSE(jconst_idct_float_sse2)) jsimd_idct_float_sse2(compptr->dct_table, coef_block, output_buf, output_col); @@ -1212,7 +1278,7 @@ jsimd_can_encode_mcu_AC_first_prepare(void) GLOBAL(void) jsimd_encode_mcu_AC_first_prepare(const JCOEF *block, const int *jpeg_natural_order_start, int Sl, - int Al, JCOEF *values, size_t *zerobits) + int Al, UJCOEF *values, size_t *zerobits) { jsimd_encode_mcu_AC_first_prepare_sse2(block, jpeg_natural_order_start, Sl, Al, values, zerobits); @@ -1238,7 +1304,7 @@ jsimd_can_encode_mcu_AC_refine_prepare(void) GLOBAL(int) jsimd_encode_mcu_AC_refine_prepare(const JCOEF *block, const int *jpeg_natural_order_start, int Sl, - int Al, JCOEF *absvalues, size_t *bits) + int Al, UJCOEF *absvalues, size_t *bits) { return jsimd_encode_mcu_AC_refine_prepare_sse2(block, jpeg_natural_order_start, diff --git a/simd/jsimd.h b/simd/jsimd.h index 64747c6..a28754a 100644 --- a/simd/jsimd.h +++ b/simd/jsimd.h @@ -2,10 +2,10 @@ * simd/jsimd.h * * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2011, 2014-2016, 2018, 2020, D. R. Commander. + * Copyright (C) 2011, 2014-2016, 2018, 2020, 2022, D. R. Commander. * Copyright (C) 2013-2014, MIPS Technologies, Inc., California. * Copyright (C) 2014, Linaro Limited. - * Copyright (C) 2015-2016, 2018, Matthieu Darbois. + * Copyright (C) 2015-2016, 2018, 2022, Matthieu Darbois. * Copyright (C) 2016-2018, Loongson Technology Corporation Limited, BeiJing. * Copyright (C) 2020, Arm Limited. * @@ -1243,16 +1243,16 @@ EXTERN(JOCTET *) jsimd_huff_encode_one_block_neon_slowtbl /* Progressive Huffman encoding */ EXTERN(void) jsimd_encode_mcu_AC_first_prepare_sse2 (const JCOEF *block, const int *jpeg_natural_order_start, int Sl, int Al, - JCOEF *values, size_t *zerobits); + UJCOEF *values, size_t *zerobits); EXTERN(void) jsimd_encode_mcu_AC_first_prepare_neon (const JCOEF *block, const int *jpeg_natural_order_start, int Sl, int Al, - JCOEF *values, size_t *zerobits); + UJCOEF *values, size_t *zerobits); EXTERN(int) jsimd_encode_mcu_AC_refine_prepare_sse2 (const JCOEF *block, const int *jpeg_natural_order_start, int Sl, int Al, - JCOEF *absvalues, size_t *bits); + UJCOEF *absvalues, size_t *bits); EXTERN(int) jsimd_encode_mcu_AC_refine_prepare_neon (const JCOEF *block, const int *jpeg_natural_order_start, int Sl, int Al, - JCOEF *absvalues, size_t *bits); + UJCOEF *absvalues, size_t *bits); diff --git a/simd/mips/jsimd.c b/simd/mips/jsimd.c index 36ea865..c6e789a 100644 --- a/simd/mips/jsimd.c +++ b/simd/mips/jsimd.c @@ -2,9 +2,9 @@ * jsimd_mips.c * * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2009-2011, 2014, 2016, 2018, 2020, D. R. Commander. + * Copyright (C) 2009-2011, 2014, 2016, 2018, 2020, 2022, D. R. Commander. * Copyright (C) 2013-2014, MIPS Technologies, Inc., California. - * Copyright (C) 2015-2016, 2018, Matthieu Darbois. + * Copyright (C) 2015-2016, 2018, 2022, Matthieu Darbois. * * Based on the x86 SIMD extension for IJG JPEG library, * Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -25,7 +25,7 @@ #include -static unsigned int simd_support = ~0; +static THREAD_LOCAL unsigned int simd_support = ~0; #if !(defined(__mips_dsp) && (__mips_dsp_rev >= 2)) && defined(__linux__) @@ -55,8 +55,6 @@ parse_proc_cpuinfo(const char *search_string) /* * Check what SIMD accelerations are supported. - * - * FIXME: This code is racy under a multi-threaded environment. */ LOCAL(void) init_simd(void) @@ -1126,7 +1124,7 @@ jsimd_can_encode_mcu_AC_first_prepare(void) GLOBAL(void) jsimd_encode_mcu_AC_first_prepare(const JCOEF *block, const int *jpeg_natural_order_start, int Sl, - int Al, JCOEF *values, size_t *zerobits) + int Al, UJCOEF *values, size_t *zerobits) { } @@ -1139,7 +1137,7 @@ jsimd_can_encode_mcu_AC_refine_prepare(void) GLOBAL(int) jsimd_encode_mcu_AC_refine_prepare(const JCOEF *block, const int *jpeg_natural_order_start, int Sl, - int Al, JCOEF *absvalues, size_t *bits) + int Al, UJCOEF *absvalues, size_t *bits) { return 0; } diff --git a/simd/mips64/jsimd.c b/simd/mips64/jsimd.c index 2e626b2..917440b 100644 --- a/simd/mips64/jsimd.c +++ b/simd/mips64/jsimd.c @@ -2,9 +2,9 @@ * jsimd_mips64.c * * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2009-2011, 2014, 2016, 2018, D. R. Commander. + * Copyright (C) 2009-2011, 2014, 2016, 2018, 2022, D. R. Commander. * Copyright (C) 2013-2014, MIPS Technologies, Inc., California. - * Copyright (C) 2015, 2018, Matthieu Darbois. + * Copyright (C) 2015, 2018, 2022, Matthieu Darbois. * Copyright (C) 2016-2018, Loongson Technology Corporation Limited, BeiJing. * * Based on the x86 SIMD extension for IJG JPEG library, @@ -26,7 +26,7 @@ #include -static unsigned int simd_support = ~0; +static THREAD_LOCAL unsigned int simd_support = ~0; #if defined(__linux__) @@ -94,8 +94,6 @@ parse_proc_cpuinfo(int bufsize) /* * Check what SIMD accelerations are supported. - * - * FIXME: This code is racy under a multi-threaded environment. */ LOCAL(void) init_simd(void) @@ -849,7 +847,7 @@ jsimd_can_encode_mcu_AC_first_prepare(void) GLOBAL(void) jsimd_encode_mcu_AC_first_prepare(const JCOEF *block, const int *jpeg_natural_order_start, int Sl, - int Al, JCOEF *values, size_t *zerobits) + int Al, UJCOEF *values, size_t *zerobits) { } @@ -862,7 +860,7 @@ jsimd_can_encode_mcu_AC_refine_prepare(void) GLOBAL(int) jsimd_encode_mcu_AC_refine_prepare(const JCOEF *block, const int *jpeg_natural_order_start, int Sl, - int Al, JCOEF *absvalues, size_t *bits) + int Al, UJCOEF *absvalues, size_t *bits) { return 0; } diff --git a/simd/nasm/jsimdext.inc b/simd/nasm/jsimdext.inc index e8d50b0..bebcb20 100644 --- a/simd/nasm/jsimdext.inc +++ b/simd/nasm/jsimdext.inc @@ -5,6 +5,7 @@ ; Copyright (C) 2010, 2016, 2018-2019, D. R. Commander. ; Copyright (C) 2018, Matthieu Darbois. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library - version 1.02 ; @@ -397,11 +398,11 @@ const_base: %endif %if %1 > 4 push r14 - mov r14, [rax+48] + mov r14, [rbp+48] %endif %if %1 > 5 push r15 - mov r15, [rax+56] + mov r15, [rbp+56] %endif push rsi push rdi diff --git a/simd/powerpc/jsimd.c b/simd/powerpc/jsimd.c index 9a452a3..461f603 100644 --- a/simd/powerpc/jsimd.c +++ b/simd/powerpc/jsimd.c @@ -2,8 +2,8 @@ * jsimd_powerpc.c * * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2009-2011, 2014-2016, 2018, D. R. Commander. - * Copyright (C) 2015-2016, 2018, Matthieu Darbois. + * Copyright (C) 2009-2011, 2014-2016, 2018, 2022, D. R. Commander. + * Copyright (C) 2015-2016, 2018, 2022, Matthieu Darbois. * * Based on the x86 SIMD extension for IJG JPEG library, * Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -41,7 +41,7 @@ #include #endif -static unsigned int simd_support = ~0; +static THREAD_LOCAL unsigned int simd_support = ~0; #if !defined(__ALTIVEC__) && (defined(__linux__) || defined(ANDROID) || defined(__ANDROID__)) @@ -109,8 +109,6 @@ parse_proc_cpuinfo(int bufsize) /* * Check what SIMD accelerations are supported. - * - * FIXME: This code is racy under a multi-threaded environment. */ LOCAL(void) init_simd(void) @@ -867,7 +865,7 @@ jsimd_can_encode_mcu_AC_first_prepare(void) GLOBAL(void) jsimd_encode_mcu_AC_first_prepare(const JCOEF *block, const int *jpeg_natural_order_start, int Sl, - int Al, JCOEF *values, size_t *zerobits) + int Al, UJCOEF *values, size_t *zerobits) { } @@ -880,7 +878,7 @@ jsimd_can_encode_mcu_AC_refine_prepare(void) GLOBAL(int) jsimd_encode_mcu_AC_refine_prepare(const JCOEF *block, const int *jpeg_natural_order_start, int Sl, - int Al, JCOEF *absvalues, size_t *bits) + int Al, UJCOEF *absvalues, size_t *bits) { return 0; } diff --git a/simd/x86_64/jccolext-avx2.asm b/simd/x86_64/jccolext-avx2.asm index ffb527d..dd7ea39 100644 --- a/simd/x86_64/jccolext-avx2.asm +++ b/simd/x86_64/jccolext-avx2.asm @@ -4,6 +4,7 @@ ; Copyright (C) 2009, 2016, D. R. Commander. ; Copyright (C) 2015, Intel Corporation. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -33,7 +34,7 @@ ; r13d = JDIMENSION output_row ; r14d = int num_rows -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_YMMWORD ; ymmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_YMMWORD ; ymmword wk[WK_NUM] %define WK_NUM 8 align 32 @@ -41,12 +42,12 @@ EXTN(jsimd_rgb_ycc_convert_avx2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_YMMWORD) ; align to 256 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, (SIZEOF_YMMWORD * WK_NUM) collect_args 5 push rbx @@ -549,8 +550,8 @@ EXTN(jsimd_rgb_ycc_convert_avx2): pop rbx vzeroupper uncollect_args 5 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jccolext-sse2.asm b/simd/x86_64/jccolext-sse2.asm index af70ed6..bc1e817 100644 --- a/simd/x86_64/jccolext-sse2.asm +++ b/simd/x86_64/jccolext-sse2.asm @@ -3,6 +3,7 @@ ; ; Copyright (C) 2009, 2016, D. R. Commander. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -32,7 +33,7 @@ ; r13d = JDIMENSION output_row ; r14d = int num_rows -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 8 align 32 @@ -40,12 +41,12 @@ EXTN(jsimd_rgb_ycc_convert_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, (SIZEOF_XMMWORD * WK_NUM) collect_args 5 push rbx @@ -474,8 +475,8 @@ EXTN(jsimd_rgb_ycc_convert_sse2): .return: pop rbx uncollect_args 5 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jcgryext-avx2.asm b/simd/x86_64/jcgryext-avx2.asm index ddcc2c0..c8c8d12 100644 --- a/simd/x86_64/jcgryext-avx2.asm +++ b/simd/x86_64/jcgryext-avx2.asm @@ -4,6 +4,7 @@ ; Copyright (C) 2011, 2016, D. R. Commander. ; Copyright (C) 2015, Intel Corporation. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -33,7 +34,7 @@ ; r13d = JDIMENSION output_row ; r14d = int num_rows -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_YMMWORD ; ymmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_YMMWORD ; ymmword wk[WK_NUM] %define WK_NUM 2 align 32 @@ -41,12 +42,12 @@ EXTN(jsimd_rgb_gray_convert_avx2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_YMMWORD) ; align to 256 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (SIZEOF_YMMWORD * WK_NUM) collect_args 5 push rbx @@ -428,8 +429,8 @@ EXTN(jsimd_rgb_gray_convert_avx2): pop rbx vzeroupper uncollect_args 5 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jcgryext-sse2.asm b/simd/x86_64/jcgryext-sse2.asm index f1d399a..7e5a0f2 100644 --- a/simd/x86_64/jcgryext-sse2.asm +++ b/simd/x86_64/jcgryext-sse2.asm @@ -3,6 +3,7 @@ ; ; Copyright (C) 2011, 2016, D. R. Commander. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -32,7 +33,7 @@ ; r13d = JDIMENSION output_row ; r14d = int num_rows -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 2 align 32 @@ -40,12 +41,12 @@ EXTN(jsimd_rgb_gray_convert_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (SIZEOF_XMMWORD * WK_NUM) collect_args 5 push rbx @@ -353,8 +354,8 @@ EXTN(jsimd_rgb_gray_convert_sse2): .return: pop rbx uncollect_args 5 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jchuff-sse2.asm b/simd/x86_64/jchuff-sse2.asm index 9ea6df9..a0eb9ce 100644 --- a/simd/x86_64/jchuff-sse2.asm +++ b/simd/x86_64/jchuff-sse2.asm @@ -1,9 +1,10 @@ ; ; jchuff-sse2.asm - Huffman entropy encoding (64-bit SSE2) ; -; Copyright (C) 2009-2011, 2014-2016, 2019, 2021, D. R. Commander. +; Copyright (C) 2009-2011, 2014-2016, 2019, 2021, 2023, D. R. Commander. ; Copyright (C) 2015, Matthieu Darbois. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -208,15 +209,15 @@ times 1 << 15 db 16 ; rax - buffer ; rbx - temp ; rcx - nbits -; rdx - block --> free_bits +; rdx - code ; rsi - nbits_base ; rdi - t -; rbp - code ; r8 - dctbl --> code_temp ; r9 - actbl ; r10 - state ; r11 - index ; r12 - put_buffer +; r15 - block --> free_bits %define buffer rax %ifdef WIN64 @@ -231,12 +232,11 @@ times 1 << 15 db 16 %define nbitsq rcx %define nbits ecx %define nbitsb cl -%define block rdx +%define codeq rdx +%define code edx %define nbits_base rsi %define t rdi %define td edi -%define codeq rbp -%define code ebp %define dctbl r8 %define actbl r9 %define state r10 @@ -244,6 +244,7 @@ times 1 << 15 db 16 %define indexd r11d %define put_buffer r12 %define put_bufferd r12d +%define block r15 ; Step 1: Re-arrange input data according to jpeg_natural_order ; xx 01 02 03 04 05 06 07 xx 01 08 16 09 02 03 10 @@ -259,6 +260,8 @@ times 1 << 15 db 16 GLOBAL_FUNCTION(jsimd_huff_encode_one_block_sse2) EXTN(jsimd_huff_encode_one_block_sse2): + push rbp + mov rbp, rsp %ifdef WIN64 @@ -266,15 +269,15 @@ EXTN(jsimd_huff_encode_one_block_sse2): ; rdx = JOCTET *buffer ; r8 = JCOEFPTR block ; r9 = int last_dc_val -; [rax+48] = c_derived_tbl *dctbl -; [rax+56] = c_derived_tbl *actbl +; [rbp+48] = c_derived_tbl *dctbl +; [rbp+56] = c_derived_tbl *actbl ;X: X = code stream mov buffer, rdx + push r15 mov block, r8 movups xmm3, XMMWORD [block + 0 * SIZEOF_WORD] ;D: w3 = xx 01 02 03 04 05 06 07 push rbx - push rbp movdqa xmm0, xmm3 ;A: w0 = xx 01 02 03 04 05 06 07 push rsi push rdi @@ -284,12 +287,10 @@ EXTN(jsimd_huff_encode_one_block_sse2): movsx code, word [block] ;Z: code = block[0]; pxor xmm4, xmm4 ;A: w4[i] = 0; sub code, r9d ;Z: code -= last_dc_val; - mov dctbl, POINTER [rsp+6*8+4*8] - mov actbl, POINTER [rsp+6*8+5*8] + mov dctbl, POINTER [rbp+48] + mov actbl, POINTER [rbp+56] punpckldq xmm0, xmm1 ;A: w0 = xx 01 08 09 02 03 10 11 lea nbits_base, [rel jpeg_nbits_table] - add rsp, -DCTSIZE2 * SIZEOF_WORD - mov t, rsp %else @@ -301,9 +302,10 @@ EXTN(jsimd_huff_encode_one_block_sse2): ; r9 = c_derived_tbl *actbl ;X: X = code stream + push r15 + mov block, rdx movups xmm3, XMMWORD [block + 0 * SIZEOF_WORD] ;D: w3 = xx 01 02 03 04 05 06 07 push rbx - push rbp movdqa xmm0, xmm3 ;A: w0 = xx 01 02 03 04 05 06 07 push r12 mov state, rdi @@ -314,10 +316,13 @@ EXTN(jsimd_huff_encode_one_block_sse2): pxor xmm4, xmm4 ;A: w4[i] = 0; sub codeq, rcx ;Z: code -= last_dc_val; punpckldq xmm0, xmm1 ;A: w0 = xx 01 08 09 02 03 10 11 - lea t, [rsp - DCTSIZE2 * SIZEOF_WORD] ; use red zone for t_ %endif + ; Allocate stack space for t array, and realign stack. + add rsp, -DCTSIZE2 * SIZEOF_WORD - 8 + mov t, rsp + pshuflw xmm0, xmm0, 11001001b ;A: w0 = 01 08 xx 09 02 03 10 11 pinsrw xmm0, word [block + 16 * SIZEOF_WORD], 2 ;A: w0 = 01 08 16 09 02 03 10 11 punpckhdq xmm3, xmm1 ;D: w3 = 04 05 12 13 06 07 14 15 @@ -443,9 +448,9 @@ EXTN(jsimd_huff_encode_one_block_sse2): pinsrw xmm5, word [block + 29 * SIZEOF_WORD], 7 ;E: w5 = 42 49 56 57 50 43 36 29 ; (Row 4, offset 1) %undef block -%define free_bitsq rdx -%define free_bitsd edx -%define free_bitsb dl +%define free_bitsq r15 +%define free_bitsd r15d +%define free_bitsb r15b pcmpeqw xmm1, xmm0 ;F: w1[i] = (w1[i] == 0 ? -1 : 0); shl tempq, 48 ;Z: temp <<= 48; pxor xmm2, xmm2 ;E: w2[i] = 0; @@ -534,12 +539,8 @@ EXTN(jsimd_huff_encode_one_block_sse2): test index, index jnz .BLOOP ; } while (index != 0); .ELOOP: ; } /* index != 0 */ - sub td, esp ; t -= (WIN64: &t_[0], UNIX: &t_[64]); -%ifdef WIN64 + sub td, esp ; t -= &t_[0]; cmp td, (DCTSIZE2 - 2) * SIZEOF_WORD ; if (t != 62) -%else - cmp td, -2 * SIZEOF_WORD ; if (t != -2) -%endif je .EFN ; { movzx nbits, byte [actbl + c_derived_tbl.ehufsi + 0] ; nbits = actbl->ehufsi[0]; @@ -556,18 +557,17 @@ EXTN(jsimd_huff_encode_one_block_sse2): ; state->cur.put_buffer.simd = put_buffer; mov byte [state + working_state.cur.free_bits], free_bitsb ; state->cur.free_bits = free_bits; -%ifdef WIN64 - sub rsp, -DCTSIZE2 * SIZEOF_WORD + sub rsp, -DCTSIZE2 * SIZEOF_WORD - 8 pop r12 +%ifdef WIN64 pop rdi pop rsi - pop rbp pop rbx %else - pop r12 - pop rbp pop rbx %endif + pop r15 + pop rbp ret ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/simd/x86_64/jcphuff-sse2.asm b/simd/x86_64/jcphuff-sse2.asm index 01b5c02..11db4b2 100644 --- a/simd/x86_64/jcphuff-sse2.asm +++ b/simd/x86_64/jcphuff-sse2.asm @@ -3,6 +3,7 @@ ; (64-bit SSE2) ; ; Copyright (C) 2016, 2018, Matthieu Darbois +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -282,16 +283,12 @@ EXTN(jsimd_encode_mcu_AC_first_prepare_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [rbp - 16] + sub rsp, SIZEOF_XMMWORD + movdqa XMMWORD [rsp], ZERO collect_args 6 - movdqa XMMWORD [rbp - 16], ZERO - movd AL, r13d pxor ZERO, ZERO mov K, LEN @@ -384,10 +381,9 @@ EXTN(jsimd_encode_mcu_AC_first_prepare_sse2): REDUCE0 - movdqa ZERO, XMMWORD [rbp - 16] uncollect_args 6 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + movdqa ZERO, XMMWORD [rsp] + mov rsp, rbp pop rbp ret @@ -450,16 +446,12 @@ EXTN(jsimd_encode_mcu_AC_first_prepare_sse2): EXTN(jsimd_encode_mcu_AC_refine_prepare_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [rbp - 16] + sub rsp, SIZEOF_XMMWORD + movdqa XMMWORD [rsp], ZERO collect_args 6 - movdqa XMMWORD [rbp - 16], ZERO - xor SIGN, SIGN xor EOB, EOB xor KK, KK @@ -606,10 +598,9 @@ EXTN(jsimd_encode_mcu_AC_refine_prepare_sse2): REDUCE0 mov eax, EOB - movdqa ZERO, XMMWORD [rbp - 16] uncollect_args 6 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + movdqa ZERO, XMMWORD [rsp] + mov rsp, rbp pop rbp ret diff --git a/simd/x86_64/jcsample-avx2.asm b/simd/x86_64/jcsample-avx2.asm index b32527a..589c52b 100644 --- a/simd/x86_64/jcsample-avx2.asm +++ b/simd/x86_64/jcsample-avx2.asm @@ -45,7 +45,6 @@ EXTN(jsimd_h2v1_downsample_avx2): push rbp - mov rax, rsp mov rbp, rsp collect_args 6 @@ -207,7 +206,6 @@ EXTN(jsimd_h2v1_downsample_avx2): EXTN(jsimd_h2v2_downsample_avx2): push rbp - mov rax, rsp mov rbp, rsp collect_args 6 diff --git a/simd/x86_64/jcsample-sse2.asm b/simd/x86_64/jcsample-sse2.asm index 2fcfe45..7a4f1bc 100644 --- a/simd/x86_64/jcsample-sse2.asm +++ b/simd/x86_64/jcsample-sse2.asm @@ -44,7 +44,6 @@ EXTN(jsimd_h2v1_downsample_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 6 @@ -189,7 +188,6 @@ EXTN(jsimd_h2v1_downsample_sse2): EXTN(jsimd_h2v2_downsample_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 6 diff --git a/simd/x86_64/jdcolext-avx2.asm b/simd/x86_64/jdcolext-avx2.asm index 2370fda..070436c 100644 --- a/simd/x86_64/jdcolext-avx2.asm +++ b/simd/x86_64/jdcolext-avx2.asm @@ -5,6 +5,7 @@ ; Copyright (C) 2009, 2012, 2016, D. R. Commander. ; Copyright (C) 2015, Intel Corporation. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -34,7 +35,7 @@ ; r13 = JSAMPARRAY output_buf ; r14d = int num_rows -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_YMMWORD ; ymmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_YMMWORD ; ymmword wk[WK_NUM] %define WK_NUM 2 align 32 @@ -42,12 +43,12 @@ EXTN(jsimd_ycc_rgb_convert_avx2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_YMMWORD) ; align to 256 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (WK_NUM * SIZEOF_YMMWORD) collect_args 5 push rbx @@ -486,8 +487,8 @@ EXTN(jsimd_ycc_rgb_convert_avx2): pop rbx vzeroupper uncollect_args 5 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jdcolext-sse2.asm b/simd/x86_64/jdcolext-sse2.asm index e07c8d7..bba3a30 100644 --- a/simd/x86_64/jdcolext-sse2.asm +++ b/simd/x86_64/jdcolext-sse2.asm @@ -4,6 +4,7 @@ ; Copyright 2009, 2012 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2012, 2016, D. R. Commander. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -33,7 +34,7 @@ ; r13 = JSAMPARRAY output_buf ; r14d = int num_rows -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 2 align 32 @@ -41,12 +42,12 @@ EXTN(jsimd_ycc_rgb_convert_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (SIZEOF_XMMWORD * WK_NUM) collect_args 5 push rbx @@ -429,8 +430,8 @@ EXTN(jsimd_ycc_rgb_convert_sse2): .return: pop rbx uncollect_args 5 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jdmrgext-avx2.asm b/simd/x86_64/jdmrgext-avx2.asm index 8b264b4..1191645 100644 --- a/simd/x86_64/jdmrgext-avx2.asm +++ b/simd/x86_64/jdmrgext-avx2.asm @@ -5,6 +5,7 @@ ; Copyright (C) 2009, 2012, 2016, D. R. Commander. ; Copyright (C) 2015, Intel Corporation. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -34,7 +35,7 @@ ; r12d = JDIMENSION in_row_group_ctr ; r13 = JSAMPARRAY output_buf -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_YMMWORD ; ymmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_YMMWORD ; ymmword wk[WK_NUM] %define WK_NUM 3 align 32 @@ -42,12 +43,12 @@ EXTN(jsimd_h2v1_merged_upsample_avx2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_YMMWORD) ; align to 256 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, SIZEOF_YMMWORD * WK_NUM collect_args 4 push rbx @@ -480,8 +481,8 @@ EXTN(jsimd_h2v1_merged_upsample_avx2): pop rbx vzeroupper uncollect_args 4 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret @@ -506,7 +507,6 @@ EXTN(jsimd_h2v1_merged_upsample_avx2): EXTN(jsimd_h2v2_merged_upsample_avx2): push rbp - mov rax, rsp mov rbp, rsp collect_args 4 push rbx diff --git a/simd/x86_64/jdmrgext-sse2.asm b/simd/x86_64/jdmrgext-sse2.asm index eb3ab9d..8988dd0 100644 --- a/simd/x86_64/jdmrgext-sse2.asm +++ b/simd/x86_64/jdmrgext-sse2.asm @@ -4,6 +4,7 @@ ; Copyright 2009, 2012 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2012, 2016, D. R. Commander. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -33,7 +34,7 @@ ; r12d = JDIMENSION in_row_group_ctr ; r13 = JSAMPARRAY output_buf -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 3 align 32 @@ -41,12 +42,12 @@ EXTN(jsimd_h2v1_merged_upsample_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (SIZEOF_XMMWORD * WK_NUM) collect_args 4 push rbx @@ -422,8 +423,8 @@ EXTN(jsimd_h2v1_merged_upsample_sse2): .return: pop rbx uncollect_args 4 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret @@ -448,7 +449,6 @@ EXTN(jsimd_h2v1_merged_upsample_sse2): EXTN(jsimd_h2v2_merged_upsample_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 4 push rbx diff --git a/simd/x86_64/jdsample-avx2.asm b/simd/x86_64/jdsample-avx2.asm index 1e4979f..c6ddbb5 100644 --- a/simd/x86_64/jdsample-avx2.asm +++ b/simd/x86_64/jdsample-avx2.asm @@ -5,6 +5,7 @@ ; Copyright (C) 2009, 2016, D. R. Commander. ; Copyright (C) 2015, Intel Corporation. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -62,7 +63,6 @@ PW_EIGHT times 16 dw 8 EXTN(jsimd_h2v1_fancy_upsample_avx2): push rbp - mov rax, rsp mov rbp, rsp push_xmm 3 collect_args 4 @@ -208,7 +208,7 @@ EXTN(jsimd_h2v1_fancy_upsample_avx2): ; r12 = JSAMPARRAY input_data ; r13 = JSAMPARRAY *output_data_ptr -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_YMMWORD ; ymmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_YMMWORD ; ymmword wk[WK_NUM] %define WK_NUM 4 align 32 @@ -216,12 +216,12 @@ EXTN(jsimd_h2v1_fancy_upsample_avx2): EXTN(jsimd_h2v2_fancy_upsample_avx2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 - and rsp, byte (-SIZEOF_YMMWORD) ; align to 256 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + mov rbp, rsp + push r15 + and rsp, byte (-SIZEOF_YMMWORD) ; align to 128 bits + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, (SIZEOF_YMMWORD * WK_NUM) push_xmm 3 collect_args 4 push rbx @@ -500,8 +500,8 @@ EXTN(jsimd_h2v2_fancy_upsample_avx2): vzeroupper uncollect_args 4 pop_xmm 3 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret @@ -525,7 +525,6 @@ EXTN(jsimd_h2v2_fancy_upsample_avx2): EXTN(jsimd_h2v1_upsample_avx2): push rbp - mov rax, rsp mov rbp, rsp collect_args 4 @@ -614,7 +613,6 @@ EXTN(jsimd_h2v1_upsample_avx2): EXTN(jsimd_h2v2_upsample_avx2): push rbp - mov rax, rsp mov rbp, rsp collect_args 4 push rbx diff --git a/simd/x86_64/jdsample-sse2.asm b/simd/x86_64/jdsample-sse2.asm index 38dbcee..24cd389 100644 --- a/simd/x86_64/jdsample-sse2.asm +++ b/simd/x86_64/jdsample-sse2.asm @@ -4,6 +4,7 @@ ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, D. R. Commander. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -61,7 +62,6 @@ PW_EIGHT times 8 dw 8 EXTN(jsimd_h2v1_fancy_upsample_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 4 @@ -195,7 +195,7 @@ EXTN(jsimd_h2v1_fancy_upsample_sse2): ; r12 = JSAMPARRAY input_data ; r13 = JSAMPARRAY *output_data_ptr -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 4 align 32 @@ -203,12 +203,12 @@ EXTN(jsimd_h2v1_fancy_upsample_sse2): EXTN(jsimd_h2v2_fancy_upsample_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (SIZEOF_XMMWORD * WK_NUM) collect_args 4 push rbx @@ -473,8 +473,8 @@ EXTN(jsimd_h2v2_fancy_upsample_sse2): .return: pop rbx uncollect_args 4 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret @@ -498,7 +498,6 @@ EXTN(jsimd_h2v2_fancy_upsample_sse2): EXTN(jsimd_h2v1_upsample_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 4 @@ -585,7 +584,6 @@ EXTN(jsimd_h2v1_upsample_sse2): EXTN(jsimd_h2v2_upsample_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 4 push rbx diff --git a/simd/x86_64/jfdctflt-sse.asm b/simd/x86_64/jfdctflt-sse.asm index ef27966..3595496 100644 --- a/simd/x86_64/jfdctflt-sse.asm +++ b/simd/x86_64/jfdctflt-sse.asm @@ -3,6 +3,7 @@ ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, D. R. Commander. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -58,7 +59,7 @@ PD_1_306 times 4 dd 1.306562964876376527856643 ; r10 = FAST_FLOAT *data -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 2 align 32 @@ -66,12 +67,12 @@ PD_1_306 times 4 dd 1.306562964876376527856643 EXTN(jsimd_fdct_float_sse): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (SIZEOF_XMMWORD * WK_NUM) collect_args 1 ; ---- Pass 1: process rows. @@ -345,8 +346,8 @@ EXTN(jsimd_fdct_float_sse): jnz near .columnloop uncollect_args 1 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jfdctfst-sse2.asm b/simd/x86_64/jfdctfst-sse2.asm index 2e1bfe6..d33c58a 100644 --- a/simd/x86_64/jfdctfst-sse2.asm +++ b/simd/x86_64/jfdctfst-sse2.asm @@ -3,6 +3,7 @@ ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, D. R. Commander. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -73,7 +74,7 @@ PW_F1306 times 8 dw F_1_306 << CONST_SHIFT ; r10 = DCTELEM *data -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 2 align 32 @@ -81,12 +82,12 @@ PW_F1306 times 8 dw F_1_306 << CONST_SHIFT EXTN(jsimd_fdct_ifast_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (SIZEOF_XMMWORD * WK_NUM) collect_args 1 ; ---- Pass 1: process rows. @@ -379,8 +380,8 @@ EXTN(jsimd_fdct_ifast_sse2): movdqa XMMWORD [XMMBLOCK(1,0,rdx,SIZEOF_DCTELEM)], xmm2 uncollect_args 1 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jfdctint-avx2.asm b/simd/x86_64/jfdctint-avx2.asm index e56258b..d0afe5e 100644 --- a/simd/x86_64/jfdctint-avx2.asm +++ b/simd/x86_64/jfdctint-avx2.asm @@ -261,7 +261,6 @@ PW_1_NEG1 times 8 dw 1 EXTN(jsimd_fdct_islow_avx2): push rbp - mov rax, rsp mov rbp, rsp collect_args 1 diff --git a/simd/x86_64/jfdctint-sse2.asm b/simd/x86_64/jfdctint-sse2.asm index ec1f383..024ce90 100644 --- a/simd/x86_64/jfdctint-sse2.asm +++ b/simd/x86_64/jfdctint-sse2.asm @@ -3,6 +3,7 @@ ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2020, D. R. Commander. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -94,7 +95,7 @@ PW_DESCALE_P2X times 8 dw 1 << (PASS1_BITS - 1) ; r10 = DCTELEM *data -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 6 align 32 @@ -102,12 +103,12 @@ PW_DESCALE_P2X times 8 dw 1 << (PASS1_BITS - 1) EXTN(jsimd_fdct_islow_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (SIZEOF_XMMWORD * WK_NUM) collect_args 1 ; ---- Pass 1: process rows. @@ -609,8 +610,8 @@ EXTN(jsimd_fdct_islow_sse2): movdqa XMMWORD [XMMBLOCK(3,0,rdx,SIZEOF_DCTELEM)], xmm3 uncollect_args 1 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jidctflt-sse2.asm b/simd/x86_64/jidctflt-sse2.asm index 60bf961..952fbe3 100644 --- a/simd/x86_64/jidctflt-sse2.asm +++ b/simd/x86_64/jidctflt-sse2.asm @@ -4,6 +4,7 @@ ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, D. R. Commander. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -65,8 +66,7 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE ; r12 = JSAMPARRAY output_buf ; r13d = JDIMENSION output_col -%define original_rbp rbp + 0 -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 2 %define workspace wk(0) - DCTSIZE2 * SIZEOF_FAST_FLOAT @@ -77,11 +77,11 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE EXTN(jsimd_idct_float_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp lea rsp, [workspace] collect_args 4 push rbx @@ -322,7 +322,6 @@ EXTN(jsimd_idct_float_sse2): ; ---- Pass 2: process rows from work array, store into output array. - mov rax, [original_rbp] lea rsi, [workspace] ; FAST_FLOAT *wsptr mov rdi, r12 ; (JSAMPROW *) mov eax, r13d @@ -472,8 +471,8 @@ EXTN(jsimd_idct_float_sse2): pop rbx uncollect_args 4 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jidctfst-sse2.asm b/simd/x86_64/jidctfst-sse2.asm index cb97fdf..a3da8d8 100644 --- a/simd/x86_64/jidctfst-sse2.asm +++ b/simd/x86_64/jidctfst-sse2.asm @@ -4,6 +4,7 @@ ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, D. R. Commander. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -86,8 +87,7 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE ; r12 = JSAMPARRAY output_buf ; r13d = JDIMENSION output_col -%define original_rbp rbp + 0 -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 2 @@ -96,12 +96,12 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE EXTN(jsimd_idct_ifast_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (SIZEOF_XMMWORD * WK_NUM) collect_args 4 ; ---- Pass 1: process columns from input. @@ -320,7 +320,6 @@ EXTN(jsimd_idct_ifast_sse2): ; ---- Pass 2: process rows from work array, store into output array. - mov rax, [original_rbp] mov rdi, r12 ; (JSAMPROW *) mov eax, r13d @@ -480,8 +479,8 @@ EXTN(jsimd_idct_ifast_sse2): movq XMM_MMWORD [rsi+rax*SIZEOF_JSAMPLE], xmm2 uncollect_args 4 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret ret diff --git a/simd/x86_64/jidctint-avx2.asm b/simd/x86_64/jidctint-avx2.asm index ca7e317..528da01 100644 --- a/simd/x86_64/jidctint-avx2.asm +++ b/simd/x86_64/jidctint-avx2.asm @@ -283,7 +283,6 @@ PW_1_NEG1 times 8 dw 1 EXTN(jsimd_idct_islow_avx2): push rbp - mov rax, rsp ; rax = original rbp mov rbp, rsp ; rbp = aligned rbp push_xmm 4 collect_args 4 diff --git a/simd/x86_64/jidctint-sse2.asm b/simd/x86_64/jidctint-sse2.asm index 7aa869b..92f633e 100644 --- a/simd/x86_64/jidctint-sse2.asm +++ b/simd/x86_64/jidctint-sse2.asm @@ -4,6 +4,7 @@ ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2020, D. R. Commander. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -99,8 +100,7 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE ; r12 = JSAMPARRAY output_buf ; r13d = JDIMENSION output_col -%define original_rbp rbp + 0 -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 12 @@ -109,12 +109,12 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE EXTN(jsimd_idct_islow_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, (SIZEOF_XMMWORD * WK_NUM) collect_args 4 ; ---- Pass 1: process columns from input. @@ -512,7 +512,6 @@ EXTN(jsimd_idct_islow_sse2): ; ---- Pass 2: process rows from work array, store into output array. - mov rax, [original_rbp] mov rdi, r12 ; (JSAMPROW *) mov eax, r13d @@ -837,8 +836,8 @@ EXTN(jsimd_idct_islow_sse2): movq XMM_MMWORD [rsi+rax*SIZEOF_JSAMPLE], xmm5 uncollect_args 4 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret diff --git a/simd/x86_64/jidctred-sse2.asm b/simd/x86_64/jidctred-sse2.asm index 4ece9d8..1ec500c 100644 --- a/simd/x86_64/jidctred-sse2.asm +++ b/simd/x86_64/jidctred-sse2.asm @@ -4,6 +4,7 @@ ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, D. R. Commander. ; Copyright (C) 2018, Matthias Räncker. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on the x86 SIMD extension for IJG JPEG library ; Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -107,8 +108,7 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE ; r12 = JSAMPARRAY output_buf ; r13d = JDIMENSION output_col -%define original_rbp rbp + 0 -%define wk(i) rbp - (WK_NUM - (i)) * SIZEOF_XMMWORD +%define wk(i) r15 - (WK_NUM - (i)) * SIZEOF_XMMWORD ; xmmword wk[WK_NUM] %define WK_NUM 2 @@ -117,12 +117,12 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE EXTN(jsimd_idct_4x4_sse2): push rbp - mov rax, rsp ; rax = original rbp - sub rsp, byte 4 + mov rbp, rsp + push r15 and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits - mov [rsp], rax - mov rbp, rsp ; rbp = aligned rbp - lea rsp, [wk(0)] + ; Allocate stack space for wk array. r15 is used to access it. + mov r15, rsp + sub rsp, byte (SIZEOF_XMMWORD * WK_NUM) collect_args 4 ; ---- Pass 1: process columns from input. @@ -309,7 +309,6 @@ EXTN(jsimd_idct_4x4_sse2): ; ---- Pass 2: process rows, store into output array. - mov rax, [original_rbp] mov rdi, r12 ; (JSAMPROW *) mov eax, r13d @@ -390,8 +389,8 @@ EXTN(jsimd_idct_4x4_sse2): movd XMM_DWORD [rsi+rax*SIZEOF_JSAMPLE], xmm3 uncollect_args 4 - mov rsp, rbp ; rsp <- aligned rbp - pop rsp ; rsp <- original rbp + lea rsp, [rbp-8] + pop r15 pop rbp ret @@ -415,7 +414,6 @@ EXTN(jsimd_idct_4x4_sse2): EXTN(jsimd_idct_2x2_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 4 push rbx diff --git a/simd/x86_64/jquantf-sse2.asm b/simd/x86_64/jquantf-sse2.asm index ab2e395..232bbb2 100644 --- a/simd/x86_64/jquantf-sse2.asm +++ b/simd/x86_64/jquantf-sse2.asm @@ -38,7 +38,6 @@ EXTN(jsimd_convsamp_float_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 3 push rbx @@ -111,7 +110,6 @@ EXTN(jsimd_convsamp_float_sse2): EXTN(jsimd_quantize_float_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 3 diff --git a/simd/x86_64/jquanti-avx2.asm b/simd/x86_64/jquanti-avx2.asm index 70fe811..66104d7 100644 --- a/simd/x86_64/jquanti-avx2.asm +++ b/simd/x86_64/jquanti-avx2.asm @@ -39,7 +39,6 @@ EXTN(jsimd_convsamp_avx2): push rbp - mov rax, rsp mov rbp, rsp collect_args 3 @@ -117,7 +116,6 @@ EXTN(jsimd_convsamp_avx2): EXTN(jsimd_quantize_avx2): push rbp - mov rax, rsp mov rbp, rsp collect_args 3 diff --git a/simd/x86_64/jquanti-sse2.asm b/simd/x86_64/jquanti-sse2.asm index 3ee4420..11e9f4c 100644 --- a/simd/x86_64/jquanti-sse2.asm +++ b/simd/x86_64/jquanti-sse2.asm @@ -38,7 +38,6 @@ EXTN(jsimd_convsamp_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 3 push rbx @@ -117,7 +116,6 @@ EXTN(jsimd_convsamp_sse2): EXTN(jsimd_quantize_sse2): push rbp - mov rax, rsp mov rbp, rsp collect_args 3 diff --git a/simd/x86_64/jsimd.c b/simd/x86_64/jsimd.c index 584a010..3f5ee77 100644 --- a/simd/x86_64/jsimd.c +++ b/simd/x86_64/jsimd.c @@ -2,8 +2,8 @@ * jsimd_x86_64.c * * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2009-2011, 2014, 2016, 2018, 2022, D. R. Commander. - * Copyright (C) 2015-2016, 2018, Matthieu Darbois. + * Copyright (C) 2009-2011, 2014, 2016, 2018, 2022-2023, D. R. Commander. + * Copyright (C) 2015-2016, 2018, 2022, Matthieu Darbois. * * Based on the x86 SIMD extension for IJG JPEG library, * Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -21,7 +21,6 @@ #include "../../jdct.h" #include "../../jsimddct.h" #include "../jsimd.h" -#include "jconfigint.h" /* * In the PIC cases, we have no guarantee that constants will keep @@ -32,13 +31,11 @@ #define IS_ALIGNED_SSE(ptr) (IS_ALIGNED(ptr, 4)) /* 16 byte alignment */ #define IS_ALIGNED_AVX(ptr) (IS_ALIGNED(ptr, 5)) /* 32 byte alignment */ -static unsigned int simd_support = (unsigned int)(~0); -static unsigned int simd_huffman = 1; +static THREAD_LOCAL unsigned int simd_support = (unsigned int)(~0); +static THREAD_LOCAL unsigned int simd_huffman = 1; /* * Check what SIMD accelerations are supported. - * - * FIXME: This code is racy under a multi-threaded environment. */ LOCAL(void) init_simd(void) @@ -148,6 +145,9 @@ jsimd_rgb_ycc_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, void (*avx2fct) (JDIMENSION, JSAMPARRAY, JSAMPIMAGE, JDIMENSION, int); void (*sse2fct) (JDIMENSION, JSAMPARRAY, JSAMPIMAGE, JDIMENSION, int); + if (simd_support == ~0U) + init_simd(); + switch (cinfo->in_color_space) { case JCS_EXT_RGB: avx2fct = jsimd_extrgb_ycc_convert_avx2; @@ -197,6 +197,9 @@ jsimd_rgb_gray_convert(j_compress_ptr cinfo, JSAMPARRAY input_buf, void (*avx2fct) (JDIMENSION, JSAMPARRAY, JSAMPIMAGE, JDIMENSION, int); void (*sse2fct) (JDIMENSION, JSAMPARRAY, JSAMPIMAGE, JDIMENSION, int); + if (simd_support == ~0U) + init_simd(); + switch (cinfo->in_color_space) { case JCS_EXT_RGB: avx2fct = jsimd_extrgb_gray_convert_avx2; @@ -246,6 +249,9 @@ jsimd_ycc_rgb_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, void (*avx2fct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY, int); void (*sse2fct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY, int); + if (simd_support == ~0U) + init_simd(); + switch (cinfo->out_color_space) { case JCS_EXT_RGB: avx2fct = jsimd_ycc_extrgb_convert_avx2; @@ -336,6 +342,9 @@ GLOBAL(void) jsimd_h2v2_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY output_data) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v2_downsample_avx2(cinfo->image_width, cinfo->max_v_samp_factor, compptr->v_samp_factor, @@ -352,6 +361,9 @@ GLOBAL(void) jsimd_h2v1_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY output_data) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v1_downsample_avx2(cinfo->image_width, cinfo->max_v_samp_factor, compptr->v_samp_factor, @@ -406,6 +418,9 @@ GLOBAL(void) jsimd_h2v2_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v2_upsample_avx2(cinfo->max_v_samp_factor, cinfo->output_width, input_data, output_data_ptr); @@ -418,6 +433,9 @@ GLOBAL(void) jsimd_h2v1_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v1_upsample_avx2(cinfo->max_v_samp_factor, cinfo->output_width, input_data, output_data_ptr); @@ -472,6 +490,9 @@ GLOBAL(void) jsimd_h2v2_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v2_fancy_upsample_avx2(cinfo->max_v_samp_factor, compptr->downsampled_width, input_data, @@ -486,6 +507,9 @@ GLOBAL(void) jsimd_h2v1_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_h2v1_fancy_upsample_avx2(cinfo->max_v_samp_factor, compptr->downsampled_width, input_data, @@ -545,6 +569,9 @@ jsimd_h2v2_merged_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, void (*avx2fct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY); void (*sse2fct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY); + if (simd_support == ~0U) + init_simd(); + switch (cinfo->out_color_space) { case JCS_EXT_RGB: avx2fct = jsimd_h2v2_extrgb_merged_upsample_avx2; @@ -593,6 +620,9 @@ jsimd_h2v1_merged_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, void (*avx2fct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY); void (*sse2fct) (JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY); + if (simd_support == ~0U) + init_simd(); + switch (cinfo->out_color_space) { case JCS_EXT_RGB: avx2fct = jsimd_h2v1_extrgb_merged_upsample_avx2; @@ -682,6 +712,9 @@ GLOBAL(void) jsimd_convsamp(JSAMPARRAY sample_data, JDIMENSION start_col, DCTELEM *workspace) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_convsamp_avx2(sample_data, start_col, workspace); else @@ -751,6 +784,9 @@ jsimd_can_fdct_float(void) GLOBAL(void) jsimd_fdct_islow(DCTELEM *data) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_fdct_islow_avx2(data); else @@ -812,6 +848,9 @@ jsimd_can_quantize_float(void) GLOBAL(void) jsimd_quantize(JCOEFPTR coef_block, DCTELEM *divisors, DCTELEM *workspace) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_quantize_avx2(coef_block, divisors, workspace); else @@ -966,6 +1005,9 @@ jsimd_idct_islow(j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col) { + if (simd_support == ~0U) + init_simd(); + if (simd_support & JSIMD_AVX2) jsimd_idct_islow_avx2(compptr->dct_table, coef_block, output_buf, output_col); @@ -1036,7 +1078,7 @@ jsimd_can_encode_mcu_AC_first_prepare(void) GLOBAL(void) jsimd_encode_mcu_AC_first_prepare(const JCOEF *block, const int *jpeg_natural_order_start, int Sl, - int Al, JCOEF *values, size_t *zerobits) + int Al, UJCOEF *values, size_t *zerobits) { jsimd_encode_mcu_AC_first_prepare_sse2(block, jpeg_natural_order_start, Sl, Al, values, zerobits); @@ -1060,7 +1102,7 @@ jsimd_can_encode_mcu_AC_refine_prepare(void) GLOBAL(int) jsimd_encode_mcu_AC_refine_prepare(const JCOEF *block, const int *jpeg_natural_order_start, int Sl, - int Al, JCOEF *absvalues, size_t *bits) + int Al, UJCOEF *absvalues, size_t *bits) { return jsimd_encode_mcu_AC_refine_prepare_sse2(block, jpeg_natural_order_start, diff --git a/simd/x86_64/jsimdcpu.asm b/simd/x86_64/jsimdcpu.asm index 705f813..251bc4c 100644 --- a/simd/x86_64/jsimdcpu.asm +++ b/simd/x86_64/jsimdcpu.asm @@ -3,6 +3,7 @@ ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, D. R. Commander. +; Copyright (C) 2023, Aliaksiej Kandracienka. ; ; Based on ; x86 SIMD extension for IJG JPEG library @@ -31,6 +32,8 @@ GLOBAL_FUNCTION(jpeg_simd_cpu_support) EXTN(jpeg_simd_cpu_support): + push rbp + mov rbp, rsp push rbx push rdi @@ -79,6 +82,7 @@ EXTN(jpeg_simd_cpu_support): pop rdi pop rbx + pop rbp ret ; For some reason, the OS X linker does not honor the request to align the diff --git a/strtest.c b/strtest.c index 88b568c..3d5e004 100644 --- a/strtest.c +++ b/strtest.c @@ -1,5 +1,5 @@ /* - * Copyright (C)2022 D. R. Commander. All Rights Reserved. + * Copyright (C)2022-2023 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -55,8 +55,12 @@ void invalid_parameter_handler(const wchar_t *expression, int main(int argc, char **argv) { +#if !defined(NO_GETENV) || !defined(NO_PUTENV) int err; +#endif +#ifndef NO_GETENV char env[3]; +#endif #ifdef _MSC_VER _set_invalid_parameter_handler(invalid_parameter_handler); diff --git a/structure.txt b/structure.txt index 15b8d37..030b8e8 100644 --- a/structure.txt +++ b/structure.txt @@ -2,8 +2,10 @@ IJG JPEG LIBRARY: SYSTEM ARCHITECTURE This file was part of the Independent JPEG Group's software: Copyright (C) 1991-2012, Thomas G. Lane, Guido Vollbeding. -It was modified by The libjpeg-turbo Project to include only information -relevant to libjpeg-turbo. +Lossless JPEG Modifications: +Copyright (C) 1999, Ken Murchison. +libjpeg-turbo Modifications: +Copyright (C) 2022-2023, D. R. Commander. For conditions of distribution and use, see the accompanying README.ijg file. @@ -23,8 +25,10 @@ In this document, JPEG-specific terminology follows the JPEG standard: A "sample" is a single component value (i.e., one number in the image data). A "coefficient" is a frequency coefficient (a DCT transform output number). A "block" is an 8x8 group of samples or coefficients. - An "MCU" (minimum coded unit) is an interleaved set of blocks of size - determined by the sampling factors, or a single block in a + A "data unit" is an abstract data type that is either a block for lossy + (DCT-based) codecs or a sample for lossless (predictive) codecs. + An "MCU" (minimum coded unit) is an interleaved set of data units of size + determined by the sampling factors, or a single data unit in a noninterleaved scan. We do not use the terms "pixel" and "sample" interchangeably. When we say pixel, we mean an element of the full-size image, while a sample is an element @@ -45,22 +49,14 @@ command-line user interface and I/O routines for several uncompressed image formats. This document concentrates on the library itself. We desire the library to be capable of supporting all JPEG baseline, extended -sequential, and progressive DCT processes. Hierarchical processes are not -supported. - -The library does not support the lossless (spatial) JPEG process. Lossless -JPEG shares little or no code with lossy JPEG, and would normally be used -without the extensive pre- and post-processing provided by this library. -We feel that lossless JPEG is better handled by a separate library. +sequential, progressive DCT, and lossless (spatial) processes. Hierarchical +processes are not supported. Within these limits, any set of compression parameters allowed by the JPEG spec should be readable for decompression. (We can be more restrictive about what formats we can generate.) Although the system design allows for all parameter values, some uncommon settings are not yet implemented and may -never be; nonintegral sampling ratios are the prime example. Furthermore, -we treat 8-bit vs. 12-bit data precision as a compile-time switch, not a -run-time option, because most machines can store 8-bit pixels much more -compactly than 12-bit. +never be; nonintegral sampling ratios are the prime example. By itself, the library handles only interchange JPEG datastreams --- in particular the widely used JFIF file format. The library can be used by @@ -103,9 +99,13 @@ elements: * Color space conversion (e.g., RGB to YCbCr). * Edge expansion and downsampling. Optionally, this step can do simple smoothing --- this is often helpful for low-quality source data. - JPEG proper: + Lossy JPEG proper: * MCU assembly, DCT, quantization. * Entropy coding (sequential or progressive, Huffman or arithmetic). + Lossless JPEG proper: + * Point transform. + * Prediction, differencing. + * Entropy coding (Huffman or arithmetic) In addition to these modules we need overall control, marker generation, and support code (memory management & error handling). There is also a @@ -115,9 +115,13 @@ do something else with the data. The decompressor library contains the following main elements: - JPEG proper: + Lossy JPEG proper: * Entropy decoding (sequential or progressive, Huffman or arithmetic). * Dequantization, inverse DCT, MCU disassembly. + Lossless JPEG proper: + * Entropy decoding (Huffman or arithmetic). + * Prediction, undifferencing. + * Point transform, sample size scaling. Postprocessing: * Upsampling. Optionally, this step may be able to do more general rescaling of the image. @@ -283,7 +287,8 @@ task performed by any one controller. *** Compression object structure *** -Here is a sketch of the logical structure of the JPEG compression library: +Here is a sketch of the logical structure of the JPEG compression library in +lossy mode: |-- Colorspace conversion |-- Preprocessing controller --| @@ -293,6 +298,19 @@ Main controller --| |-- Coefficient controller --| |-- Entropy encoding +... and in lossless mode: + + |-- Colorspace conversion + |-- Preprocessing controller --| + | |-- Downsampling +Main controller --| + | |-- Point transform + | | + |-- Difference controller --|-- Prediction, differencing + | + |-- Lossless mode entropy + encoding + This sketch also describes the flow of control (subroutine calls) during typical image data processing. Each of the components shown in the diagram is an "object" which may have several different implementations available. One @@ -346,6 +364,22 @@ The objects shown above are: during each pass, and the coder must emit the appropriate subset of coefficients. +* Difference controller: buffer controller for the spatial difference data. + When emitting a multiscan JPEG file, this controller is responsible for + buffering the full image. The equivalent of one fully interleaved MCU row + of subsampled data is processed per call, even when the JPEG file is + noninterleaved. + +* Point transform: Downscale the data by the point transform value. + +* Prediction and differencing: Calculate the predictor and subtract it + from the input. Works on one scanline per call. The difference + controller supplies the prior scanline, which is used for prediction. + +* Lossless mode entropy encoding: Perform Huffman or arithmetic entropy coding + and emit the coded data to the data destination module. This module handles + MCU assembly. Works on one MCU row per call. + In addition to the above objects, the compression library includes these objects: @@ -386,7 +420,8 @@ decompression; the progress monitor, if used, may be shared as well. *** Decompression object structure *** -Here is a sketch of the logical structure of the JPEG decompression library: +Here is a sketch of the logical structure of the JPEG decompression library in +lossy mode: |-- Entropy decoding |-- Coefficient controller --| @@ -397,6 +432,20 @@ Main controller --| |-- Color quantization |-- Color precision reduction +... and in lossless mode: + + |-- Lossless mode entropy + | decoding + | + |-- Difference controller --|-- Prediction, undifferencing + | | + | |-- Point transform, sample size + | scaling +Main controller --| + | |-- Upsampling + |-- Postprocessing controller --| + |-- Color precision reduction + As before, this diagram also represents typical control flow. The objects shown are: @@ -430,6 +479,22 @@ shown are: that emit fewer samples per DCT block, not the full 8x8. Works on one DCT block at a time. +* Difference controller: buffer controller for the spatial difference data. + When reading a multiscan JPEG file, this controller is responsible for + buffering the full image. The equivalent of one fully interleaved MCU row + is processed per call, even when the source JPEG file is noninterleaved. + +* Lossless mode entropy decoding: Read coded data from the data source module + and perform Huffman or arithmetic entropy decoding. Works on one MCU row per + call. + +* Prediction and undifferencing: Calculate the predictor and add it to the + decoded difference. Works on one scanline per call. The difference + controller supplies the prior scanline, which is used for prediction. + +* Point transform and sample size scaling: Upscale the data by the point + transform value and downscale it to fit into the compiled-in sample size. + * Postprocessing controller: buffer controller for the color quantization input buffer, when quantization is in use. (Without quantization, this controller just calls the upsampler.) For two-pass quantization, this @@ -453,9 +518,9 @@ shown are: is not used for full-color output. Works on one pixel row at a time; may require two passes to generate a color map. Note that the output will always be a single component representing colormap indexes. In the current - design, the output values are JSAMPLEs, so an 8-bit compilation cannot - quantize to more than 256 colors. This is unlikely to be a problem in - practice. + design, the output values are JSAMPLEs, J12SAMPLEs, or J16SAMPLEs, so the + library cannot quantize to more than 256 colors when using 8-bit data + precision. This is unlikely to be a problem in practice. * Color reduction: this module handles color precision reduction, e.g., generating 15-bit color (5 bits/primary) from JPEG's 24-bit output. @@ -541,28 +606,44 @@ there isn't any real need for it. *** Data formats *** -Arrays of pixel sample values use the following data structure: +Arrays of 8-bit pixel sample values use the following data structure: typedef something JSAMPLE; a pixel component value, 0..MAXJSAMPLE typedef JSAMPLE *JSAMPROW; ptr to a row of samples typedef JSAMPROW *JSAMPARRAY; ptr to a list of rows typedef JSAMPARRAY *JSAMPIMAGE; ptr to a list of color-component arrays -The basic element type JSAMPLE will be one of unsigned char or short. Short -will be used if samples wider than 8 bits are to be supported (this is a -compile-time option). Otherwise, unsigned char is used. +Arrays of 12-bit pixel sample values use the following data structure: + + typedef something J12SAMPLE; a pixel component value, 0..MAXJ12SAMPLE + typedef J12SAMPLE *J12SAMPROW; ptr to a row of samples + typedef J12SAMPROW *J12SAMPARRAY; ptr to a list of rows + typedef J12SAMPARRAY *J12SAMPIMAGE; ptr to a list of color-component arrays + +Arrays of 16-bit pixel sample values use the following data structure: -With these conventions, JSAMPLE values can be assumed to be >= 0. This helps + typedef something J16SAMPLE; a pixel component value, 0..MAXJ16SAMPLE + typedef J16SAMPLE *J16SAMPROW; ptr to a row of samples + typedef J16SAMPROW *J16SAMPARRAY; ptr to a list of rows + typedef J16SAMPARRAY *J16SAMPIMAGE; ptr to a list of color-component arrays + +The basic element type JSAMPLE (8-bit sample) will be unsigned char, the basic +element type J12SAMPLE (12-bit sample) will be short, and the basic element +type J16SAMPLE (16-bit sample) will be unsigned short. + +With these conventions, J*SAMPLE values can be assumed to be >= 0. This helps simplify correct rounding during downsampling, etc. The JPEG standard's -specification that sample values run from -128..127 is accommodated by +specification that 8-bit sample values run from -128..127 is accommodated by subtracting 128 from the sample value in the DCT step. Similarly, during decompression the output of the IDCT step will be immediately shifted back to -0..255. (NB: different values are required when 12-bit samples are in use. -The code is written in terms of MAXJSAMPLE and CENTERJSAMPLE, which will be -defined as 255 and 128 respectively in an 8-bit implementation, and as 4095 -and 2048 in a 12-bit implementation.) - -We use a pointer per row, rather than a two-dimensional JSAMPLE array. This +0..255. (NOTE: different values are required when 12-bit samples are in use. +When 8-bit samples are in use, the code uses MAXJSAMPLE and CENTERJSAMPLE, +which are defined as 255 and 128 respectively. When 12-bit samples are in use, +the code uses MAXJ12SAMPLE and CENTERJ12SAMPLE, which are defined as 4095 and +2048 respectively. When 16-bit samples are in use, the code uses MAXJ16SAMPLE +and CENTERJ16SAMPLE, which are defined as 65535 and 32768 respectively.) + +We use a pointer per row, rather than a two-dimensional J*SAMPLE array. This choice costs only a small amount of memory and has several benefits: * Code using the data structure doesn't need to know the allocated width of the rows. This simplifies edge expansion/compression, since we can work @@ -592,9 +673,9 @@ be precomputed by copying the relevant pointer. Since most image-processing applications prefer to work on images in which the components of a pixel are stored together, the data passed to or from the surrounding application uses the traditional convention: a single pixel is -represented by N consecutive JSAMPLE values, and an image row is an array of -(# of color components)*(image width) JSAMPLEs. One or more rows of data can -be represented by a pointer of type JSAMPARRAY in this scheme. This scheme is +represented by N consecutive J*SAMPLE values, and an image row is an array of +(# of color components)*(image width) J*SAMPLEs. One or more rows of data can +be represented by a pointer of type J*SAMPARRAY in this scheme. This scheme is converted to component-wise storage inside the JPEG library. (Applications that want to skip JPEG preprocessing or postprocessing will have to contend with component-wise storage.) @@ -733,17 +814,17 @@ The memory manager deals with three kinds of object: pointers on MS-DOS machines.) Note that individual "large" objects cannot exceed the size allowed by type size_t, which may be 64K or less on some machines. -3. "Virtual" objects. These are large 2-D arrays of JSAMPLEs or JBLOCKs +3. "Virtual" objects. These are large 2-D arrays of J*SAMPLEs or JBLOCKs (typically large enough for the entire image being processed). The memory manager provides stripwise access to these arrays. On machines without virtual memory, the rest of the array may be swapped out to a temporary file. -(Note: JSAMPARRAY and JBLOCKARRAY data structures are a combination of large +(Note: J*SAMPARRAY and JBLOCKARRAY data structures are a combination of large objects for the data proper and small objects for the row pointers. For convenience and speed, the memory manager provides single routines to create these structures. Similarly, virtual arrays include a small control block -and a JSAMPARRAY or JBLOCKARRAY working buffer, all created with one call.) +and a J*SAMPARRAY or JBLOCKARRAY working buffer, all created with one call.) In the present implementation, virtual arrays are only permitted to have image lifespan. (Permanent lifespan would not be reasonable, and pass lifespan is @@ -770,7 +851,7 @@ To support all this, we establish the following protocol for doing business with the memory manager: 1. Modules must request virtual arrays (which may have only image lifespan) during the initial setup phase, i.e., in their jinit_xxx routines. - 2. All "large" objects (including JSAMPARRAYs and JBLOCKARRAYs) must also be + 2. All "large" objects (including J*SAMPARRAYs and JBLOCKARRAYs) must also be allocated during initial setup. 3. realize_virt_arrays will be called at the completion of initial setup. The above conventions ensure that sufficient information is available 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_TJG(); + if (tj3Set(handle, TJPARAM_STOPONWARNING, stopOnWarning) == -1) + THROW_TJ(); + if (tj3Set(handle, TJPARAM_BOTTOMUP, bottomUp) == -1) + THROW_TJ(); + if (tj3Set(handle, TJPARAM_FASTUPSAMPLE, fastUpsample) == -1) + THROW_TJ(); + if (tj3Set(handle, TJPARAM_FASTDCT, fastDCT) == -1) + THROW_TJ(); + if (tj3Set(handle, TJPARAM_SCANLIMIT, limitScans ? 500 : 0) == -1) + THROW_TJ(); + + if (IS_CROPPED(cr)) { + if (tj3DecompressHeader(handle, jpegBufs[0], jpegSizes[0]) == -1) + THROW_TJ(); + } + if (tj3SetScalingFactor(handle, sf) == -1) + THROW_TJ(); + if (tj3SetCroppingRegion(handle, cr) == -1) + THROW_TJ(); + 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, yuvPad, height, subsamp); + size_t yuvSize = tj3YUVBufSize(width, yuvAlign, height, subsamp); - if (yuvSize == (unsigned long)-1) - THROW_TJ("allocating YUV buffer"); + if (yuvSize == 0) + THROW_TJG(); if ((yuvBuf = (unsigned char *)malloc(yuvSize)) == NULL) THROW_UNIX("allocating YUV buffer"); memset(yuvBuf, 127, yuvSize); @@ -199,27 +257,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, yuvPad, height, flags) == -1) - THROW_TJ("executing tjDecompressToYUV2()"); + if (tj3DecompressToYUV8(handle, jpegBufs[tile], jpegSizes[tile], + yuvBuf, yuvAlign) == -1) + THROW_TJ(); startDecode = getTime(); - if (tjDecodeYUV(handle, yuvBuf, yuvPad, 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(); 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(); + } else if (precision == 12) { + if (tj3Decompress12(handle, jpegBufs[tile], jpegSizes[tile], + (short *)dstPtr2, pitch, pf) == -1) + THROW_TJ(); + } else { + if (tj3Decompress16(handle, jpegBufs[tile], jpegSizes[tile], + (unsigned short *)dstPtr2, pitch, pf) == -1) + THROW_TJ(); + } + } } } elapsed += getTime() - start; @@ -233,9 +302,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, @@ -269,80 +335,58 @@ static int decomp(unsigned char *srcBuf, unsigned char **jpegBuf, if (decompOnly) SNPRINTF(tempStr, 1024, "%s_%s.%s", fileName, sizeStr, ext); else - SNPRINTF(tempStr, 1024, "%s_%s%s_%s.%s", fileName, subName[subsamp], - qualStr, sizeStr, ext); - - if (tjSaveImage(tempStr, dstBuf, scaledw, 0, scaledh, pf, flags) == -1) - THROW_TJG("saving bitmap"); - 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 == TJ_GRAYSCALE) { - 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) - THROW_TJG("saving bitmap"); + SNPRINTF(tempStr, 1024, "%s_%s%s_%s.%s", fileName, + lossless ? "LOSSLS" : subName[subsamp], qualStr, sizeStr, ext); + + if (precision == 8) { + if (tj3SaveImage8(handle, tempStr, (unsigned char *)dstBuf, scaledw, 0, + scaledh, pf) == -1) + THROW_TJ(); + } else if (precision == 12) { + if (tj3SaveImage12(handle, tempStr, (short *)dstBuf, scaledw, 0, scaledh, + pf) == -1) + THROW_TJ(); + } else { + if (tj3SaveImage16(handle, tempStr, (unsigned short *)dstBuf, scaledw, 0, + scaledh, pf) == -1) + THROW_TJ(); } 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) { @@ -351,38 +395,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) - THROW("getting buffer size", "Image is too large"); - if ((jpegBuf[i] = (unsigned char *) - tjAlloc(tjBufSize(tilew, tileh, subsamp))) == NULL) + size_t jpegBufSize = tj3JPEGBufSize(tilew, tileh, subsamp); + + if (jpegBufSize == 0) + THROW_TJG(); + 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(); + if (tj3Set(handle, TJPARAM_SUBSAMP, subsamp) == -1) + THROW_TJ(); + if (tj3Set(handle, TJPARAM_FASTDCT, fastDCT) == -1) + THROW_TJ(); + if (tj3Set(handle, TJPARAM_OPTIMIZE, optimize) == -1) + THROW_TJ(); + if (tj3Set(handle, TJPARAM_PROGRESSIVE, progressive) == -1) + THROW_TJ(); + if (tj3Set(handle, TJPARAM_ARITHMETIC, arithmetic) == -1) + THROW_TJ(); + if (tj3Set(handle, TJPARAM_LOSSLESS, lossless) == -1) + THROW_TJ(); + if (lossless) { + if (tj3Set(handle, TJPARAM_LOSSLESSPSV, jpegQual) == -1) + THROW_TJ(); + } else { + if (tj3Set(handle, TJPARAM_QUALITY, jpegQual) == -1) + THROW_TJ(); + } + if (tj3Set(handle, TJPARAM_RESTARTBLOCKS, restartIntervalBlocks) == -1) + THROW_TJ(); + if (tj3Set(handle, TJPARAM_RESTARTROWS, restartIntervalRows) == -1) + THROW_TJ(); if (doYUV) { - yuvSize = tjBufSizeYUV2(tilew, yuvPad, tileh, subsamp); - if (yuvSize == (unsigned long)-1) - THROW_TJ("allocating YUV buffer"); + yuvSize = tj3YUVBufSize(tilew, yuvAlign, tileh, subsamp); + if (yuvSize == 0) + THROW_TJG(); if ((yuvBuf = (unsigned char *)malloc(yuvSize)) == NULL) THROW_UNIX("allocating YUV buffer"); memset(yuvBuf, 127, yuvSize); @@ -397,30 +473,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, - yuvPad, subsamp, flags) == -1) - THROW_TJ("executing tjEncodeYUV3()"); + if (tj3EncodeYUV8(handle, srcPtr2, width, pitch, height, pf, + yuvBuf, yuvAlign) == -1) + THROW_TJ(); if (iter >= 0) elapsedEncode += getTime() - startEncode; - if (tjCompressFromYUV(handle, yuvBuf, width, yuvPad, 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(); } 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(); + } else if (precision == 12) { + if (tj3Compress12(handle, (short *)srcPtr2, width, pitch, height, + pf, &jpegBufs[tile], &jpegSizes[tile]) == -1) + THROW_TJ(); + } else { + if (tj3Compress16(handle, (unsigned short *)srcPtr2, width, + pitch, height, pf, &jpegBufs[tile], + &jpegSizes[tile]) == -1) + THROW_TJ(); + } } - totalJpegSize += jpegSize[tile]; + totalJpegSize += jpegSizes[tile]; } } elapsed += getTime() - start; @@ -434,9 +519,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) @@ -457,7 +539,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", @@ -468,8 +551,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", @@ -478,11 +561,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); @@ -490,17 +574,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; } @@ -510,15 +594,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; } @@ -527,22 +610,22 @@ 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; char *temp = NULL, tempStr[80], tempStr2[80]; /* Original image */ - int w = 0, h = 0, tilew, tileh, ntilesw = 1, ntilesh = 1, subsamp = -1, - cs = -1; + int w = 0, h = 0, minTile = 16, tilew, tileh, ntilesw = 1, ntilesh = 1, + subsamp = -1, cs = -1; /* Transformed image */ 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"); @@ -555,68 +638,114 @@ static int decompTest(char *fileName) temp = strrchr(fileName, '.'); if (temp != NULL) *temp = '\0'; - if ((handle = tjInitTransform()) == NULL) - THROW_TJ("executing tjInitTransform()"); - if (tjDecompressHeader3(handle, srcBuf, srcSize, &w, &h, &subsamp, - &cs) == -1) - THROW_TJ("executing tjDecompressHeader3()"); + if ((handle = tj3Init(TJINIT_TRANSFORM)) == NULL) + THROW_TJG(); + if (tj3Set(handle, TJPARAM_STOPONWARNING, stopOnWarning) == -1) + THROW_TJ(); + if (tj3Set(handle, TJPARAM_BOTTOMUP, bottomUp) == -1) + THROW_TJ(); + if (tj3Set(handle, TJPARAM_NOREALLOC, noRealloc) == -1) + THROW_TJ(); + if (tj3Set(handle, TJPARAM_FASTUPSAMPLE, fastUpsample) == -1) + THROW_TJ(); + if (tj3Set(handle, TJPARAM_FASTDCT, fastDCT) == -1) + THROW_TJ(); + if (tj3Set(handle, TJPARAM_SCANLIMIT, limitScans ? 500 : 0) == -1) + THROW_TJ(); + + if (tj3DecompressHeader(handle, srcBuf, srcSize) == -1) + THROW_TJ(); + 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(); + if (tj3Set(handle, TJPARAM_ARITHMETIC, arithmetic) == -1) + THROW_TJ(); + + 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 (lossless) sf = TJUNSCALED; + + if (tj3SetScalingFactor(handle, sf) == -1) + THROW_TJ(); + if (tj3SetCroppingRegion(handle, cr) == -1) + THROW_TJ(); if (quiet == 1) { printf("All performance values in Mpixels/sec\n\n"); - printf("Bitmap 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; ; + if (doTile) { + if (subsamp == TJSAMP_UNKNOWN) + THROW("transforming", + "Could not determine subsampling level of JPEG image"); + minTile = max(tjMCUWidth[subsamp], tjMCUHeight[subsamp]); + } + for (tilew = doTile ? minTile : w, tileh = doTile ? minTile : h; ; tilew *= 2, tileh *= 2) { if (tilew > w) tilew = w; if (tileh > h) tileh = h; 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) - THROW("getting buffer size", "Image is too large"); - if ((jpegBuf[i] = (unsigned char *) - tjAlloc(tjBufSize(tilew, tileh, subsamp))) == NULL) + size_t jpegBufSize; + + if (xformOp == TJXOP_TRANSPOSE || xformOp == TJXOP_TRANSVERSE || + xformOp == TJXOP_ROT90 || xformOp == TJXOP_ROT270) + jpegBufSize = tj3JPEGBufSize(tileh, tilew, subsamp); + else + jpegBufSize = tj3JPEGBufSize(tilew, tileh, subsamp); + if (jpegBufSize == 0) + THROW_TJG(); + if ((jpegBufs[i] = tj3Alloc(jpegBufSize)) == NULL) 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; @@ -630,7 +759,11 @@ static int decompTest(char *fileName) tw = h; th = w; ttilew = tileh; ttileh = tilew; } - if (xformOpt & TJXOPT_GRAY) tsubsamp = TJ_GRAYSCALE; + 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]); if (xformOp == TJXOP_VFLIP || xformOp == TJXOP_ROT180) @@ -646,6 +779,8 @@ static int decompTest(char *fileName) xformOp == TJXOP_ROT90 || xformOp == TJXOP_ROT270) { if (tsubsamp == TJSAMP_422) tsubsamp = TJSAMP_440; else if (tsubsamp == TJSAMP_440) tsubsamp = TJSAMP_422; + else if (tsubsamp == TJSAMP_411) tsubsamp = TJSAMP_441; + else if (tsubsamp == TJSAMP_441) tsubsamp = TJSAMP_411; } for (row = 0, tile = 0; row < tntilesh; row++) { @@ -657,8 +792,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; } } } @@ -667,9 +802,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(); elapsed += getTime() - start; if (iter >= 0) { iter++; @@ -683,7 +818,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", @@ -696,7 +831,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", @@ -706,41 +841,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; } @@ -750,38 +885,63 @@ 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("-alloc = Dynamically allocate JPEG image buffers\n"); - printf("-bmp = Generate output images in Windows Bitmap format (default = PPM)\n"); - printf("-bottomup = Test bottom-up compression/decompression\n"); - printf("-tile = Test performance of the codec when the image is encoded as separate\n"); - printf(" tiles of varying sizes.\n"); + printf(" [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("-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(" Test the specified color conversion path in the codec (default = BGR)\n"); - printf("-cmyk = Indirectly test YCCK JPEG compression/decompression (the source\n"); - printf(" and destination bitmaps are still RGB. The conversion is done\n"); - printf(" internally prior to compression or after decompression.)\n"); - printf("-fastupsample = Use the fastest chrominance upsampling algorithm available in\n"); - printf(" the underlying codec\n"); - printf("-fastdct = Use the fastest DCT/IDCT algorithms available in the underlying\n"); - printf(" codec\n"); - printf("-accuratedct = Use the most accurate DCT/IDCT algorithms available in the\n"); - printf(" underlying codec\n"); - printf("-progressive = Use progressive entropy coding in JPEG images generated by\n"); - printf(" compression and transform operations.\n"); - printf("-subsamp = When testing JPEG compression, this option specifies the level\n"); - printf(" of chrominance subsampling to use ( = 444, 422, 440, 420, 411, or\n"); - printf(" GRAY). The default is to test Grayscale, 4:2:0, 4:2:2, and 4:4:4 in\n"); - printf(" sequence.\n"); + printf(" 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("-precision N = Use N-bit data precision when compressing [N is 8, 12, or 16;\n"); + printf(" default = 8; if N is 16, then -lossless must also be specified]\n"); + printf(" (-precision 12 implies -optimize unless -arithmetic is also specified)\n"); printf("-quiet = Output results in tabular rather than verbose format\n"); - printf("-yuv = Test YUV encoding/decoding functions\n"); - printf("-yuvpad

        = If testing YUV encoding/decoding, this specifies the number of\n"); - printf(" bytes to which each row of each plane in the intermediate YUV image is\n"); - printf(" padded (default = 1)\n"); - printf("-scale M/N = Scale down the width/height of the decompressed JPEG image by a\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("-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 (can be combined with -arithmetic;\n"); + printf(" implies -optimize unless -arithmetic is also specified)\n"); + printf("-limitscans = Refuse to decompress or transform progressive JPEG images that\n"); + printf(" have an unreasonably large number of scans\n"); + printf("-scale M/N = When decompressing, scale the width/height of the JPEG image by a\n"); printf(" factor of M/N (M/N = "); for (i = 0; i < nsf; i++) { printf("%d/%d", scalingFactors[i].num, scalingFactors[i].denom); @@ -793,40 +953,38 @@ 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, 441, or GRAY) [default = test\n"); + printf(" Grayscale, 4:2:0, 4:2:2, and 4:4:4 in sequence]\n"); printf("-hflip, -vflip, -transpose, -transverse, -rot90, -rot180, -rot270 =\n"); - printf(" Perform the corresponding lossless transform prior to\n"); - printf(" decompression (these options are mutually exclusive)\n"); - printf("-grayscale = Perform lossless grayscale conversion prior to decompression\n"); - printf(" test (can be combined with the other transforms above)\n"); + printf(" Perform the specified lossless transform operation on the input image\n"); + printf(" prior to decompression (these operations are mutually exclusive)\n"); + printf("-grayscale = Transform the input image into a grayscale JPEG image prior to\n"); + printf(" decompression (can be combined with the other transform operations above)\n"); printf("-copynone = Do not copy any extra markers (including EXIF and ICC profile data)\n"); - printf(" when transforming the image.\n"); - printf("-benchtime = Run each benchmark for at least seconds (default = 5.0)\n"); - printf("-warmup = Run each benchmark for seconds (default = 1.0) prior to\n"); - printf(" starting the timer, in order to prime the caches and thus improve the\n"); - printf(" consistency of the results.\n"); - printf("-componly = Stop after running compression tests. Do not test decompression.\n"); - printf("-nowrite = Do not write reference or output images (improves consistency of\n"); - printf(" performance measurements.)\n"); - printf("-limitscans = Refuse to decompress or transform progressive JPEG images that\n"); - printf(" have an unreasonably large number of scans\n"); - printf("-stoponwarning = Immediately discontinue the current\n"); - printf(" compression/decompression/transform operation if the underlying codec\n"); - printf(" throws a warning (non-fatal error)\n\n"); - printf("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(" when transforming the input image\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]); @@ -842,13 +1000,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; } @@ -856,18 +1010,33 @@ 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 fast upsampling code\n\n"); - flags |= TJFLAG_FASTUPSAMPLE; + printf("Using fastest upsampling algorithm\n\n"); + 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"); + arithmetic = 1; + xformOpt |= TJXOPT_ARITHMETIC; + } else if (!strcasecmp(argv[i], "-lossless")) { + lossless = 1; + subsamp = TJSAMP_444; } else if (!strcasecmp(argv[i], "-rgb")) pf = TJPF_RGB; else if (!strcasecmp(argv[i], "-rgbx")) @@ -883,7 +1052,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")) @@ -893,15 +1062,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")) @@ -936,16 +1112,17 @@ 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")) { - printf("Testing YUV planar encoding/decoding\n\n"); + printf("Testing planar YUV encoding/decoding\n\n"); doYUV = 1; } else if (!strcasecmp(argv[i], "-yuvpad") && i < argc - 1) { int tempi = atoi(argv[++i]); - if (tempi >= 1) yuvPad = tempi; + if (tempi >= 1 && (tempi & (tempi - 1)) == 0) yuvAlign = tempi; + else usage(argv[0]); } else if (!strcasecmp(argv[i], "-subsamp") && i < argc - 1) { i++; if (toupper(argv[i][0]) == 'G') subsamp = TJSAMP_GRAY; @@ -958,6 +1135,8 @@ int main(int argc, char *argv[]) case 440: subsamp = TJSAMP_440; break; case 420: subsamp = TJSAMP_420; break; case 411: subsamp = TJSAMP_411; break; + case 441: subsamp = TJSAMP_441; break; + default: usage(argv[0]); } } } else if (!strcasecmp(argv[i], "-componly")) @@ -965,41 +1144,99 @@ 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 && 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"); - doTile = 0; + printf("work when scaled decompression is enabled.\n\n"); + doTile = 0; xformOpt &= (~TJXOPT_CROP); + } + + 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 ((flags & TJFLAG_NOREALLOC) == 0 && doTile) { + 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; + doTile = 0; xformOpt &= (~TJXOPT_CROP); } if (!decompOnly) { - if ((srcBuf = tjLoadImage(argv[1], &w, 1, &h, &pf, flags)) == NULL) - THROW_TJG("loading bitmap"); + if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL) + THROW_TJG(); + if (tj3Set(handle, TJPARAM_STOPONWARNING, stopOnWarning) == -1) + THROW_TJ(); + if (tj3Set(handle, TJPARAM_BOTTOMUP, bottomUp) == -1) + THROW_TJ(); + + if (precision == 8) { + if ((srcBuf = tj3LoadImage8(handle, argv[1], &w, 1, &h, &pf)) == NULL) + THROW_TJ(); + } else if (precision == 12) { + if ((srcBuf = tj3LoadImage12(handle, argv[1], &w, 1, &h, &pf)) == NULL) + THROW_TJ(); + } else { + if ((srcBuf = tj3LoadImage16(handle, argv[1], &w, 1, &h, &pf)) == NULL) + THROW_TJ(); + } temp = strrchr(argv[1], '.'); if (temp != NULL) *temp = '\0'; } if (quiet == 1 && !decompOnly) { printf("All performance values in Mpixels/sec\n\n"); - printf("Bitmap JPEG JPEG %s %s ", + printf("Pixel JPEG JPEG %s %s ", doTile ? "Tile " : "Image", doTile ? "Tile " : "Image"); if (doYUV) printf("Encode "); printf("Comp Comp Decomp "); 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"); @@ -1011,28 +1248,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 1c08b37..4f5a972 100755 --- a/tjbenchtest.in +++ b/tjbenchtest.in @@ -16,20 +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= +ENTROPYARG= +JAVAARG= +LOSSLSARG= +LOSSLSPSV= +TJQUAL=95 +x1SUBSAMP="444 GRAY" +x24SUBSAMP="422 440 420 411 441" +ALLSUBSAMP="444 422 440 420 411 441 GRAY" +PRECISION=8 if [ "$EXT" = "bmp" ]; then BMPARG=-bmp; fi if [ -d $OUTDIR ]; then @@ -43,213 +54,391 @@ 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 + if [ "$ENTROPYARG" = "-arithmetic" ]; then + ENTROPYARG=-progressive-arithmetic + else + ENTROPYARG=-progressive + fi + ;; + -arithmetic) + if [ "$ENTROPYARG" = "-progressive" ]; then + ENTROPYARG=-progressive-arithmetic + else + ENTROPYARG=-arithmetic + fi + ;; + -lossless) + LOSSLSARG="-lossless" + LOSSLSPSV=4 + TJQUAL=4 + x1SUBSAMP=444 + x24SUBSAMP=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.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" -o \ + "$ENTROPYARG" = "-progressive-arithmetic" ]; then + IMAGES="big_tree8.${EXT}" + fi +fi + +exec >$EXEDIR/tjbenchtest$JAVAARG$YUVARG$ALLOCARG$ENTROPYARG$LOSSLSARG-$PRECISION.log + +if [ "$ENTROPYARG" = "-progressive-arithmetic" ]; then + ENTROPYARG="-progressive -arithmetic" +fi # Standard tests for image in $IMAGES; do cp $IMGDIR/$image $OUTDIR basename=`basename $image .${EXT}` - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG -grayscale -outfile $OUTDIR/${basename}_GRAY_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG -sample 2x2 -outfile $OUTDIR/${basename}_420_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG -sample 2x1 -outfile $OUTDIR/${basename}_422_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG -sample 1x1 -outfile $OUTDIR/${basename}_444_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG -grayscale -outfile $OUTDIR/${basename}_GRAY_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG -sample 2x2 -outfile $OUTDIR/${basename}_420_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG -sample 2x1 -outfile $OUTDIR/${basename}_422_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG -sample 1x1 -outfile $OUTDIR/${basename}_444_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} - for samp in GRAY 420 422 444; do - runme $EXEDIR/djpeg -rgb $NSARG $BMPARG -outfile $OUTDIR/${basename}_${samp}_default_djpeg.${EXT} $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct fast $ENTROPYARG $LOSSLSARG $LOSSLSPSV -grayscale -outfile $OUTDIR/${basename}_GRAY_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct fast $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 1x4 -outfile $OUTDIR/${basename}_441_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct fast $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 4x1 -outfile $OUTDIR/${basename}_411_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct fast $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 2x2 -outfile $OUTDIR/${basename}_420_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct fast $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 1x2 -outfile $OUTDIR/${basename}_440_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct fast $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 2x1 -outfile $OUTDIR/${basename}_422_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct fast $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 1x1 -outfile $OUTDIR/${basename}_444_fast_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct int $ENTROPYARG $LOSSLSARG $LOSSLSPSV -grayscale -outfile $OUTDIR/${basename}_GRAY_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct int $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 1x4 -outfile $OUTDIR/${basename}_441_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct int $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 4x1 -outfile $OUTDIR/${basename}_411_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct int $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 2x2 -outfile $OUTDIR/${basename}_420_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct int $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 1x2 -outfile $OUTDIR/${basename}_440_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct int $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 2x1 -outfile $OUTDIR/${basename}_422_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} + runme $EXEDIR/cjpeg -quality 95 -precision $PRECISION -dct int $ENTROPYARG $LOSSLSARG $LOSSLSPSV -sample 1x1 -outfile $OUTDIR/${basename}_444_accurate_cjpeg.jpg $IMGDIR/${basename}.${EXT} + for samp in $ALLSUBSAMP; do runme $EXEDIR/djpeg -dct fast -rgb $NSARG $BMPARG -outfile $OUTDIR/${basename}_${samp}_fast_djpeg.${EXT} $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg runme $EXEDIR/djpeg -dct int -rgb $NSARG $BMPARG -outfile $OUTDIR/${basename}_${samp}_accurate_djpeg.${EXT} $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg done - for samp in 420 422; do - runme $EXEDIR/djpeg -nosmooth $BMPARG -outfile $OUTDIR/${basename}_${samp}_default_nosmooth_djpeg.${EXT} $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg + for samp in $x24SUBSAMP; do runme $EXEDIR/djpeg -dct fast -nosmooth $BMPARG -outfile $OUTDIR/${basename}_${samp}_fast_nosmooth_djpeg.${EXT} $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg runme $EXEDIR/djpeg -dct int -nosmooth $BMPARG -outfile $OUTDIR/${basename}_${samp}_accurate_nosmooth_djpeg.${EXT} $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg done # Compression for dct in accurate fast; do - runme $EXEDIR/tjbench $OUTDIR/$image 95 -rgb -quiet -benchtime 0.01 -warmup 0 -${dct}dct $YUVARG $ALLOCARG $PROGARG - for samp in GRAY 420 422 444; do - runme cmp $OUTDIR/${basename}_${samp}_Q95.jpg $OUTDIR/${basename}_${samp}_${dct}_cjpeg.jpg + 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 + if [ "$LOSSLSARG" != "-lossless" ]; then + runme $TJBENCH $OUTDIR/$image $TJQUAL -precision $PRECISION -subsamp 440 -rgb -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + runme $TJBENCH $OUTDIR/$image $TJQUAL -precision $PRECISION -subsamp 411 -rgb -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + runme $TJBENCH $OUTDIR/$image $TJQUAL -precision $PRECISION -subsamp 441 -rgb -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + fi + for samp in $ALLSUBSAMP; do + if [ "$LOSSLSARG" = "-lossless" ]; then + runme cmp $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}.jpg $OUTDIR/${basename}_${samp}_${dct}_cjpeg.jpg + 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 95 -rgb -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $PROGARG - for samp in GRAY 444; do + runme $TJBENCH $OUTDIR/$image $TJQUAL -precision $PRECISION -rgb -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + for samp in $x1SUBSAMP; do 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} + 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}_Q95_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ - $OUTDIR/${basename}_${samp}_Q95_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]*x[0-9]*.${EXT} \ + $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}_full.${EXT}; do + runme cmp $i $OUTDIR/${basename}_${samp}_${dct}_djpeg.${EXT} + rm $i + done + else + for i in $OUTDIR/${basename}_${samp}_Q${TJQUAL}_[0-9]*x[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 95 -rgb -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $ALLOCARG $PROGARG - for samp in 420 422; do + runme $TJBENCH $OUTDIR/$image $TJQUAL -precision $PRECISION -rgb -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + if [ "$LOSSLSARG" != "-lossless" ]; then + runme $TJBENCH $OUTDIR/$image $TJQUAL -precision $PRECISION -subsamp 440 -rgb -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + runme $TJBENCH $OUTDIR/$image $TJQUAL -precision $PRECISION -subsamp 411 -rgb -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + runme $TJBENCH $OUTDIR/$image $TJQUAL -precision $PRECISION -subsamp 441 -rgb -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + fi + for samp in $x24SUBSAMP; do if [ $ALLOC = 1 ]; then - runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.${EXT} - rm $OUTDIR/${basename}_${samp}_Q95_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}_Q95_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ - $OUTDIR/${basename}_${samp}_Q95_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]*x[0-9]*.${EXT} \ + $OUTDIR/${basename}_LOSSLS_PSV${TJQUAL}_full.${EXT}; do + runme cmp $i $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.${EXT} + rm $i + done + else + for i in $OUTDIR/${basename}_${samp}_Q${TJQUAL}_[0-9]*x[0-9]*.${EXT} \ + $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.${EXT}; do + # If the tile size is smaller than the MCU size, then there will be + # edge artifacts at the tile boundaries, so the decompressed image + # will not be identical to the untiled decompressed image. + TILESIZE=$(basename $(echo $i | sed 's/.*_//g') .${EXT}) + if [ "$TILESIZE" = "8x8" ]; then + continue + fi + if [ "$TILESIZE" = "16x16" -a \ + \( "${samp}" = "411" -o "${samp}" = "441" \) ]; then + continue + fi + runme cmp $i $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.${EXT} + rm $i + done + fi fi done # Tiled decompression - for samp in GRAY 444; do - runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $PROGARG - if [ $ALLOC = 1 ]; then - runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${dct}_djpeg.${EXT} - rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} - else - for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ - $OUTDIR/${basename}_${samp}_Q95_full.${EXT}; do - runme cmp $i $OUTDIR/${basename}_${samp}_${dct}_djpeg.${EXT} - rm $i - done - fi - done - for samp in 420 422; do - runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $ALLOCARG $PROGARG - if [ $ALLOC = 1 ]; then - runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.${EXT} - rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} - else - for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ - $OUTDIR/${basename}_${samp}_Q95_full.${EXT}; do - runme cmp $i $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.${EXT} - rm $i - done + if [ "$LOSSLSARG" != "-lossless" ]; then + for samp in $x1SUBSAMP; do + runme $TJBENCH $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG + if [ $ALLOC = 1 ]; then + runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${dct}_djpeg.${EXT} + rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} + else + for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*x[0-9]*.${EXT} \ + $OUTDIR/${basename}_${samp}_Q95_full.${EXT}; do + runme cmp $i $OUTDIR/${basename}_${samp}_${dct}_djpeg.${EXT} + rm $i + done + fi + done + for samp in $x24SUBSAMP; do + runme $TJBENCH $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG + if [ $ALLOC = 1 ]; then + runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.${EXT} + rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} + else + for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*x[0-9]*.${EXT} \ + $OUTDIR/${basename}_${samp}_Q95_full.${EXT}; do + TILESIZE=$(basename $(echo $i | sed 's/.*_//g') .${EXT}) + if [ "$TILESIZE" = "8x8" ]; then + continue + fi + if [ "$TILESIZE" = "16x16" -a \ + \( "${samp}" = "411" -o "${samp}" = "441" \) ]; then + continue + fi + runme cmp $i $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.${EXT} + rm $i + done + fi + done + fi + done + + # Partial decompression + if [ "$LOSSLSARG" != "-lossless" -a "$YUVARG" != "-yuv" ]; then + for samp in $ALLSUBSAMP; do + CROPW8_8=103 + CROPL8_8=16 + CROPW7_8=91 + CROPL7_8=14 + if [ "${samp}" = "411" ]; then + CROPW8_8=87 + CROPL8_8=32 + CROPW7_8=77 + CROPL7_8=28 fi + runme $EXEDIR/djpeg -rgb -crop ${CROPW8_8}x90+${CROPL8_8}+5 $NSARG -outfile $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg + runme $TJBENCH $OUTDIR/${basename}_${samp}_Q${TJQUAL}.jpg -crop ${CROPW8_8}x90+${CROPL8_8}+5 -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + runme cmp $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.ppm $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm + rm $OUTDIR/${basename}_${samp}_Q${TJQUAL}_full.ppm $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm + + runme $EXEDIR/djpeg -rgb -scale 7/8 -crop ${CROPW7_8}x81+${CROPL7_8}+3 $NSARG -outfile $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm $OUTDIR/${basename}_${samp}_accurate_cjpeg.jpg + runme $TJBENCH $OUTDIR/${basename}_${samp}_Q${TJQUAL}.jpg -scale 7/8 -crop ${CROPW7_8}x81+${CROPL7_8}+3 -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $ALLOCARG $ENTROPYARG $LOSSLSARG + runme cmp $OUTDIR/${basename}_${samp}_Q${TJQUAL}_7_8.ppm $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm + rm $OUTDIR/${basename}_${samp}_Q${TJQUAL}_7_8.ppm $OUTDIR/${basename}_${samp}_scale_crop_djpeg.ppm + + 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 - 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'` - for samp in GRAY 420 422 444; do - runme $EXEDIR/djpeg -rgb -scale ${scalearg} $NSARG $BMPARG -outfile $OUTDIR/${basename}_${samp}_${scale}_djpeg.${EXT} $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg - runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -scale ${scalearg} -quiet -benchtime 0.01 -warmup 0 $YUVARG $ALLOCARG $PROGARG - runme cmp $OUTDIR/${basename}_${samp}_Q95_${scale}.${EXT} $OUTDIR/${basename}_${samp}_${scale}_djpeg.${EXT} - rm $OUTDIR/${basename}_${samp}_Q95_${scale}.${EXT} + SCALE=$scale + if [ "$LOSSLSARG" = "-lossless" ]; then + 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}_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 # Transforms - 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 $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 - if [ $ALLOC = 1 ]; then - runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} - rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} - else - for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ - $OUTDIR/${basename}_${samp}_Q95_full.${EXT}; do - runme cmp $i $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} - rm $i - done - fi + if [ "$LOSSLSARG" != "-lossless" ]; then + for samp in $ALLSUBSAMP; do + runme $EXEDIR/jpegtran -flip horizontal -trim -outfile $OUTDIR/${basename}_${samp}_hflip_jpegtran.jpg $OUTDIR/${basename}_${samp}_Q95.jpg + runme $EXEDIR/jpegtran -flip vertical -trim -outfile $OUTDIR/${basename}_${samp}_vflip_jpegtran.jpg $OUTDIR/${basename}_${samp}_Q95.jpg + runme $EXEDIR/jpegtran -transpose -trim -outfile $OUTDIR/${basename}_${samp}_transpose_jpegtran.jpg $OUTDIR/${basename}_${samp}_Q95.jpg + 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 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 - if [ $ALLOC = 1 ]; then - runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} - rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} - else - for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ - $OUTDIR/${basename}_${samp}_Q95_full.${EXT}; do - runme cmp $i $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} - rm $i - done - fi + for xform in hflip vflip transpose transverse rot90 rot180 rot270; do + for samp in $x1SUBSAMP; do + runme $EXEDIR/djpeg -rgb $BMPARG -outfile $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg + runme $TJBENCH $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -tile -quiet -benchtime 0.01 -warmup 0 $YUVARG $ALLOCARG $ENTROPYARG + if [ $ALLOC = 1 ]; then + runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} + rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} + else + for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*x[0-9]*.${EXT} \ + $OUTDIR/${basename}_${samp}_Q95_full.${EXT}; do + runme cmp $i $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} + rm $i + done + fi + done + for samp in $x24SUBSAMP; do + runme $EXEDIR/djpeg -nosmooth -rgb $BMPARG -outfile $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg + runme $TJBENCH $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample $YUVARG $ALLOCARG $ENTROPYARG + if [ $ALLOC = 1 ]; then + runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} + rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} + else + for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*x[0-9]*.${EXT} \ + $OUTDIR/${basename}_${samp}_Q95_full.${EXT}; do + TILESIZE=$(basename $(echo $i | sed 's/.*_//g') .${EXT}) + if [ "$TILESIZE" = "8x8" ]; then + continue + fi + if [ "$TILESIZE" = "16x16" -a \ + \( "${samp}" = "411" -o "${samp}" = "441" \) ]; then + continue + fi + runme cmp $i $OUTDIR/${basename}_${samp}_${xform}_jpegtran.${EXT} + rm $i + done + fi + 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 $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -tile -quiet -benchtime 0.01 -warmup 0 -grayscale $YUVARG $ALLOCARG $PROGARG - if [ $ALLOC = 1 ]; then - runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_GRAY_${xform}_jpegtran.${EXT} - rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} - else - for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].${EXT} \ - $OUTDIR/${basename}_${samp}_Q95_full.${EXT}; do - runme cmp $i $OUTDIR/${basename}_GRAY_${xform}_jpegtran.${EXT} - rm $i - done - fi + # Grayscale transform + for xform in hflip vflip transpose transverse rot90 rot180 rot270; do + for samp in $ALLSUBSAMP; do + runme $TJBENCH $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -tile -quiet -benchtime 0.01 -warmup 0 -grayscale $YUVARG $ALLOCARG $ENTROPYARG + if [ $ALLOC = 1 ]; then + runme cmp $OUTDIR/${basename}_${samp}_Q95_full.${EXT} $OUTDIR/${basename}_GRAY_${xform}_jpegtran.${EXT} + rm $OUTDIR/${basename}_${samp}_Q95_full.${EXT} + else + for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*x[0-9]*.${EXT} \ + $OUTDIR/${basename}_${samp}_Q95_full.${EXT}; do + TILESIZE=$(basename $(echo $i | sed 's/.*_//g') .${EXT}) + if [ "$TILESIZE" = "8x8" -a \ + "${samp}" != "444" -a "${samp}" != "GRAY" ]; then + continue + fi + if [ "$TILESIZE" = "16x16" -a \ + \( "${samp}" = "411" -o "${samp}" = "441" \) ]; then + continue + fi + runme cmp $i $OUTDIR/${basename}_GRAY_${xform}_jpegtran.${EXT} + rm $i + done + fi + 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 $BMPARG -outfile $OUTDIR/${basename}_${samp}_${xform}_${scale}_jpegtran.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg - runme $EXEDIR/tjbench $OUTDIR/${basename}_${samp}_Q95.jpg $BMPARG -$xform -scale ${scalearg} -quiet -benchtime 0.01 -warmup 0 $YUVARG $ALLOCARG $PROGARG - runme cmp $OUTDIR/${basename}_${samp}_Q95_${scale}.${EXT} $OUTDIR/${basename}_${samp}_${xform}_${scale}_jpegtran.${EXT} - rm $OUTDIR/${basename}_${samp}_Q95_${scale}.${EXT} + # Transforms with scaling + for xform in hflip vflip transpose transverse rot90 rot180 rot270; do + for samp in $ALLSUBSAMP; do + for scale in 2_1 15_8 7_4 13_8 3_2 11_8 5_4 9_8 7_8 3_4 5_8 1_2 3_8 1_4 1_8; do + scalearg=`echo $scale | sed 's/\_/\//g'` + runme $EXEDIR/djpeg -rgb -scale ${scalearg} $NSARG $BMPARG -outfile $OUTDIR/${basename}_${samp}_${xform}_${scale}_jpegtran.${EXT} $OUTDIR/${basename}_${samp}_${xform}_jpegtran.jpg + 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 done done - done + fi done diff --git a/tjbenchtest.java.in b/tjbenchtest.java.in deleted file mode 100755 index 689561d..0000000 --- a/tjbenchtest.java.in +++ /dev/null @@ -1,215 +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= - -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 - ;; - esac - shift -done - -exec >$EXEDIR/tjbenchtest-java$YUVARG$PROGARG.log - -# Standard tests -for image in $IMAGES; do - - cp $IMGDIR/$image $OUTDIR - basename=`basename $image .bmp` - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG -grayscale -outfile $OUTDIR/${basename}_GRAY_fast_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG -sample 2x2 -outfile $OUTDIR/${basename}_420_fast_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG -sample 2x1 -outfile $OUTDIR/${basename}_422_fast_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct fast $PROGARG -sample 1x1 -outfile $OUTDIR/${basename}_444_fast_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG -grayscale -outfile $OUTDIR/${basename}_GRAY_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG -sample 2x2 -outfile $OUTDIR/${basename}_420_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG -sample 2x1 -outfile $OUTDIR/${basename}_422_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp - runme $EXEDIR/cjpeg -quality 95 -dct int $PROGARG -sample 1x1 -outfile $OUTDIR/${basename}_444_accurate_cjpeg.jpg $IMGDIR/${basename}.bmp - 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 accurate fast; do - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/$image 95 -rgb -quiet -benchtime 0.01 -warmup 0 -${dct}dct $YUVARG $PROGARG - for samp in GRAY 420 422 444; do - runme cmp $OUTDIR/${basename}_${samp}_Q95.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 95 -rgb -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $PROGARG - for samp in GRAY 444; do - for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].bmp \ - $OUTDIR/${basename}_${samp}_Q95_full.bmp; do - runme cmp -i 54:54 $i $OUTDIR/${basename}_${samp}_${dct}_djpeg.bmp - rm $i - done - done - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/$image 95 -rgb -tile -quiet -benchtime 0.01 -warmup 0 -fastupsample ${dctarg} $YUVARG $PROGARG - for samp in 420 422; do - for i in $OUTDIR/${basename}_${samp}_Q95_[0-9]*[0-9]x[0-9]*[0-9].bmp \ - $OUTDIR/${basename}_${samp}_Q95_full.bmp; do - runme cmp -i 54:54 $i $OUTDIR/${basename}_${samp}_${dct}_nosmooth_djpeg.bmp - rm $i - done - done - - # Tiled decompression - for samp in GRAY 444; do - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -tile -quiet -benchtime 0.01 -warmup 0 ${dctarg} $YUVARG $PROGARG - 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 - 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 - 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 -scale ${scalearg} $NSARG -bmp -outfile $OUTDIR/${basename}_${samp}_${scale}_djpeg.bmp $OUTDIR/${basename}_${samp}_fast_cjpeg.jpg - runme "$JAVA" $JAVAARGS TJBench $OUTDIR/${basename}_${samp}_Q95.jpg -scale ${scalearg} -quiet -benchtime 0.01 -warmup 0 $YUVARG $PROGARG - runme cmp -i 54:54 $OUTDIR/${basename}_${samp}_Q95_${scale}.bmp $OUTDIR/${basename}_${samp}_${scale}_djpeg.bmp - rm $OUTDIR/${basename}_${samp}_Q95_${scale}.bmp - done - done - - # Transforms - 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 - 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 - 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 - 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 - 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 - -done - -echo SUCCESS! diff --git a/tjexample.c b/tjexample.c index 505c9dd..579ebe5 100644 --- a/tjexample.c +++ b/tjexample.c @@ -1,5 +1,5 @@ /* - * Copyright (C)2011-2012, 2014-2015, 2017, 2019, 2021-2022 + * Copyright (C)2011-2012, 2014-2015, 2017, 2019, 2021-2023 * D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without @@ -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)) @@ -62,7 +62,7 @@ const char *subsampName[TJ_NUMSAMP] = { - "4:4:4", "4:2:2", "4:2:0", "Grayscale", "4:4:0", "4:1:1" + "4:4:4", "4:2:2", "4:2:0", "Grayscale", "4:4:0", "4:1:1", "4:4:1" }; const char *colorspaceName[TJ_NUMCS] = { @@ -149,14 +149,9 @@ static void usage(char *programName) printf("General Options\n"); printf("---------------\n\n"); - printf("-fastupsample = Use the fastest chrominance upsampling algorithm available in\n"); - printf(" the underlying codec.\n\n"); + printf("-fastupsample = Use the fastest chrominance upsampling algorithm available\n\n"); - printf("-fastdct = Use the fastest DCT/IDCT algorithms available in the underlying\n"); - printf(" codec.\n\n"); - - printf("-accuratedct = Use the most accurate DCT/IDCT algorithms available in the\n"); - printf(" underlying codec.\n\n"); + printf("-fastdct = Use the fastest DCT/IDCT algorithm available\n\n"); exit(1); } @@ -164,10 +159,10 @@ static void usage(char *programName) 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; @@ -175,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)); @@ -241,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]); } @@ -266,7 +258,7 @@ int main(int argc, char **argv) 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) @@ -276,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"); @@ -286,27 +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 (tjDecompressHeader3(tjInstance, jpegBuf, jpegSize, &width, &height, - &inSubsamp, &inColorspace) < 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 (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,33 @@ 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 ((unsigned long long)width * height * tjPixelSize[pixelFormat] > + (unsigned long long)((size_t)-1)) + THROW("allocating uncompressed image buffer", "Image is too large"); + if ((imgBuf = + (unsigned char *)malloc(sizeof(unsigned char) * width * height * + tjPixelSize[pixelFormat])) == NULL) THROW_UNIX("allocating uncompressed image buffer"); - if (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; + tj3Destroy(tjInstance); tjInstance = 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 +371,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 +380,40 @@ int main(int argc, char **argv) printf(", %s subsampling, quality = %d\n", subsampName[outSubsamp], outQual); - if ((tjInstance = tjInitCompress()) == NULL) + if (!tjInstance && (tjInstance = tj3Init(TJINIT_COMPRESS)) == 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 0d3047e..c56fc42 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 d4b63bc..0000000 --- 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 b3f0311..dcd5006 100644 --- a/tjunittest.c +++ b/tjunittest.c @@ -1,6 +1,6 @@ /* - * Copyright (C)2009-2014, 2017-2019, 2022 D. R. Commander. - * All Rights Reserved. + * Copyright (C)2009-2014, 2017-2019, 2022-2023 D. R. Commander. + * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -38,14 +38,17 @@ #include #include #include +#include #include #include "tjutil.h" #include "turbojpeg.h" #include "md5/md5.h" -#include "cmyk.h" +#include "jconfigint.h" #ifdef _WIN32 #include +#include #define random() rand() +#define getpid() _getpid() #else #include #endif @@ -55,20 +58,24 @@ static void usage(char *progName) { printf("\nUSAGE: %s [options]\n\n", progName); printf("Options:\n"); - printf("-yuv = test YUV encoding/decoding support\n"); - printf("-noyuvpad = do not pad each line of each Y, U, and V plane to the nearest\n"); - printf(" 4-byte boundary\n"); - printf("-alloc = test automatic buffer allocation\n"); - printf("-bmp = tjLoadImage()/tjSaveImage() unit test\n\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"); exit(1); } -#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, \ @@ -77,10 +84,10 @@ static void usage(char *progName) } const char *subNameLong[TJ_NUMSAMP] = { - "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1" + "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1", "4:4:1" }; const char *subName[TJ_NUMSAMP] = { - "444", "422", "420", "GRAY", "440", "411" + "444", "422", "420", "GRAY", "440", "411", "441" }; const char *pixFormatStr[TJ_NUMPF] = { @@ -88,67 +95,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, alloc = 0, pad = 4; +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); } } } @@ -157,7 +176,7 @@ static void initBuf(unsigned char *buf, int w, int h, int pf, int flags) #define CHECKVAL(v, cv) { \ - if (v < cv - 1 || v > cv + 1) { \ + if (v < cv - 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; \ @@ -165,22 +184,33 @@ static void initBuf(unsigned char *buf, int w, int h, int pf, int flags) } #define CHECKVAL0(v) { \ - if (v > 1) { \ + 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 < 254) { \ - 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]; @@ -196,22 +226,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) } } } @@ -220,36 +250,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); } } @@ -258,13 +289,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"); } @@ -282,7 +315,7 @@ static int checkBufYUV(unsigned char *buf, int w, int h, int subsamp, int hsf = tjMCUWidth[subsamp] / 8, vsf = tjMCUHeight[subsamp] / 8; int pw = PAD(w, hsf), ph = PAD(h, vsf); int cw = pw / hsf, ch = ph / vsf; - int ypitch = PAD(pw, pad), uvpitch = PAD(cw, pad); + int ypitch = PAD(pw, yuvAlign), uvpitch = PAD(cw, yuvAlign); int retval = 1; int halfway = 16 * sf.num / sf.denom; int blocksize = 8 * sf.num / sf.denom; @@ -292,11 +325,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); } } } @@ -312,7 +345,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); } @@ -347,8 +380,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"); @@ -362,55 +394,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"; - - if ((srcBuf = (unsigned char *)malloc(w * h * tjPixelSize[pf])) == NULL) - THROW("Memory allocation failure"); - initBuf(srcBuf, w, h, pf, flags); + int bottomUp = tj3Get(handle, TJPARAM_BOTTOMUP); + int subsamp = tj3Get(handle, TJPARAM_SUBSAMP); + int jpegPSV = tj3Get(handle, TJPARAM_LOSSLESSPSV); + int jpegQual = tj3Get(handle, TJPARAM_QUALITY); + const char *buStrLong = bottomUp ? "Bottom-Up" : "Top-Down "; + const char *buStr = bottomUp ? "BU" : "TD"; + + if ((srcBuf = malloc(w * h * tjPixelSize[pf] * sampleSize)) == NULL) + THROW("Memory allocation failure"); + initBuf(srcBuf, w, h, pf, bottomUp); if (*dstBuf && *dstSize > 0) memset(*dstBuf, 0, *dstSize); - if (!alloc) flags |= TJFLAG_NOREALLOC; if (doYUV) { - unsigned long yuvSize = tjBufSizeYUV2(w, pad, 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, pad, 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, pad, 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); @@ -421,32 +473,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(handle, tj3SetScalingFactor(handle, sf)); - TRY_TJ(tjDecompressHeader2(handle, jpegBuf, jpegSize, &_hdrw, &_hdrh, - &_hdrsubsamp)); + TRY_TJ(handle, tj3DecompressHeader(handle, jpegBuf, jpegSize)); + _hdrw = tj3Get(handle, TJPARAM_JPEGWIDTH); + _hdrh = tj3Get(handle, TJPARAM_JPEGHEIGHT); + _hdrsubsamp = tj3Get(handle, TJPARAM_SUBSAMP); + if (lossless && subsamp != TJSAMP_444 && subsamp != TJSAMP_GRAY) + subsamp = TJSAMP_444; if (_hdrw != w || _hdrh != h || _hdrsubsamp != subsamp) THROW("Incorrect JPEG header"); dstSize = scaledWidth * scaledHeight * tjPixelSize[pf]; - if ((dstBuf = (unsigned char *)malloc(dstSize)) == NULL) + if ((dstBuf = malloc(dstSize * sampleSize)) == NULL) THROW("Memory allocation failure"); - memset(dstBuf, 0, dstSize); + memset(dstBuf, 0, dstSize * sampleSize); if (doYUV) { - unsigned long yuvSize = tjBufSizeYUV2(scaledWidth, pad, 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"); @@ -456,28 +518,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("... "); - TRY_TJ(tjDecompressToYUV2(handle, jpegBuf, jpegSize, yuvBuf, scaledWidth, - pad, scaledHeight, 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, pad, 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("... "); - TRY_TJ(tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, scaledWidth, 0, - scaledHeight, 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"); @@ -489,22 +560,29 @@ 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 = tjGetScalingFactors(&n); + tjscalingfactor *sf = NULL; + + if (lossless) { + _decompTest(handle, jpegBuf, jpegSize, w, h, pf, basename, subsamp, + TJUNSCALED); + return; + } - 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 || - (subsamp == TJSAMP_411 && sf[i].num == 1 && + ((subsamp == TJSAMP_411 || subsamp == TJSAMP_441) && sf[i].num == 1 && (sf[i].denom == 2 || sf[i].denom == 1)) || - (subsamp != TJSAMP_411 && sf[i].num == 1 && + (subsamp != TJSAMP_411 && subsamp != TJSAMP_441 && sf[i].num == 1 && (sf[i].denom == 4 || sf[i].denom == 2 || sf[i].denom == 1))) _decompTest(handle, jpegBuf, jpegSize, w, h, pf, basename, subsamp, - flags, sf[i]); + sf[i]); } bailout: @@ -517,35 +595,47 @@ 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; + 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 || + subsamp == TJSAMP_441) + 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 (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, 100, - 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"); } @@ -553,42 +643,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 != 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 = tjBufSize(26755, 26755, TJSAMP_444); - CHECKSIZE(tjBufSize()); - size = TJBUFSIZE(26755, 26755); - CHECKSIZE(TJBUFSIZE()); - size = tjBufSizeYUV2(37838, 1, 37838, 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()); + size_t size; + unsigned long ulsize; + int intsize; + + 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; @@ -598,71 +720,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; + size_t dstSize = 0; + int numSamp = TJ_NUMSAMP; + + if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL) + THROW_TJ(NULL); - if ((handle = tjInitCompress()) == NULL) THROW_TJ(); + TRY_TJ(handle, tj3Set(handle, TJPARAM_NOREALLOC, !alloc)); + if (lossless) { + TRY_TJ(handle, tj3Set(handle, TJPARAM_LOSSLESS, lossless)); + TRY_TJ(handle, tj3Set(handle, TJPARAM_LOSSLESSPSV, + ((psv++ - 1) % 7) + 1)); + numSamp = 1; + } else + TRY_TJ(handle, tj3Set(handle, TJPARAM_QUALITY, 100)); printf("Buffer size regression test\n"); - for (subsamp = 0; subsamp < TJ_NUMSAMP; subsamp++) { + 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, pad, 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, pad, - 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, 100, - alloc ? 0 : TJFLAG_NOREALLOC)); + 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, pad, 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, pad, - 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, 100, - alloc ? 0 : TJFLAG_NOREALLOC)); + 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; } } } @@ -671,47 +820,78 @@ static void bufSizeTest(void) bailout: free(srcBuf); - tjFree(dstBuf); - if (handle) tjDestroy(handle); + tj3Free(dstBuf); + tj3Destroy(handle); } -static void initBitmap(unsigned char *buf, int width, int pitch, int height, - int pf, int flags) +static void rgb_to_cmyk(int r, int g, int b, int *c, int *m, int *y, int *k) +{ + double ctmp = 1.0 - ((double)r / (double)maxSample); + double mtmp = 1.0 - ((double)g / (double)maxSample); + double ytmp = 1.0 - ((double)b / (double)maxSample); + double ktmp = min(min(ctmp, mtmp), ytmp); + + if (ktmp == 1.0) ctmp = mtmp = ytmp = 0.0; + else { + ctmp = (ctmp - ktmp) / (1.0 - ktmp); + mtmp = (mtmp - ktmp) / (1.0 - ktmp); + ytmp = (ytmp - ktmp) / (1.0 - ktmp); + } + *c = (int)((double)maxSample - ctmp * (double)maxSample + 0.5); + *m = (int)((double)maxSample - mtmp * (double)maxSample + 0.5); + *y = (int)((double)maxSample - ytmp * (double)maxSample + 0.5); + *k = (int)((double)maxSample - ktmp * (double)maxSample + 0.5); +} + +static void initBitmap(void *buf, int width, int pitch, int height, int pf, + int bottomUp) { int roffset = tjRedOffset[pf]; int goffset = tjGreenOffset[pf]; int boffset = tjBlueOffset[pf]; int ps = tjPixelSize[pf]; - int i, j; + int i, j, ci; for (j = 0; j < height; j++) { - int row = (flags & TJFLAG_BOTTOMUP) ? height - j - 1 : j; + int row = bottomUp ? height - j - 1 : j; for (i = 0; i < width; i++) { - unsigned char r = (i * 256 / width) % 256; - unsigned char g = (j * 256 / height) % 256; - unsigned char b = (j * 256 / height + i * 256 / width) % 256; - - memset(&buf[row * pitch + i * ps], 0, ps); - if (pf == TJPF_GRAY) buf[row * pitch + i * ps] = b; - else if (pf == TJPF_CMYK) - rgb_to_cmyk(r, g, b, &buf[row * pitch + i * ps + 0], - &buf[row * pitch + i * ps + 1], - &buf[row * pitch + i * ps + 2], - &buf[row * pitch + i * ps + 3]); - else { - buf[row * pitch + i * ps + roffset] = r; - buf[row * pitch + i * ps + goffset] = g; - buf[row * pitch + i * ps + boffset] = b; + int r = (i * (maxSample + 1) / width) % (maxSample + 1); + int g = (j * (maxSample + 1) / height) % (maxSample + 1); + int b = (j * (maxSample + 1) / height + + i * (maxSample + 1) / width) % (maxSample + 1); + + for (ci = 0; ci < ps; ci++) + setVal(buf, row * pitch + i * ps + ci, 0); + if (pf == TJPF_GRAY) setVal(buf, row * pitch + i * ps, b); + else if (pf == TJPF_CMYK) { + int c, m, y, k; + + rgb_to_cmyk(r, g, b, &c, &m, &y, &k); + setVal(buf, row * pitch + i * ps + 0, c); + setVal(buf, row * pitch + i * ps + 1, m); + setVal(buf, row * pitch + i * ps + 2, y); + setVal(buf, row * pitch + i * ps + 3, k); + } else { + setVal(buf, row * pitch + i * ps + roffset, r); + setVal(buf, row * pitch + i * ps + goffset, g); + setVal(buf, row * pitch + i * ps + boffset, b); } } } } -static int cmpBitmap(unsigned char *buf, int width, int pitch, int height, - int pf, int flags, int gray2rgb) +static void cmyk_to_rgb(int c, int m, int y, int k, int *r, int *g, int *b) +{ + *r = (int)((double)c * (double)k / (double)maxSample + 0.5); + *g = (int)((double)m * (double)k / (double)maxSample + 0.5); + *b = (int)((double)y * (double)k / (double)maxSample + 0.5); +} + +static int cmpBitmap(void *buf, int width, int pitch, int height, int pf, + int bottomUp, int gray2rgb) { int roffset = tjRedOffset[pf]; int goffset = tjGreenOffset[pf]; @@ -721,38 +901,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; } } @@ -762,89 +944,158 @@ static int cmpBitmap(unsigned char *buf, int width, int pitch, int height, static int doBmpTest(const char *ext, int width, int align, int height, int pf, - int flags) + int bottomUp) { + tjhandle handle = NULL; char filename[80], *md5sum, md5buf[65]; int ps = tjPixelSize[pf], pitch = PAD(width * ps, align), loadWidth = 0, loadHeight = 0, retval = 0, pixelFormat = pf; - unsigned char *buf = NULL; + void *buf = NULL; char *md5ref; + if ((handle = tj3Init(TJINIT_TRANSFORM)) == NULL) + THROW_TJ(NULL); + TRY_TJ(handle, tj3Set(handle, TJPARAM_BOTTOMUP, bottomUp)); + if (pf == TJPF_GRAY) { - md5ref = !strcasecmp(ext, "ppm") ? "112c682e82ce5de1cca089e20d60000b" : - "51976530acf75f02beddf5d21149101d"; + if (precision == 8) + md5ref = !strcasecmp(ext, "ppm") ? "112c682e82ce5de1cca089e20d60000b" : + "51976530acf75f02beddf5d21149101d"; + else if (precision == 12) + md5ref = "0d1895c7e6f2b2c9af6e821a655c239c"; + else + md5ref = "64f3320b226ea37fb58080713b4df1b2"; } else { - md5ref = !strcasecmp(ext, "ppm") ? "c0c9f772b464d1896326883a5c79c545" : - "6d659071b9bfcdee2def22cb58ddadca"; + if (precision == 8) + md5ref = !strcasecmp(ext, "ppm") ? "c0c9f772b464d1896326883a5c79c545" : + "6d659071b9bfcdee2def22cb58ddadca"; + else if (precision == 12) + md5ref = "2ff5299287017502832c99718450c90a"; + else + md5ref = "623f54661b928d170bd2324bc3620565"; } - if ((buf = (unsigned char *)tjAlloc(pitch * height)) == NULL) + if ((buf = tj3Alloc(pitch * height * sampleSize)) == NULL) THROW("Could not allocate memory"); - initBitmap(buf, width, pitch, height, pf, flags); - - SNPRINTF(filename, 80, "test_bmp_%s_%d_%s.%s", pixFormatStr[pf], align, - (flags & TJFLAG_BOTTOMUP) ? "bu" : "td", ext); - TRY_TJ(tjSaveImage(filename, buf, width, pitch, height, pf, flags)); + initBitmap(buf, width, pitch, height, pf, bottomUp); + + SNPRINTF(filename, 80, "test_bmp%d_%s_%d_%s_%d.%s", precision, pixFormatStr[pf], + align, bottomUp ? "bu" : "td", getpid(), ext); + if (precision == 8) { + TRY_TJ(handle, tj3SaveImage8(handle, filename, (unsigned char *)buf, width, + pitch, height, pf)); + } 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 (!md5sum) { + printf("\n Could not determine MD5 sum of %s\n", filename); + retval = -1; goto bailout; + } if (strcasecmp(md5sum, md5ref)) THROW_MD5(filename, md5sum, md5ref); - 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; } @@ -856,29 +1107,31 @@ static int bmpTest(void) for (align = 1; align <= 8; align *= 2) { for (format = 0; format < TJ_NUMPF; format++) { - printf("%s Top-Down BMP (row alignment = %d bytes) ... ", - pixFormatStr[format], align); - if (doBmpTest("bmp", width, align, height, format, 0) == -1) - return -1; - printf("OK.\n"); + if (precision == 8) { + printf("%s Top-Down BMP (row alignment = %d samples) ... ", + pixFormatStr[format], align); + if (doBmpTest("bmp", width, align, height, format, 0) == -1) + return -1; + printf("OK.\n"); + } - printf("%s Top-Down PPM (row alignment = %d bytes) ... ", + printf("%s Top-Down PPM (row alignment = %d samples) ... ", pixFormatStr[format], align); - if (doBmpTest("ppm", width, align, height, format, - TJFLAG_BOTTOMUP) == -1) + if (doBmpTest("ppm", width, align, height, format, 1) == -1) return -1; printf("OK.\n"); - printf("%s Bottom-Up BMP (row alignment = %d bytes) ... ", - pixFormatStr[format], align); - if (doBmpTest("bmp", width, align, height, format, 0) == -1) - return -1; - printf("OK.\n"); + if (precision == 8) { + printf("%s Bottom-Up BMP (row alignment = %d samples) ... ", + pixFormatStr[format], align); + if (doBmpTest("bmp", width, align, height, format, 0) == -1) + return -1; + printf("OK.\n"); + } - printf("%s Bottom-Up PPM (row alignment = %d bytes) ... ", + printf("%s Bottom-Up PPM (row alignment = %d samples) ... ", pixFormatStr[format], align); - if (doBmpTest("ppm", width, align, height, format, - TJFLAG_BOTTOMUP) == -1) + if (doBmpTest("ppm", width, align, height, format, 1) == -1) return -1; printf("OK.\n"); } @@ -890,7 +1143,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)); @@ -898,28 +1151,56 @@ int main(int argc, char *argv[]) if (argc > 1) { for (i = 1; i < argc; i++) { if (!strcasecmp(argv[i], "-yuv")) doYUV = 1; - else if (!strcasecmp(argv[i], "-noyuvpad")) pad = 1; + else if (!strcasecmp(argv[i], "-noyuvpad")) yuvAlign = 1; + else if (!strcasecmp(argv[i], "-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, _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, _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, _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, _3sampleFormats, 2, TJSAMP_441, "test"); + doTest(41, 35, _4sampleFormats, num4bf, TJSAMP_441, "test"); + } doTest(39, 41, _onlyGray, 1, TJSAMP_GRAY, "test"); - doTest(41, 35, _3byteFormats, 2, TJSAMP_GRAY, "test"); - doTest(35, 39, _4byteFormats, 4, TJSAMP_GRAY, "test"); + if (!lossless) { + doTest(41, 35, _3sampleFormats, 2, TJSAMP_GRAY, "test"); + doTest(35, 39, _4sampleFormats, 4, TJSAMP_GRAY, "test"); + } bufSizeTest(); if (doYUV) { printf("\n--------------------\n\n"); @@ -928,9 +1209,11 @@ int main(int argc, char *argv[]) doTest(48, 48, _onlyRGB, 1, TJSAMP_420, "test_yuv0"); doTest(48, 48, _onlyRGB, 1, TJSAMP_440, "test_yuv0"); doTest(48, 48, _onlyRGB, 1, TJSAMP_411, "test_yuv0"); + doTest(48, 48, _onlyRGB, 1, TJSAMP_441, "test_yuv0"); doTest(48, 48, _onlyRGB, 1, TJSAMP_GRAY, "test_yuv0"); doTest(48, 48, _onlyGray, 1, TJSAMP_GRAY, "test_yuv0"); } + bailout: return exitStatus; } diff --git a/transupp.c b/transupp.c index a3d878c..34fbb37 100644 --- a/transupp.c +++ b/transupp.c @@ -23,7 +23,7 @@ #include "jinclude.h" #include "jpeglib.h" #include "transupp.h" /* My own external interface */ -#include "jpegcomp.h" +#include "jpegapicomp.h" #include /* to declare isdigit() */ @@ -143,7 +143,7 @@ requant_comp(j_decompress_ptr cinfo, jpeg_component_info *compptr, for (k = 0; k < DCTSIZE2; k++) { temp = qtblptr->quantval[k]; qval = qtblptr1->quantval[k]; - if (temp != qval) { + if (temp != qval && qval != 0) { temp *= ptr[k]; /* The following quantization code is copied from jcdctmgr.c */ #ifdef FAST_DIVIDE diff --git a/turbojpeg-jni.c b/turbojpeg-jni.c index 0cf5f70..32186f3 100644 --- a/turbojpeg-jni.c +++ b/turbojpeg-jni.c @@ -1,5 +1,5 @@ /* - * Copyright (C)2011-2022 D. R. Commander. All Rights Reserved. + * Copyright (C)2011-2023 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -26,6 +26,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include #include "turbojpeg.h" #include "jinclude.h" #include @@ -60,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; \ } @@ -84,81 +85,38 @@ 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) { - jint retval = (jint)tjBufSize(width, height, jpegSubsamp); + size_t retval = tj3JPEGBufSize(width, height, jpegSubsamp); - if (retval == -1) THROW_ARG(tjGetErrorStr()); + if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL)); + if (retval > (size_t)INT_MAX) + THROW_ARG("Image is too large"); bailout: - return retval; + return (jint)retval; } /* TurboJPEG 1.4.x: TJ::bufSizeYUV() */ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSizeYUV__IIII - (JNIEnv *env, jclass cls, jint width, jint pad, jint height, jint subsamp) + (JNIEnv *env, jclass cls, jint width, jint align, jint height, jint subsamp) { - jint retval = (jint)tjBufSizeYUV2(width, pad, height, subsamp); + size_t retval = tj3YUVBufSize(width, align, height, subsamp); - if (retval == -1) THROW_ARG(tjGetErrorStr()); + if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL)); + if (retval > (size_t)INT_MAX) + THROW_ARG("Image is too large"); bailout: - return retval; -} - -/* TurboJPEG 1.2.x: TJ::bufSizeYUV() */ -JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSizeYUV__III - (JNIEnv *env, jclass cls, jint width, jint height, jint subsamp) -{ - return Java_org_libjpegturbo_turbojpeg_TJ_bufSizeYUV__IIII(env, cls, width, - 4, height, - subsamp); + return (jint)retval; } /* TurboJPEG 1.4.x: TJ::planeSizeYUV() */ @@ -166,22 +124,23 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_planeSizeYUV__IIIII (JNIEnv *env, jclass cls, jint componentID, jint width, jint stride, jint height, jint subsamp) { - jint retval = (jint)tjPlaneSizeYUV(componentID, width, stride, height, - subsamp); + size_t retval = tj3YUVPlaneSize(componentID, width, stride, height, subsamp); - if (retval == -1) THROW_ARG(tjGetErrorStr()); + if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL)); + if (retval > (size_t)INT_MAX) + THROW_ARG("Image is too large"); bailout: - return retval; + return (jint)retval; } /* TurboJPEG 1.4.x: TJ::planeWidth() */ 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; @@ -191,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; @@ -207,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")); @@ -218,21 +177,52 @@ bailout: return; } +/* TurboJPEG 3: TJCompressor::set() */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_set + (JNIEnv *env, jobject obj, jint param, jint value) +{ + tjhandle handle = 0; + + GET_HANDLE(); + + if (tj3Set(handle, param, value) == -1) + THROW_TJ(); + +bailout: + return; +} + +/* TurboJPEG 3: TJCompressor::get() */ +JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_get + (JNIEnv *env, jobject obj, jint param) +{ + tjhandle handle = 0; + + GET_HANDLE(); + + return tj3Get(handle, param); + +bailout: + return -1; +} + static jint TJCompressor_compress - (JNIEnv *env, jobject obj, jarray src, jint srcElementSize, jint x, jint y, - jint width, jint pitch, jint height, jint pf, jbyteArray dst, - jint jpegSubsamp, jint jpegQual, jint flags) + (JNIEnv *env, jobject obj, jarray src, jint srcElementSize, jint precision, + jint x, jint y, jint width, jint pitch, jint height, jint pf, + jbyteArray dst) { tjhandle handle = 0; - unsigned long jpegSize = 0; + size_t jpegSize = 0; jsize arraySize = 0, actualPitch; - unsigned char *srcBuf = NULL, *jpegBuf = NULL; + void *srcBuf = NULL; + unsigned char *jpegBuf = NULL; + int jpegSubsamp; GET_HANDLE(); if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF || width < 1 || height < 1 || pitch < 0) - THROW_ARG("Invalid argument in compress()"); + THROW_ARG("Invalid argument in compress*()"); if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF) THROW_ARG("Mismatch between Java and C API"); @@ -240,21 +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"); - jpegSize = tjBufSize(width, height, jpegSubsamp); + jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP); + if (tj3Get(handle, TJPARAM_LOSSLESS) && jpegSubsamp != TJSAMP_GRAY) + jpegSubsamp = TJSAMP_444; + 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,87 +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.2.x: TJCompressor::compress() byte source */ -JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3BIIII_3BIII - (JNIEnv *env, jobject obj, jbyteArray src, jint width, jint pitch, - jint height, jint pf, jbyteArray dst, jint jpegSubsamp, jint jpegQual, - jint flags) +/* 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, 0, 0, width, pitch, height, - pf, dst, jpegSubsamp, jpegQual, flags); + return TJCompressor_compress(env, obj, src, 1, 12, 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 - (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) +/* 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) { - if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF) - THROW_ARG("Invalid argument in compress()"); - 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); - -bailout: - return 0; + return TJCompressor_compress(env, obj, src, 1, 16, x, y, width, pitch, + height, pf, dst); } -/* TurboJPEG 1.2.x: TJCompressor::compress() int source */ -JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3IIIII_3BIII - (JNIEnv *env, jobject obj, jintArray src, jint width, jint stride, - jint height, jint pf, jbyteArray dst, jint jpegSubsamp, jint jpegQual, - jint flags) +/* 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) { 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), 0, 0, 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) @@ -351,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; @@ -368,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++) { @@ -391,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]); @@ -407,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; @@ -420,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) @@ -455,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++) { @@ -478,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]); @@ -493,95 +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); - -bailout: - return; -} - -static void JNICALL TJCompressor_encodeYUV_12 - (JNIEnv *env, jobject obj, jarray src, jint srcElementSize, jint width, - jint pitch, jint height, jint pf, jbyteArray dst, jint subsamp, jint flags) -{ - tjhandle handle = 0; - jsize arraySize = 0; - unsigned char *srcBuf = NULL, *dstBuf = NULL; - - GET_HANDLE(); - - if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF || width < 1 || - height < 1 || pitch < 0) - THROW_ARG("Invalid argument in encodeYUV()"); - if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF) - THROW_ARG("Mismatch between Java and C API"); - - arraySize = (pitch == 0) ? width * tjPixelSize[pf] * height : pitch * height; - if ((*env)->GetArrayLength(env, src) * srcElementSize < arraySize) - THROW_ARG("Source buffer is not large enough"); - if ((*env)->GetArrayLength(env, dst) < - (jsize)tjBufSizeYUV(width, height, subsamp)) - THROW_ARG("Destination buffer is not large enough"); - - BAILIF0NOEC(srcBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0)); - BAILIF0NOEC(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0)); - - if (tjEncodeYUV2(handle, srcBuf, width, pitch, height, pf, dstBuf, subsamp, - flags) == -1) { - SAFE_RELEASE(dst, dstBuf); - SAFE_RELEASE(src, srcBuf); - THROW_TJ(); - } - -bailout: - SAFE_RELEASE(dst, dstBuf); - SAFE_RELEASE(src, srcBuf); -} - -/* TurboJPEG 1.2.x: TJCompressor::encodeYUV() byte source */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3BIIII_3BII - (JNIEnv *env, jobject obj, jbyteArray src, jint width, jint pitch, - jint height, jint pf, jbyteArray dst, jint subsamp, jint flags) -{ - TJCompressor_encodeYUV_12(env, obj, src, 1, width, pitch, height, pf, dst, - subsamp, flags); -} - -/* TurboJPEG 1.2.x: TJCompressor::encodeYUV() int source */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3IIIII_3BII - (JNIEnv *env, jobject obj, jintArray src, jint width, jint stride, - jint height, jint pf, jbyteArray dst, jint subsamp, jint flags) -{ - if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF) - THROW_ARG("Invalid argument in encodeYUV()"); - if (tjPixelSize[pf] != sizeof(jint)) - THROW_ARG("Pixel format must be 32-bit when encoding from an integer buffer."); - - TJCompressor_encodeYUV_12(env, obj, src, sizeof(jint), width, - stride * sizeof(jint), height, pf, dst, subsamp, - flags); + TJCompressor_encodeYUV8(env, obj, src, sizeof(jint), x, y, width, + stride * sizeof(jint), height, pf, dstobjs, + jDstOffsets, jDstStrides); bailout: return; @@ -595,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: @@ -610,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")); @@ -621,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) @@ -632,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")); @@ -658,7 +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; GET_HANDLE(); @@ -667,61 +624,158 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0)); - if (tjDecompressHeader3(handle, jpegBuf, (unsigned long)jpegSize, &width, - &height, &jpegSubsamp, &jpegColorspace) == -1) { + if (tj3DecompressHeader(handle, jpegBuf, (size_t)jpegSize) == -1) { SAFE_RELEASE(src, jpegBuf); THROW_TJ(); } +bailout: SAFE_RELEASE(src, jpegBuf); +} - BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegSubsamp", "I")); - (*env)->SetIntField(env, obj, _fid, jpegSubsamp); - if ((_fid = (*env)->GetFieldID(env, _cls, "jpegColorspace", "I")) == 0) - (*env)->ExceptionClear(env); - else - (*env)->SetIntField(env, obj, _fid, jpegColorspace); - BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegWidth", "I")); - (*env)->SetIntField(env, obj, _fid, width); - BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegHeight", "I")); - (*env)->SetIntField(env, obj, _fid, height); +/* TurboJPEG 3: TJDecompressor::setCroppingRegion() */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_setCroppingRegion + (JNIEnv *env, jobject obj) +{ + tjhandle handle = 0; + jclass sfcls, crcls; + jobject sfobj, crobj; + tjregion croppingRegion; + tjscalingfactor scalingFactor; + + GET_HANDLE(); + + BAILIF0(sfcls = (*env)->FindClass(env, + "org/libjpegturbo/turbojpeg/TJScalingFactor")); + BAILIF0(_fid = + (*env)->GetFieldID(env, _cls, "scalingFactor", + "Lorg/libjpegturbo/turbojpeg/TJScalingFactor;")); + BAILIF0(sfobj = (*env)->GetObjectField(env, obj, _fid)); + BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "num", "I")); + scalingFactor.num = (*env)->GetIntField(env, sfobj, _fid); + BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "denom", "I")); + scalingFactor.denom = (*env)->GetIntField(env, sfobj, _fid); + + if (tj3SetScalingFactor(handle, scalingFactor) == -1) + THROW_TJ(); + + BAILIF0(crcls = (*env)->FindClass(env, "java/awt/Rectangle")); + BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "croppingRegion", + "Ljava/awt/Rectangle;")); + BAILIF0(crobj = (*env)->GetObjectField(env, obj, _fid)); + BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "x", "I")); + croppingRegion.x = (*env)->GetIntField(env, crobj, _fid); + BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "y", "I")); + croppingRegion.y = (*env)->GetIntField(env, crobj, _fid); + BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "width", "I")); + croppingRegion.w = (*env)->GetIntField(env, crobj, _fid); + BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "height", "I")); + croppingRegion.h = (*env)->GetIntField(env, crobj, _fid); + + if (tj3SetCroppingRegion(handle, croppingRegion) == -1) + THROW_TJ(); bailout: - SAFE_RELEASE(src, jpegBuf); + return; } static void TJDecompressor_decompress (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jarray dst, - jint dstElementSize, jint x, jint y, jint width, jint pitch, jint height, - jint pf, jint flags) + jint dstElementSize, int precision, jint x, jint y, jint pitch, jint pf) { tjhandle handle = 0; jsize arraySize = 0, actualPitch; - unsigned char *jpegBuf = NULL, *dstBuf = NULL; + unsigned char *jpegBuf = NULL; + void *dstBuf = NULL; + jclass sfcls, crcls; + jobject sfobj, crobj; + tjscalingfactor scalingFactor; + tjregion cr; + int jpegWidth, jpegHeight, scaledWidth, scaledHeight; GET_HANDLE(); if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF) - THROW_ARG("Invalid argument in decompress()"); + THROW_ARG("Invalid argument in decompress*()"); if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF) THROW_ARG("Mismatch between Java and C API"); if ((*env)->GetArrayLength(env, src) < jpegSize) THROW_ARG("Source buffer is not large enough"); - actualPitch = (pitch == 0) ? width * tjPixelSize[pf] : pitch; - arraySize = (y + height - 1) * actualPitch + (x + width) * tjPixelSize[pf]; + if ((jpegWidth = tj3Get(handle, TJPARAM_JPEGWIDTH)) == -1) + THROW_ARG("JPEG header has not yet been read"); + if ((jpegHeight = tj3Get(handle, TJPARAM_JPEGHEIGHT)) == -1) + THROW_ARG("JPEG header has not yet been read"); + + BAILIF0(sfcls = (*env)->FindClass(env, + "org/libjpegturbo/turbojpeg/TJScalingFactor")); + BAILIF0(_fid = + (*env)->GetFieldID(env, _cls, "scalingFactor", + "Lorg/libjpegturbo/turbojpeg/TJScalingFactor;")); + BAILIF0(sfobj = (*env)->GetObjectField(env, obj, _fid)); + BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "num", "I")); + scalingFactor.num = (*env)->GetIntField(env, sfobj, _fid); + BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "denom", "I")); + scalingFactor.denom = (*env)->GetIntField(env, sfobj, _fid); + + if (tj3SetScalingFactor(handle, scalingFactor) == -1) + THROW_TJ(); + scaledWidth = TJSCALED(jpegWidth, scalingFactor); + scaledHeight = TJSCALED(jpegHeight, scalingFactor); + + BAILIF0(crcls = (*env)->FindClass(env, "java/awt/Rectangle")); + BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "croppingRegion", + "Ljava/awt/Rectangle;")); + BAILIF0(crobj = (*env)->GetObjectField(env, obj, _fid)); + BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "x", "I")); + cr.x = (*env)->GetIntField(env, crobj, _fid); + BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "y", "I")); + cr.y = (*env)->GetIntField(env, crobj, _fid); + BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "width", "I")); + cr.w = (*env)->GetIntField(env, crobj, _fid); + BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "height", "I")); + cr.h = (*env)->GetIntField(env, crobj, _fid); + if (cr.x != 0 || cr.y != 0 || cr.w != 0 || cr.h != 0) { + scaledWidth = cr.w ? cr.w : scaledWidth - cr.x; + scaledHeight = cr.h ? cr.h : scaledHeight - cr.y; + } + + actualPitch = (pitch == 0) ? scaledWidth * tjPixelSize[pf] : pitch; + arraySize = (y + scaledHeight - 1) * actualPitch + + (x + scaledWidth) * tjPixelSize[pf]; if ((*env)->GetArrayLength(env, dst) * dstElementSize < arraySize) THROW_ARG("Destination buffer is not large enough"); BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0)); BAILIF0NOEC(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0)); - if (tjDecompress2(handle, jpegBuf, (unsigned long)jpegSize, - &dstBuf[y * actualPitch + x * tjPixelSize[pf]], width, - pitch, height, pf, flags) == -1) { - SAFE_RELEASE(dst, dstBuf); - SAFE_RELEASE(src, jpegBuf); - THROW_TJ(); + if (precision == 8) { + if (tj3Decompress8(handle, jpegBuf, (size_t)jpegSize, + &((unsigned char *)dstBuf)[y * actualPitch + + x * tjPixelSize[pf]], + pitch, pf) == -1) { + SAFE_RELEASE(dst, dstBuf); + SAFE_RELEASE(src, jpegBuf); + THROW_TJ(); + } + } else if (precision == 12) { + if (tj3Decompress12(handle, jpegBuf, (size_t)jpegSize, + &((short *)dstBuf)[y * actualPitch + + x * tjPixelSize[pf]], + pitch, pf) == -1) { + SAFE_RELEASE(dst, dstBuf); + SAFE_RELEASE(src, jpegBuf); + THROW_TJ(); + } + } else { + if (tj3Decompress16(handle, jpegBuf, (size_t)jpegSize, + &((unsigned short *)dstBuf)[y * actualPitch + + x * tjPixelSize[pf]], + pitch, pf) == -1) { + SAFE_RELEASE(dst, dstBuf); + SAFE_RELEASE(src, jpegBuf); + THROW_TJ(); + } } bailout: @@ -729,63 +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.2.x: TJDecompressor::decompress() byte destination */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress___3BI_3BIIIII - (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jbyteArray dst, - jint width, jint pitch, jint height, jint pf, jint flags) +/* 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, 0, 0, width, - pitch, height, pf, flags); + TJDecompressor_decompress(env, obj, src, jpegSize, dst, 1, 12, x, y, pitch, + pf); } -/* TurboJPEG 1.3.x: TJDecompressor::decompress() int destination */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress___3BI_3IIIIIIII - (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jintArray dst, - jint x, jint y, jint width, jint stride, jint height, jint pf, jint flags) +/* 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) { - if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF) - THROW_ARG("Invalid argument in decompress()"); - 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); - -bailout: - return; + TJDecompressor_decompress(env, obj, src, jpegSize, dst, 1, 16, x, y, pitch, + pf); } -/* TurboJPEG 1.2.x: TJDecompressor::decompress() int destination */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress___3BI_3IIIIII +/* 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 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), 0, 0, - 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; @@ -794,38 +839,40 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress unsigned char *dstPlanes[3] = { NULL, NULL, NULL }; jint dstOffsetsTmp[3] = { 0, 0, 0 }, dstStridesTmp[3] = { 0, 0, 0 }; int dstOffsets[3] = { 0, 0, 0 }, dstStrides[3] = { 0, 0, 0 }; - int jpegSubsamp = -1, jpegWidth = 0, jpegHeight = 0; - int nc = 0, i, width, height, scaledWidth, scaledHeight, nsf = 0; - tjscalingfactor *sf; + jclass sfcls; + jobject sfobj; + int jpegSubsamp, jpegWidth = 0, jpegHeight = 0; + int nc = 0, i, scaledWidth, scaledHeight; + tjscalingfactor scalingFactor; GET_HANDLE(); if ((*env)->GetArrayLength(env, src) < jpegSize) THROW_ARG("Source buffer is not large enough"); - BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegSubsamp", "I")); - jpegSubsamp = (int)(*env)->GetIntField(env, obj, _fid); - BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegWidth", "I")); - jpegWidth = (int)(*env)->GetIntField(env, obj, _fid); - BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegHeight", "I")); - jpegHeight = (int)(*env)->GetIntField(env, obj, _fid); - - nc = (jpegSubsamp == org_libjpegturbo_turbojpeg_TJ_SAMP_GRAY ? 1 : 3); - - width = desiredWidth; - height = desiredHeight; - if (width == 0) width = jpegWidth; - if (height == 0) height = jpegHeight; - sf = tjGetScalingFactors(&nsf); - if (!sf || nsf < 1) - THROW_ARG(tjGetErrorStr()); - for (i = 0; i < nsf; i++) { - scaledWidth = TJSCALED(jpegWidth, sf[i]); - scaledHeight = TJSCALED(jpegHeight, sf[i]); - if (scaledWidth <= width && scaledHeight <= height) - break; - } - if (i >= nsf) - THROW_ARG("Could not scale down to desired image dimensions"); + if ((jpegWidth = tj3Get(handle, TJPARAM_JPEGWIDTH)) == -1) + THROW_ARG("JPEG header has not yet been read"); + if ((jpegHeight = tj3Get(handle, TJPARAM_JPEGHEIGHT)) == -1) + THROW_ARG("JPEG header has not yet been read"); + + BAILIF0(sfcls = (*env)->FindClass(env, + "org/libjpegturbo/turbojpeg/TJScalingFactor")); + BAILIF0(_fid = + (*env)->GetFieldID(env, _cls, "scalingFactor", + "Lorg/libjpegturbo/turbojpeg/TJScalingFactor;")); + BAILIF0(sfobj = (*env)->GetObjectField(env, obj, _fid)); + BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "num", "I")); + scalingFactor.num = (*env)->GetIntField(env, sfobj, _fid); + BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "denom", "I")); + scalingFactor.denom = (*env)->GetIntField(env, sfobj, _fid); + + if (tj3SetScalingFactor(handle, scalingFactor) == -1) + THROW_TJ(); + scaledWidth = TJSCALED(jpegWidth, scalingFactor); + scaledHeight = TJSCALED(jpegHeight, scalingFactor); + + if ((jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP)) == TJSAMP_UNKNOWN) + THROW_ARG("TJPARAM_SUBSAMP must be specified"); + nc = jpegSubsamp == TJSAMP_GRAY ? 1 : 3; (*env)->GetIntArrayRegion(env, jDstOffsets, 0, nc, dstOffsetsTmp); if ((*env)->ExceptionCheck(env)) goto bailout; @@ -838,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++) { @@ -862,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]); @@ -877,48 +925,10 @@ bailout: SAFE_RELEASE(jDstPlanes[i], dstPlanesTmp[i]); } -/* TurboJPEG 1.2.x: TJDecompressor::decompressToYUV() */ -JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressToYUV___3BI_3BI - (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jbyteArray dst, - jint flags) -{ - tjhandle handle = 0; - unsigned char *jpegBuf = NULL, *dstBuf = NULL; - int jpegSubsamp = -1, jpegWidth = 0, jpegHeight = 0; - - 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 ((*env)->GetArrayLength(env, dst) < - (jsize)tjBufSizeYUV(jpegWidth, jpegHeight, jpegSubsamp)) - THROW_ARG("Destination buffer is not large enough"); - - BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0)); - BAILIF0NOEC(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0)); - - if (tjDecompressToYUV(handle, jpegBuf, (unsigned long)jpegSize, dstBuf, - flags) == -1) { - SAFE_RELEASE(dst, dstBuf); - SAFE_RELEASE(src, jpegBuf); - THROW_TJ(); - } - -bailout: - SAFE_RELEASE(dst, dstBuf); - SAFE_RELEASE(src, jpegBuf); -} - -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; @@ -928,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) @@ -962,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++) { @@ -985,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]); @@ -1000,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; @@ -1038,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")); @@ -1117,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; @@ -1134,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)) @@ -1150,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(); @@ -1193,14 +1207,21 @@ 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].op == TJXOP_TRANSPOSE || t[i].op == TJXOP_TRANSVERSE || + t[i].op == TJXOP_ROT90 || t[i].op == TJXOP_ROT270) { + w = jpegHeight; h = jpegWidth; + } if (t[i].r.w != 0) w = t[i].r.w; if (t[i].r.h != 0) h = t[i].r.h; BAILIF0(jdstBufs[i] = (*env)->GetObjectArrayElement(env, dstobjs, i)); - 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)); @@ -1208,8 +1229,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); @@ -1246,3 +1266,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 5477fed..6aab871 100644 --- a/turbojpeg-mapfile +++ b/turbojpeg-mapfile @@ -1,14 +1,14 @@ TURBOJPEG_1.0 { global: - tjInitCompress; - tjCompress; TJBUFSIZE; - tjInitDecompress; - tjDecompressHeader; + tjCompress; tjDecompress; + tjDecompressHeader; tjDestroy; tjGetErrorStr; + tjInitCompress; + tjInitDecompress; local: *; }; @@ -63,3 +63,46 @@ TURBOJPEG_2.0 tjLoadImage; tjSaveImage; } TURBOJPEG_1.4; + +TURBOJPEG_3 +{ + global: + 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 4432791..31be750 100644 --- a/turbojpeg-mapfile.jni +++ b/turbojpeg-mapfile.jni @@ -1,14 +1,14 @@ TURBOJPEG_1.0 { global: - tjInitCompress; - tjCompress; TJBUFSIZE; - tjInitDecompress; - tjDecompressHeader; + tjCompress; tjDecompress; + tjDecompressHeader; tjDestroy; tjGetErrorStr; + tjInitCompress; + tjInitDecompress; local: *; }; @@ -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 { @@ -99,3 +76,67 @@ TURBOJPEG_2.0 tjLoadImage; tjSaveImage; } TURBOJPEG_1.4; + +TURBOJPEG_3 +{ + global: + 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 0000000..5699869 --- /dev/null +++ b/turbojpeg-mp.c @@ -0,0 +1,530 @@ +/* + * 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 (dinfo->global_state <= DSTATE_START) { + jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize); + jpeg_read_header(dinfo, TRUE); + } + 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 (%d) and\n" + "actual (%d) cropping region left boundary", + this->croppingRegion.x, (int)crop_x); + if ((int)crop_w != this->croppingRegion.w) + THROWI("Unexplained mismatch between specified (%d) and\n" + "actual (%d) cropping region width", + this->croppingRegion.w, (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 (%d) and\n" + "actual (%d) cropping region upper boundary", + this->croppingRegion.y, (int)lines); + } + while ((int)dinfo->output_scanline < + this->croppingRegion.y + this->croppingRegion.h) + _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 (%d) and\n" + "actual (%d) cropping region lower boundary", + this->croppingRegion.y + this->croppingRegion.h, + (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); + +#if BITS_IN_JSAMPLE != 16 || defined(C_LOSSLESS_SUPPORTED) + + 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; + +#else /* BITS_IN_JSAMPLE != 16 || defined(C_LOSSLESS_SUPPORTED) */ + + static const char ERROR_MSG[] = + "16-bit data precision requires lossless JPEG,\n" + "which was disabled at build time."; + _JSAMPLE *retval = NULL; + + GET_TJINSTANCE(handle, NULL) + SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s(): %s", FUNCTION_NAME, + ERROR_MSG); + this->isInstanceError = TRUE; THROWG(ERROR_MSG, NULL) + +bailout: + return retval; + +#endif +} + + +/* 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; + +#if BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED) + + 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; + +#else /* BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED) */ + + GET_TJINSTANCE(handle, -1) + THROW("16-bit data precision requires lossless JPEG,\n" + "which was disabled at build time.") +bailout: + return retval; + +#endif +} + + +#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 a1544f2..b75d414 100644 --- a/turbojpeg.c +++ b/turbojpeg.c @@ -1,5 +1,5 @@ /* - * Copyright (C)2009-2022 D. R. Commander. All Rights Reserved. + * Copyright (C)2009-2023 D. R. Commander. All Rights Reserved. * Copyright (C)2021 Alex Richardson. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,6 +31,7 @@ libjpeg-turbo */ #include +#include #include #define JPEG_INTERNALS #include @@ -40,20 +41,18 @@ #include "./turbojpeg.h" #include "./tjutil.h" #include "transupp.h" -#include "./jpegcomp.h" +#include "./jpegapicomp.h" #include "./cdjpeg.h" -#include "jconfigint.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) -/* Error handling (based on example in example.txt) */ +/* Error handling (based on example in example.c) */ static THREAD_LOCAL char errStr[JMSG_LENGTH_MAX] = "No error"; @@ -98,7 +97,7 @@ static void my_emit_message(j_common_ptr cinfo, int msg_level) } -/* Global structures, macros, etc. */ +/********************** Global structures, macros, etc. **********************/ enum { COMPRESS = 1, DECOMPRESS = 2 }; @@ -106,11 +105,42 @@ typedef struct _tjinstance { struct jpeg_compress_struct cinfo; struct jpeg_decompress_struct dinfo; struct my_error_mgr jerr; - int init, headerRead; + int init; 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 +155,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 +169,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 +220,43 @@ 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, val1, val2) { \ + SNPRINTF(this->errStr, JMSG_LENGTH_MAX, "%s(): " format, FUNCTION_NAME, \ + val1, val2); \ + this->isInstanceError = TRUE; \ + SNPRINTF(errStr, JMSG_LENGTH_MAX, "%s(): " format, FUNCTION_NAME, val1, \ + val2); \ + 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,80 +324,74 @@ static int getPixelFormat(int pixelSize, int flags) return -1; } -static void setCompDefaults(struct jpeg_compress_struct *cinfo, - int pixelFormat, int subsamp, int jpegQual, - int flags) +static void setCompDefaults(tjinstance *this, int pixelFormat) { -#ifndef NO_GETENV - char env[7] = { 0 }; -#endif - - cinfo->in_color_space = pf2cs[pixelFormat]; - cinfo->input_components = tjPixelSize[pixelFormat]; - jpeg_set_defaults(cinfo); - -#ifndef NO_GETENV - if (!GETENV_S(env, 7, "TJ_OPTIMIZE") && !strcmp(env, "1")) - cinfo->optimize_coding = TRUE; - if (!GETENV_S(env, 7, "TJ_ARITHMETIC") && !strcmp(env, "1")) - cinfo->arith_code = 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) { + this->cinfo.in_color_space = pf2cs[pixelFormat]; + this->cinfo.input_components = tjPixelSize[pixelFormat]; + jpeg_set_defaults(&this->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; + + if (this->lossless) { +#ifdef C_LOSSLESS_SUPPORTED + jpeg_enable_lossless(&this->cinfo, this->losslessPSV, this->losslessPt); #endif - if (toupper(tempc) == 'B') { - cinfo->restart_interval = temp; - cinfo->restart_in_rows = 0; - } else - cinfo->restart_in_rows = temp; - } - } -#endif - - if (jpegQual >= 0) { - jpeg_set_quality(cinfo, jpegQual, TRUE); - if (jpegQual >= 96 || flags & TJFLAG_ACCURATEDCT) - cinfo->dct_method = JDCT_ISLOW; + if (pixelFormat == TJPF_GRAY) + this->subsamp = TJSAMP_GRAY; + else if (this->subsamp != TJSAMP_GRAY) + this->subsamp = TJSAMP_444; + return; + } + + jpeg_set_quality(&this->cinfo, this->quality, TRUE); + this->cinfo.dct_method = this->fastDCT ? JDCT_FASTEST : JDCT_ISLOW; + + switch (this->colorspace) { + case TJCS_RGB: + jpeg_set_colorspace(&this->cinfo, JCS_RGB); break; + case TJCS_YCbCr: + jpeg_set_colorspace(&this->cinfo, JCS_YCbCr); break; + case TJCS_GRAY: + jpeg_set_colorspace(&this->cinfo, JCS_GRAYSCALE); break; + case TJCS_CMYK: + jpeg_set_colorspace(&this->cinfo, JCS_CMYK); break; + case TJCS_YCCK: + jpeg_set_colorspace(&this->cinfo, JCS_YCCK); break; + default: + if (this->subsamp == TJSAMP_GRAY) + jpeg_set_colorspace(&this->cinfo, JCS_GRAYSCALE); + else if (pixelFormat == TJPF_CMYK) + jpeg_set_colorspace(&this->cinfo, JCS_YCCK); else - cinfo->dct_method = JDCT_FASTEST; + jpeg_set_colorspace(&this->cinfo, JCS_YCbCr); } - if (subsamp == TJSAMP_GRAY) - jpeg_set_colorspace(cinfo, JCS_GRAYSCALE); - else if (pixelFormat == TJPF_CMYK) - jpeg_set_colorspace(cinfo, JCS_YCCK); - else - jpeg_set_colorspace(cinfo, JCS_YCbCr); - 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); + this->cinfo.optimize_coding = this->optimize; +#ifdef C_PROGRESSIVE_SUPPORTED + if (this->progressive) jpeg_simple_progression(&this->cinfo); #endif - - cinfo->comp_info[0].h_samp_factor = tjMCUWidth[subsamp] / 8; - cinfo->comp_info[1].h_samp_factor = 1; - cinfo->comp_info[2].h_samp_factor = 1; - if (cinfo->num_components > 3) - cinfo->comp_info[3].h_samp_factor = tjMCUWidth[subsamp] / 8; - cinfo->comp_info[0].v_samp_factor = tjMCUHeight[subsamp] / 8; - cinfo->comp_info[1].v_samp_factor = 1; - cinfo->comp_info[2].v_samp_factor = 1; - if (cinfo->num_components > 3) - cinfo->comp_info[3].v_samp_factor = tjMCUHeight[subsamp] / 8; + this->cinfo.arith_code = this->arithmetic; + + this->cinfo.comp_info[0].h_samp_factor = tjMCUWidth[this->subsamp] / 8; + this->cinfo.comp_info[1].h_samp_factor = 1; + this->cinfo.comp_info[2].h_samp_factor = 1; + if (this->cinfo.num_components > 3) + this->cinfo.comp_info[3].h_samp_factor = tjMCUWidth[this->subsamp] / 8; + this->cinfo.comp_info[0].v_samp_factor = tjMCUHeight[this->subsamp] / 8; + this->cinfo.comp_info[1].v_samp_factor = 1; + this->cinfo.comp_info[2].v_samp_factor = 1; + if (this->cinfo.num_components > 3) + this->cinfo.comp_info[3].v_samp_factor = tjMCUHeight[this->subsamp] / 8; } static int getSubsamp(j_decompress_ptr dinfo) { - int retval = -1, i, k; + int retval = TJSAMP_UNKNOWN, i, k; /* The sampling factors actually have no meaning with grayscale JPEG files, and in fact it's possible to generate grayscale JPEGs with sampling @@ -355,11 +400,13 @@ static int getSubsamp(j_decompress_ptr dinfo) if (dinfo->num_components == 1 && dinfo->jpeg_color_space == JCS_GRAYSCALE) return TJSAMP_GRAY; - for (i = 0; i < NUMSUBOPT; i++) { - if (dinfo->num_components == pixelsize[i] || + for (i = 0; i < TJ_NUMSAMP; 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; @@ -405,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 == @@ -424,9 +471,309 @@ static int getSubsamp(j_decompress_ptr dinfo) } -/* General API functions */ +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; +} -DLLEXPORT char *tjGetErrorStr2(tjhandle handle) + +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 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; @@ -437,14 +784,21 @@ 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) { return errStr; } -DLLEXPORT int tjGetErrorCode(tjhandle handle) +/* TurboJPEG 3+ */ +DLLEXPORT int tj3GetErrorCode(tjhandle handle) { tjinstance *this = (tjinstance *)handle; @@ -452,16 +806,46 @@ DLLEXPORT int tjGetErrorCode(tjhandle handle) else return TJERR_FATAL; } +/* TurboJPEG 2.0+ */ +DLLEXPORT int tjGetErrorCode(tjhandle handle) +{ + return tj3GetErrorCode(handle); +} + -DLLEXPORT int tjDestroy(tjhandle handle) +/* TurboJPEG 3+ */ +DLLEXPORT void tj3Destroy(tjhandle handle) { - GET_INSTANCE(handle); + tjinstance *this = (tjinstance *)handle; + j_compress_ptr cinfo = NULL; + j_decompress_ptr dinfo = NULL; + + if (!this) return; - if (setjmp(this->jerr.setjmp_buffer)) return -1; + 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); - return 0; +} + +/* TurboJPEG 1.0+ */ +DLLEXPORT int tjDestroy(tjhandle handle) +{ + static const char FUNCTION_NAME[] = "tjDestroy"; + int retval = 0; + + if (!handle) THROWG("Invalid handle", -1); + + SNPRINTF(errStr, JMSG_LENGTH_MAX, "No error"); + tj3Destroy(handle); + if (strcmp(errStr, "No error")) retval = -1; + +bailout: + return retval; } @@ -470,27 +854,41 @@ DLLEXPORT int tjDestroy(tjhandle handle) with turbojpeg.dll for compatibility reasons. However, these functions can potentially be used for other purposes by different implementations. */ -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); } -/* Compressor */ +/******************************** Compressor *********************************/ 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.txt */ + /* This is also straight out of example.c */ this->cinfo.err = jpeg_std_error(&this->jerr.pub); this->jerr.pub.error_exit = my_error_exit; this->jerr.pub.output_message = my_output_message; @@ -514,28 +912,26 @@ static tjhandle _tjInitCompress(tjinstance *this) return (tjhandle)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); } -DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp) +/* TurboJPEG 3+ */ +DLLEXPORT size_t tj3JPEGBufSize(int width, int height, int jpegSubsamp) { + static const char FUNCTION_NAME[] = "tj3JPEGBufSize"; unsigned long long retval = 0; int mcuw, mcuh, chromasf; - if (width < 1 || height < 1 || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT) - THROWG("tjBufSize(): Invalid argument"); + if (width < 1 || height < 1 || jpegSubsamp < TJSAMP_UNKNOWN || + jpegSubsamp >= TJ_NUMSAMP) + THROWG("Invalid argument", 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 @@ -545,209 +941,246 @@ DLLEXPORT unsigned long tjBufSize(int width, int height, int 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"); + THROWG("Image is too large", 0); bailout: - return (unsigned long)retval; + return (size_t)retval; +} + +/* TurboJPEG 1.2+ */ +DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp) +{ + static const char FUNCTION_NAME[] = "tjBufSize"; + size_t retval; + + if (jpegSubsamp < 0) + THROWG("Invalid argument", 0); + + retval = tj3JPEGBufSize(width, height, jpegSubsamp); + +bailout: + 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; } -DLLEXPORT unsigned long tjBufSizeYUV2(int width, int pad, int height, - int subsamp) +/* TurboJPEG 3+ */ +DLLEXPORT size_t tj3YUVBufSize(int width, int align, int height, int subsamp) { + static const char FUNCTION_NAME[] = "tj3YUVBufSize"; unsigned long long retval = 0; int nc, i; - if (subsamp < 0 || subsamp >= NUMSUBOPT) - THROWG("tjBufSizeYUV2(): Invalid argument"); + if (align < 1 || !IS_POW2(align) || subsamp < 0 || subsamp >= TJ_NUMSAMP) + THROWG("Invalid argument", 0); nc = (subsamp == TJSAMP_GRAY ? 1 : 3); for (i = 0; i < nc; i++) { - int pw = tjPlaneWidth(i, width, subsamp); - int stride = PAD(pw, pad); - int ph = tjPlaneHeight(i, height, subsamp); + int pw = tj3YUVPlaneWidth(i, width, subsamp); + int stride = PAD(pw, align); + 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"); + THROWG("Image is too large", 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+ */ DLLEXPORT unsigned long tjBufSizeYUV(int width, int height, int subsamp) { return tjBufSizeYUV2(width, 4, height, subsamp); } +/* TurboJPEG 1.1+ */ DLLEXPORT unsigned long TJBUFSIZEYUV(int width, int height, int subsamp) { return tjBufSizeYUV(width, height, subsamp); } -DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp) +/* TurboJPEG 3+ */ +DLLEXPORT int tj3YUVPlaneWidth(int componentID, int width, int subsamp) { - int pw, nc, retval = 0; + 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(width, tjMCUWidth[subsamp] / 8); + pw = PAD((unsigned long long)width, tjMCUWidth[subsamp] / 8); if (componentID == 0) retval = pw; else retval = pw * 8 / tjMCUWidth[subsamp]; + if (retval > (unsigned long long)INT_MAX) + THROWG("Width is too large", 0); + bailout: - return retval; + return (int)retval; +} + +/* TurboJPEG 1.4+ */ +DLLEXPORT int tjPlaneWidth(int componentID, int width, int subsamp) +{ + int retval = tj3YUVPlaneWidth(componentID, width, subsamp); + return (retval == 0) ? -1 : retval; } -DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp) +/* TurboJPEG 3+ */ +DLLEXPORT int tj3YUVPlaneHeight(int componentID, int height, int subsamp) { - int ph, nc, retval = 0; + 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(height, tjMCUHeight[subsamp] / 8); + ph = PAD((unsigned long long)height, tjMCUHeight[subsamp] / 8); if (componentID == 0) retval = ph; else retval = ph * 8 / tjMCUHeight[subsamp]; + if (retval > (unsigned long long)INT_MAX) + THROWG("Height is too large", 0); + bailout: - return retval; + return (int)retval; +} + +/* TurboJPEG 1.4+ */ +DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp) +{ + int retval = tj3YUVPlaneHeight(componentID, height, subsamp); + return (retval == 0) ? -1 : retval; } -DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride, - int height, int subsamp) +/* TurboJPEG 3+ */ +DLLEXPORT size_t tj3YUVPlaneSize(int componentID, int width, int stride, + int height, int subsamp) { + static const char FUNCTION_NAME[] = "tj3YUVPlaneSize"; unsigned long long retval = 0; int pw, ph; - if (width < 1 || height < 1 || subsamp < 0 || subsamp >= NUMSUBOPT) - THROWG("tjPlaneSizeYUV(): Invalid argument"); + if (width < 1 || height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP) + THROWG("Invalid argument", 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"); + THROWG("Image is too large", 0); bailout: - return (unsigned long)retval; + return (size_t)retval; +} + +/* TurboJPEG 1.4+ */ +DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride, + int height, int subsamp) +{ + size_t retval = tj3YUVPlaneSize(componentID, width, stride, height, subsamp); + return (retval == 0) ? -1 : (unsigned long)retval; } +/* tj3Compress*() is implemented in turbojpeg-mp.c */ +#define BITS_IN_JSAMPLE 8 +#include "turbojpeg-mp.c" +#undef BITS_IN_JSAMPLE +#define BITS_IN_JSAMPLE 12 +#include "turbojpeg-mp.c" +#undef BITS_IN_JSAMPLE +#define BITS_IN_JSAMPLE 16 +#include "turbojpeg-mp.c" +#undef BITS_IN_JSAMPLE + +/* TurboJPEG 1.2+ */ DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf, int width, int pitch, int height, int pixelFormat, unsigned char **jpegBuf, unsigned long *jpegSize, int jpegSubsamp, int jpegQual, int flags) { - int i, retval = 0; - boolean alloc = TRUE; - JSAMPROW *row_pointer = NULL; + static const char FUNCTION_NAME[] = "tjCompress2"; + int retval = 0; + size_t size; - GET_CINSTANCE(handle) - this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE; - if ((this->init & COMPRESS) == 0) - THROW("tjCompress2(): Instance has not been initialized for compression"); + GET_TJINSTANCE(handle, -1); - if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 || - pixelFormat < 0 || pixelFormat >= TJ_NUMPF || jpegBuf == NULL || - jpegSize == NULL || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT || + if (jpegSize == NULL || jpegSubsamp < 0 || jpegSubsamp >= TJ_NUMSAMP || jpegQual < 0 || jpegQual > 100) - THROW("tjCompress2(): Invalid argument"); - - if (pitch == 0) pitch = width * tjPixelSize[pixelFormat]; - - 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 + THROW("Invalid argument"); - if (flags & TJFLAG_NOREALLOC) { - alloc = FALSE; *jpegSize = tjBufSize(width, height, jpegSubsamp); - } - jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc); - setCompDefaults(cinfo, pixelFormat, jpegSubsamp, jpegQual, flags); + this->quality = jpegQual; + this->subsamp = jpegSubsamp; + processFlags(handle, flags, COMPRESS); - 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; } +/* TurboJPEG 1.0+ */ DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width, int pitch, int height, int pixelSize, unsigned char *jpegBuf, unsigned long *jpegSize, 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); @@ -764,11 +1197,13 @@ DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width, } -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]; @@ -777,8 +1212,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; @@ -786,17 +1220,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 >= NUMSUBOPT) - 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 CMYK pixels"); + THROW("Cannot generate YUV images from packed-pixel CMYK images"); if (pitch == 0) pitch = width * tjPixelSize[pixelFormat]; @@ -807,21 +1243,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 - - setCompDefaults(cinfo, pixelFormat, subsamp, -1, flags); + 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); @@ -832,9 +1263,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]; @@ -849,11 +1280,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); @@ -866,10 +1297,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); @@ -881,7 +1312,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; @@ -918,57 +1349,109 @@ bailout: free(outbuf[i]); } if (this->jerr.warning) retval = -1; - this->jerr.stopOnWarning = FALSE; return retval; } -DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf, - int width, int pitch, int height, int pixelFormat, - unsigned char *dstBuf, int pad, int subsamp, - int flags) +/* 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; - tjinstance *this = (tjinstance *)handle; - if (!this) THROWG("tjEncodeYUV3(): Invalid handle"); - this->isInstanceError = FALSE; + GET_TJINSTANCE(handle, -1); + + if (width <= 0 || height <= 0 || dstBuf == NULL || align < 1 || + !IS_POW2(align)) + THROW("Invalid argument"); - if (width <= 0 || height <= 0 || dstBuf == NULL || pad < 0 || - !IS_POW2(pad) || subsamp < 0 || subsamp >= NUMSUBOPT) - THROW("tjEncodeYUV3(): Invalid argument"); + if (this->subsamp == TJSAMP_UNKNOWN) + THROW("TJPARAM_SUBSAMP must be specified"); - pw0 = tjPlaneWidth(0, width, subsamp); - ph0 = tjPlaneHeight(0, height, subsamp); + pw0 = tj3YUVPlaneWidth(0, width, this->subsamp); + ph0 = tj3YUVPlaneHeight(0, height, this->subsamp); dstPlanes[0] = dstBuf; - strides[0] = PAD(pw0, pad); - if (subsamp == TJSAMP_GRAY) { + strides[0] = PAD(pw0, align); + if (this->subsamp == TJSAMP_GRAY) { strides[1] = strides[2] = 0; dstPlanes[1] = dstPlanes[2] = NULL; } else { - int pw1 = tjPlaneWidth(1, width, subsamp); - int ph1 = tjPlaneHeight(1, height, subsamp); + int pw1 = tj3YUVPlaneWidth(1, width, this->subsamp); + int ph1 = tj3YUVPlaneHeight(1, height, this->subsamp); - strides[1] = strides[2] = PAD(pw1, pad); + strides[1] = strides[2] = PAD(pw1, align); dstPlanes[1] = dstPlanes[0] + strides[0] * ph0; dstPlanes[2] = dstPlanes[1] + strides[1] * ph1; } - return tjEncodeYUVPlanes(handle, srcBuf, width, pitch, height, pixelFormat, - dstPlanes, strides, subsamp, flags); + return tj3EncodeYUVPlanes8(handle, srcBuf, width, pitch, height, pixelFormat, + dstPlanes, strides); bailout: return retval; } -DLLEXPORT int tjEncodeYUV2(tjhandle handle, unsigned char *srcBuf, int width, - int pitch, int height, int pixelFormat, - unsigned char *dstBuf, int subsamp, int flags) +/* TurboJPEG 1.4+ */ +DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf, + int width, int pitch, int height, int pixelFormat, + unsigned char *dstBuf, int align, int subsamp, + int flags) { - return tjEncodeYUV3(handle, srcBuf, width, pitch, height, pixelFormat, - dstBuf, 4, subsamp, flags); + static const char FUNCTION_NAME[] = "tjEncodeYUV3"; + int retval = 0; + + GET_TJINSTANCE(handle, -1); + + if (subsamp < 0 || subsamp >= TJ_NUMSAMP) + THROW("Invalid argument"); + + this->subsamp = subsamp; + processFlags(handle, flags, COMPRESS); + + return tj3EncodeYUV8(handle, srcBuf, width, pitch, height, pixelFormat, + dstBuf, align); + +bailout: + return retval; +} + +/* TurboJPEG 1.2+ */ +DLLEXPORT int tjEncodeYUV2(tjhandle handle, unsigned char *srcBuf, int width, + int pitch, int height, int pixelFormat, + unsigned char *dstBuf, int subsamp, int flags) +{ + return tjEncodeYUV3(handle, srcBuf, width, pitch, height, pixelFormat, + dstBuf, 4, subsamp, flags); } +/* TurboJPEG 1.1+ */ DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width, int pitch, int height, int pixelSize, unsigned char *dstBuf, int subsamp, int flags) @@ -979,14 +1462,14 @@ DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width, } -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], @@ -995,21 +1478,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 >= NUMSUBOPT || 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. */ @@ -1018,18 +1504,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); - setCompDefaults(cinfo, TJPF_RGB, subsamp, jpegQual, flags); + setCompDefaults(this, TJPF_RGB); cinfo->raw_data_in = TRUE; jpeg_start_compress(cinfo, TRUE); @@ -1047,7 +1528,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; @@ -1056,11 +1537,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]; @@ -1113,58 +1594,121 @@ bailout: } free(_tmpbuf); if (this->jerr.warning) retval = -1; - this->jerr.stopOnWarning = FALSE; return retval; } -DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf, - int width, int pad, int height, int subsamp, - unsigned char **jpegBuf, - unsigned long *jpegSize, int jpegQual, - int flags) +/* 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; - tjinstance *this = (tjinstance *)handle; - if (!this) THROWG("tjCompressFromYUV(): Invalid handle"); - this->isInstanceError = FALSE; + GET_TJINSTANCE(handle, -1); - if (srcBuf == NULL || width <= 0 || pad < 1 || height <= 0 || subsamp < 0 || - subsamp >= NUMSUBOPT) - THROW("tjCompressFromYUV(): Invalid argument"); + if (srcBuf == NULL || width <= 0 || align < 1 || !IS_POW2(align) || + height <= 0) + THROW("Invalid argument"); - pw0 = tjPlaneWidth(0, width, subsamp); - ph0 = tjPlaneHeight(0, height, subsamp); + if (this->subsamp == TJSAMP_UNKNOWN) + THROW("TJPARAM_SUBSAMP must be specified"); + + pw0 = tj3YUVPlaneWidth(0, width, this->subsamp); + ph0 = tj3YUVPlaneHeight(0, height, this->subsamp); srcPlanes[0] = srcBuf; - strides[0] = PAD(pw0, pad); - if (subsamp == TJSAMP_GRAY) { + 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, subsamp); - int ph1 = tjPlaneHeight(1, height, subsamp); + int pw1 = tjPlaneWidth(1, width, this->subsamp); + int ph1 = tjPlaneHeight(1, height, this->subsamp); - strides[1] = strides[2] = PAD(pw1, pad); + strides[1] = strides[2] = PAD(pw1, align); srcPlanes[1] = srcPlanes[0] + strides[0] * ph0; srcPlanes[2] = srcPlanes[1] + strides[1] * ph1; } - return tjCompressFromYUVPlanes(handle, srcPlanes, width, strides, height, - subsamp, jpegBuf, jpegSize, jpegQual, flags); + return tj3CompressFromYUVPlanes8(handle, srcPlanes, width, strides, height, + jpegBuf, jpegSize); + +bailout: + return retval; +} + +/* TurboJPEG 1.4+ */ +DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf, + int width, int align, int height, int subsamp, + unsigned char **jpegBuf, + unsigned long *jpegSize, int jpegQual, + int flags) +{ + static const char FUNCTION_NAME[] = "tjCompressFromYUV"; + int retval = -1; + size_t size; + + GET_TJINSTANCE(handle, -1); + + if (subsamp < 0 || subsamp >= TJ_NUMSAMP) + THROW("Invalid argument"); + + this->quality = jpegQual; + this->subsamp = subsamp; + processFlags(handle, flags, COMPRESS); + + size = (size_t)(*jpegSize); + retval = tj3CompressFromYUV8(handle, srcBuf, width, align, height, jpegBuf, + &size); + *jpegSize = (unsigned long)size; bailout: return retval; } -/* Decompressor */ +/******************************* Decompressor ********************************/ static tjhandle _tjInitDecompress(tjinstance *this) { static unsigned char buffer[1]; - /* This is also straight out of example.txt */ + /* This is also straight out of example.c */ this->dinfo.err = jpeg_std_error(&this->jerr.pub); this->jerr.pub.error_exit = my_error_exit; this->jerr.pub.output_message = my_output_message; @@ -1188,36 +1732,27 @@ static tjhandle _tjInitDecompress(tjinstance *this) return (tjhandle)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); } -DLLEXPORT int tjDecompressHeader3(tjhandle handle, +/* TurboJPEG 3+ */ +DLLEXPORT int tj3DecompressHeader(tjhandle handle, const unsigned char *jpegBuf, - unsigned long jpegSize, int *width, - int *height, int *jpegSubsamp, - int *jpegColorspace) + size_t jpegSize) { + static const char FUNCTION_NAME[] = "tj3DecompressHeader"; int retval = 0; GET_DINSTANCE(handle); if ((this->init & DECOMPRESS) == 0) - THROW("tjDecompressHeader3(): 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) - THROW("tjDecompressHeader3(): 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. */ @@ -1233,32 +1768,50 @@ DLLEXPORT int tjDecompressHeader3(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; - } + setDecompParameters(this); jpeg_abort_decompress(dinfo); - if (*jpegSubsamp < 0) - THROW("tjDecompressHeader3(): Could not determine subsampling type for JPEG image"); - if (*jpegColorspace < 0) - THROW("tjDecompressHeader3(): Could not determine colorspace of JPEG image"); - if (*width < 1 || *height < 1) - THROW("tjDecompressHeader3(): 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; return retval; } +/* TurboJPEG 1.4+ */ +DLLEXPORT int tjDecompressHeader3(tjhandle handle, + const unsigned char *jpegBuf, + unsigned long jpegSize, int *width, + int *height, int *jpegSubsamp, + int *jpegColorspace) +{ + static const char FUNCTION_NAME[] = "tjDecompressHeader3"; + int retval = 0; + + 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+ */ DLLEXPORT int tjDecompressHeader2(tjhandle handle, unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height, int *jpegSubsamp) @@ -1269,6 +1822,7 @@ DLLEXPORT int tjDecompressHeader2(tjhandle handle, unsigned char *jpegBuf, jpegSubsamp, &jpegColorspace); } +/* TurboJPEG 1.0+ */ DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height) @@ -1280,50 +1834,121 @@ DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf, } -DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numscalingfactors) +/* TurboJPEG 3+ */ +DLLEXPORT tjscalingfactor *tj3GetScalingFactors(int *numScalingFactors) { - if (numscalingfactors == NULL) { - SNPRINTF(errStr, JMSG_LENGTH_MAX, - "tjGetScalingFactors(): Invalid argument"); - return NULL; + static const char FUNCTION_NAME[] = "tj3GetScalingFactors"; + tjscalingfactor *retval = (tjscalingfactor *)sf; + + if (numScalingFactors == NULL) + THROWG("Invalid argument", NULL); + + *numScalingFactors = NUMSF; + +bailout: + return retval; +} + +/* 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; } - *numscalingfactors = NUMSF; - return (tjscalingfactor *)sf; + 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 (%d) is not\n" + "divisible by the scaled MCU width (%d)", + croppingRegion.x, + TJSCALED(tjMCUWidth[this->subsamp], this->scalingFactor)); + if (croppingRegion.w == 0) + croppingRegion.w = scaledWidth - croppingRegion.x; + 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, unsigned long jpegSize, unsigned char *dstBuf, int width, int pitch, int height, int pixelFormat, int flags) { - JSAMPROW *row_pointer = NULL; + static const char FUNCTION_NAME[] = "tjDecompress2"; int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh; - struct my_progress_mgr progress; GET_DINSTANCE(handle); - this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE; if ((this->init & DECOMPRESS) == 0) - THROW("tjDecompress2(): Instance has not been initialized for decompression"); - - if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 || - pitch < 0 || height < 0 || pixelFormat < 0 || pixelFormat >= TJ_NUMPF) - THROW("tjDecompress2(): Invalid argument"); - -#ifndef NO_PUTENV - if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1"); - else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1"); - else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1"); -#endif + THROW("Instance has not been initialized for decompression"); - if (flags & TJFLAG_LIMITSCANS) { - memset(&progress, 0, sizeof(struct my_progress_mgr)); - progress.pub.progress_monitor = my_progress_monitor; - progress.this = this; - dinfo->progress = &progress.pub; - } else - dinfo->progress = NULL; + if (jpegBuf == NULL || jpegSize <= 0 || width < 0 || height < 0) + THROW("Invalid argument"); if (setjmp(this->jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. */ @@ -1332,10 +1957,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; @@ -1346,40 +1967,23 @@ 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); + 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; } +/* TurboJPEG 1.0+ */ DLLEXPORT int tjDecompress(tjhandle handle, unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf, int width, int pitch, int height, int pixelSize, @@ -1393,44 +1997,42 @@ DLLEXPORT int tjDecompress(tjhandle handle, unsigned char *jpegBuf, } -static int 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); } - - return 0; } @@ -1443,12 +2045,14 @@ static void my_reset_marker_reader(j_decompress_ptr dinfo) { } -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]; @@ -1459,46 +2063,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 >= NUMSUBOPT || - 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 CMYK pixels."); + 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; - if (setDecodeDefaults(dinfo, pixelFormat, subsamp, flags) == -1) { - retval = -1; goto bailout; - } + 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; @@ -1508,7 +2104,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); @@ -1520,9 +2116,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]; @@ -1536,10 +2132,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); @@ -1551,7 +2147,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; @@ -1587,57 +2183,110 @@ bailout: free(inbuf[i]); } if (this->jerr.warning) retval = -1; - this->jerr.stopOnWarning = FALSE; return retval; } -DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf, - int pad, int subsamp, unsigned char *dstBuf, - int width, int pitch, int height, int pixelFormat, - int flags) +/* 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; - tjinstance *this = (tjinstance *)handle; - if (!this) THROWG("tjDecodeYUV(): Invalid handle"); - this->isInstanceError = FALSE; + GET_TJINSTANCE(handle, -1); + + if (srcBuf == NULL || align < 1 || !IS_POW2(align) || width <= 0 || + height <= 0) + THROW("Invalid argument"); - if (srcBuf == NULL || pad < 0 || !IS_POW2(pad) || subsamp < 0 || - subsamp >= NUMSUBOPT || width <= 0 || height <= 0) - THROW("tjDecodeYUV(): Invalid argument"); + if (this->subsamp == TJSAMP_UNKNOWN) + THROW("TJPARAM_SUBSAMP must be specified"); - pw0 = tjPlaneWidth(0, width, subsamp); - ph0 = tjPlaneHeight(0, height, subsamp); + pw0 = tj3YUVPlaneWidth(0, width, this->subsamp); + ph0 = tj3YUVPlaneHeight(0, height, this->subsamp); srcPlanes[0] = srcBuf; - strides[0] = PAD(pw0, pad); - if (subsamp == TJSAMP_GRAY) { + 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, subsamp); - int ph1 = tjPlaneHeight(1, height, subsamp); + int pw1 = tj3YUVPlaneWidth(1, width, this->subsamp); + int ph1 = tj3YUVPlaneHeight(1, height, this->subsamp); - strides[1] = strides[2] = PAD(pw1, pad); + strides[1] = strides[2] = PAD(pw1, align); srcPlanes[1] = srcPlanes[0] + strides[0] * ph0; srcPlanes[2] = srcPlanes[1] + strides[1] * ph1; } - return tjDecodeYUVPlanes(handle, srcPlanes, strides, subsamp, dstBuf, width, - pitch, height, pixelFormat, flags); + return tj3DecodeYUVPlanes8(handle, srcPlanes, strides, dstBuf, width, pitch, + height, pixelFormat); bailout: return retval; } -DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle, - const unsigned char *jpegBuf, - unsigned long jpegSize, - unsigned char **dstPlanes, int width, - int *strides, int height, int flags) +/* TurboJPEG 1.4+ */ +DLLEXPORT int tjDecodeYUV(tjhandle handle, const unsigned char *srcBuf, + int align, int subsamp, unsigned char *dstBuf, + int width, int pitch, int height, int pixelFormat, + int flags) +{ + static const char FUNCTION_NAME[] = "tjDecodeYUV"; + int retval = -1; + + GET_TJINSTANCE(handle, -1); + + if (subsamp < 0 || subsamp >= TJ_NUMSAMP) + THROW("Invalid argument"); + + this->subsamp = subsamp; + processFlags(handle, flags, DECOMPRESS); + + return tj3DecodeYUV8(handle, srcBuf, align, dstBuf, width, pitch, height, + pixelFormat); + +bailout: + return retval; +} + + +/* TurboJPEG 3+ */ +DLLEXPORT int tj3DecompressToYUVPlanes8(tjhandle handle, + const unsigned char *jpegBuf, + size_t jpegSize, + unsigned char **dstPlanes, + int *strides) { - int i, sfi, row, retval = 0; - int jpegwidth, jpegheight, jpegSubsamp, scaledw, scaledh; + static const char FUNCTION_NAME[] = "tj3DecompressToYUVPlanes8"; + int i, row, retval = 0; int pw[MAX_COMPONENTS], ph[MAX_COMPONENTS], iw[MAX_COMPONENTS], tmpbufsize = 0, usetmpbuf = 0, th[MAX_COMPONENTS]; JSAMPLE *_tmpbuf = NULL, *ptr; @@ -1646,26 +2295,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; @@ -1678,39 +2319,25 @@ DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle, retval = -1; goto bailout; } - if (!this->headerRead) { + if (dinfo->global_state <= DSTATE_START) { jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize); jpeg_read_header(dinfo, TRUE); } - this->headerRead = 0; - jpegSubsamp = getSubsamp(dinfo); - if (jpegSubsamp < 0) - THROW("tjDecompressToYUVPlanes(): Could not determine subsampling type for JPEG image"); + 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]; @@ -1718,13 +2345,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; @@ -1733,11 +2360,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]; @@ -1750,8 +2377,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); @@ -1763,7 +2390,7 @@ DLLEXPORT int tjDecompressToYUVPlanes(tjhandle handle, for (i = 0; i < dinfo->num_components; i++) { jpeg_component_info *compptr = &dinfo->comp_info[i]; - if (jpegSubsamp == TJ_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 @@ -1775,8 +2402,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]; } @@ -1806,40 +2433,36 @@ bailout: } free(_tmpbuf); if (this->jerr.warning) retval = -1; - this->jerr.stopOnWarning = FALSE; return retval; } -DLLEXPORT int tjDecompressToYUV2(tjhandle handle, const unsigned char *jpegBuf, - unsigned long jpegSize, unsigned char *dstBuf, - int width, int pad, int height, int flags) +/* 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) { - 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 || - pad < 1 || !IS_POW2(pad) || 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; - for (i = 0; i < NUMSF; i++) { scaledw = TJSCALED(jpegwidth, sf[i]); scaledh = TJSCALED(jpegheight, sf[i]); @@ -1847,33 +2470,129 @@ 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"); + + processFlags(handle, flags, DECOMPRESS); + + 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 (dinfo->global_state <= DSTATE_START) { + 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 = tjPlaneWidth(0, width, jpegSubsamp); - ph0 = tjPlaneHeight(0, height, jpegSubsamp); + pw0 = tj3YUVPlaneWidth(0, width, this->subsamp); + ph0 = tj3YUVPlaneHeight(0, height, this->subsamp); dstPlanes[0] = dstBuf; - strides[0] = PAD(pw0, pad); - if (jpegSubsamp == TJSAMP_GRAY) { + strides[0] = PAD(pw0, align); + 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, pad); + 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); + 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); + + 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; +} + +/* TurboJPEG 1.1+ */ DLLEXPORT int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf, int flags) @@ -1882,54 +2601,36 @@ DLLEXPORT int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf, } -/* Transformer */ +/******************************** Transformer ********************************/ +/* 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); } -DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf, - unsigned long jpegSize, int n, - unsigned char **dstBufs, unsigned long *dstSizes, - tjtransform *t, int flags) +/* TurboJPEG 3+ */ +DLLEXPORT int tj3Transform(tjhandle handle, const unsigned char *jpegBuf, + size_t jpegSize, int n, unsigned char **dstBufs, + size_t *dstSizes, const tjtransform *t) { + static const char FUNCTION_NAME[] = "tj3Transform"; jpeg_transform_info *xinfo = NULL; jvirt_barray_ptr *srccoefs, *dstcoefs; - int retval = 0, i, jpegSubsamp, saveMarkers = 0; + int retval = 0, i, saveMarkers = 0; boolean alloc = TRUE; struct my_progress_mgr progress; GET_INSTANCE(handle); - this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE; if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0) - THROW("tjTransform(): Instance has not been initialized for transformation"); + THROW("Instance has not been initialized for transformation"); if (jpegBuf == NULL || jpegSize <= 0 || n < 1 || dstBufs == NULL || - dstSizes == NULL || t == NULL || flags < 0) - THROW("tjTransform(): Invalid argument"); - -#ifndef NO_PUTENV - if (flags & TJFLAG_FORCEMMX) PUTENV_S("JSIMD_FORCEMMX", "1"); - else if (flags & TJFLAG_FORCESSE) PUTENV_S("JSIMD_FORCESSE", "1"); - else if (flags & TJFLAG_FORCESSE2) PUTENV_S("JSIMD_FORCESSE2", "1"); -#endif + dstSizes == NULL || t == NULL) + THROW("Invalid argument"); - if (flags & TJFLAG_LIMITSCANS) { + if (this->scanLimit) { memset(&progress, 0, sizeof(struct my_progress_mgr)); progress.pub.progress_monitor = my_progress_monitor; progress.this = this; @@ -1939,7 +2640,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)) { @@ -1947,7 +2648,8 @@ DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf, retval = -1; goto bailout; } - jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize); + if (dinfo->global_state <= DSTATE_START) + jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize); for (i = 0; i < n; i++) { xinfo[i].transform = xformtypes[t[i].op]; @@ -1974,25 +2676,22 @@ 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 (dinfo->global_state <= DSTATE_START) + 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) { - 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]); - this->isInstanceError = TRUE; - retval = -1; goto bailout; - } + 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) + THROWI("To crop this JPEG image, x must be a multiple of %d\n" + "and y must be a multiple of %d.", tjMCUWidth[this->subsamp], + tjMCUHeight[this->subsamp]); } } @@ -2003,18 +2702,30 @@ DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf, if (!xinfo[i].crop) { w = dinfo->image_width; h = dinfo->image_height; + if (t[i].op == TJXOP_TRANSPOSE || t[i].op == TJXOP_TRANSVERSE || + t[i].op == TJXOP_ROT90 || t[i].op == TJXOP_ROT270) { + w = dinfo->image_height; h = dinfo->image_width; + } } else { w = xinfo[i].crop_width; h = xinfo[i].crop_height; } - 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 (flags & TJFLAG_PROGRESSIVE || t[i].options & TJXOPT_PROGRESSIVE) + if (this->optimize || t[i].options & TJXOPT_OPTIMIZE) + cinfo->optimize_coding = TRUE; +#ifdef C_PROGRESSIVE_SUPPORTED + if (this->progressive || t[i].options & TJXOPT_PROGRESSIVE) jpeg_simple_progression(cinfo); +#endif + if (this->arithmetic || t[i].options & TJXOPT_ARITHMETIC) { + cinfo->arith_code = TRUE; + cinfo->optimize_coding = FALSE; + } if (!(t[i].options & TJXOPT_NOOUTPUT)) { jpeg_write_coefficients(cinfo, dstcoefs); jcopy_markers_execute(dinfo, cinfo, t[i].options & TJXOPT_COPYNONE ? @@ -2044,8 +2755,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; } } @@ -2064,185 +2775,92 @@ 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; } - -DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width, - int align, int *height, int *pixelFormat, - int flags) +/* TurboJPEG 1.2+ */ +DLLEXPORT int tjTransform(tjhandle handle, const unsigned char *jpegBuf, + unsigned long jpegSize, int n, + unsigned char **dstBufs, unsigned long *dstSizes, + tjtransform *t, int flags) { - int retval = 0, tempc; - size_t pitch; - tjhandle handle = NULL; - tjinstance *this; - j_compress_ptr cinfo = NULL; - cjpeg_source_ptr src; - unsigned char *dstBuf = NULL; - FILE *file = NULL; - boolean invert; - - if (!filename || !width || align < 1 || !height || !pixelFormat || - *pixelFormat < TJPF_UNKNOWN || *pixelFormat >= TJ_NUMPF) - THROWG("tjLoadImage(): Invalid argument"); - if ((align & (align - 1)) != 0) - THROWG("tjLoadImage(): Alignment must be a power of 2"); - - if ((handle = tjInitCompress()) == NULL) return NULL; - this = (tjinstance *)handle; - cinfo = &this->cinfo; + static const char FUNCTION_NAME[] = "tjTransform"; + int i, retval = 0; + size_t *sizes = NULL; -#ifdef _MSC_VER - if (fopen_s(&file, filename, "rb") || file == NULL) -#else - if ((file = fopen(filename, "rb")) == NULL) -#endif - THROW_UNIX("tjLoadImage(): Cannot open input file"); + GET_DINSTANCE(handle); + if ((this->init & DECOMPRESS) == 0) + THROW("Instance has not been initialized for decompression"); - if ((tempc = getc(file)) < 0 || ungetc(tempc, file) == EOF) - THROW_UNIX("tjLoadImage(): Could not read input file") - else if (tempc == EOF) - THROWG("tjLoadImage(): Input file contains no data"); + if (n < 1 || dstSizes == NULL) + THROW("Invalid argument"); if (setjmp(this->jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. */ retval = -1; goto bailout; } - if (*pixelFormat == TJPF_UNKNOWN) cinfo->in_color_space = JCS_UNKNOWN; - else cinfo->in_color_space = pf2cs[*pixelFormat]; - if (tempc == 'B') { - if ((src = jinit_read_bmp(cinfo, FALSE)) == NULL) - THROWG("tjLoadImage(): Could not initialize bitmap loader"); - invert = (flags & TJFLAG_BOTTOMUP) == 0; - } else if (tempc == 'P') { - if ((src = jinit_read_ppm(cinfo)) == NULL) - THROWG("tjLoadImage(): Could not initialize bitmap loader"); - invert = (flags & TJFLAG_BOTTOMUP) != 0; - } else - THROWG("tjLoadImage(): Unsupported file type"); + jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize); + jpeg_read_header(dinfo, TRUE); + if (getSubsamp(dinfo) == TJSAMP_UNKNOWN) + THROW("Could not determine subsampling level of JPEG image"); + processFlags(handle, flags, COMPRESS); + + if ((sizes = (size_t *)malloc(n * sizeof(size_t))) == NULL) + THROW("Memory allocation failure"); + for (i = 0; i < n; i++) + sizes[i] = (size_t)dstSizes[i]; + retval = tj3Transform(handle, jpegBuf, (size_t)jpegSize, n, dstBufs, sizes, + t); + for (i = 0; i < n; i++) + dstSizes[i] = (unsigned long)sizes[i]; - src->input_file = file; -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - /* Refuse to load images larger than 1 Megapixel when fuzzing. */ - if (flags & TJFLAG_FUZZING) - src->max_pixels = 1048576; -#endif - (*src->start_input) (cinfo, src); - (*cinfo->mem->realize_virt_arrays) ((j_common_ptr)cinfo); +bailout: + free(sizes); + return retval; +} - *width = cinfo->image_width; *height = cinfo->image_height; - *pixelFormat = cs2pf[cinfo->in_color_space]; - pitch = PAD((*width) * tjPixelSize[*pixelFormat], align); - if ((unsigned long long)pitch * (unsigned long long)(*height) > - (unsigned long long)((size_t)-1) || - (dstBuf = (unsigned char *)malloc(pitch * (*height))) == NULL) - THROWG("tjLoadImage(): Memory allocation failure"); +/*************************** Packed-Pixel Image I/O **************************/ - if (setjmp(this->jerr.setjmp_buffer)) { - /* If we get here, the JPEG code has signaled an error. */ - retval = -1; goto bailout; - } +/* tj3LoadImage*() is implemented in turbojpeg-mp.c */ - while (cinfo->next_scanline < cinfo->image_height) { - int i, nlines = (*src->get_pixel_rows) (cinfo, src); +/* TurboJPEG 2.0+ */ +DLLEXPORT unsigned char *tjLoadImage(const char *filename, int *width, + int align, int *height, + int *pixelFormat, int flags) +{ + tjhandle handle = NULL; + unsigned char *dstBuf = NULL; - for (i = 0; i < nlines; i++) { - unsigned char *dstptr; - int row; + if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL) return NULL; - row = cinfo->next_scanline + i; - if (invert) dstptr = &dstBuf[((*height) - row - 1) * pitch]; - else dstptr = &dstBuf[row * pitch]; - memcpy(dstptr, src->buffer[i], (*width) * tjPixelSize[*pixelFormat]); - } - cinfo->next_scanline += nlines; - } + processFlags(handle, flags, COMPRESS); - (*src->finish_input) (cinfo, src); + dstBuf = tj3LoadImage8(handle, filename, width, align, height, pixelFormat); -bailout: - if (handle) tjDestroy(handle); - if (file) fclose(file); - if (retval < 0) { free(dstBuf); dstBuf = NULL; } + tj3Destroy(handle); return dstBuf; } +/* tj3SaveImage*() is implemented in turbojpeg-mp.c */ + +/* TurboJPEG 2.0+ */ DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer, int width, int pitch, int height, int pixelFormat, int flags) { - int retval = 0; tjhandle handle = NULL; - tjinstance *this; - j_decompress_ptr dinfo = NULL; - djpeg_dest_ptr dst; - FILE *file = NULL; - char *ptr = NULL; - boolean invert; + 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 02b54ca..12efcbc 100644 --- a/turbojpeg.h +++ b/turbojpeg.h @@ -1,5 +1,5 @@ /* - * Copyright (C)2009-2015, 2017, 2020-2021 D. R. Commander. + * Copyright (C)2009-2015, 2017, 2020-2023 D. R. Commander. * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,6 +30,8 @@ #ifndef __TURBOJPEG_H__ #define __TURBOJPEG_H__ +#include + #if defined(_WIN32) && defined(DLLDEFINE) #define DLLEXPORT __declspec(dllexport) #else @@ -54,40 +56,66 @@ * Each plane is simply a 2D array of bytes, each byte representing the value * of one of the components (Y, Cb, or Cr) at a particular location in the * image. The width and height of each plane are determined by the image - * width, height, and level of chrominance subsampling. The luminance plane + * width, height, and level of chrominance subsampling. The luminance plane * width is the image width padded to the nearest multiple of the horizontal - * subsampling factor (2 in the case of 4:2:0 and 4:2:2, 4 in the case of - * 4:1:1, 1 in the case of 4:4:4 or grayscale.) Similarly, the luminance plane - * height is the image height padded to the nearest multiple of the vertical - * subsampling factor (2 in the case of 4:2:0 or 4:4:0, 1 in the case of 4:4:4 - * or grayscale.) This is irrespective of any additional padding that may be - * specified as an argument to the various YUV functions. The chrominance - * plane width is equal to the luminance plane width divided by the horizontal - * subsampling factor, and the chrominance plane height is equal to the - * luminance plane height divided by the vertical subsampling factor. + * subsampling factor (1 in the case of 4:4:4, grayscale, 4:4:0, or 4:4:1; 2 in + * the case of 4:2:2 or 4:2:0; 4 in the case of 4:1:1.) Similarly, the + * luminance plane height is the image height padded to the nearest multiple of + * the vertical subsampling factor (1 in the case of 4:4:4, 4:2:2, grayscale, + * or 4:1:1; 2 in the case of 4:2:0 or 4:4:0; 4 in the case of 4:4:1.) This is + * irrespective of any additional padding that may be specified as an argument + * to the various YUV functions. The chrominance plane width is equal to the + * luminance plane width divided by the horizontal subsampling factor, and the + * chrominance plane height is equal to the luminance plane height divided by + * the vertical subsampling factor. * * For example, if the source image is 35 x 35 pixels and 4:2:2 subsampling is * used, then the luminance plane would be 36 x 35 bytes, and each of the - * chrominance planes would be 18 x 35 bytes. If you specify a line padding of - * 4 bytes on top of this, then the luminance plane would be 36 x 35 bytes, and - * each of the chrominance planes would be 20 x 35 bytes. + * chrominance planes would be 18 x 35 bytes. If you specify a row alignment + * of 4 bytes on top of this, then the luminance plane would be 36 x 35 bytes, + * and each of the chrominance planes would be 20 x 35 bytes. * * @{ */ /** + * 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 */ -#define TJ_NUMSAMP 6 +#define TJ_NUMSAMP 7 /** * Chrominance subsampling options. * When pixels are converted from RGB to YCbCr (see #TJCS_YCbCr) or from CMYK * to YCCK (see #TJCS_YCCK) as part of the JPEG compression process, some of * the Cb and Cr (chrominance) components can be discarded or averaged together - * to produce a smaller image with little perceptible loss of image clarity - * (the human eye is more sensitive to small changes in brightness than to + * to produce a smaller image with little perceptible loss of image clarity. + * (The human eye is more sensitive to small changes in brightness than to * small changes in color.) This is called "chrominance subsampling". */ enum TJSAMP { @@ -96,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. @@ -129,7 +157,28 @@ enum TJSAMP { * * @note 4:1:1 subsampling is not fully accelerated in libjpeg-turbo. */ - TJSAMP_411 + TJSAMP_411, + /** + * 4:4:1 chrominance subsampling. The JPEG or YUV image will contain one + * chrominance component for every 1x4 block of pixels in the source image. + * JPEG images compressed with 4:4:1 subsampling will be almost exactly the + * same size as those compressed with 4:2:0 subsampling, and in the + * aggregate, both subsampling methods produce approximately the same + * perceptual quality. However, 4:4:1 is better able to reproduce sharp + * vertical features. + * + * @note 4:4:1 subsampling is not fully accelerated in libjpeg-turbo. + */ + TJSAMP_441, + /** + * Unknown subsampling. The JPEG image uses an unusual type of chrominance + * subsampling. Such images can be decompressed into packed-pixel images, + * 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 }; /** @@ -140,8 +189,9 @@ enum TJSAMP { * - 8x16 for 4:4:0 * - 16x16 for 4:2:0 * - 32x8 for 4:1:1 + * - 8x32 for 4:4:1 */ -static const int tjMCUWidth[TJ_NUMSAMP] = { 8, 16, 16, 8, 8, 32 }; +static const int tjMCUWidth[TJ_NUMSAMP] = { 8, 16, 16, 8, 8, 32, 8 }; /** * MCU block height (in pixels) for a given level of chrominance subsampling. @@ -151,8 +201,9 @@ static const int tjMCUWidth[TJ_NUMSAMP] = { 8, 16, 16, 8, 8, 32 }; * - 8x16 for 4:4:0 * - 16x16 for 4:2:0 * - 32x8 for 4:1:1 + * - 8x32 for 4:4:1 */ -static const int tjMCUHeight[TJ_NUMSAMP] = { 8, 8, 16, 8, 16, 8 }; +static const int tjMCUHeight[TJ_NUMSAMP] = { 8, 8, 16, 8, 16, 8, 32 }; /** @@ -166,71 +217,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, /** @@ -245,60 +297,63 @@ enum TJPF { * vice versa, but the mapping is typically not 1:1 or reversible, nor can it * be defined with a simple formula. Thus, such a conversion is out of scope * for a codec library. However, the TurboJPEG API allows for compressing - * CMYK pixels into a YCCK JPEG image (see #TJCS_YCCK) and decompressing YCCK - * JPEG images into CMYK pixels. + * packed-pixel CMYK images into YCCK JPEG images (see #TJCS_YCCK) and + * decompressing YCCK JPEG images into packed-pixel CMYK images. */ TJPF_CMYK, /** - * 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 TJ_BGRX is stored in char pixel[], - * then the red component will be pixel[tjRedOffset[TJ_BGRX]]. This - * will be -1 if the pixel format does not have a red component. + * 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 TJ_BGRX is stored in - * char pixel[], then the green component will be - * pixel[tjGreenOffset[TJ_BGRX]]. This will be -1 if the pixel format - * does not have a green component. + * 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. */ static const int tjGreenOffset[TJ_NUMPF] = { 1, 1, 1, 1, 2, 2, -1, 1, 1, 2, 2, -1 }; /** - * Blue offset (in bytes) for a given pixel format. This specifies the number - * of bytes that the Blue component is offset from the start of the pixel. For - * instance, if a pixel of format TJ_BGRX is stored in char pixel[], - * then the blue component will be pixel[tjBlueOffset[TJ_BGRX]]. This - * will be -1 if the pixel format does not have a blue component. + * 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. */ static const int tjBlueOffset[TJ_NUMPF] = { 2, 0, 2, 0, 1, 3, -1, 2, 0, 1, 3, -1 }; /** - * Alpha offset (in bytes) for a given pixel format. This specifies the number - * of bytes that the Alpha component is offset from the start of the pixel. - * For instance, if a pixel of format TJ_BGRA is stored in - * char pixel[], then the alpha component will be - * pixel[tjAlphaOffset[TJ_BGRA]]. This will be -1 if the pixel format - * does not have an alpha component. + * 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. */ 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 @@ -318,10 +373,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 any of the extended RGB pixel formats or grayscale, but - * they cannot be decompressed to 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 @@ -332,25 +388,28 @@ enum TJCS { * original image. Originally, the analog equivalent of this transformation * allowed the same signal to drive both black & white and color televisions, * but JPEG images use YCbCr primarily because it allows the color data to be - * optionally subsampled for the purposes of reducing bandwidth or disk - * space. YCbCr is the most common JPEG colorspace, and YCbCr JPEG images - * can be compressed from and decompressed to any of the extended RGB pixel - * formats or grayscale, or they can be decompressed to YUV planar images. + * optionally subsampled for the purposes of reducing network or disk usage. + * YCbCr is the most common JPEG colorspace, and YCbCr JPEG images can be + * compressed from and decompressed to packed-pixel images with any of the + * extended RGB or grayscale pixel formats. YCbCr JPEG images can also be + * compressed from and decompressed to planar YUV images. */ TJCS_YCbCr, /** * Grayscale colorspace. The JPEG image retains only the luminance data (Y * component), and any color data from the source image is discarded. - * Grayscale JPEG images can be compressed from and decompressed to any of - * the extended RGB pixel formats or grayscale, or they can be decompressed - * to YUV planar images. + * Grayscale JPEG images can be compressed from and decompressed to + * packed-pixel images with any of the extended RGB or grayscale pixel + * formats, or they can be compressed from and decompressed to planar YUV + * images. */ TJCS_GRAY, /** * CMYK colorspace. When compressing the JPEG image, the C, M, Y, and K * components in the source image are reordered into image planes, but no * colorspace conversion or subsampling is performed. CMYK JPEG images can - * only be decompressed to CMYK pixels. + * only be compressed from and decompressed to packed-pixel images with the + * CMYK pixel format. */ TJCS_CMYK, /** @@ -360,75 +419,347 @@ enum TJCS { * reversibly transformed into YCCK, and as with YCbCr, the chrominance * components in the YCCK pixels can be subsampled without incurring major * perceptual loss. YCCK JPEG images can only be compressed from and - * decompressed to CMYK pixels. + * decompressed to packed-pixel images with the CMYK pixel format. */ TJCS_YCCK }; /** - * The uncompressed source/destination image is stored in bottom-up (Windows, - * OpenGL) order, not top-down (X11) order. - */ -#define TJFLAG_BOTTOMUP 2 -/** - * When decompressing an image that was compressed using chrominance - * subsampling, use the fastest chrominance upsampling algorithm available in - * the underlying codec. The default is to use smooth upsampling, which - * creates a smooth transition between neighboring chrominance components in - * order to reduce upsampling artifacts in the decompressed image. - */ -#define TJFLAG_FASTUPSAMPLE 256 -/** - * Disable buffer (re)allocation. If passed to one of the JPEG compression or - * transform functions, this flag will cause those functions to generate an - * error if the JPEG image buffer is invalid or too small rather than - * attempting to allocate or reallocate that buffer. This reproduces the - * behavior of earlier versions of TurboJPEG. - */ -#define TJFLAG_NOREALLOC 1024 -/** - * Use the fastest DCT/IDCT algorithm available in the underlying codec. The - * default if this flag is not specified is implementation-specific. For - * example, the implementation of TurboJPEG for libjpeg[-turbo] uses the fast - * algorithm by default when compressing, because this has been shown to have - * only a very slight effect on accuracy, but it uses the accurate algorithm - * when decompressing, because this has been shown to have a larger effect. + * The number of parameters */ -#define TJFLAG_FASTDCT 2048 -/** - * Use the most accurate DCT/IDCT algorithm available in the underlying codec. - * The default if this flag is not specified is implementation-specific. For - * example, the implementation of TurboJPEG for libjpeg[-turbo] uses the fast - * algorithm by default when compressing, because this has been shown to have - * only a very slight effect on accuracy, but it uses the accurate algorithm - * when decompressing, because this has been shown to have a larger effect. - */ -#define TJFLAG_ACCURATEDCT 4096 -/** - * Immediately discontinue the current compression/decompression/transform - * operation if the underlying codec throws a warning (non-fatal error). The - * default behavior is to allow the operation to complete unless a fatal error - * is encountered. - */ -#define TJFLAG_STOPONWARNING 8192 -/** - * 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. - */ -#define TJFLAG_PROGRESSIVE 16384 +#define TJ_NUMPARAM + /** - * 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. + * Parameters */ -#define TJFLAG_LIMITSCANS 32768 +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 unless #TJPARAM_ARITHMETIC + * is set. + */ + 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. Can be combined with #TJPARAM_ARITHMETIC. + * Implies #TJPARAM_OPTIMIZE unless #TJPARAM_ARITHMETIC is also set. + */ + 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. + */ + 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 +}; /** @@ -441,10 +772,10 @@ enum TJCS { */ enum TJERR { /** - * The error was non-fatal and recoverable, but the image may still be - * corrupt. + * The error was non-fatal and recoverable, but the destination image may + * still be corrupt. */ - TJERR_WARNING = 0, + TJERR_WARNING, /** * The error was fatal and non-recoverable. */ @@ -458,13 +789,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.) @@ -507,54 +838,69 @@ enum TJXOP { /** - * This option will cause #tjTransform() to return an error if the transform is - * not perfect. Lossless transforms operate on MCU blocks, whose size depends - * on the level of chrominance subsampling used (see #tjMCUWidth - * and #tjMCUHeight.) If the image's width or height is not evenly divisible - * by the MCU block size, then there will be partial MCU blocks on the right + * 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. */ -#define TJXOPT_PERFECT 1 +#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 2 +#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 4 +#define TJXOPT_CROP (1 << 2) /** - * This option will discard the color data in the input image and produce - * a grayscale output image. + * This option will discard the color data in the source image and produce a + * grayscale destination image. */ -#define TJXOPT_GRAY 8 +#define TJXOPT_GRAY (1 << 3) /** - * This option will prevent #tjTransform() from outputting a JPEG image for - * this particular transform (this can be used in conjunction with a custom + * 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.) */ -#define TJXOPT_NOOUTPUT 16 +#define TJXOPT_NOOUTPUT (1 << 4) /** - * This option will enable progressive entropy coding in the output image + * This option will enable progressive entropy coding in the JPEG image * generated by this particular transform. Progressive entropy coding will * generally improve compression relative to baseline entropy coding (the - * default), but it will reduce compression and decompression performance - * considerably. + * default), but it will reduce decompression performance considerably. + * Can be combined with #TJXOPT_ARITHMETIC. Implies #TJXOPT_OPTIMIZE unless + * #TJXOPT_ARITHMETIC is also specified. */ -#define TJXOPT_PROGRESSIVE 32 +#define TJXOPT_PROGRESSIVE (1 << 5) /** - * This option will prevent #tjTransform() from copying any extra markers - * (including EXIF and ICC profile data) from the source image to the output - * image. + * This option will prevent #tj3Transform() from copying any extra markers + * (including EXIF and ICC profile data) from the source image to the + * destination image. */ -#define TJXOPT_COPYNONE 64 +#define TJXOPT_COPYNONE (1 << 6) +/** + * 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. + */ +#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) /** @@ -581,23 +927,28 @@ 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; /** - * The width of the cropping region. Setting this to 0 is the equivalent of + * The width of the cropping region. Setting this to 0 is the equivalent of * setting it to the width of the source JPEG image - x. */ int w; /** - * The height of the cropping region. Setting this to 0 is the equivalent of + * The height of the cropping region. Setting this to 0 is the equivalent of * setting it to the height of the source JPEG image - y. */ int h; } tjregion; /** + * A #tjregion structure that specifies no cropping + */ +static const tjregion TJUNCROPPED = { 0, 0, 0, 0 }; + +/** * Lossless transform */ typedef struct tjtransform { @@ -610,7 +961,8 @@ typedef struct tjtransform { */ int op; /** - * The bitwise OR of one of more of the @ref TJXOPT_CROP "transform options" + * The bitwise OR of one of more of the @ref TJXOPT_ARITHMETIC + * "transform options" */ int options; /** @@ -619,10 +971,10 @@ typedef struct tjtransform { */ void *data; /** - * A callback function that can be used to modify the DCT coefficients - * after they are losslessly transformed but before they are transcoded to a - * new JPEG image. This allows for custom filters or other transformations - * to be applied in the frequency domain. + * A callback function that can be used to modify the DCT coefficients after + * they are losslessly transformed but before they are transcoded to a new + * JPEG image. This allows for custom filters or other transformations to be + * applied in the frequency domain. * * @param coeffs pointer to an array of transformed DCT coefficients. (NOTE: * this pointer is not guaranteed to be valid once the callback returns, so @@ -630,21 +982,21 @@ typedef struct tjtransform { * or library should make a copy of them within the body of the callback.) * * @param arrayRegion #tjregion structure containing the width and height of - * the array pointed to by coeffs as well as its offset relative to - * the component plane. TurboJPEG implementations may choose to split each + * the array pointed to by `coeffs` as well as its offset relative to the + * component plane. TurboJPEG implementations may choose to split each * component plane into multiple DCT coefficient arrays and call the callback * function once for each array. * * @param planeRegion #tjregion structure containing the width and height of - * the component plane to which coeffs belongs + * the component plane to which `coeffs` belongs * - * @param componentID ID number of the component plane to which - * coeffs belongs (Y, Cb, and Cr have, respectively, ID's of 0, 1, - * and 2 in typical JPEG images.) + * @param componentID ID number of the component plane to which `coeffs` + * belongs. (Y, Cb, and Cr have, respectively, ID's of 0, 1, and 2 in + * typical JPEG images.) * - * @param transformID ID number of the transformed image to which - * coeffs belongs. This is the same as the index of the transform - * in the transforms array that was passed to #tjTransform(). + * @param transformID ID number of the transformed image to which `coeffs` + * belongs. This is the same as the index of the transform in the + * `transforms` array that was passed to #tj3Transform(). * * @param transform a pointer to a #tjtransform structure that specifies the * parameters and/or cropping region for this transform @@ -652,8 +1004,8 @@ typedef struct tjtransform { * @return 0 if the callback was successful, or -1 if an error occurred. */ int (*customFilter) (short *coeffs, tjregion arrayRegion, - tjregion planeRegion, int componentIndex, - int transformIndex, struct tjtransform *transform); + tjregion planeRegion, int componentID, int transformID, + struct tjtransform *transform); } tjtransform; /** @@ -663,19 +1015,20 @@ typedef void *tjhandle; /** - * Pad the given width to the nearest 32-bit boundary - */ -#define TJPAD(width) (((width) + 3) & (~3)) - -/** - * Compute the scaled value of dimension using the given scaling - * factor. This macro performs the integer equivalent of ceil(dimension * - * scalingFactor). + * Compute the scaled value of `dimension` using the given scaling factor. + * This macro performs the integer equivalent of `ceil(dimension * + * scalingFactor)`. */ #define TJSCALED(dimension, scalingFactor) \ (((dimension) * scalingFactor.num + scalingFactor.denom - 1) / \ 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" { @@ -683,231 +1036,253 @@ 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().) + * @return a handle to the newly-created instance, or NULL if an error occurred + * (see #tj3GetErrorStr().) */ -DLLEXPORT tjhandle tjInitCompress(void); +DLLEXPORT tjhandle tj3Init(int initType); /** - * Compress an 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 srcBuf pointer to an image buffer containing RGB, grayscale, or - * CMYK pixels to be compressed + * @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. 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 line in the source image. Normally, this should be - * width * #tjPixelSize[pixelFormat] if the image is unpadded, or - * #TJPAD(width * #tjPixelSize[pixelFormat]) if each line of the image - * is padded to the nearest 32-bit boundary, as is the case for Windows - * bitmaps. You can also be clever and use this parameter to skip lines, etc. - * Setting this parameter to 0 is the equivalent of setting it to - * width * #tjPixelSize[pixelFormat]. + * @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 * * @param pixelFormat pixel format of the source image (see @ref TJPF * "Pixel formats".) * - * @param jpegBuf address of a pointer to an image buffer that will receive the - * JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer - * to accommodate the size of the JPEG image. Thus, you can choose to: - * -# pre-allocate the JPEG buffer with an arbitrary size using #tjAlloc() and + * @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 #tj3Alloc() and * let TurboJPEG grow the buffer as needed, - * -# set *jpegBuf to NULL to tell TurboJPEG to allocate the buffer - * for you, or + * -# set `*jpegBuf` to NULL to tell TurboJPEG to allocate the buffer for you, + * or * -# pre-allocate the buffer to a "worst case" size determined by calling - * #tjBufSize(). This should ensure that the buffer never has to be - * re-allocated (setting #TJFLAG_NOREALLOC guarantees that it won't be.) + * #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, *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. - * - * @param jpegSize pointer to an unsigned long variable that holds the size of - * the JPEG image buffer. If *jpegBuf points to a pre-allocated - * buffer, then *jpegSize should be set to the size of the buffer. - * Upon return, *jpegSize will contain the size of the JPEG image (in - * bytes.) If *jpegBuf points to a JPEG image buffer that is being - * reused from a previous call to one of the JPEG compression functions, then - * *jpegSize is ignored. - * - * @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) + * 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. + * + * @param jpegSize pointer to a size_t variable that holds the size of the JPEG + * buffer. If `*jpegBuf` points to a pre-allocated buffer, then `*jpegSize` + * should be set to the size of the buffer. Upon return, `*jpegSize` will + * contain the size of the JPEG image (in bytes.) If `*jpegBuf` points to a + * JPEG buffer that is being reused from a previous call to one of the JPEG + * compression functions, then `*jpegSize` is ignored. + * + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) + */ +DLLEXPORT int tj3Compress8(tjhandle handle, const unsigned char *srcBuf, + int width, int pitch, int height, int pixelFormat, + unsigned char **jpegBuf, size_t *jpegSize); + +/** + * Compress a 12-bit-per-sample packed-pixel RGB, grayscale, or CMYK image into + * a 12-bit-per-sample JPEG image. * - * @param flags the bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT - * "flags" + * \details \copydetails tj3Compress8() + */ +DLLEXPORT int tj3Compress12(tjhandle handle, const short *srcBuf, int width, + int pitch, int height, int pixelFormat, + unsigned char **jpegBuf, size_t *jpegSize); + +/** + * Compress a 16-bit-per-sample packed-pixel RGB, grayscale, or CMYK image into + * a 16-bit-per-sample lossless JPEG image. * - * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() - * and #tjGetErrorCode().) -*/ -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); + * \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 YUV planar 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 an image buffer containing a YUV planar image to be - * compressed. The size of this buffer should match the value returned by - * #tjBufSizeYUV2() for the given image width, height, padding, and level of - * chrominance subsampling. The Y, U (Cb), and V (Cr) image planes should be - * stored sequentially in the source buffer (refer to @ref YUVnotes - * "YUV Image Format Notes".) + * @param srcBuf pointer to a buffer containing a unified planar YUV source + * image to be compressed. The size of this buffer should match the value + * returned by #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 - * buffer copy will be performed within TurboJPEG. + * buffer copy will be performed. * - * @param pad the line padding used in the source image. For instance, if each - * line in each plane of the YUV image is padded to the nearest multiple of 4 - * bytes, then pad should be set to 4. + * @param align row alignment (in bytes) of the source image (must be a power + * of 2.) Setting this parameter to n indicates that each row in each plane of + * the source image is padded to the nearest multiple of n bytes + * (1 = unpadded.) * * @param height height (in pixels) of the source image. If the height is not * an even multiple of the MCU block height (see #tjMCUHeight), then an - * intermediate buffer copy will be performed within TurboJPEG. + * intermediate buffer copy will be performed. * - * @param subsamp the level of chrominance subsampling used in the source - * image (see @ref TJSAMP "Chrominance subsampling options".) - * - * @param jpegBuf address of a pointer to an image buffer that will receive the + * @param jpegBuf address of a pointer to a byte buffer that will receive the * JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to * accommodate the size of the JPEG image. Thus, you can choose to: - * -# pre-allocate the JPEG buffer with an arbitrary size using #tjAlloc() and + * -# 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 + * -# 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, *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. - * - * @param jpegSize pointer to an unsigned long variable that holds the size of - * the JPEG image buffer. If *jpegBuf points to a pre-allocated - * buffer, then *jpegSize should be set to the size of the buffer. - * Upon return, *jpegSize will contain the size of the JPEG image (in - * bytes.) If *jpegBuf points to a JPEG image buffer that is being - * reused from a previous call to one of the JPEG compression functions, then - * *jpegSize is ignored. - * - * @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().) -*/ -DLLEXPORT int tjCompressFromYUV(tjhandle handle, const unsigned char *srcBuf, - int width, int pad, int height, int subsamp, - unsigned char **jpegBuf, - unsigned long *jpegSize, int jpegQual, - int flags); + * 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. + * + * @param jpegSize pointer to a size_t variable that holds the size of the JPEG + * buffer. If `*jpegBuf` points to a pre-allocated buffer, then `*jpegSize` + * should be set to the size of the buffer. Upon return, `*jpegSize` will + * contain the size of the JPEG image (in bytes.) If `*jpegBuf` points to a + * JPEG buffer that is being reused from a previous call to one of the JPEG + * compression functions, then `*jpegSize` is ignored. + * + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) + */ +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 - * image to be compressed. These planes can be contiguous or non-contiguous in - * memory. The size of each plane should match the value returned by - * #tjPlaneSizeYUV() for the given image width, height, strides, and level of - * chrominance subsampling. Refer to @ref YUVnotes "YUV Image Format Notes" - * for more details. + * source image to be compressed. These planes can be contiguous or + * non-contiguous in memory. The size of each plane should match the value + * returned by #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 - * buffer copy will be performed within TurboJPEG. + * buffer copy will be performed. * * @param strides an array of integers, each specifying the number of bytes per - * line in the corresponding plane of the YUV source image. Setting the stride + * row in the corresponding plane of the YUV source image. Setting the stride * for any plane to 0 is the same as setting it to the plane width (see - * @ref YUVnotes "YUV Image Format Notes".) If strides is NULL, then - * the strides for all planes will be set to their respective plane widths. - * You can adjust the strides in order to specify an arbitrary amount of line + * @ref YUVnotes "YUV Image Format Notes".) If `strides` is NULL, then the + * strides for all planes will be set to their respective plane widths. You + * can adjust the strides in order to specify an arbitrary amount of row * padding in each plane or to create a JPEG image from a subregion of a larger - * YUV planar image. + * planar YUV image. * * @param height height (in pixels) of the source image. If the height is not * an even multiple of the MCU block height (see #tjMCUHeight), then an - * intermediate buffer copy will be performed within TurboJPEG. + * intermediate buffer copy will be performed. * - * @param subsamp the level of chrominance subsampling used in the source - * image (see @ref TJSAMP "Chrominance subsampling options".) - * - * @param jpegBuf address of a pointer to an image buffer that will receive the + * @param jpegBuf address of a pointer to a byte buffer that will receive the * JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to * accommodate the size of the JPEG image. Thus, you can choose to: - * -# pre-allocate the JPEG buffer with an arbitrary size using #tjAlloc() and + * -# 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 + * -# 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, *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. - * - * @param jpegSize pointer to an unsigned long variable that holds the size of - * the JPEG image buffer. If *jpegBuf points to a pre-allocated - * buffer, then *jpegSize should be set to the size of the buffer. - * Upon return, *jpegSize will contain the size of the JPEG image (in - * bytes.) If *jpegBuf points to a JPEG image buffer that is being - * reused from a previous call to one of the JPEG compression functions, then - * *jpegSize is ignored. - * - * @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().) -*/ -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); + * 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. + * + * @param jpegSize pointer to a size_t variable that holds the size of the JPEG + * buffer. If `*jpegBuf` points to a pre-allocated buffer, then `*jpegSize` + * should be set to the size of the buffer. Upon return, `*jpegSize` will + * contain the size of the JPEG image (in bytes.) If `*jpegBuf` points to a + * JPEG buffer that is being reused from a previous call to one of the JPEG + * compression functions, then `*jpegSize` is ignored. + * + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) + */ +DLLEXPORT int tj3CompressFromYUVPlanes8(tjhandle handle, + const unsigned char * const *srcPlanes, + int width, const int *strides, + int height, unsigned char **jpegBuf, + size_t *jpegSize); /** * The maximum size of the buffer (in bytes) required to hold a JPEG image with * the given parameters. The number of bytes returned by this function is * larger than the size of the uncompressed source image. The reason for this - * is that the JPEG format uses 16-bit coefficients, and it is thus possible - * for a very high-quality JPEG image with very high-frequency content to - * expand rather than compress when converted to the JPEG format. Such images - * represent a very rare corner case, but since there is no way to predict the - * size of a JPEG image prior to compression, the corner case has to be + * is that the JPEG format uses 16-bit coefficients, so it is possible for a + * very high-quality source image with very high-frequency content to expand + * rather than compress when converted to the JPEG format. Such images + * represent very rare corner cases, but since there is no way to predict the + * size of a JPEG image prior to compression, the corner cases have to be * handled. * * @param width width (in pixels) of the image @@ -916,33 +1291,37 @@ 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); /** - * The size of the buffer (in bytes) required to hold a YUV planar image with - * the given parameters. + * The size of the buffer (in bytes) required to hold a unified planar YUV + * image with the given parameters. * * @param width width (in pixels) of the image * - * @param pad the width of each line in each plane of the image is padded to - * the nearest multiple of this number of bytes (must be a power of 2.) + * @param align row alignment (in bytes) of the image (must be a power of 2.) + * Setting this parameter to n specifies that each row in each plane of the + * image will be padded to the nearest multiple of n bytes (1 = unpadded.) * * @param height height (in pixels) of the image * * @param subsamp level of chrominance subsampling in the image (see * @ref TJSAMP "Chrominance subsampling options".) * - * @return the size of the buffer (in bytes) required to hold the image, or - * -1 if the arguments are out of bounds. + * @return the size of the buffer (in bytes) required to hold the image, or 0 + * if the arguments are out of bounds. */ -DLLEXPORT unsigned long tjBufSizeYUV2(int width, int pad, int height, - int subsamp); +DLLEXPORT size_t tj3YUVBufSize(int width, int align, int height, int subsamp); /** @@ -954,7 +1333,7 @@ DLLEXPORT unsigned long tjBufSizeYUV2(int width, int pad, int height, * @param width width (in pixels) of the YUV image. NOTE: this is the width of * the whole image, not the plane width. * - * @param stride bytes per line in the image plane. Setting this to 0 is the + * @param stride bytes per row in the image plane. Setting this to 0 is the * equivalent of setting it to the plane width. * * @param height height (in pixels) of the YUV image. NOTE: this is the height @@ -964,10 +1343,10 @@ DLLEXPORT unsigned long tjBufSizeYUV2(int width, int pad, 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); /** @@ -981,10 +1360,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); /** @@ -999,86 +1378,82 @@ 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 an RGB or grayscale image into a YUV planar image. This function - * uses the accelerated color conversion routines in the underlying - * codec but does not execute any of the other steps in the JPEG compression - * process. + * Encode 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 an image buffer containing RGB or grayscale pixels - * to be encoded + * @param srcBuf pointer to a buffer containing a packed-pixel RGB or grayscale + * source image to be encoded. 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 line in the source image. Normally, this should be - * width * #tjPixelSize[pixelFormat] if the image is unpadded, or - * #TJPAD(width * #tjPixelSize[pixelFormat]) if each line of the image - * is padded to the nearest 32-bit boundary, as is the case for Windows - * bitmaps. You can also be clever and use this parameter to skip lines, etc. - * Setting this parameter to 0 is the equivalent of setting it to - * width * #tjPixelSize[pixelFormat]. + * @param pitch bytes 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. * * @param height height (in pixels) of the source image * * @param pixelFormat pixel format of the source image (see @ref TJPF * "Pixel formats".) * - * @param dstBuf pointer to an image buffer that will receive the YUV image. - * Use #tjBufSizeYUV2() to determine the appropriate size for this buffer based - * on the image width, height, padding, and level of chrominance subsampling. - * The Y, U (Cb), and V (Cr) image planes will be stored sequentially in the - * buffer (refer to @ref YUVnotes "YUV Image Format Notes".) - * - * @param pad the width of each line in each plane of the YUV image will be - * padded to the nearest multiple of this number of bytes (must be a power of - * 2.) To generate images suitable for X Video, pad should be set to - * 4. - * - * @param subsamp the level of chrominance subsampling to be used when - * generating the YUV image (see @ref TJSAMP - * "Chrominance subsampling options".) To generate images suitable for X - * Video, subsamp should be set to @ref TJSAMP_420. This produces an - * image compatible with the I420 (AKA "YUV420P") format. - * - * @param flags the bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT - * "flags" - * - * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() - * and #tjGetErrorCode().) -*/ -DLLEXPORT int tjEncodeYUV3(tjhandle handle, const unsigned char *srcBuf, - int width, int pitch, int height, int pixelFormat, - unsigned char *dstBuf, int pad, int subsamp, - int flags); + * @param dstBuf pointer 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 + * @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. + * + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) + */ +DLLEXPORT int tj3EncodeYUV8(tjhandle handle, const unsigned char *srcBuf, + int width, int pitch, int height, int pixelFormat, + unsigned char *dstBuf, int align); /** - * Encode an RGB or grayscale image into separate Y, U (Cb), and V (Cr) image - * planes. This function uses the accelerated color conversion routines in the - * underlying codec but does not execute any of the other steps in the JPEG - * compression process. + * Encode 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. 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 srcBuf pointer to an image buffer containing RGB or grayscale pixels - * to be encoded * * @param width width (in pixels) of the source image * - * @param pitch bytes per line in the source image. Normally, this should be - * width * #tjPixelSize[pixelFormat] if the image is unpadded, or - * #TJPAD(width * #tjPixelSize[pixelFormat]) if each line of the image - * is padded to the nearest 32-bit boundary, as is the case for Windows - * bitmaps. You can also be clever and use this parameter to skip lines, etc. - * Setting this parameter to 0 is the equivalent of setting it to - * width * #tjPixelSize[pixelFormat]. + * @param pitch bytes 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. * * @param height height (in pixels) of the source image * @@ -1088,53 +1463,39 @@ 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 - * line in the corresponding plane of the output image. Setting the stride for - * any plane to 0 is the same as setting it to the plane width (see - * @ref YUVnotes "YUV Image Format Notes".) If strides is NULL, then - * the strides for all planes will be set to their respective plane widths. - * You can adjust the strides in order to add an arbitrary amount of line - * padding to each plane or to encode an RGB or grayscale image into a - * subregion of a larger YUV planar image. - * - * @param subsamp the level of chrominance subsampling to be used when - * generating the YUV image (see @ref TJSAMP - * "Chrominance subsampling options".) To generate images suitable for X - * Video, subsamp should be set to @ref TJSAMP_420. This produces an - * image compatible with the I420 (AKA "YUV420P") format. - * - * @param flags the bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT - * "flags" - * - * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() - * and #tjGetErrorCode().) -*/ -DLLEXPORT int tjEncodeYUVPlanes(tjhandle handle, const unsigned char *srcBuf, - int width, int pitch, int height, - int pixelFormat, unsigned char **dstPlanes, - int *strides, int subsamp, int flags); - - -/** - * Create a TurboJPEG decompressor instance. + * row in the corresponding plane of the YUV image. Setting the stride for any + * plane to 0 is the same as setting it to the plane width (see @ref YUVnotes + * "YUV Image Format Notes".) If `strides` is NULL, then the strides for all + * planes will be set to their respective plane widths. You can adjust the + * strides in order to add an arbitrary amount of row padding to each plane or + * to encode an RGB or grayscale image into a subregion of a larger planar YUV + * image. * - * @return a handle to the newly-created instance, or NULL if an error - * occurred (see #tjGetErrorStr2().) -*/ -DLLEXPORT tjhandle tjInitDecompress(void); + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) + */ +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 buffer containing a JPEG image or an + * @param jpegBuf pointer to a byte buffer containing a JPEG image or an * "abbreviated table specification" (AKA "tables-only") datastream. Passing a * tables-only datastream to this function primes the decompressor with * quantization and Huffman tables that can be used when decompressing @@ -1144,334 +1505,328 @@ 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. - * - * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() - * and #tjGetErrorCode().) -*/ -DLLEXPORT int tjDecompressHeader3(tjhandle handle, + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) + */ +DLLEXPORT int tj3DecompressHeader(tjhandle handle, const unsigned char *jpegBuf, - unsigned long jpegSize, int *width, - int *height, int *jpegSubsamp, - int *jpegColorspace); + size_t jpegSize); /** - * Returns a list of fractional scaling factors that the JPEG decompressor in - * this implementation of TurboJPEG supports. + * Returns a list of fractional scaling factors that the JPEG decompressor + * supports. * - * @param numscalingfactors pointer to an integer variable that will receive + * @param numScalingFactors pointer to an integer variable that will receive * the number of elements in the list * * @return a pointer to a list of fractional scaling factors, or NULL if an - * error is encountered (see #tjGetErrorStr2().) -*/ -DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numscalingfactors); + * error is encountered (see #tj3GetErrorStr().) + */ +DLLEXPORT tjscalingfactor *tj3GetScalingFactors(int *numScalingFactors); /** - * Decompress a JPEG image to an 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 jpegBuf pointer to a buffer containing the JPEG image to decompress + * @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. * - * @param jpegSize size of the JPEG image (in bytes) + * @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 dstBuf pointer to an image buffer that will receive the decompressed - * image. This buffer should normally be pitch * scaledHeight bytes - * in size, where scaledHeight can be determined by calling - * #TJSCALED() with the JPEG image height and one of the scaling factors - * returned by #tjGetScalingFactors(). The dstBuf pointer may also be - * used to decompress into a specific region of a larger buffer. - * - * @param width desired width (in pixels) of the destination image. If this is - * different than the width of the JPEG image being decompressed, then - * TurboJPEG will use scaling in the JPEG decompressor to generate the largest - * possible image that will fit within the desired width. If width is - * set to 0, then only the height will be considered when determining the - * scaled image size. - * - * @param pitch bytes per line in the destination image. Normally, this is - * scaledWidth * #tjPixelSize[pixelFormat] if the decompressed image - * is unpadded, else #TJPAD(scaledWidth * #tjPixelSize[pixelFormat]) - * if each line of the decompressed image is padded to the nearest 32-bit - * boundary, as is the case for Windows bitmaps. (NOTE: scaledWidth - * can be determined by calling #TJSCALED() with the JPEG image width and one - * of the scaling factors returned by #tjGetScalingFactors().) You can also be - * clever and use the pitch parameter to skip lines, etc. Setting this - * parameter to 0 is the equivalent of setting it to - * scaledWidth * #tjPixelSize[pixelFormat]. + * @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 + * + * @param jpegSize size of the JPEG image (in bytes) * - * @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. + * @param dstBuf pointer 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. + * + * @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 + * destinationWidth * #tjPixelSize[pixelFormat].) However, you can + * also use this parameter to specify the row alignment/padding of the + * destination image, to skip rows, or to decompress into a specific region of + * a larger buffer. NOTE: If the JPEG image is lossy, then `destinationWidth` + * is either the scaled JPEG width (see #TJSCALED(), #TJPARAM_JPEGWIDTH, and + * #tj3SetScalingFactor()) or the width of the cropping region (see + * #tj3SetCroppingRegion().) If the JPEG image is lossless, then + * `destinationWidth` is the JPEG width. * * @param pixelFormat pixel format of the destination image (see @ref * TJPF "Pixel formats".) * - * @param flags the bitwise OR of one or more of the @ref TJFLAG_ACCURATEDCT - * "flags" + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr() + * and #tj3GetErrorCode().) + */ +DLLEXPORT int tj3Decompress8(tjhandle handle, const unsigned char *jpegBuf, + size_t jpegSize, unsigned char *dstBuf, int pitch, + int pixelFormat); + +/** + * Decompress a 12-bit-per-sample JPEG image into a 12-bit-per-sample + * packed-pixel RGB, grayscale, or CMYK image. * - * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2() - * and #tjGetErrorCode().) + * \details \copydetails tj3Decompress8() */ -DLLEXPORT int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf, - unsigned long jpegSize, unsigned char *dstBuf, - int width, int pitch, int height, int pixelFormat, - int flags); +DLLEXPORT int tj3Decompress12(tjhandle handle, const unsigned char *jpegBuf, + size_t jpegSize, short *dstBuf, int pitch, + int pixelFormat); + +/** + * Decompress a 16-bit-per-sample lossless JPEG image into a 16-bit-per-sample + * packed-pixel RGB, grayscale, or CMYK image. + * + * \details \copydetails tj3Decompress8() + */ +DLLEXPORT int tj3Decompress16(tjhandle handle, const unsigned char *jpegBuf, + size_t jpegSize, unsigned short *dstBuf, + int pitch, int pixelFormat); /** - * Decompress a JPEG image to a YUV planar image. This function performs JPEG - * decompression but leaves out the color conversion step, so a planar YUV - * image is generated instead of an RGB image. + * Decompress 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 buffer containing the JPEG image to decompress + * @param jpegBuf pointer to a byte buffer containing the JPEG image to + * decompress * * @param jpegSize size of the JPEG image (in bytes) * - * @param dstBuf pointer to an image buffer that will receive the YUV image. - * Use #tjBufSizeYUV2() to determine the appropriate size for this buffer based - * on the image width, height, padding, and level of subsampling. The Y, - * U (Cb), and V (Cr) image planes will be stored sequentially in the buffer - * (refer to @ref YUVnotes "YUV Image Format Notes".) - * - * @param width desired width (in pixels) of the YUV image. If this is - * different than the width of the JPEG image being decompressed, then - * TurboJPEG will use scaling in the JPEG decompressor to generate the largest - * possible image that will fit within the desired width. If width is - * set to 0, then only the height will be considered when determining the - * scaled image size. If the scaled width is not an even multiple of the MCU - * block width (see #tjMCUWidth), then an intermediate buffer copy will be - * performed within TurboJPEG. - * - * @param pad the width of each line in each plane of the YUV image will be - * padded to the nearest multiple of this number of bytes (must be a power of - * 2.) To generate images suitable for X Video, pad should be set to - * 4. - * - * @param height desired height (in pixels) of the YUV image. If this is - * different than the height of the JPEG image being decompressed, then - * TurboJPEG will use scaling in the JPEG decompressor to generate the largest - * possible image that will fit within the desired height. If height - * is set to 0, then only the width will be considered when determining the - * scaled image size. If the scaled height is not an even multiple of the MCU - * block height (see #tjMCUHeight), then an intermediate buffer copy will be - * performed within TurboJPEG. - * - * @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().) + * @param dstBuf pointer 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 @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. + * + * @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 pad, 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 an RGB 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 buffer containing the JPEG image to decompress + * @param jpegBuf pointer to a byte buffer containing the JPEG image to + * decompress * * @param jpegSize size of the JPEG image (in bytes) * * @param dstPlanes an array of pointers to Y, U (Cb), and V (Cr) image planes * (or just a Y plane, if decompressing a grayscale image) that will receive - * the YUV image. These planes can be contiguous or non-contiguous in memory. - * Use #tjPlaneSizeYUV() to determine the appropriate size for each plane based - * on the scaled image width, scaled image height, strides, and level of - * chrominance subsampling. Refer to @ref YUVnotes "YUV Image Format Notes" - * for more details. - * - * @param width desired width (in pixels) of the YUV image. If this is - * different than the width of the JPEG image being decompressed, then - * TurboJPEG will use scaling in the JPEG decompressor to generate the largest - * possible image that will fit within the desired width. If width is - * set to 0, then only the height will be considered when determining the - * scaled image size. If the scaled width is not an even multiple of the MCU - * block width (see #tjMCUWidth), then an intermediate buffer copy will be - * performed within TurboJPEG. + * 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 @ref YUVnotes "YUV Image Format Notes" for more details. * * @param strides an array of integers, each specifying the number of bytes per - * line in the corresponding plane of the output image. Setting the stride for - * any plane to 0 is the same as setting it to the scaled plane width (see - * @ref YUVnotes "YUV Image Format Notes".) If strides is NULL, then - * the strides for all planes will be set to their respective scaled plane - * widths. You can adjust the strides in order to add an arbitrary amount of - * line padding to each plane or to decompress the JPEG image into a subregion - * of a larger YUV planar image. - * - * @param height desired height (in pixels) of the YUV image. If this is - * different than the height of the JPEG image being decompressed, then - * TurboJPEG will use scaling in the JPEG decompressor to generate the largest - * possible image that will fit within the desired height. If height - * is set to 0, then only the width will be considered when determining the - * scaled image size. If the scaled height is not an even multiple of the MCU - * block height (see #tjMCUHeight), then an intermediate buffer copy will be - * performed within TurboJPEG. - * - * @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().) + * row in the corresponding plane of the YUV image. Setting the stride for any + * plane to 0 is the same as setting it to the scaled plane width (see + * @ref YUVnotes "YUV Image Format Notes".) If `strides` is NULL, then the + * strides for all planes will be set to their respective scaled plane widths. + * You can adjust the strides in order to add an arbitrary amount of row + * padding to each plane or to decompress the JPEG image into a subregion of a + * larger planar YUV image. + * + * @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 YUV planar image into an RGB or grayscale image. This function - * uses the accelerated color conversion routines in the underlying - * codec but does not execute any of the other steps in the JPEG decompression - * process. - * - * @param handle a handle to a TurboJPEG decompressor or transformer instance + * 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 srcBuf pointer to an image buffer containing a YUV planar image to be - * decoded. The size of this buffer should match the value returned by - * #tjBufSizeYUV2() for the given image width, height, padding, and level of - * chrominance subsampling. The Y, U (Cb), and V (Cr) image planes should be - * stored sequentially in the source buffer (refer to @ref YUVnotes - * "YUV Image Format Notes".) + * @param handle handle to a TurboJPEG instance that has been initialized for + * decompression * - * @param pad Use this parameter to specify that the width of each line in each - * plane of the YUV source image is padded to the nearest multiple of this - * number of bytes (must be a power of 2.) + * @param 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 #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 subsamp the level of chrominance subsampling used in the YUV source - * image (see @ref TJSAMP "Chrominance subsampling options".) + * @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 dstBuf pointer to an image buffer that will receive the decoded - * image. This buffer should normally be pitch * height bytes in - * size, but the dstBuf pointer can also be used to decode into a - * specific region of a larger buffer. + * @param dstBuf pointer to a buffer that will receive the packed-pixel decoded + * image. This buffer should normally be `pitch * height` bytes in size. + * 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 line in the destination image. Normally, this should - * be width * #tjPixelSize[pixelFormat] if the destination image is - * unpadded, or #TJPAD(width * #tjPixelSize[pixelFormat]) if each line - * of the destination image should be padded to the nearest 32-bit boundary, as - * is the case for Windows bitmaps. You can also be clever and use the pitch - * parameter to skip lines, etc. Setting this parameter to 0 is the equivalent - * of setting it to width * #tjPixelSize[pixelFormat]. + * @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. (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 pad, 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 an RGB or grayscale - * image. This function uses the accelerated color conversion routines in the - * underlying codec but does not execute any of the other steps in the JPEG + * 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 - * line in the corresponding plane of the YUV source image. Setting the stride + * row in the corresponding plane of the YUV source image. Setting the stride * for any plane to 0 is the same as setting it to the plane width (see - * @ref YUVnotes "YUV Image Format Notes".) If strides is NULL, then - * the strides for all planes will be set to their respective plane widths. - * You can adjust the strides in order to specify an arbitrary amount of line - * padding in each plane or to decode a subregion of a larger YUV planar image. - * - * @param subsamp the level of chrominance subsampling used in the YUV source - * image (see @ref TJSAMP "Chrominance subsampling options".) + * @ref YUVnotes "YUV Image Format Notes".) If `strides` is NULL, then the + * strides for all planes will be set to their respective plane widths. You + * can adjust the strides in order to specify an arbitrary amount of row + * padding in each plane or to decode a subregion of a larger planar YUV image. * - * @param dstBuf pointer to an image buffer that will receive the decoded - * image. This buffer should normally be pitch * height bytes in - * size, but the dstBuf pointer can also be used to decode into a - * specific region of a larger buffer. + * @param dstBuf pointer to a buffer that will receive the packed-pixel decoded + * image. This buffer should normally be `pitch * height` bytes in size. + * 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 line in the destination image. Normally, this should - * be width * #tjPixelSize[pixelFormat] if the destination image is - * unpadded, or #TJPAD(width * #tjPixelSize[pixelFormat]) if each line - * of the destination image should be padded to the nearest 32-bit boundary, as - * is the case for Windows bitmaps. You can also be clever and use the pitch - * parameter to skip lines, etc. Setting this parameter to 0 is the equivalent - * of setting it to width * #tjPixelSize[pixelFormat]. + * @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. (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); /** @@ -1480,226 +1835,256 @@ 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. + * 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 buffer containing the JPEG source image to + * @param jpegBuf pointer to a byte buffer containing the JPEG source image to * transform * * @param jpegSize size of the JPEG source image (in bytes) * * @param n the number of transformed JPEG images to generate * - * @param dstBufs pointer to an array of n image buffers. dstBufs[i] - * will receive a JPEG image that has been transformed using the parameters in - * transforms[i]. TurboJPEG has the ability to reallocate the JPEG - * buffer to accommodate the size of the JPEG image. Thus, you can choose to: - * -# pre-allocate the JPEG buffer with an arbitrary size using #tjAlloc() and - * let TurboJPEG grow the buffer as needed, - * -# set dstBufs[i] to NULL to tell TurboJPEG to allocate the buffer - * for you, or + * @param dstBufs pointer to an array of n byte buffers. `dstBufs[i]` will + * receive a JPEG image that has been transformed using the parameters in + * `transforms[i]`. TurboJPEG has the ability to reallocate the JPEG + * destination buffer to accommodate the size of the transformed JPEG image. + * Thus, you can choose to: + * -# pre-allocate the JPEG destination buffer with an arbitrary size using + * #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.) Note, - * however, that there are some rare cases (such as transforming images with a - * large amount of embedded EXIF or ICC profile data) in which the output image - * will be larger than the worst-case size, and #TJFLAG_NOREALLOC cannot be - * used in those cases. + * #tj3JPEGBufSize() with the transformed or cropped width and height and the + * level of subsampling used in the source image. Under normal circumstances, + * this should ensure that the buffer never has to be re-allocated. (Setting + * #TJPARAM_NOREALLOC guarantees that it won't be.) Note, however, that there + * are some rare cases (such as transforming images with a large amount of + * embedded EXIF or ICC profile data) in which the transformed JPEG image will + * be larger than the worst-case size, and #TJPARAM_NOREALLOC cannot be used in + * those cases. * . - * If you choose option 1, dstSizes[i] should be set to the size of - * your pre-allocated buffer. In any case, unless you have set - * #TJFLAG_NOREALLOC, you should always check dstBufs[i] upon return - * from this function, as it may have changed. + * If you choose option 1, then `dstSizes[i]` should be set to the size of your + * pre-allocated buffer. In any case, unless you have set #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 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 output image. + * 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 an image buffer for use with TurboJPEG. You should always use - * this function to allocate the JPEG destination buffer(s) for the compression - * and transform functions unless you are disabling automatic buffer - * (re)allocation (by setting #TJFLAG_NOREALLOC.) + * Allocate a byte buffer for use with TurboJPEG. You should always use this + * function to allocate the JPEG destination buffer(s) for the compression and + * transform functions unless you are disabling automatic buffer (re)allocation + * (by setting #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 an uncompressed image from disk into memory. + * Load an 8-bit-per-sample packed-pixel image from disk into memory. * - * @param filename name of a file containing an uncompressed image in Windows - * BMP or PBMPLUS (PPM/PGM) format + * @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. 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 uncompressed image + * pixels) of the packed-pixel image * - * @param align row alignment of the image buffer to be returned (must be a - * power of 2.) For instance, setting this parameter to 4 will cause all rows - * in the image buffer to be padded to the nearest 32-bit boundary, and setting - * this parameter to 1 will cause all rows in the image buffer to be unpadded. + * @param align row alignment (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 uncompressed image + * (in pixels) of the packed-pixel image * * @param pixelFormat pointer to an integer variable that specifies or will - * receive the pixel format of the uncompressed image buffer. The behavior of - * #tjLoadImage() will vary depending on the value of *pixelFormat - * passed to the function: - * - @ref TJPF_UNKNOWN : The uncompressed image buffer returned by the function - * will use the most optimal pixel format for the file type, and - * *pixelFormat will contain the ID of this pixel format upon - * successful return from the function. - * - @ref TJPF_GRAY : Only PGM files and 8-bit BMP files with a grayscale - * colormap can be loaded. + * receive the pixel format of the packed-pixel buffer. The behavior of 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 + * function. + * - @ref TJPF_GRAY : Only PGM files and 8-bit-per-pixel BMP files with a + * grayscale colormap can be loaded. * - @ref TJPF_CMYK : The RGB or grayscale pixels stored in the file will be * converted using a quick & dirty algorithm that is suitable only for testing - * purposes (proper conversion between CMYK and other formats requires a color - * management system.) - * - Other @ref TJPF "pixel formats" : The uncompressed image buffer will use - * the specified pixel format, and pixel format conversion will be performed if + * purposes. (Proper conversion between CMYK and other formats requires a + * color management system.) + * - Other @ref TJPF "pixel formats" : The packed-pixel buffer will use the + * specified pixel format, and pixel format conversion will be performed if * necessary. * - * @param flags the bitwise OR of one or more of the @ref TJFLAG_BOTTOMUP - * "flags". - * - * @return a pointer to a newly-allocated buffer containing the uncompressed + * @return a pointer to a newly-allocated buffer containing the packed-pixel * image, converted to the chosen pixel format and with the chosen row - * alignment, or NULL if an error occurred (see #tjGetErrorStr2().) This - * buffer should be freed using #tjFree(). + * 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 an uncompressed image from memory to disk. + * Save an 8-bit-per-sample packed-pixel image from memory to disk. * - * @param filename name of a file to which to save the uncompressed image. - * The image will be stored in Windows BMP or PBMPLUS (PPM/PGM) format, - * depending on the file extension. + * @param handle handle to a TurboJPEG instance * - * @param buffer pointer to an image buffer containing RGB, grayscale, or - * CMYK pixels to be saved + * @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. Windows BMP files require 8-bit-per-sample data + * precision. * - * @param width width (in pixels) of the uncompressed image + * @param buffer pointer to a buffer containing a packed-pixel RGB, grayscale, + * or CMYK image to be saved * - * @param pitch bytes per line in the image buffer. Setting this parameter to - * 0 is the equivalent of setting it to + * @param width width (in pixels) of the packed-pixel image + * + * @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]. * - * @param height height (in pixels) of the uncompressed image + * @param height height (in pixels) of the packed-pixel image * - * @param pixelFormat pixel format of the image buffer (see @ref TJPF + * @param pixelFormat pixel format of the packed-pixel image (see @ref TJPF * "Pixel formats".) If this parameter is set to @ref TJPF_GRAY, then the - * image will be stored in PGM or 8-bit (indexed color) BMP format. Otherwise, - * the image will be stored in PPM or 24-bit BMP format. If this parameter - * is set to @ref TJPF_CMYK, then the CMYK pixels will be converted to RGB - * using a quick & dirty algorithm that is suitable only for testing (proper - * conversion between CMYK and other formats requires a color management - * system.) + * image will be stored in PGM or 8-bit-per-pixel (indexed color) BMP format. + * Otherwise, the image will be stored in PPM or 24-bit-per-pixel BMP format. + * If this parameter is set to @ref TJPF_CMYK, then the CMYK pixels will be + * converted to RGB using a quick & dirty algorithm that is suitable only for + * testing purposes. (Proper conversion between CMYK and other formats + * requires a color management system.) + * + * @return 0 if successful, or -1 if an error occurred (see #tj3GetErrorStr().) + */ +DLLEXPORT int tj3SaveImage8(tjhandle handle, const char *filename, + const unsigned char *buffer, int width, int pitch, + int height, int pixelFormat); + +/** + * Save a 12-bit-per-sample packed-pixel image from memory to disk. * - * @param flags the bitwise OR of one or more of the @ref TJFLAG_BOTTOMUP - * "flags". + * \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. * - * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2().) + * \details \copydetails tj3SaveImage8() */ -DLLEXPORT int tjSaveImage(const char *filename, unsigned char *buffer, - int width, int pitch, int height, int pixelFormat, - int flags); +DLLEXPORT int tj3SaveImage16(tjhandle handle, const char *filename, + const unsigned short *buffer, int width, + int pitch, int height, int pixelFormat); /** - * Free an image buffer previously allocated by TurboJPEG. You should always - * use this function to free JPEG destination buffer(s) that were automatically + * Free a byte buffer previously allocated by TurboJPEG. You should always use + * this function to free JPEG destination buffer(s) that were automatically * (re)allocated by the compression and transform functions or that were - * manually allocated using #tjAlloc(). + * 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); -/* Deprecated functions and macros */ -#define TJFLAG_FORCEMMX 8 -#define TJFLAG_FORCESSE 16 -#define TJFLAG_FORCESSE2 32 -#define TJFLAG_FORCESSE3 128 +/* Backward compatibility functions and macros (nothing to see here) */ +/* TurboJPEG 1.0+ */ -/* Backward compatibility functions and macros (nothing to see here) */ #define NUMSUBOPT TJ_NUMSAMP #define TJ_444 TJSAMP_444 #define TJ_422 TJSAMP_422 @@ -1715,46 +2100,180 @@ DLLEXPORT int tjGetErrorCode(tjhandle handle); #define TJ_ALPHAFIRST 64 #define TJ_FORCESSE3 TJFLAG_FORCESSE3 #define TJ_FASTUPSAMPLE TJFLAG_FASTUPSAMPLE -#define TJ_YUV 512 -DLLEXPORT unsigned long TJBUFSIZE(int width, int height); - -DLLEXPORT unsigned long TJBUFSIZEYUV(int width, int height, int jpegSubsamp); +#define TJPAD(width) (((width) + 3) & (~3)) -DLLEXPORT unsigned long tjBufSizeYUV(int width, int height, int subsamp); +DLLEXPORT unsigned long TJBUFSIZE(int width, int height); DLLEXPORT int tjCompress(tjhandle handle, unsigned char *srcBuf, int width, int pitch, int height, int pixelSize, unsigned char *dstBuf, unsigned long *compressedSize, int jpegSubsamp, int jpegQual, int flags); +DLLEXPORT int tjDecompress(tjhandle handle, unsigned char *jpegBuf, + unsigned long jpegSize, unsigned char *dstBuf, + int width, int pitch, int height, int pixelSize, + int flags); + +DLLEXPORT int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf, + unsigned long jpegSize, int *width, + int *height); + +DLLEXPORT int tjDestroy(tjhandle handle); + +DLLEXPORT char *tjGetErrorStr(void); + +DLLEXPORT tjhandle tjInitCompress(void); + +DLLEXPORT tjhandle tjInitDecompress(void); + +/* TurboJPEG 1.1+ */ + +#define TJ_YUV 512 + +DLLEXPORT unsigned long TJBUFSIZEYUV(int width, int height, int jpegSubsamp); + +DLLEXPORT int tjDecompressHeader2(tjhandle handle, unsigned char *jpegBuf, + unsigned long jpegSize, int *width, + int *height, int *jpegSubsamp); + +DLLEXPORT int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf, + unsigned long jpegSize, unsigned char *dstBuf, + int flags); + DLLEXPORT int tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, int width, int pitch, int height, int pixelSize, unsigned char *dstBuf, int subsamp, int flags); +/* 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 int tjDecompressHeader(tjhandle handle, unsigned char *jpegBuf, - unsigned long jpegSize, int *width, - int *height); +DLLEXPORT void tjFree(unsigned char *buffer); -DLLEXPORT int tjDecompressHeader2(tjhandle handle, unsigned char *jpegBuf, +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 *height, int *jpegSubsamp, + int *jpegColorspace); -DLLEXPORT int tjDecompress(tjhandle handle, unsigned char *jpegBuf, - unsigned long jpegSize, unsigned char *dstBuf, - int width, int pitch, int height, int pixelSize, +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 tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf, - unsigned long jpegSize, unsigned char *dstBuf, - 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 char *tjGetErrorStr(void); +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 /** * @} diff --git a/usage.txt b/usage.txt index 0b6036a..c1bba4a 100644 --- a/usage.txt +++ b/usage.txt @@ -160,6 +160,39 @@ file size is about the same --- often a little smaller. Switches for advanced users: + -precision N Create JPEG file with N-bit data precision. + N is 8, 12, or 16; default is 8. If N is 16, then + -lossless must also be specified. CAUTION: 12-bit and + 16-bit JPEG is not yet widely implemented, so many + decoders will be unable to view a 12-bit or 16-bit JPEG + file at all. + + -lossless psv[,Pt] Create a lossless JPEG file using the specified + predictor selection value (1 - 7) and optional point + transform (0 - {precision}-1, where {precision} is the + JPEG data precision in bits). A point transform value + of 0 (the default) is necessary in order to create a + fully lossless JPEG file. (A non-zero point transform + value right-shifts the input samples by the specified + number of bits, which is effectively a form of lossy + color quantization.) CAUTION: lossless JPEG is not yet + widely implemented, so many decoders will be unable to + view a lossless JPEG file at all. Note that the + following features will be unavailable when compressing + or decompressing a lossless JPEG file: + * Quality/quantization table selection + * Color space conversion (the JPEG image will use the + same color space as the input image) + * Color quantization + * DCT/IDCT algorithm selection + * Smoothing + * Downsampling/upsampling + * IDCT scaling + * Partial image decompression + * Transformations using jpegtran + Any switches used to enable or configure those features + will be ignored. + -arithmetic Use arithmetic coding. CAUTION: arithmetic coded JPEG is not yet widely implemented, so many decoders will be unable to view an arithmetic coded JPEG file at @@ -204,8 +237,9 @@ Switches for advanced users: same results on all machines. -restart N Emit a JPEG restart marker every N MCU rows, or every - N MCU blocks if "B" is attached to the number. - -restart 0 (the default) means no restart markers. + N MCU blocks (samples in lossless mode) if "B" is + attached to the number. -restart 0 (the default) means + no restart markers. -smooth N Smooth the input image to eliminate dithering noise. N, ranging from 1 to 100, indicates the strength of diff --git a/win/jconfig.h.in b/win/jconfig.h.in deleted file mode 100644 index 0fca77b..0000000 --- a/win/jconfig.h.in +++ /dev/null @@ -1,25 +0,0 @@ -#define JPEG_LIB_VERSION @JPEG_LIB_VERSION@ -#define LIBJPEG_TURBO_VERSION @VERSION@ -#define LIBJPEG_TURBO_VERSION_NUMBER @LIBJPEG_TURBO_VERSION_NUMBER@ - -#cmakedefine C_ARITH_CODING_SUPPORTED -#cmakedefine D_ARITH_CODING_SUPPORTED -#cmakedefine MEM_SRCDST_SUPPORTED -#cmakedefine WITH_SIMD - -#define BITS_IN_JSAMPLE @BITS_IN_JSAMPLE@ /* use 8 or 12 */ - -#undef RIGHT_SHIFT_IS_UNSIGNED - -/* Define "boolean" as unsigned char, not int, per Windows custom */ -#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */ -typedef unsigned char boolean; -#endif -#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */ - -/* Define "INT32" as int, not long, per Windows custom */ -#if !(defined(_BASETSD_H_) || defined(_BASETSD_H)) /* don't conflict if basetsd.h already read */ -typedef short INT16; -typedef signed int INT32; -#endif -#define XMD_H /* prevent jmorecfg.h from redefining it */ diff --git a/win/jpeg.rc.in b/win/jpeg.rc.in index fca72b7..650fbe9 100644 --- a/win/jpeg.rc.in +++ b/win/jpeg.rc.in @@ -24,7 +24,7 @@ BEGIN VALUE "ProductVersion", "@VERSION@" VALUE "ProductName", "@CMAKE_PROJECT_NAME@" VALUE "InternalName", "jpeg@SO_MAJOR_VERSION@" - VALUE "LegalCopyright", "Copyright \xA9 @COPYRIGHT_YEAR@ The libjpeg-turbo Project and many others" + VALUE "LegalCopyright", L"Copyright \xA9 @COPYRIGHT_YEAR@ The libjpeg-turbo Project and many others" VALUE "OriginalFilename", "jpeg@SO_MAJOR_VERSION@.dll" END END diff --git a/win/jpeg62-memsrcdst.def b/win/jpeg62-memsrcdst.def deleted file mode 100644 index 4d24a14..0000000 --- a/win/jpeg62-memsrcdst.def +++ /dev/null @@ -1,108 +0,0 @@ -EXPORTS - jcopy_block_row @ 1 ; - jcopy_sample_rows @ 2 ; - jdiv_round_up @ 3 ; - jinit_1pass_quantizer @ 4 ; - jinit_2pass_quantizer @ 5 ; - jinit_c_coef_controller @ 6 ; - jinit_c_main_controller @ 7 ; - jinit_c_master_control @ 8 ; - jinit_c_prep_controller @ 9 ; - jinit_color_converter @ 10 ; - jinit_color_deconverter @ 11 ; - jinit_compress_master @ 12 ; - jinit_d_coef_controller @ 13 ; - jinit_d_main_controller @ 14 ; - jinit_d_post_controller @ 15 ; - jinit_downsampler @ 16 ; - jinit_forward_dct @ 17 ; - jinit_huff_decoder @ 18 ; - jinit_huff_encoder @ 19 ; - jinit_input_controller @ 20 ; - jinit_inverse_dct @ 21 ; - jinit_marker_reader @ 22 ; - jinit_marker_writer @ 23 ; - jinit_master_decompress @ 24 ; - jinit_memory_mgr @ 25 ; - jinit_merged_upsampler @ 26 ; - jinit_phuff_decoder @ 27 ; - jinit_phuff_encoder @ 28 ; - jinit_upsampler @ 29 ; - jpeg_CreateCompress @ 30 ; - jpeg_CreateDecompress @ 31 ; - jpeg_abort @ 32 ; - jpeg_abort_compress @ 33 ; - jpeg_abort_decompress @ 34 ; - jpeg_add_quant_table @ 35 ; - jpeg_alloc_huff_table @ 36 ; - jpeg_alloc_quant_table @ 37 ; - jpeg_calc_output_dimensions @ 38 ; - jpeg_consume_input @ 39 ; - jpeg_copy_critical_parameters @ 40 ; - jpeg_default_colorspace @ 41 ; - jpeg_destroy @ 42 ; - jpeg_destroy_compress @ 43 ; - jpeg_destroy_decompress @ 44 ; - jpeg_fdct_float @ 45 ; - jpeg_fdct_ifast @ 46 ; - jpeg_fdct_islow @ 47 ; - jpeg_fill_bit_buffer @ 48 ; - jpeg_finish_compress @ 49 ; - jpeg_finish_decompress @ 50 ; - jpeg_finish_output @ 51 ; - jpeg_free_large @ 52 ; - jpeg_free_small @ 53 ; - jpeg_gen_optimal_table @ 54 ; - jpeg_get_large @ 55 ; - jpeg_get_small @ 56 ; - jpeg_has_multiple_scans @ 57 ; - jpeg_huff_decode @ 58 ; - jpeg_idct_1x1 @ 59 ; - jpeg_idct_2x2 @ 60 ; - jpeg_idct_4x4 @ 61 ; - jpeg_idct_float @ 62 ; - jpeg_idct_ifast @ 63 ; - jpeg_idct_islow @ 64 ; - jpeg_input_complete @ 65 ; - jpeg_make_c_derived_tbl @ 66 ; - jpeg_make_d_derived_tbl @ 67 ; - jpeg_mem_available @ 68 ; - jpeg_mem_init @ 69 ; - jpeg_mem_term @ 70 ; - jpeg_new_colormap @ 71 ; - jpeg_open_backing_store @ 72 ; - jpeg_quality_scaling @ 73 ; - jpeg_read_coefficients @ 74 ; - jpeg_read_header @ 75 ; - jpeg_read_raw_data @ 76 ; - jpeg_read_scanlines @ 77 ; - jpeg_resync_to_restart @ 78 ; - jpeg_save_markers @ 79 ; - jpeg_set_colorspace @ 80 ; - jpeg_set_defaults @ 81 ; - jpeg_set_linear_quality @ 82 ; - jpeg_set_marker_processor @ 83 ; - jpeg_set_quality @ 84 ; - jpeg_simple_progression @ 85 ; - jpeg_start_compress @ 86 ; - jpeg_start_decompress @ 87 ; - jpeg_start_output @ 88 ; - jpeg_std_error @ 89 ; - jpeg_stdio_dest @ 90 ; - jpeg_stdio_src @ 91 ; - jpeg_suppress_tables @ 92 ; - jpeg_write_coefficients @ 93 ; - jpeg_write_m_byte @ 94 ; - jpeg_write_m_header @ 95 ; - jpeg_write_marker @ 96 ; - jpeg_write_raw_data @ 97 ; - jpeg_write_scanlines @ 98 ; - jpeg_write_tables @ 99 ; - jround_up @ 100 ; - jzero_far @ 101 ; - jpeg_mem_dest @ 102 ; - jpeg_mem_src @ 103 ; - jpeg_skip_scanlines @ 104 ; - jpeg_crop_scanline @ 105 ; - jpeg_read_icc_profile @ 106 ; - jpeg_write_icc_profile @ 107 ; diff --git a/win/jpeg62.def b/win/jpeg62.def index f3c69b2..cded425 100644 --- a/win/jpeg62.def +++ b/win/jpeg62.def @@ -100,7 +100,34 @@ EXPORTS jpeg_write_tables @ 99 ; jround_up @ 100 ; jzero_far @ 101 ; - jpeg_skip_scanlines @ 102 ; - jpeg_crop_scanline @ 103 ; - jpeg_read_icc_profile @ 104 ; - jpeg_write_icc_profile @ 105 ; + jpeg_mem_dest @ 102 ; + jpeg_mem_src @ 103 ; + jpeg_skip_scanlines @ 104 ; + jpeg_crop_scanline @ 105 ; + jpeg_read_icc_profile @ 106 ; + jpeg_write_icc_profile @ 107 ; + j12copy_sample_rows @ 108 ; + j12init_1pass_quantizer @ 109 ; + j12init_2pass_quantizer @ 110 ; + j12init_c_coef_controller @ 111 ; + j12init_c_main_controller @ 112 ; + j12init_c_prep_controller @ 113 ; + j12init_color_converter @ 114 ; + j12init_color_deconverter @ 115 ; + j12init_d_coef_controller @ 116 ; + j12init_d_main_controller @ 117 ; + j12init_d_post_controller @ 118 ; + j12init_downsampler @ 119 ; + j12init_forward_dct @ 120 ; + j12init_inverse_dct @ 121 ; + j12init_merged_upsampler @ 122 ; + j12init_upsampler @ 123 ; + jpeg12_read_raw_data @ 124 ; + jpeg12_read_scanlines @ 125 ; + jpeg12_write_raw_data @ 126 ; + jpeg12_write_scanlines @ 127 ; + jpeg12_skip_scanlines @ 128 ; + jpeg12_crop_scanline @ 129 ; + jpeg_enable_lossless @ 130 ; + jpeg16_read_scanlines @ 131 ; + jpeg16_write_scanlines @ 132 ; diff --git a/win/jpeg7-memsrcdst.def b/win/jpeg7-memsrcdst.def deleted file mode 100644 index a005aff..0000000 --- a/win/jpeg7-memsrcdst.def +++ /dev/null @@ -1,110 +0,0 @@ -EXPORTS - jcopy_block_row @ 1 ; - jcopy_sample_rows @ 2 ; - jdiv_round_up @ 3 ; - jinit_1pass_quantizer @ 4 ; - jinit_2pass_quantizer @ 5 ; - jinit_c_coef_controller @ 6 ; - jinit_c_main_controller @ 7 ; - jinit_c_master_control @ 8 ; - jinit_c_prep_controller @ 9 ; - jinit_color_converter @ 10 ; - jinit_color_deconverter @ 11 ; - jinit_compress_master @ 12 ; - jinit_d_coef_controller @ 13 ; - jinit_d_main_controller @ 14 ; - jinit_d_post_controller @ 15 ; - jinit_downsampler @ 16 ; - jinit_forward_dct @ 17 ; - jinit_huff_decoder @ 18 ; - jinit_huff_encoder @ 19 ; - jinit_input_controller @ 20 ; - jinit_inverse_dct @ 21 ; - jinit_marker_reader @ 22 ; - jinit_marker_writer @ 23 ; - jinit_master_decompress @ 24 ; - jinit_memory_mgr @ 25 ; - jinit_merged_upsampler @ 26 ; - jinit_phuff_decoder @ 27 ; - jinit_phuff_encoder @ 28 ; - jinit_upsampler @ 29 ; - jpeg_CreateCompress @ 30 ; - jpeg_CreateDecompress @ 31 ; - jpeg_abort @ 32 ; - jpeg_abort_compress @ 33 ; - jpeg_abort_decompress @ 34 ; - jpeg_add_quant_table @ 35 ; - jpeg_alloc_huff_table @ 36 ; - jpeg_alloc_quant_table @ 37 ; - jpeg_calc_jpeg_dimensions @ 38 ; - jpeg_calc_output_dimensions @ 39 ; - jpeg_consume_input @ 40 ; - jpeg_copy_critical_parameters @ 41 ; - jpeg_default_colorspace @ 42 ; - jpeg_default_qtables @ 43 ; - jpeg_destroy @ 44 ; - jpeg_destroy_compress @ 45 ; - jpeg_destroy_decompress @ 46 ; - jpeg_fdct_float @ 47 ; - jpeg_fdct_ifast @ 48 ; - jpeg_fdct_islow @ 49 ; - jpeg_fill_bit_buffer @ 50 ; - jpeg_finish_compress @ 51 ; - jpeg_finish_decompress @ 52 ; - jpeg_finish_output @ 53 ; - jpeg_free_large @ 54 ; - jpeg_free_small @ 55 ; - jpeg_gen_optimal_table @ 56 ; - jpeg_get_large @ 57 ; - jpeg_get_small @ 58 ; - jpeg_has_multiple_scans @ 59 ; - jpeg_huff_decode @ 60 ; - jpeg_idct_1x1 @ 61 ; - jpeg_idct_2x2 @ 62 ; - jpeg_idct_4x4 @ 63 ; - jpeg_idct_float @ 64 ; - jpeg_idct_ifast @ 65 ; - jpeg_idct_islow @ 66 ; - jpeg_input_complete @ 67 ; - jpeg_make_c_derived_tbl @ 68 ; - jpeg_make_d_derived_tbl @ 69 ; - jpeg_mem_available @ 70 ; - jpeg_mem_init @ 71 ; - jpeg_mem_term @ 72 ; - jpeg_new_colormap @ 73 ; - jpeg_open_backing_store @ 74 ; - jpeg_quality_scaling @ 75 ; - jpeg_read_coefficients @ 76 ; - jpeg_read_header @ 77 ; - jpeg_read_raw_data @ 78 ; - jpeg_read_scanlines @ 79 ; - jpeg_resync_to_restart @ 80 ; - jpeg_save_markers @ 81 ; - jpeg_set_colorspace @ 82 ; - jpeg_set_defaults @ 83 ; - jpeg_set_linear_quality @ 84 ; - jpeg_set_marker_processor @ 85 ; - jpeg_set_quality @ 86 ; - jpeg_simple_progression @ 87 ; - jpeg_start_compress @ 88 ; - jpeg_start_decompress @ 89 ; - jpeg_start_output @ 90 ; - jpeg_std_error @ 91 ; - jpeg_stdio_dest @ 92 ; - jpeg_stdio_src @ 93 ; - jpeg_suppress_tables @ 94 ; - jpeg_write_coefficients @ 95 ; - jpeg_write_m_byte @ 96 ; - jpeg_write_m_header @ 97 ; - jpeg_write_marker @ 98 ; - jpeg_write_raw_data @ 99 ; - jpeg_write_scanlines @ 100 ; - jpeg_write_tables @ 101 ; - jround_up @ 102 ; - jzero_far @ 103 ; - jpeg_mem_dest @ 104 ; - jpeg_mem_src @ 105 ; - jpeg_skip_scanlines @ 106 ; - jpeg_crop_scanline @ 107 ; - jpeg_read_icc_profile @ 108 ; - jpeg_write_icc_profile @ 109 ; diff --git a/win/jpeg7.def b/win/jpeg7.def index 49f4c02..ba7f442 100644 --- a/win/jpeg7.def +++ b/win/jpeg7.def @@ -102,7 +102,34 @@ EXPORTS jpeg_write_tables @ 101 ; jround_up @ 102 ; jzero_far @ 103 ; - jpeg_skip_scanlines @ 104 ; - jpeg_crop_scanline @ 105 ; - jpeg_read_icc_profile @ 106 ; - jpeg_write_icc_profile @ 107 ; + jpeg_mem_dest @ 104 ; + jpeg_mem_src @ 105 ; + jpeg_skip_scanlines @ 106 ; + jpeg_crop_scanline @ 107 ; + jpeg_read_icc_profile @ 108 ; + jpeg_write_icc_profile @ 109 ; + j12copy_sample_rows @ 110 ; + j12init_1pass_quantizer @ 111 ; + j12init_2pass_quantizer @ 112 ; + j12init_c_coef_controller @ 113 ; + j12init_c_main_controller @ 114 ; + j12init_c_prep_controller @ 115 ; + j12init_color_converter @ 116 ; + j12init_color_deconverter @ 117 ; + j12init_d_coef_controller @ 118 ; + j12init_d_main_controller @ 119 ; + j12init_d_post_controller @ 120 ; + j12init_downsampler @ 121 ; + j12init_forward_dct @ 122 ; + j12init_inverse_dct @ 123 ; + j12init_merged_upsampler @ 124 ; + j12init_upsampler @ 125 ; + jpeg12_read_raw_data @ 126 ; + jpeg12_read_scanlines @ 127 ; + jpeg12_write_raw_data @ 128 ; + jpeg12_write_scanlines @ 129 ; + jpeg12_skip_scanlines @ 130 ; + jpeg12_crop_scanline @ 131 ; + jpeg_enable_lossless @ 132 ; + jpeg16_read_scanlines @ 133 ; + jpeg16_write_scanlines @ 134 ; diff --git a/win/jpeg8.def b/win/jpeg8.def index 0a53125..9fa1fe6 100644 --- a/win/jpeg8.def +++ b/win/jpeg8.def @@ -109,3 +109,28 @@ EXPORTS jpeg_crop_scanline @ 108 ; jpeg_read_icc_profile @ 109 ; jpeg_write_icc_profile @ 110 ; + j12copy_sample_rows @ 111 ; + j12init_1pass_quantizer @ 112 ; + j12init_2pass_quantizer @ 113 ; + j12init_c_coef_controller @ 114 ; + j12init_c_main_controller @ 115 ; + j12init_c_prep_controller @ 116 ; + j12init_color_converter @ 117 ; + j12init_color_deconverter @ 118 ; + j12init_d_coef_controller @ 119 ; + j12init_d_main_controller @ 120 ; + j12init_d_post_controller @ 121 ; + j12init_downsampler @ 122 ; + j12init_forward_dct @ 123 ; + j12init_inverse_dct @ 124 ; + j12init_merged_upsampler @ 125 ; + j12init_upsampler @ 126 ; + jpeg12_read_raw_data @ 127 ; + jpeg12_read_scanlines @ 128 ; + jpeg12_write_raw_data @ 129 ; + jpeg12_write_scanlines @ 130 ; + jpeg12_skip_scanlines @ 131 ; + jpeg12_crop_scanline @ 132 ; + jpeg_enable_lossless @ 133 ; + jpeg16_read_scanlines @ 134 ; + jpeg16_write_scanlines @ 135 ; diff --git a/win/turbojpeg.rc.in b/win/turbojpeg.rc.in index cc7ab3a..c6cfc2d 100644 --- a/win/turbojpeg.rc.in +++ b/win/turbojpeg.rc.in @@ -24,7 +24,7 @@ BEGIN VALUE "ProductVersion", "@VERSION@" VALUE "ProductName", "@CMAKE_PROJECT_NAME@" VALUE "InternalName", "turbojpeg" - VALUE "LegalCopyright", "Copyright \xA9 @COPYRIGHT_YEAR@ The libjpeg-turbo Project and many others" + VALUE "LegalCopyright", L"Copyright \xA9 @COPYRIGHT_YEAR@ The libjpeg-turbo Project and many others" VALUE "OriginalFilename", "turbojpeg.dll" END END diff --git a/wizard.txt b/wizard.txt index c57fe38..a62fc1a 100644 --- a/wizard.txt +++ b/wizard.txt @@ -116,23 +116,23 @@ Multiple Scan / Progression Control By default, cjpeg emits a single-scan sequential JPEG file. The -progressive switch generates a progressive JPEG file using a default series -of progression parameters. You can create multiple-scan sequential JPEG -files or progressive JPEG files with custom progression parameters by using -the -scans switch: +of progression parameters. You can create multiple-scan sequential or lossless +JPEG files or progressive JPEG files with custom progression parameters by +using the -scans switch: -scans file Use the scan sequence given in the named file. The specified file should be a text file containing a "scan script". The script specifies the contents and ordering of the scans to be emitted. Each entry in the script defines one scan. A scan definition specifies -the components to be included in the scan, and for progressive JPEG it also -specifies the progression parameters Ss,Se,Ah,Al for the scan. Scan -definitions are separated by semicolons (';'). A semicolon after the last -scan definition is optional. +the components to be included in the scan, and for progressive and lossless +JPEG it also specifies the progression/lossless parameters Ss,Se,Ah,Al for the +scan. Scan definitions are separated by semicolons (';'). A semicolon after +the last scan definition is optional. Each scan definition contains one to four component indexes, optionally -followed by a colon (':') and the four progressive-JPEG parameters. The -component indexes denote which color component(s) are to be transmitted in +followed by a colon (':') and the four progressive/lossless-JPEG parameters. +The component indexes denote which color component(s) are to be transmitted in the scan. Components are numbered in the order in which they appear in the JPEG SOF marker, with the first component being numbered 0. (Note that these indexes are not the "component ID" codes assigned to the components, just @@ -143,13 +143,32 @@ The progression parameters for each scan are: Se Zigzag index of last coefficient included in scan Ah Zero for first scan of a coefficient, else Al of prior scan Al Successive approximation low bit position for scan -If the progression parameters are omitted, the values 0,63,0,0 are used, -producing a sequential JPEG file. cjpeg automatically determines whether + +The lossless parameters for each scan are: + Ss Predictor selection value + Se Must be zero + Ah Must be zero + Al Point transform value + +If the progression/lossless parameters are omitted, the values 0,63,0,0 are +used, producing a sequential JPEG file. cjpeg automatically determines whether the script represents a progressive or sequential file, by observing whether -Ss and Se values other than 0 and 63 appear. (The -progressive switch is -not needed to specify this; in fact, it is ignored when -scans appears.) -The scan script must meet the JPEG restrictions on progression sequences. -(cjpeg checks that the spec's requirements are obeyed.) +Ss and Se values other than 0 and 63 appear. cjpeg also automatically +determines whether the script represents a lossless file, by observing whether +Ss (the predictor selection value) is non-zero and Se is zero, which are +illegal values for progressive and sequential files. (The -progressive and +-lossless switches are not needed to specify this; in fact, they are ignored +when -scans appears.) The scan script must meet the JPEG restrictions on +progression sequences. (cjpeg checks that the spec's requirements are obeyed.) +More specifically: + + * An AC scan cannot include coefficients from more than one component. + + * An AC scan for a particular component must be preceded by a DC scan + that includes the same component. + + * Only the first AC scan that includes a particular coefficient for a + particular component can include more than one bit from that coefficient. Scan script files are free format, in that arbitrary whitespace can appear between numbers and around punctuation. Also, comments can be included: a diff --git a/wrbmp.c b/wrbmp.c index 45fff68..7850e7b 100644 --- a/wrbmp.c +++ b/wrbmp.c @@ -480,6 +480,9 @@ jinit_write_bmp(j_decompress_ptr cinfo, boolean is_os2, bmp_dest_ptr dest; JDIMENSION row_width; + if (cinfo->data_precision != 8) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + /* Create module interface object, fill in method pointers */ dest = (bmp_dest_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, diff --git a/wrgif.c b/wrgif.c index 620a3ba..2377357 100644 --- a/wrgif.c +++ b/wrgif.c @@ -5,7 +5,7 @@ * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 2015-2019 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2015, 2017, 2022, D. R. Commander. + * Copyright (C) 2015, 2017, 2022-2023, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -31,8 +31,9 @@ */ #include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ +#include "jsamplecomp.h" -#ifdef GIF_SUPPORTED +#if defined(GIF_SUPPORTED) && BITS_IN_JSAMPLE != 16 #define MAX_LZW_BITS 12 /* maximum LZW code size (4096 symbols) */ @@ -249,7 +250,7 @@ put_3bytes(gif_dest_ptr dinfo, int val) LOCAL(void) -emit_header(gif_dest_ptr dinfo, int num_colors, JSAMPARRAY colormap) +emit_header(gif_dest_ptr dinfo, int num_colors, _JSAMPARRAY colormap) /* Output the GIF file header, including color map */ /* If colormap == NULL, synthesize a grayscale colormap */ { @@ -308,7 +309,7 @@ emit_header(gif_dest_ptr dinfo, int num_colors, JSAMPARRAY colormap) } } else { /* fill out the map to a power of 2 */ - put_3bytes(dinfo, CENTERJSAMPLE >> cshift); + put_3bytes(dinfo, _CENTERJSAMPLE >> cshift); } } /* Write image separator and Image Descriptor */ @@ -337,9 +338,10 @@ start_output_gif(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) gif_dest_ptr dest = (gif_dest_ptr)dinfo; if (cinfo->quantize_colors) - emit_header(dest, cinfo->actual_number_of_colors, cinfo->colormap); + emit_header(dest, cinfo->actual_number_of_colors, + (_JSAMPARRAY)cinfo->colormap); else - emit_header(dest, 256, (JSAMPARRAY)NULL); + emit_header(dest, 256, (_JSAMPARRAY)NULL); } @@ -358,14 +360,14 @@ put_LZW_pixel_rows(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, JDIMENSION rows_supplied) { gif_dest_ptr dest = (gif_dest_ptr)dinfo; - register JSAMPROW ptr; + register _JSAMPROW ptr; register JDIMENSION col; code_int c; register hash_int i; register hash_int disp; register hash_entry probe_value; - ptr = dest->pub.buffer[0]; + ptr = dest->pub._buffer[0]; for (col = cinfo->output_width; col > 0; col--) { /* Accept and compress one 8-bit byte */ c = (code_int)(*ptr++); @@ -459,11 +461,11 @@ put_raw_pixel_rows(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, JDIMENSION rows_supplied) { gif_dest_ptr dest = (gif_dest_ptr)dinfo; - register JSAMPROW ptr; + register _JSAMPROW ptr; register JDIMENSION col; code_int c; - ptr = dest->pub.buffer[0]; + ptr = dest->pub._buffer[0]; for (col = cinfo->output_width; col > 0; col--) { c = (code_int)(*ptr++); /* Accept and output one pixel value. @@ -522,10 +524,13 @@ calc_buffer_dimensions_gif(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) */ GLOBAL(djpeg_dest_ptr) -jinit_write_gif(j_decompress_ptr cinfo, boolean is_lzw) +_jinit_write_gif(j_decompress_ptr cinfo, boolean is_lzw) { gif_dest_ptr dest; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + /* Create module interface object, fill in method pointers */ dest = (gif_dest_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, @@ -554,7 +559,7 @@ jinit_write_gif(j_decompress_ptr cinfo, boolean is_lzw) ERREXIT(cinfo, JERR_GIF_BUG); /* Create decompressor output buffer. */ - dest->pub.buffer = (*cinfo->mem->alloc_sarray) + dest->pub._buffer = (_JSAMPARRAY)(*cinfo->mem->alloc_sarray) ((j_common_ptr)cinfo, JPOOL_IMAGE, cinfo->output_width, (JDIMENSION)1); dest->pub.buffer_height = 1; @@ -577,4 +582,4 @@ jinit_write_gif(j_decompress_ptr cinfo, boolean is_lzw) return (djpeg_dest_ptr)dest; } -#endif /* GIF_SUPPORTED */ +#endif /* defined(GIF_SUPPORTED) && BITS_IN_JSAMPLE != 16 */ diff --git a/wrppm.c b/wrppm.c index 57c8aaf..b4f9897 100644 --- a/wrppm.c +++ b/wrppm.c @@ -22,7 +22,8 @@ #include "cmyk.h" #include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ -#ifdef PPM_SUPPORTED +#if defined(PPM_SUPPORTED) && \ + (BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED)) /* @@ -58,7 +59,7 @@ /* - * When JSAMPLE is the same size as char, we can just fwrite() the + * When _JSAMPLE is the same size as char, we can just fwrite() the * decompressed data to the PPM or PGM file. */ @@ -70,9 +71,9 @@ typedef struct { /* Usually these two pointers point to the same place: */ char *iobuffer; /* fwrite's I/O buffer */ - JSAMPROW pixrow; /* decompressor output buffer */ + _JSAMPROW pixrow; /* decompressor output buffer */ size_t buffer_width; /* width of I/O buffer */ - JDIMENSION samples_per_row; /* JSAMPLEs per output row */ + JDIMENSION samples_per_row; /* _JSAMPLEs per output row */ } ppm_dest_struct; typedef ppm_dest_struct *ppm_dest_ptr; @@ -107,12 +108,12 @@ copy_pixel_rows(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, { ppm_dest_ptr dest = (ppm_dest_ptr)dinfo; register char *bufferptr; - register JSAMPROW ptr; + register _JSAMPROW ptr; #if BITS_IN_JSAMPLE != 8 register JDIMENSION col; #endif - ptr = dest->pub.buffer[0]; + ptr = dest->pub._buffer[0]; bufferptr = dest->iobuffer; #if BITS_IN_JSAMPLE == 8 memcpy(bufferptr, ptr, dest->samples_per_row); @@ -134,14 +135,14 @@ put_rgb(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, JDIMENSION rows_supplied) { ppm_dest_ptr dest = (ppm_dest_ptr)dinfo; register char *bufferptr; - register JSAMPROW ptr; + register _JSAMPROW ptr; register JDIMENSION col; register int rindex = rgb_red[cinfo->out_color_space]; register int gindex = rgb_green[cinfo->out_color_space]; register int bindex = rgb_blue[cinfo->out_color_space]; register int ps = rgb_pixelsize[cinfo->out_color_space]; - ptr = dest->pub.buffer[0]; + ptr = dest->pub._buffer[0]; bufferptr = dest->iobuffer; for (col = cinfo->output_width; col > 0; col--) { PUTPPMSAMPLE(bufferptr, ptr[rindex]); @@ -163,13 +164,13 @@ put_cmyk(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, { ppm_dest_ptr dest = (ppm_dest_ptr)dinfo; register char *bufferptr; - register JSAMPROW ptr; + register _JSAMPROW ptr; register JDIMENSION col; - ptr = dest->pub.buffer[0]; + ptr = dest->pub._buffer[0]; bufferptr = dest->iobuffer; for (col = cinfo->output_width; col > 0; col--) { - JSAMPLE r, g, b, c = *ptr++, m = *ptr++, y = *ptr++, k = *ptr++; + _JSAMPLE r, g, b, c = *ptr++, m = *ptr++, y = *ptr++, k = *ptr++; cmyk_to_rgb(c, m, y, k, &r, &g, &b); PUTPPMSAMPLE(bufferptr, r); PUTPPMSAMPLE(bufferptr, g); @@ -191,13 +192,13 @@ put_demapped_rgb(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, ppm_dest_ptr dest = (ppm_dest_ptr)dinfo; register char *bufferptr; register int pixval; - register JSAMPROW ptr; - register JSAMPROW color_map0 = cinfo->colormap[0]; - register JSAMPROW color_map1 = cinfo->colormap[1]; - register JSAMPROW color_map2 = cinfo->colormap[2]; + register _JSAMPROW ptr; + register _JSAMPROW color_map0 = ((_JSAMPARRAY)cinfo->colormap)[0]; + register _JSAMPROW color_map1 = ((_JSAMPARRAY)cinfo->colormap)[1]; + register _JSAMPROW color_map2 = ((_JSAMPARRAY)cinfo->colormap)[2]; register JDIMENSION col; - ptr = dest->pub.buffer[0]; + ptr = dest->pub._buffer[0]; bufferptr = dest->iobuffer; for (col = cinfo->output_width; col > 0; col--) { pixval = *ptr++; @@ -215,11 +216,11 @@ put_demapped_gray(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, { ppm_dest_ptr dest = (ppm_dest_ptr)dinfo; register char *bufferptr; - register JSAMPROW ptr; - register JSAMPROW color_map = cinfo->colormap[0]; + register _JSAMPROW ptr; + register _JSAMPROW color_map = ((_JSAMPARRAY)cinfo->colormap)[0]; register JDIMENSION col; - ptr = dest->pub.buffer[0]; + ptr = dest->pub._buffer[0]; bufferptr = dest->iobuffer; for (col = cinfo->output_width; col > 0; col--) { PUTPPMSAMPLE(bufferptr, color_map[*ptr++]); @@ -304,10 +305,13 @@ calc_buffer_dimensions_ppm(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) */ GLOBAL(djpeg_dest_ptr) -jinit_write_ppm(j_decompress_ptr cinfo) +_jinit_write_ppm(j_decompress_ptr cinfo) { ppm_dest_ptr dest; + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + /* Create module interface object, fill in method pointers */ dest = (ppm_dest_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, @@ -325,7 +329,7 @@ jinit_write_ppm(j_decompress_ptr cinfo) ((j_common_ptr)cinfo, JPOOL_IMAGE, dest->buffer_width); if (cinfo->quantize_colors || BITS_IN_JSAMPLE != 8 || - sizeof(JSAMPLE) != sizeof(char) || + sizeof(_JSAMPLE) != sizeof(char) || #if RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 3 (cinfo->out_color_space != JCS_EXT_RGB && cinfo->out_color_space != JCS_RGB)) { @@ -336,7 +340,7 @@ jinit_write_ppm(j_decompress_ptr cinfo) * that's separate from the physical I/O buffer. We also need a * separate buffer if pixel format translation must take place. */ - dest->pub.buffer = (*cinfo->mem->alloc_sarray) + dest->pub._buffer = (_JSAMPARRAY)(*cinfo->mem->alloc_sarray) ((j_common_ptr)cinfo, JPOOL_IMAGE, cinfo->output_width * cinfo->output_components, (JDIMENSION)1); dest->pub.buffer_height = 1; @@ -353,9 +357,9 @@ jinit_write_ppm(j_decompress_ptr cinfo) dest->pub.put_pixel_rows = put_demapped_rgb; } else { /* We will fwrite() directly from decompressor output buffer. */ - /* Synthesize a JSAMPARRAY pointer structure */ - dest->pixrow = (JSAMPROW)dest->iobuffer; - dest->pub.buffer = &dest->pixrow; + /* Synthesize a _JSAMPARRAY pointer structure */ + dest->pixrow = (_JSAMPROW)dest->iobuffer; + dest->pub._buffer = &dest->pixrow; dest->pub.buffer_height = 1; dest->pub.put_pixel_rows = put_pixel_rows; } @@ -363,4 +367,5 @@ jinit_write_ppm(j_decompress_ptr cinfo) return (djpeg_dest_ptr)dest; } -#endif /* PPM_SUPPORTED */ +#endif /* defined(PPM_SUPPORTED) && + (BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED)) */ diff --git a/wrtarga.c b/wrtarga.c index 67ca1f0..110e421 100644 --- a/wrtarga.c +++ b/wrtarga.c @@ -230,6 +230,9 @@ jinit_write_targa(j_decompress_ptr cinfo) { tga_dest_ptr dest; + if (cinfo->data_precision != 8) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + /* Create module interface object, fill in method pointers */ dest = (tga_dest_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, -- 2.7.4

    • Exception Summary 
      Exception
      TJExceptionTJException